@syncmatters/connector-sdk 1.0.20 → 1.0.21
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/09-testing.md
CHANGED
|
@@ -135,6 +135,17 @@ To disable those tests use `cannotTestReason` instead
|
|
|
135
135
|
contract under "What each test needs" below. Use `withIssueSimulators` /
|
|
136
136
|
`UpsertIssueSimulator` (`verifyFields`, expected issue `type` + `fatal`) to deliberately
|
|
137
137
|
exercise your `upsertClean` issue reporting.
|
|
138
|
+
- **File fields in `upsertPrepare`.** When the object has an upsert field of `type: "file"`,
|
|
139
|
+
every `upsertPrepare` call (upsert, delete and upsertClean tests) carries `withFileField`
|
|
140
|
+
set to its path (a field with `constraints: { mandatory: "add" }` is preferred, else the
|
|
141
|
+
first file field). Put an `SDK.FileProvider` at that path in `insert` (and `update`), and
|
|
142
|
+
return a **fresh provider on every call**: the harness passes the connector a clone
|
|
143
|
+
(`SDK.utilities.clone(row, { withFileProviders: true })`), so the connector may close what
|
|
144
|
+
it is handed, and the harness closes your originals and its clones once the test has
|
|
145
|
+
verified them ([13-file-fields.md](./13-file-fields.md)). A file field listed in `verifyFields` is
|
|
146
|
+
compared by bytes with `FileProvider.equals()`: the query-back must return a provider with
|
|
147
|
+
the same content. The harness never adds the file field to the query-back on its own,
|
|
148
|
+
since that downloads the content; list it in `verifyFields` or `fields` to opt in.
|
|
138
149
|
- delete features support soft-delete verification — `isDeleted: { path, valueWhenDeleted }`
|
|
139
150
|
checks a flag field instead of row absence, and `afterDeleteMaxIndexWaitTimeMs` tolerates
|
|
140
151
|
APIs whose deletions surface asynchronously.
|
|
@@ -119,7 +119,9 @@
|
|
|
119
119
|
The two you will use constantly (they appear in nearly every fleet connector):
|
|
120
120
|
|
|
121
121
|
- `SDK.utilities.clone(value)` — deep clone. Use before mutating anything you received or
|
|
122
|
-
return (metadata, rows, options) — shared references are how callers get corrupted.
|
|
122
|
+
return (metadata, rows, options) — shared references are how callers get corrupted. A
|
|
123
|
+
`FileProvider` in the value becomes an unreadable shim unless you pass
|
|
124
|
+
`{ withFileProviders: true }` ([13-file-fields.md](./13-file-fields.md)).
|
|
123
125
|
- `SDK.utilities.tryGet(() => deeply.nested.maybe.missing)` — returns the value or
|
|
124
126
|
`undefined`, never throws. The idiom for prodding uncertain API payloads:
|
|
125
127
|
`const sig = tryGet(() => options.payload.headers["x-signature"][0]);`
|
package/docs/13-file-fields.md
CHANGED
|
@@ -82,10 +82,34 @@ SDK.utilities.fileProvider({ file: { path: tempPath, deleteOnClose: true } });
|
|
|
82
82
|
| `save(path?)` | persist to disk, returns the path (pair with `SDK.utilities.tempFile`) |
|
|
83
83
|
| `length()` | size in bytes (e.g. for a `size` field or Content-Length header) |
|
|
84
84
|
| `close()` | release the resource — **always, in a `finally`** |
|
|
85
|
+
| `clone()` | an independent provider over the same bytes; close it like any other |
|
|
86
|
+
| `equals(other)` | `true` when both hold the same bytes; `false` if either side is closed |
|
|
85
87
|
|
|
86
88
|
Parsing structured file content: `SDK.utilities.csvReader({ source: { fileProvider } })`
|
|
87
89
|
(same idea for `xlsxReader`).
|
|
88
90
|
|
|
91
|
+
## Cloning and comparing
|
|
92
|
+
|
|
93
|
+
`clone()` returns an independent provider: closing the original does not close the clone, and
|
|
94
|
+
the clone **must be closed too**. A clone of a temp-file provider (`deleteOnClose`) shares the
|
|
95
|
+
file by reference count, so the file is removed only when the last holder closes. A provider
|
|
96
|
+
dropped without `close()` is released when the runtime collects it, which may be much later, so
|
|
97
|
+
closing stays the rule. Cloning a closed provider throws `ResourceAlreadyClosed`.
|
|
98
|
+
|
|
99
|
+
Deep-copying a row that holds a file:
|
|
100
|
+
|
|
101
|
+
- `SDK.utilities.clone(row)` copies the data and replaces each provider with a **shim**. The
|
|
102
|
+
shim serialises like a provider (`"[object FileProvider]"`) and needs no close, but every read
|
|
103
|
+
(`stream()`, `length()`, `save()`, `blob()`, `formDataValue()`, `clone()`, `equals()`) throws
|
|
104
|
+
`SCRIPT_FILE_PROVIDER_SHALLOW_CLONE`. That is what a snapshot for logging wants.
|
|
105
|
+
- `SDK.utilities.clone(row, { withFileProviders: true })` clones each provider with `clone()`,
|
|
106
|
+
so the copy can read the files. The code that made the copy owns its providers and must
|
|
107
|
+
close them.
|
|
108
|
+
|
|
109
|
+
`equals(other)` compares bytes: lengths first, then both streams chunk by chunk, without
|
|
110
|
+
reading whole files into memory. It resolves `false` rather than throwing when either side has
|
|
111
|
+
been closed.
|
|
112
|
+
|
|
89
113
|
## Upserting: the file arrives as a field value
|
|
90
114
|
|
|
91
115
|
The upsert row carries a `FileProvider` at the file field's position. Stream it out and close
|
|
@@ -121,3 +145,9 @@ async upsert(options) {
|
|
|
121
145
|
3. **Close what you open.** Providers can be backed by temp files; `close()` in a `finally`
|
|
122
146
|
on both the query and upsert paths.
|
|
123
147
|
|
|
148
|
+
## Testing
|
|
149
|
+
|
|
150
|
+
The connector test harness exercises file fields through the upsert, delete and upsertClean
|
|
151
|
+
tests: it asks `upsertPrepare` for a file at `withFileField`, passes the connector a clone, and compares a file
|
|
152
|
+
field listed in `verifyFields` by bytes. See
|
|
153
|
+
[09-testing.md](./09-testing.md#preparecleanup-hooks-and-their-data-types).
|
|
@@ -7,5 +7,7 @@ export declare class FileProviderBuffer implements FileProvider {
|
|
|
7
7
|
blob(fileName?: string): Promise<Blob>;
|
|
8
8
|
save(path?: string): Promise<string>;
|
|
9
9
|
close(): Promise<void>;
|
|
10
|
+
clone(): FileProvider;
|
|
11
|
+
equals(other: FileProvider): Promise<boolean>;
|
|
10
12
|
toJSON(): string;
|
|
11
13
|
}
|
|
@@ -7,5 +7,7 @@ export declare class FileProviderDisk implements FileProvider {
|
|
|
7
7
|
blob(fileName?: string): Promise<Blob>;
|
|
8
8
|
save(path?: string): Promise<string>;
|
|
9
9
|
close(): Promise<void>;
|
|
10
|
+
clone(): FileProvider;
|
|
11
|
+
equals(other: FileProvider): Promise<boolean>;
|
|
10
12
|
toJSON(): string;
|
|
11
13
|
}
|
|
@@ -7,5 +7,7 @@ export declare class FileProviderString implements FileProvider {
|
|
|
7
7
|
blob(fileName?: string): Promise<Blob>;
|
|
8
8
|
save(path?: string): Promise<string>;
|
|
9
9
|
close(): Promise<void>;
|
|
10
|
+
clone(): FileProvider;
|
|
11
|
+
equals(other: FileProvider): Promise<boolean>;
|
|
10
12
|
toJSON(): string;
|
|
11
13
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncmatters/connector-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.21",
|
|
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": "cbce2eec280ba657cbe2b0aa321b46b3578ab203622d95a3030beb8e5180cd78",
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@types/node": "*",
|
|
18
|
-
"@syncmatters/script-api": "^1.0.
|
|
18
|
+
"@syncmatters/script-api": "^1.0.23"
|
|
19
19
|
}
|
|
20
20
|
}
|