@syncmatters/connector-sdk 1.0.5 → 1.0.6
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/docs/04-meta-objects-fields.md +6 -1
- package/docs/06-upsert-delete.md +2 -1
- package/docs/09-testing.md +43 -9
- package/module.d.ts +1 -1
- package/package.json +3 -3
|
@@ -75,7 +75,7 @@ same as "no list support". See [the query capability bar](#the-query-capability-
|
|
|
75
75
|
| `canUpdateOptions: true` | new picklist options may be added via `upsertFieldOptions()` |
|
|
76
76
|
| `objectFields` | child fields when `type: "object"` |
|
|
77
77
|
| `arrayType` / `arrayObjectFields` | element type / element fields when `type: "array"` |
|
|
78
|
-
| `constraints` | validation: `mandatory` (`"always"` · `"add"` · `"update"
|
|
78
|
+
| `constraints` | validation: `mandatory` (`"always"` · `"add"` · `"update"`; array children only when parent array is non-empty), `max/min/maxLen/minLen`, `regex`, `email`, `integer`, `decimals`, `trim`, `dt: { format: "iso8601", "unix", ... }` |
|
|
79
79
|
| `isCustom` | account-specific/custom field |
|
|
80
80
|
| `data` | connector-private payload, echoed back in metadata |
|
|
81
81
|
|
|
@@ -215,6 +215,11 @@ last-modified filter actually exists on that object.
|
|
|
215
215
|
- `mandatory` (`"always" | "add" | "update"`) raises a `ValueRequired` issue — and
|
|
216
216
|
`mandatoryAccepts: ["null" | "blank_string"]` lets a mandatory field treat `null`/`""` as a
|
|
217
217
|
satisfying value (for APIs where "explicitly blank" is meaningful).
|
|
218
|
+
- For fields inside **`arrayObjectFields`**, `mandatory` is only checked when the parent array
|
|
219
|
+
has at least one element. An empty array (`[]`), a missing array property, or a missing parent
|
|
220
|
+
object does **not** raise `ValueRequired` for child fields — e.g. `ContactLinks.list.[0-n].CompanyId`
|
|
221
|
+
with `mandatory: "always"` is required per link object, but not when `list` is empty.
|
|
222
|
+
When the array is non-empty, **every** element must satisfy the mandatory constraint.
|
|
218
223
|
|
|
219
224
|
## `identity`
|
|
220
225
|
|
package/docs/06-upsert-delete.md
CHANGED
|
@@ -108,7 +108,8 @@ async upsertClean(options) {
|
|
|
108
108
|
auto-created via your `upsertFieldOptions()` (resolved ids written back into the upsert).
|
|
109
109
|
**Because it mutates, always run the verifies BEFORE `detectFirstChange`** — the production
|
|
110
110
|
fleet does, without exception.
|
|
111
|
-
- `verifyFieldConstraints` enforces your `ObjectField.constraints
|
|
111
|
+
- `verifyFieldConstraints` enforces your `ObjectField.constraints` (including `mandatory` on
|
|
112
|
+
array item fields — only when the parent array is non-empty; see [04](./04-meta-objects-fields.md)).
|
|
112
113
|
- Issues are `{ type, fatal?, fieldPath?, value?, option? }`; `fatal: true` blocks the row.
|
|
113
114
|
- Returning `hasChanges` with no fatal issues queues the row; the platform batches queued rows
|
|
114
115
|
and calls your `upsert()` with them later (clean order = write order; `upsertClean` calls
|
package/docs/09-testing.md
CHANGED
|
@@ -112,14 +112,9 @@ To disable those tests use `cannotTestReason` instead
|
|
|
112
112
|
(`rowFiltersPrepare` exists in the contract but the suite does NOT run rowFilter tests yet —
|
|
113
113
|
when `meta()` exposes `queryFilter.rowFilter`, cover it yourself with a direct query in a
|
|
114
114
|
hook you already implement.)
|
|
115
|
-
- `checkpointFilterStep1Prepare` / `Step2Prepare` — step
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
(`{ meta, step1TestData, checkpointReceived }`) and returns the `expectedRowIds` a query
|
|
119
|
-
FROM it must surface. Mutating data between the steps is one way to produce step-2 rows —
|
|
120
|
-
but not required: on APIs where you can't create rows in-test, pick a step-1 checkpoint far
|
|
121
|
-
enough back that existing rows fall on both sides (remember your connector's overlap window
|
|
122
|
-
re-surfaces recent rows in step 2 — include them in `expectedRowIds`).
|
|
115
|
+
- `checkpointFilterStep1Prepare` / `Step2Prepare` — the two-step round-trip test of your
|
|
116
|
+
differential contract; see [Checkpoint testing](#checkpoint-testing-what-the-two-steps-prove)
|
|
117
|
+
below, it is the most-misimplemented pair.
|
|
123
118
|
- `matchFilterPrepare` — returns `MatchData` including `expectedMatchRowIds`.
|
|
124
119
|
- `relatedFilterPrepare({ fromObjectMeta, toObjectMeta, fromRelationshipId })` —
|
|
125
120
|
`fromObjectMeta` is the object where the relationship is DECLARED (the parent, in the
|
|
@@ -135,6 +130,44 @@ To disable those tests use `cannotTestReason` instead
|
|
|
135
130
|
checks a flag field instead of row absence, and `afterDeleteMaxIndexWaitTimeMs` tolerates
|
|
136
131
|
APIs whose deletions surface asynchronously.
|
|
137
132
|
|
|
133
|
+
## Checkpoint testing: what the two steps prove
|
|
134
|
+
|
|
135
|
+
The checkpoint contract is a ROUND-TRIP ([05](./05-query.md)): the platform stores whatever
|
|
136
|
+
checkpoint string your final page emits and hands it back on the next differential run. One
|
|
137
|
+
query can't test a round-trip — that's why there are two steps, and they prove different
|
|
138
|
+
things:
|
|
139
|
+
|
|
140
|
+
- **Step 1 proves your filter reads correctly**: given a checkpoint, the query returns
|
|
141
|
+
exactly the changed rows.
|
|
142
|
+
- **Step 2 proves the checkpoint you EMIT is usable**: the harness takes the cursor your
|
|
143
|
+
step-1 query returned — not one you craft — and queries FROM it. This is the step that
|
|
144
|
+
catches the real bugs: an emitted cursor the query can't parse back, a `Date.now()`
|
|
145
|
+
checkpoint with no overlap (rows modified mid-query lost forever), a cursor in the wrong
|
|
146
|
+
timezone/format.
|
|
147
|
+
|
|
148
|
+
Mechanics: `checkpointFilterStep1Prepare({ meta })` returns
|
|
149
|
+
`{ nextCheckpoint, expectedRowIds }` — your chosen STARTING checkpoint and every row a query
|
|
150
|
+
from it must return. The harness runs the query, verifies the rows, and captures the
|
|
151
|
+
checkpoint your connector emitted. `checkpointFilterStep2Prepare({ meta, step1TestData,
|
|
152
|
+
checkpointReceived })` then returns the `expectedRowIds` for a query from
|
|
153
|
+
`checkpointReceived`.
|
|
154
|
+
|
|
155
|
+
Three rules that decide whether your expectations are right:
|
|
156
|
+
|
|
157
|
+
1. **The comparison is an exact set match, both steps** — a missing expected row fails AND
|
|
158
|
+
an unexpected returned row fails. Never guess; when in doubt, run the same
|
|
159
|
+
`checkpointFilter` query inside the prepare hook and report what it returns (the worked
|
|
160
|
+
hooks in [15](./15-example-test-file.md) do exactly this — a shared sandbox is never
|
|
161
|
+
quiet).
|
|
162
|
+
2. **Your own overlap window works against you in step 2.** You emit checkpoints
|
|
163
|
+
`startTime - overlap` (05), so a query from `checkpointReceived` legitimately re-returns
|
|
164
|
+
step-1 rows modified inside the window. Expect them — the classic step-2 failure is
|
|
165
|
+
listing only the fresh mutation and being surprised by "Returned unexpected row".
|
|
166
|
+
3. **Mutating data between the steps is optional.** Creating one fresh row step 2 must
|
|
167
|
+
surface is the strongest test, but on APIs where you can't create rows, a self-consistent
|
|
168
|
+
expectation (query from `checkpointReceived`, report what came back) is valid — the
|
|
169
|
+
round-trip and overlap behavior still get proven.
|
|
170
|
+
|
|
138
171
|
## What each test needs from your sandbox data
|
|
139
172
|
|
|
140
173
|
Requirements that only surface as cryptic failures when unmet:
|
|
@@ -151,7 +184,8 @@ Requirements that only surface as cryptic failures when unmet:
|
|
|
151
184
|
`expectedRelatedRowIds` must be non-empty. In the parent-owns-children shape that means a
|
|
152
185
|
parent with at least one child in the sandbox (create one in `before()` when the API allows;
|
|
153
186
|
otherwise seed the sandbox account once and document it in the test file).
|
|
154
|
-
- **Checkpoint step 2 does not have to create data** — see
|
|
187
|
+
- **Checkpoint step 2 does not have to create data** — see
|
|
188
|
+
[Checkpoint testing](#checkpoint-testing-what-the-two-steps-prove) above.
|
|
155
189
|
|
|
156
190
|
## Insert-only / immutable objects
|
|
157
191
|
|
package/module.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* SDK specific elements that would be of no interest to users of the API.
|
|
4
4
|
*/
|
|
5
5
|
export { EventScriptComplete } from "@syncmatters/script-api";
|
|
6
|
-
export type { Cache, FileProvider, HttpRequest, HttpClientOptions, KvCollection, KvIterator, Logger, EventContext, TestResult, Row, ObjectMeta, ObjectField, ObjectFieldOption, RateLimiterOptions, ScriptType, JsonValuePath, JsonValuePathPart, QueryMatchFilter, QueryCheckpointFilter, EventTypeMeta, ObjectIndex, ObjectMetaUpsert, ObjectRowFilter, ObjectRelationship, ObjectFieldConstraints, ObjectSettingMeta, UpsertIssue, UpsertFieldOptionMeta, RowMatchRuleType, SyncSession, SyncOptimalBatchSizeOperation, SyncOptimalBatchSizeBatchType, } from "@syncmatters/script-api";
|
|
6
|
+
export type { Cache, FileProvider, HttpRequest, HttpClientOptions, KvCollection, KvIterator, Logger, EventContext, TestResult, Row, ObjectMeta, ObjectField, ObjectFieldOption, RateLimiterOptions, ScriptType, JsonValuePath, JsonValuePathPart, QueryMatchFilter, QueryCheckpointFilter, EventTypeMeta, ObjectIndex, ObjectMetaUpsert, ObjectRowFilter, ObjectRelationship, ObjectFieldConstraints, ObjectSettingMeta, UpsertIssue, UpsertFieldOptionMeta, RowMatchRuleType, RowMatchData, RowMatchRuleOrder, SyncSession, SyncOptimalBatchSizeOperation, SyncOptimalBatchSizeBatchType, } from "@syncmatters/script-api";
|
|
7
7
|
export * from "./lib/connector.js";
|
|
8
8
|
export * from "./lib/connector-error.js";
|
|
9
9
|
export { envSet } from "./lib/environment.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncmatters/connector-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "TypeScript type definitions for the SyncMatters connector SDK (types only - connectors execute on the SyncMatters platform)",
|
|
5
5
|
"types": "./index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
"license": "MIT",
|
|
13
13
|
"author": "SyncMatters",
|
|
14
14
|
"homepage": "https://syncmatters.com",
|
|
15
|
-
"typesContentHash": "
|
|
15
|
+
"typesContentHash": "9d23ef0dc02fc7886f97e31eba1d50be7f8f6dae368c9b3ef4b1c969807d005c",
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@types/node": "*",
|
|
18
|
-
"@syncmatters/script-api": "^1.0.
|
|
18
|
+
"@syncmatters/script-api": "^1.0.5"
|
|
19
19
|
}
|
|
20
20
|
}
|