Skip to content

Commit

Permalink
fix: Fix curcular bug on flattenObject
Browse files Browse the repository at this point in the history
  • Loading branch information
neet committed Mar 25, 2021
1 parent 93c2a14 commit bd4463a
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 51 deletions.
59 changes: 39 additions & 20 deletions src/http/http-axios-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,48 @@ import {
MastoUnprocessableEntityError,
} from '../errors';
import { Serializer } from '../serializers';
import { Body, Http, Request, Response } from './http';
import { Data, Http, Request, Response } from './http';

export class HttpAxiosImpl implements Http {
private readonly axios: AxiosInstance;

constructor(readonly config: MastoConfig, readonly serializer: Serializer) {
this.axios = axios.create({
baseURL: config.url,
headers: this.config.accessToken
? {
Authorization: `Bearer ${this.config.accessToken}`,
}
: {},
transformRequest: (data, headers) =>
this.serializer.serialize(headers['Content-Type'], data),
headers: {
Authorization:
this.config.accessToken && `Bearer ${this.config.accessToken}`,
'Content-Type': 'application/json',
...config.headers,
},
proxy: config.proxy,
timeout: config.timeout,
transformRequest: (data, headers) => {
const result = this.serializer.serialize(headers['Content-Type'], data);

// In Node.js, axios doesn't set boundary data to the header
// so set it manually by using getHeaders of form-data node.js package
// https://github.com/form-data/form-data#headers-getheaders-headers-userheaders-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (typeof (result as any)?.getHeaders === 'function') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
headers['Content-Type'] = (result as any).getHeaders()[
'content-type'
];
}

return result;
},
transformResponse: (data, headers) =>
this.serializer.deserialize(
headers['Content-Type'] ?? 'application/json',
data,
),
paramsSerializer: (params) =>
this.serializer.serialize('application/json', params) as string,
...config,
this.serializer.serialize(
'application/x-www-form-urlencoded',
params,
) as string,
});
}

Expand Down Expand Up @@ -82,47 +101,47 @@ export class HttpAxiosImpl implements Http {
}
}

get<T>(url: string, body?: Body, init: Request = {}): Promise<T> {
get<T>(url: string, data?: Data, init: Request = {}): Promise<T> {
return this.request({
method: 'get',
url,
body,
params: data,
...init,
}).then((response) => response.data as T);
}

post<T>(url: string, body?: Body, init: Request = {}): Promise<T> {
post<T>(url: string, data?: Data, init: Request = {}): Promise<T> {
return this.request({
method: 'post',
url,
body,
data,
...init,
}).then((response) => response.data as T);
}

delete<T>(url: string, body?: Body, init: Request = {}): Promise<T> {
delete<T>(url: string, data?: Data, init: Request = {}): Promise<T> {
return this.request({
method: 'delete',
url,
body,
data,
...init,
}).then((response) => response.data as T);
}

put<T>(url: string, body?: Body, init: Request = {}): Promise<T> {
put<T>(url: string, data?: Data, init: Request = {}): Promise<T> {
return this.request({
method: 'put',
url,
body,
data,
...init,
}).then((response) => response.data as T);
}

patch<T>(url: string, body?: Body, init: Request = {}): Promise<T> {
patch<T>(url: string, data?: Data, init: Request = {}): Promise<T> {
return this.request({
method: 'patch',
url,
body,
data,
...init,
}).then((response) => response.data as T);
}
Expand Down
7 changes: 4 additions & 3 deletions src/http/http.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
export type Headers = { readonly [key: string]: unknown };
export type Body = unknown;
export type Data = unknown;

export type Request = {
readonly url?: string;
readonly method?: 'get' | 'post' | 'patch' | 'delete' | 'put' | 'options';
readonly headers?: Headers;
readonly body?: Body;
readonly params?: Data;
readonly data?: Data;
};

export type Response<T> = {
Expand All @@ -15,7 +16,7 @@ export type Response<T> = {

export type Method = <T>(
path: string,
body?: Body,
data?: Data,
request?: Request,
) => Promise<T>;

Expand Down
2 changes: 1 addition & 1 deletion src/paginator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class Paginator<Params, Result>
method: 'get',
// if no params specified, use link header
url: params ? this.initialUrl : this.nextUrl,
body: params ?? this.nextParams,
data: params ?? this.nextParams,
});

this.nextUrl = this.pluckNext(response.headers?.link as string);
Expand Down
38 changes: 22 additions & 16 deletions src/serializers/form-data.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,23 @@ test('flat value', () => {
grapes: 'purple',
});

expect(result['apple']).toEqual('red');
expect(result['mandarin']).toEqual('orange');
expect(result['grapes']).toEqual('purple');
expect(result).toStrictEqual({
apple: 'red',
mandarin: 'orange',
grapes: 'purple',
});
});

test('array', () => {
const result = flattenObject({
animals: ['lion', 'giraffe', 'elephant'],
});

expect(result['animals[0]']).toEqual('lion');
expect(result['animals[1]']).toEqual('giraffe');
expect(result['animals[2]']).toEqual('elephant');
expect(result).toStrictEqual({
'animals[0]': 'lion',
'animals[1]': 'giraffe',
'animals[2]': 'elephant',
});
});

test('nested object', () => {
Expand All @@ -38,14 +42,16 @@ test('nested object', () => {
},
});

expect(result['a']).toEqual('string');
expect(result['b']).toEqual(123);
expect(result['c[0]']).toEqual(1);
expect(result['c[1]']).toEqual(2);
expect(result['c[2]']).toEqual(3);
expect(result['e[e1]']).toEqual('string');
expect(result['e[e2][e21][e211]']).toEqual('string');
expect(result['e[e2][e22][0][value]']).toEqual(1);
expect(result['e[e2][e22][1][value]']).toEqual(2);
expect(result['e[e2][e22][2][value]']).toEqual(3);
expect(result).toStrictEqual({
a: 'string',
b: 123,
'c[0]': 1,
'c[1]': 2,
'c[2]': 3,
'e[e1]': 'string',
'e[e2][e21][e211]': 'string',
'e[e2][e22][0][value]': 1,
'e[e2][e22][1][value]': 2,
'e[e2][e22][2][value]': 3,
});
});
25 changes: 14 additions & 11 deletions src/serializers/form-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,24 @@ export const flattenObject = (
object: unknown,
parent = '',
): Record<string, unknown> => {
if (isObject(object)) {
return Object.entries(object)
.map(([key, value]) =>
flattenObject(value, parent ? `${parent}[${key}]` : key),
)
.reduce(Object.assign);
}

if (Array.isArray(object)) {
return object
.map((value, i) =>
flattenObject(value, parent ? `${parent}[${i}]` : i.toString()),
flattenObject(value, parent !== '' ? `${parent}[${i}]` : i.toString()),
)
.reduce((prev, curr) => Object.assign(prev, curr), {});
}

if (isObject(object)) {
return Object.entries(object)
.map(([key, value]) =>
flattenObject(value, parent !== '' ? `${parent}[${key}]` : key),
)
.reduce(Object.assign);
.reduce((prev, curr) => Object.assign(prev, curr), {});
}

return { [parent]: object };
// Unit of the monoid is always an object
return parent !== ''
? { [parent]: object }
: (object as Record<string, unknown>);
};

0 comments on commit bd4463a

Please sign in to comment.