Skip to content

Commit

Permalink
fix(oas): use application/json when consumes media-type configura…
Browse files Browse the repository at this point in the history
…tion missing (#211)

closes #210
  • Loading branch information
ostridm committed Sep 21, 2023
1 parent aead75c commit d7a3cbe
Show file tree
Hide file tree
Showing 21 changed files with 1,834 additions and 37 deletions.
38 changes: 38 additions & 0 deletions packages/oas/src/converter/parts/Oas2MediaTypesResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { OpenAPIV2 } from '@har-sdk/core';

export class Oas2MediaTypesResolver {
private readonly DEFAULT_CONSUME_MEDIA_TYPE: OpenAPIV2.MimeTypes = [
'application/json'
];
private readonly DEFAULT_PRODUCE_MEDIA_TYPE: OpenAPIV2.MimeTypes = ['*/*'];

constructor(private readonly spec: OpenAPIV2.Document) {}

public resolveToConsume(operation: OpenAPIV2.OperationObject) {
return this.resolve(operation, 'consumes', this.DEFAULT_CONSUME_MEDIA_TYPE);
}

public resolveToProduce(operation: OpenAPIV2.OperationObject) {
return this.resolve(operation, 'produces', this.DEFAULT_PRODUCE_MEDIA_TYPE);
}

private resolve(
operation: OpenAPIV2.OperationObject,
node: 'consumes' | 'produces',
defaultMediaTypes: OpenAPIV2.MimeTypes
): OpenAPIV2.MimeTypes {
let mediaTypes: OpenAPIV2.MimeTypes;

if (operation[node]?.length) {
mediaTypes = operation[node];
} else if (this.spec[node]?.length) {
mediaTypes = this.spec[node];
}

mediaTypes = mediaTypes
?.map((mediaType) => mediaType?.trim())
.filter(Boolean);

return mediaTypes?.length ? mediaTypes : defaultMediaTypes;
}
}
13 changes: 12 additions & 1 deletion packages/oas/src/converter/parts/headers/HeadersConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import jsonPointer from 'json-pointer';
export abstract class HeadersConverter<T extends OpenAPI.Document>
implements SubConverter<Header[]>
{
private readonly CONTENT_TYPE_METHODS = ['post', 'put', 'patch', 'delete'];
private readonly CONTENT_TYPE_HEADER = 'content-type';

protected constructor(
private readonly spec: T,
private readonly sampler: Sampler
Expand All @@ -32,7 +35,15 @@ export abstract class HeadersConverter<T extends OpenAPI.Document>
const headers: Header[] = [];
const pathObj = getOperation(this.spec, path, method);

headers.push(...this.createContentTypeHeaders(pathObj));
if (
this.CONTENT_TYPE_METHODS.includes(method.toLowerCase()) &&
!headers.some(
(header) => header.name.toLowerCase() === this.CONTENT_TYPE_HEADER
)
) {
headers.push(...this.createContentTypeHeaders(pathObj));
}

headers.push(...this.createAcceptHeaders(pathObj));

headers.push(...this.parseFromParams(path, method));
Expand Down
19 changes: 15 additions & 4 deletions packages/oas/src/converter/parts/headers/Oas2HeadersConveter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,34 @@ import { LocationParam } from '../LocationParam';
import { Oas2ValueSerializer } from '../Oas2ValueSerializer';
import { Sampler } from '../../Sampler';
import { HeadersConverter } from './HeadersConverter';
import { Oas2MediaTypesResolver } from '../Oas2MediaTypesResolver';
import { Header, OpenAPIV2 } from '@har-sdk/core';

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);
this.oas2MediaTypeResolver = new Oas2MediaTypesResolver(spec);
}

protected createContentTypeHeaders(
pathObj: OpenAPIV2.OperationObject
operation: OpenAPIV2.OperationObject
): Header[] {
return this.createHeaders('content-type', pathObj.consumes);
return this.createHeaders(
'content-type',
this.oas2MediaTypeResolver.resolveToConsume(operation)
);
}

protected createAcceptHeaders(pathObj: OpenAPIV2.OperationObject): Header[] {
return this.createHeaders('accept', pathObj.produces);
protected createAcceptHeaders(
operation: OpenAPIV2.OperationObject
): Header[] {
return this.createHeaders(
'accept',
this.oas2MediaTypeResolver.resolveToProduce(operation)
);
}

protected convertHeaderParam(
Expand Down
16 changes: 6 additions & 10 deletions packages/oas/src/converter/parts/postdata/Oas2BodyConverter.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { BodyConverter } from './BodyConverter';
import type { Sampler } from '../../Sampler';
import { filterLocationParams, getParameters, isOASV2 } from '../../../utils';
import { filterLocationParams, getParameters } from '../../../utils';
import { Oas2MediaTypesResolver } from '../Oas2MediaTypesResolver';
import type { OpenAPIV2, PostData } from '@har-sdk/core';

export class Oas2BodyConverter extends BodyConverter<OpenAPIV2.Document> {
private readonly oas2MediaTypeResolver: Oas2MediaTypesResolver;

constructor(spec: OpenAPIV2.Document, sampler: Sampler) {
super(spec, sampler);
this.oas2MediaTypeResolver = new Oas2MediaTypesResolver(spec);
}

public convert(path: string, method: string): PostData | null {
Expand Down Expand Up @@ -35,17 +39,9 @@ export class Oas2BodyConverter extends BodyConverter<OpenAPIV2.Document> {
protected getContentType(path: string, method: string): string | undefined {
const operation = this.spec.paths[path][method];

let consumes: OpenAPIV2.MimeTypes;

if (operation.consumes?.length) {
consumes = operation.consumes;
} else if (isOASV2(this.spec) && this.spec.consumes?.length) {
consumes = this.spec.consumes;
}

return this.sampler.sample({
type: 'array',
examples: consumes
examples: this.oas2MediaTypeResolver.resolveToConsume(operation)
});
}

Expand Down
23 changes: 23 additions & 0 deletions packages/oas/tests/DefaultConverter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,29 @@ describe('DefaultConverter', () => {
expected: 'empty-schema.swagger.result.json',
message: 'should correctly handle empty schemas (swagger)'
},
{
input: 'consumes-produces-missing.swagger.yaml',
expected: 'consumes-produces-missing.swagger.result.json',
message: 'should correctly handle missing consumes/produces (swagger)'
},
{
input: 'consumes-produces-override.swagger.yaml',
expected: 'consumes-produces-override.swagger.result.json',
message:
'should correctly handle override consumes/produces to empty (swagger)'
},
{
input: 'consumes-produces-root.swagger.yaml',
expected: 'consumes-produces-root.swagger.result.json',
message:
'should correctly handle root level only consumes/produces (swagger)'
},
{
input: 'consumes-produces-operation.swagger.yaml',
expected: 'consumes-produces-operation.swagger.result.json',
message:
'should correctly handle operation level only consumes/produces (swagger)'
},
{
input: 'empty-schema.oas.yaml',
expected: 'empty-schema.oas.result.json',
Expand Down
16 changes: 16 additions & 0 deletions packages/oas/tests/fixtures/binary-body.swagger.result.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
{
"name": "content-type",
"value": "image/jpeg"
},
{
"name": "accept",
"value": "*/*"
}
],
"headersSize": 0,
Expand All @@ -25,6 +29,10 @@
{
"name": "content-type",
"value": "image/png"
},
{
"name": "accept",
"value": "*/*"
}
],
"headersSize": 0,
Expand All @@ -44,6 +52,10 @@
{
"name": "content-type",
"value": "image/ico"
},
{
"name": "accept",
"value": "*/*"
}
],
"headersSize": 0,
Expand All @@ -63,6 +75,10 @@
{
"name": "content-type",
"value": "multipart/form-data"
},
{
"name": "accept",
"value": "*/*"
}
],
"headersSize": 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"queryString": [],
"url": "https://brokencrystals.com/api/v1/dummy1",
"method": "POST",
"headers": [
{
"name": "content-type",
"value": "application/json"
},
{
"name": "accept",
"value": "*/*"
}
],
"httpVersion": "HTTP/1.1",
"cookies": [],
"headersSize": 0,
"bodySize": 0,
"postData": {
"mimeType": "application/json",
"text": "{\"id\":42}"
}
}
]
26 changes: 26 additions & 0 deletions packages/oas/tests/fixtures/consumes-produces-missing.swagger.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
swagger: '2.0'
info:
title: Future is Bright
version: 1.0.0
host: brokencrystals.com
basePath: /api/v1
schemes:
- https
paths:
'/dummy1':
post:
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/requestBody'
responses:
200:
description: successful operation
schema: {}
definitions:
requestBody:
type: object
properties:
id:
type: integer
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"bodySize": 0,
"cookies": [],
"headers": [
{
"name": "content-type",
"value": "application/xml"
},
{
"name": "accept",
"value": "application/xml"
}
],
"headersSize": 0,
"httpVersion": "HTTP/1.1",
"method": "POST",
"postData": {
"mimeType": "application/xml",
"text": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<requestBody>\n\t<id>42</id>\n</requestBody>"
},
"queryString": [],
"url": "https://petstore.swagger.io/v2/dummy1"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
swagger: '2.0'
info:
title: Swagger Petstore
version: 1.0.0
host: petstore.swagger.io
basePath: /v2
schemes:
- https
paths:
'/dummy1':
post:
consumes:
- application/xml
produces:
- application/xml
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/requestBody'
responses:
200:
description: successful operation
schema: {}
definitions:
requestBody:
type: object
properties:
id:
type: integer
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[
{
"bodySize": 0,
"cookies": [],
"headers": [
{
"name": "content-type",
"value": "application/json"
},
{
"name": "accept",
"value": "*/*"
}
],
"headersSize": 0,
"httpVersion": "HTTP/1.1",
"method": "POST",
"postData": {
"mimeType": "application/json",
"text": "{\"id\":42}"
},
"queryString": [],
"url": "https://petstore.swagger.io/v2/dummy1"
},
{
"bodySize": 0,
"cookies": [],
"headers": [
{
"name": "content-type",
"value": "multipart/form-data"
},
{
"name": "accept",
"value": "application/vnd.api+json"
}
],
"headersSize": 0,
"httpVersion": "HTTP/1.1",
"method": "PUT",
"postData": {
"mimeType": "multipart/form-data; boundary=956888039105887155673143",
"text": "--956888039105887155673143\r\nContent-Disposition: form-data; name=\"id\"\r\n\r\n42\r\n--956888039105887155673143--"
},
"queryString": [],
"url": "https://petstore.swagger.io/v2/dummy1"
}
]
Loading

0 comments on commit d7a3cbe

Please sign in to comment.