-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: make content type header case-insensitive #135
Conversation
@@ -124,7 +124,6 @@ describe('proxy', () => { | |||
...baseEvent, | |||
headers: { | |||
...baseEvent.headers, | |||
'content-type': undefined, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This key now fails the test because our validation schema expects the value to be exactly application/json
or application/x-www-form-urlencoded
. If this header is undefined I think we would want the proxy to throw an error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in TypeScript, setting a key to undefined using object spread syntax effectively removes the key from the resulting object.
So in this case this line is effectively deleting 'content-type' from the baseEvent
object, not leaving it in place with a value of undefined
. I think we do want to remove this key one way or another, otherwise baseEvent.headers
in this test will have key/value pairs for both content-type
and Content-Type
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😨 I see why you did this now! I did not realize content-type
was already in the baseEvent
object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I now understand the delete
thing you asked about. The problem with delete
is that it would screw up other tests that use the object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just tested this in a console and I'm not sure it's fully effective:
const originalObject = {
key1: 'value1',
key2: 'value2',
};
const modifiedObject = {
...originalObject,
key1: undefined,
};
console.log(modifiedObject);
Console output:
{ key1: undefined, key2: 'value2' }
So it may be necessary to use some variation of delete headers['content-type'];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found a another way. Better? Maybe.
const { 'content-type', ...headersWithoutContentType } = baseEvent.headers;
const event: APIGatewayProxyWithLambdaAuthorizerEvent<any> = {
...baseEvent,
headers: {
...headersWithoutContentType,
'Content-Type': 'application/json'
},
body: JSON.stringify(VALID_PUSH_PAYLOAD),
pathParameters: {
endpointId
}
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way you wrote the test is really the best way. The tests passed in my first attempt because I made the test wrong and implemented wrong. Now I implemented correctly and the test (with no change) passes!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a little more testing and I think the way you've rewritten the headersSchema function does require changes to the tests to get a truly valid result.
Right now the only reason Content-Type
applicaton/json
value is the final value in the modified object is it is accessed by the headersSchema method second, and thus its value overwrites the undefined
value for the content-type
key. I do not know if that order can always be relied upon.
Previously the way I was setting content-type
to undefined
works reliably because of this line:
const contentType = headersResult['content-type'] || headersResult['Content-Type'];
The content-type
will resolve to undefined, so Content-Type
is used instead, and this is a reliable comparison that won't change based on order the key/value pair is processed.
It's worth pointing out this will only ever be an edge case for our own tests because no request from GitHub will ever come over with multiple keys for content-type
, so the order they are processed won't ever matter, since it will only be one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So to confirm I have the correct understanding, I ran just that one Content-Type
test and logged the value of mapKeys(obj, (_, key) => key.toLowerCase())
inside the preprocess method, and got
{
accept: '*/*',
'content-type': 'application/json',
host: 'some-api.us-west-2.amazonaws.com'
}
So it is naturally sanitizing the object of any keys with values of undefined
. When zod performs the actual validation using
z.object({
'content-type': z.nativeEnum(CONTENT_TYPES)
})
it will reject 'content-type': undefined
, so if the test passes it means that it's not seeing that. With your change from #134, it may have allowed that since the schema was .optional()
In any case, I updated the test to be more clear so there's no ambiguity whatsoever, and so it mimics a real headers object that the proxy would actually receive.
@@ -61,6 +61,6 @@ export async function handler(event: APIGatewayProxyWithLambdaAuthorizerEvent<an | |||
} | |||
|
|||
console.error('An unknown error occurred.', error); | |||
return { statusCode: 500, body: 'Internal server error' }; | |||
return { statusCode: 500, body: `Internal server error: ${error}` }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's actually include the error thrown in the response! 😄
lambda/schema.ts
Outdated
message: 'Missing Content-Type header' | ||
} | ||
); | ||
.transform(obj => mapKeys(obj, (_, key) => key.toLowerCase())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This effectively calls .toLowerCase() on all the header keys prior to validating against the schema. So if GitHub changed the header to CONTENT-TYPE
it would still pass
🎉 This PR is included in version 2.0.7 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
📝 Description