Skip to content

Commit

Permalink
Merge pull request #5 from qkang07/feat/kuaishou
Browse files Browse the repository at this point in the history
Support Kuaishou miniprogram
  • Loading branch information
YanagiEiichi authored Jul 5, 2024
2 parents a9c22ff + 4f0a807 commit 603e90e
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 1 deletion.
8 changes: 7 additions & 1 deletion src/internalRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ import { requestWithMy } from './requestWithMy';
import { requestWithSwan } from './requestWithSwan';
import { requestWithTt } from './requestWithTt';
import { requestWithWx } from './requestWithWx';
import { requestWithKs } from './requestWithKs';
import { requestWithXhr } from './requestWithXhr';
import { InvokeParams } from './types/InvokeParams';
import { My } from './types/My';
import { Swan } from './types/Swan';
import { Wx } from './types/Wx';
import { Ks } from './types/Ks';
import { isValidMPO as isValidMpo } from './utils/predicates';

declare const wx: Wx;
declare const my: My;
declare const swan: Swan;
declare const tt: Swan;
declare const ks: Ks

export const internalRequest = <T>(args: InvokeParams) => {
// Detect the current platform and call the corresponding method.
Expand All @@ -22,7 +25,10 @@ export const internalRequest = <T>(args: InvokeParams) => {
// detect the existence of document object to determine if it is a browser platform.
case typeof XMLHttpRequest === 'function' && typeof document === 'object' && document !== null:
return requestWithXhr<T>(args);

// Noted that the 'ks' must be used ahead of 'wx', because kuaishou just copied wx's base code and has the 'wx'
// variable left in the runtime.
case typeof ks === 'object' && isValidMpo(ks):
return requestWithKs<T>(args);
case typeof wx === 'object' && isValidMpo(wx):
return requestWithWx<T>(args);
case typeof my === 'object' && isValidMpo(my):
Expand Down
73 changes: 73 additions & 0 deletions src/requestWithKs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { InvokeResult } from './types/InvokeResult';
import { InvokeParams } from './types/InvokeParams';
import { BatchUploadError, MiniProgramError } from './errors';
import { fixInvokeParams } from './utils/fixInvokeParams';
import { Ks } from './types/Ks';

declare const ks: Ks;

const convertResponseType = (responseType?: InvokeParams['responseType']) => {
if (!responseType) return {};
if (responseType === 'arraybuffer') return { responseType } as const;
if (responseType === 'json') return { dataType: 'json' } as const;
if (responseType === 'text') return { dataType: 'string' } as const;
throw new TypeError(`The responseType "${responseType}" is not supported by Kuaishou Miniprogram`);
};

export const requestWithKs = <T>(args: InvokeParams) =>
new Promise<InvokeResult<T>>((resolve, reject) => {
const { headers, files, data, responseType, onUploadProgress, signal, ...rest } = fixInvokeParams(args);
const fileNames = files ? Object.keys(files) : [];

const fail = (obj: unknown) => {
// Perhaps the official is joking with us, so important information actually no documentation :joy:.
const { errCode, errMsg } = Object(obj);
reject(new MiniProgramError(errCode, errMsg));
};

try {
if (fileNames.length === 0) {
/**
* @see https://mp.kuaishou.com/docs/develop/api/network/request/request.html
*/
const task = ks.request({
header: headers,
data,
...convertResponseType(responseType),
...rest,
success: ({ header, data, ...rest }) => resolve({ data: data as T, headers: header, ...rest }),
fail,
});
signal?.addEventListener('abort', () => task.abort());
} else if (files && fileNames.length === 1) {
if (responseType) {
throw new TypeError('The `responseType` is not supported if `files` not empty in Kuaishou Miniprogram');
}
const name = fileNames[0];
const filePath = files[name];
/**
* @see https://mp.kuaishou.com/docs/develop/api/network/upload/uploadFile.html
*/
const task = ks.uploadFile({
header: headers,
formData: data,
name,
filePath,
...rest,
success: ({ header, data, ...rest }) => resolve({ headers: header, data: data as T, ...rest }),
fail,
});
// Bind onUploadProgress event.
if (onUploadProgress)
task.onProgressUpdate((e) => {
onUploadProgress({ loaded: e.totalBytesSent, total: e.totalBytesExpectedToSend });
});
// Bind abort event.
signal?.addEventListener('abort', () => task.abort());
} else {
throw new BatchUploadError();
}
} catch (error) {
reject(error);
}
});
3 changes: 3 additions & 0 deletions src/tests/batch-upload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ import './libs/mock-xhr';
import './libs/mock-wx';
import './libs/mock-my';
import './libs/mock-swan';
import './libs/mock-ks';
import { BatchUploadError } from '../errors';
import { requestWithTt } from '../requestWithTt';
import { requestWithKs } from '../requestWithKs';

describe('all libs tests', () => {
for (const [name, request] of [
['wx', requestWithWx],
['my', requestWithMy],
['tt', requestWithTt],
['swan', requestWithSwan],
['ks', requestWithKs],
] as const) {
test(`[${name}] batch upload error`, async () => {
const f1 = new Blob(['f1'], { type: 'text/plain' });
Expand Down
48 changes: 48 additions & 0 deletions src/tests/libs/mock-ks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { RequestParams, UploadParams } from '../../types/common';
import type { Ks } from '../../types/Ks';
import { UploadTaskImpl } from './UploadTaskImpl';
import { BaseMpoImpl } from './BaseMpoImpl';
import { readAsDataURL } from './readAsDataURL';
import { RequestTaskImpl } from './RequestTaskImpl';

class KsConstructor extends BaseMpoImpl implements Ks {
request(req: RequestParams) {
const timer = setTimeout(async () => {
const { header, ...rest } = req;
const { code, msg } = Object(header);
if (req.fail && (code || msg)) {
return req.fail({ errno: Number(code), errMsg: msg });
}
req.success({
statusCode: Number(Object(header)['status-code']) || 200,
header: { server: 'mock' },
data: this.makeData({ ...rest, headers: header }, rest.dataType || rest.responseType),
});
});
return new RequestTaskImpl(() => {
clearTimeout(timer);
if (req.fail) req.fail({ errMsg: 'request:fail abort' });
});
}
uploadFile(req: UploadParams) {
const timer = setTimeout(async () => {
const { header, name, filePath, formData, ...rest } = req;
req.success({
statusCode: Number(Object(header)['status-code']) || 200,
header: { server: 'mock' },
data: {
...rest,
data: formData,
headers: header,
files: { [name]: await readAsDataURL(filePath) },
},
});
}, 100);
return new UploadTaskImpl(() => {
clearTimeout(timer);
if (req.fail) req.fail({ errMsg: 'request:fail abort' });
});
}
}

Object.defineProperty(global, 'ks', { value: new KsConstructor() });
24 changes: 24 additions & 0 deletions src/tests/platforms/platform-ks.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { request } from '../..';
// Kuaishou has just copied wx's base code and has the 'wx' variable left...
import '../libs/mock-wx';
import '../libs/mock-ks';
import { requestWithKs } from '../../requestWithKs';
import { requestWithWx } from '../../requestWithWx';

Object.defineProperty(global, 'XMLHttpRequest', { configurable: true, value: null });
Object.defineProperty(global, 'document', { configurable: true, value: null });

jest.mock('../../requestWithKs', () => ({ requestWithKs: jest.fn() }))
jest.mock('../../requestWithWx', () => ({ requestWithWx: jest.fn() }))

test('look for `ks` object ahead of `wx`', async () => {

await request({ method: 'GET', url: '/test' })
expect(requestWithKs).toHaveBeenCalledTimes(1);
expect(requestWithWx).not.toHaveBeenCalled()

})

test(`basic`, async () => {
expect(request({ method: 'GET', url: '/test' })).resolves.not.toBeNull();
});
3 changes: 3 additions & 0 deletions src/tests/request-all.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import './libs/mock-wx';
import './libs/mock-my';
import './libs/mock-tt';
import './libs/mock-swan';
import './libs/mock-ks';
import { requestWithTt } from '../requestWithTt';
import { requestWithKs } from '../requestWithKs';

describe('all libs tests', () => {
for (const [name, request] of [
Expand All @@ -18,6 +20,7 @@ describe('all libs tests', () => {
['my', requestWithMy],
['tt', requestWithTt],
['swan', requestWithSwan],
['ks', requestWithKs],
] as const) {
test(`[${name}] basic`, async () => {
const res = await request({
Expand Down
4 changes: 4 additions & 0 deletions src/tests/request-mp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ import './libs/mock-wx';
import './libs/mock-my';
import './libs/mock-tt';
import './libs/mock-swan';
import './libs/mock-ks';
import { MiniProgramError } from '../errors';
import { RequestController } from '../RequestController';
import { requestWithTt } from '../requestWithTt';
import { requestWithKs } from '../requestWithKs';


describe('all libs tests', () => {
for (const [name, request] of [
['wx', requestWithWx],
['my', requestWithMy],
['tt', requestWithTt],
['swan', requestWithSwan],
['ks', requestWithKs],
] as const) {
test(`[${name}] fail`, async () => {
const params = { method: 'GET', url: '/test' };
Expand Down
6 changes: 6 additions & 0 deletions src/types/Ks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { RequestParams, UploadParams, UploadTask, ProgressInfo, BaseMpo, RequestTask } from './common';

export interface Ks extends BaseMpo {
request(req: RequestParams): RequestTask;
uploadFile(req: UploadParams): UploadTask<ProgressInfo>;
}

0 comments on commit 603e90e

Please sign in to comment.