@weclapp/sdk 2.0.0-dev.6 → 2.0.0-dev.60
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/README.md +522 -23
- package/dist/cli.js +805 -623
- package/package.json +53 -35
- package/tsconfig.sdk.json +14 -0
- package/tsconfig.lib.json +0 -17
package/README.md
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
# weclapp SDK Generator
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
<h3>weclapp sdk generator</h3>
|
|
5
|
-
</div>
|
|
3
|
+
## Introduction
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
This is an SDK generator, it will take an [`openapi.json`](https://swagger.io/specification/) from [weclapp](https://weclapp.com/) and build services and typescript types according to the entities defined in it. Each service is responsible for a _single_ entity. Every service contains all functions available for this
|
|
6
|
+
entity, this may include functions such as `update`, `remove`, `some` and many more. With these functions you can send requests to your weclapp system to get data or manipulate them.
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
An entity is a resource with properties in weclapp. This can be an article, a contract or a quotation. The SDK generates a well-defined typescript type for each entity and its property. In this way, the SDK makes it possible to work with the data retrieved via the API in a type-safe manner.
|
|
9
|
+
|
|
10
|
+
Check out [generated types and utilities](#generated-types-and-utilities) for more possibilities!
|
|
10
11
|
|
|
11
|
-
This is an SDK generator, it will take an [`openapi.json`](https://swagger.io/specification/) from [weclapp](https://weclapp.com/) and build services and types according to the entities defined in it.
|
|
12
12
|
What's being generated depends on the weclapp version you're using.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## Getting started
|
|
15
|
+
|
|
16
|
+
The SDK generator requires the current or LTS version of nodejs, as well as npm v10.
|
|
15
17
|
|
|
16
18
|
You can install the generator via the package manager of your choice:
|
|
17
19
|
|
|
@@ -31,28 +33,525 @@ This way, every time someone installs or updates dependencies, the SDK is genera
|
|
|
31
33
|
```json5
|
|
32
34
|
{
|
|
33
35
|
// in your package.json
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
scripts: {
|
|
37
|
+
'sdk:generate': 'build-weclapp-sdk company.weclapp.com --key [your api key] --cache --target browser',
|
|
38
|
+
postinstall: 'npm run sdk:generate'
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
```
|
|
40
42
|
|
|
43
|
+
### Available flags
|
|
44
|
+
|
|
45
|
+
| Flag | Description | Value / Type |
|
|
46
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
|
|
47
|
+
| `--cache` / `-c` | Extra query params when fetching the openapi.json from a server. | `boolean` |
|
|
48
|
+
| `--deprecated` / `-d` | Include deprecated functions and services. | `boolean` |
|
|
49
|
+
| `--from-env` / `-e` | Use env variables `WECLAPP_BACKEND_URL` and `WECLAPP_API_KEY` as credentials. | `boolean` |
|
|
50
|
+
| `--generate-unique` | Generate additional `.unique` functions. | `boolean` |
|
|
51
|
+
| `--use-query-language` | Use the advanced query language. The property _filter_ will be removed from _SomeQuery_ and _CountQuery_ and the property _where_ will be added instead. | `boolean` |
|
|
52
|
+
| `--help` / `-h` | Show help. | `boolean` |
|
|
53
|
+
| `--key` / `-k` | API Key in case of using a remote. | `string` |
|
|
54
|
+
| `--target` / `-t` | Specify the target platform. | `browser`, `browser.rx`, `node` or `node.rx` |
|
|
55
|
+
| `--query` / `-q` | Extra query params when fetching the openapi.json from a server | `string` |
|
|
56
|
+
| `--version` / `-v` | Show version of SDK. | `boolean` |
|
|
57
|
+
|
|
41
58
|
After that, you can import the sdk via `@weclapp/sdk`.
|
|
42
59
|
Check out the [docs](docs) for how the generated SDK looks like and how to use it!
|
|
43
60
|
|
|
44
|
-
|
|
61
|
+
## Initialization
|
|
62
|
+
|
|
63
|
+
To initialize a service a `ServiceConfig` is needed, you can specify a global config in case you don't want to pass the config every time to the service you
|
|
64
|
+
want to use manually. If you pass an empty object as ServiceConfig the [location](https://developer.mozilla.org/en-US/docs/Web/API/Window/location) will be used to get the domain and the protocol (secure config option).
|
|
65
|
+
|
|
66
|
+
### Service config
|
|
67
|
+
|
|
68
|
+
The configuration looks like the following (taken from [globalConfig.ts](/src/generator/01-base/static/globalConfig.ts.txt)):
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
interface ServiceConfig {
|
|
72
|
+
// Your API-Key, this is optional in the sense of if you omit this, and you're in a browser, the
|
|
73
|
+
// cookie-authentication (include-credentials) will be used.
|
|
74
|
+
key?: string;
|
|
75
|
+
|
|
76
|
+
// Your domain, if omitted location.host will be used.
|
|
77
|
+
domain?: string;
|
|
78
|
+
|
|
79
|
+
// If you want to use https, defaults to false.
|
|
80
|
+
secure?: boolean;
|
|
81
|
+
|
|
82
|
+
// If you want that some and count requests are bundled into multi requests.
|
|
83
|
+
multiRequest?: boolean;
|
|
84
|
+
|
|
85
|
+
// If you want that the ignoreMissingProperties parameter to be set for each post request.
|
|
86
|
+
ignoreMissingProperties?: boolean;
|
|
87
|
+
|
|
88
|
+
// Optional request/response interceptors.
|
|
89
|
+
interceptors?: {
|
|
90
|
+
// Takes the generated request, you can either return a new request,
|
|
91
|
+
// a response (which will be taken as "the" response) or nothing.
|
|
92
|
+
// The payload contains the raw input generated by the SDK.
|
|
93
|
+
request?: (
|
|
94
|
+
request: Request,
|
|
95
|
+
payload: RequestPayload
|
|
96
|
+
) => Request | Response | void | Promise<Request | Response | void>;
|
|
97
|
+
|
|
98
|
+
// Takes the response. This can either be the one from the server or an
|
|
99
|
+
// artificially-crafted one by the request interceptor.
|
|
100
|
+
response?: (response: Response) => Response | void | Promise<Response | void>;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Whether POST should be used instead of GET for some() and count() operations
|
|
104
|
+
usePost?: boolean;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Using the config
|
|
109
|
+
|
|
110
|
+
There are three ways of accessing a service through the SDK:
|
|
111
|
+
|
|
112
|
+
#### Using the global config
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { setGlobalConfig, partyService } from '@weclapp/sdk';
|
|
116
|
+
|
|
117
|
+
setGlobalConfig({
|
|
118
|
+
key: 'mykey',
|
|
119
|
+
domain: 'myweclapp.com'
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const party = partyService();
|
|
123
|
+
|
|
124
|
+
console.log(`Total amount of parties: ${await party.count()}`);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### Using a config per service
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { partyService } from '@weclapp/sdk';
|
|
131
|
+
|
|
132
|
+
const party = partyService({
|
|
133
|
+
key: 'mykey',
|
|
134
|
+
domain: 'myweclapp.com'
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
console.log(`Total amount of parties: ${await party.count()}`);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
> If a global config is specified as well the one passed to the service will **always** take precedence over the global one!
|
|
141
|
+
|
|
142
|
+
#### Using the raw function
|
|
143
|
+
|
|
144
|
+
The raw function can be used if none of the provided function suits your needs. The services use the raw function, which makes the API requests via [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API):
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { raw } from '@weclapp/sdk';
|
|
148
|
+
|
|
149
|
+
const result = await raw(
|
|
150
|
+
cfg: ServiceConfig | undefined = globalConfig,
|
|
151
|
+
endpoint: string,
|
|
152
|
+
payload: RequestPayload = {}
|
|
153
|
+
);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
where `RequestPayload` looks like this:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
interface RequestPayload {
|
|
160
|
+
method?: RequestPayloadMethod; // Optional request method, default is GET
|
|
161
|
+
query?: Record<string, any>; // Optional query parameters
|
|
162
|
+
body?: any; // Optional body
|
|
163
|
+
|
|
164
|
+
// In case the response body is an object with a result-prop the value will be extracted
|
|
165
|
+
unwrap?: boolean;
|
|
166
|
+
|
|
167
|
+
// If in any case the response should be returned as a blob.
|
|
168
|
+
// Required for download endpoints as json will be parsed automatically.
|
|
169
|
+
forceBlob?: boolean;
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
and `RequestOptions` looks like this:
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
interface RequestOptions {
|
|
177
|
+
signal?: AbortSignal;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Generated types and utilities
|
|
182
|
+
|
|
183
|
+
The generator generates various utilities that can be used to integrate it in a generic way into your app.
|
|
184
|
+
|
|
185
|
+
### Exported constants
|
|
186
|
+
|
|
187
|
+
| Constant | Description |
|
|
188
|
+
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
189
|
+
| `wServiceWith[method]Names` | An array of services (strings) that have this function. Example: `wServiceWithUpdateNames`. |
|
|
190
|
+
| `wCustomValueServiceNames` | An array of services (strings) that work with `CustomValue`. |
|
|
191
|
+
| `wEnums` | Object with the name of the enum as key and the corresponding enum as value. |
|
|
192
|
+
| `wServiceFactories` | Object with all services where the name is the key and the value a function taking a config and returning the service. |
|
|
193
|
+
| `wServices` | Object with all services where the name is the key and the value the service (that is using the global config). |
|
|
194
|
+
| `wEntityProperties` | Object with all entity names as key and properties including the type and format as value. |
|
|
195
|
+
|
|
196
|
+
### Exported types
|
|
197
|
+
|
|
198
|
+
| Type | Description | Type guards available? |
|
|
199
|
+
| ----------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------- |
|
|
200
|
+
| `WServiceWith[method]` | A tuple of strings with the names of the services that contain this function. Example: `WServiceWithSome`. | Yes, for example `isWServiceWithSome` |
|
|
201
|
+
| `WServicesWith[method]` | An interface with the service-name as key and corresponding service as value. Example: `WServicesWithSome`. | |
|
|
202
|
+
| `WCustomValueService` | Tuple of services (strings) that work with `CustomValue`. | Yes: `isWCustomValueService` |
|
|
203
|
+
| `WEnums` | Type of `wEnums`. | |
|
|
204
|
+
| `WEnum` | All keys of `WEnums`. | |
|
|
205
|
+
| `WEntities` | Interface will all entities from weclapp with their name as key and type as value | |
|
|
206
|
+
| `WEntity` | All keys of `WEntities`. | |
|
|
207
|
+
| `WServiceFactories` | Type of `wServiceFactories`. | |
|
|
208
|
+
| `WServices` | Type of `wServices`. | |
|
|
209
|
+
| `WEntityProperties` | Generalized type of `wEntityProperties`. | |
|
|
210
|
+
|
|
211
|
+
## Services and raw-function in depth
|
|
212
|
+
|
|
213
|
+
As already described above, the api endpoints can be requested via services or the raw function. The advantage of wServices over the raw function is that all endpoints of the entities are available as functions and these functions are typed. This makes it easier to work with the data provided via the weclapp API.
|
|
214
|
+
|
|
215
|
+
A service of an entity has in general the following base function:
|
|
216
|
+
|
|
217
|
+
some: // get entity data
|
|
218
|
+
create: // creates a entity
|
|
219
|
+
count: // get the count of entities
|
|
220
|
+
remove: // deletes a entity
|
|
221
|
+
update: // updates a entity
|
|
222
|
+
|
|
223
|
+
In addition, there are some custom endpoint functions. The generated PartyService is shown below as an example:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
interface PartyService {
|
|
227
|
+
some: PartyService_Some;
|
|
228
|
+
create: PartyService_Create;
|
|
229
|
+
count: PartyService_Count;
|
|
230
|
+
remove: PartyService_Remove;
|
|
231
|
+
update: PartyService_Update;
|
|
232
|
+
postCreatePublicPageById: PartyService_PostCreatePublicPageById;
|
|
233
|
+
getDownloadImageById: PartyService_GetDownloadImageById;
|
|
234
|
+
getTopPurchaseArticlesById: PartyService_GetTopPurchaseArticlesById;
|
|
235
|
+
getTopSalesArticlesById: PartyService_GetTopSalesArticlesById;
|
|
236
|
+
postUploadImageById: PartyService_PostUploadImageById;
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Example
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import { PartyType, setGlobalConfig, wServices } from '@weclapp/sdk';
|
|
244
|
+
|
|
245
|
+
setGlobalConfig({
|
|
246
|
+
domain: 'company.weclapp.com',
|
|
247
|
+
secure: true
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// to get the count of parties via service
|
|
251
|
+
const serviceN = await wServices['party'].count();
|
|
252
|
+
|
|
253
|
+
// to get the count of parties via raw
|
|
254
|
+
const rawN = await raw(undefined, '/party/count');
|
|
255
|
+
|
|
256
|
+
// to get all parties via service
|
|
257
|
+
const partiesService = await wServices['party'].some();
|
|
258
|
+
|
|
259
|
+
// to get all parties via raw
|
|
260
|
+
const partiesRaw = await raw(undefined, '/party');
|
|
261
|
+
|
|
262
|
+
// to create a party via service
|
|
263
|
+
const contact = await wServices['party'].create({
|
|
264
|
+
partyType: PartyType.PERSON,
|
|
265
|
+
lastName: 'Mueller'
|
|
266
|
+
}); // the returned object is already typed as Party
|
|
267
|
+
|
|
268
|
+
// to create a party via raw
|
|
269
|
+
const contactRaw = await await raw(undefined, '/party', {
|
|
270
|
+
// the returned object has the type any.
|
|
271
|
+
method: 'POST',
|
|
272
|
+
body: { partyType: PartyType.PERSON, lastName: 'Mueller' }
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// to delete a party via service
|
|
276
|
+
await wServices['party'].remove(contact.id);
|
|
45
277
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
278
|
+
// to delete a party via raw
|
|
279
|
+
if (contactRaw && typeof contactRaw.id === 'string') {
|
|
280
|
+
await raw(undefined, `/party/id/${contactRaw.id}`, {
|
|
281
|
+
method: 'DELETE'
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Service request params
|
|
287
|
+
|
|
288
|
+
#### Filtering
|
|
289
|
+
|
|
290
|
+
With the some and count functions you can filter the requested data.
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
wServices['article'].some({
|
|
294
|
+
filter: {
|
|
295
|
+
name: { EQ: 'toy 1' },
|
|
296
|
+
articleNumber: { EQ: '12345' }
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
The SDK makes an AND operator between the properties. So this equivalent to the follwing expression:
|
|
302
|
+
|
|
303
|
+
name EQ 'toy 1' AND articleNumber EQ '12345'.
|
|
304
|
+
|
|
305
|
+
If you want an OR operator you have to set an array in the or property:
|
|
306
|
+
|
|
307
|
+
```ts
|
|
308
|
+
wServices['article'].some({
|
|
309
|
+
or: [
|
|
310
|
+
{
|
|
311
|
+
name: { EQ: 'toy 1' },
|
|
312
|
+
articleNumber: { EQ: '12345' }
|
|
313
|
+
}
|
|
314
|
+
]
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
The above example is the equivalent of the expression
|
|
319
|
+
|
|
320
|
+
name EQ 'toy 1' OR articleNumber EQ '12345'.
|
|
321
|
+
|
|
322
|
+
To combine OR and AND clauses, you can also group OR expressions by adding several objects to the array:
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
wServices['article'].some({
|
|
326
|
+
or: [
|
|
327
|
+
{
|
|
328
|
+
name: { EQ: 'toy 1' },
|
|
329
|
+
articleNumber: { EQ: '12345' }
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
batchNumberRequired: { EQ: true }
|
|
333
|
+
}
|
|
334
|
+
]
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
This is evaluated to:
|
|
339
|
+
(name EQ 'toy 1' OR articleNumber EQ '12345') AND batchNumberRequired EQ true
|
|
340
|
+
|
|
341
|
+
#### Where filter
|
|
342
|
+
|
|
343
|
+
> Warning: This is still a beta feature
|
|
344
|
+
|
|
345
|
+
It is also possible to specify complex filter expressions that can combine multiple conditions and express relations between properties:
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
wServices['article'].some({
|
|
349
|
+
where: {
|
|
350
|
+
AND: [
|
|
351
|
+
{
|
|
352
|
+
OR: [{ name: { LIKE: '%test%', lower: true } }, { articleNumber: { LIKE: '%345%' } }]
|
|
353
|
+
},
|
|
354
|
+
{ batchNumberRequired: { EQ: true } }
|
|
355
|
+
]
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
"where" parameters are ANDed with other filter parameters.
|
|
361
|
+
|
|
362
|
+
It is also possible to set an empty list within an IN-query:
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
wServices['article'].some({
|
|
366
|
+
where: {
|
|
367
|
+
id: {
|
|
368
|
+
IN: []
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
This actually evaluates to `filter = 1 = 0`. The API does not accept filtering for empty lists. Therefore, in this case you have to send a falsy expression. You cannot send `false` directly, though.
|
|
375
|
+
|
|
376
|
+
#### Sort
|
|
377
|
+
|
|
378
|
+
You can sort your requested data with an array properties.
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
wServices['article'].some({
|
|
382
|
+
sort: [{ name: 'asc' }, { minimumPurchaseQuantity: 'desc' }]
|
|
383
|
+
});
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Sort by name (ascending) and then minimumPurchaseQuantity descending.
|
|
387
|
+
|
|
388
|
+
#### Pagination
|
|
389
|
+
|
|
390
|
+
By default the API returns only the first 100 entities. You can increase the size of one response to the maximum of 1000. To get the next 1000 entities you have increase the page number.
|
|
391
|
+
|
|
392
|
+
```ts
|
|
393
|
+
wServices['article'].some({
|
|
394
|
+
pagination: {
|
|
395
|
+
page: 2,
|
|
396
|
+
pageSize: 10
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
This returns the first 10 articles of the second page.
|
|
402
|
+
|
|
403
|
+
#### Select
|
|
404
|
+
|
|
405
|
+
With the select option you can fetch specific subset of properties:
|
|
406
|
+
|
|
407
|
+
```ts
|
|
408
|
+
wServices['article'].some({
|
|
409
|
+
select: { articleNumber: true }
|
|
410
|
+
});
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
This only returns the articleNumber property of all articles.
|
|
414
|
+
|
|
415
|
+
#### Ordering
|
|
416
|
+
|
|
417
|
+
With the some function you can order requested data. You can either merely order by fields or by complex expressions.
|
|
418
|
+
|
|
419
|
+
```ts
|
|
420
|
+
/**
|
|
421
|
+
* Order by createdDate in ascending order.
|
|
422
|
+
*
|
|
423
|
+
* ?orderBy=createdDate asc
|
|
424
|
+
*/
|
|
425
|
+
wServices['article'].some({
|
|
426
|
+
orderBy: [{ FIELD: 'createdDate', SORT: 'asc' }]
|
|
427
|
+
});
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
```ts
|
|
431
|
+
/**
|
|
432
|
+
* First order by createdDate in ascending order, then by articleNumber in descending order
|
|
433
|
+
* If you omit SORT the default ordering will be set to ascending.
|
|
434
|
+
*
|
|
435
|
+
* ?orderBy=createdDate asc, articleNumber desc
|
|
436
|
+
*/
|
|
437
|
+
wServices['article'].some({
|
|
438
|
+
orderBy: [{ FIELD: 'createdDate' }, { FIELD: 'articleNumber', SORT: 'desc' }]
|
|
439
|
+
});
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
```ts
|
|
443
|
+
/**
|
|
444
|
+
* Order with conditional sorting: if internalNote is not null then 1,
|
|
445
|
+
* if packagingQuantity > 400 then 2, otherwise 3.
|
|
446
|
+
* Then order by articleNumber in ascending order.
|
|
447
|
+
*
|
|
448
|
+
* ?orderBy=(not internalNote null) ? 1 : (packagingQuantity > 400) ? 2 : 3 asc, articleNumber asc
|
|
449
|
+
*/
|
|
450
|
+
wServices['article'].some({
|
|
451
|
+
orderBy: [
|
|
452
|
+
{
|
|
453
|
+
CASE: [
|
|
454
|
+
{ WHEN: { internalNote: { NULL: false } }, THEN: 1 },
|
|
455
|
+
{ WHEN: { packagingQuantity: { GT: 400 } }, THEN: 2 }
|
|
456
|
+
],
|
|
457
|
+
ELSE: 3,
|
|
458
|
+
SORT: 'asc'
|
|
459
|
+
},
|
|
460
|
+
{ FIELD: 'articleNumber' }
|
|
461
|
+
]
|
|
462
|
+
});
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
The `THEN` and `ELSE` values can also be a `FieldOrderBy` to fall back to a field-based ordering:
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
/**
|
|
469
|
+
* Order with conditional sorting: if internalNote is not null, order by articleNumber,
|
|
470
|
+
* otherwise order by createdDate.
|
|
471
|
+
*
|
|
472
|
+
* ?orderBy=(not internalNote null) ? articleNumber : createdDate asc
|
|
473
|
+
*/
|
|
474
|
+
wServices['article'].some({
|
|
475
|
+
orderBy: [
|
|
476
|
+
{
|
|
477
|
+
CASE: [{ WHEN: { internalNote: { NULL: false } }, THEN: { FIELD: 'articleNumber' } }],
|
|
478
|
+
ELSE: { FIELD: 'createdDate' },
|
|
479
|
+
SORT: 'asc'
|
|
480
|
+
}
|
|
481
|
+
]
|
|
482
|
+
});
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
For properties that are objects or arrays of objects, use dot-notation to reference nested fields:
|
|
486
|
+
|
|
487
|
+
```ts
|
|
488
|
+
/**
|
|
489
|
+
* Order by a nested property of an array/object field.
|
|
490
|
+
*
|
|
491
|
+
* ?orderBy=articlePrices.price asc
|
|
492
|
+
*/
|
|
493
|
+
wServices['article'].some({
|
|
494
|
+
orderBy: [{ FIELD: 'articlePrices.price', SORT: 'asc' }]
|
|
495
|
+
});
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
There are three modifier functions, `TRIM`, `LOWER` and `LENGTH`, which can be used to adjust the expessions:
|
|
499
|
+
|
|
500
|
+
```ts
|
|
501
|
+
/**
|
|
502
|
+
* First order by the length of the trimmed lastName in descending order, then by firstName in ascending order
|
|
503
|
+
*
|
|
504
|
+
* ?orderBy=length(trim(lastName)) desc, firstName asc
|
|
505
|
+
*/
|
|
506
|
+
wServices['party'].some({
|
|
507
|
+
orderBy: [
|
|
508
|
+
{
|
|
509
|
+
FIELD: 'lastName',
|
|
510
|
+
LENGTH: true,
|
|
511
|
+
TRIM: true,
|
|
512
|
+
SORT: 'desc'
|
|
513
|
+
},
|
|
514
|
+
{ FIELD: 'firstName' }
|
|
515
|
+
]
|
|
516
|
+
});
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### Aborting a request
|
|
520
|
+
|
|
521
|
+
To abort a request an AbortController has to be instantiated and its signal has to be passed to the request. The controller can
|
|
522
|
+
abort the request when needed and the case can be handled with a catch.
|
|
523
|
+
|
|
524
|
+
```ts
|
|
525
|
+
import { wServices } from '@sdk/dist';
|
|
526
|
+
|
|
527
|
+
const controller = new AbortController();
|
|
528
|
+
let count = 0;
|
|
529
|
+
|
|
530
|
+
wServices.article
|
|
531
|
+
.count(
|
|
532
|
+
{
|
|
533
|
+
where: {
|
|
534
|
+
active: { EQ: true }
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
{ signal: controller.signal }
|
|
538
|
+
)
|
|
539
|
+
.then((c) => (count = c))
|
|
540
|
+
.catch((err) => {
|
|
541
|
+
if (controller.signal.aborted) {
|
|
542
|
+
if (controller.signal.reason) {
|
|
543
|
+
console.log(`Request aborted with reason: ${controller.signal.reason}`);
|
|
544
|
+
} else {
|
|
545
|
+
console.log('Request aborted but no reason was given.');
|
|
546
|
+
}
|
|
547
|
+
} else {
|
|
548
|
+
console.log(err);
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
controller.abort('Abort article count request');
|
|
553
|
+
```
|
|
55
554
|
|
|
56
|
-
|
|
555
|
+
## Contributing
|
|
57
556
|
|
|
58
|
-
Check out the [contributing guidelines](.github/CONTRIBUTING.md).
|
|
557
|
+
Check out the [contributing guidelines](.github/CONTRIBUTING.md).
|