Skip to content

Commit

Permalink
feat(oas): allow skipping the Accept header inference (#254)
Browse files Browse the repository at this point in the history
closes #253
  • Loading branch information
ostridm authored Oct 7, 2024
1 parent e24dd94 commit dbc3b45
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 14 deletions.
10 changes: 10 additions & 0 deletions packages/oas/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/).
Expand Down
4 changes: 4 additions & 0 deletions packages/oas/src/converter/Converter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { OpenAPI, Request } from '@har-sdk/core';

export interface ConverterOptions {
skipAcceptHeaderInference?: boolean;
}

export interface Converter {
convert(collection: OpenAPI.Document): Promise<Request[]>;
}
16 changes: 11 additions & 5 deletions packages/oas/src/converter/parts/SubConverterFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -63,15 +67,17 @@ export class SubConverterFactory {
spec: OpenAPI.Document,
oas2Ctor: new (
spec: OpenAPIV2.Document,
sampler: Sampler
sampler: Sampler,
options: ConverterOptions
) => SubConverter<unknown>,
oas3Ctor: new (
spec: OpenAPIV3.Document,
sampler: Sampler
sampler: Sampler,
options: ConverterOptions
) => SubConverter<unknown>
) {
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);
}
}
14 changes: 11 additions & 3 deletions packages/oas/src/converter/parts/headers/HeadersConverter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ConverterOptions } from '../../Converter';
import { OperationObject, ParameterObject } from '../../../types';
import {
filterLocationParams,
Expand All @@ -18,7 +19,8 @@ export abstract class HeadersConverter<T extends OpenAPI.Document>

protected constructor(
private readonly spec: T,
private readonly sampler: Sampler
private readonly sampler: Sampler,
private readonly options: ConverterOptions
) {}

protected abstract createContentTypeHeaders(
Expand All @@ -44,9 +46,15 @@ export abstract class HeadersConverter<T extends OpenAPI.Document>
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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ConverterOptions } from '../../Converter';
import { LocationParam } from '../LocationParam';
import { Oas2ValueSerializer } from '../Oas2ValueSerializer';
import { Sampler } from '../../Sampler';
Expand All @@ -9,8 +10,12 @@ export class Oas2HeadersConverter extends HeadersConverter<OpenAPIV2.Document> {
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);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ConverterOptions } from '../../Converter';
import { isObject } from '../../../utils';
import { LocationParam } from '../LocationParam';
import { Sampler } from '../../Sampler';
Expand All @@ -8,8 +9,12 @@ import { Header, OpenAPIV3 } from '@har-sdk/core';
export class Oas3HeadersConverter extends HeadersConverter<OpenAPIV3.Document> {
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(
Expand Down
7 changes: 5 additions & 2 deletions packages/oas/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@ export * from './errors';

export const oas2har = (
collection: OpenAPI.Document,
options: { includeVendorExamples?: boolean } = {}
options: {
includeVendorExamples?: boolean;
skipAcceptHeaderInference?: boolean;
} = {}
): Promise<Request[]> => {
if (!collection) {
throw new TypeError('Please provide a valid OAS specification.');
}

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);

Expand Down
16 changes: 16 additions & 0 deletions packages/oas/tests/DefaultConverter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
});
Original file line number Diff line number Diff line change
@@ -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"
}
]
39 changes: 39 additions & 0 deletions packages/oas/tests/fixtures/params-header.skip-inference.oas.yaml
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit dbc3b45

Please sign in to comment.