Skip to content

Commit

Permalink
Case insensitive response header (#72)
Browse files Browse the repository at this point in the history
* Make response header validation cas-insensitive

* Added jest config files and dependencies for debuging spec files

* Added changeset

* Ran prettier

* Remove reflect-metadata dependency

* Lowercase header name to be validated

* Move jest config from package.json to jest.config

* prettier formatting

* Simplify tests with toThrowErrorMatchingSnapshot

* Change parameter name to fix duplicate name scope warning

* Simplify no error expectation. Formatting

* remove jest.setup.js

* Remove setupFilesAfterEnv prop

* prettier

* Remove package-lock.json

Co-authored-by: Chris Olson <chris.olson@dat.com>
  • Loading branch information
crizo23 and Chris Olson authored Jan 27, 2021
1 parent 2fb84d5 commit 48fa398
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 26 deletions.
5 changes: 5 additions & 0 deletions .changeset/nice-terms-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'oas3-chow-chow': minor
---

Make response header name validation case-insensitive
5 changes: 5 additions & 0 deletions __test__/__snapshots__/response.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Response validateResponseByOperationId should fail if header value is invalid 1`] = `"ResponseValidationError: Schema validation error"`;

exports[`Response validateResponseByPath should fail if header value is invalid 1`] = `"ResponseValidationError: Schema validation error"`;
2 changes: 1 addition & 1 deletion __test__/fixtures/response.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
"/header": {
"get": {
"summary": "Info for a specific pet",
"operationId": "showHeader",
"operationId": "getHeader",
"tags": ["pets"],
"parameters": [],
"responses": {
Expand Down
56 changes: 49 additions & 7 deletions __test__/response.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,20 +197,62 @@ describe('Response', () => {
}).toThrow();
});

it('should fail if header is invalid', () => {
it('validateResponseByPath should fail if header value is invalid', () => {
const responseMeta: ResponseMeta = {
status: 200,
header: {
'content-type': 'application/json',
version: [1, 2],
},
};
expect(() => {
chowchow.validateResponseByPath('/header', 'get', responseMeta);
}).toThrow();
expect(() => {
chowchow.validateResponseByOperationId('getHeader', responseMeta);
}).toThrow();

expect(() =>
chowchow.validateResponseByPath('/header', 'get', responseMeta)
).toThrowErrorMatchingSnapshot();
});

it('validateResponseByOperationId should fail if header value is invalid', () => {
const responseMeta: ResponseMeta = {
status: 200,
header: {
'content-type': 'application/json',
version: [1, 2],
},
};

expect(() =>
chowchow.validateResponseByOperationId('getHeader', responseMeta)
).toThrowErrorMatchingSnapshot();
});

it('should succeed if header case is different than spec', () => {
const lowercaseHeaderName = 'version';
const responseMeta: ResponseMeta = {
status: 200,
header: {
[lowercaseHeaderName]: '1',
},
};

// test validateResponseByPath
expect(() =>
chowchow.validateResponseByPath('/header', 'get', responseMeta)
).not.toThrow();
});

it('should succeed if header case is same as spec', () => {
const uppercaseHeaderName = 'Version';
const responseMeta: ResponseMeta = {
status: 200,
header: {
[uppercaseHeaderName]: '1',
},
};

// test validateResponseByPath
expect(() =>
chowchow.validateResponseByPath('/header', 'get', responseMeta)
).not.toThrow();
});

it('should fail if required header is empty', () => {
Expand Down
25 changes: 25 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module.exports = {
preset: 'ts-jest',
globals: {
'ts-jest': {
tsconfig: 'tsconfig.json'
}
},
roots: [
"<rootDir>/src",
"<rootDir>__test__"
],
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
moduleFileExtensions: [
'ts',
'tsx',
'js',
'jsx',
'json',
'node'
],
testEnvironment: 'node',
transform: {
"^.+\\.tsx?$": "ts-jest"
}
};
14 changes: 0 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,6 @@
"prepublishOnly": "rm -rf lib && tsc",
"release": "yarn prepublishOnly && changeset publish"
},
"jest": {
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
},
"prettier": {
"singleQuote": true
},
Expand Down
19 changes: 15 additions & 4 deletions src/compiler/CompiledResponseHeader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ export default class CompiledResponseHeader {
if (this.ignoreHeaders.includes(name)) {
continue;
}

const headerNameLower = name.toLowerCase();
const header = headers[name] as HeaderObject;

if (header.schema) {
this.headerSchema.properties![name] = header.schema;
this.headerSchema.properties![headerNameLower] = header.schema;
}

if (header.required) {
this.headerSchema.required!.push(name);
this.headerSchema.required!.push(headerNameLower);
}
}
this.compiledSchema = new CompiledSchema(
Expand All @@ -36,9 +38,18 @@ export default class CompiledResponseHeader {
);
}

public validate(value: any = {}) {
public validate(header: any = {}) {
try {
this.compiledSchema.validate(value);
// Before validation, lowercase the header name, since header name is also lowercased in compiledSchema
const newHeader = Object.entries(header).reduce(
(newObject: any, [key, value]) => {
newObject[key.toLowerCase()] = value;
return newObject;
},
{}
);

this.compiledSchema.validate(newHeader);
} catch (e) {
throw new ChowError('Schema validation error', {
in: 'response-header',
Expand Down

0 comments on commit 48fa398

Please sign in to comment.