Cleaned up the generation script, fixing the doc-blocks above the client actions.
Replaced the transport, giving it all of the functionality that was brought over to the client and making the client simply a place for the API to live. Essentially a shell that can easily be removed. spec'd out the TransportRequest which will eventually inherit from one of server possible promise implementations and will be plugable. It will also implement the "abort" functionality needed in an environment like node.js
This commit is contained in:
123
scripts/generate/js_api/actions.js
Normal file
123
scripts/generate/js_api/actions.js
Normal file
@ -0,0 +1,123 @@
|
||||
var _ = require('../../../src/lib/utils');
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var aliases = require('./aliases');
|
||||
|
||||
var castExistsRE = /exists/;
|
||||
var usesBulkBodyRE = /^(bulk|msearch)$/;
|
||||
var urlParamRE = /\{(\w+)\}/g;
|
||||
|
||||
var specCount = 0;
|
||||
var actions = [];
|
||||
var doneParsing = false;
|
||||
|
||||
require('../../get_spec')
|
||||
.get('api/*.json')
|
||||
.on('entry', transformFile)
|
||||
.on('end', function () {
|
||||
doneParsing = true;
|
||||
if (actions.length === specCount) {
|
||||
module.exports.emit('ready', actions);
|
||||
}
|
||||
});
|
||||
|
||||
function transformFile(entry) {
|
||||
specCount++;
|
||||
|
||||
// itterate all of the specs within the file, should only be one
|
||||
_.each(JSON.parse(entry.data), function (def, name) {
|
||||
var steps = name.split('.');
|
||||
var allParams = _.extend({}, def.url.params, def.url.parts);
|
||||
var spec = {
|
||||
name: name,
|
||||
methods: _.map(def.methods, function (m) { return m.toUpperCase(); }),
|
||||
params: def.url.params,
|
||||
body: def.body || null,
|
||||
path2lib: _.repeat('../', steps.length + 1) + 'lib/'
|
||||
};
|
||||
|
||||
if (def.body && def.body.requires) {
|
||||
spec.needBody = true;
|
||||
}
|
||||
|
||||
if (usesBulkBodyRE.test(name)) {
|
||||
spec.bulkBody = true;
|
||||
}
|
||||
|
||||
if (castExistsRE.test(name)) {
|
||||
spec.castExists = true;
|
||||
}
|
||||
|
||||
var urls = _.difference(def.url.paths, aliases[name]);
|
||||
urls = _.map(urls, function (url, i) {
|
||||
var optionalVars = {};
|
||||
var requiredVars = {};
|
||||
var param;
|
||||
var target;
|
||||
var match;
|
||||
|
||||
if (url.charAt(0) !== '/') {
|
||||
url = '/' + url;
|
||||
}
|
||||
|
||||
while (match = urlParamRE.exec(url)) {
|
||||
param = def.url.parts[match[1]] || {};
|
||||
target = (param.required || !param.default) ? requiredVars : optionalVars;
|
||||
target[match[1]] = _.omit(param, 'required');
|
||||
}
|
||||
|
||||
[requiredVars, optionalVars].forEach(function (vars) {
|
||||
_.each(vars, function (v, name) {
|
||||
vars[name] = _.omit(v, 'description');
|
||||
});
|
||||
});
|
||||
|
||||
return _.omit({
|
||||
fmt: url.replace(urlParamRE, '<%=$1%>'),
|
||||
opt: _.size(optionalVars) ? optionalVars : null,
|
||||
req: _.size(requiredVars) ? requiredVars : null,
|
||||
sortOrder: _.size(requiredVars) * -1
|
||||
}, function (v) {
|
||||
return !v;
|
||||
});
|
||||
});
|
||||
|
||||
spec.urls = _.map(_.sortBy(urls, 'sortOrder'), function (url) {
|
||||
return _.omit(url, 'sortOrder');
|
||||
});
|
||||
|
||||
spec.params = _.transform(spec.params, function (note, param, name) {
|
||||
param.name = name;
|
||||
note[name] = _.pick(param, [
|
||||
'type', 'default', 'options', 'required'
|
||||
]);
|
||||
}, {});
|
||||
|
||||
// escape method names with "special" keywords
|
||||
var location = _.map(spec.name.split('.'), _.camelCase)
|
||||
.join('.prototype.')
|
||||
.replace(/(^|\.)(delete|default)(\.|$)/g, '[\'$2\']');
|
||||
|
||||
var action = {
|
||||
spec: _.pick(spec, [
|
||||
'methods',
|
||||
'params',
|
||||
'urls',
|
||||
'needBody',
|
||||
'bulkBody',
|
||||
'castExists',
|
||||
'castNotFound'
|
||||
]),
|
||||
location: location,
|
||||
docUrl: def.documentation,
|
||||
name: spec.name,
|
||||
allParams: allParams
|
||||
};
|
||||
|
||||
if (actions.push(action) === specCount && doneParsing) {
|
||||
module.exports.emit('ready', action);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = new EventEmitter();
|
||||
@ -9,83 +9,18 @@ var urlParamRE = /\{(\w+)\}/g;
|
||||
|
||||
var outputPath = _.joinPath(__dirname, '../../../src/lib/api.js');
|
||||
|
||||
require('./spec').on('ready', function (specs) {
|
||||
require('./actions').on('ready', function (actions) {
|
||||
var defs = [];
|
||||
var namespaces = [];
|
||||
|
||||
var namespaces = _.filter(_.map(actions, function (action) {
|
||||
if (~action.location.indexOf('.')) {
|
||||
var path = action.location.split('.').slice(0, -1);
|
||||
_.pull(path, 'prototype');
|
||||
return path.join('.');
|
||||
}
|
||||
}));
|
||||
|
||||
clean(outputPath);
|
||||
var actions = _.map(specs, function (spec) {
|
||||
spec.urls = _.map(
|
||||
_.sortBy(
|
||||
_.transform(spec.urls, function (note, url, i) {
|
||||
var optionalVars = {};
|
||||
var requiredVars = {};
|
||||
var param;
|
||||
var target;
|
||||
var match;
|
||||
|
||||
if (url.charAt(0) !== '/') {
|
||||
url = '/' + url;
|
||||
}
|
||||
|
||||
while (match = urlParamRE.exec(url)) {
|
||||
param = spec.urlParts[match[1]] || {};
|
||||
target = (param.required || !param.default) ? requiredVars : optionalVars;
|
||||
target[match[1]] = _.omit(param, 'required');
|
||||
}
|
||||
|
||||
[requiredVars, optionalVars].forEach(function (vars) {
|
||||
_.each(vars, function (v, name) {
|
||||
vars[name] = _.omit(v, 'description');
|
||||
});
|
||||
});
|
||||
|
||||
note.push(_.omit({
|
||||
fmt: url.replace(urlParamRE, '<%=$1%>'),
|
||||
opt: _.size(optionalVars) ? optionalVars : null,
|
||||
req: _.size(requiredVars) ? requiredVars : null,
|
||||
sortOrder: _.size(requiredVars) * -1
|
||||
}, function (v) { return !v; }));
|
||||
}, [])
|
||||
, 'sortOrder')
|
||||
, function (url) {
|
||||
return _.omit(url, 'sortOrder');
|
||||
});
|
||||
|
||||
var docUrl = spec.docUrl;
|
||||
var location = _.map(spec.name.split('.'), _.camelCase).join('.');
|
||||
|
||||
spec = _.pick(spec, [
|
||||
'methods',
|
||||
'params',
|
||||
'urls',
|
||||
'needBody',
|
||||
'bulkBody',
|
||||
'castNotFound'
|
||||
]);
|
||||
|
||||
spec.params = _.transform(spec.params, function (note, param, name) {
|
||||
param.name = name;
|
||||
note[name] = _.pick(param, [
|
||||
'type', 'default', 'options', 'required'
|
||||
]);
|
||||
}, {});
|
||||
|
||||
if (~location.indexOf('.')) {
|
||||
var steps = location.split('.');
|
||||
namespaces.push(steps.slice(0, -1).join('.'));
|
||||
location = steps.join('.prototype.');
|
||||
}
|
||||
|
||||
// escape method names with "special" keywords
|
||||
location = location.replace(/(^|\.)(delete|default)(\.|$)/g, '[\'$2\']');
|
||||
return {
|
||||
spec: spec,
|
||||
location: location,
|
||||
docUrl: docUrl
|
||||
};
|
||||
});
|
||||
|
||||
console.log('writing', actions.length, 'api actions to', outputPath);
|
||||
fs.writeFileSync(outputPath, templates.apiFile({
|
||||
actions: actions,
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
var _ = require('../../../src/lib/utils');
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var aliases = require('./aliases');
|
||||
|
||||
var castNotFoundRE = /exists/;
|
||||
var usesBulkBodyRE = /^(bulk|msearch)$/;
|
||||
|
||||
var specCount = 0;
|
||||
var completedSpecs = [];
|
||||
var doneParsing = false;
|
||||
|
||||
require('../../get_spec')
|
||||
.get('api/*.json')
|
||||
.on('entry', transformFile)
|
||||
.on('end', function () {
|
||||
doneParsing = true;
|
||||
if (completedSpecs.length === specCount) {
|
||||
module.exports.emit('ready', completedSpecs);
|
||||
}
|
||||
});
|
||||
|
||||
function transformFile(entry) {
|
||||
specCount++;
|
||||
|
||||
var file = entry.data;
|
||||
// itterate all of the specs within the file, should only be one
|
||||
_.each(JSON.parse(file), function (def, name) {
|
||||
var steps = name.split('.');
|
||||
var spec = {
|
||||
name: name,
|
||||
methods: _.map(def.methods, function (m) { return m.toUpperCase(); }),
|
||||
docUrl: def.documentation,
|
||||
urlParts: def.url.parts,
|
||||
params: def.url.params,
|
||||
urls: _.difference(def.url.paths, aliases[name]),
|
||||
body: def.body || null,
|
||||
path2lib: _.repeat('../', steps.length + 1) + 'lib/'
|
||||
};
|
||||
|
||||
if (def.body && def.body.requires) {
|
||||
spec.needBody = true;
|
||||
}
|
||||
|
||||
if (usesBulkBodyRE.test(name)) {
|
||||
spec.bulkBody = true;
|
||||
}
|
||||
|
||||
if (castNotFoundRE.test(name)) {
|
||||
spec.castNotFound = true;
|
||||
}
|
||||
if (completedSpecs.push(spec) === specCount && doneParsing) {
|
||||
module.exports.emit('ready', completedSpecs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = new EventEmitter();
|
||||
@ -1,4 +1,4 @@
|
||||
var ca = require('./client_action').create;
|
||||
var ca = require('./client_action');
|
||||
var errors = require('./errors');
|
||||
|
||||
var api = module.exports = {};
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Perform a [<%= spec.name %>](<%= docUrl %>) request
|
||||
* Perform a [<%= name %>](<%= docUrl %>) request
|
||||
*
|
||||
* @param {Object} params - An object with parameters used to carry out this action<%
|
||||
_.each(spec.params, function(param, paramName) { %>
|
||||
_.each(allParams, function(param, paramName) { %>
|
||||
* @param {<%= paramType(param.type) %>} <%= paramWithDefault('params.' + paramName, param.default) %><%
|
||||
if (param.description) {
|
||||
%> - <%= param.description %><%
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
|
||||
var _ = require('../../../../src/lib/utils')
|
||||
, fs = require('fs')
|
||||
, path = require('path')
|
||||
, urlParamRE = /\{(\w+)\}/g;
|
||||
var _ = require('../../../../src/lib/utils');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
|
||||
/**
|
||||
@ -16,7 +15,7 @@ function lines(i) {
|
||||
if (line === '') {
|
||||
// no indent on empty lines
|
||||
l.lines.push('');
|
||||
} else if (typeof line !== 'undefined') {
|
||||
} else if (line === void 0) {
|
||||
l.lines.push(_.repeat(' ', l.indent) + line);
|
||||
}
|
||||
return l;
|
||||
@ -81,152 +80,6 @@ var templates = {};
|
||||
*/
|
||||
var templateGlobals = {
|
||||
|
||||
writeParams: function (indent, params, namespace) {
|
||||
var l = lines(indent);
|
||||
|
||||
_.each(params, function (param, name) {
|
||||
if (!param.required) {
|
||||
l('if (typeof params.' + name + ' !== \'undefined\') {').in();
|
||||
}
|
||||
|
||||
l.split(templates[param.type || 'any']({
|
||||
get: 'params.' + name,
|
||||
set: namespace + name,
|
||||
name: name
|
||||
}));
|
||||
|
||||
if (!param.required) {
|
||||
l.out();
|
||||
l('}');
|
||||
}
|
||||
l('');
|
||||
});
|
||||
|
||||
return l.toString();
|
||||
},
|
||||
|
||||
writeBrowserParams: function (indent, params, namespace) {
|
||||
var l = lines(indent);
|
||||
|
||||
_.each(params, function (param, name) {
|
||||
if (!param.required) {
|
||||
l('if (_.has(params, ' + stringify(name) + ')) {').in();
|
||||
}
|
||||
switch (param.type) {
|
||||
case 'enum':
|
||||
l(
|
||||
namespace + name + ' = _.' +
|
||||
(param.type || 'any') + 'Param(params.' + name + ', ' + stringify(param.options) +
|
||||
');'
|
||||
);
|
||||
break;
|
||||
default:
|
||||
l(namespace + name + ' = _.' + (param.type || 'any') + 'Param(params.' + name + ');');
|
||||
break;
|
||||
}
|
||||
if (!param.required) {
|
||||
l.out('}');
|
||||
}
|
||||
l('');
|
||||
});
|
||||
|
||||
return l.toString();
|
||||
},
|
||||
|
||||
writeUrls: function (indent, urls, urlParams, queryStringParams) {
|
||||
var l = lines(indent);
|
||||
|
||||
function urlVarIsRequired(varDetails) {
|
||||
varDetails = typeof varDetails === 'string' ? urlParams[varDetails] : varDetails;
|
||||
return varDetails && (varDetails.required || !varDetails.default);
|
||||
}
|
||||
|
||||
// turn a url string into an object describing the url, then sort them in decending order by how many args they have
|
||||
urls = _.sortBy(urls, function (url) {
|
||||
var requiredVars = _.filter(_.collectMatches(url, urlParamRE), function (match) {
|
||||
return urlVarIsRequired(urlParams[match[1]]);
|
||||
});
|
||||
return requiredVars ? requiredVars.length * -1 : 0;
|
||||
});
|
||||
|
||||
_.each(urls, function (url, urlIndex) {
|
||||
// collect the vars from the url and replace them to form the js that will build the url
|
||||
var makeL = lines(), vars = [];
|
||||
|
||||
makeL('request.path = \'' + url.replace(urlParamRE, function (match, varName) {
|
||||
var varDetails = urlParams[varName];
|
||||
varDetails.name = varName;
|
||||
vars.push(varDetails);
|
||||
if (urlVarIsRequired(varDetails)) {
|
||||
return '\' + encodeURIComponent(parts.' + varName + ') + \'';
|
||||
} else {
|
||||
return '\' + encodeURIComponent(parts.' + varName + ' || ' + stringify(varDetails.default) + ') + \'';
|
||||
}
|
||||
}) + '\';');
|
||||
|
||||
makeL(_.filter(_.map(vars, function (v, i) {
|
||||
if (_.has(queryStringParams, v.name)) {
|
||||
// delete the param so that it's not used later on in the queryString
|
||||
return 'delete params.' + v.name + ';';
|
||||
}
|
||||
})).join(' '));
|
||||
|
||||
if (vars.length || urlIndex) {
|
||||
var requiredVars = _.filter(vars, urlVarIsRequired);
|
||||
|
||||
var condition = _.map(requiredVars, function (v) {
|
||||
return 'parts.' + v.name + ')';
|
||||
}).join(' && ');
|
||||
|
||||
l((urlIndex > 0 ? 'else ' : '') + (condition ? 'if (' + condition + ') ' : '') + '{')
|
||||
.in()
|
||||
.split(makeL.toString())
|
||||
.out('}');
|
||||
|
||||
if (urlIndex === urls.length - 1 && condition) {
|
||||
l('else {')
|
||||
.in('throw new TypeError(\'Unable to build a path with those params. Supply at least ' +
|
||||
vars.join(', ') + '\');'
|
||||
)
|
||||
.out('}');
|
||||
}
|
||||
|
||||
} else {
|
||||
l.split(makeL.toString());
|
||||
}
|
||||
});
|
||||
l('');
|
||||
|
||||
return l.toString();
|
||||
},
|
||||
|
||||
writeRequestObjectBody: function (indent, name, body, methods) {
|
||||
var parts = [], l = lines(indent);
|
||||
if (~name.indexOf('exists')) {
|
||||
parts.push('ignore: _.union([404], params.ignore)');
|
||||
} else {
|
||||
parts.push('ignore: params.ignore');
|
||||
}
|
||||
|
||||
if (body) {
|
||||
if (_.contains(['bulk', 'msearch'], name)) {
|
||||
parts.push('body: this.client.config.serializer.bulkBody(params.body || null)');
|
||||
} else {
|
||||
parts.push('body: params.body || null');
|
||||
}
|
||||
}
|
||||
|
||||
if (methods.length === 1) {
|
||||
parts.push('method: ' + stringify(methods[0]));
|
||||
}
|
||||
|
||||
_.each(parts, function (part, i) {
|
||||
l(part + (i < parts.length - 1 ? ',' : ''));
|
||||
});
|
||||
|
||||
return l.toString();
|
||||
},
|
||||
|
||||
stringify: stringify,
|
||||
|
||||
_: _,
|
||||
@ -254,22 +107,6 @@ var templateGlobals = {
|
||||
}
|
||||
},
|
||||
|
||||
returnStatement: function (indent, name) {
|
||||
var l = lines(indent);
|
||||
if (name.match(/(^|\.)exists/)) {
|
||||
l('this.client.request(request, function (err, response) {')
|
||||
.in('if (err instanceof errors.NotFound) {')
|
||||
.in('cb(err, false);')
|
||||
.out('} else {')
|
||||
.in('cb(err, true);')
|
||||
.out('}')
|
||||
.out('});');
|
||||
} else {
|
||||
l('this.client.request(request, cb);');
|
||||
}
|
||||
return l.toString();
|
||||
},
|
||||
|
||||
partials: templates
|
||||
};
|
||||
|
||||
@ -290,6 +127,5 @@ fs.readdirSync(path.resolve(__dirname)).forEach(function (filename) {
|
||||
templates.text = templates.string;
|
||||
|
||||
module.exports = {
|
||||
apiFile: templates.api_file,
|
||||
urlParamRE: urlParamRE
|
||||
apiFile: templates.api_file
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user