Skip to content

Commit 070cebb

Browse files
committed
fix: add dynamic query params support
1 parent 767c542 commit 070cebb

File tree

1 file changed

+107
-15
lines changed

1 file changed

+107
-15
lines changed

index.js

Lines changed: 107 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,72 @@ const matchDynamicPath = (pattern, path) => {
703703
return { match: false };
704704
};
705705

706+
const parseQueryString = (queryString) => {
707+
// Parse query string into key-value pairs
708+
const params = {};
709+
if (!queryString) return params;
710+
711+
const pairs = queryString.split('&');
712+
for (const pair of pairs) {
713+
const equalIndex = pair.indexOf('=');
714+
if (equalIndex === -1) {
715+
// No = sign, treat as key with empty value
716+
const key = decodeURIComponent(pair);
717+
if (key) {
718+
params[key] = '';
719+
}
720+
} else {
721+
const key = decodeURIComponent(pair.substring(0, equalIndex));
722+
const value = decodeURIComponent(pair.substring(equalIndex + 1));
723+
if (key) {
724+
params[key] = value;
725+
}
726+
}
727+
}
728+
return params;
729+
};
730+
731+
const matchDynamicQuery = (patternQuery, requestQuery) => {
732+
// Match query strings with dynamic variables
733+
// Pattern: start_date={start}&end_date={end}
734+
// Request: start_date=test-value&end_date=test-value2
735+
736+
const patternParams = parseQueryString(patternQuery);
737+
const requestParams = parseQueryString(requestQuery);
738+
739+
// Check if all pattern keys exist in request
740+
for (const [key, patternValue] of Object.entries(patternParams)) {
741+
if (!(key in requestParams)) {
742+
return { match: false };
743+
}
744+
745+
// If pattern value is a dynamic variable {var}, it matches any value
746+
if (patternValue.startsWith('{') && patternValue.endsWith('}')) {
747+
// This is a dynamic variable, it matches any value
748+
continue;
749+
}
750+
751+
// Otherwise, values must match exactly
752+
if (patternValue !== requestParams[key]) {
753+
return { match: false };
754+
}
755+
}
756+
757+
// Check if request has extra params not in pattern (optional - you might want to allow this)
758+
// For now, we'll allow extra params in the request
759+
760+
// Extract dynamic parameter values
761+
const params = {};
762+
for (const [key, patternValue] of Object.entries(patternParams)) {
763+
if (patternValue.startsWith('{') && patternValue.endsWith('}')) {
764+
const paramName = patternValue.slice(1, -1);
765+
params[paramName] = requestParams[key];
766+
}
767+
}
768+
769+
return { match: true, params };
770+
};
771+
706772
// Dynamic mock routing - this should be last to catch all routes
707773
app.use((req, res, next) => {
708774
// Skip static files and management API routes
@@ -739,30 +805,56 @@ app.use((req, res, next) => {
739805
// If no exact match, try dynamic path matching (without query params for path matching)
740806
if (!endpoint) {
741807
const pathWithoutQuery = requestPath.split('?')[0];
808+
const reqQuery = requestPath.includes('?') ? requestPath.split('?')[1] : '';
809+
742810
endpoint = endpoints.find(ep => {
743811
if (ep.method !== method) return false;
744812

745813
const epPathWithoutQuery = ep.path.split('?')[0];
814+
const epQuery = ep.path.includes('?') ? ep.path.split('?')[1] : '';
815+
816+
// Check if path has dynamic segments
817+
const pathHasDynamic = epPathWithoutQuery.includes('{');
818+
819+
// Match path part
820+
let pathMatch = false;
821+
let pathParams = {};
746822

747-
// Check if this endpoint has dynamic segments (contains {})
748-
if (epPathWithoutQuery.includes('{')) {
823+
if (pathHasDynamic) {
749824
const matchResult = matchDynamicPath(epPathWithoutQuery, pathWithoutQuery);
750825
if (matchResult.match) {
751-
// Check if query params also match
752-
const epQuery = ep.path.includes('?') ? ep.path.split('?')[1] : '';
753-
const reqQuery = requestPath.includes('?') ? requestPath.split('?')[1] : '';
754-
755-
// If endpoint has query params, they must match exactly
756-
if (epQuery && epQuery !== reqQuery) {
757-
return false;
758-
}
759-
760-
// Store the matched parameters in the request object for potential use
761-
req.dynamicParams = matchResult.params;
762-
return true;
826+
pathMatch = true;
827+
pathParams = matchResult.params;
828+
}
829+
} else if (epPathWithoutQuery === pathWithoutQuery) {
830+
pathMatch = true;
831+
}
832+
833+
if (!pathMatch) {
834+
return false;
835+
}
836+
837+
// Match query part
838+
if (epQuery && reqQuery) {
839+
// Both have query params - match them
840+
const queryMatch = matchDynamicQuery(epQuery, reqQuery);
841+
if (!queryMatch.match) {
842+
return false;
763843
}
844+
// Merge query params with path params
845+
req.dynamicParams = { ...pathParams, ...queryMatch.params };
846+
} else if (epQuery && !reqQuery) {
847+
// Endpoint expects query params but request doesn't have them
848+
return false;
849+
} else if (!epQuery && reqQuery) {
850+
// Endpoint doesn't have query params but request does - this is OK
851+
req.dynamicParams = pathParams;
852+
} else {
853+
// Neither has query params
854+
req.dynamicParams = pathParams;
764855
}
765-
return false;
856+
857+
return true;
766858
});
767859
}
768860

0 commit comments

Comments
 (0)