Skip to content

Commit 98d7593

Browse files
author
youncccat
committed
[Init] migrate to a new repository
1 parent 764fb12 commit 98d7593

34 files changed

+759
-0
lines changed

.babelrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"presets": ["@babel/preset-env"],
3+
"plugins": ["@babel/plugin-transform-runtime"]
4+
}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
dist
3+
yarn-error.log

example/configs/sever-config.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"baseUrl": "api.github.com",
3+
"port": 80,
4+
"protocol": "https",
5+
"mode": "prod"
6+
}

example/js/github-service.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {http} from './http/http-service'
2+
3+
export class GitHubService {
4+
getUserDetail(name) {
5+
return http.get(`/users/${name}`)
6+
}
7+
8+
// Request to an incorrect addr
9+
async getWhatever() {
10+
const res = await http.get('/foo')
11+
12+
res.expect(() => "This addr cannot be reached!").success(() => "impossible!")
13+
}
14+
}

example/js/http/http-service.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import {HTTPRequestFactory} from '../../..'
2+
import ServerConfig from '../../configs/sever-config.json'
3+
import {ReqLogger} from './interceptors/req-logger'
4+
import {ResLogger} from './interceptors/res-logger'
5+
import {ResErrorCatcher} from './interceptors/res-error-catcher'
6+
7+
export class HTTPFactory extends HTTPRequestFactory {
8+
configure({interceptor, serverConfigure}) {
9+
serverConfigure.setConfig(ServerConfig)
10+
11+
interceptor.use([
12+
ReqLogger,
13+
ResLogger,
14+
ResErrorCatcher
15+
])
16+
}
17+
18+
errorInteract(errMsg, err) {
19+
console.info(err)
20+
}
21+
}
22+
23+
const httpFactory = new HTTPFactory()
24+
25+
export const http = httpFactory.create()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export class ReqLogger {
2+
onRequest(config) {
3+
console.info("REQ LOG: ===> ", JSON.stringify(config))
4+
5+
return config
6+
}
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export class ResErrorCatcher {
2+
catchRes(err) {
3+
console.info("Error: ", err)
4+
5+
throw err
6+
}
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export class ResLogger {
2+
onResponse(res){
3+
// The res is an raw Axios response, so if u wanna get data when u using it,
4+
// u need to return data of res from res interceptor
5+
return res.data
6+
}
7+
}

example/js/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
require("@babel/register")
2+
require("./main.js")

example/js/main.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import {GitHubService} from './github-service';
2+
3+
const MOCK_USERNAME = 'youncccat';
4+
5+
async function main() {
6+
// The service might inject in class as well as bound a certain variable using
7+
// DI lib that implemented by metadata in particular condition
8+
const githubService = new GitHubService();
9+
const res = await githubService.getUserDetail(MOCK_USERNAME);
10+
11+
const {data: avatarURL} = res
12+
.expect(() => `Cannot access ${MOCK_USERNAME} information`)
13+
.pipe(data => data?.avatar_url);
14+
15+
console.info(`${MOCK_USERNAME}'s avatar is ${avatarURL}`);
16+
}
17+
18+
main();
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"baseUrl": "api.github.com",
3+
"port": 80,
4+
"protocol": "https",
5+
"mode": "prod"
6+
}

example/ts/src/http/http-service.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {
2+
HTTPModule,
3+
HTTPFactory,
4+
RequestHook,
5+
HTTPMethod,
6+
ResValueArea,
7+
} from '../../../..';
8+
import ServerConfig from '../configs/sever-config.json';
9+
import {RequestLogger} from './interceptors/request-logger.interceptor';
10+
11+
@HTTPModule({
12+
server: ServerConfig,
13+
interceptors: [RequestLogger],
14+
})
15+
export class AppModule implements RequestHook {
16+
beforeRequest() {
17+
console.info('before request');
18+
}
19+
afterResponse() {
20+
console.info('after response');
21+
}
22+
}
23+
24+
const http = HTTPFactory.create(AppModule);
25+
26+
(async () => {
27+
const res = await http.get('/users/youncccat');
28+
29+
// console.info(res);
30+
})();
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {HTTPRequestInterceptor, AxiosRequestConfig} from '../../../../..';
2+
3+
export class RequestLogger implements HTTPRequestInterceptor {
4+
onRequest(
5+
config: AxiosRequestConfig,
6+
): AxiosRequestConfig | Promise<AxiosRequestConfig> {
7+
console.info('request ===> ', config.url);
8+
9+
return config;
10+
}
11+
}

example/ts/src/index.ts

Whitespace-only changes.

example/ts/tsconfig.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"compilerOptions": {
3+
"rootDir": "src",
4+
"lib": ["dom", "esnext"],
5+
"outDir": "dist",
6+
"types": ["node"],
7+
"declaration": true,
8+
"suppressImplicitAnyIndexErrors": true,
9+
"emitDecoratorMetadata":true,
10+
"experimentalDecorators": true,
11+
"resolveJsonModule": true,
12+
"esModuleInterop": true
13+
},
14+
"include": ["src/**/*"],
15+
"exclude": ["node_modules/**/*"]
16+
}

package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "@wizardoc/http-request",
3+
"version": "1.3.1",
4+
"main": "dist/index.js",
5+
"license": "MIT",
6+
"dependencies": {
7+
"axios": "^0.19.2",
8+
"reflect-metadata": "^0.1.13"
9+
},
10+
"devDependencies": {
11+
"@babel/core": "^7.12.9",
12+
"@babel/plugin-transform-runtime": "^7.12.1",
13+
"@babel/preset-env": "^7.12.7",
14+
"@babel/register": "^7.12.1",
15+
"@types/axios": "^0.14.0",
16+
"babel-plugin-dynamic-import-node": "^2.3.3"
17+
}
18+
}

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from 'axios';
2+
export * from './lib';

src/lib/core/client.ts

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import {AxiosError, AxiosStatic} from 'axios';
2+
3+
import {ErrorInteractProcessor, ErrorMessage} from './error';
4+
5+
type Request<R = any> = () => R;
6+
7+
export type Response<R = any> = Promise<ResValueArea<R>>;
8+
9+
type Requests<R> = {
10+
[index in HttpType]: Request<R>;
11+
};
12+
13+
interface Doer<R> {
14+
Do(): Response<R>;
15+
}
16+
17+
export type ResValueArea<R = any> = Expectable<R> &
18+
Successable<R> &
19+
Result<R> &
20+
Pipable<R>;
21+
22+
export type ExpectableCB = (err: AxiosError) => ErrorMessage;
23+
24+
export interface Expectable<R> {
25+
expect: OnExpect<R>;
26+
}
27+
28+
export type OnExpect<R> = (cb?: ExpectableCB) => ResValueArea<R>;
29+
30+
export interface Successable<R> {
31+
success: OnSuccess<R>;
32+
}
33+
34+
export interface Pipable<R> {
35+
pipe: OnSuccess<R>;
36+
}
37+
38+
export type OnSuccess<R = any> = (cb: OnSuccessCB<R>) => ResValueArea<R>;
39+
export type OnSuccessCB<R, T = any> = (data: R | undefined) => T;
40+
41+
export interface Result<R> extends Successable<R> {
42+
data?: R;
43+
ok: boolean;
44+
}
45+
46+
export type ExpectFn = () => string | void;
47+
export type SimpleHTTPMethod = 'GET' | 'HEAD';
48+
export type ComplexHTTPMethod = 'POST' | 'PUT' | 'DELETE' | 'PATCH';
49+
export type HTTPMethod = SimpleHTTPMethod | ComplexHTTPMethod;
50+
export type HttpType = 'SimpleHTTPMethod' | 'ComplexHTTPMethod';
51+
52+
interface DispatchPayload<T> {
53+
method: HTTPMethod;
54+
path: string;
55+
data?: T;
56+
headers?: any;
57+
}
58+
59+
export interface HTTPClientOptions {
60+
addr: string;
61+
axios: AxiosStatic;
62+
catcher: ErrorInteractProcessor;
63+
}
64+
65+
export enum ContentType {
66+
Form = 'application/x-www-form-urlencoded',
67+
}
68+
69+
export interface HTTPClient {
70+
create<T, R>(type: HttpType, payload: DispatchPayload<T>): Doer<R>;
71+
}
72+
73+
export class BrowserClient {
74+
constructor(private options: HTTPClientOptions) {}
75+
76+
create<T, R>(type: HttpType, payload: DispatchPayload<T>): Doer<R> {
77+
const {path, data, headers, method} = payload;
78+
const lowerCaseMethod = method.toLowerCase();
79+
80+
const requests: Requests<R> = {
81+
ComplexHTTPMethod: () =>
82+
this.options.axios[lowerCaseMethod]<R>(this.join(path), data || {}, {
83+
headers: {
84+
'Content-Type': ContentType.Form,
85+
...headers,
86+
},
87+
}),
88+
SimpleHTTPMethod: () =>
89+
this.options.axios[lowerCaseMethod]<R>(this.join(path), {
90+
params: data,
91+
headers: {
92+
...headers,
93+
},
94+
}),
95+
};
96+
97+
return {
98+
Do: () => this.Do<R>(() => requests[type]()),
99+
};
100+
}
101+
102+
private Do<R>(request: Request<R>): Response<R> {
103+
let err: AxiosError | undefined;
104+
let data: R | undefined;
105+
106+
const sendRequest = async (): Promise<ResValueArea<R>> => {
107+
try {
108+
data = await request();
109+
} catch (e) {
110+
err = e;
111+
}
112+
113+
const valueArea: ResValueArea<R> = {
114+
data,
115+
ok: !err,
116+
success: onSuccuss,
117+
expect: onExpect,
118+
pipe,
119+
};
120+
const that = this;
121+
122+
function pipe(cb: OnSuccessCB<R>): ResValueArea {
123+
return {
124+
...valueArea,
125+
data: cb(data),
126+
};
127+
}
128+
129+
function onSuccuss(cb: OnSuccessCB<R>): ResValueArea {
130+
return {
131+
...valueArea,
132+
data: err ? data : cb(data),
133+
};
134+
}
135+
136+
function onExpect(cb: ExpectableCB): ResValueArea {
137+
if (err) {
138+
const errMsg = cb(err);
139+
140+
if (errMsg !== undefined || errMsg !== null) {
141+
that.options.catcher(errMsg, err);
142+
}
143+
}
144+
145+
return valueArea;
146+
}
147+
148+
return valueArea;
149+
};
150+
151+
return sendRequest();
152+
}
153+
154+
private join(path: string): string {
155+
if (path.startsWith('http')) {
156+
return path;
157+
}
158+
159+
const parsedPath = path.replace(/^\/(.*)/, (_, cap) => cap);
160+
161+
return `${this.options.addr}${parsedPath}`;
162+
}
163+
}
164+
165+
const noopFn: any = () => {};
166+
export const noop: ResValueArea = {
167+
success: noopFn,
168+
expect: noopFn,
169+
data: undefined,
170+
ok: false,
171+
pipe: noopFn,
172+
};

src/lib/core/error.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {AxiosError} from 'axios';
2+
3+
export enum ErrorOperates {
4+
GLOBAL_PROCESS,
5+
}
6+
7+
export type ErrorMessage = string | ErrorOperates | void;
8+
9+
export type ErrorInteractProcessor = (errMsg: ErrorMessage, err: AxiosError) => void;
10+
11+
export interface ErrorInteractModule {
12+
errorInteract: ErrorInteractProcessor;
13+
}
14+
15+
export const isErrorInteractModule = (module: any): module is ErrorInteractModule =>
16+
!!module.errorInteract;
17+
18+
export const getErrorInteractFromModule = (module: any): ErrorInteractProcessor => {
19+
if (!isErrorInteractModule(module)) {
20+
return () => {};
21+
}
22+
23+
return module.errorInteract;
24+
};

src/lib/core/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './client';
2+
export * from './error';
3+
export * from './interceptor';

0 commit comments

Comments
 (0)