UNPKG

@typespec/ts-http-runtime

Version:

Isomorphic client library for making HTTP requests in node.js and browser.

152 lines 6.51 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. function isQueryParameterWithOptions(x) { const value = x.value; return (value !== undefined && value.toString !== undefined && typeof value.toString === "function"); } /** * Builds the request url, filling in query and path parameters * @param endpoint - base url which can be a template url * @param routePath - path to append to the endpoint * @param pathParameters - values of the path parameters * @param options - request parameters including query parameters * @returns a full url with path and query parameters */ export function buildRequestUrl(endpoint, routePath, pathParameters, options = {}) { if (routePath.startsWith("https://") || routePath.startsWith("http://")) { return routePath; } endpoint = buildBaseUrl(endpoint, options); routePath = buildRoutePath(routePath, pathParameters, options); const requestUrl = appendQueryParams(`${endpoint}/${routePath}`, options); const url = new URL(requestUrl); return (url .toString() // Remove double forward slashes .replace(/([^:]\/)\/+/g, "$1")); } function getQueryParamValue(key, allowReserved, style, param) { let separator; if (style === "pipeDelimited") { separator = "|"; } else if (style === "spaceDelimited") { separator = "%20"; } else { separator = ","; } let paramValues; if (Array.isArray(param)) { paramValues = param; } else if (typeof param === "object" && param.toString === Object.prototype.toString) { // If the parameter is an object without a custom toString implementation (e.g. a Date), // then we should deconstruct the object into an array [key1, value1, key2, value2, ...]. paramValues = Object.entries(param).flat(); } else { paramValues = [param]; } const value = paramValues .map((p) => { if (p === null || p === undefined) { return ""; } if (!p.toString || typeof p.toString !== "function") { throw new Error(`Query parameters must be able to be represented as string, ${key} can't`); } const rawValue = p.toISOString !== undefined ? p.toISOString() : p.toString(); return allowReserved ? rawValue : encodeURIComponent(rawValue); }) .join(separator); return `${allowReserved ? key : encodeURIComponent(key)}=${value}`; } function appendQueryParams(url, options = {}) { var _a, _b, _c, _d; if (!options.queryParameters) { return url; } const parsedUrl = new URL(url); const queryParams = options.queryParameters; const paramStrings = []; for (const key of Object.keys(queryParams)) { const param = queryParams[key]; if (param === undefined || param === null) { continue; } const hasMetadata = isQueryParameterWithOptions(param); const rawValue = hasMetadata ? param.value : param; const explode = hasMetadata ? ((_a = param.explode) !== null && _a !== void 0 ? _a : false) : false; const style = hasMetadata && param.style ? param.style : "form"; if (explode) { if (Array.isArray(rawValue)) { for (const item of rawValue) { paramStrings.push(getQueryParamValue(key, (_b = options.skipUrlEncoding) !== null && _b !== void 0 ? _b : false, style, item)); } } else if (typeof rawValue === "object") { // For object explode, the name of the query parameter is ignored and we use the object key instead for (const [actualKey, value] of Object.entries(rawValue)) { paramStrings.push(getQueryParamValue(actualKey, (_c = options.skipUrlEncoding) !== null && _c !== void 0 ? _c : false, style, value)); } } else { // Explode doesn't really make sense for primitives throw new Error("explode can only be set to true for objects and arrays"); } } else { paramStrings.push(getQueryParamValue(key, (_d = options.skipUrlEncoding) !== null && _d !== void 0 ? _d : false, style, rawValue)); } } if (parsedUrl.search !== "") { parsedUrl.search += "&"; } parsedUrl.search += paramStrings.join("&"); return parsedUrl.toString(); } export function buildBaseUrl(endpoint, options) { var _a; if (!options.pathParameters) { return endpoint; } const pathParams = options.pathParameters; for (const [key, param] of Object.entries(pathParams)) { if (param === undefined || param === null) { throw new Error(`Path parameters ${key} must not be undefined or null`); } if (!param.toString || typeof param.toString !== "function") { throw new Error(`Path parameters must be able to be represented as string, ${key} can't`); } let value = param.toISOString !== undefined ? param.toISOString() : String(param); if (!options.skipUrlEncoding) { value = encodeURIComponent(param); } endpoint = (_a = replaceAll(endpoint, `{${key}}`, value)) !== null && _a !== void 0 ? _a : ""; } return endpoint; } function buildRoutePath(routePath, pathParameters, options = {}) { var _a; for (const pathParam of pathParameters) { const allowReserved = typeof pathParam === "object" && ((_a = pathParam.allowReserved) !== null && _a !== void 0 ? _a : false); let value = typeof pathParam === "object" ? pathParam.value : pathParam; if (!options.skipUrlEncoding && !allowReserved) { value = encodeURIComponent(value); } routePath = routePath.replace(/\{[\w-]+\}/, String(value)); } return routePath; } /** * Replace all of the instances of searchValue in value with the provided replaceValue. * @param value - The value to search and replace in. * @param searchValue - The value to search for in the value argument. * @param replaceValue - The value to replace searchValue with in the value argument. * @returns The value where each instance of searchValue was replaced with replacedValue. */ export function replaceAll(value, searchValue, replaceValue) { return !value || !searchValue ? value : value.split(searchValue).join(replaceValue || ""); } //# sourceMappingURL=urlHelpers.js.map