1 | const jsYml = require('js-yaml');
|
2 | const fs = require('fs');
|
3 | const OptionsResolver = require('./options-resolver');
|
4 | const AddCorsMethods = require('./pipelines/add-cors-methods')
|
5 | const AddCorsParams = require('./pipelines/add-cors-params')
|
6 | const AddCorsHeader = require('./pipelines/add-cors-header')
|
7 | const WriteOutputFile = require('./pipelines/write-file-output')
|
8 | const AddIntegrations = require('./pipelines/add-integration')
|
9 | const AddCorsIntegration = require('./pipelines/add-cors-integrations')
|
10 | const AddCorsResponseParameters = require('./pipelines/add-cors-response-parameters')
|
11 | const AddAutoMockIntegration = require('./pipelines/add-automock-integrations')
|
12 | const ConfigureAutowire = require('./pipelines/create-proxy')
|
13 | const AddValidation = require('./pipelines/add-validation')
|
14 | const CreateIntegrationFile = require('./pipelines/create-integration-file')
|
15 | const Pipeline = require('./pipeline')
|
16 |
|
17 | function toArray(x) {
|
18 | return Array.isArray(x) || x == null ? x : [x];
|
19 | }
|
20 |
|
21 | class MergeIntegrationPlugin {
|
22 | constructor(serverless, options) {
|
23 | this.serverless = serverless;
|
24 | this.options = options;
|
25 | this.commands = {
|
26 | integration: {
|
27 | usage: 'AWS Gateway Integration management for Serverless',
|
28 | lifecycleEvents: ["help"],
|
29 | commands: {
|
30 | merge: {
|
31 | usage: 'Merges swagger api definition with integration in yml files',
|
32 | lifecycleEvents: [
|
33 | 'process'
|
34 | ]
|
35 | },
|
36 | create: {
|
37 | usage: 'Creates the required x-amazon-apigateway-integration blocks',
|
38 | lifecycleEvents: [
|
39 | 'files'
|
40 | ],
|
41 | options: {
|
42 | output: {
|
43 | usage:
|
44 | 'Specify the output directory '
|
45 | + '(e.g. "--output \'integration\'" or "-o \'integration\'")',
|
46 | required: true,
|
47 | shortcut: 'o',
|
48 | type: 'string'
|
49 | },
|
50 | type: {
|
51 | usage:
|
52 | 'Specify the type of integration '
|
53 | + '(e.g. "--type \'http_proxy\'" or "-t \'http_proxy\'")',
|
54 | required: true,
|
55 | shortcut: 't',
|
56 | type: 'string'
|
57 | }
|
58 | },
|
59 | }
|
60 | },
|
61 | }
|
62 | };
|
63 |
|
64 | this.hooks = {
|
65 | 'integration:help': this.generateHelp.bind(this),
|
66 | 'integration:merge:process': this.processMergeCommand.bind(this),
|
67 | 'integration:create:files': this.createFilesCommand.bind(this),
|
68 | 'before:aws:package:finalize:mergeCustomProviderResources': this.processPackage.bind(this)
|
69 | };
|
70 |
|
71 |
|
72 | if (this.serverless.configSchemaHandler) {
|
73 | const proxyNamePattern = '^[a-zA-Z0-9-_]+$'
|
74 | const openApiIntegrationSchema = {
|
75 | type: 'object',
|
76 | properties: {
|
77 | package: {type: 'string'},
|
78 | inputFile: {type: 'string'},
|
79 | inputDirectory: {type: 'string'},
|
80 | mapping: {type: 'array'},
|
81 | outputFile: {type: 'string'},
|
82 | outputDirectory: {type: 'string'},
|
83 | cors: {type: 'string'},
|
84 | autoMock: {type: 'string'},
|
85 | validation: {type: 'string'},
|
86 | proxyManager: {
|
87 | type: 'object',
|
88 | properties: {
|
89 | [proxyNamePattern] : {
|
90 | type: 'object',
|
91 | properties: {
|
92 | 'baseUrl': {type: 'string'},
|
93 | 'ignore': {type: 'string'},
|
94 | 'type': {type: 'string'},
|
95 | }
|
96 | }
|
97 | }
|
98 | },
|
99 | apiResourceName: {type: 'string'},
|
100 | },
|
101 | required: ['inputFile'],
|
102 | };
|
103 | this.serverless.configSchemaHandler.defineTopLevelProperty(
|
104 | 'openApiIntegration',
|
105 | {
|
106 | oneOf: [
|
107 | openApiIntegrationSchema,
|
108 | {
|
109 | type: 'array',
|
110 | items: openApiIntegrationSchema
|
111 | }
|
112 | ]
|
113 | },
|
114 | );
|
115 | }
|
116 | }
|
117 |
|
118 | generateHelp(command = "integration") {
|
119 | this.serverless.cli.generateCommandsHelp([command]);
|
120 | }
|
121 |
|
122 | resolve() {
|
123 | let configurations = toArray(this.serverless.configurationInput.openApiIntegration);
|
124 |
|
125 | if (!configurations || !configurations.length) {
|
126 | this.serverless.cli.log('Openapi Integration: missing configuration');
|
127 | }
|
128 |
|
129 | return configurations.map(configuration => {
|
130 | if (Object.keys(configuration).length === 0) {
|
131 | this.serverless.cli.log('Openapi Integration: missing configuration');
|
132 | }
|
133 |
|
134 | let optionsResolver = new OptionsResolver(configuration);
|
135 | let configuredOptions = optionsResolver.resolve(this.options.stage);
|
136 |
|
137 | if (this.options.output) {
|
138 | configuredOptions.outputDirectory = this.options.output;
|
139 | }
|
140 |
|
141 | if (this.options.type) {
|
142 | configuredOptions.type = this.options.type;
|
143 | }
|
144 | return configuredOptions;
|
145 | });
|
146 | }
|
147 |
|
148 | process(options) {
|
149 | let content = jsYml.load(fs.readFileSync(options.inputFullPath, 'utf8'))
|
150 | const pipelineRunner = new Pipeline(options, content, this.serverless);
|
151 | pipelineRunner
|
152 | .step(new AddIntegrations())
|
153 | .step(new ConfigureAutowire())
|
154 | .step(new AddValidation())
|
155 | .step(new AddAutoMockIntegration())
|
156 | .step(new AddCorsMethods())
|
157 | .step(new AddCorsParams())
|
158 | .step(new AddCorsHeader())
|
159 | .step(new AddCorsIntegration())
|
160 | .step(new AddCorsResponseParameters())
|
161 | .step(new WriteOutputFile())
|
162 | }
|
163 |
|
164 | processMergeCommand() {
|
165 | for (const options of this.resolve()) {
|
166 | this.process(options);
|
167 | }
|
168 | }
|
169 |
|
170 | createFilesCommand() {
|
171 | for (const options of this.resolve()) {
|
172 | let content = jsYml.load(fs.readFileSync(options.inputFullPath, 'utf8'))
|
173 | const pipelineRunner = new Pipeline(options, content, this.serverless);
|
174 | pipelineRunner
|
175 | .step(new AddIntegrations())
|
176 | .step(new CreateIntegrationFile())
|
177 | }
|
178 | }
|
179 |
|
180 | processPackage() {
|
181 | const configurations = toArray(this.serverless.configurationInput.openApiIntegration);
|
182 | if (!configurations || !configurations.length) {
|
183 | return;
|
184 | }
|
185 |
|
186 | const outputPaths = new Set();
|
187 |
|
188 | for (const configuration of configurations) {
|
189 | let optionsResolver = new OptionsResolver(configuration);
|
190 | const options = optionsResolver.resolve(this.options.stage);
|
191 |
|
192 | if (!options.package) {
|
193 | this.serverless.cli.log(`Openapi Integration: PROCESS & DEPLOY HOOK IS DEACTIVATED. Refer to manual for further information `);
|
194 | return;
|
195 | }
|
196 |
|
197 | if (Object.entries(options).length === 0) {
|
198 | this.serverless.cli.log(`Openapi Integration: No matching configuration available for the ${this.options.stage} stage `);
|
199 | return;
|
200 | }
|
201 |
|
202 | if (!options.apiResourceName && configurations.length > 1) {
|
203 | this.serverless.cli.log(`Openapi Integration: apiResourceName is mandatory when specifying multiple configurations`);
|
204 | return;
|
205 | }
|
206 |
|
207 | if (outputPaths.has(options.outputFullPath)) {
|
208 | this.serverless.cli.log(`Openapi Integration: conflicting output paths, make sure to use unique outputPath and/or outputFile for each configuration`);
|
209 | return;
|
210 | }
|
211 | outputPaths.add(options.outputFullPath);
|
212 |
|
213 | this.process(options);
|
214 | this.addApiAGatewayBody(options.outputFullPath, options.apiResourceName)
|
215 | }
|
216 | }
|
217 |
|
218 | addApiAGatewayBody(generatedApiSpecification, apiResourceName) {
|
219 | let resources = this.serverless.service.resources.Resources;
|
220 | Object.entries(resources).forEach(([resourceName, resource]) => {
|
221 | if (resource.Type === 'AWS::ApiGateway::RestApi' &&
|
222 | (!apiResourceName || resourceName === apiResourceName)) {
|
223 | if (resource.hasOwnProperty('Properties') && !resource.Properties.hasOwnProperty('Body')) {
|
224 | resource.Properties.Body = {}
|
225 | }
|
226 |
|
227 | resource.Properties.Body = jsYml.load(fs.readFileSync(generatedApiSpecification))
|
228 | }
|
229 |
|
230 | this.serverless.service.resources.Resources = resources;
|
231 | });
|
232 | }
|
233 | }
|
234 |
|
235 | module.exports = MergeIntegrationPlugin;
|