fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #2479 from dracomithril/reduce_security_schema_duplicates

Reducing security schema duplicates in OpenAPI parser

authored by

Lubos and committed by
GitHub
c74279c2 4bc4dd13

+282 -12
+5
.changeset/ninety-kings-confess.md
··· 1 + --- 2 + "@hey-api/openapi-ts": patch 3 + --- 4 + 5 + fix(parser): deduplicate security schemas based on name
+93
packages/openapi-ts/src/openApi/2.0.x/parser/__tests__/operation.test.ts
··· 1 + import { describe, expect, it } from 'vitest'; 2 + 3 + import type { IR } from '../../../../ir/types'; 4 + import type { SecuritySchemeObject } from '../../types/spec'; 5 + import { parseOperation } from '../operation'; 6 + 7 + type ParseOperationProps = Parameters<typeof parseOperation>[0]; 8 + 9 + describe('operation', () => { 10 + const context = { 11 + config: { 12 + plugins: {}, 13 + }, 14 + ir: { 15 + paths: {}, 16 + servers: [], 17 + }, 18 + } as unknown as IR.Context; 19 + 20 + it('should parse operation correctly', () => { 21 + const method = 'get'; 22 + const operation: ParseOperationProps['operation'] = { 23 + operationId: 'testOperation', 24 + responses: {}, 25 + security: [ 26 + { 27 + apiKeyAuth: [], 28 + basicAuthRule: [], 29 + }, 30 + { 31 + apiKeyAuth: [], 32 + oauthRule: [], 33 + }, 34 + ], 35 + summary: 'Test Operation', 36 + }; 37 + const path = '/test'; 38 + const securitySchemesMap = new Map<string, SecuritySchemeObject>([ 39 + ['apiKeyAuth', { in: 'header', name: 'Auth', type: 'apiKey' }], 40 + ['basicAuthRule', { description: 'Basic Auth', type: 'basic' }], 41 + [ 42 + 'oauthRule', 43 + { 44 + description: 'OAuth2', 45 + flow: 'password', 46 + scopes: { 47 + read: 'Grants read access', 48 + write: 'Grants write access', 49 + }, 50 + tokenUrl: 'https://example.com/oauth/token', 51 + type: 'oauth2', 52 + }, 53 + ], 54 + ]); 55 + const state: ParseOperationProps['state'] = { 56 + ids: new Map<string, string>(), 57 + }; 58 + 59 + parseOperation({ 60 + context, 61 + method, 62 + operation, 63 + path, 64 + securitySchemesMap, 65 + state, 66 + }); 67 + 68 + expect(context.ir.paths?.[path]?.[method]).toEqual({ 69 + id: 'testOperation', 70 + method, 71 + operationId: 'testOperation', 72 + path, 73 + security: [ 74 + { in: 'header', name: 'Auth', type: 'apiKey' }, 75 + { description: 'Basic Auth', scheme: 'basic', type: 'http' }, 76 + { 77 + description: 'OAuth2', 78 + flows: { 79 + password: { 80 + scopes: { 81 + read: 'Grants read access', 82 + write: 'Grants write access', 83 + }, 84 + tokenUrl: 'https://example.com/oauth/token', 85 + }, 86 + }, 87 + type: 'oauth2', 88 + }, 89 + ], 90 + summary: 'Test Operation', 91 + }); 92 + }); 93 + });
+4 -4
packages/openapi-ts/src/openApi/2.0.x/parser/operation.ts
··· 261 261 } 262 262 263 263 if (operation.security) { 264 - const securitySchemeObjects: Array<IR.SecurityObject> = []; 264 + const securitySchemeObjects: Map<string, IR.SecurityObject> = new Map(); 265 265 266 266 for (const securityRequirementObject of operation.security) { 267 267 for (const name in securityRequirementObject) { ··· 325 325 continue; 326 326 } 327 327 328 - securitySchemeObjects.push(irSecuritySchemeObject); 328 + securitySchemeObjects.set(name, irSecuritySchemeObject); 329 329 } 330 330 } 331 331 332 - if (securitySchemeObjects.length) { 333 - irOperation.security = securitySchemeObjects; 332 + if (securitySchemeObjects.size) { 333 + irOperation.security = Array.from(securitySchemeObjects.values()); 334 334 } 335 335 } 336 336
+86
packages/openapi-ts/src/openApi/3.0.x/parser/__tests__/operation.test.ts
··· 1 + import { describe, expect, it } from 'vitest'; 2 + 3 + import type { IR } from '../../../../ir/types'; 4 + import type { SecuritySchemeObject } from '../../types/spec'; 5 + import { parseOperation } from '../operation'; 6 + 7 + type ParseOperationProps = Parameters<typeof parseOperation>[0]; 8 + 9 + describe('operation', () => { 10 + const context = { 11 + config: { 12 + plugins: {}, 13 + }, 14 + ir: { 15 + paths: {}, 16 + servers: [], 17 + }, 18 + } as unknown as IR.Context; 19 + 20 + it('should parse operation correctly', () => { 21 + const method = 'get'; 22 + const operation: ParseOperationProps['operation'] = { 23 + operationId: 'testOperation', 24 + responses: {}, 25 + security: [ 26 + { 27 + apiKeyAuth: [], 28 + }, 29 + { 30 + apiKeyAuth: [], 31 + }, 32 + { 33 + oauthRule: ['read'], 34 + }, 35 + { 36 + oauthRule: ['write'], 37 + }, 38 + ], 39 + summary: 'Test Operation', 40 + }; 41 + const path = '/test'; 42 + 43 + const oauth2: SecuritySchemeObject = { 44 + description: 'OAuth2', 45 + flows: { 46 + password: { 47 + scopes: { 48 + read: 'Grants read access', 49 + write: 'Grants write access', 50 + }, 51 + tokenUrl: 'https://example.com/oauth/token', 52 + }, 53 + }, 54 + type: 'oauth2', 55 + }; 56 + const securitySchemesMap = new Map<string, SecuritySchemeObject>([ 57 + ['apiKeyAuth', { in: 'header', name: 'Auth', type: 'apiKey' }], 58 + [ 59 + 'basicAuthRule', 60 + { description: 'Basic Auth', scheme: 'basic', type: 'http' }, 61 + ], 62 + ['oauthRule', oauth2], 63 + ]); 64 + const state: ParseOperationProps['state'] = { 65 + ids: new Map<string, string>(), 66 + }; 67 + 68 + parseOperation({ 69 + context, 70 + method, 71 + operation, 72 + path, 73 + securitySchemesMap, 74 + state, 75 + }); 76 + 77 + expect(context.ir.paths?.[path]?.[method]).toEqual({ 78 + id: 'testOperation', 79 + method, 80 + operationId: 'testOperation', 81 + path, 82 + security: [{ in: 'header', name: 'Auth', type: 'apiKey' }, oauth2], 83 + summary: 'Test Operation', 84 + }); 85 + }); 86 + });
+4 -4
packages/openapi-ts/src/openApi/3.0.x/parser/operation.ts
··· 203 203 } 204 204 205 205 if (operation.security) { 206 - const securitySchemeObjects: Array<IR.SecurityObject> = []; 206 + const securitySchemeObjects: Map<string, IR.SecurityObject> = new Map(); 207 207 208 208 for (const securityRequirementObject of operation.security) { 209 209 for (const name in securityRequirementObject) { ··· 213 213 continue; 214 214 } 215 215 216 - securitySchemeObjects.push(securitySchemeObject); 216 + securitySchemeObjects.set(name, securitySchemeObject); 217 217 } 218 218 } 219 219 220 - if (securitySchemeObjects.length) { 221 - irOperation.security = securitySchemeObjects; 220 + if (securitySchemeObjects.size) { 221 + irOperation.security = Array.from(securitySchemeObjects.values()); 222 222 } 223 223 } 224 224
+86
packages/openapi-ts/src/openApi/3.1.x/parser/__tests__/operation.test.ts
··· 1 + import { describe, expect, it } from 'vitest'; 2 + 3 + import type { IR } from '../../../../ir/types'; 4 + import type { SecuritySchemeObject } from '../../types/spec'; 5 + import { parseOperation } from '../operation'; 6 + 7 + type ParseOperationProps = Parameters<typeof parseOperation>[0]; 8 + 9 + describe('operation', () => { 10 + const context = { 11 + config: { 12 + plugins: {}, 13 + }, 14 + ir: { 15 + paths: {}, 16 + servers: [], 17 + }, 18 + } as unknown as IR.Context; 19 + 20 + it('should parse operation correctly', () => { 21 + const method = 'get'; 22 + const operation: ParseOperationProps['operation'] = { 23 + operationId: 'testOperation', 24 + responses: {}, 25 + security: [ 26 + { 27 + apiKeyAuth: [], 28 + }, 29 + { 30 + apiKeyAuth: [], 31 + }, 32 + { 33 + oauthRule: ['read'], 34 + }, 35 + { 36 + oauthRule: ['write'], 37 + }, 38 + ], 39 + summary: 'Test Operation', 40 + }; 41 + const path = '/test'; 42 + 43 + const oauth2: SecuritySchemeObject = { 44 + description: 'OAuth2', 45 + flows: { 46 + password: { 47 + scopes: { 48 + read: 'Grants read access', 49 + write: 'Grants write access', 50 + }, 51 + tokenUrl: 'https://example.com/oauth/token', 52 + }, 53 + }, 54 + type: 'oauth2', 55 + }; 56 + const securitySchemesMap = new Map<string, SecuritySchemeObject>([ 57 + ['apiKeyAuth', { in: 'header', name: 'Auth', type: 'apiKey' }], 58 + [ 59 + 'basicAuthRule', 60 + { description: 'Basic Auth', scheme: 'basic', type: 'http' }, 61 + ], 62 + ['oauthRule', oauth2], 63 + ]); 64 + const state: ParseOperationProps['state'] = { 65 + ids: new Map<string, string>(), 66 + }; 67 + 68 + parseOperation({ 69 + context, 70 + method, 71 + operation, 72 + path, 73 + securitySchemesMap, 74 + state, 75 + }); 76 + 77 + expect(context.ir.paths?.[path]?.[method]).toEqual({ 78 + id: 'testOperation', 79 + method, 80 + operationId: 'testOperation', 81 + path, 82 + security: [{ in: 'header', name: 'Auth', type: 'apiKey' }, oauth2], 83 + summary: 'Test Operation', 84 + }); 85 + }); 86 + });
+4 -4
packages/openapi-ts/src/openApi/3.1.x/parser/operation.ts
··· 188 188 } 189 189 190 190 if (operation.security) { 191 - const securitySchemeObjects: Array<IR.SecurityObject> = []; 191 + const securitySchemeObjects: Map<string, IR.SecurityObject> = new Map(); 192 192 193 193 for (const securityRequirementObject of operation.security) { 194 194 for (const name in securityRequirementObject) { ··· 198 198 continue; 199 199 } 200 200 201 - securitySchemeObjects.push(securitySchemeObject); 201 + securitySchemeObjects.set(name, securitySchemeObject); 202 202 } 203 203 } 204 204 205 - if (securitySchemeObjects.length) { 206 - irOperation.security = securitySchemeObjects; 205 + if (securitySchemeObjects.size) { 206 + irOperation.security = Array.from(securitySchemeObjects.values()); 207 207 } 208 208 } 209 209