diff --git a/src/CookieParameter.ts b/src/CookieParameter.ts index 6d53ebe..8a6ca4e 100644 --- a/src/CookieParameter.ts +++ b/src/CookieParameter.ts @@ -1,5 +1,5 @@ -import { PrimitiveType, ObjectType, ArrayType } from "./Types"; -import * as Core from "./Core"; +import { PrimitiveType, ObjectType, ArrayType, ParameterOfForm } from "./Types"; +import * as Guard from "./Guard"; export interface Parameter { value: PrimitiveType | ObjectType | ArrayType; @@ -9,7 +9,36 @@ export interface Parameter { export const generate = (key: string | number, params: Parameter): string | undefined => { if (params.style === "form") { - return Core.generateFormParamter(key, params); + return generateFormParamter(key, params); } return undefined; }; + +const generateFormParamter = (key: string | number, params: ParameterOfForm): string => { + if (Guard.isEmpty(params.value)) { + return `${key}=`; + } + if (Guard.isPrimitive(params.value)) { + return `${key}=${params.value}`; + } + if (Guard.isArray(params.value)) { + if (params.explode) { + return params.value.map(item => `${key}=${item}`).join(";"); + } else { + return `${key}=${params.value.join(",")}`; + } + } + if (Guard.isObject(params.value)) { + if (params.explode) { + return Object.entries(params.value) + .map(([k, v]) => `${k}=${v}`) + .join(";"); + } else { + const value = Object.entries(params.value) + .map(([k, v]) => `${k},${v}`) + .join(","); + return `${key}=${value}`; + } + } + return `${key}=`; +}; diff --git a/src/Core.ts b/src/Core.ts index bc36b20..2e22780 100644 --- a/src/Core.ts +++ b/src/Core.ts @@ -37,74 +37,121 @@ export const generateFromSimple = (key: string | number, params: ParameterOfSimp }; export const generateFormParamter = (key: string | number, params: ParameterOfForm): string => { + return generateFormParamterAsURLSearchParams(key, params).toString(); +}; + +export const generateFormParamterAsURLSearchParams = (key: string | number, params: ParameterOfForm): URLSearchParams => { + const queryParams = new URLSearchParams(); if (Guard.isEmpty(params.value)) { - return `${key}=`; + queryParams.append(key.toString(), ""); + return queryParams; } + if (Guard.isPrimitive(params.value)) { - return `${key}=${params.value}`; + queryParams.append(key.toString(), params.value?.toString() ?? ""); + return queryParams; } + if (Guard.isArray(params.value)) { if (params.explode) { - return params.value.map(item => `${key}=${item}`).join("&"); + params.value.map(item => { + queryParams.append(key.toString(), item.toString()); + }); } else { - return `${key}=${params.value.join(",")}`; + queryParams.append(key.toString(), params.value.join(",")); } + return queryParams; } + if (Guard.isObject(params.value)) { if (params.explode) { - return Object.entries(params.value) - .map(([k, v]) => `${k}=${v}`) - .join("&"); + Object.entries(params.value).map(([k, v]) => { + queryParams.append(k.toString(), v.toString()); + }); } else { const value = Object.entries(params.value) .map(([k, v]) => `${k},${v}`) .join(","); - return `${key}=${value}`; + queryParams.append(key.toString(), value); } + return queryParams; } - return `${key}=`; + + queryParams.append(key.toString(), ""); + return queryParams; }; export const generateSpaceDelimited = (key: string | number, params: ParameterOfSpaceDelimited): string | undefined => { + return generateSpaceDelimitedAsURLSearchParams(key, params)?.toString(); +}; + +export const generateSpaceDelimitedAsURLSearchParams = ( + key: string | number, + params: ParameterOfSpaceDelimited, +): URLSearchParams | undefined => { + const queryParams = new URLSearchParams(); if (Guard.isArray(params.value)) { - return encodeURIComponent(params.value.join(" ")); + queryParams.append(key.toString(), params.value.join(" ")); + return queryParams; } + if (Guard.isObject(params.value)) { const value = Object.entries(params.value) .map(([k, v]) => `${k} ${v}`) .join(" "); - return encodeURIComponent(value); + queryParams.append(key.toString(), value); + return queryParams; } + return undefined; }; export const generatePipeDelimitedParameter = (key: string | number, params: ParameterOfPipeDelimited): string | undefined => { + return generatePipeDelimitedParameterAsURLSearchParams(key, params)?.toString(); +}; + +export const generatePipeDelimitedParameterAsURLSearchParams = ( + key: string | number, + params: ParameterOfPipeDelimited, +): URLSearchParams | undefined => { + const queryParams = new URLSearchParams(); if (Guard.isArray(params.value)) { - return params.value.join("|"); + queryParams.append(key.toString(), params.value.join("|")); + return queryParams; } + if (Guard.isObject(params.value)) { const value = Object.entries(params.value) .map(([k, v]) => `${k}|${v}`) .join("|"); - return value; + queryParams.append(key.toString(), value); + return queryParams; } + return undefined; }; export const generateDeepObjectParameter = (key: string | number, params: ParameterOfDeepObject): string | undefined => { + return generateDeepObjectParameterAsURLSearchParams(key, params)?.toString(); +}; + +export const generateDeepObjectParameterAsURLSearchParams = ( + key: string | number, + params: ParameterOfDeepObject, +): URLSearchParams | undefined => { if (!Guard.isObject(params.value)) { return undefined; } + const queryParams = new URLSearchParams(); const flatObject = flatten(params.value); - return Object.entries(flatObject) - .map(([dotKeyName, primitiveValue]) => { - const nestedKey = dotKeyName - .split(".") - .map(k1 => `[${k1}]`) - .join(""); - return `${[key]}${nestedKey}=${primitiveValue}`; - }) - .join("&"); + Object.entries(flatObject).map(([dotKeyName, primitiveValue]) => { + const nestedKey = dotKeyName + .split(".") + .map(k1 => `[${k1}]`) + .join(""); + queryParams.append(`${key}${nestedKey}`, primitiveValue?.toString() ?? ""); + }); + return queryParams; }; export const generateFromMatrix = (key: string | number, params: ParameterOfMatrix): string | undefined => { diff --git a/src/QueryParameter.ts b/src/QueryParameter.ts index a20769c..937efff 100644 --- a/src/QueryParameter.ts +++ b/src/QueryParameter.ts @@ -18,3 +18,19 @@ export const generate = (key: string | number, params: Parameter): string | unde } return undefined; }; + +export const generateAsURLSearchParams = (key: string | number, params: Parameter): URLSearchParams | undefined => { + if (params.style === "form") { + return Core.generateFormParamterAsURLSearchParams(key, params); + } + if (params.style === "spaceDelimited") { + return Core.generateSpaceDelimitedAsURLSearchParams(key, params); + } + if (params.style === "pipeDelimited") { + return Core.generatePipeDelimitedParameterAsURLSearchParams(key, params); + } + if (params.style === "deepObject") { + return Core.generateDeepObjectParameterAsURLSearchParams(key, params); + } + return undefined; +}; diff --git a/src/__tests__/CookieParameter-test.ts b/src/__tests__/CookieParameter-test.ts index 7e4ce04..b2c8c15 100644 --- a/src/__tests__/CookieParameter-test.ts +++ b/src/__tests__/CookieParameter-test.ts @@ -55,7 +55,7 @@ describe("CookieParameter - style:form", () => { style: "form", explode: true, }); - expect(result).toBe("color=blue&color=black&color=brown"); + expect(result).toBe("color=blue;color=black;color=brown"); }); test("explode:false value:object", () => { const result1 = CookieParameter.generate("color", { @@ -79,6 +79,6 @@ describe("CookieParameter - style:form", () => { style: "form", explode: true, }); - expect(result1).toBe("R=100&G=200&B=150"); + expect(result1).toBe("R=100;G=200;B=150"); }); }); diff --git a/src/__tests__/DeepObject-test.ts b/src/__tests__/DeepObject-test.ts index af4a11e..13593c5 100644 --- a/src/__tests__/DeepObject-test.ts +++ b/src/__tests__/DeepObject-test.ts @@ -7,7 +7,7 @@ describe("QueryParameter - style:deepObject", () => { style: "deepObject", explode: true, }); - expect(result1).toBe(`filters[level1][level2]=hello`); + expect(result1?.toString()).toBe(`filters%5Blevel1%5D%5Blevel2%5D=hello`); }); test("explode:true / nest value", () => { const result1 = QueryParameter.generate("filters", { @@ -15,7 +15,7 @@ describe("QueryParameter - style:deepObject", () => { style: "deepObject", explode: true, }); - expect(result1).toBe(`filters[level1][level2][level3]=hello`); + expect(result1?.toString()).toBe(`filters%5Blevel1%5D%5Blevel2%5D%5Blevel3%5D=hello`); }); test("explode:true / nest value", () => { const result1 = QueryParameter.generate("filters", { @@ -23,6 +23,6 @@ describe("QueryParameter - style:deepObject", () => { style: "deepObject", explode: true, }); - expect(result1).toBe(`filters[level1][level2][level3]=hello&filters[level1][level2-1][level3-1]=world`); + expect(result1?.toString()).toBe(`filters%5Blevel1%5D%5Blevel2%5D%5Blevel3%5D=hello&filters%5Blevel1%5D%5Blevel2-1%5D%5Blevel3-1%5D=world`); }); }); diff --git a/src/__tests__/QueryParameter-test.ts b/src/__tests__/QueryParameter-test.ts index 9ad4cfd..4e47175 100644 --- a/src/__tests__/QueryParameter-test.ts +++ b/src/__tests__/QueryParameter-test.ts @@ -47,7 +47,7 @@ describe("QueryParameter - style:form", () => { style: "form", explode: false, }); - expect(result).toBe("color=blue,black,brown"); + expect(result).toBe("color=blue%2Cblack%2Cbrown"); }); test("explode:true value:string[]", () => { const result = QueryParameter.generate("color", { @@ -67,7 +67,7 @@ describe("QueryParameter - style:form", () => { style: "form", explode: false, }); - expect(result1).toBe("color=R,100,G,200,B,150"); + expect(result1).toBe("color=R%2C100%2CG%2C200%2CB%2C150"); }); test("explode:true value:object", () => { const result1 = QueryParameter.generate("color", { @@ -90,7 +90,7 @@ describe("QueryParameter - style:spaceDelimited", () => { style: "spaceDelimited", explode: false, }); - expect(result1).toBe("blue%20black%20brown"); + expect(result1).toBe("color=blue+black+brown"); }); test("explode:false value:object", () => { const result1 = QueryParameter.generate("color", { @@ -102,7 +102,7 @@ describe("QueryParameter - style:spaceDelimited", () => { style: "spaceDelimited", explode: false, }); - expect(result1).toBe("R%20100%20G%20200%20B%20150"); + expect(result1).toBe("color=R+100+G+200+B+150"); }); }); @@ -113,7 +113,7 @@ describe("QueryParameter - style:pipeDelimited", () => { style: "pipeDelimited", explode: false, }); - expect(result1).toBe("blue|black|brown"); + expect(result1).toBe("color=blue%7Cblack%7Cbrown"); }); test("explode:false value:object", () => { const result1 = QueryParameter.generate("color", { @@ -125,7 +125,7 @@ describe("QueryParameter - style:pipeDelimited", () => { style: "pipeDelimited", explode: false, }); - expect(result1).toBe("R|100|G|200|B|150"); + expect(result1).toBe("color=R%7C100%7CG%7C200%7CB%7C150"); }); }); @@ -140,6 +140,6 @@ describe("QueryParameter - style:deepObject", () => { style: "deepObject", explode: true, }); - expect(result1).toBe("color[R]=100&color[G]=200&color[B]=150"); + expect(result1).toBe("color%5BR%5D=100&color%5BG%5D=200&color%5BB%5D=150"); }); });