Conversation
|
|
|
Error agent completed without reporting progress |
|
@malcolm-kee is attempting to deploy a commit to the Hey API Team on Vercel. A member of the Team first needs to authorize it. |
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #3570 +/- ##
==========================================
- Coverage 39.23% 38.62% -0.62%
==========================================
Files 513 530 +17
Lines 18773 19906 +1133
Branches 5567 6006 +439
==========================================
+ Hits 7365 7688 +323
- Misses 9218 9860 +642
- Partials 2190 2358 +168
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
7a46406 to
bc978a9
Compare
|
@malcolm-kee Before I go into it, two questions:
|
bc978a9 to
dedf542
Compare
|
@mrlubos I come up with the API design and AI was doing most of the implementations while I watch. Not final. I'm happy to iterate on this, just want some progress on this plugin. |
|
The diff is big is mostly because of the tests and snapshots. |
dedf542 to
c7133e3
Compare
@hey-api/codegen-core
@hey-api/json-schema-ref-parser
@hey-api/nuxt
@hey-api/openapi-ts
@hey-api/shared
@hey-api/types
@hey-api/vite-plugin
commit: |
c7133e3 to
b119b40
Compare
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/msw/response-example/msw.gen.ts
Outdated
Show resolved
Hide resolved
|
I did some refactoring/enhancements:
|
|
@mrlubos I manually validated the code and made some refactoring. It would be great if you can provide some feedbacks, especially on the API. |
ac3c300 to
b34b5bd
Compare
|
More revision:
|
b34b5bd to
9b8e509
Compare
|
Added |
9b8e509 to
1963185
Compare
|
Change plugin options from |
e3e18f0 to
7bb2ae4
Compare
|
Ideas on how to continue enhancing this PR, in case anyone want to take over this, since I might not be free to iterate on this: Implement
|
|
@malcolm-kee Which prior art did you consider when designing this plugin? In other words, how did you come up with the |
|
@mrlubos that's a good question. I think I was influenced by some Java library that I've been reading recently, which I think is not a good reference. 😆 I'm happy to change to other APIs. |
7d6b2f4 to
e337675
Compare
Implemented |
@malcolm-kee Have you used any other generators in the past? Did you write MSW by hand? What would be helpful is noting what works and what doesn't work in other solutions. I'm going to play with this pull request as well and compare. More context: |
|
I did write MSW by hand, and that's why I actually implemented this API for a private project a while ago, by parsing the OpenAPI spec and use ts-morph to read the output file of hey-api/typescript. That code was pretty ugly because I also did some logic to generate fake data. Is there any chance we can publish this as experimental or with |
|
I didn't try others as they wasn't what I really wanted, because the pain point wasn't just about the fake data, it was also the need to look at the implementation details of each API call code generated by hey api, and translate it to how to define them for MSW. |
mrlubos
left a comment
There was a problem hiding this comment.
@malcolm-kee couple smaller ones
Yes. I won't do another release for a while but after it's in main it will be under next |
@malcolm-kee can you show me an example or explain more? In the past I'd always tell people they can use another generator for MSW with Hey API for the rest. Are you saying that's not possible/painful? |
|
My two cents: I find the |
I did some homework, here is what I found: OrvalThis is actually very similar to what we have now, the API is very similar. But it doesn't make sense to use it in conjunction of hey-api, since Orval is like a replacement of Hey API. import { HttpResponse, delay, http } from 'msw';
import type { RequestHandlerOptions } from 'msw';
export const getShowPetByIdMockHandler = (
overrideResponse?:
| Pet
| ((info: Parameters<Parameters<typeof http.get>[1]>[0]) => Promise<Pet> | Pet),
options?: RequestHandlerOptions,
) => {
return http.get('*/pets/:petId', async (info) => {
await delay(1000);
return HttpResponse.json(
overrideResponse !== undefined
? typeof overrideResponse === 'function'
? await overrideResponse(info)
: overrideResponse
: getShowPetByIdResponseMock(),
{ status: 200 },
);
}, options);
};
|
|
There are a few weaknesses with the Orval supports:
but I like the idea of using I think @kettanaito point is valid, so I think we could do import { setupServer } from "msw/node";
import { createMswHandlerFactory } from "./msw.gen";
const mocks = createMswHandlerFactory({})
const server = setupServer(
...mocks.getAllMocks(),
mocks.getPetByIdMock({ status: 200, result: { id: 1, name: "Fido", photoUrls: [] } }),
);The naming clashing is avoided with
In fact, to be consistent with other client code, import { setupServer } from "msw/node";
import { getAllMocks, getPetByIdMock } from "./msw.gen";
const server = setupServer(
...getAllMocks(),
getPetByIdMock({ status: 200, result: { id: 1, name: "Fido", photoUrls: [] } }),
);Those default mock factory would be binded with |
|
@malcolm-kee did you see my code comments? |
88bba39 to
863c7e2
Compare
|
I've made a few more improvements, other than the one mentioned here:
|
|
@malcolm-kee I think that's all for now! I'll review this pull request later and potentially make changes to the plugin, but as far as the initial functionality is concerned, this looks good! |
3ef2694 to
75076ac
Compare
|
Some ideas to explore, probably not going to implement it in this PR:
|
75076ac to
c4334b5
Compare
|
Fix another edge case: In
If the OpenAPI spec specifies the former first, it would shadow the latter. This scenario is raised in Orval repo, so let's just fix it. |
bd35f3d to
dcb4bb9
Compare
|
More edge cases to be fixed. MSW actually has support of supports of different content type:
I think we should map to those field whenever we can. This wouldn't be a blocker, user can always overwrite the entire handler and returns with those MSW helpers, but it would be great if we can do them correctly in the default handler. |
dcb4bb9 to
8d0e313
Compare
|
Simplify types - Only include |
|
I think there are still gap in the API. Right now the generated code use the 'dominant' response to determine if it should use Again, this is not a blocker because users always can provide custom function, but the type we're providing may be misleading. |

Closes #1486
Summary
Implement
mswplugin that generates amsw.gen.tsfile with type-safe mock handler factories from OpenAPI specs. Each operation is exported as a named handler creator (<operationId>Mock) with a wildcard base URL, plus agetAllMockshelper to generate handlers for all operations at once. AcreateMswHandlerFactoryfunction is also exported for custom base URL binding.Important
Even though many expect fake data generation is part of this plugin, that probably overlaps with faker plugin. The only mock data handled by this plugin at the moment is the
exampledefined in the OpenAPI spec.API Design
Configuration
Usage
Individual handler exports (wildcard base URL)
Handler options
MSW handler options can be passed as a second argument:
Custom base URL (
createMswHandlerFactory)All handlers (
getAllMocks)Design decisions
Why
<operationId>Mocknaming? — AppendingMockavoids naming collisions with other generated artifacts (types, SDK functions) while keeping the handler clearly associated with its operation.Why both individual exports and
createMswHandlerFactory? — Individual exports use a wildcard (*) base URL for zero-config convenience. The factory function allows binding to a specific base URL when needed (e.g. integration tests against a specific server).Why
valueSourcesinstead ofexample: boolean? — Extensible for future sources (e.g.['example', 'faker']when faker plugin is ready).onMissingMock— Operations that require a response argument (no default example) are either skipped ('skip') or return a 501 ('error'). Overrides always take precedence.Handler creator signatures
{ result, status? } | ToResponseUnion<Responses> | HttpResponseResolver<PathParams, Body>{ result, status? } | ToResponseUnion<Responses> | HttpResponseResolver<PathParams, Body>HttpResponseResolver<PathParams, Body>* Optional if the spec defines an
examplefor the dominant response.Response method selection
application/jsonHttpResponse.json()text/*HttpResponse.text()binary/octet-streamnew HttpResponse()new HttpResponse(null)When multiple 2xx responses exist, the dominant one is chosen by priority: json > text > binary > void.
Known limitations
HttpResponseResolverto avoid MSW'sDefaultBodyTypeconstraint issues with union/void response types