Created
September 30, 2019 20:22
-
-
Save calebastey/130f38bd7b2bde97e4efcdfa178ad38c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby -w | |
require 'cgi' | |
require 'io/console' | |
require 'json' | |
require 'logger' | |
require 'net/http' | |
require 'optionparser' | |
require 'pathname' | |
require 'uri' | |
class FilesDotCom | |
# Struct to hold the configuration options read in from the command line | |
Options = Struct.new( | |
:api_key, | |
:district, | |
:username, | |
:full_name, | |
:email, | |
:user_type, | |
:public_key_path, | |
:pm_username, | |
:analyst_usernames, | |
:pd_usernames, | |
:verbose, | |
:dry_run | |
) | |
end | |
# Class for managing files.com resources and configurations via the the [https://developers.files.com](API) | |
class FilesDotCom::Manager < FilesDotCom | |
# Dummy class to serve as our Net::HTTP instance in dry run mode | |
class DryRunHttp | |
def request(request) | |
end | |
end | |
LOGGER = Logger.new(STDOUT) | |
LOGGER.level = Logger::INFO | |
API_BASE = "https://app.files.com/api/rest/v1" | |
API_BASE_URI = URI(API_BASE) | |
POST = 'POST' | |
GET = 'GET' | |
PUT = 'PUT' | |
USER_ID_CACHE = {} | |
GROUP_ID_CACHE = {} | |
DIRECTORY_ID_CACHE = {} | |
# stupidly large int to be used as a dummy value for IDs in dry run mode so we don't damage anything if we accidentally | |
# make a real API call | |
DRY_RUN_ID = 4611686018427387903 | |
attr_accessor :http, :options, :dry_run | |
# Initialize a `FilesDotComManager` | |
# | |
# @param api_key [String] the API key to be used for files.com authentication | |
# @return verbose [Boolean] whether to enable verbose (`DEBUG`-level) logging | |
def initialize(api_key:, verbose: false, dry_run: true) | |
@api_key = api_key | |
LOGGER.level = Logger::DEBUG if verbose | |
@dry_run = dry_run | |
if dry_run | |
@http = DryRunHttp.new | |
original_formatter = Logger::Formatter.new | |
LOGGER.formatter = proc { |severity, datetime, progname, msg| | |
original_formatter.call(severity, datetime, progname, "[DRY RUN] #{msg}\n") | |
} | |
LOGGER.info "Running in dry-run mode. Requests will be logged but nothing will actually happen." | |
else | |
@http = Net::HTTP.start(API_BASE_URI.host, API_BASE_URI.port, :use_ssl => API_BASE_URI.scheme == 'https') | |
end | |
end | |
# Make an HTTP request against the an endpoint on the files.com API | |
# | |
# @param endpoint [String] the full endpoint path including any query parameters | |
# @param method [String] the HTTP method to be used. Must be one of [`GET`, `PUT`, `POST`] | |
# @param authenticate [Boolean] whether to authenticate the request with the API token; defaults to `true` | |
# | |
# @return [Net::HTTPResponse] the response returned from the request | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def http_request(endpoint:, method: GET, data: {}, authenticate: true) | |
uri = URI("#{API_BASE}/#{endpoint}") | |
case method | |
when GET | |
request = Net::HTTP::Get.new uri | |
when POST | |
request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json') | |
request.body = data.to_json | |
when PUT | |
request = Net::HTTP::Put.new(uri, 'Content-Type' => 'application/json') | |
request.body = data.to_json | |
else | |
raise ArgumentError, "Invalid request method: #{method}" | |
end | |
request.basic_auth @api_key, "xxx" if authenticate | |
request['Accept'] = 'application/json' | |
LOGGER.debug "Making #{method} request to URI #{uri} with data #{data}" | |
return if @dry_run | |
response = request_with_retries(request: request) | |
raise IOError, "Received response code #{response[:data].code} with body #{response[:data].body}" unless response[:success] | |
LOGGER.debug "Received response code #{response[:data].code} with body #{response[:data].body}" | |
response[:data] | |
end | |
# Makes a request with retry logic. Files.com will occasionally throw 500s, stopping the entire job. This is a | |
# known bug and the retry logic should provide a bit of a workaround. | |
# | |
# @param request HTTP request to retry | |
# @param num_retries Number of times to try for success before giving up | |
# | |
# @return a map containing whether or not it was successful and the last response before success or giving up | |
def request_with_retries(request:, num_retries: 5) | |
tries = 0 | |
success = false | |
while tries < num_retries and !success do | |
response = @http.request request | |
if response.code.to_i < 300 | |
success = true | |
else | |
LOGGER.info "Received response code #{response.code} with body #{response.body}. Retrying #{num_retries - tries} times" | |
end | |
tries += 1 | |
end | |
{:success => success, :data => response} | |
end | |
# Create a directory on files.com. Noop if the directory already exists | |
# | |
# @param directory_name [String] the full path to the directory to be created | |
# | |
# @return [Net::HTTPResponse] the response from the directory creation request | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def create_directory(directory_name:) | |
id = get_directory_id directory_name | |
unless id.nil? | |
LOGGER.info "Not creating directory #{directory_name} because it already exists" | |
return id | |
end | |
LOGGER.info "Creating directory #{directory_name}" | |
endpoint = "folders/#{directory_name}" | |
http_request(endpoint: endpoint, method: POST) | |
end | |
# Create a files.com user. Noop if the user already exists | |
# | |
# @param username [String] the username | |
# @param email [String] the email associated with the user | |
# @param full_name [String] the user's full name | |
# @param user_type [String] the type of user to be created; must be one of :uploader, :viewer, :uploader_viewer, :bot] | |
# Create a directory on files.com. Noop if the directory already exists | |
# | |
# @return [Net::HTTPResponse] the response from the user creation request | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def create_user(username:, email:, full_name:, user_type:) | |
id = get_user_id username | |
unless id.nil? | |
LOGGER.info "Not creating user #{username} because it already exists" | |
return id | |
end | |
LOGGER.info "Creating user #{username}" | |
is_bot_user = user_type == :bot | |
user_data = { | |
email: email, | |
authentication_method: "password", | |
dav_permission: false, | |
ftp_permission: false, | |
language: "en", | |
name: full_name, | |
receive_admin_alerts: false, | |
require_password_change: true, | |
restapi_permission: !is_bot_user, # this covers both API and web UI access | |
self_managed: !is_bot_user, | |
sftp_permission: true, | |
site_admin: false, | |
ssl_required: "always_require", | |
time_zone: "Pacific Time (US & Canada)", | |
username: username, | |
} | |
LOGGER.debug("Creating user with attributes #{user_data}") | |
endpoint = "users.json" | |
response = http_request(endpoint: endpoint, method: POST, data: user_data) | |
unless @dry_run | |
id = JSON.parse(response.body)['id'] | |
else | |
id = DRY_RUN_ID | |
end | |
USER_ID_CACHE[username] = id | |
if user_type != "bot" | |
generate_password_reset(username: username) | |
end | |
id | |
end | |
# Create a group on files.com. Noop if the group already exists | |
# | |
# @param group_name [String] the name of the group to be created | |
# | |
# @return [Net::HTTPResponse] the response from the group creation request | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def create_group(group_name:) | |
id = get_group_id group_name | |
unless id.nil? | |
LOGGER.info "Not creating group #{group_name} becausae it already exists" | |
return id | |
end | |
LOGGER.info "Creating group #{group_name}" | |
endpoint = "groups.json" | |
data = { name: group_name } | |
response = http_request(endpoint: endpoint, method: POST, data: data) | |
unless @dry_run | |
id = JSON.parse(response.body)['id'] | |
else | |
id = DRY_RUN_ID | |
end | |
GROUP_ID_CACHE[group_name] = id | |
id | |
end | |
# Add a permission on a folder for a group on files.com. Noop if the permission already exists | |
# | |
# @param group_name [String] the name of the group for which the permission is being added | |
# @param directory_name [String] the name of the directory on which the permission is being added | |
# @param permission_type [String] the type of permission to add. Must be one of :full, :readonly, :writeonly, :previewonly, | |
# :history] | |
# | |
# @return [Net::HTTPResponse] the response from the permission creation request | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def add_permission_to_group(group_name:, directory_name:, permission_type:) | |
if group_has_permission?( | |
group_name: group_name, | |
directory_name: directory_name, | |
permission_type: permission_type | |
) | |
LOGGER.info "Not adding #{permission_type} permission on directory #{directory_name} to group #{group_name} because it already exists" | |
return | |
end | |
LOGGER.info "Adding #{permission_type} permission on directory #{directory_name} to group #{group_name}" | |
endpoint = "permissions.json" | |
unless @dry_run | |
group_id = get_group_id group_name | |
else | |
group_id = DRY_RUN_ID | |
end | |
if group_id.nil? | |
LOGGER.info "Not adding #{permission_type} permission on directory #{directory_name} to group #{group_name} because the group does not exist" | |
return | |
end | |
data = { | |
group_id: group_id, | |
path: directory_name, | |
recursive: true, | |
permission: permission_type | |
} | |
http_request(endpoint: endpoint, method: POST, data: data) | |
end | |
# Add a notification for files uploaded to a given folder on files.com. Noop if the notification already exists | |
# | |
# @param directory_name [String] the name of the directory on which the notification is being added | |
# @param group_name [String] the name of the group for which the notification is being added | |
# | |
# @return [Net::HTTPResponse] the response from the notification creation request | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def add_notification_to_directory(directory_name:, group_name:) | |
if group_has_notification?(group_name: group_name, directory_name: directory_name) | |
LOGGER.info "Not adding notification for group #{group_name} on directory #{directory_name} because it already exists" | |
return | |
end | |
LOGGER.info "Adding notification for group #{group_name} on directory #{directory_name}" | |
unless @dry_run | |
group_id = get_group_id group_name | |
else | |
group_id = DRY_RUN_ID | |
end | |
if group_id.nil? | |
LOGGER.info "Not adding notification for group #{group_name} on directory #{directory_name} because the group does not exist" | |
return | |
end | |
endpoint = "notifications.json" | |
data = { | |
group_id: group_id, | |
notify_on_copy: true, | |
path: directory_name, | |
send_interval: "five_minutes" | |
} | |
http_request(endpoint: endpoint, method: POST, data: data) | |
end | |
# Add a user to a group on files.com. Request is idempotent. | |
# | |
# @param username [String] the name of the user to be added to th egroup | |
# @param group_name [String] the name of the group for which the user is being added | |
# | |
# @return [Net::HTTPResponse] the response from the group addition request | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def add_user_to_group(username:, group_name:) | |
LOGGER.info "Adding user #{username} to group #{group_name}" | |
unless @dry_run | |
group_id = get_group_id group_name | |
user_id = get_user_id username | |
else | |
group_id = DRY_RUN_ID | |
user_id = DRY_RUN_ID | |
end | |
if group_id.nil? || user_id.nil? | |
LOGGER.info "Not adding user #{username} to group #{group_name} because the user or group does not exist" | |
end | |
endpoint = "groups/#{group_id}/memberships/#{user_id}.json" | |
data = { user_id: user_id, admin: false } | |
http_request(endpoint: endpoint, method: PUT, data: data) | |
end | |
# Add a public key for a user on files.com. Request is idempotent | |
# | |
# @param username [String] the username to which the key is being added | |
# @param public_key_path [String] the path to the public key on the file system. Must be a valid path. May be absolute or | |
# relative to the script directory | |
# | |
# @return [Net::HTTPResponse] the response from the key addition request | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def add_public_key(username:, public_key_path:) | |
LOGGER.info "Adding public key for user #{username} from path #{public_key_path}}" | |
unless @dry_run | |
user_id = get_user_id(username) | |
else | |
user_id = DRY_RUN_ID | |
end | |
raise ArgumentError, "Public key file #{public_key_path} does not exist" unless File.exist? public_key_path | |
endpoint = "users/#{user_id}/public_keys" | |
data = { | |
title: "Public SSH Key", | |
public_key: File.open(public_key_path, 'r').read | |
} | |
http_request(endpoint: endpoint, method: POST, data: data) | |
end | |
# Trigger a password reset and generate a password reset email for a user on files.com | |
# | |
# @param username [String] the user whose password is being reset | |
# | |
# @return [Net::HTTPResponse] the response from the password reset request | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def generate_password_reset(username:) | |
LOGGER.info("Generating password reset email for user #{username}") | |
endpoint = "sessions/forgot" | |
data = {username: username} | |
http_request(endpoint: endpoint, method: POST, data: data, authenticate: false) | |
end | |
# Check whether a group on files.com is set to receive notifications for files added to a given directory | |
# | |
# @param directory_name [String] the name of the directory on which to check for notifications | |
# @param group_name [String] the name of the group to check for notifications on the directory | |
# | |
# @return [Boolean] `true` if the group is set to receive notifications on the specified folder | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def group_has_notification?(group_name:, directory_name:) | |
LOGGER.info "Checking if group #{group_name} has notifications on directory #{directory_name}" | |
unless @dry_run | |
group_id = get_group_id(group_name) | |
else | |
group_id = DRY_RUN_ID | |
end | |
return false if group_id.nil? | |
endpoint = "notifications" | |
response = http_request(endpoint: endpoint, method: GET) | |
return false if @dry_run | |
data = JSON.parse(response.body).select do |notification| | |
notification['path'] == directory_name && notification['group_id'] == group_id | |
end | |
return !data.empty? | |
end | |
# Check whether a group on files.com has a given permission type on a given directory | |
# | |
# @param directory_name [String] the name of the directory on which to check for permissions | |
# @param group_name [String] the name of the group to check for permissions on the directory | |
# @param permission_type [String] the type of permission to check for. Must be one of [`full, `readonly`, `writeonly`, | |
# `previewonly`, `history`] | |
# | |
# @return [Boolean] `true` if the group has the specified permission on the specified folder | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def group_has_permission?(group_name:, directory_name:, permission_type:) | |
LOGGER.info "Checking if group #{group_name} has permission #{permission_type} on directory #{directory_name}" | |
unless @dry_run | |
group_id = get_group_id(group_name) | |
else | |
group_id = DRY_RUN_ID | |
end | |
return false if group_id.nil? | |
endpoint = "groups/#{group_id}/permissions" | |
response = http_request(endpoint: endpoint, method: GET) | |
return false if @dry_run | |
data = JSON.parse(response.body).select do |permission| | |
permission['path'] == directory_name && permission['permission'] == permission_type | |
end | |
return !data.empty? | |
end | |
# Get the ID for a given directory on files.com | |
# | |
# @param directory_name [String] the name of the directory | |
# | |
# @return [Integer] the ID for the directory or `nil` if the directory does not exist | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def get_directory_id(directory_name) | |
absolute_path = directory_name.gsub(%r{^([^/])}, '/\1') | |
LOGGER.info "Getting ID for directory #{absolute_path}" | |
id = DIRECTORY_ID_CACHE[directory_name] | |
return id unless id.nil? | |
pathname = Pathname(absolute_path) | |
parent = pathname.parent.to_s | |
directory = pathname.basename.to_s | |
endpoint = "folders/#{parent}?filter=#{directory}" | |
response = http_request(endpoint: endpoint, method: GET) | |
return nil if @dry_run | |
data = JSON.parse(response.body) | |
return nil if data.empty? | |
if (data.length > 1) | |
raise NameError, "Did not find exactly one result for directory #{directory_name}. Found #{data.length} results" | |
end | |
id = data[0]['id'] | |
DIRECTORY_ID_CACHE[directory_name] = id | |
id | |
end | |
# Get the ID for a given user on files.com | |
# | |
# @param directory_name [String] the username | |
# | |
# @return [Integer] the ID for the user or `nil` if the user does not exist | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def get_user_id(username) | |
LOGGER.info "Getting ID for user #{username}" | |
id = USER_ID_CACHE[username] | |
return id unless id.nil? | |
# URL-escape the username in the query to ensure the correct results are returned if special characters are present | |
endpoint = "users?q[username]=#{CGI.escape username}" | |
response = http_request(endpoint: endpoint, method: GET) | |
return nil if @dry_run | |
data = JSON.parse(response.body) | |
return nil if data.empty? | |
if (data.length > 1) | |
raise NameError, "Did not find exactly one result for user #{username}. Found #{data.length} results" | |
end | |
id = data[0]['id'] | |
USER_ID_CACHE[username] = id | |
id | |
end | |
# Get the ID for a given group on files.com | |
# | |
# @param group_name [String] the name of the group | |
# | |
# @return [Integer] the ID for the group or `nil` if the group does not exist | |
# @throws IOError if there is an error making the request or a non-2xx status is returned | |
def get_group_id(group_name) | |
LOGGER.info "Getting ID for group #{group_name}" | |
id = GROUP_ID_CACHE[group_name] | |
return id unless id.nil? | |
endpoint = "groups" | |
response = http_request(endpoint: endpoint, method: GET) | |
return nil if @dry_run | |
data = JSON.parse(response.body).select do |group| | |
group['name'] == group_name | |
end | |
return nil if data.empty? | |
if (data.length > 1) | |
raise NameError, "Did not find exactly one result for group #{group_name}. Found #{data.length} results" | |
end | |
id = data[0]['id'] | |
GROUP_ID_CACHE[group_name] = id | |
id | |
end | |
end | |
class FilesDotCom::Runner < FilesDotCom | |
AUTOMATED_UPLOAD_FOLDER_NAME = "standard-uploads-for-ict" | |
AD_HOC_UPLOAD_FOLDER_NAME = "other-uploads-for-ict" | |
REPORT_FOLDER_NAME = "files-from-ict" | |
INTERNAL_UPLOADER_GROUP = "ict-internal_all-districts-files-from-ict-uploader" | |
attr_accessor :options, | |
:manager, | |
:district_root_folder, | |
:uploader_group_name, | |
:viewer_group_name, | |
:upload_notifications_group_name, | |
:report_notifications_group_name, | |
:automated_upload_path, | |
:ad_hoc_upload_path, | |
:report_upload_path | |
def initialize(options) | |
@options = options | |
@uploader_group_name = "#{@options.district}-data-uploader" | |
@viewer_group_name = "#{@options.district}_report-viewer" | |
@upload_notifications_group_name = "#{@options.district}_uploads-for-ict-notifications" | |
@report_notifications_group_name = "#{@options.district}_files-from-ict-notifications" | |
@district_root_folder = @options.district | |
@automated_upload_path = "#{@district_root_folder}/#{AUTOMATED_UPLOAD_FOLDER_NAME}" | |
@ad_hoc_upload_path = "#{@district_root_folder}/#{AD_HOC_UPLOAD_FOLDER_NAME}" | |
@report_upload_path = "#{@district_root_folder}/#{REPORT_FOLDER_NAME}" | |
@manager = FilesDotCom::Manager.new(api_key: @options.api_key, verbose: @options.verbose, dry_run: @options.dry_run) | |
end | |
def create_district_directories | |
@manager.create_directory(directory_name: @district_root_folder) | |
[@automated_upload_path, @ad_hoc_upload_path, @report_upload_path].each do |path| | |
@manager.create_directory(directory_name: path) | |
end | |
end | |
def create_district_permission_groups | |
[@viewer_group_name, @uploader_group_name].each do |group| | |
@manager.create_group(group_name: group) | |
end | |
end | |
def create_district_nofification_groups | |
[@report_notifications_group_name, @upload_notifications_group_name].each do |group| | |
@manager.create_group(group_name: group) | |
end | |
end | |
def add_district_group_permissions | |
@manager.add_permission_to_group( | |
group_name: @uploader_group_name, | |
directory_name: @automated_upload_path, | |
permission_type: "full" | |
) | |
@manager.add_permission_to_group( | |
group_name: @uploader_group_name, | |
directory_name: @ad_hoc_upload_path, | |
permission_type: "full" | |
) | |
manager.add_permission_to_group( | |
group_name: @viewer_group_name, | |
directory_name: @report_upload_path, | |
permission_type: "readonly" | |
) | |
end | |
def add_internal_group_permissions | |
manager.add_permission_to_group( | |
group_name: INTERNAL_UPLOADER_GROUP, | |
directory_name: @report_upload_path, | |
permission_type: "full" | |
) | |
end | |
def add_pm_to_district_notification_groups | |
@manager.add_user_to_group(username: @options.pm_username, group_name: @upload_notifications_group_name) | |
@manager.add_user_to_group(username: @options.pm_username, group_name: @report_notifications_group_name) | |
end | |
def add_pds_to_district_notification_groups | |
@options.pd_usernames.each do |pd_username| | |
@manager.add_user_to_group(username: pd_username, group_name: @upload_notifications_group_name) | |
end | |
end | |
def add_analysts_to_district_notification_groups | |
@options.analyst_usernames.each do |analyst_username| | |
@manager.add_user_to_group(username: analyst_username, group_name: @upload_notifications_group_name) | |
end | |
end | |
def add_district_group_notifications | |
@manager.add_notification_to_directory( | |
directory_name: @automated_upload_path, | |
group_name: @upload_notifications_group_name | |
) | |
@manager.add_notification_to_directory( | |
directory_name: @ad_hoc_upload_path, | |
group_name: @upload_notifications_group_name | |
) | |
@manager.add_notification_to_directory( | |
directory_name: @report_upload_path, | |
group_name: @report_notifications_group_name | |
) | |
end | |
def create_district_user | |
@manager.create_user( | |
username: @options.username, | |
full_name: @options.full_name, | |
email: @options.email, | |
user_type: @options.user_type | |
) | |
end | |
def add_district_user_public_key | |
@manager.add_public_key(username: @options.username, public_key_path: @options.public_key_path) | |
end | |
def add_district_user_to_groups | |
case @options.user_type | |
when :bot | |
when :uploader | |
manager.add_user_to_group(username: @options.username, group_name: uploader_group_name) | |
when :viewer | |
manager.add_user_to_group(username: @options.username, group_name: viewer_group_name) | |
when :uploader_viewer | |
manager.add_user_to_group(username: @options.username, group_name: uploader_group_name) | |
manager.add_user_to_group(username: @options.username, group_name: viewer_group_name) | |
else | |
raise ArgumentError, "Invalid user type: #{@options.user_type}" | |
end | |
end | |
def run | |
create_district_directories | |
create_district_permission_groups | |
create_district_nofification_groups | |
add_district_group_permissions | |
add_internal_group_permissions | |
add_district_group_notifications | |
add_pm_to_district_notification_groups unless @options.pm_username.nil? | |
add_pds_to_district_notification_groups | |
add_analysts_to_district_notification_groups | |
create_district_user | |
add_district_user_public_key unless @options.public_key_path.nil? | |
add_district_user_to_groups | |
end | |
end | |
class FilesDotCom::CLI < FilesDotCom | |
REQUIRED_ARGS = %i(district full_name email user_type) | |
VALID_USER_TYPES = %i(uploader viewer uploader_viewer bot) | |
API_KEY_KEY = "FILESDOTCOM_API_KEY" | |
DEFAULT_VERBOSE = false | |
DEFAULT_DRY_RUN = true | |
DEFAULT_ANALYST_USERNAMES = %w(in-class-today_brandon in-class-today_jessica) | |
DEFAULT_PD_USERNAMES = %w(in-class-today_santi in-class-today_manny) | |
OPTION_DELIMITER = ',' | |
attr_accessor :options | |
def initialize | |
@options = parse_command_line_options | |
end | |
def run | |
FilesDotCom::Runner.new(@options).run | |
end | |
def parse_command_line_options | |
options = FilesDotCom::Options.new | |
options.verbose = DEFAULT_VERBOSE | |
options.dry_run = DEFAULT_DRY_RUN | |
options.analyst_usernames = DEFAULT_ANALYST_USERNAMES | |
options.pd_usernames = DEFAULT_PD_USERNAMES | |
OptionParser.new do |opts| | |
opts.banner = <<~HEREDOC | |
Automated account setup script for district users on files.com | |
Example usage: | |
./files-dot-com-account-setup.rb --district townerton-usa \\ | |
--email john.fakenamington@example.com \\ | |
--full-name "John Fakenamington" \\ | |
--user-type uploader \\ | |
--verbose \\ | |
--pm-username in-class-today_some.user \\ | |
--pd-usernames 'in-class-today_someone,in-class-today_someone.else' | |
--analyst-usernames '--pd-usernames 'in-class-today_analyst,in-class-today_other.analyst' | |
--public-key-path test_key.pub \\ | |
--dry-run | |
HEREDOC | |
opts.on("-d DISTRICT", "--district DISTRICT", "District (required)") do |d| | |
options.district = d | |
end | |
opts.on("-f FULL_NAME", "--full-name FULL_NAME", "Full Name (required)") do |f| | |
options.full_name = f | |
end | |
opts.on("-email EMAIL", "--email EMAIL", "Email (required)") do |e| | |
options.email = e | |
end | |
opts.on("-t USER_TYPE", "--user-type USER_TYPE", "User Type (required); must be one of uploader, viewer, uploader_viewer, bot") do |t| | |
options.user_type = t.to_sym | |
end | |
opts.on("-k PUBLIC_KEY_PATH", "--public-key-path PUBLIC_KEY_PATH", "Public Key Path (optional)") do |k| | |
options.public_key_path = k | |
end | |
opts.on("-p PM_USERNAME", "--pm-username PM_USERNAME", "Program Manager Username (for notifiations)") do |p| | |
options.pm_username = p | |
end | |
opts.on("-P PD_USERNAMES", "--pd-usernames PD_USERNAME", | |
"Comma-delimited list of program delivery usernames who should receive notifications (optional)") do |p| | |
options.pd_usernames = (p.split OPTION_DELIMITER).map(&:strip) | |
end | |
opts.on("-a ANALYST_USERNAMES", "--analyst-usernames ANALYST_USERNAMES", | |
"Comma-delimited list of analyst usernames who should receive notifications (optional); Defaults to Manny and Santi") do |a| | |
options.analyst_usernames = (a.split OPTION_DELIMITER).map(&:strip) | |
end | |
opts.on("-v", "--[no-]verbose", "Run verbosely. Defaults to false") do |v| | |
options.verbose = v | |
end | |
opts.on("-D", "--[no-]dry-run", "Dry run (log commands but do not actually run them). Defaults to true") do |d| | |
options.dry_run = d | |
end | |
opts.on("-h", "--help", "Prints this help") do | |
puts opts | |
exit | |
end | |
end.parse! | |
REQUIRED_ARGS.each do |arg| | |
raise ArgumentError, "Argument #{arg} is required but was not provided" if(options[arg].nil?) | |
end | |
unless VALID_USER_TYPES.include? options.user_type | |
raise ArgumentError, "User type #{options.user_type} is invalid. Must be one of #{VALID_USER_TYPES.to_s}" | |
end | |
options.username = "#{options.district}_#{options.email.gsub(/@.*$/, "").downcase}" | |
if !options.public_key_path.nil? && !File.exist?(options.public_key_path) | |
raise ArgumentError, "Public key file #{options.public_key_path} does not exist" | |
end | |
if ENV[API_KEY_KEY].nil? | |
p "Input your files.com API key" | |
options.api_key = STDIN.noecho(&:gets).chomp | |
else | |
options.api_key = ENV[API_KEY_KEY] | |
end | |
options | |
end | |
end | |
FilesDotCom::CLI.new.run |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment