@mimik/api-helper 2.0.7 → 2.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +9 -0
- package/.husky/pre-commit +2 -0
- package/.husky/pre-push +2 -0
- package/README.md +57 -63
- package/eslint.config.js +30 -11
- package/index.js +113 -117
- package/lib/ajvHelpers.js +1 -1
- package/lib/baseHandlers.js +2 -3
- package/lib/oauthValidation-helper.js +1 -1
- package/lib/securityHandlers.js +1 -1
- package/package.json +24 -23
- package/test/ajvHelpers.test.js +159 -0
- package/test/baseHandlers.test.js +150 -0
- package/test/extract-helper.test.js +100 -0
- package/test/index-async.test.js +599 -0
- package/test/index-sync.test.js +282 -0
- package/test/oauthValidation-helper.test.js +136 -0
- package/test/securityHandlers.test.js +557 -0
- package/.nycrc +0 -4
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
import esmock from 'esmock';
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import fsReal from 'fs';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
const getRichError = (type, message, info, cause) => {
|
|
8
|
+
const error = new Error(message);
|
|
9
|
+
|
|
10
|
+
error.type = type;
|
|
11
|
+
error.info = info;
|
|
12
|
+
if (cause) error.cause = cause;
|
|
13
|
+
return error;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const mockLogger = {
|
|
17
|
+
info: () => {},
|
|
18
|
+
debug: () => {},
|
|
19
|
+
warn: () => {},
|
|
20
|
+
error: () => {},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const TOKEN_PARAMS = {
|
|
24
|
+
userId: 'userId', onBehalfId: 'onBehalfId', appId: 'appId',
|
|
25
|
+
clientId: 'clientId', customer: 'customer', onBehalf: 'onBehalf',
|
|
26
|
+
tokenType: 'tokenType', cluster: 'cluster', claims: 'claims',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let tmpDir;
|
|
30
|
+
|
|
31
|
+
describe('getAPIFile', () => {
|
|
32
|
+
let getAPIFile;
|
|
33
|
+
let mockRpRetry;
|
|
34
|
+
let mockSwaggerResolve;
|
|
35
|
+
|
|
36
|
+
before(async () => {
|
|
37
|
+
mockRpRetry = () => Promise.resolve({ openapi: '3.0.0' });
|
|
38
|
+
mockSwaggerResolve = opts => Promise.resolve({ spec: opts.spec, errors: [] });
|
|
39
|
+
|
|
40
|
+
const mod = await esmock('../index.js', {
|
|
41
|
+
'@mimik/response-helper': { getRichError },
|
|
42
|
+
'@mimik/sumologic-winston-logger': mockLogger,
|
|
43
|
+
'@mimik/swagger-helper': { rejectRequest: () => {}, TOKEN_PARAMS },
|
|
44
|
+
'@mimik/request-retry': { rpRetry: (...args) => mockRpRetry(...args) },
|
|
45
|
+
'swagger-client': { default: { resolve: (...args) => mockSwaggerResolve(...args) } },
|
|
46
|
+
'openapi-backend': {
|
|
47
|
+
OpenAPIBackend: class {
|
|
48
|
+
register() {}
|
|
49
|
+
registerSecurityHandler() {}
|
|
50
|
+
init() { return Promise.resolve(); }
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
({ getAPIFile } = mod);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
tmpDir = fsReal.mkdtempSync(path.join(os.tmpdir(), 'getapifile-test-'));
|
|
60
|
+
mockRpRetry = () => Promise.resolve({ openapi: '3.0.0' });
|
|
61
|
+
mockSwaggerResolve = opts => Promise.resolve({ spec: opts.spec, errors: [] });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
afterEach(() => {
|
|
65
|
+
fsReal.rmSync(tmpDir, { recursive: true, force: true });
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should load and resolve an existing JSON file', async () => {
|
|
69
|
+
const apiFile = path.join(tmpDir, 'api.json');
|
|
70
|
+
|
|
71
|
+
fsReal.writeFileSync(apiFile, JSON.stringify({ openapi: '3.0.0', info: { title: 'test' } }));
|
|
72
|
+
const result = await getAPIFile(apiFile, 'corr-1');
|
|
73
|
+
|
|
74
|
+
expect(result).to.have.property('openapi', '3.0.0');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should reject when existing file has invalid JSON', async () => {
|
|
78
|
+
const apiFile = path.join(tmpDir, 'api.json');
|
|
79
|
+
|
|
80
|
+
fsReal.writeFileSync(apiFile, 'not json{{{');
|
|
81
|
+
try {
|
|
82
|
+
await getAPIFile(apiFile, 'corr-2');
|
|
83
|
+
expect.fail('should have rejected');
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
expect(err.message).to.equal('wrong file format');
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should reject when no options provided and file does not exist', async () => {
|
|
91
|
+
const apiFile = path.join(tmpDir, 'nonexistent.json');
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
await getAPIFile(apiFile, 'corr-3');
|
|
95
|
+
expect.fail('should have rejected');
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
expect(err.message).to.equal('no options');
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should reject when options has no apiInfo', async () => {
|
|
103
|
+
const apiFile = path.join(tmpDir, 'nonexistent.json');
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
await getAPIFile(apiFile, 'corr-4', {});
|
|
107
|
+
expect.fail('should have rejected');
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
expect(err.message).to.equal('no information for swaggerFile');
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should reject for wrong api filename format', async () => {
|
|
115
|
+
const apiFile = path.join(tmpDir, 'bad-name.json');
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
await getAPIFile(apiFile, 'corr-5', { apiInfo: { provider: 'bitbucket' } });
|
|
119
|
+
expect.fail('should have rejected');
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
expect(err.message).to.equal('wrong api name');
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should reject when bitbucket auth is missing', async () => {
|
|
127
|
+
const apiFile = path.join(tmpDir, 'customer_apiname_v1_swagger.json');
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
await getAPIFile(apiFile, 'corr-6', {
|
|
131
|
+
apiInfo: { provider: 'bitbucket' },
|
|
132
|
+
});
|
|
133
|
+
expect.fail('should have rejected');
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
expect(err.message).to.equal('missing username/password for accessing Bitbucket');
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should reject when bitbucket auth uses default values', async () => {
|
|
141
|
+
const apiFile = path.join(tmpDir, 'customer_apiname_v1_swagger.json');
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
await getAPIFile(apiFile, 'corr-7', {
|
|
145
|
+
apiInfo: {
|
|
146
|
+
provider: 'bitbucket',
|
|
147
|
+
apiBasicAuth: { username: ' ', password: 'real-pass' },
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
expect.fail('should have rejected');
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
expect(err.message).to.equal('missing username/password for accessing Bitbucket');
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should fetch from bitbucket and resolve spec', async () => {
|
|
158
|
+
const apiFile = path.join(tmpDir, 'customer_apiname_v1_swagger.json');
|
|
159
|
+
|
|
160
|
+
const result = await getAPIFile(apiFile, 'corr-8', {
|
|
161
|
+
apiInfo: {
|
|
162
|
+
provider: 'bitbucket',
|
|
163
|
+
apiBasicAuth: { username: 'user', password: 'pass' },
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
expect(result).to.have.property('openapi');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should fetch from swaggerhub and resolve spec', async () => {
|
|
171
|
+
const apiFile = path.join(tmpDir, 'customer_apiname_v1_swagger.json');
|
|
172
|
+
|
|
173
|
+
const result = await getAPIFile(apiFile, 'corr-9', {
|
|
174
|
+
apiInfo: { provider: 'swaggerhub' },
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
expect(result).to.have.property('openapi');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should include apiApiKey as authorization for swaggerhub', async () => {
|
|
181
|
+
const apiFile = path.join(tmpDir, 'customer_apiname_v1_swagger.json');
|
|
182
|
+
let capturedOpts;
|
|
183
|
+
|
|
184
|
+
mockRpRetry = (opts) => {
|
|
185
|
+
capturedOpts = opts;
|
|
186
|
+
return Promise.resolve({ openapi: '3.0.0' });
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
await getAPIFile(apiFile, 'corr-10', {
|
|
190
|
+
apiInfo: { provider: 'swaggerhub', apiApiKey: 'my-key' },
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
expect(capturedOpts.headers.Authorization).to.equal('my-key');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should spread metrics with url added', async () => {
|
|
197
|
+
const apiFile = path.join(tmpDir, 'customer_apiname_v1_swagger.json');
|
|
198
|
+
let capturedOpts;
|
|
199
|
+
|
|
200
|
+
mockRpRetry = (opts) => {
|
|
201
|
+
capturedOpts = opts;
|
|
202
|
+
return Promise.resolve({ openapi: '3.0.0' });
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
await getAPIFile(apiFile, 'corr-11', {
|
|
206
|
+
apiInfo: { provider: 'swaggerhub' },
|
|
207
|
+
metrics: { name: 'api-fetch' },
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
expect(capturedOpts.metrics).to.have.property('name', 'api-fetch');
|
|
211
|
+
expect(capturedOpts.metrics).to.have.property('url');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should reject for invalid provider', async () => {
|
|
215
|
+
const apiFile = path.join(tmpDir, 'customer_apiname_v1_swagger.json');
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
await getAPIFile(apiFile, 'corr-12', {
|
|
219
|
+
apiInfo: { provider: 'invalid' },
|
|
220
|
+
});
|
|
221
|
+
expect.fail('should have rejected');
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
expect(err.message).to.equal('invalid API provider');
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('should parse YAML response from provider', async () => {
|
|
229
|
+
mockRpRetry = () => Promise.resolve('openapi: "3.0.0"\ninfo:\n title: test');
|
|
230
|
+
|
|
231
|
+
const apiFile = path.join(tmpDir, 'customer_apiname_v1_swagger.json');
|
|
232
|
+
|
|
233
|
+
const result = await getAPIFile(apiFile, 'corr-13', {
|
|
234
|
+
apiInfo: { provider: 'swaggerhub' },
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
expect(result).to.have.property('openapi');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should reject with resolve errors', async () => {
|
|
241
|
+
mockSwaggerResolve = () => Promise.resolve({
|
|
242
|
+
spec: {},
|
|
243
|
+
errors: [{ message: 'bad ref' }],
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const apiFile = path.join(tmpDir, 'customer_apiname_v1_swagger.json');
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
await getAPIFile(apiFile, 'corr-14', {
|
|
250
|
+
apiInfo: { provider: 'swaggerhub' },
|
|
251
|
+
});
|
|
252
|
+
expect.fail('should have rejected');
|
|
253
|
+
}
|
|
254
|
+
catch (err) {
|
|
255
|
+
expect(err.message).to.equal('errors while resolving definition');
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('should re-throw errors with statusCode from rpRetry', async () => {
|
|
260
|
+
const httpError = new Error('Not Found');
|
|
261
|
+
|
|
262
|
+
httpError.statusCode = 404;
|
|
263
|
+
mockRpRetry = () => Promise.reject(httpError);
|
|
264
|
+
|
|
265
|
+
const apiFile = path.join(tmpDir, 'customer_apiname_v1_swagger.json');
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
await getAPIFile(apiFile, 'corr-15', {
|
|
269
|
+
apiInfo: { provider: 'swaggerhub' },
|
|
270
|
+
});
|
|
271
|
+
expect.fail('should have rejected');
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
expect(err.statusCode).to.equal(404);
|
|
275
|
+
expect(err.message).to.equal('Not Found');
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should create api directory when it does not exist', async () => {
|
|
280
|
+
const apiDir = path.join(tmpDir, 'newdir');
|
|
281
|
+
const apiFile = path.join(apiDir, 'customer_apiname_v1_swagger.json');
|
|
282
|
+
|
|
283
|
+
await getAPIFile(apiFile, 'corr-16', {
|
|
284
|
+
apiInfo: { provider: 'swaggerhub' },
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
expect(fsReal.existsSync(apiDir)).to.equal(true);
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
describe('apiSetup', () => {
|
|
292
|
+
let apiSetup;
|
|
293
|
+
let registeredSecurityHandlers;
|
|
294
|
+
let registeredOps;
|
|
295
|
+
|
|
296
|
+
before(async () => {
|
|
297
|
+
registeredSecurityHandlers = {};
|
|
298
|
+
registeredOps = null;
|
|
299
|
+
|
|
300
|
+
const mod = await esmock('../index.js', {
|
|
301
|
+
'@mimik/response-helper': { getRichError },
|
|
302
|
+
'@mimik/sumologic-winston-logger': mockLogger,
|
|
303
|
+
'@mimik/swagger-helper': { rejectRequest: () => {}, TOKEN_PARAMS },
|
|
304
|
+
'@mimik/request-retry': { rpRetry: () => Promise.resolve() },
|
|
305
|
+
'swagger-client': { default: { resolve: () => Promise.resolve() } },
|
|
306
|
+
'openapi-backend': {
|
|
307
|
+
OpenAPIBackend: class {
|
|
308
|
+
constructor(opts) { this.opts = opts; }
|
|
309
|
+
register(ops) { registeredOps = ops; }
|
|
310
|
+
registerSecurityHandler(name, handler) { registeredSecurityHandlers[name] = handler; }
|
|
311
|
+
init() { return Promise.resolve(); }
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
({ apiSetup } = mod);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
beforeEach(() => {
|
|
320
|
+
registeredSecurityHandlers = {};
|
|
321
|
+
registeredOps = null;
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
const makeConfig = (env = 'production') => ({
|
|
325
|
+
nodeEnvironment: env,
|
|
326
|
+
serverSettings: { basePath: '/api', securitySet: 'off' },
|
|
327
|
+
security: {
|
|
328
|
+
server: { audience: 'aud', issuer: 'iss', accessKey: 'key' },
|
|
329
|
+
generic: { audience: 'noGeneric', key: 'noGeneric' },
|
|
330
|
+
admin: { externalId: 'admin' },
|
|
331
|
+
apiKeys: ['key1'],
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should return a promise that resolves to the api object', async () => {
|
|
336
|
+
const setup = {
|
|
337
|
+
apiFilename: '/test/api.json',
|
|
338
|
+
existingSecuritySchemes: [],
|
|
339
|
+
definedSecuritySchemes: [],
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const result = await apiSetup(setup, {}, null, null, makeConfig(), 'corr-1');
|
|
343
|
+
expect(result).to.have.property('opts');
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('should register default security handlers for existing schemes', async () => {
|
|
347
|
+
const setup = {
|
|
348
|
+
apiFilename: '/test/api.json',
|
|
349
|
+
existingSecuritySchemes: ['SystemSecurity', 'AdminSecurity'],
|
|
350
|
+
definedSecuritySchemes: ['SystemSecurity', 'AdminSecurity'],
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
await apiSetup(setup, {}, null, null, makeConfig(), 'corr-2');
|
|
354
|
+
expect(registeredSecurityHandlers).to.have.property('SystemSecurity');
|
|
355
|
+
expect(registeredSecurityHandlers).to.have.property('AdminSecurity');
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('should use mock mode for local environment', async () => {
|
|
359
|
+
const setup = {
|
|
360
|
+
apiFilename: '/test/api.json',
|
|
361
|
+
existingSecuritySchemes: ['SystemSecurity'],
|
|
362
|
+
definedSecuritySchemes: ['SystemSecurity'],
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
await apiSetup(setup, {}, null, null, makeConfig('local'), 'corr-3');
|
|
366
|
+
expect(registeredSecurityHandlers).to.have.property('SystemSecurity');
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('should throw for unused security handlers', () => {
|
|
370
|
+
const setup = {
|
|
371
|
+
apiFilename: '/test/api.json',
|
|
372
|
+
existingSecuritySchemes: [],
|
|
373
|
+
definedSecuritySchemes: ['SystemSecurity'],
|
|
374
|
+
};
|
|
375
|
+
const handlers = {
|
|
376
|
+
SystemSecurity: { regular: () => true, mock: () => true },
|
|
377
|
+
UnknownSecurity: { regular: () => true, mock: () => true },
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
expect(() => apiSetup(setup, {}, handlers, null, makeConfig(), 'corr-4'))
|
|
381
|
+
.to.throw('unused handlers for security schemes');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('should throw for missing handlers when remainingSecurities exist', () => {
|
|
385
|
+
const setup = {
|
|
386
|
+
apiFilename: '/test/api.json',
|
|
387
|
+
existingSecuritySchemes: [],
|
|
388
|
+
definedSecuritySchemes: ['CustomSecurity'],
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
expect(() => apiSetup(setup, {}, null, null, makeConfig(), 'corr-5'))
|
|
392
|
+
.to.throw('missing handlers for security schemes');
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('should throw for missing handler for a specific security scheme', () => {
|
|
396
|
+
const setup = {
|
|
397
|
+
apiFilename: '/test/api.json',
|
|
398
|
+
existingSecuritySchemes: [],
|
|
399
|
+
definedSecuritySchemes: ['CustomSecurity', 'OtherSecurity'],
|
|
400
|
+
};
|
|
401
|
+
const handlers = {
|
|
402
|
+
CustomSecurity: { regular: () => true, mock: () => true },
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
expect(() => apiSetup(setup, {}, handlers, null, makeConfig(), 'corr-6'))
|
|
406
|
+
.to.throw('missing handler for security scheme');
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it('should allow disabled security schemes via notEnabled', async () => {
|
|
410
|
+
const setup = {
|
|
411
|
+
apiFilename: '/test/api.json',
|
|
412
|
+
existingSecuritySchemes: [],
|
|
413
|
+
definedSecuritySchemes: ['PeerSecurity'],
|
|
414
|
+
};
|
|
415
|
+
const handlers = {
|
|
416
|
+
PeerSecurity: { notEnabled: true },
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
const result = await apiSetup(setup, {}, handlers, null, makeConfig(), 'corr-7');
|
|
420
|
+
expect(result).to.have.property('opts');
|
|
421
|
+
expect(registeredSecurityHandlers).to.not.have.property('PeerSecurity');
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('should not register default handler when custom handler is provided', async () => {
|
|
425
|
+
const customHandler = () => 'custom';
|
|
426
|
+
const setup = {
|
|
427
|
+
apiFilename: '/test/api.json',
|
|
428
|
+
existingSecuritySchemes: ['SystemSecurity'],
|
|
429
|
+
definedSecuritySchemes: ['SystemSecurity'],
|
|
430
|
+
};
|
|
431
|
+
const handlers = {
|
|
432
|
+
SystemSecurity: { regular: customHandler, mock: customHandler },
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
await apiSetup(setup, {}, handlers, null, makeConfig(), 'corr-8');
|
|
436
|
+
expect(registeredSecurityHandlers.SystemSecurity).to.equal(customHandler);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('should register operations', async () => {
|
|
440
|
+
const ops = { getUsers: () => {}, createUser: () => {} };
|
|
441
|
+
const setup = {
|
|
442
|
+
apiFilename: '/test/api.json',
|
|
443
|
+
existingSecuritySchemes: [],
|
|
444
|
+
definedSecuritySchemes: [],
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
await apiSetup(setup, ops, null, null, makeConfig(), 'corr-9');
|
|
448
|
+
expect(registeredOps).to.equal(ops);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('should use regular mode for production with securitySet on', async () => {
|
|
452
|
+
const config = makeConfig('local');
|
|
453
|
+
|
|
454
|
+
config.serverSettings.securitySet = 'on';
|
|
455
|
+
const setup = {
|
|
456
|
+
apiFilename: '/test/api.json',
|
|
457
|
+
existingSecuritySchemes: ['SystemSecurity'],
|
|
458
|
+
definedSecuritySchemes: ['SystemSecurity'],
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
await apiSetup(setup, {}, null, null, config, 'corr-10');
|
|
462
|
+
expect(registeredSecurityHandlers).to.have.property('SystemSecurity');
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
describe('setupServerFiles', () => {
|
|
467
|
+
let setupServerFiles;
|
|
468
|
+
let mockRpRetry;
|
|
469
|
+
let mockSwaggerResolve;
|
|
470
|
+
|
|
471
|
+
before(async () => {
|
|
472
|
+
mockRpRetry = () => Promise.resolve({ openapi: '3.0.0' });
|
|
473
|
+
mockSwaggerResolve = opts => Promise.resolve({ spec: opts.spec, errors: [] });
|
|
474
|
+
|
|
475
|
+
const mod = await esmock('../index.js', {
|
|
476
|
+
'@mimik/response-helper': { getRichError },
|
|
477
|
+
'@mimik/sumologic-winston-logger': mockLogger,
|
|
478
|
+
'@mimik/swagger-helper': { rejectRequest: () => {}, TOKEN_PARAMS },
|
|
479
|
+
'@mimik/request-retry': { rpRetry: (...args) => mockRpRetry(...args) },
|
|
480
|
+
'swagger-client': { default: { resolve: (...args) => mockSwaggerResolve(...args) } },
|
|
481
|
+
'openapi-backend': {
|
|
482
|
+
OpenAPIBackend: class {
|
|
483
|
+
register() {}
|
|
484
|
+
registerSecurityHandler() {}
|
|
485
|
+
init() { return Promise.resolve(); }
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
({ setupServerFiles } = mod);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
beforeEach(() => {
|
|
494
|
+
tmpDir = fsReal.mkdtempSync(path.join(os.tmpdir(), 'setupserverfiles-test-'));
|
|
495
|
+
mockRpRetry = () => Promise.resolve({ openapi: '3.0.0' });
|
|
496
|
+
mockSwaggerResolve = opts => Promise.resolve({ spec: opts.spec, errors: [] });
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
afterEach(() => {
|
|
500
|
+
fsReal.rmSync(tmpDir, { recursive: true, force: true });
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it('should return apiDefinition, apiFilename, and security schemes', async () => {
|
|
504
|
+
const apiDefinition = {
|
|
505
|
+
openapi: '3.0.0',
|
|
506
|
+
components: {
|
|
507
|
+
securitySchemes: {
|
|
508
|
+
SystemSecurity: { type: 'oauth2', flows: { clientCredentials: {} } },
|
|
509
|
+
},
|
|
510
|
+
},
|
|
511
|
+
};
|
|
512
|
+
const apiFile = path.join(tmpDir, 'api.json');
|
|
513
|
+
|
|
514
|
+
fsReal.writeFileSync(apiFile, JSON.stringify(apiDefinition));
|
|
515
|
+
const controllersDir = path.join(tmpDir, 'controllers');
|
|
516
|
+
|
|
517
|
+
fsReal.mkdirSync(controllersDir);
|
|
518
|
+
const buildDir = path.join(tmpDir, 'build');
|
|
519
|
+
|
|
520
|
+
fsReal.mkdirSync(buildDir);
|
|
521
|
+
|
|
522
|
+
const result = await setupServerFiles(apiFile, controllersDir, buildDir, 'corr-1');
|
|
523
|
+
expect(result).to.have.property('apiDefinition');
|
|
524
|
+
expect(result).to.have.property('apiFilename', apiFile);
|
|
525
|
+
expect(result.existingSecuritySchemes).to.include('SystemSecurity');
|
|
526
|
+
expect(result.definedSecuritySchemes).to.include('SystemSecurity');
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it('should return empty security schemes when none defined', async () => {
|
|
530
|
+
const apiDefinition = { openapi: '3.0.0' };
|
|
531
|
+
const apiFile = path.join(tmpDir, 'api.json');
|
|
532
|
+
|
|
533
|
+
fsReal.writeFileSync(apiFile, JSON.stringify(apiDefinition));
|
|
534
|
+
const controllersDir = path.join(tmpDir, 'controllers');
|
|
535
|
+
|
|
536
|
+
fsReal.mkdirSync(controllersDir);
|
|
537
|
+
const buildDir = path.join(tmpDir, 'build');
|
|
538
|
+
|
|
539
|
+
fsReal.mkdirSync(buildDir);
|
|
540
|
+
|
|
541
|
+
const result = await setupServerFiles(apiFile, controllersDir, buildDir, 'corr-2');
|
|
542
|
+
expect(result.existingSecuritySchemes).to.deep.equal([]);
|
|
543
|
+
expect(result.definedSecuritySchemes).to.deep.equal([]);
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
it('should create register.js in build directory', async () => {
|
|
547
|
+
const apiDefinition = {
|
|
548
|
+
openapi: '3.0.0',
|
|
549
|
+
paths: {
|
|
550
|
+
'/users': {
|
|
551
|
+
get: {
|
|
552
|
+
'operationId': 'getUsers',
|
|
553
|
+
'x-swagger-router-controller': 'userCtrl',
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
};
|
|
558
|
+
const apiFile = path.join(tmpDir, 'api.json');
|
|
559
|
+
|
|
560
|
+
fsReal.writeFileSync(apiFile, JSON.stringify(apiDefinition));
|
|
561
|
+
const controllersDir = path.join(tmpDir, 'controllers');
|
|
562
|
+
|
|
563
|
+
fsReal.mkdirSync(controllersDir);
|
|
564
|
+
fsReal.writeFileSync(path.join(controllersDir, 'userCtrl.js'), 'export {\n getUsers,\n};');
|
|
565
|
+
const buildDir = path.join(tmpDir, 'build');
|
|
566
|
+
|
|
567
|
+
fsReal.mkdirSync(buildDir);
|
|
568
|
+
|
|
569
|
+
await setupServerFiles(apiFile, controllersDir, buildDir, 'corr-3');
|
|
570
|
+
expect(fsReal.existsSync(path.join(buildDir, 'register.js'))).to.equal(true);
|
|
571
|
+
const content = fsReal.readFileSync(path.join(buildDir, 'register.js'), 'utf8');
|
|
572
|
+
|
|
573
|
+
expect(content).to.include('getUsers');
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
it('should filter out null values from existingSecuritySchemes', async () => {
|
|
577
|
+
const apiDefinition = {
|
|
578
|
+
openapi: '3.0.0',
|
|
579
|
+
components: {
|
|
580
|
+
securitySchemes: {
|
|
581
|
+
AdminSecurity: { type: 'oauth2', flows: { clientCredentials: {} } },
|
|
582
|
+
},
|
|
583
|
+
},
|
|
584
|
+
};
|
|
585
|
+
const apiFile = path.join(tmpDir, 'api.json');
|
|
586
|
+
|
|
587
|
+
fsReal.writeFileSync(apiFile, JSON.stringify(apiDefinition));
|
|
588
|
+
const controllersDir = path.join(tmpDir, 'controllers');
|
|
589
|
+
|
|
590
|
+
fsReal.mkdirSync(controllersDir);
|
|
591
|
+
const buildDir = path.join(tmpDir, 'build');
|
|
592
|
+
|
|
593
|
+
fsReal.mkdirSync(buildDir);
|
|
594
|
+
|
|
595
|
+
const result = await setupServerFiles(apiFile, controllersDir, buildDir, 'corr-4');
|
|
596
|
+
expect(result.existingSecuritySchemes).to.not.include(null);
|
|
597
|
+
expect(result.existingSecuritySchemes).to.include('AdminSecurity');
|
|
598
|
+
});
|
|
599
|
+
});
|