-
-
Notifications
You must be signed in to change notification settings - Fork 66
Open
Labels
enhancementNew feature or requestNew feature or request
Description
With MSW exact matching is not a thing and apparently some BE's aswell like express:
mswjs/msw#1426
So we end up having the below happen in our code generated tool:
In the contract we don't care what the order of the paths are. The problem with this is that the order matters for the msw handlers.
If I import all handlers and run a mocked version of the site. Then handlers could intercept incorrect paths.
example:
This handler is ordered firstexport const getGetPermissionsForUserMockHandler = (overrideResponse?: UsersPermissionEntries | ((info: Parameters<Parameters<typeof http.get>[1]>[0]) => Promise<UsersPermissionEntries> | UsersPermissionEntries)) => { return http.get('*/api/authentication/v1/permissions/:userId', async (info) => {await delay(10); return new HttpResponse(JSON.stringify(overrideResponse !== undefined ? (typeof overrideResponse === "function" ? await overrideResponse(info) : overrideResponse) : getGetPermissionsForUserResponseMock()), { status: 200, headers: { 'Content-Type': 'application/json' } }) }) }
This handler is ordered second
export const getGetAllPermissionsForCurrentUserMockHandler = (overrideResponse?: UsersPermissionEntries | ((info: Parameters<Parameters<typeof http.get>[1]>[0]) => Promise<UsersPermissionEntries> | UsersPermissionEntries)) => { return http.get('*/api/authentication/v1/permissions/all', async (info) => {await delay(10); return new HttpResponse(JSON.stringify(overrideResponse !== undefined ? (typeof overrideResponse === "function" ? await overrideResponse(info) : overrideResponse) : getGetAllPermissionsForCurrentUserResponseMock()), { status: 200, headers: { 'Content-Type': 'application/json' } }) }) }
'permissions/:userId', is the same as 'permissions/*',
which is a problem when you have a separate endpoint for 'permissions/all'.This could be checked on build and ordered correctly:
/** * Reorders an array of URL path strings to prevent conflicts in routing libraries like MSW. * It sorts paths from most specific to least specific. * * The sorting rules are: * 1. Static segments are more specific than dynamic segments (e.g., 'all' comes before ':id'). * 2. Longer paths are generally more specific (e.g., '/users/all/details' comes before '/users/all'). * 3. Wildcards ('*') are the least specific. * * @param {string[]} paths - An array of path strings. * @returns {string[]} A new array with the paths sorted correctly. */ function reorderPathsForMsw(paths) { // Create a copy to avoid modifying the original array. const pathsCopy = [...paths]; const getSegmentSpecificity = (segment) => { if (segment.startsWith(':')) { return 1; // Dynamic parameter } if (segment === '*') { return 0; // Wildcard } return 2; // Static segment }; pathsCopy.sort((pathA, pathB) => { const segmentsA = pathA.split('/'); const segmentsB = pathB.split('/'); const minLength = Math.min(segmentsA.length, segmentsB.length); // Compare segments one by one for (let i = 0; i < minLength; i++) { const specificityA = getSegmentSpecificity(segmentsA[i]); const specificityB = getSegmentSpecificity(segmentsB[i]); if (specificityA !== specificityB) { // Higher specificity value should come first (sort descending) return specificityB - specificityA; } } // If all common segments are equally specific, the longer path is more specific // (e.g., '/users/all/details' vs '/users/all'). Sort descending by length. return segmentsB.length - segmentsA.length; }); return pathsCopy; } // --- Example Usage --- const conflictingPaths = [ '*/api/permissions/:userId', // Dynamic '/api/items/:itemId/details', // Dynamic but more specific '*/api/permissions/all', // Static '/api/items/latest', // Static '/api/items/:itemId', // Dynamic '*', // Wildcard ]; const orderedPaths = reorderPathsForMsw(conflictingPaths); console.log("Original Paths:"); console.log(conflictingPaths); /* [ "*/api/permissions/:userId", "/api/items/:itemId/details", "*/api/permissions/all", "/api/items/latest", "/api/items/:itemId", "*" ] */
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request