-
Notifications
You must be signed in to change notification settings - Fork 88
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
Add thenCallback method #2
Conversation
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.
Nice, I can definitely see how this would be useful, it's been in my plan for a little while but I haven't had any time to get to it, thanks for taking a look! I've added a few comments.
I'd like to ensure as much as possible that users don't need to use this though, so if there are cases where you're hitting this, do file an issue for the kind of handler you'd need to be built in, and we can look at that in future. E.g. in your example above we could have a json matcher for the body, and a json template responder like:
mockServer.get('/mocked-endpoint')
.withJsonMatching({ myVar: 'foo' })
.thenReplyWithTemplate(200, {
obj: '{{ body.myVar }}'
});
Exact template format needs thought, but you get the idea. A real handler like this can be built to run faster, we can make it work more consistently across platforms etc etc etc, but mainly it just avoids lots of people all reimplementing the same kinds of callbacks.
There's an extra bonus issue here too, that I suspect you'll hit when you try to add tests: this is quite tricky to implement in browsers. To do this well you need some kind of persistent connection, where the server can ask the browser what to do, and hold the request until it responds.
For this first step we can ignore that imo (wrap tests with nodeOnly(() => { ... })
, mention this in the docs, and make sure it throws something helpful if you try to use it in a browser). As long as we make sure the interface is something we can implement for browsers in future I'm fine with that, and I think this should work pretty easily as is. Nice work!
src/rules/handlers.ts
Outdated
@@ -27,7 +29,16 @@ export class SimpleHandlerData { | |||
|
|||
constructor( | |||
public status: number, | |||
public data?: string | |||
public data?: string, | |||
public headers?: http.OutgoingHttpHeaders |
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.
Looks like this is still on top of the headers PR - if you rebase on the latest master now these changes should disappear.
src/rules/partial-mock-rule.ts
Outdated
@@ -25,7 +25,8 @@ import { | |||
WildcardMatcherData | |||
} from "./matchers"; | |||
|
|||
import { SimpleHandlerData, PassThroughHandlerData } from "./handlers"; |
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.
Just fyi - partial mock rule has now been renamed to mock rule builder (git's conflict resolution should be able to magically handle that for you if you rebase), but there might be some conflicts because I've added jsdoc to all the methods through here. Should be easy to sort out, just a heads up.
src/rules/partial-mock-rule.ts
Outdated
}; | ||
|
||
return this.addRule(rule); | ||
} | ||
|
||
thenCallback(callback: Function): Promise<MockedEndpoint> { | ||
const rule: MockRuleData= { |
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.
A space before the =
here would be nice
test/integration/smoke-test.spec.ts
Outdated
|
||
let response = await fetch(server.urlFor("/mocked-endpoint")); | ||
|
||
expect(await response.headers.get("myHeader")).to.equal("foo"); |
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 is just the test for the headers - it'd be good to add one for thenCallback
too. There's now a separate handlers.spec.ts
for that (which I've moved this header test into).
src/rules/handlers.ts
Outdated
formData = await request.body.asFormData(); | ||
} catch (err) { | ||
formData = 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.
I don't think we should have all this body parsing logic here. It'd be nice to have the callback be given requests in basically the same format as request verification, and to share the code to do that. Right now that's over here:
We carefully avoid doing this before the handler normally, because if we're passing the request through we want to avoid reading the entire body into memory unnecessarily, and it means we can start matching & responding before the request body has even arrived. Here though that's less important of course, as most callbacks are going to do that anyway.
I think we should pull that logic out to turn this into request = waitForCompletedRequest(ongoingRequest)
, and put the shared logic to somewhere else (maybe in parse-body
?), so we can use it both here and in the above code in mock rule. That saves us a bunch of duplication, and makes the whole interface a bit more consistent.
src/rules/handlers.ts
Outdated
path: request.path, | ||
headers: request.headers, | ||
body: { buffer, text, json, formData } | ||
} |
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.
Why does the request need cleaning?
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 is a bad name for variables :) Will rename it
src/rules/handlers.ts
Outdated
try { | ||
ourResponse = await callback(cleanRequest); | ||
} catch (err) { | ||
throw err; |
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.
We should return a clear response with the error here, so it's easier to tell what's gone wrong. This error will appear in the server logs, but it's much easier to debug if we send it as a response (with a failing status code) so it appears in people's test failure message.
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.
What failing status code will be best in this case?
src/rules/handlers.ts
Outdated
throw err; | ||
} | ||
if (typeof ourResponse.body === 'object') { | ||
ourResponse.body = JSON.stringify(ourResponse.body); |
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 don't think we should automatically encode bodies like this. In future there might be other object structured bodies we'd like to support (like XML or something), and it's a bit too magic. I'd be fine with something a bit more explicit though, like a .json
property:
if (!!ourResponse.json) {
outResponse.headers['Content-Type'] = outResponse.headers['Content-Type'] || 'application/json';
ourResponse.body = JSON.stringify(ourResponse.json);
}
src/rules/handlers.ts
Outdated
readonly type: 'callback' = 'callback'; | ||
|
||
constructor( | ||
public callback: Function |
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.
We should be more specific here than just Function
, so we can check usage and properly document what you're allowed here. I'd suggest:
public callback: (request: CompletedRequest) => {
status?: number;
body?: string;
json?: any;
headers: {
[key: string]: string;
};
}
Just one more tip about why this feature is necessary. If you want to mock any SOAP service, than you have to implement logic in callback to receive different responses in different cases. |
@pimterry is there any blockers to merge this feature and update mockttp? |
No specific blockers, I just need time to sit down and review it again. Life's got a bit hectic, but I should be able to sort this this weekend. |
I've made a couple more tweaks here, but otherwise this all looks good to me now - I'll merge in a sec once the tests pass. |
thenCallback method allow to complete some operations with request before sending response.
example of usage:
Wait for your thought and suggestions
this PR should be merged after #1