diff --git a/packages/oas/README.md b/packages/oas/README.md index fc7dda1d..35663f33 100644 --- a/packages/oas/README.md +++ b/packages/oas/README.md @@ -69,6 +69,16 @@ console.log(requests); Notice the `includeVendorExamples` option affects Swagger specifications only. +Some specifications may have configuration for `Accept` header value in request parameters section. The automatically inferred `Accept` header values may be skipped, to skip these inferred values in output use the `oas2har` function as follows: + +```js +import schema from './swagger.json' assert { type: 'json' }; +import { oas2har } from '@har-sdk/oas'; + +const requests = await oas2har(schema, { skipAcceptHeaderInference: true }); +console.log(requests); +``` + ## License Copyright © 2023 [Bright Security](https://brightsec.com/). diff --git a/packages/oas/src/converter/Converter.ts b/packages/oas/src/converter/Converter.ts index 23a622c7..d6fcde8f 100644 --- a/packages/oas/src/converter/Converter.ts +++ b/packages/oas/src/converter/Converter.ts @@ -1,5 +1,9 @@ import { OpenAPI, Request } from '@har-sdk/core'; +export interface ConverterOptions { + skipAcceptHeaderInference?: boolean; +} + export interface Converter { convert(collection: OpenAPI.Document): Promise; } diff --git a/packages/oas/src/converter/parts/SubConverterFactory.ts b/packages/oas/src/converter/parts/SubConverterFactory.ts index 13b0c1ab..7995cf17 100644 --- a/packages/oas/src/converter/parts/SubConverterFactory.ts +++ b/packages/oas/src/converter/parts/SubConverterFactory.ts @@ -7,10 +7,14 @@ import { Oas2BodyConverter, Oas3RequestBodyConverter } from './postdata'; import { Oas2QueryStringConverter, Oas3QueryStringConverter } from './query'; import { Sampler } from '../Sampler'; import { Oas2CookiesConverter, Oas3CookiesConverter } from './cookies'; +import { ConverterOptions } from '../Converter'; import { OpenAPI, OpenAPIV2, OpenAPIV3 } from '@har-sdk/core'; export class SubConverterFactory { - constructor(private readonly sampler: Sampler) {} + constructor( + private readonly sampler: Sampler, + private readonly options: ConverterOptions + ) {} public createSubConverter( spec: OpenAPI.Document, @@ -63,15 +67,17 @@ export class SubConverterFactory { spec: OpenAPI.Document, oas2Ctor: new ( spec: OpenAPIV2.Document, - sampler: Sampler + sampler: Sampler, + options: ConverterOptions ) => SubConverter, oas3Ctor: new ( spec: OpenAPIV3.Document, - sampler: Sampler + sampler: Sampler, + options: ConverterOptions ) => SubConverter ) { return isOASV3(spec) - ? new oas3Ctor(spec as OpenAPIV3.Document, this.sampler) - : new oas2Ctor(spec as OpenAPIV2.Document, this.sampler); + ? new oas3Ctor(spec as OpenAPIV3.Document, this.sampler, this.options) + : new oas2Ctor(spec as OpenAPIV2.Document, this.sampler, this.options); } } diff --git a/packages/oas/src/converter/parts/headers/HeadersConverter.ts b/packages/oas/src/converter/parts/headers/HeadersConverter.ts index 9b511b1c..7a605a9f 100644 --- a/packages/oas/src/converter/parts/headers/HeadersConverter.ts +++ b/packages/oas/src/converter/parts/headers/HeadersConverter.ts @@ -1,3 +1,4 @@ +import { ConverterOptions } from '../../Converter'; import { OperationObject, ParameterObject } from '../../../types'; import { filterLocationParams, @@ -18,7 +19,8 @@ export abstract class HeadersConverter protected constructor( private readonly spec: T, - private readonly sampler: Sampler + private readonly sampler: Sampler, + private readonly options: ConverterOptions ) {} protected abstract createContentTypeHeaders( @@ -44,9 +46,15 @@ export abstract class HeadersConverter headers.push(...this.createContentTypeHeaders(pathObj)); } - headers.push(...this.createAcceptHeaders(pathObj)); + const acceptHeaders = this.createAcceptHeaders(pathObj); - headers.push(...this.parseFromParams(path, method)); + const paramsHeaders = this.parseFromParams(path, method); + + const addInferred = + !this.options.skipAcceptHeaderInference || + !paramsHeaders.some((x) => x.name === 'accept'); + + headers.push(...(addInferred ? acceptHeaders : []), ...paramsHeaders); return headers; } diff --git a/packages/oas/src/converter/parts/headers/Oas2HeadersConveter.ts b/packages/oas/src/converter/parts/headers/Oas2HeadersConveter.ts index 423cc2ca..90fff90f 100644 --- a/packages/oas/src/converter/parts/headers/Oas2HeadersConveter.ts +++ b/packages/oas/src/converter/parts/headers/Oas2HeadersConveter.ts @@ -1,3 +1,4 @@ +import { ConverterOptions } from '../../Converter'; import { LocationParam } from '../LocationParam'; import { Oas2ValueSerializer } from '../Oas2ValueSerializer'; import { Sampler } from '../../Sampler'; @@ -9,8 +10,12 @@ export class Oas2HeadersConverter extends HeadersConverter { private readonly oas2ValueSerializer = new Oas2ValueSerializer(); private readonly oas2MediaTypeResolver: Oas2MediaTypesResolver; - constructor(spec: OpenAPIV2.Document, sampler: Sampler) { - super(spec, sampler); + constructor( + spec: OpenAPIV2.Document, + sampler: Sampler, + options: ConverterOptions + ) { + super(spec, sampler, options); this.oas2MediaTypeResolver = new Oas2MediaTypesResolver(spec); } diff --git a/packages/oas/src/converter/parts/headers/Oas3HeadersConverter.ts b/packages/oas/src/converter/parts/headers/Oas3HeadersConverter.ts index 9ab1b507..66c921df 100644 --- a/packages/oas/src/converter/parts/headers/Oas3HeadersConverter.ts +++ b/packages/oas/src/converter/parts/headers/Oas3HeadersConverter.ts @@ -1,3 +1,4 @@ +import { ConverterOptions } from '../../Converter'; import { isObject } from '../../../utils'; import { LocationParam } from '../LocationParam'; import { Sampler } from '../../Sampler'; @@ -8,8 +9,12 @@ import { Header, OpenAPIV3 } from '@har-sdk/core'; export class Oas3HeadersConverter extends HeadersConverter { private readonly uriTemplator = new UriTemplator(); - constructor(spec: OpenAPIV3.Document, sampler: Sampler) { - super(spec, sampler); + constructor( + spec: OpenAPIV3.Document, + sampler: Sampler, + options: ConverterOptions + ) { + super(spec, sampler, options); } protected createContentTypeHeaders( diff --git a/packages/oas/src/index.ts b/packages/oas/src/index.ts index 5907bedc..d31853ad 100644 --- a/packages/oas/src/index.ts +++ b/packages/oas/src/index.ts @@ -12,7 +12,10 @@ export * from './errors'; export const oas2har = ( collection: OpenAPI.Document, - options: { includeVendorExamples?: boolean } = {} + options: { + includeVendorExamples?: boolean; + skipAcceptHeaderInference?: boolean; + } = {} ): Promise => { if (!collection) { throw new TypeError('Please provide a valid OAS specification.'); @@ -20,7 +23,7 @@ export const oas2har = ( const sampler = new Sampler(options); const baseUrlParser = new BaseUrlParser(sampler); - const subConverterFactory = new SubConverterFactory(sampler); + const subConverterFactory = new SubConverterFactory(sampler, options); const subConverterRegistry = new SubConverterRegistry(subConverterFactory); const securityRequirementsFactory = new SecurityRequirementsFactory(sampler); diff --git a/packages/oas/tests/DefaultConverter.spec.ts b/packages/oas/tests/DefaultConverter.spec.ts index f0bcecfb..fe3302ce 100644 --- a/packages/oas/tests/DefaultConverter.spec.ts +++ b/packages/oas/tests/DefaultConverter.spec.ts @@ -412,5 +412,21 @@ describe('DefaultConverter', () => { // assert expect(result).toStrictEqual(expectedDoc); }); + + it('should skip accept header inference when it comes from parameters', async () => { + // arrange + const { inputDoc, expectedDoc } = await createFixture({ + inputFile: `./fixtures/params-header.skip-inference.oas.yaml`, + expectedFile: `./fixtures/params-header.skip-inference.oas.result.json` + }); + + // act + const result: Request[] = await oas2har(inputDoc as any, { + skipAcceptHeaderInference: true + }); + + // assert + expect(result).toStrictEqual(expectedDoc); + }); }); }); diff --git a/packages/oas/tests/fixtures/params-header.skip-inference.oas.result.json b/packages/oas/tests/fixtures/params-header.skip-inference.oas.result.json new file mode 100644 index 00000000..c2d8dd37 --- /dev/null +++ b/packages/oas/tests/fixtures/params-header.skip-inference.oas.result.json @@ -0,0 +1,32 @@ +[ + { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json;v=1.0" + } + ], + "headersSize": 0, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [], + "url": "https://example.com/api/v1/user" + }, + { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json" + } + ], + "headersSize": 0, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [], + "url": "https://example.com/api/v1/users" + } +] diff --git a/packages/oas/tests/fixtures/params-header.skip-inference.oas.yaml b/packages/oas/tests/fixtures/params-header.skip-inference.oas.yaml new file mode 100644 index 00000000..65c6a4a8 --- /dev/null +++ b/packages/oas/tests/fixtures/params-header.skip-inference.oas.yaml @@ -0,0 +1,39 @@ +openapi: 3.0.3 +info: + version: '1.0' +servers: + - url: https://example.com/api/v1 +paths: + /user: + get: + parameters: + - name: Accept + in: header + description: '' + schema: + type: string + example: application/json;v=1.0 + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/User' + /users: + get: + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' +components: + schemas: + User: + properties: + id: + type: string + name: + type: string