@inwink-mcp/backoffice-extension-helpers 1.0.11 → 1.0.13
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 +15 -1
- package/dist/cli.js +133 -34
- package/package.json +1 -1
- package/templates/vite-extension/AGENTS.md.tpl +13 -5
- package/templates/vite-extension/docs/inwink-api/README.md +65 -24
- package/templates/vite-extension/docs/inwink-api/aggregate.md +238 -0
- package/templates/vite-extension/docs/inwink-api/assets.md +107 -107
- package/templates/vite-extension/docs/inwink-api/create.md +3 -3
- package/templates/vite-extension/docs/inwink-api/delete.md +4 -2
- package/templates/vite-extension/docs/inwink-api/entities.md +24 -60
- package/templates/vite-extension/docs/inwink-api/entity-template.md +201 -0
- package/templates/vite-extension/docs/inwink-api/files.md +136 -135
- package/templates/vite-extension/docs/inwink-api/history.md +68 -0
- package/templates/vite-extension/docs/inwink-api/import.md +4 -4
- package/templates/vite-extension/docs/inwink-api/query-language.md +127 -38
- package/templates/vite-extension/docs/inwink-api/query.md +12 -10
- package/templates/vite-extension/docs/inwink-api/update.md +7 -5
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
◂
|
|
1
|
+
◂ Back to [README](./README.md)
|
|
2
2
|
|
|
3
|
-
# inwink JSON
|
|
3
|
+
# inwink JSON query language
|
|
4
4
|
|
|
5
5
|
The query language is inspired by MongoDB syntax. Expressions are JSON objects where `$`-prefixed keys are operators and non-prefixed keys are entity property names.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Used by the `filters`, `orders` and `selects` of the [query](./query.md), [update](./update.md) (massupdate), [delete](./delete.md) (massdelete) and [history](./history.md) operations, and by the `filters` of [aggregate](./aggregate.md).
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Filters
|
|
10
10
|
|
|
11
11
|
### Comparison operators
|
|
12
12
|
|
|
13
|
-
| Operator |
|
|
13
|
+
| Operator | Alias | Meaning |
|
|
14
14
|
|---|---|---|
|
|
15
15
|
| `$eq` | `$equal` | Equals |
|
|
16
16
|
| `$neq` | `$notEqual` | Not equals |
|
|
@@ -18,38 +18,53 @@ Utilisé par les `filters`, `orders` et `selects` des opérations [query](./quer
|
|
|
18
18
|
| `$gte` | `$greaterThanOrEqual` | Greater than or equal |
|
|
19
19
|
| `$lt` | `$lessThan` | Less than |
|
|
20
20
|
| `$lte` | `$lessThanOrEqual` | Less than or equal |
|
|
21
|
+
| `$in` | — | In the list: `{ "p1": { "$in": ["v1", "v2"] } }` |
|
|
21
22
|
|
|
22
23
|
**Implicit equality:** `{ "p1": "v1" }` is equivalent to `{ "p1": { "$eq": "v1" } }`.
|
|
23
24
|
|
|
25
|
+
⚠️ **This table is exhaustive: use only these names, exactly as written.** When unsure about a negation, compose with `$not`
|
|
26
|
+
(see "Logical operators") rather than guessing a name.
|
|
27
|
+
|
|
24
28
|
### Logical operators
|
|
25
29
|
|
|
26
30
|
- `$and`: `{ "$and": [ { "p1": "v1" }, { "p2": "v2" } ] }`
|
|
27
31
|
- `$or`: `{ "$or": [ { "p1": "v1" }, { "p2": "v2" } ] }`
|
|
28
32
|
- `$not`: negates a predicate
|
|
29
33
|
|
|
30
|
-
### Text operators (
|
|
34
|
+
### Text operators (`$text` namespace)
|
|
35
|
+
|
|
36
|
+
**Predicates** — used directly as a filter:
|
|
37
|
+
|
|
38
|
+
| Operator | Example | Returns |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `$contains` | `{ "title": { "$text": { "$contains": "val" } } }` | true if `title` contains "val" |
|
|
41
|
+
| `$startsWith` | `{ "title": { "$text": { "$startsWith": "val" } } }` | true if `title` starts with "val" |
|
|
42
|
+
| `$endsWith` | `{ "title": { "$text": { "$endsWith": "val" } } }` | true if `title` ends with "val" |
|
|
43
|
+
|
|
44
|
+
**Value expressions** — not predicates: they return a value (number or string), so they must be compared, and
|
|
45
|
+
fluent chaining does not apply (see "Verbose binary operator syntax"):
|
|
31
46
|
|
|
32
47
|
| Operator | Example | Returns |
|
|
33
48
|
|---|---|---|
|
|
34
|
-
| `$
|
|
35
|
-
| `$
|
|
36
|
-
| `$
|
|
37
|
-
| `$indexOf` | `{ "p1": { "$text.$indexOf": "val" } }` | position of first occurrence, -1 if none |
|
|
38
|
-
| `$substring` | `{ "p1": { "$text.$substring": { "$startIndex": 1, "$length": 3 } } }` | substring extraction |
|
|
39
|
-
| `$length` | `{ "p1": { "$text.$length": {} } }` | length of the string |
|
|
49
|
+
| `$length` | `{ "$gt": { "$left": { "title": { "$text": { "$length": {} } } }, "$right": 60 } }` | length of the string |
|
|
50
|
+
| `$indexOf` | `{ "$gte": { "$left": { "title": { "$text": { "$indexOf": "val" } } }, "$right": 0 } }` | position of the first occurrence, -1 if none |
|
|
51
|
+
| `$substring` | `{ "$eq": { "$left": { "title": { "$text": { "$substring": { "$startIndex": 1, "$length": 3 } } } }, "$right": "upl" } }` | substring |
|
|
40
52
|
|
|
41
|
-
|
|
53
|
+
`$substring` takes `$startIndex` (integer, 0-based, required) and `$length` (integer, optional — up to the end
|
|
54
|
+
of the string if absent).
|
|
42
55
|
|
|
43
56
|
### Collection operators
|
|
44
57
|
|
|
45
|
-
|
|
46
|
-
|
|
58
|
+
To filter on a **to-many** relation, the quantifier is **required**: it sits between the collection name and
|
|
59
|
+
the predicate.
|
|
47
60
|
|
|
48
|
-
|
|
61
|
+
- `$any`: `{ "relateds": { "$any": { "p1": "v1" } } }` — true if **at least one** item matches.
|
|
62
|
+
- `$all`: `{ "relateds": { "$all": { "p1": "v1" } } }` — true if **every** item matches.
|
|
63
|
+
|
|
64
|
+
### Date/time operators
|
|
49
65
|
|
|
50
66
|
| Operator | Purpose |
|
|
51
67
|
|---|---|
|
|
52
|
-
| `$now` | Current date/time |
|
|
53
68
|
| `$utcNow` | Current UTC date/time |
|
|
54
69
|
| `$days`, `$hours`, `$minutes`, `$seconds`, `$milliseconds`, `$ticks`, `$timespan` | Duration construction |
|
|
55
70
|
|
|
@@ -65,24 +80,90 @@ Alternate syntax: `{ "p1": { "$text": { "$contains": "val" } } }` is equivalent.
|
|
|
65
80
|
|
|
66
81
|
**Example — sum of two properties:** `{ "p1": { "$add": { "p2": {} } } }`
|
|
67
82
|
|
|
68
|
-
|
|
83
|
+
### Value expressions
|
|
69
84
|
|
|
70
85
|
A property is referenced by nesting JSON keys terminated with `{}`:
|
|
71
86
|
|
|
72
87
|
- `{ "p1": {} }` → value of `p1`
|
|
73
|
-
- `{ "p1": { "p2": {} } }` → value of `p1.p2` (deep access)
|
|
88
|
+
- `{ "p1": { "p2": {} } }` → value of `p1.p2` (deep access through a **to-one** relation)
|
|
89
|
+
|
|
90
|
+
Deep access through a **to-many** relation must go through `$any` / `$all` (see "Collection operators"). And
|
|
91
|
+
this deep-access syntax is **not** accepted everywhere: `aggregate` restricts it further — see the "What can be
|
|
92
|
+
traversed" table in [aggregate.md](./aggregate.md).
|
|
74
93
|
|
|
75
|
-
|
|
94
|
+
### Verbose binary operator syntax
|
|
76
95
|
|
|
77
|
-
When fluent chaining
|
|
96
|
+
When fluent chaining does not work (e.g. an operand is a terminal expression):
|
|
78
97
|
|
|
79
98
|
```json
|
|
80
99
|
{ "$operator": { "$left": { ... }, "$right": { ... } } }
|
|
81
100
|
```
|
|
82
101
|
|
|
83
|
-
|
|
102
|
+
### Localizable properties and `$meta`
|
|
103
|
+
|
|
104
|
+
Some properties (such as `title`, `description`) are localized objects: `{ "fr": "...", "en": "..." }`. By default, operations apply to the event's default language.
|
|
105
|
+
|
|
106
|
+
To force a specific language, wrap the expression in `$meta`:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"$meta": {
|
|
111
|
+
"lang": "en",
|
|
112
|
+
"$token": { "title": { "$text.$contains": "search term" } }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Nested `$meta` operators are cumulative; the closest ancestor `$meta` wins when a localizable property is evaluated.
|
|
118
|
+
|
|
119
|
+
### JSON fields (type `Object`, 95)
|
|
120
|
+
|
|
121
|
+
A field that `inwink_mcp_get_entity_description` returns with `"type": 95` stores a JSON document. Its name depends on the entity
|
|
122
|
+
(e.g. `configuration` on `MailTemplate`); the description gives that name, but not the structure of the document.
|
|
123
|
+
Two filter forms apply. Below, `<field>` is the name of the JSON field.
|
|
124
|
+
|
|
125
|
+
**1. Text anywhere in the document (default form).** It only needs the field name. `$meta` with
|
|
126
|
+
`"as": "string"` reads the field as its serialized JSON, to which the `$text` predicates apply:
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{ "$meta": { "as": "string", "$token": { "<field>": { "$text": { "$contains": "searched text" } } } } }
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Example on `MailTemplate`:
|
|
133
|
+
`{ "$meta": { "as": "string", "$token": { "configuration": { "$text": { "$contains": "isRegistered" } } } } }`.
|
|
134
|
+
|
|
135
|
+
The search covers the whole JSON text: it matches a template variable as well as a key name, an attribute value
|
|
136
|
+
or HTML. It is case-insensitive.
|
|
137
|
+
|
|
138
|
+
**2. Value at a known path in the document.** If you know the structure, the keys under `<field>` are the keys
|
|
139
|
+
of the document: descend to a scalar value (string, number, boolean), without going through an array, then
|
|
140
|
+
apply a comparison or a `$text` predicate. Examples on `MailTemplate`:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{ "configuration": { "maildata": { "relationEntityName": "order" } } }
|
|
144
|
+
{ "configuration": { "maildata": { "senderEmail": { "$text": { "$contains": "@example.com" } } } } }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
To learn the structure, read the field on a single row (`query` with `page.size` 1 and `selects` limited to that
|
|
148
|
+
field): a document can weigh tens of KB. If the searched value sits in an array of the document, or if its
|
|
149
|
+
location varies from one row to another, use form 1.
|
|
150
|
+
|
|
151
|
+
⚠️ **Under a JSON field, a `$`-prefixed key that is not an operator from this page is read as a document
|
|
152
|
+
key.** The filter then runs without error and returns 0 results. Write the operators exactly as shown above.
|
|
153
|
+
A 0 obtained with another form does not prove the value is absent.
|
|
154
|
+
|
|
155
|
+
Related errors:
|
|
156
|
+
|
|
157
|
+
- `400 … cannot convert value of type 'JObject' to string`: a `$text` predicate was applied to the field
|
|
158
|
+
itself. Wrap it in `$meta` `"as": "string"` (form 1) or descend to a scalar value (form 2).
|
|
159
|
+
- `500` on a filter that targets a JSON field: the form used is not one of the two above. Rewrite the filter
|
|
160
|
+
using form 1 or 2.
|
|
84
161
|
|
|
85
|
-
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Sorting (orders)
|
|
165
|
+
|
|
166
|
+
The `orders` array holds objects with `desc` (boolean) and `value` (a value expression):
|
|
86
167
|
|
|
87
168
|
```json
|
|
88
169
|
{
|
|
@@ -92,29 +173,37 @@ The `orders` array contains objects with `desc` (boolean) and `value` (a value e
|
|
|
92
173
|
}
|
|
93
174
|
```
|
|
94
175
|
|
|
95
|
-
|
|
176
|
+
- `value` references a field using the value expression syntax: `{ "field": {} }`.
|
|
177
|
+
- `desc: true` for descending order; `false` or absent for ascending.
|
|
178
|
+
|
|
179
|
+
### Sorting on a related entity
|
|
180
|
+
|
|
181
|
+
A **to-one** relation is traversed by nesting, including on a localizable field:
|
|
96
182
|
|
|
97
183
|
```json
|
|
98
|
-
{ "
|
|
184
|
+
{ "orders": [ { "desc": true, "value": { "member": { "lastname": {} } } } ] }
|
|
99
185
|
```
|
|
100
186
|
|
|
101
|
-
|
|
102
|
-
- `$all` sets the default for all properties.
|
|
103
|
-
- Nested objects define joins/expands: `{ "sessions": { "$all": true } }`
|
|
187
|
+
Entities whose relation is empty come first in ascending order.
|
|
104
188
|
|
|
105
|
-
|
|
189
|
+
A **to-many** relation is **not** sortable.
|
|
106
190
|
|
|
107
|
-
|
|
191
|
+
---
|
|
108
192
|
|
|
109
|
-
|
|
193
|
+
## Projection (selects)
|
|
110
194
|
|
|
111
195
|
```json
|
|
112
|
-
{
|
|
113
|
-
"$meta": {
|
|
114
|
-
"lang": "en",
|
|
115
|
-
"$token": { "title": { "$text.$contains": "search term" } }
|
|
116
|
-
}
|
|
117
|
-
}
|
|
196
|
+
{ "$all": true, "secretField": false }
|
|
118
197
|
```
|
|
119
198
|
|
|
120
|
-
|
|
199
|
+
- `true` = include, `false` = exclude.
|
|
200
|
+
- `$all` sets the default for all properties.
|
|
201
|
+
- Nested objects define joins/expansions: `{ "sessions": { "$all": true } }`.
|
|
202
|
+
|
|
203
|
+
Related entities are **never loaded automatically** — they must be requested explicitly in `selects`.
|
|
204
|
+
|
|
205
|
+
### Null values
|
|
206
|
+
|
|
207
|
+
A field whose value is null can be **left out of the row**, even when it is selected by name; other null fields come back as `null`. Both mean null. An absent field is not the sign of a wrong name: a name that does not exist on the entity fails the whole request with `400 … property resolution error`. A field selected by name, with no error, and absent from the row is therefore null.
|
|
208
|
+
|
|
209
|
+
To list the rows where a field is null, filter with `{ "<field>": null }`.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
◂
|
|
1
|
+
◂ Back to [README](./README.md)
|
|
2
2
|
|
|
3
3
|
# Query & Count (read entities)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
For the `filters`, `orders` and `selects` syntax, see [query-language.md](./query-language.md).
|
|
6
6
|
|
|
7
|
-
## Query
|
|
7
|
+
## Query
|
|
8
8
|
|
|
9
9
|
```
|
|
10
10
|
POST {api}/{shardId}/{entity}/query
|
|
@@ -22,19 +22,19 @@ POST {api}/{shardId}/{entity}/query
|
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
- `page` — **required**. `index` starts at 0, `size` between 1 and 300 (recommended: 50).
|
|
25
|
-
- `filters` — optional. A predicate
|
|
26
|
-
- `orders` — optional.
|
|
27
|
-
- `selects` — optional. A projection
|
|
25
|
+
- `filters` — optional. A predicate (see [query-language.md](./query-language.md#filters)).
|
|
26
|
+
- `orders` — optional. Array of sort clauses (see [query-language.md](./query-language.md#sorting-orders)). If omitted, the API sorts by `id` when possible.
|
|
27
|
+
- `selects` — optional. A projection (see [query-language.md](./query-language.md#projection-selects)). Default: `{ "$all": true }`.
|
|
28
28
|
|
|
29
29
|
**Response:** `{ "data": [ ...entities ] }`
|
|
30
30
|
|
|
31
31
|
### Pagination
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
**Stateless offset-based** pagination (no cursor). To fetch everything, increment `page.index` until `data.length < page.size`. Because pagination is stateless, **duplicates can appear across pages** — always deduplicate by `id` (`useInwinkPagedQuery` does it). Records can also be missed if the sort property is non-unique or mutable; omitting `orders` lets the API pick the safest sort.
|
|
34
34
|
|
|
35
35
|
### Selecting specific fields
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
Example on the EventAPI `person` entity — field names are specific to each entity and do not carry over from one API to another.
|
|
38
38
|
|
|
39
39
|
```json
|
|
40
40
|
{
|
|
@@ -55,7 +55,7 @@ Return only specific properties:
|
|
|
55
55
|
}
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
Related entities are **never loaded automatically** —
|
|
58
|
+
Related entities are **never loaded automatically** — they must be requested explicitly in `selects`.
|
|
59
59
|
|
|
60
60
|
---
|
|
61
61
|
|
|
@@ -65,4 +65,6 @@ Related entities are **never loaded automatically** — you must explicitly requ
|
|
|
65
65
|
POST {api}/{shardId}/{entity}/count
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
Same body as query (supports `filters` and `page`) but without `selects`. Returns a **single integer**.
|
|
68
|
+
Same body as query (supports `filters` and `page`) but **without** `selects`. Returns a **single integer**.
|
|
69
|
+
|
|
70
|
+
Best for "how many …" questions. For a breakdown (GROUP BY), use [aggregate.md](./aggregate.md) rather than counting page by page.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
◂
|
|
1
|
+
◂ Back to [README](./README.md)
|
|
2
2
|
|
|
3
3
|
# Edit & Massupdate
|
|
4
4
|
|
|
@@ -17,9 +17,11 @@ POST {api}/{shardId}/{entity}/{entityId}
|
|
|
17
17
|
}
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
**Important:**
|
|
20
|
+
**Important:** this is a **PATCH** in meaning, sent with `POST` — not a replacement. Only the properties present in the payload are updated. To clear a property, set it explicitly to `null`.
|
|
21
21
|
|
|
22
|
-
`selects`
|
|
22
|
+
`selects` controls the returned properties (see [query-language.md](./query-language.md#projection-selects)).
|
|
23
|
+
|
|
24
|
+
> Changing the **definition** of an entity's fields (adding a custom field, for instance) is not an entity edit: see [entity-template.md](./entity-template.md).
|
|
23
25
|
|
|
24
26
|
---
|
|
25
27
|
|
|
@@ -29,6 +31,6 @@ POST {api}/{shardId}/{entity}/{entityId}
|
|
|
29
31
|
POST {api}/{shardId}/{entity}/massupdate
|
|
30
32
|
```
|
|
31
33
|
|
|
32
|
-
Bulk EDIT like `UPDATE … WHERE` in SQL.
|
|
34
|
+
Bulk EDIT, like `UPDATE … WHERE` in SQL. Relies on a filter (see [query-language.md](./query-language.md#filters)).
|
|
33
35
|
|
|
34
|
-
>
|
|
36
|
+
> To update a batch of entities identified one by one, see also [import.md](./import.md).
|