import request from 'request'; const showLogMessages = process.env.NODE_DEBUG ? process.env.NODE_DEBUG.indexOf('apidoc') !== -1 : false; const enums = [{"name":"default_emr_job_flow_role","plural":"default_emr_job_flow_roles","values":[{"name":"default_emr_job_flow_role","attributes":[],"value":"EMR_EC2_DefaultRole"}],"attributes":[],"description":"The default EMR EC2 Instance role"},{"name":"default_emr_service_role","plural":"default_emr_service_roles","values":[{"name":"default_emr_service_role","attributes":[],"value":"EMR_DefaultRole"}],"attributes":[],"description":"The default EMR Service role"},{"name":"emr_application","plural":"emr_applications","values":[{"name":"Hadoop","attributes":[]},{"name":"Hive","attributes":[]},{"name":"Mahout","attributes":[]},{"name":"Pig","attributes":[]},{"name":"Spark","attributes":[]}],"attributes":[],"description":"List of applications to install in the EMR cluster"},{"name":"emr_release_label","plural":"emr_release_labels","values":[{"name":"emr-5.14.0","attributes":[]},{"name":"emr-5.13.0","attributes":[]},{"name":"emr-5.12.1","attributes":[]},{"name":"emr-5.12.0","attributes":[]},{"name":"emr-5.11.1","attributes":[]},{"name":"emr-5.11.0","attributes":[]},{"name":"emr-5.10.0","attributes":[]},{"name":"emr-5.9.0","attributes":[]},{"name":"emr-4.9.2","attributes":[]},{"name":"emr-4.9.1","attributes":[]}],"attributes":[],"description":"Version (aka label in AWS-EMR) of the EMR stack to create."},{"name":"notification_options","plural":"notification_optionses","values":[{"name":"always","attributes":[],"description":"Always notify when a process completes"},{"name":"on_failure","attributes":[],"description":"Notify when a process fails"},{"name":"on_state_change","attributes":[],"description":"Notify when a process goes from succeeding to failing and vica versa"},{"name":"on_state_change_and_failures","attributes":[],"description":"Notify when going from failing to succeeded and on each failure"},{"name":"never","attributes":[],"description":"Never notify"}],"attributes":[]},{"name":"on_demand","plural":"on_demands","values":[{"name":"on_demand","attributes":[]}],"attributes":[],"description":"The onDemand type of Market Type"},{"name":"process_overlap_action","plural":"process_overlap_actions","values":[{"name":"wait","attributes":[],"description":"The process should wait until the currently running instance finishes"},{"name":"terminate","attributes":[],"description":"The currently running process should be killed"}],"attributes":[]},{"name":"process_status","plural":"process_statuses","values":[{"name":"running","attributes":[],"description":"The process has tasks currently executing"},{"name":"succeeded","attributes":[],"description":"All of the process's tasks succeeded on its last run"},{"name":"failed","attributes":[],"description":"At least one of the process's tasks failed on its last run"}],"attributes":[]},{"name":"task_status","plural":"task_statuses","values":[{"name":"submitted","attributes":[],"description":"The task has been submitted"},{"name":"runnable","attributes":[]},{"name":"starting","attributes":[]},{"name":"pending","attributes":[],"description":"The task is waiting on compute resources"},{"name":"running","attributes":[],"description":"The task is currently executing or awaiting backoff"},{"name":"failed","attributes":[],"description":"The task has irrevocably failed"},{"name":"succeeded","attributes":[],"description":"The task has succeeded without serious errors"}],"attributes":[]}]; const unions = [{"name":"aws_market","plural":"aws_markets","types":[{"type":"on_demand","attributes":[],"discriminator_value":"on_demand"},{"type":"spot","attributes":[],"discriminator_value":"spot"}],"attributes":[],"interfaces":[],"description":"The EC2 market of the EC2 instances"},{"name":"emr_cluster","plural":"emr_clusters","types":[{"type":"new_emr_cluster","attributes":[],"discriminator_value":"new_emr_cluster"},{"type":"existing_emr_cluster","attributes":[],"discriminator_value":"existing_emr_cluster"}],"attributes":[],"interfaces":[],"description":"Whether or not to create a new EMR cluster or re-use a pre-existing one"},{"name":"emr_job_flow_role","plural":"emr_job_flow_roles","types":[{"type":"default_emr_job_flow_role","attributes":[],"discriminator_value":"default_emr_job_flow_role"},{"type":"custom_emr_job_flow_role","attributes":[],"discriminator_value":"custom_emr_job_flow_role"}],"attributes":[],"interfaces":[],"description":"The EMR role to use on the EC2 instances"},{"name":"emr_service_role","plural":"emr_service_roles","types":[{"type":"default_emr_service_role","attributes":[],"discriminator_value":"default_emr_service_role"},{"type":"custom_emr_service_role","attributes":[],"discriminator_value":"custom_emr_service_role"}],"attributes":[],"interfaces":[],"description":"The EMR service role to use"},{"name":"notification","plural":"notifications","types":[{"type":"email","attributes":[],"discriminator_value":"email"},{"type":"pagerduty","attributes":[],"discriminator_value":"pagerduty"}],"attributes":[],"interfaces":[]},{"name":"process_schedule","plural":"process_schedules","types":[{"type":"cron_schedule","attributes":[],"discriminator_value":"cron_schedule"},{"type":"continuous_schedule","attributes":[],"discriminator_value":"continuous_schedule"}],"attributes":[],"interfaces":[],"description":"A specification for when a process should be run"},{"name":"task_executable","plural":"task_executables","types":[{"type":"docker_image_command","attributes":[],"description":"Docker image to run on ECS with Sundial companion container","deprecation":{"description":"Running jobs on AWS ECS is now deprecated. Use AWS Batch via batch_image_command instead."},"discriminator_value":"docker_image_command"},{"type":"shell_script_command","attributes":[],"description":"Shell command to run on Sundial service instance (Experimental)","discriminator_value":"shell_script_command"},{"type":"batch_image_command","attributes":[],"description":"Docker image to run on AWS Batch","discriminator_value":"batch_image_command"},{"type":"emr_command","attributes":[],"description":"Command to submit Spark Jobs on AWS EMR","discriminator_value":"emr_command"}],"attributes":[],"interfaces":[]}]; export function getRequestOptions (settings, userOptions) { return new Promise ((resolve, reject) => { if (!isObject(userOptions || {})) { reject(new Error('options parameter is required and must be a plain object')); return; } // Don't mutate the options the user provided var options = Object.assign({}, userOptions); // Handle the body of the request (options.data as provided by the user) if (settings.requiresBody) { if (!options.data) { reject(new Error('options.data property is required.')); return; } if (settings.discriminator) { if(dataIsAndCanBePrimitive(settings.bodyType, options.data)) { const dataUpdate = { [settings.discriminator]: getApidocTypeForPrimitive(settings.bodyType, options.data), value: options.data }; options.data = Object.assign({}, dataUpdate); } if (!options.data[settings.discriminator]) { reject(new Error(`options.data.${settings.discriminator} must be defined. Available options for [${settings.bodyType}] are: ${getTypesForUnion(settings.bodyType).join(', ')}`)); return; } } } var qs = {}; settings.queryParams.forEach((param) => { if (options[param.nameCamelCase]) { qs[param.name] = options[param.nameCamelCase]; } }); const json = settings.isApplicationJson ? options.data : undefined; const form = settings.isFormUrlEncoded ? options.data : undefined; const method = settings.method; resolve(Object.assign(options, { json, form, method, qs })); }); } export function dataIsAndCanBePrimitive (bodyType, data) { const typesForUnion = getTypesForUnion(bodyType); const dataType = typeof data; if (dataType === 'string') { return typesForUnion.indexOf('string') != -1; } else if (dataType === 'number') { return typesForUnion.indexOf('decimal') != -1 || typesForUnion.indexOf('double') != -1 || typesForUnion.indexOf('integer') != -1 || typesForUnion.indexOf('long') != -1; } else { return false; } } // Within the context of a union type export function getApidocTypeForPrimitive (bodyType, data) { const typesForUnion = getTypesForUnion(bodyType); const dataType = typeof data; if (dataType === 'string') { // Check if data matches an enum. Use the enum name if it does. const enumForData = enums.find((e) => { return !!e.values.find((v) => { return v.name === data; }); }); if (enumForData) { return enumForData.name; } // default to 'string' return dataType; } else if (dataType === 'number') { const containsDecimalPoint = data.toString().indexOf('.') !== -1; const possibleApidocTypes = containsDecimalPoint ? ['decimal', 'double'] : ['integer', 'long']; // Return the first one found. Buggy in that this code should know the // difference between each of the apidoc types. They are all 'number' in // JavaScript though. return typesForUnion.find((t) => { return possibleApidocTypes.indexOf(t) !== -1; }); } } export function getTypesForUnion (unionType) { const ut = unions.find((u) => u.name === unionType); if (ut) { return ut.types.map((t) => t.type); } return []; } /** * Simple plain object check. This is actually really involved to be 100% * accurate */ export function isObject (obj) { return typeof obj === 'object' && !Array.isArray(obj); } function log (message) { if (showLogMessages) { console.log('[apidoc] ' + message); } } export function getQueryString (options) { const qs = options.qs || {}; const params = Object.keys(qs).reduce((kvPairs, key) => { return kvPairs.concat(`${key}=${qs[key]}`); }, []); return params.join('&'); } export function doRequest (options) { var startMs = Date.now(); options = options || {}; options.qsStringifyOptions = { arrayFormat: 'repeat' }; var queryString = getQueryString(options); var url = options.uri + (queryString ? '?' + queryString : ''); log(options.method + ' ' + url); if (options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH') { log('|--> With body:\n\t\t' + JSON.stringify(options.json)); } return new Promise(function (resolve, reject) { request(options, function (err, response) { if (err) { reject(err); return; } var duration = Date.now() - startMs; var requestHref = response.request ? response.request.href : ''; log('Completed ' + options.method + ' ' + requestHref + ' ' + response.statusCode + ' ' + duration + 'ms'); resolve(response); }); }); }