@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.
@@ -1,68 +1,32 @@
1
- ◂ Retour à [README](./README.md)
1
+ ◂ Back to [README](./README.md)
2
2
 
3
- # Entity Contracts, Schemas & Other Endpoints
3
+ # Entity contracts & other endpoints
4
4
 
5
- > Les champs exacts d'une entité se découvrent via `inwink_mcp_get_entity_description`. Les schémas ci-dessous sont indicatifs.
5
+ > The exact fields of an entity are discovered with `inwink_mcp_get_entity_description`: this page lists no schema.
6
6
 
7
- ## Other Endpoints
7
+ ## Entity contracts
8
8
 
9
- | Endpoint | Pattern | Purpose |
10
- |---|---|---|
11
- | Worktemplate | `GET {api}/{shardId}/{entity}/worktemplate` | Returns the entity template (list of properties, types, required flags, localization info) |
12
- | History | `POST {api}/{shardId}/{entity}/history/query` | Query past modifications (same body as query). Use sparingly — filter by `id` for performance. |
13
- | Massupdate | `POST {api}/{shardId}/{entity}/massupdate` | Bulk EDIT like `UPDATE … WHERE` in SQL (voir [update.md](./update.md)) |
14
- | Massdelete | `POST {api}/{shardId}/{entity}/massdelete` | Bulk DELETE like `DELETE … WHERE` in SQL (voir [delete.md](./delete.md)) |
15
- | Export | Returns entities as an Excel file | |
16
- | File | Upload files (voir [files.md](./files.md)) | |
17
-
18
- ---
19
-
20
- ## Entity Contracts
21
-
22
- ### Event Entity
23
-
24
- Scoped to EventAPI. Always has an `eventId` property. URL uses `{eventId}` as shardId.
25
-
26
- ### Customer Entity
27
-
28
- Scoped to CustomerAPI. Always has a `customerId` property. URL uses `{customerId}` as shardId.
29
-
30
- ### Dynamic Entity
31
-
32
- Can have custom properties added via backoffice or template API. Custom properties behave identically to built-in properties in all endpoints.
33
-
34
- ### With Publication Status
35
-
36
- Has a `status` property (string, case-insensitive): `Draft`, `Preview`, `Published`, `Canceled`, `Rejected`.
37
-
38
- ### With History
39
-
40
- Tracked modifications. Read-only properties: `validFrom` (last modification date, UTC), `modifiedById` (UUID), `modifiedByDisplayName` (string).
9
+ - **Event Entity** — scoped to EventAPI. Always has an `eventId` property. The URL uses `{eventId}` as shardId.
10
+ - **Community Entity** — scoped to CommunityAPI. Always has a `communityId` property. The URL uses `{communityId}` as shardId. Community entities (members, forums, discussion subjects…) are **not** reachable through EventAPI: an event and a community are two separate containers.
11
+ - **Customer Entity** — scoped to CustomerAPI. Always has a `customerId` property. The URL uses `{customerId}` as shardId.
12
+ - **Auth Tenant Entity** — scoped to an **audience** (tenant) of a customer, on CustomerAPI. Its name starts with `authtenant` (`authtenantcommunity`, `authtenantcontact`, `authtenantcompany`, `authtenantmailtemplate`…). The URL takes two segments: `{customerId}/audience/{tenantId}` — see [README.md](./README.md#audience-scoped-entities-customerapi).
13
+ - **Dynamic Entity** — can have custom properties added through the backoffice or the template API. Custom properties declared on the entity's own template (the one `inwink_mcp_get_entity_description` returns) behave exactly like native properties on every endpoint. Some fields are declared by a parent record instead: they resolve only on the routes scoped to that parent record, and fail elsewhere with `400 … property resolution error`, like an unknown name. To declare a custom property from the extension, see [entity-template.md](./entity-template.md).
14
+ - **With Publication Status** — has a `status` property (string, case-insensitive): `Draft`, `Preview`, `Published`, `Canceled`, `Rejected`.
15
+ - **With History** — modifications are tracked in a history table (see [history.md](./history.md)). Read-only properties: `validFrom` (start of validity of the current version = last modification date, UTC), `validTo` (end of validity; on the current table, an "infinity" sentinel value), `modifiedById` (UUID), `modifiedByDisplayName` (string).
41
16
 
42
17
  ---
43
18
 
44
- ## Common Entities & Schemas
45
-
46
- ### Person (EventAPI)
47
-
48
- Contracts: Event entity, Dynamic, With Publication Status, With History.
49
-
50
- Key properties: `id` (uuid), `mail`, `firstname`, `lastname`, `company`, `jobTitle`, `preferredLanguage`, `isRegistered` (bool), `hasAttended` (bool), `status`, `registrationDate`, `registrationMode` (enum: BackOffice, Website, OnSite, Order, Invitation, ExhibitorAccount), `checkinDate`, `kinds` (string[]), `badgeRef`, `userId`, `contactId`, `description` (localizable), `photo` (file ref).
51
-
52
- Expandable relations: `exhibitor`, `infos`, `roomingInfos`, `exhibitorAccounts`, `orders`, `hotelRoomNights`, `sessions`, `favoriteExhibitors`, `animatedSessions`, `orderItems`, `personScans`, `tasks`, `meetingPersons`, `availabilities`, `agenda`, `activityView`.
53
-
54
- ### EventDetail (EventAPI)
55
-
56
- Contracts: Event entity, With History.
19
+ ## Other endpoints
57
20
 
58
- Key properties: `id`, `eventId`, `eventType` (array: Onsite/Online), `configuration`, `agenda`, `previewHost`.
59
-
60
- Expandable relations: `sessions`, `exhibitors`.
61
-
62
- ### Event (CustomerAPI)
63
-
64
- Contracts: Customer entity, Dynamic, With History.
65
-
66
- Key properties: `id`, `customerId`, `title`, `description` (localizable), `startDate`, `endDate`, `city`, `country`, `eventKey`, `computedUrl`, `status` (Active/Archived/Deleted), `eventType`, `isMaster`, `timeZone`, `parentEventId`, `goalRegistered`, `goalAttendees`, `goalSales`.
67
-
68
- Expandable relations: `stat`, `authTenant`, `eventHttpHostAliases`, `websites`.
21
+ | Endpoint | Pattern | Purpose |
22
+ |---|---|---|
23
+ | Worktemplate | `GET {api}/{shardId}/{entity}/worktemplate` | Returns the entity template (properties, types, required flags, localization) — see [entity-template.md](./entity-template.md) |
24
+ | Template (patch) | `PATCH {api}/{shardId}/{entity}/template` | Adds/edits/removes **one** custom field without touching the others — see [entity-template.md](./entity-template.md) |
25
+ | Template (replace) | `POST {api}/{shardId}/{entity}/updateentitytemplate` | Replaces the **whole** custom template: whatever is missing from the body is lost — see [entity-template.md](./entity-template.md) |
26
+ | Aggregate | `POST {api}/{shardId}/{entity}/aggregate` | Grouping / aggregation (GROUP BY) — see [aggregate.md](./aggregate.md) |
27
+ | History | `POST {api}/{shardId}/{entity}/history/query` | Past versions (same body as query). Always filter by `id` — see [history.md](./history.md) |
28
+ | Removed | `POST {api}/{shardId}/{entity}/history/removed/query` | Deleted entities (+ `/history/removed/count`) — see [history.md](./history.md) |
29
+ | Massupdate | `POST {api}/{shardId}/{entity}/massupdate` | Bulk EDIT, like `UPDATE … WHERE` in SQL — see [update.md](./update.md) |
30
+ | Massdelete | `POST {api}/{shardId}/{entity}/massdelete` | Bulk DELETE, like `DELETE … WHERE` in SQL — see [delete.md](./delete.md) |
31
+ | Export | | Returns entities as an Excel file |
32
+ | File | | File upload — see [files.md](./files.md) |
@@ -0,0 +1,201 @@
1
+ ◂ Back to [README](./README.md)
2
+
3
+ # Entity template (custom fields)
4
+
5
+ The **template** of a dynamic entity describes its custom fields: the ones the backoffice
6
+ (or an extension) adds on top of the native columns. Once declared in the
7
+ template, a custom field behaves exactly like a native field on `query`,
8
+ `count`, mutations, export…
9
+
10
+ Three routes, all scoped by the usual `shardId`:
11
+
12
+ | Verb | Route | Role |
13
+ |---|---|---|
14
+ | `GET` | `{api}/{shardId}/{entity}/worktemplate` | Read the consolidated template (native + custom) |
15
+ | `PATCH` | `{api}/{shardId}/{entity}/template` | **Partial edit** — touch only the named fields |
16
+ | `POST` | `{api}/{shardId}/{entity}/updateentitytemplate` | **Full replacement** of the custom template |
17
+
18
+ If the entity is also exposed under a nested route (e.g.
19
+ `{communityId}/forums/{forumId}/subjects/`), the three suffixes exist there too.
20
+
21
+ > ⚠️ **Writing to the template of a production community or event changes a
22
+ > customer's data model.** To declare a field, use `PATCH`: it is the only one
23
+ > of the two writes that cannot erase custom fields you did not write.
24
+
25
+ `inwink_mcp_find_api_endpoint` publishes the PATCH body, typed, with the list of accepted
26
+ field types: call it for the exact payload shape. This page covers what it does not
27
+ say — the compared semantics of the three routes, and their blind spots.
28
+
29
+ ---
30
+
31
+ ## PATCH — add / modify / remove a field (preferred)
32
+
33
+ ```
34
+ PATCH {api}/{shardId}/{entity}/template
35
+ ```
36
+
37
+ ```json
38
+ {
39
+ "replaceFields": {
40
+ "feedbackType": {
41
+ "type": "selectlist",
42
+ "labels": { "fr": "Type de retour", "en": "Feedback type" },
43
+ "valuesList": [
44
+ { "key": "bug", "labels": { "fr": "Anomalie", "en": "Bug" } },
45
+ { "key": "idea", "labels": { "fr": "Idée", "en": "Idea" } }
46
+ ]
47
+ }
48
+ },
49
+ "scopes": { "feedbackType": ["private"] }
50
+ }
51
+ ```
52
+
53
+ The API reads the stored template, applies the patch to it, then writes it back. **Any key not
54
+ mentioned in the patch is left as is** — this is what makes the operation safe without
55
+ having to read the template beforehand.
56
+
57
+ - `replaceFields` — map `field key → definition`. The entry fully **replaces** the
58
+ field definition (no property-by-property merge): send the complete
59
+ field definition, not just the property that changes. A key absent from the template is
60
+ added to it.
61
+ - `replaceFields: { "myKey": null }` — **removes** the field from the template. Two side effects:
62
+ its entry in `scopes` remains, and the key is not recorded in `oldKeys`, which the
63
+ PATCH body does not carry.
64
+ - `scopes` — map `field key → array of scopes`. Each entry **overwrites** the scopes array
65
+ of that key; a patch cannot remove an entry from `scopes`. A patch that
66
+ contains only `scopes` is valid: it is used to expose or hide a field, including a
67
+ native field, without redefining any field.
68
+ - A field added by a patch is placed at the end of `fields`.
69
+ - The dictionary key is authoritative: a `key` property inside the definition is
70
+ overwritten with it.
71
+ - Response: `200` with no body; `204` if the body is empty.
72
+
73
+ ### What is outside the scope of PATCH
74
+
75
+ The PATCH body carries only `replaceFields` and `scopes`, and expresses deletion only
76
+ on fields. Three needs therefore require POST:
77
+
78
+ - **removing an entry from `scopes`** — a patch rewrites it, never removes it;
79
+ - **setting `oldKeys`** — absent from the PATCH body;
80
+ - **resetting a template**, or **enforcing the order of `fields`**.
81
+
82
+ None of these three needs comes up when declaring a custom field: stay on PATCH.
83
+
84
+ ## POST — replace the whole template
85
+
86
+ ```
87
+ POST {api}/{shardId}/{entity}/updateentitytemplate
88
+ ```
89
+ *(`POST {api}/{shardId}/{entity}/template` is a second endpoint, with the same body and the same
90
+ effect.)*
91
+
92
+ ```json
93
+ {
94
+ "fields": [
95
+ { "key": "feedbackType", "type": "selectlist", "valuesList": [] }
96
+ ],
97
+ "scopes": { "feedbackType": ["private"] },
98
+ "oldKeys": []
99
+ }
100
+ ```
101
+
102
+ The body **becomes** the stored template: any custom field absent from `fields` is lost, and
103
+ any scope absent from `scopes` along with it. Note that `fields` is here an **array** whose every
104
+ element carries its own `key`, whereas `PATCH` and `GET worktemplate` use a **map**.
105
+
106
+ Use `POST` only if you own the entire template, or for one
107
+ of the three needs listed above. In particular, **the result of `GET worktemplate` cannot be
108
+ sent back as is**: the shape of `fields` differs and the response contains the native
109
+ fields, which the write rejects.
110
+
111
+ ## GET worktemplate — read
112
+
113
+ ```
114
+ GET {api}/{shardId}/{entity}/worktemplate
115
+ ```
116
+
117
+ ```json
118
+ {
119
+ "fields": { "mail": { "key": "mail", "type": "Mail" } },
120
+ "scopes": { "mail": ["bo"] },
121
+ "oldKeys": ["id", "eventId", "mail"],
122
+ "version": "2026-09-21T09:14:22Z"
123
+ }
124
+ ```
125
+
126
+ - `fields` — consolidated map of **native fields + custom fields**, in display shape
127
+ (`display`, `validation`, `file`…), different from the write shape.
128
+ - `oldKeys` — the keys of the entity's **static** fields (its native columns), in
129
+ camelCase. This is the list of names already taken: a custom field cannot reuse them.
130
+ - `version` — date of the template's last modification. Neither of the two writes takes
131
+ this token as input: there is no optimistic concurrency control, hence the value of
132
+ `PATCH`, whose read-write window stays server-side.
133
+
134
+ ---
135
+
136
+ ## Rejected fields
137
+
138
+ Some keys or some types cannot be saved as a custom field. By
139
+ default the API **silently ignores them** and responds `200`. To have them reported,
140
+ add the header:
141
+
142
+ ```
143
+ iwk-Validation: strict
144
+ ```
145
+
146
+ The request then responds `400` with, per offending key,
147
+ `errors.<key> = "forbidden field: <reason>"`:
148
+
149
+ | Reason | Cause |
150
+ |---|---|
151
+ | `MissingKey` | empty key |
152
+ | `StaticField` | the key is a native column of the entity (see `oldKeys`) |
153
+ | `LockedField` | the key is a dynamic field locked by the platform |
154
+ | `DeclaredAsStatic` | you set `isStatic: true` on the field |
155
+ | `DeclaredAsLocked` | you set `isLocked: true` on the field |
156
+ | `UnsupportedType` | `type` missing, unknown, or reserved for the platform |
157
+
158
+ On `POST`, the rejected field is removed but its entry in `scopes` is kept.
159
+
160
+ ## Field definition
161
+
162
+ `type` is written by its name (`Text`, `SelectList`, `Bool`…). **The list of types accepted
163
+ for a custom field is the one published by `inwink_mcp_find_api_endpoint`** in the `type`
164
+ enumeration of the PATCH body: the API reads it from its own validator, so it cannot diverge —
165
+ do not copy it from here.
166
+
167
+ The numeric value is accepted too, and that is the form in which the type may appear
168
+ elsewhere (notably in the output of `inwink_mcp_get_entity_description`): `Text` 1,
169
+ `MultiLineText` 2, `SelectList` 3, `MultiSelectList` 4, `Regex` 5, `Mail` 6, `Date` 7,
170
+ `Number` 8, `Bool` 9, `NullableBool` 10, `File` 11, `Time` 12, `Picture` 13, `Image` 14,
171
+ `FileVideo` 15, `SelectTree` 16, `MultiSelectTree` 17, `NestedEntity` 50, `Object` 95,
172
+ `Document` 113.
173
+
174
+ Common properties:
175
+
176
+ - Shared — `isMandatory`, `isLocalizable`, `isPersonalData`, `isTechnical`,
177
+ `isCollection`, `defaultValue`, `defaultDisplay`.
178
+ - Display, per language — `labels`, `placeholders`, `descriptions`, `remarks`:
179
+ `{ "fr": "…", "en": "…" }`.
180
+ - `Number` — `min`, `max`, `nbDecimalLimit`. `Regex` — `regex`.
181
+ - `SelectList` / `MultiSelectList` / `*Tree` — `valuesList`, a list of entries
182
+ `{ "key": "…", "labels": { "fr": "…" }, "valuesList": [] }` (nested `valuesList` for
183
+ trees), plus `valuesListSort` for display order. On native fields,
184
+ `GET worktemplate` marks these entries `isSelectable: true`.
185
+ - Files (`File`, `Image`, `FileVideo`, `Document`) — `isPublicFile`, `maxSize`,
186
+ `mimeTypes`, `aspectRatio`. See [files.md](./files.md) for uploading values.
187
+
188
+ Do not set `isStatic` or `isLocked`: these flags describe what the platform
189
+ declares itself and cause the field to be rejected.
190
+
191
+ ## scopes
192
+
193
+ `scopes` maps a field key to an array of scope names, which decides who sees the
194
+ field (`public`, `bo`, `private`, `own`… depending on the API and the entity). These are strings
195
+ interpreted by the entity's configuration, not a closed enumeration: to find out
196
+ which ones are meaningful on a given entity, look at the `scopes` that `GET worktemplate`
197
+ already returns for its fields.
198
+
199
+ ---
200
+
201
+ *See also: [update.md](./update.md), [entities.md](./entities.md), [files.md](./files.md).*