-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSamHelper.js
165 lines (130 loc) · 5.15 KB
/
SamHelper.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
const fs = require('fs-extra');
const Handlebars = require('handlebars');
const path = require('path');
Handlebars.registerHelper('functionNameList', (items) => {
return items.map((i) => ` - ${i.functionName}`).join('\r\n')
});
Handlebars.registerHelper('routeList', (functions) => {
return functions.map((i) => ` - ${i.functionName}`).join('\r\n')
});
Handlebars.registerHelper('safeVal', function (value, safeValue) {
const out = value || safeValue;
return new Handlebars.SafeString(out);
});
const DEFAULT_FUNCTION_DEF_FILENAME = 'function.json';
class SamHelper {
constructor(apiDefinitions, functionDefinitions, options = {}) {
this.apiDefinitions = apiDefinitions;
this.functionDefinitions = functionDefinitions;
this.options = options;
}
async loadTemplates(realtivePathToTemplates = 'templates') {
const templatePath = path.join(__dirname, realtivePathToTemplates);
const files = await fs.readdir(templatePath);
this.templates = await files.reduce(async (prev, curr) => {
const templateFilePath = path.join(templatePath, curr);
const source = await fs.readFile(templateFilePath, { encoding: 'utf8' });
const template = Handlebars.compile(source);
const prevValue = await prev;
prevValue[path.basename(curr.toLowerCase(), '.yaml')] = { templateFilePath, source, template };
return prevValue;
}, {});
return this.templates;
}
static async loadFunctionDefinitions(startingPath = __dirname) {
if (startingPath.indexOf('node_modules') >= 0) return;
const dirItems = await fs.readdir(startingPath, { withFileTypes: true });
const dirs = [];
const files = [];
for(let dir of dirItems) {
const dirPath = path.join(startingPath, dir.name ? dir.name : dir);
const stat = await fs.lstat(dirPath);
if (stat.isDirectory()) {
dirs.push(dirPath)
} else {
files.push(dirPath);
}
}
const functionDefinitions = [];
for(const dir of dirs) {
const funcDefs = await SamHelper.loadFunctionDefinitions(dir) || [];
for(const funcDef of funcDefs) {
functionDefinitions.push(funcDef);
}
}
for(const file of files) {
if (file.endsWith(DEFAULT_FUNCTION_DEF_FILENAME)) {
let functionDefinition = await fs.readJson(file);
functionDefinition.functionDefinitionFilePath = file;
functionDefinitions.push(functionDefinition);
}
}
return functionDefinitions;
}
/**
* Loads the api definitions. Can load from package.json under the "api" key or from a stand alone file.
* @param options - { usePackageJson: *true|false, apiDefinitionFile: 'path_to_json_file' }
* @returns {Promise<void>}
* @description - The apiDefinitionFile is of the format: [{ apiDef }, { apiDef }, ...]
*/
static async loadApiDefinitions(options = { usePackageJson: true, apiDefinitionFile: '' }) {
if (options.usePackageJson) {
const pkg = await fs.readJson('package.json');
if (pkg) {
return pkg.api;
}
}
const apiDefs = await fs.readJson(options.apiDefinitionFile);
return apiDefs;
}
/**
* Renders a complete SAM Template with all the API's, Routes, Functions based on the apis and functions.
* @param apis - Array of api definitions.
* @param functions - Array of function definitions.
* @returns {Promise<void>} - Writes a file.
*/
async render(apis = this.apiDefinitions, functions = this.functionDefinitions) {
const templates = await this.loadTemplates(this.options.templatePath);
const apiDescriptions = [];
for(let apiDescription of apis) {
// find each api, add related functions to api description.
apiDescription.functions = functions.filter((f) => f.apiId === apiDescription.apiId);
apiDescriptions.push(apiDescription);
}
const renderedSamTemplate = this.renderSamTemplate(apiDescriptions, functions, templates);
if (this.options.writeFile) {
await fs.writeFile(this.options.outputFilename, renderedSamTemplate);
}
return renderedSamTemplate;
}
renderSamTemplate(apis = this.apiDefinitions, functions = this.functionDefinitions, templates) {
const renderedApis = [];
for(let api of apis) {
const renderedApi = this.renderApi(api, templates);
renderedApis.push(renderedApi);
}
const renderedFunctions = this.renderFunctions(functions, templates);
const samTemplate = templates['sam-header'].template({renderedApis, renderedFunctions});
return samTemplate;
}
renderApi(apiDescription, templates) {
const routes = this.renderRoutes(apiDescription.functions, templates);
const apiDescriptionWithRoutes = { ...apiDescription, routes };
return templates['websocket-api'].template(apiDescriptionWithRoutes);
}
renderRoutes(functions, templates) {
const routes = functions.map((f) => {
const route = templates['websocket-route'].template(f);
return route;
});
return routes.join('\r\n');
}
renderFunctions(functions, templates) {
const fns = functions.map((f) => {
const fn = templates['function'].template(f);
return fn;
});
return fns.join('\r\n');
}
}
module.exports = SamHelper;