1 | #!/usr/bin/env node
|
2 |
|
3 | 'use strict';
|
4 |
|
5 | // Ignore unused `heroku` var that's called in `eval` below
|
6 | // jshint -W098
|
7 |
|
8 | var assert = require('assert');
|
9 | var fs = require('fs');
|
10 | var Heroku = require('../lib/heroku');
|
11 | var heroku = new Heroku({});
|
12 | var inflection = require('inflection');
|
13 | var path = require('path');
|
14 | var resources = require('../lib/schema').definitions;
|
15 | var resource, resourceName;
|
16 |
|
17 | for (resourceName in resources) {
|
18 | resource = resources[resourceName];
|
19 | createDocumentation(resourceName, resource);
|
20 | }
|
21 |
|
22 | function createDocumentation(resourceName, resource) {
|
23 | var file = createFile(resourceName);
|
24 |
|
25 | process.stdout.write('\nBuilding docs for ' + resourceName + ':\n');
|
26 |
|
27 | fs.appendFileSync(file, '# ' + resourceName + '\n\n');
|
28 | if (resource.description) fs.appendFileSync(file, resource.description + '\n\n');
|
29 |
|
30 | addActions(file, resource.links);
|
31 | addAttributes(file, resource.attributes);
|
32 | }
|
33 |
|
34 | function getName(name) {
|
35 | name = name.toLowerCase();
|
36 | name = inflection.dasherize(name).replace(/-/g, '_');
|
37 | name = inflection.camelize(name, true);
|
38 | return name;
|
39 | }
|
40 |
|
41 | function createFile(resourceName) {
|
42 | var fileName = getName(resourceName);
|
43 | var file = path.join(__dirname, '../docs/' + fileName + '.md');
|
44 | fs.writeFileSync(file, '');
|
45 | return file;
|
46 | }
|
47 |
|
48 | function addActions(file, actions) {
|
49 | var action, actionName, i;
|
50 |
|
51 | process.stdout.write(' Building actions:');
|
52 | fs.appendFileSync(file, '## Actions\n\n');
|
53 |
|
54 | for (i in actions) {
|
55 | action = actions[i];
|
56 | actionName = action.title;
|
57 |
|
58 | process.stdout.write('\n ' + actionName);
|
59 | addAction(file, actionName, action);
|
60 | }
|
61 | }
|
62 |
|
63 | function addAction(file, actionName, action) {
|
64 | fs.appendFileSync(file, '### `' + getName(actionName) + '`\n\n');
|
65 | fs.appendFileSync(file, '`' + getFunctionCall(actionName, action) + '`\n\n');
|
66 | fs.appendFileSync(file, 'Method | ');
|
67 | fs.appendFileSync(file, 'Path\n');
|
68 | fs.appendFileSync(file, '--- | ');
|
69 | fs.appendFileSync(file, '---\n');
|
70 | fs.appendFileSync(file, action.method + ' | ');
|
71 | fs.appendFileSync(file, prettifyHref(action.href) + '\n\n');
|
72 | addActionAttributes(file, action);
|
73 |
|
74 | checkAction(actionName, action);
|
75 | }
|
76 |
|
77 | function getFunctionCall(actionName, action) {
|
78 | var path = action.href.split(/\//);
|
79 | var segments = path.slice(1, path.length);
|
80 | var functionCall = 'heroku';
|
81 | var i, segment;
|
82 |
|
83 | for (i = 0; i < segments.length; i ++) {
|
84 | segment = segments[i];
|
85 |
|
86 | if (segment.match(/{[^}]+}/)) {
|
87 | continue;
|
88 | } else {
|
89 | functionCall += '.' + getName(segment);
|
90 |
|
91 | if (segments[i + 1] && segments[i + 1].match(/{[^}]+}/)) {
|
92 | functionCall += '(' + prettifySegment(segments[i + 1]) + ')';
|
93 | } else {
|
94 | functionCall += '()';
|
95 | }
|
96 | }
|
97 | }
|
98 |
|
99 | functionCall += '.' + getName(actionName) + '(';
|
100 |
|
101 | if (['PATCH', 'POST', 'PUT'].indexOf(action.method) > -1) {
|
102 | functionCall += '{attributes}, ';
|
103 | }
|
104 |
|
105 | functionCall += '{callback});';
|
106 |
|
107 | return functionCall;
|
108 | }
|
109 |
|
110 | function prettifyHref(href) {
|
111 | var segments = href.split(/\//);
|
112 | segments = segments.slice(1, segments.length);
|
113 | segments = segments.map(prettifySegment);
|
114 | href = '/' + segments.join('/');
|
115 | return href;
|
116 | }
|
117 |
|
118 | function prettifySegment(segment) {
|
119 | var unescaped = unescape(segment);
|
120 | var identities;
|
121 |
|
122 | if (unescaped === segment) {
|
123 | return segment;
|
124 | }
|
125 |
|
126 | identities = getIdentitiesFromParam(unescaped);
|
127 | return '{' + identities[0] + '_' + identities.slice(1, identities.length).sort().join('_or_') + '}';
|
128 | }
|
129 |
|
130 | function getIdentitiesFromParam(param) {
|
131 | var identities = param.replace(/[{}()#]/g, '').split("/");
|
132 | var resourceName;
|
133 |
|
134 | identities = identities.slice(1, identities.length);
|
135 | identities = resources[identities[1]].definitions.identity;
|
136 | identities = identities.anyOf || [identities];
|
137 |
|
138 | identities = identities.map(function (identity) {
|
139 | var parts = identity.$ref.split('/');
|
140 | resourceName = parts[2];
|
141 | return parts.slice(-1)[0];
|
142 | });
|
143 |
|
144 | identities.unshift(resourceName);
|
145 |
|
146 | return identities;
|
147 | }
|
148 |
|
149 | function addActionAttributes(file, action) {
|
150 | if (!action.attributes) return;
|
151 |
|
152 | addActionAttributeGroup(file, action.attributes.optional, 'Optional');
|
153 | fs.appendFileSync(file, '\n');
|
154 |
|
155 | addActionAttributeGroup(file, action.attributes.required, 'Required');
|
156 | fs.appendFileSync(file, '\n');
|
157 | }
|
158 |
|
159 | function addActionAttributeGroup(file, attributes, type) {
|
160 | if (!attributes) return;
|
161 |
|
162 | fs.appendFileSync(file, '#### ' + type + ' Attributes\n\n');
|
163 |
|
164 | attributes.forEach(function (attribute) {
|
165 | fs.appendFileSync(file, '- ' + attribute + '\n');
|
166 | });
|
167 | }
|
168 |
|
169 | function addAttributes(file, attributes) {
|
170 | var attributeName, attribute;
|
171 |
|
172 | if (!attributes) return;
|
173 |
|
174 | process.stdout.write('\n Building attributes\n');
|
175 | fs.appendFileSync(file, '## Attributes\n\n');
|
176 |
|
177 | for (attributeName in attributes) {
|
178 | attribute = attributes[attributeName];
|
179 | addAttribute(file, attributeName, attribute);
|
180 | }
|
181 | }
|
182 |
|
183 | function addAttribute(file, attributeName, attribute) {
|
184 | fs.appendFileSync(file, '### `' + attributeName + '`\n\n');
|
185 |
|
186 | fs.appendFileSync(file, '*' + attribute.description + '*\n\n');
|
187 |
|
188 | fs.appendFileSync(file, 'Example | ');
|
189 | fs.appendFileSync(file, 'Serialized? | ');
|
190 | fs.appendFileSync(file, 'Type\n');
|
191 | fs.appendFileSync(file, '--- | ');
|
192 | fs.appendFileSync(file, '--- | ');
|
193 | fs.appendFileSync(file, '---\n');
|
194 | fs.appendFileSync(file, '`' + attribute.example + '` | ');
|
195 | fs.appendFileSync(file, attribute.serialized + ' | ');
|
196 | fs.appendFileSync(file, attribute.type + '\n\n');
|
197 | }
|
198 |
|
199 | function checkAction(actionName, action) {
|
200 | // Ignore eval for testing
|
201 | // jshint -W061
|
202 | var functionToTest = getFunctionCall(actionName, action).replace(/{.*}/g, '').slice(0, -3);
|
203 | assert.equal(eval('typeof ' + functionToTest), 'function', functionToTest + ' is not a Function');
|
204 | process.stdout.write(' ' + String.fromCharCode(0x2713));
|
205 | }
|