Skip to content

Commit

Permalink
feat: plugin-request for umi 3
Browse files Browse the repository at this point in the history
  • Loading branch information
yutingzhao1991 committed Feb 10, 2020
1 parent c29b8cc commit 1a0e7ee
Show file tree
Hide file tree
Showing 18 changed files with 973 additions and 0 deletions.
1 change: 1 addition & 0 deletions example/.umirc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export default defineConfig({
require.resolve('../packages/plugin-locale/lib'),
require.resolve('../packages/plugin-dva/lib'),
require.resolve('../packages/plugin-model/lib'),
require.resolve('../packages/plugin-request/lib'),
],
});
8 changes: 8 additions & 0 deletions example/mock/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default {
'/api/user': {
success: true,
data: {
name: 'yutingzhao1991',
},
},
};
13 changes: 13 additions & 0 deletions example/pages/request/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import { useRequest } from 'umi';
import * as services from '../../services';

export default () => {
const { data, loading } = useRequest(services.fetchCurrentUser);

if (loading) {
return <div>loading...</div>;
}

return <div>{data.name}</div>;
};
1 change: 1 addition & 0 deletions example/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './user';
5 changes: 5 additions & 0 deletions example/services/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { request } from 'umi';

export const fetchCurrentUser = () => {
return request('/api/user');
};
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ module.exports = {
'^react-dom$': require.resolve('react-dom'),
});
},
testEnvironment: 'node',
};
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@
]
},
"devDependencies": {
"@testing-library/react-hooks": "^3.2.1",
"@testing-library/react": "^9.4.0",
"@types/jest": "^24.0.25",
"@types/node": "^13.1.6",
"@umijs/test": "^3.0.0-beta.7",
"react-test-renderer": "^16.9.0",
"create-test-server": "^3.0.1",
"father-build": "^1.17.2",
"lerna": "^3.20.2",
"lint-staged": "^9.5.0",
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-request/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @umijs/plugin-request
34 changes: 34 additions & 0 deletions packages/plugin-request/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "@umijs/plugin-request",
"version": "2.0.0-alpha.0",
"description": "@umijs/plugin-request",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib",
"src"
],
"repository": {
"type": "git",
"url": "https://github.com/umijs/plugins"
},
"keywords": [
"umi"
],
"authors": [
"chencheng <sorrycc@gmail.com> (https://github.com/sorrycc)"
],
"license": "MIT",
"bugs": "http://github.com/umijs/plugins/issues",
"homepage": "https://github.com/umijs/plugins/tree/master/packages/plugin-request#readme",
"peerDependencies": {
"umi": "3.x"
},
"dependencies": {
"@umijs/use-request": "^1.0.0",
"umi-request": "^1.2.14"
},
"publishConfig": {
"access": "public"
}
}
61 changes: 61 additions & 0 deletions packages/plugin-request/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { IApi } from 'umi';
import { join, dirname } from 'path';
import assert from 'assert';
import { readFileSync } from 'fs';

export interface RequestOptions {
dataField?: string;
}

export default function(api: IApi, options: RequestOptions) {
const {
paths,
utils: { winPath },
} = api;

api.addRuntimePluginKey(() => 'request');

const { dataField = 'data' } = options || {};
const source = join(__dirname, '..', 'src', 'request.ts');
const requestTemplate = readFileSync(source, 'utf-8');
const namespace = 'plugin-request';
assert(/^[a-zA-Z]*$/.test(dataField), 'dataField should match /^[a-zA-Z]*$/');

api.onGenerateFiles(() => {
try {
// Write .umi/plugin-request/request.ts
let formatResultStr;
if (dataField === '') {
formatResultStr = 'formatResult: result => result';
} else {
formatResultStr = `formatResult: result => result?.${dataField}`;
}
api.writeTmpFile({
path: `${namespace}/request.ts`,
content: requestTemplate
.replace(/\/\*FRS\*\/(.+)\/\*FRE\*\//, formatResultStr)
.replace(/\['data'\]/g, dataField ? `['${dataField}']` : '')
.replace(/data: T;/, dataField ? `${dataField}: T;` : '')
.replace(
/umi-request/g,
winPath(dirname(require.resolve('umi-request/package'))),
)
.replace(
/@umijs\/use-request/g,
winPath(dirname(require.resolve('@umijs/use-request/package'))),
),
});
} catch (e) {
api.logger.error(e);
}
});

api.addUmiExports(() => {
return [
{
exportAll: true,
source: winPath(join(paths.absTmpPath!, namespace, 'request')),
},
];
});
}
209 changes: 209 additions & 0 deletions packages/plugin-request/src/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/**
* Base on https://github.com/umijs/umi-request
*/
import {
extend,
Context,
RequestOptionsInit,
OnionMiddleware,
} from 'umi-request';
// @ts-ignore
import { ApplyPluginsType, history, plugin } from 'umi';
import { message, notification } from 'antd';
import useUmiRequest from '@umijs/use-request';
import {
BaseOptions,
BasePaginatedOptions,
BaseResult,
CombineService,
LoadMoreFormatReturn,
LoadMoreOptions,
LoadMoreOptionsWithFormat,
LoadMoreParams,
LoadMoreResult,
OptionsWithFormat,
PaginatedFormatReturn,
PaginatedOptionsWithFormat,
PaginatedParams,
PaginatedResult,
} from '@umijs/use-request/lib/types';

type ResultWithData<T = any> = { data: T; [key: string]: any };

function useRequest<
R = any,
P extends any[] = any,
U = any,
UU extends U = any
>(
service: CombineService<R, P>,
options: OptionsWithFormat<R, P, U, UU>,
): BaseResult<U, P>;
function useRequest<R extends ResultWithData = any, P extends any[] = any>(
service: CombineService<R, P>,
options?: BaseOptions<R['data'], P>,
): BaseResult<R['data'], P>;
function useRequest<R = any, Item = any, U extends Item = any>(
service: CombineService<R, LoadMoreParams>,
options: LoadMoreOptionsWithFormat<R, Item, U>,
): LoadMoreResult<Item>;
function useRequest<Item = any, U extends Item = any>(
service: CombineService<
ResultWithData<LoadMoreFormatReturn<Item>>,
LoadMoreParams
>,
options: LoadMoreOptions<U>,
): LoadMoreResult<Item>;

function useRequest<R = any, Item = any, U extends Item = any>(
service: CombineService<R, PaginatedParams<U>>,
options: PaginatedOptionsWithFormat<R, Item, U>,
): PaginatedResult<Item>;
function useRequest<Item = any, U extends Item = any>(
service: CombineService<
ResultWithData<PaginatedFormatReturn<Item>>,
PaginatedParams<U>
>,
options: BasePaginatedOptions<U>,
): PaginatedResult<Item>;
function useRequest(service: any, options: any = {}) {
return useUmiRequest(service, {
/*FRS*/ formatResult: res => res?.data /*FRE*/,
requestMehod: request,
...options,
});
}

export { useRequest };

export interface RequestConfig extends RequestOptionsInit {
errorConfig?: {
errorPage?: string;
adaptor?: (resData: any, ctx: Context) => ErrorInfoStructure;
};
middlewares?: OnionMiddleware[];
}

export enum ErrorShowType {
SILENT = 0,
WARN_MESSAGE = 1,
ERROR_MESSAGE = 2,
NOTIFICATION = 4,
REDIRECT = 9,
}

interface ErrorInfoStructure {
success: boolean;
data?: any;
errorCode?: string;
errorMessage?: string;
showType?: ErrorShowType;
traceId?: string;
host?: string;
[key: string]: any;
}

interface RequestError extends Error {
data?: any;
info?: ErrorInfoStructure;
request?: Context['req'];
response?: Context['res'];
}

const DEFAULT_ERROR_PAGE = '/exception';
const requestConfig: RequestConfig = plugin.applyPlugins({
key: 'request',
type: ApplyPluginsType.modify,
initialValue: {},
});

const errorAdaptor = requestConfig.errorConfig?.adaptor || (resData => resData);

export const request = extend({
errorHandler: (error: RequestError) => {
// @ts-ignore
if (error?.request?.options?.skipErrorHandler) {
throw error;
}
let errorInfo: ErrorInfoStructure | undefined;
if (error.name === 'ResponseError' && error.data && error.request) {
const ctx: Context = {
req: error.request,
res: error.response,
};
errorInfo = errorAdaptor(error.data, ctx);
error.message = errorInfo?.errorMessage || error.message;
error.data = error.data;
error.info = errorInfo;
}
errorInfo = error.info;

if (errorInfo) {
const errorMessage = errorInfo?.errorMessage;
const errorCode = errorInfo?.errorCode;
const errorPage =
requestConfig.errorConfig?.errorPage || DEFAULT_ERROR_PAGE;

switch (errorInfo?.showType) {
case ErrorShowType.SILENT:
// do nothing
break;
case ErrorShowType.WARN_MESSAGE:
message.warn(errorMessage);
break;
case ErrorShowType.ERROR_MESSAGE:
message.error(errorMessage);
break;
case ErrorShowType.NOTIFICATION:
notification.open({
message: errorMessage,
});
break;
case ErrorShowType.REDIRECT:
history.push({
pathname: errorPage,
query: { errorCode, errorMessage },
});
// redirect to error page
break;
default:
message.error(errorMessage);
break;
}
} else {
message.error(error.message || 'Request error, please retry.');
}
throw error;
},
...requestConfig,
});

// 中间件统一错误处理
// 后端返回格式 { success: boolean, data: any }
// 按照项目具体情况修改该部分逻辑
request.use(async (ctx, next) => {
await next();
const { req, res } = ctx;
// @ts-ignore
if (req.options?.skipErrorHandler) {
return;
}
const { options } = req;
const { getResponse } = options;
const resData = getResponse ? res.data : res;
const errorInfo = errorAdaptor(resData, ctx);
if (errorInfo.success === false) {
// 抛出错误到 errorHandler 中处理
const error: RequestError = new Error(errorInfo.errorMessage);
error.name = 'BizError';
error.data = resData;
error.info = errorInfo;
throw error;
}
});

// Add user custom middlewares
const customMiddlewares = requestConfig.middlewares || [];
customMiddlewares.forEach(mw => {
request.use(mw);
});
8 changes: 8 additions & 0 deletions packages/plugin-request/tests/__mocks__/antd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const message = {
error: () => {},
warn: () => {},
};

export const notification = {
open: () => {},
};
13 changes: 13 additions & 0 deletions packages/plugin-request/tests/__mocks__/umi/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
exports.ApplyPluginsType = {};

exports.history = {
push: params => {
require('historyPush')(params);
},
};

exports.plugin = {
applyPlugins: () => {
return require('runtimeConfig');
},
};
Loading

0 comments on commit 1a0e7ee

Please sign in to comment.