@nocobase/sdk 0.11.1-alpha.5 → 0.12.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/APIClient.ts +0 -321
- package/src/__tests__/api-client.test.ts +0 -124
- package/src/getSubAppName.ts +0 -9
- package/src/index.ts +0 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0-alpha.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "lib",
|
|
6
6
|
"module": "es/index.js",
|
|
@@ -12,5 +12,5 @@
|
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"axios-mock-adapter": "^1.20.0"
|
|
14
14
|
},
|
|
15
|
-
"gitHead": "
|
|
15
|
+
"gitHead": "543dcc0216b7b1451b552b0f685bc3e0682f9d59"
|
|
16
16
|
}
|
package/src/APIClient.ts
DELETED
|
@@ -1,321 +0,0 @@
|
|
|
1
|
-
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
2
|
-
import qs from 'qs';
|
|
3
|
-
import getSubAppName from './getSubAppName';
|
|
4
|
-
|
|
5
|
-
export interface ActionParams {
|
|
6
|
-
filterByTk?: any;
|
|
7
|
-
[key: string]: any;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
type ResourceActionOptions<P = any> = {
|
|
11
|
-
resource?: string;
|
|
12
|
-
resourceOf?: any;
|
|
13
|
-
action?: string;
|
|
14
|
-
params?: P;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export interface IResource {
|
|
18
|
-
list?: (params?: ActionParams) => Promise<any>;
|
|
19
|
-
get?: (params?: ActionParams) => Promise<any>;
|
|
20
|
-
create?: (params?: ActionParams) => Promise<any>;
|
|
21
|
-
update?: (params?: ActionParams) => Promise<any>;
|
|
22
|
-
destroy?: (params?: ActionParams) => Promise<any>;
|
|
23
|
-
[key: string]: (params?: ActionParams) => Promise<any>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export class Auth {
|
|
27
|
-
protected api: APIClient;
|
|
28
|
-
|
|
29
|
-
protected KEYS = {
|
|
30
|
-
locale: 'NOCOBASE_LOCALE',
|
|
31
|
-
role: 'NOCOBASE_ROLE',
|
|
32
|
-
token: 'NOCOBASE_TOKEN',
|
|
33
|
-
authenticator: 'NOCOBASE_AUTH',
|
|
34
|
-
theme: 'NOCOBASE_THEME',
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
protected options = {
|
|
38
|
-
locale: null,
|
|
39
|
-
role: null,
|
|
40
|
-
authenticator: null,
|
|
41
|
-
token: null,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
constructor(api: APIClient) {
|
|
45
|
-
this.api = api;
|
|
46
|
-
this.initKeys();
|
|
47
|
-
this.api.axios.interceptors.request.use(this.middleware.bind(this));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
initKeys() {
|
|
51
|
-
if (!window) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const appName = getSubAppName();
|
|
55
|
-
if (!appName) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
this.KEYS['role'] = `${appName.toUpperCase()}_` + this.KEYS['role'];
|
|
59
|
-
this.KEYS['locale'] = `${appName.toUpperCase()}_` + this.KEYS['locale'];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
get locale() {
|
|
63
|
-
return this.getLocale();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
set locale(value: string) {
|
|
67
|
-
this.setLocale(value);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
get role() {
|
|
71
|
-
return this.getRole();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
set role(value: string) {
|
|
75
|
-
this.setRole(value);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
get token() {
|
|
79
|
-
return this.getToken();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
set token(value: string) {
|
|
83
|
-
this.setToken(value);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
get authenticator() {
|
|
87
|
-
return this.getAuthenticator();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
set authenticator(value: string) {
|
|
91
|
-
this.setAuthenticator(value);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
getOption(key: string) {
|
|
95
|
-
if (!this.KEYS[key]) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
return this.api.storage.getItem(this.KEYS[key]);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
setOption(key: string, value?: string) {
|
|
102
|
-
if (!this.KEYS[key]) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
this.options[key] = value;
|
|
106
|
-
return this.api.storage.setItem(this.KEYS[key], value || '');
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
getLocale() {
|
|
110
|
-
return this.getOption('locale');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
setLocale(locale: string) {
|
|
114
|
-
this.setOption('locale', locale);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
getRole() {
|
|
118
|
-
return this.getOption('role');
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
setRole(role: string) {
|
|
122
|
-
this.setOption('role', role);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
getToken() {
|
|
126
|
-
return this.getOption('token');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
setToken(token: string) {
|
|
130
|
-
this.setOption('token', token);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
getAuthenticator() {
|
|
134
|
-
return this.getOption('authenticator');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
setAuthenticator(authenticator: string) {
|
|
138
|
-
this.setOption('authenticator', authenticator);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
middleware(config: AxiosRequestConfig) {
|
|
142
|
-
if (this.locale) {
|
|
143
|
-
config.headers['X-Locale'] = this.locale;
|
|
144
|
-
}
|
|
145
|
-
if (this.role) {
|
|
146
|
-
config.headers['X-Role'] = this.role;
|
|
147
|
-
}
|
|
148
|
-
if (this.authenticator && !config.headers['X-Authenticator']) {
|
|
149
|
-
config.headers['X-Authenticator'] = this.authenticator;
|
|
150
|
-
}
|
|
151
|
-
if (this.token) {
|
|
152
|
-
config.headers['Authorization'] = `Bearer ${this.token}`;
|
|
153
|
-
}
|
|
154
|
-
return config;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
async signIn(values: any, authenticator?: string): Promise<AxiosResponse<any>> {
|
|
158
|
-
const response = await this.api.request({
|
|
159
|
-
method: 'post',
|
|
160
|
-
url: 'auth:signIn',
|
|
161
|
-
data: values,
|
|
162
|
-
headers: {
|
|
163
|
-
'X-Authenticator': authenticator,
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
const data = response?.data?.data;
|
|
167
|
-
this.setToken(data?.token);
|
|
168
|
-
this.setAuthenticator(authenticator);
|
|
169
|
-
return response;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async signUp(values: any, authenticator?: string): Promise<AxiosResponse<any>> {
|
|
173
|
-
return await this.api.request({
|
|
174
|
-
method: 'post',
|
|
175
|
-
url: 'auth:signUp',
|
|
176
|
-
data: values,
|
|
177
|
-
headers: {
|
|
178
|
-
'X-Authenticator': authenticator,
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
async signOut() {
|
|
184
|
-
const response = await this.api.request({
|
|
185
|
-
method: 'post',
|
|
186
|
-
url: 'auth:signOut',
|
|
187
|
-
});
|
|
188
|
-
this.setToken(null);
|
|
189
|
-
this.setRole(null);
|
|
190
|
-
this.setAuthenticator(null);
|
|
191
|
-
return response;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export abstract class Storage {
|
|
196
|
-
abstract clear(): void;
|
|
197
|
-
abstract getItem(key: string): string | null;
|
|
198
|
-
abstract removeItem(key: string): void;
|
|
199
|
-
abstract setItem(key: string, value: string): void;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export class MemoryStorage extends Storage {
|
|
203
|
-
items = new Map();
|
|
204
|
-
|
|
205
|
-
clear() {
|
|
206
|
-
this.items.clear();
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
getItem(key: string) {
|
|
210
|
-
return this.items.get(key);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
setItem(key: string, value: string) {
|
|
214
|
-
return this.items.set(key, value);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
removeItem(key: string) {
|
|
218
|
-
return this.items.delete(key);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
interface ExtendedOptions {
|
|
223
|
-
authClass?: any;
|
|
224
|
-
storageClass?: any;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export type APIClientOptions = AxiosInstance | (AxiosRequestConfig & ExtendedOptions);
|
|
228
|
-
|
|
229
|
-
export class APIClient {
|
|
230
|
-
axios: AxiosInstance;
|
|
231
|
-
auth: Auth;
|
|
232
|
-
storage: Storage;
|
|
233
|
-
|
|
234
|
-
constructor(instance?: APIClientOptions) {
|
|
235
|
-
if (typeof instance === 'function') {
|
|
236
|
-
this.axios = instance;
|
|
237
|
-
} else {
|
|
238
|
-
const { authClass, storageClass, ...others } = instance || {};
|
|
239
|
-
this.axios = axios.create(others);
|
|
240
|
-
this.initStorage(storageClass);
|
|
241
|
-
if (authClass) {
|
|
242
|
-
this.auth = new authClass(this);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
if (!this.storage) {
|
|
246
|
-
this.initStorage();
|
|
247
|
-
}
|
|
248
|
-
if (!this.auth) {
|
|
249
|
-
this.auth = new Auth(this);
|
|
250
|
-
}
|
|
251
|
-
this.interceptors();
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
private initStorage(storage?: any) {
|
|
255
|
-
if (storage) {
|
|
256
|
-
this.storage = new storage(this);
|
|
257
|
-
} else if (typeof localStorage !== 'undefined') {
|
|
258
|
-
this.storage = localStorage;
|
|
259
|
-
} else {
|
|
260
|
-
this.storage = new MemoryStorage();
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
interceptors() {
|
|
265
|
-
this.axios.interceptors.request.use((config) => {
|
|
266
|
-
config.paramsSerializer = (params) => {
|
|
267
|
-
return qs.stringify(params, {
|
|
268
|
-
strictNullHandling: true,
|
|
269
|
-
arrayFormat: 'brackets',
|
|
270
|
-
});
|
|
271
|
-
};
|
|
272
|
-
return config;
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D> | ResourceActionOptions): Promise<R> {
|
|
277
|
-
const { resource, resourceOf, action, params } = config as any;
|
|
278
|
-
if (resource) {
|
|
279
|
-
return this.resource(resource, resourceOf)[action](params);
|
|
280
|
-
}
|
|
281
|
-
return this.axios.request<T, R, D>(config);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
resource(name: string, of?: any): IResource {
|
|
285
|
-
const target = {};
|
|
286
|
-
const handler = {
|
|
287
|
-
get: (_: any, actionName: string) => {
|
|
288
|
-
let url = name.split('.').join(`/${of || '_'}/`);
|
|
289
|
-
url += `:${actionName}`;
|
|
290
|
-
const config: AxiosRequestConfig = { url };
|
|
291
|
-
if (['get', 'list'].includes(actionName)) {
|
|
292
|
-
config['method'] = 'get';
|
|
293
|
-
} else {
|
|
294
|
-
config['method'] = 'post';
|
|
295
|
-
}
|
|
296
|
-
return async (params?: ActionParams, opts?: any) => {
|
|
297
|
-
const { values, filter, ...others } = params || {};
|
|
298
|
-
config['params'] = others;
|
|
299
|
-
if (filter) {
|
|
300
|
-
if (typeof filter === 'string') {
|
|
301
|
-
config['params']['filter'] = filter;
|
|
302
|
-
} else {
|
|
303
|
-
if (filter['*']) {
|
|
304
|
-
delete filter['*'];
|
|
305
|
-
}
|
|
306
|
-
config['params']['filter'] = JSON.stringify(filter);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
if (config.method !== 'get') {
|
|
310
|
-
config['data'] = values || {};
|
|
311
|
-
}
|
|
312
|
-
return await this.request({
|
|
313
|
-
...config,
|
|
314
|
-
...opts,
|
|
315
|
-
});
|
|
316
|
-
};
|
|
317
|
-
},
|
|
318
|
-
};
|
|
319
|
-
return new Proxy(target, handler);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { AxiosResponse } from 'axios';
|
|
2
|
-
import MockAdapter from 'axios-mock-adapter';
|
|
3
|
-
import { APIClient, Storage } from '../';
|
|
4
|
-
import { Auth } from '../APIClient';
|
|
5
|
-
|
|
6
|
-
describe('api-client', () => {
|
|
7
|
-
test('instance', async () => {
|
|
8
|
-
const api = new APIClient({
|
|
9
|
-
baseURL: 'https://localhost:8000/api',
|
|
10
|
-
});
|
|
11
|
-
const mock = new MockAdapter(api.axios);
|
|
12
|
-
mock.onGet('users:get').reply(200, {
|
|
13
|
-
data: { id: 1, name: 'John Smith' },
|
|
14
|
-
});
|
|
15
|
-
const response = await api.request({ url: 'users:get' });
|
|
16
|
-
expect(response.status).toBe(200);
|
|
17
|
-
expect(response.data).toMatchObject({
|
|
18
|
-
data: { id: 1, name: 'John Smith' },
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test('signIn', async () => {
|
|
23
|
-
const api = new APIClient({
|
|
24
|
-
baseURL: 'https://localhost:8000/api',
|
|
25
|
-
});
|
|
26
|
-
const mock = new MockAdapter(api.axios);
|
|
27
|
-
mock.onPost('auth:signIn').reply(200, {
|
|
28
|
-
data: { id: 1, name: 'John Smith', token: '123' },
|
|
29
|
-
});
|
|
30
|
-
const response = await api.auth.signIn({}, 'basic');
|
|
31
|
-
expect(response.status).toBe(200);
|
|
32
|
-
expect(api.auth.getToken()).toBe('123');
|
|
33
|
-
const token = localStorage.getItem('NOCOBASE_TOKEN');
|
|
34
|
-
expect(token).toBe('123');
|
|
35
|
-
const auth = localStorage.getItem('NOCOBASE_AUTH');
|
|
36
|
-
expect(auth).toBe('basic');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test('resource action', async () => {
|
|
40
|
-
const api = new APIClient({
|
|
41
|
-
baseURL: 'https://localhost:8000/api',
|
|
42
|
-
});
|
|
43
|
-
const mock = new MockAdapter(api.axios);
|
|
44
|
-
mock.onPost('users:test').reply(200, {
|
|
45
|
-
data: { id: 1, name: 'John Smith', token: '123' },
|
|
46
|
-
});
|
|
47
|
-
const response = await api.resource('users').test();
|
|
48
|
-
expect(response.status).toBe(200);
|
|
49
|
-
expect(response.data).toMatchObject({
|
|
50
|
-
data: { id: 1, name: 'John Smith', token: '123' },
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('custom storage', async () => {
|
|
55
|
-
const items = new Map();
|
|
56
|
-
|
|
57
|
-
class TestStorage extends Storage {
|
|
58
|
-
clear() {
|
|
59
|
-
items.clear();
|
|
60
|
-
}
|
|
61
|
-
getItem(key: string) {
|
|
62
|
-
return items.get(key);
|
|
63
|
-
}
|
|
64
|
-
setItem(key: string, value: string) {
|
|
65
|
-
return items.set(key, value);
|
|
66
|
-
}
|
|
67
|
-
removeItem(key: string) {
|
|
68
|
-
return items.delete(key);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const api = new APIClient({
|
|
73
|
-
baseURL: 'https://localhost:8000/api',
|
|
74
|
-
storageClass: TestStorage,
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
const mock = new MockAdapter(api.axios);
|
|
78
|
-
mock.onPost('auth:signIn').reply(200, {
|
|
79
|
-
data: { id: 1, name: 'John Smith', token: '123' },
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const response = await api.auth.signIn({});
|
|
83
|
-
expect(response.status).toBe(200);
|
|
84
|
-
expect(api.auth.getToken()).toBe('123');
|
|
85
|
-
const token = items.get('NOCOBASE_TOKEN');
|
|
86
|
-
expect(token).toBe('123');
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test('custom auth', async () => {
|
|
90
|
-
class TestAuth extends Auth {
|
|
91
|
-
async signIn(values: any): Promise<AxiosResponse<any, any>> {
|
|
92
|
-
const response = await this.api.request({
|
|
93
|
-
method: 'post',
|
|
94
|
-
url: 'auth:test',
|
|
95
|
-
data: values,
|
|
96
|
-
});
|
|
97
|
-
const data = response?.data?.data;
|
|
98
|
-
this.setAuthenticator('test');
|
|
99
|
-
this.setToken(data?.token);
|
|
100
|
-
return response;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const api = new APIClient({
|
|
105
|
-
baseURL: 'https://localhost:8000/api',
|
|
106
|
-
authClass: TestAuth,
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
expect(api.auth).toBeInstanceOf(TestAuth);
|
|
110
|
-
|
|
111
|
-
const mock = new MockAdapter(api.axios);
|
|
112
|
-
mock.onPost('auth:test').reply(200, {
|
|
113
|
-
data: { id: 1, name: 'John Smith', token: '123' },
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
const response = await api.auth.signIn({});
|
|
117
|
-
expect(response.status).toBe(200);
|
|
118
|
-
expect(api.auth.getToken()).toBe('123');
|
|
119
|
-
const token = localStorage.getItem('NOCOBASE_TOKEN');
|
|
120
|
-
expect(token).toBe('123');
|
|
121
|
-
const auth = localStorage.getItem('NOCOBASE_AUTH');
|
|
122
|
-
expect(auth).toBe('test');
|
|
123
|
-
});
|
|
124
|
-
});
|
package/src/getSubAppName.ts
DELETED
package/src/index.ts
DELETED