Skip to content

Commit

Permalink
fix(core): utils encodeParams support Date, allowDots and `arra…
Browse files Browse the repository at this point in the history
…yFormat` options
  • Loading branch information
suhaotian committed Jul 9, 2024
1 parent 3a235b8 commit ab1cc23
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 13 deletions.
57 changes: 44 additions & 13 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,39 @@ const undefinedValue = undefined;
export function encodeParams<T = any>(
params: T,
encodeURI = true,
parentKey: string | null = null
parentKey: string | null = null,
options?: {
allowDots?: boolean;
serializeDate?: (value: Date) => string;
arrayFormat?: 'indices' | 'repeat' | 'brackets';
}
): string {
if (params === undefinedValue || params === null) return '';
const encodedParams = [];
const encodeURIFunc = encodeURI ? encodeURIComponent : (v: string) => v;

const encodeURIFn = encodeURI ? encodeURIComponent : (v: string) => v;
const paramsIsArray = Array.isArray(params);
for (const key in params) {
if (Object.prototype.hasOwnProperty.call(params, key)) {
const value = (params as any)[key];
let value = (params as any)[key];
if (value !== undefinedValue) {
const encodedKey = parentKey ? `${parentKey}[${encodeURIFunc(key)}]` : encodeURIFunc(key);
const encodedKey = encodeKey(
parentKey,
key,
paramsIsArray,
options?.arrayFormat,
options?.allowDots
);

if (value instanceof Date) {
value = options?.serializeDate ? options?.serializeDate(value) : value.toISOString();
}
if (typeof value === 'object') {
// If the value is an object or array, recursively encode its contents
const result = encodeParams(value, encodeURI, encodedKey);
const result = encodeParams(value, encodeURI, encodedKey, options);
if (result !== '') encodedParams.push(result);
} else if (Array.isArray(value)) {
// If the value is an array, encode each element individually
value.forEach((element, index) => {
const arrayKey = `${encodedKey}[${index}]`;
encodedParams.push(`${encodeURIFunc(arrayKey)}=${encodeURIFunc(element)}`);
});
} else {
// Otherwise, encode the key-value pair
encodedParams.push(`${encodeURIFunc(encodedKey)}=${encodeURIFunc(value)}`);
encodedParams.push(`${encodeURIFn(encodedKey)}=${encodeURIFn(value)}`);
}
}
}
Expand All @@ -41,6 +49,29 @@ export function encodeParams<T = any>(
return encodedParams.join('&');
}

const encodeKey = (
parentKey: string | null,
key: string,
isArray: boolean,
arrayFormat?: 'indices' | 'brackets' | 'repeat',
allowDots?: boolean
) => {
if (!parentKey) return key;
// arrayFormat = arrayFormat || 'indices';
const getKey = (key: string) => {
if (allowDots && !isArray) return `.${key}`;
if (isArray) {
if (arrayFormat === 'brackets') {
return `[]`;
} else if (arrayFormat === 'repeat') {
return ``;
}
}
return `[${key}]`;
};
return `${parentKey}${getKey(key)}`;
};

export function trimUndefined(obj: any): any {
if (Array.isArray(obj)) {
return obj.map(trimUndefined);
Expand Down
93 changes: 93 additions & 0 deletions tests/src/tests/encode-params-data.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,36 @@ describe('encodeParams()', function () {
assert.strictEqual(encodeParams({ a: false, c: [] }, false), 'a=false');
});

it('encode with `arrayFormat` options', function () {
const filter = { a: ['b', 'c'] };
assert.strictEqual(
encodeParams(filter, false, null, { arrayFormat: 'indices' }),
'a[0]=b&a[1]=c'
);
assert.strictEqual(
encodeParams(filter, false, null, { arrayFormat: 'brackets' }),
'a[]=b&a[]=c'
);

assert.strictEqual(encodeParams(filter, false, null, { arrayFormat: 'repeat' }), 'a=b&a=c');
});

it('encode with `arrayFormat` and `allowDots: true` options', function () {
const filter = { a: ['b', 'c'] };
assert.strictEqual(
encodeParams(filter, false, null, { arrayFormat: 'indices', allowDots: true }),
'a[0]=b&a[1]=c'
);
assert.strictEqual(
encodeParams(filter, false, null, { arrayFormat: 'brackets', allowDots: true }),
'a[]=b&a[]=c'
);
assert.strictEqual(
encodeParams(filter, false, null, { arrayFormat: 'repeat', allowDots: true }),
'a=b&a=c'
);
});

it('encode nested object', () => {
const data = { a: 'b', c: [1, 2, 3, { d: [7, 8, 9, { e: 234 }] }] };
assert.strictEqual(encodeParams(data), stringify(data));
Expand Down Expand Up @@ -89,4 +119,67 @@ describe('encodeParams()', function () {
assert.strictEqual(data.body.hasOwnProperty('c'), false);
assert.strictEqual(data.body['d'].hasOwnProperty('e'), false);
});

it('encode nested object with `{allowDots: true}`', () => {
const data = { a: 'b', c: [1, 2, 3, { d: [7, 8, 9, { e: 234 }] }] };
assert.strictEqual(
encodeParams(data, false, null, { allowDots: true, arrayFormat: 'repeat' }),
stringify(data, { encode: false, allowDots: true, arrayFormat: 'repeat' })
);
assert.strictEqual(
encodeParams(data, false, null, { allowDots: true, arrayFormat: 'indices' }),
stringify(data, { encode: false, allowDots: true, arrayFormat: 'indices' })
);
assert.strictEqual(
encodeParams(data, false, null, { allowDots: true, arrayFormat: 'brackets' }),
stringify(data, { encode: false, allowDots: true, arrayFormat: 'brackets' })
);
assert.strictEqual(
encodeParams(data, true, null, { allowDots: true, arrayFormat: 'brackets' }),
stringify(data, { encode: true, allowDots: true, arrayFormat: 'brackets' })
);
});

it('encode nested object with `{allowDots: true}` and with Date value', () => {
const data = {
ids: [1, 2, 3],
dates: [new Date(), new Date()],
dateFrom: new Date(),
dateTo: new Date(),
};

assert.strictEqual(
encodeParams(data, false, null, { allowDots: true, arrayFormat: 'repeat' }),
stringify(data, { encode: false, allowDots: true, arrayFormat: 'repeat' })
);
assert.strictEqual(
encodeParams(data, false, null, { allowDots: true, arrayFormat: 'indices' }),
stringify(data, { encode: false, allowDots: true, arrayFormat: 'indices' })
);
assert.strictEqual(
encodeParams(data, false, null, { allowDots: true, arrayFormat: 'brackets' }),
stringify(data, { encode: false, allowDots: true, arrayFormat: 'brackets' })
);
assert.strictEqual(
encodeParams(data, true, null, { allowDots: true, arrayFormat: 'brackets' }),
stringify(data, { encode: true, allowDots: true, arrayFormat: 'brackets' })
);
});

it('encode nested object should work with server with `{allowDots: true}`', async () => {
const instance = Xior.create({
baseURL,
paramsSerializer: (params: any) =>
encodeParams(params, true, null, {
allowDots: false,
arrayFormat: 'indices',
serializeDate: (date) => date.toISOString(),
}),
});
const params = { a: 'b', c: [1, 2, 3, { d: [7, 8, 9, { e: 234, d: new Date() }] }] };
const { data } = await instance.get('/get', { params });
assert.strictEqual(encodeParams(data.query), stringify(data.query));
// assert.strictEqual(stringify(data.query), stringify(params));
// assert.strictEqual(stringify(data.query), encodeParams(params));
});
});

0 comments on commit ab1cc23

Please sign in to comment.