@ps-aux/api-client-angular 0.1.0-rc1
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/dist/AngularApiHttpClient.d.ts +5 -0
- package/dist/api-client-angular/src/AngularApiHttpClient.d.ts +3 -0
- package/dist/api-client-angular/src/index.d.ts +1 -0
- package/dist/api-client-angular/src/serializeQuery.d.ts +2 -0
- package/dist/api-client-angular/src/types.d.ts +20 -0
- package/dist/api-client-common/src/index.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +334 -0
- package/dist/index.js +338 -0
- package/dist/serializeQuery.d.ts +2 -0
- package/dist/types.d.ts +19 -0
- package/package.json +22 -0
- package/rollup.config.mjs +29 -0
- package/src/AngularApiHttpClient.ts +87 -0
- package/src/index.ts +2 -0
- package/src/serializeQuery.ts +45 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { HttpClient as AngularHttpClient } from '@angular/common/http';
|
|
2
|
+
import { UrlConverter, ObservableHttpClient } from '@ps-aux/api-client-common';
|
|
3
|
+
export declare const qsUrlConverter: () => UrlConverter;
|
|
4
|
+
export declare const springBootUrlConverter: () => UrlConverter;
|
|
5
|
+
export declare const createHttpClient: (client: AngularHttpClient, urlConverter?: UrlConverter) => ObservableHttpClient;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createHttpClient } from './AngularApiHttpClient';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
export type ContentType = {};
|
|
3
|
+
export type RequestParams = {};
|
|
4
|
+
export declare const ContentType: {
|
|
5
|
+
Json: string;
|
|
6
|
+
FormData: string;
|
|
7
|
+
};
|
|
8
|
+
export type Request = {
|
|
9
|
+
path: string;
|
|
10
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
11
|
+
format?: 'json';
|
|
12
|
+
query?: any;
|
|
13
|
+
body?: any;
|
|
14
|
+
type?: string;
|
|
15
|
+
secure?: boolean;
|
|
16
|
+
responseType?: 'arraybuffer' | 'blob' | 'json' | 'text';
|
|
17
|
+
};
|
|
18
|
+
export type HttpClient<Any = any> = {
|
|
19
|
+
request: <Data, A = any>(req: Request) => Observable<Data>;
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const myFun: (f: string) => string;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createHttpClient, springBootUrlConverter, qsUrlConverter } from './AngularApiHttpClient';
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { map } from 'rxjs';
|
|
2
|
+
import qs from 'qs';
|
|
3
|
+
|
|
4
|
+
const { isArray } = Array;
|
|
5
|
+
|
|
6
|
+
function fromPairs(listOfPairs){
|
|
7
|
+
const toReturn = {};
|
|
8
|
+
listOfPairs.forEach(([ prop, value ]) => toReturn[ prop ] = value);
|
|
9
|
+
|
|
10
|
+
return toReturn
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function partitionObject(predicate, iterable){
|
|
14
|
+
const yes = {};
|
|
15
|
+
const no = {};
|
|
16
|
+
Object.entries(iterable).forEach(([ prop, value ]) => {
|
|
17
|
+
if (predicate(value, prop)){
|
|
18
|
+
yes[ prop ] = value;
|
|
19
|
+
} else {
|
|
20
|
+
no[ prop ] = value;
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return [ yes, no ]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function partitionArray(
|
|
28
|
+
predicate, list, indexed = false
|
|
29
|
+
){
|
|
30
|
+
const yes = [];
|
|
31
|
+
const no = [];
|
|
32
|
+
let counter = -1;
|
|
33
|
+
|
|
34
|
+
while (counter++ < list.length - 1){
|
|
35
|
+
if (
|
|
36
|
+
indexed ? predicate(list[ counter ], counter) : predicate(list[ counter ])
|
|
37
|
+
){
|
|
38
|
+
yes.push(list[ counter ]);
|
|
39
|
+
} else {
|
|
40
|
+
no.push(list[ counter ]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return [ yes, no ]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function partition(predicate, iterable){
|
|
48
|
+
if (arguments.length === 1){
|
|
49
|
+
return listHolder => partition(predicate, listHolder)
|
|
50
|
+
}
|
|
51
|
+
if (!isArray(iterable)) return partitionObject(predicate, iterable)
|
|
52
|
+
|
|
53
|
+
return partitionArray(predicate, iterable)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
var flat = flatten$1;
|
|
57
|
+
flatten$1.flatten = flatten$1;
|
|
58
|
+
flatten$1.unflatten = unflatten;
|
|
59
|
+
|
|
60
|
+
function isBuffer (obj) {
|
|
61
|
+
return obj &&
|
|
62
|
+
obj.constructor &&
|
|
63
|
+
(typeof obj.constructor.isBuffer === 'function') &&
|
|
64
|
+
obj.constructor.isBuffer(obj)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function keyIdentity (key) {
|
|
68
|
+
return key
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function flatten$1 (target, opts) {
|
|
72
|
+
opts = opts || {};
|
|
73
|
+
|
|
74
|
+
const delimiter = opts.delimiter || '.';
|
|
75
|
+
const maxDepth = opts.maxDepth;
|
|
76
|
+
const transformKey = opts.transformKey || keyIdentity;
|
|
77
|
+
const output = {};
|
|
78
|
+
|
|
79
|
+
function step (object, prev, currentDepth) {
|
|
80
|
+
currentDepth = currentDepth || 1;
|
|
81
|
+
Object.keys(object).forEach(function (key) {
|
|
82
|
+
const value = object[key];
|
|
83
|
+
const isarray = opts.safe && Array.isArray(value);
|
|
84
|
+
const type = Object.prototype.toString.call(value);
|
|
85
|
+
const isbuffer = isBuffer(value);
|
|
86
|
+
const isobject = (
|
|
87
|
+
type === '[object Object]' ||
|
|
88
|
+
type === '[object Array]'
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const newKey = prev
|
|
92
|
+
? prev + delimiter + transformKey(key)
|
|
93
|
+
: transformKey(key);
|
|
94
|
+
|
|
95
|
+
if (!isarray && !isbuffer && isobject && Object.keys(value).length &&
|
|
96
|
+
(!opts.maxDepth || currentDepth < maxDepth)) {
|
|
97
|
+
return step(value, newKey, currentDepth + 1)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
output[newKey] = value;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
step(target);
|
|
105
|
+
|
|
106
|
+
return output
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function unflatten (target, opts) {
|
|
110
|
+
opts = opts || {};
|
|
111
|
+
|
|
112
|
+
const delimiter = opts.delimiter || '.';
|
|
113
|
+
const overwrite = opts.overwrite || false;
|
|
114
|
+
const transformKey = opts.transformKey || keyIdentity;
|
|
115
|
+
const result = {};
|
|
116
|
+
|
|
117
|
+
const isbuffer = isBuffer(target);
|
|
118
|
+
if (isbuffer || Object.prototype.toString.call(target) !== '[object Object]') {
|
|
119
|
+
return target
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// safely ensure that the key is
|
|
123
|
+
// an integer.
|
|
124
|
+
function getkey (key) {
|
|
125
|
+
const parsedKey = Number(key);
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
isNaN(parsedKey) ||
|
|
129
|
+
key.indexOf('.') !== -1 ||
|
|
130
|
+
opts.object
|
|
131
|
+
) ? key
|
|
132
|
+
: parsedKey
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function addKeys (keyPrefix, recipient, target) {
|
|
136
|
+
return Object.keys(target).reduce(function (result, key) {
|
|
137
|
+
result[keyPrefix + delimiter + key] = target[key];
|
|
138
|
+
|
|
139
|
+
return result
|
|
140
|
+
}, recipient)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isEmpty (val) {
|
|
144
|
+
const type = Object.prototype.toString.call(val);
|
|
145
|
+
const isArray = type === '[object Array]';
|
|
146
|
+
const isObject = type === '[object Object]';
|
|
147
|
+
|
|
148
|
+
if (!val) {
|
|
149
|
+
return true
|
|
150
|
+
} else if (isArray) {
|
|
151
|
+
return !val.length
|
|
152
|
+
} else if (isObject) {
|
|
153
|
+
return !Object.keys(val).length
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
target = Object.keys(target).reduce(function (result, key) {
|
|
158
|
+
const type = Object.prototype.toString.call(target[key]);
|
|
159
|
+
const isObject = (type === '[object Object]' || type === '[object Array]');
|
|
160
|
+
if (!isObject || isEmpty(target[key])) {
|
|
161
|
+
result[key] = target[key];
|
|
162
|
+
return result
|
|
163
|
+
} else {
|
|
164
|
+
return addKeys(
|
|
165
|
+
key,
|
|
166
|
+
result,
|
|
167
|
+
flatten$1(target[key], opts)
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
}, {});
|
|
171
|
+
|
|
172
|
+
Object.keys(target).forEach(function (key) {
|
|
173
|
+
const split = key.split(delimiter).map(transformKey);
|
|
174
|
+
let key1 = getkey(split.shift());
|
|
175
|
+
let key2 = getkey(split[0]);
|
|
176
|
+
let recipient = result;
|
|
177
|
+
|
|
178
|
+
while (key2 !== undefined) {
|
|
179
|
+
if (key1 === '__proto__') {
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const type = Object.prototype.toString.call(recipient[key1]);
|
|
184
|
+
const isobject = (
|
|
185
|
+
type === '[object Object]' ||
|
|
186
|
+
type === '[object Array]'
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
// do not write over falsey, non-undefined values if overwrite is false
|
|
190
|
+
if (!overwrite && !isobject && typeof recipient[key1] !== 'undefined') {
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if ((overwrite && !isobject) || (!overwrite && recipient[key1] == null)) {
|
|
195
|
+
recipient[key1] = (
|
|
196
|
+
typeof key2 === 'number' &&
|
|
197
|
+
!opts.object ? [] : {}
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
recipient = recipient[key1];
|
|
202
|
+
if (split.length > 0) {
|
|
203
|
+
key1 = getkey(split.shift());
|
|
204
|
+
key2 = getkey(split[0]);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// unflatten again for 'messy objects'
|
|
209
|
+
recipient[key1] = unflatten(target[key], opts);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return result
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Brings the 1st level to the 0th level.
|
|
217
|
+
* Flats the object by nesting with '.' path separator.
|
|
218
|
+
*/
|
|
219
|
+
const serializeQueryForSpringBoot = (obj) => {
|
|
220
|
+
const parts = [];
|
|
221
|
+
Object.entries(obj).forEach(([key, val]) => {
|
|
222
|
+
if (Array.isArray(val)) {
|
|
223
|
+
val.forEach((v) => {
|
|
224
|
+
parts.push([key, v]);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
else if (typeof val === 'object') {
|
|
228
|
+
objectToParams(val).forEach(([key, val]) => {
|
|
229
|
+
parts.push([key, val.toString()]);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
parts.push([key, val]);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
return parts.map((p) => `${p[0]}=${p[1]}`).join('&');
|
|
237
|
+
};
|
|
238
|
+
const flatten = (obj) => {
|
|
239
|
+
const r = flat.flatten(obj);
|
|
240
|
+
// { empty: {} } would be { empty: {} } instead of empty array
|
|
241
|
+
return Object.entries(r).filter(([k, v]) => typeof v !== 'object');
|
|
242
|
+
};
|
|
243
|
+
const objectToParams = (obj) => {
|
|
244
|
+
const [arrayProps, nonArrayProps] = partition((e) => Array.isArray(e[1]), Object.entries(obj));
|
|
245
|
+
const withoutArrayProps = fromPairs(nonArrayProps);
|
|
246
|
+
const res = flatten(withoutArrayProps);
|
|
247
|
+
arrayProps.forEach(([k, vals]) => {
|
|
248
|
+
vals.forEach((v) => res.push([k, v]));
|
|
249
|
+
});
|
|
250
|
+
return res;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// @ts-ignore
|
|
254
|
+
const serializeQueryWithQs = (obj) => {
|
|
255
|
+
return qs.stringify(obj);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const fileResFilenameRegex = /filename="([^"]+)"/;
|
|
259
|
+
const getFileNameFromContentDispositionHeader = (contentDispositionHeader) => {
|
|
260
|
+
if (!contentDispositionHeader)
|
|
261
|
+
return 'download-file';
|
|
262
|
+
const match = contentDispositionHeader.match(fileResFilenameRegex);
|
|
263
|
+
const fileName = match ? match[1] : 'downloaded-file';
|
|
264
|
+
return fileName;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const ContentType = {
|
|
268
|
+
Json: 'application/json',
|
|
269
|
+
FormData: 'multipart/form-data',
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const qsUrlConverter = () => ({ query, path }) => {
|
|
273
|
+
return {
|
|
274
|
+
url: path + (query ? `?${serializeQueryWithQs(query)}` : ''),
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
const springBootUrlConverter = () => ({ query, path }) => {
|
|
278
|
+
return {
|
|
279
|
+
url: path + (query ? `?${serializeQueryForSpringBoot(query)}` : ''),
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
const createHttpClient = (client, urlConverter = qsUrlConverter()) => {
|
|
283
|
+
return {
|
|
284
|
+
request: (req) => {
|
|
285
|
+
const { url, params } = urlConverter(req);
|
|
286
|
+
const isBlob = req.format = 'document';
|
|
287
|
+
const obs = client.request(req.method, url, {
|
|
288
|
+
params,
|
|
289
|
+
body: prepareBody(req.body, req.type),
|
|
290
|
+
responseType: (isBlob ? 'blob' : 'json'),
|
|
291
|
+
observe: 'response',
|
|
292
|
+
});
|
|
293
|
+
return obs.pipe(map((r) => {
|
|
294
|
+
const { body } = r;
|
|
295
|
+
if (!isBlob)
|
|
296
|
+
return body;
|
|
297
|
+
const data = body;
|
|
298
|
+
const contDisp = r.headers.get('content-disposition');
|
|
299
|
+
return new File([data], getFileNameFromContentDispositionHeader(contDisp), {
|
|
300
|
+
type: data.type
|
|
301
|
+
});
|
|
302
|
+
}));
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
};
|
|
306
|
+
const prepareBody = (payload, type) => {
|
|
307
|
+
if (type === ContentType.FormData) {
|
|
308
|
+
return convertToFormData(payload);
|
|
309
|
+
}
|
|
310
|
+
return payload;
|
|
311
|
+
};
|
|
312
|
+
const convertToFormData = (payload) => {
|
|
313
|
+
const formData = new FormData();
|
|
314
|
+
const addProp = (key, val) => {
|
|
315
|
+
if (Array.isArray(val) || val instanceof FileList) {
|
|
316
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
317
|
+
// @ts-ignore - seems that FileList is iterable despite the warning
|
|
318
|
+
// TODO change to other iteration method if this is not true
|
|
319
|
+
for (const valItem of val) {
|
|
320
|
+
addProp(key, valItem);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
else if (typeof val === 'object' && val != null && !(val instanceof File)) {
|
|
324
|
+
throw new Error('Object serialization into FormData not supported');
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
formData.append(key, val);
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
Object.entries(payload).forEach(([key, val]) => addProp(key, val));
|
|
331
|
+
return formData;
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
export { createHttpClient, qsUrlConverter, springBootUrlConverter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var rxjs = require('rxjs');
|
|
4
|
+
var qs = require('qs');
|
|
5
|
+
|
|
6
|
+
const { isArray } = Array;
|
|
7
|
+
|
|
8
|
+
function fromPairs(listOfPairs){
|
|
9
|
+
const toReturn = {};
|
|
10
|
+
listOfPairs.forEach(([ prop, value ]) => toReturn[ prop ] = value);
|
|
11
|
+
|
|
12
|
+
return toReturn
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function partitionObject(predicate, iterable){
|
|
16
|
+
const yes = {};
|
|
17
|
+
const no = {};
|
|
18
|
+
Object.entries(iterable).forEach(([ prop, value ]) => {
|
|
19
|
+
if (predicate(value, prop)){
|
|
20
|
+
yes[ prop ] = value;
|
|
21
|
+
} else {
|
|
22
|
+
no[ prop ] = value;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return [ yes, no ]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function partitionArray(
|
|
30
|
+
predicate, list, indexed = false
|
|
31
|
+
){
|
|
32
|
+
const yes = [];
|
|
33
|
+
const no = [];
|
|
34
|
+
let counter = -1;
|
|
35
|
+
|
|
36
|
+
while (counter++ < list.length - 1){
|
|
37
|
+
if (
|
|
38
|
+
indexed ? predicate(list[ counter ], counter) : predicate(list[ counter ])
|
|
39
|
+
){
|
|
40
|
+
yes.push(list[ counter ]);
|
|
41
|
+
} else {
|
|
42
|
+
no.push(list[ counter ]);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return [ yes, no ]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function partition(predicate, iterable){
|
|
50
|
+
if (arguments.length === 1){
|
|
51
|
+
return listHolder => partition(predicate, listHolder)
|
|
52
|
+
}
|
|
53
|
+
if (!isArray(iterable)) return partitionObject(predicate, iterable)
|
|
54
|
+
|
|
55
|
+
return partitionArray(predicate, iterable)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
var flat = flatten$1;
|
|
59
|
+
flatten$1.flatten = flatten$1;
|
|
60
|
+
flatten$1.unflatten = unflatten;
|
|
61
|
+
|
|
62
|
+
function isBuffer (obj) {
|
|
63
|
+
return obj &&
|
|
64
|
+
obj.constructor &&
|
|
65
|
+
(typeof obj.constructor.isBuffer === 'function') &&
|
|
66
|
+
obj.constructor.isBuffer(obj)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function keyIdentity (key) {
|
|
70
|
+
return key
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function flatten$1 (target, opts) {
|
|
74
|
+
opts = opts || {};
|
|
75
|
+
|
|
76
|
+
const delimiter = opts.delimiter || '.';
|
|
77
|
+
const maxDepth = opts.maxDepth;
|
|
78
|
+
const transformKey = opts.transformKey || keyIdentity;
|
|
79
|
+
const output = {};
|
|
80
|
+
|
|
81
|
+
function step (object, prev, currentDepth) {
|
|
82
|
+
currentDepth = currentDepth || 1;
|
|
83
|
+
Object.keys(object).forEach(function (key) {
|
|
84
|
+
const value = object[key];
|
|
85
|
+
const isarray = opts.safe && Array.isArray(value);
|
|
86
|
+
const type = Object.prototype.toString.call(value);
|
|
87
|
+
const isbuffer = isBuffer(value);
|
|
88
|
+
const isobject = (
|
|
89
|
+
type === '[object Object]' ||
|
|
90
|
+
type === '[object Array]'
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const newKey = prev
|
|
94
|
+
? prev + delimiter + transformKey(key)
|
|
95
|
+
: transformKey(key);
|
|
96
|
+
|
|
97
|
+
if (!isarray && !isbuffer && isobject && Object.keys(value).length &&
|
|
98
|
+
(!opts.maxDepth || currentDepth < maxDepth)) {
|
|
99
|
+
return step(value, newKey, currentDepth + 1)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
output[newKey] = value;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
step(target);
|
|
107
|
+
|
|
108
|
+
return output
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function unflatten (target, opts) {
|
|
112
|
+
opts = opts || {};
|
|
113
|
+
|
|
114
|
+
const delimiter = opts.delimiter || '.';
|
|
115
|
+
const overwrite = opts.overwrite || false;
|
|
116
|
+
const transformKey = opts.transformKey || keyIdentity;
|
|
117
|
+
const result = {};
|
|
118
|
+
|
|
119
|
+
const isbuffer = isBuffer(target);
|
|
120
|
+
if (isbuffer || Object.prototype.toString.call(target) !== '[object Object]') {
|
|
121
|
+
return target
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// safely ensure that the key is
|
|
125
|
+
// an integer.
|
|
126
|
+
function getkey (key) {
|
|
127
|
+
const parsedKey = Number(key);
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
isNaN(parsedKey) ||
|
|
131
|
+
key.indexOf('.') !== -1 ||
|
|
132
|
+
opts.object
|
|
133
|
+
) ? key
|
|
134
|
+
: parsedKey
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function addKeys (keyPrefix, recipient, target) {
|
|
138
|
+
return Object.keys(target).reduce(function (result, key) {
|
|
139
|
+
result[keyPrefix + delimiter + key] = target[key];
|
|
140
|
+
|
|
141
|
+
return result
|
|
142
|
+
}, recipient)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function isEmpty (val) {
|
|
146
|
+
const type = Object.prototype.toString.call(val);
|
|
147
|
+
const isArray = type === '[object Array]';
|
|
148
|
+
const isObject = type === '[object Object]';
|
|
149
|
+
|
|
150
|
+
if (!val) {
|
|
151
|
+
return true
|
|
152
|
+
} else if (isArray) {
|
|
153
|
+
return !val.length
|
|
154
|
+
} else if (isObject) {
|
|
155
|
+
return !Object.keys(val).length
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
target = Object.keys(target).reduce(function (result, key) {
|
|
160
|
+
const type = Object.prototype.toString.call(target[key]);
|
|
161
|
+
const isObject = (type === '[object Object]' || type === '[object Array]');
|
|
162
|
+
if (!isObject || isEmpty(target[key])) {
|
|
163
|
+
result[key] = target[key];
|
|
164
|
+
return result
|
|
165
|
+
} else {
|
|
166
|
+
return addKeys(
|
|
167
|
+
key,
|
|
168
|
+
result,
|
|
169
|
+
flatten$1(target[key], opts)
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
}, {});
|
|
173
|
+
|
|
174
|
+
Object.keys(target).forEach(function (key) {
|
|
175
|
+
const split = key.split(delimiter).map(transformKey);
|
|
176
|
+
let key1 = getkey(split.shift());
|
|
177
|
+
let key2 = getkey(split[0]);
|
|
178
|
+
let recipient = result;
|
|
179
|
+
|
|
180
|
+
while (key2 !== undefined) {
|
|
181
|
+
if (key1 === '__proto__') {
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const type = Object.prototype.toString.call(recipient[key1]);
|
|
186
|
+
const isobject = (
|
|
187
|
+
type === '[object Object]' ||
|
|
188
|
+
type === '[object Array]'
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
// do not write over falsey, non-undefined values if overwrite is false
|
|
192
|
+
if (!overwrite && !isobject && typeof recipient[key1] !== 'undefined') {
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if ((overwrite && !isobject) || (!overwrite && recipient[key1] == null)) {
|
|
197
|
+
recipient[key1] = (
|
|
198
|
+
typeof key2 === 'number' &&
|
|
199
|
+
!opts.object ? [] : {}
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
recipient = recipient[key1];
|
|
204
|
+
if (split.length > 0) {
|
|
205
|
+
key1 = getkey(split.shift());
|
|
206
|
+
key2 = getkey(split[0]);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// unflatten again for 'messy objects'
|
|
211
|
+
recipient[key1] = unflatten(target[key], opts);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return result
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Brings the 1st level to the 0th level.
|
|
219
|
+
* Flats the object by nesting with '.' path separator.
|
|
220
|
+
*/
|
|
221
|
+
const serializeQueryForSpringBoot = (obj) => {
|
|
222
|
+
const parts = [];
|
|
223
|
+
Object.entries(obj).forEach(([key, val]) => {
|
|
224
|
+
if (Array.isArray(val)) {
|
|
225
|
+
val.forEach((v) => {
|
|
226
|
+
parts.push([key, v]);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
else if (typeof val === 'object') {
|
|
230
|
+
objectToParams(val).forEach(([key, val]) => {
|
|
231
|
+
parts.push([key, val.toString()]);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
parts.push([key, val]);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
return parts.map((p) => `${p[0]}=${p[1]}`).join('&');
|
|
239
|
+
};
|
|
240
|
+
const flatten = (obj) => {
|
|
241
|
+
const r = flat.flatten(obj);
|
|
242
|
+
// { empty: {} } would be { empty: {} } instead of empty array
|
|
243
|
+
return Object.entries(r).filter(([k, v]) => typeof v !== 'object');
|
|
244
|
+
};
|
|
245
|
+
const objectToParams = (obj) => {
|
|
246
|
+
const [arrayProps, nonArrayProps] = partition((e) => Array.isArray(e[1]), Object.entries(obj));
|
|
247
|
+
const withoutArrayProps = fromPairs(nonArrayProps);
|
|
248
|
+
const res = flatten(withoutArrayProps);
|
|
249
|
+
arrayProps.forEach(([k, vals]) => {
|
|
250
|
+
vals.forEach((v) => res.push([k, v]));
|
|
251
|
+
});
|
|
252
|
+
return res;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// @ts-ignore
|
|
256
|
+
const serializeQueryWithQs = (obj) => {
|
|
257
|
+
return qs.stringify(obj);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const fileResFilenameRegex = /filename="([^"]+)"/;
|
|
261
|
+
const getFileNameFromContentDispositionHeader = (contentDispositionHeader) => {
|
|
262
|
+
if (!contentDispositionHeader)
|
|
263
|
+
return 'download-file';
|
|
264
|
+
const match = contentDispositionHeader.match(fileResFilenameRegex);
|
|
265
|
+
const fileName = match ? match[1] : 'downloaded-file';
|
|
266
|
+
return fileName;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const ContentType = {
|
|
270
|
+
Json: 'application/json',
|
|
271
|
+
FormData: 'multipart/form-data',
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const qsUrlConverter = () => ({ query, path }) => {
|
|
275
|
+
return {
|
|
276
|
+
url: path + (query ? `?${serializeQueryWithQs(query)}` : ''),
|
|
277
|
+
};
|
|
278
|
+
};
|
|
279
|
+
const springBootUrlConverter = () => ({ query, path }) => {
|
|
280
|
+
return {
|
|
281
|
+
url: path + (query ? `?${serializeQueryForSpringBoot(query)}` : ''),
|
|
282
|
+
};
|
|
283
|
+
};
|
|
284
|
+
const createHttpClient = (client, urlConverter = qsUrlConverter()) => {
|
|
285
|
+
return {
|
|
286
|
+
request: (req) => {
|
|
287
|
+
const { url, params } = urlConverter(req);
|
|
288
|
+
const isBlob = req.format = 'document';
|
|
289
|
+
const obs = client.request(req.method, url, {
|
|
290
|
+
params,
|
|
291
|
+
body: prepareBody(req.body, req.type),
|
|
292
|
+
responseType: (isBlob ? 'blob' : 'json'),
|
|
293
|
+
observe: 'response',
|
|
294
|
+
});
|
|
295
|
+
return obs.pipe(rxjs.map((r) => {
|
|
296
|
+
const { body } = r;
|
|
297
|
+
if (!isBlob)
|
|
298
|
+
return body;
|
|
299
|
+
const data = body;
|
|
300
|
+
const contDisp = r.headers.get('content-disposition');
|
|
301
|
+
return new File([data], getFileNameFromContentDispositionHeader(contDisp), {
|
|
302
|
+
type: data.type
|
|
303
|
+
});
|
|
304
|
+
}));
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
};
|
|
308
|
+
const prepareBody = (payload, type) => {
|
|
309
|
+
if (type === ContentType.FormData) {
|
|
310
|
+
return convertToFormData(payload);
|
|
311
|
+
}
|
|
312
|
+
return payload;
|
|
313
|
+
};
|
|
314
|
+
const convertToFormData = (payload) => {
|
|
315
|
+
const formData = new FormData();
|
|
316
|
+
const addProp = (key, val) => {
|
|
317
|
+
if (Array.isArray(val) || val instanceof FileList) {
|
|
318
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
319
|
+
// @ts-ignore - seems that FileList is iterable despite the warning
|
|
320
|
+
// TODO change to other iteration method if this is not true
|
|
321
|
+
for (const valItem of val) {
|
|
322
|
+
addProp(key, valItem);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
else if (typeof val === 'object' && val != null && !(val instanceof File)) {
|
|
326
|
+
throw new Error('Object serialization into FormData not supported');
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
formData.append(key, val);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
Object.entries(payload).forEach(([key, val]) => addProp(key, val));
|
|
333
|
+
return formData;
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
exports.createHttpClient = createHttpClient;
|
|
337
|
+
exports.qsUrlConverter = qsUrlConverter;
|
|
338
|
+
exports.springBootUrlConverter = springBootUrlConverter;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
export type ContentType = {};
|
|
3
|
+
export type RequestParams = {};
|
|
4
|
+
export declare const ContentType: {
|
|
5
|
+
Json: string;
|
|
6
|
+
FormData: string;
|
|
7
|
+
};
|
|
8
|
+
export type Request = {
|
|
9
|
+
path: string;
|
|
10
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
11
|
+
format?: 'json';
|
|
12
|
+
query?: any;
|
|
13
|
+
body?: any;
|
|
14
|
+
type?: string;
|
|
15
|
+
secure?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type HttpClient<Any = any> = {
|
|
18
|
+
request: <Data, A = any>(req: Request) => Observable<Data>;
|
|
19
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ps-aux/api-client-angular",
|
|
3
|
+
"version": "0.1.0-rc1",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"module": "dist/index.esm.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "rollup -c",
|
|
9
|
+
"dev": "rollup -c --watch",
|
|
10
|
+
"publish": "npm run build && npm --access public publish",
|
|
11
|
+
"tc": "tsc"
|
|
12
|
+
},
|
|
13
|
+
"author": "",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"qs": "^6.11.2",
|
|
17
|
+
"rxjs": "^7.8.1"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@angular/common": "^16.2.12"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import typescript from 'rollup-plugin-typescript2'
|
|
2
|
+
import resolve from '@rollup/plugin-node-resolve'
|
|
3
|
+
import commonjs from '@rollup/plugin-commonjs'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @type {import('rollup').RollupOptions}
|
|
7
|
+
*/
|
|
8
|
+
const config = {
|
|
9
|
+
input: 'src/index.ts',
|
|
10
|
+
output: [
|
|
11
|
+
{
|
|
12
|
+
file: `dist/index.js`,
|
|
13
|
+
format: 'cjs',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
file: `dist/index.esm.js`,
|
|
17
|
+
format: 'es',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
plugins: [
|
|
21
|
+
typescript(),
|
|
22
|
+
resolve(),
|
|
23
|
+
commonjs(),
|
|
24
|
+
],
|
|
25
|
+
external: ['qs', 'rxjs'],
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
export default config
|
|
29
|
+
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { HttpClient as AngularHttpClient, HttpResponse } from '@angular/common/http'
|
|
2
|
+
import { map, Observable } from 'rxjs'
|
|
3
|
+
import {
|
|
4
|
+
serializeQueryForSpringBoot,
|
|
5
|
+
serializeQueryWithQs,
|
|
6
|
+
UrlConverter,
|
|
7
|
+
ObservableHttpClient,
|
|
8
|
+
Request, ContentType, getFileNameFromContentDispositionHeader,
|
|
9
|
+
} from '@ps-aux/api-client-common'
|
|
10
|
+
|
|
11
|
+
export const qsUrlConverter: () => UrlConverter = () => ({ query, path }) => {
|
|
12
|
+
return {
|
|
13
|
+
url: path + (query ? `?${serializeQueryWithQs(query)}` : ''),
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const springBootUrlConverter: () => UrlConverter = () => ({ query, path }) => {
|
|
18
|
+
return {
|
|
19
|
+
url: path + (query ? `?${serializeQueryForSpringBoot(query)}` : ''),
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export const createHttpClient = (client: AngularHttpClient, urlConverter: UrlConverter = qsUrlConverter()): ObservableHttpClient => {
|
|
25
|
+
return {
|
|
26
|
+
request: <D>(req: Request): Observable<D> => {
|
|
27
|
+
const { url, params } = urlConverter(req)
|
|
28
|
+
|
|
29
|
+
const isBlob = req.format = 'document'
|
|
30
|
+
|
|
31
|
+
const obs = client.request<D>(
|
|
32
|
+
req.method,
|
|
33
|
+
url,
|
|
34
|
+
{
|
|
35
|
+
params,
|
|
36
|
+
body: prepareBody(req.body, req.type),
|
|
37
|
+
responseType: (isBlob ? 'blob' : 'json') as any,
|
|
38
|
+
observe: 'response',
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return obs.pipe(
|
|
42
|
+
map((r: HttpResponse<D>) => {
|
|
43
|
+
const { body } = r
|
|
44
|
+
|
|
45
|
+
if (!isBlob)
|
|
46
|
+
return body as D
|
|
47
|
+
|
|
48
|
+
const data = body as Blob
|
|
49
|
+
|
|
50
|
+
const contDisp = r.headers.get('content-disposition')
|
|
51
|
+
return new File([data], getFileNameFromContentDispositionHeader(contDisp), {
|
|
52
|
+
type: data.type
|
|
53
|
+
}) as D
|
|
54
|
+
}),
|
|
55
|
+
)
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const prepareBody = (payload: any, type?: string): any => {
|
|
61
|
+
if (type === ContentType.FormData) {
|
|
62
|
+
return convertToFormData(payload)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return payload
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const convertToFormData = (payload: Record<string, any>): FormData => {
|
|
69
|
+
const formData = new FormData()
|
|
70
|
+
const addProp = (key: string, val: any) => {
|
|
71
|
+
if (Array.isArray(val) || val instanceof FileList) {
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
73
|
+
// @ts-ignore - seems that FileList is iterable despite the warning
|
|
74
|
+
// TODO change to other iteration method if this is not true
|
|
75
|
+
for (const valItem of val) {
|
|
76
|
+
addProp(key, valItem)
|
|
77
|
+
}
|
|
78
|
+
} else if (typeof val === 'object' && val != null && !(val instanceof File)) {
|
|
79
|
+
throw new Error('Object serialization into FormData not supported')
|
|
80
|
+
} else {
|
|
81
|
+
formData.append(key, val)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
Object.entries(payload).forEach(([key, val]) => addProp(key, val))
|
|
86
|
+
return formData
|
|
87
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
// TODO improve typings and check implementation
|
|
4
|
+
export const serializeQuery = (obj: any): any => {
|
|
5
|
+
let result = {};
|
|
6
|
+
if (obj) {
|
|
7
|
+
Object.entries(obj).forEach(([key, val]) => {
|
|
8
|
+
if (Array.isArray(val)) {
|
|
9
|
+
val.map(v => {
|
|
10
|
+
if (result[key]) {
|
|
11
|
+
result[key] += `&${key}=${v}`;
|
|
12
|
+
} else {
|
|
13
|
+
result[key] = v;
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
} else if (typeof val === 'object') {
|
|
17
|
+
result = { ...result, ...flattenObject(val) };
|
|
18
|
+
} else {
|
|
19
|
+
result[key] = val;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const flattenObject = (obj: any, prefix?: string): any => {
|
|
27
|
+
let str = {};
|
|
28
|
+
const pfix = prefix ? prefix + '.' : '';
|
|
29
|
+
|
|
30
|
+
for (const p in obj) {
|
|
31
|
+
if (obj.hasOwnProperty(p)) {
|
|
32
|
+
if (Array.isArray(obj[p])) {
|
|
33
|
+
str[pfix + p] = obj[p];
|
|
34
|
+
} else if (obj[p] === null) {
|
|
35
|
+
str[pfix + p] = null;
|
|
36
|
+
} else if (typeof obj[p] === 'object') {
|
|
37
|
+
str = { ...str, ...flattenObject(obj[p], pfix + p) };
|
|
38
|
+
} else if (obj[p] !== undefined) {
|
|
39
|
+
str[pfix + p] = obj[p];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return str;
|
|
45
|
+
};
|