@party-stack/salesforce-ontology 0.1.0
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/LICENSE +21 -0
- package/README.md +114 -0
- package/lib/adapter/convertLoadSubsetOptions.d.ts +18 -0
- package/lib/adapter/convertLoadSubsetOptions.d.ts.map +1 -0
- package/lib/adapter/convertLoadSubsetOptions.js +144 -0
- package/lib/adapter/convertLoadSubsetOptions.js.map +1 -0
- package/lib/adapter/convertLoadSubsetOptions.test.d.ts +2 -0
- package/lib/adapter/convertLoadSubsetOptions.test.d.ts.map +1 -0
- package/lib/adapter/convertLoadSubsetOptions.test.js +54 -0
- package/lib/adapter/convertLoadSubsetOptions.test.js.map +1 -0
- package/lib/adapter/createSalesforceOntologyBackendAdapter.d.ts +13 -0
- package/lib/adapter/createSalesforceOntologyBackendAdapter.d.ts.map +1 -0
- package/lib/adapter/createSalesforceOntologyBackendAdapter.js +90 -0
- package/lib/adapter/createSalesforceOntologyBackendAdapter.js.map +1 -0
- package/lib/adapter/createSalesforceOntologyBackendAdapter.test.d.ts +2 -0
- package/lib/adapter/createSalesforceOntologyBackendAdapter.test.d.ts.map +1 -0
- package/lib/adapter/createSalesforceOntologyBackendAdapter.test.js +142 -0
- package/lib/adapter/createSalesforceOntologyBackendAdapter.test.js.map +1 -0
- package/lib/adapter/index.d.ts +5 -0
- package/lib/adapter/index.d.ts.map +1 -0
- package/lib/adapter/index.js +5 -0
- package/lib/adapter/index.js.map +1 -0
- package/lib/adapter/objectCollectionOptions.d.ts +32 -0
- package/lib/adapter/objectCollectionOptions.d.ts.map +1 -0
- package/lib/adapter/objectCollectionOptions.js +139 -0
- package/lib/adapter/objectCollectionOptions.js.map +1 -0
- package/lib/adapter/salesforceCodec.d.ts +10 -0
- package/lib/adapter/salesforceCodec.d.ts.map +1 -0
- package/lib/adapter/salesforceCodec.js +201 -0
- package/lib/adapter/salesforceCodec.js.map +1 -0
- package/lib/adapter/salesforceCodec.test.d.ts +2 -0
- package/lib/adapter/salesforceCodec.test.d.ts.map +1 -0
- package/lib/adapter/salesforceCodec.test.js +57 -0
- package/lib/adapter/salesforceCodec.test.js.map +1 -0
- package/lib/config/index.d.ts +24 -0
- package/lib/config/index.d.ts.map +1 -0
- package/lib/config/index.js +178 -0
- package/lib/config/index.js.map +1 -0
- package/lib/config/index.test.d.ts +2 -0
- package/lib/config/index.test.d.ts.map +1 -0
- package/lib/config/index.test.js +139 -0
- package/lib/config/index.test.js.map +1 -0
- package/lib/connection.d.ts +33 -0
- package/lib/connection.d.ts.map +1 -0
- package/lib/connection.js +240 -0
- package/lib/connection.js.map +1 -0
- package/lib/connection.test.d.ts +2 -0
- package/lib/connection.test.d.ts.map +1 -0
- package/lib/connection.test.js +150 -0
- package/lib/connection.test.js.map +1 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +5 -0
- package/lib/index.js.map +1 -0
- package/lib/installation/createSalesforceBackendInstallation.d.ts +28 -0
- package/lib/installation/createSalesforceBackendInstallation.d.ts.map +1 -0
- package/lib/installation/createSalesforceBackendInstallation.js +77 -0
- package/lib/installation/createSalesforceBackendInstallation.js.map +1 -0
- package/lib/installation/createSalesforceBackendInstallation.test.d.ts +2 -0
- package/lib/installation/createSalesforceBackendInstallation.test.d.ts.map +1 -0
- package/lib/installation/createSalesforceBackendInstallation.test.js +34 -0
- package/lib/installation/createSalesforceBackendInstallation.test.js.map +1 -0
- package/lib/installation/index.d.ts +2 -0
- package/lib/installation/index.d.ts.map +1 -0
- package/lib/installation/index.js +2 -0
- package/lib/installation/index.js.map +1 -0
- package/lib/meta/actionTypeCollectionOptions.d.ts +10 -0
- package/lib/meta/actionTypeCollectionOptions.d.ts.map +1 -0
- package/lib/meta/actionTypeCollectionOptions.js +51 -0
- package/lib/meta/actionTypeCollectionOptions.js.map +1 -0
- package/lib/meta/convertMetaActionType.d.ts +12 -0
- package/lib/meta/convertMetaActionType.d.ts.map +1 -0
- package/lib/meta/convertMetaActionType.js +28 -0
- package/lib/meta/convertMetaActionType.js.map +1 -0
- package/lib/meta/convertMetaActionType.test.d.ts +2 -0
- package/lib/meta/convertMetaActionType.test.d.ts.map +1 -0
- package/lib/meta/convertMetaActionType.test.js +76 -0
- package/lib/meta/convertMetaActionType.test.js.map +1 -0
- package/lib/meta/convertMetaLinkType.d.ts +9 -0
- package/lib/meta/convertMetaLinkType.d.ts.map +1 -0
- package/lib/meta/convertMetaLinkType.js +55 -0
- package/lib/meta/convertMetaLinkType.js.map +1 -0
- package/lib/meta/convertMetaObjectType.d.ts +4 -0
- package/lib/meta/convertMetaObjectType.d.ts.map +1 -0
- package/lib/meta/convertMetaObjectType.js +45 -0
- package/lib/meta/convertMetaObjectType.js.map +1 -0
- package/lib/meta/convertMetaObjectType.test.d.ts +2 -0
- package/lib/meta/convertMetaObjectType.test.d.ts.map +1 -0
- package/lib/meta/convertMetaObjectType.test.js +179 -0
- package/lib/meta/convertMetaObjectType.test.js.map +1 -0
- package/lib/meta/convertMetaTypeDef.d.ts +13 -0
- package/lib/meta/convertMetaTypeDef.d.ts.map +1 -0
- package/lib/meta/convertMetaTypeDef.js +163 -0
- package/lib/meta/convertMetaTypeDef.js.map +1 -0
- package/lib/meta/createSalesforceMetaOntologyBackendAdapter.d.ts +14 -0
- package/lib/meta/createSalesforceMetaOntologyBackendAdapter.d.ts.map +1 -0
- package/lib/meta/createSalesforceMetaOntologyBackendAdapter.js +42 -0
- package/lib/meta/createSalesforceMetaOntologyBackendAdapter.js.map +1 -0
- package/lib/meta/entityCollectionOptions.d.ts +27 -0
- package/lib/meta/entityCollectionOptions.d.ts.map +1 -0
- package/lib/meta/entityCollectionOptions.js +80 -0
- package/lib/meta/entityCollectionOptions.js.map +1 -0
- package/lib/meta/index.d.ts +7 -0
- package/lib/meta/index.d.ts.map +1 -0
- package/lib/meta/index.js +7 -0
- package/lib/meta/index.js.map +1 -0
- package/lib/meta/queryFunctionTypeCollectionOptions.d.ts +7 -0
- package/lib/meta/queryFunctionTypeCollectionOptions.d.ts.map +1 -0
- package/lib/meta/queryFunctionTypeCollectionOptions.js +16 -0
- package/lib/meta/queryFunctionTypeCollectionOptions.js.map +1 -0
- package/lib/utils/AsyncIterable.d.ts +3 -0
- package/lib/utils/AsyncIterable.d.ts.map +1 -0
- package/lib/utils/AsyncIterable.js +26 -0
- package/lib/utils/AsyncIterable.js.map +1 -0
- package/lib/utils/ids.d.ts +10 -0
- package/lib/utils/ids.d.ts.map +1 -0
- package/lib/utils/ids.js +20 -0
- package/lib/utils/ids.js.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Robert Fidler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# salesforce-ontology
|
|
2
|
+
|
|
3
|
+
Salesforce connection, installation, metadata, and runtime ontology adapters.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import {
|
|
9
|
+
createSalesforceBackendInstallation,
|
|
10
|
+
createSalesforceOntologyRoute,
|
|
11
|
+
} from "@party-stack/salesforce-ontology";
|
|
12
|
+
|
|
13
|
+
const installation =
|
|
14
|
+
await createSalesforceBackendInstallation({
|
|
15
|
+
instanceUrl:
|
|
16
|
+
"https://example.my.salesforce.com",
|
|
17
|
+
apiVersion: "65.0",
|
|
18
|
+
runtime,
|
|
19
|
+
connections: {
|
|
20
|
+
oauth: {
|
|
21
|
+
clientId,
|
|
22
|
+
redirectUrl:
|
|
23
|
+
"http://localhost:1717/oauth/callback",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
routes: [
|
|
27
|
+
createSalesforceOntologyRoute({
|
|
28
|
+
ontologyId:
|
|
29
|
+
"salesforce:task-manager",
|
|
30
|
+
ir: generatedOntology,
|
|
31
|
+
}),
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The connection adapter uses `@party-stack/oauth`, persists refresh tokens
|
|
37
|
+
through the selected runtime, and applies authentication through
|
|
38
|
+
`ConnectionEgress`. The Salesforce client and ontology backends remain
|
|
39
|
+
token-provider/egress agnostic.
|
|
40
|
+
|
|
41
|
+
## Pull configuration
|
|
42
|
+
|
|
43
|
+
Consumer projects can export a standard Party Stack pull configuration:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import {
|
|
47
|
+
createSalesforceOntologyPullConfig,
|
|
48
|
+
} from "@party-stack/salesforce-ontology/config";
|
|
49
|
+
|
|
50
|
+
export default createSalesforceOntologyPullConfig({
|
|
51
|
+
instanceUrl,
|
|
52
|
+
apiVersion: "65.0",
|
|
53
|
+
ontologyId: "salesforce:task-manager",
|
|
54
|
+
objectTypeNames: ["Task", "User"],
|
|
55
|
+
actionTypeNames: [],
|
|
56
|
+
connection: {
|
|
57
|
+
oauth: {
|
|
58
|
+
clientId,
|
|
59
|
+
redirectUrl,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Running `ontology pull` opens the Salesforce metadata ontology and emits the
|
|
66
|
+
normal `src/ontology/ontology.ts` consumed by
|
|
67
|
+
`createSalesforceOntologyRoute({ ir })`.
|
|
68
|
+
|
|
69
|
+
See `apps/salesforce-task-manager` for a real-org example.
|
|
70
|
+
# salesforce-ontology
|
|
71
|
+
|
|
72
|
+
Salesforce backend adapters for Party Stack LiveOntology.
|
|
73
|
+
|
|
74
|
+
## Runtime adapter
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { createSalesforceClient } from "@party-stack/salesforce-client";
|
|
78
|
+
import { createSalesforceOntologyBackend } from "@party-stack/salesforce-ontology";
|
|
79
|
+
import { createLiveOntology } from "@party-stack/ontology";
|
|
80
|
+
|
|
81
|
+
// jsforce-backed client; supply your own token (no OAuth ownership here)
|
|
82
|
+
const client = createSalesforceClient({
|
|
83
|
+
instanceUrl: process.env.SALESFORCE_INSTANCE_URL!,
|
|
84
|
+
apiVersion: "61.0",
|
|
85
|
+
tokenProvider: async () => process.env.SALESFORCE_ACCESS_TOKEN!,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const ontology = await createLiveOntology({
|
|
89
|
+
ir,
|
|
90
|
+
backend: createSalesforceOntologyBackend({ client }),
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Metadata adapter
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { createSalesforceMetaOntologyBackendAdapter } from "@party-stack/salesforce-ontology/meta";
|
|
98
|
+
import { createMetaLiveOntology } from "@party-stack/ontology";
|
|
99
|
+
|
|
100
|
+
const meta = await createMetaLiveOntology({
|
|
101
|
+
backend: () =>
|
|
102
|
+
createSalesforceMetaOntologyBackendAdapter({
|
|
103
|
+
client,
|
|
104
|
+
objectTypeNames: ["Account", "Contact"],
|
|
105
|
+
}),
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Scope
|
|
110
|
+
|
|
111
|
+
- sObject describe → object/link metadata
|
|
112
|
+
- Active autolaunched Flows → action metadata and invocation
|
|
113
|
+
- SOQL-backed object collections
|
|
114
|
+
- No CDC live sync, Files attachments, Apex actions, or query functions in this slice
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { LoadSubsetOptions } from "@tanstack/db";
|
|
2
|
+
export type CompiledSoqlFilter = {
|
|
3
|
+
clause?: string;
|
|
4
|
+
alwaysFalse: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function serializeSoqlLiteral(value: unknown): string;
|
|
7
|
+
export declare function isAlwaysFalseFilter(filter: CompiledSoqlFilter | undefined): boolean;
|
|
8
|
+
export declare function convertLoadSubsetFilter(filter: LoadSubsetOptions["where"]): CompiledSoqlFilter | undefined;
|
|
9
|
+
export declare function convertLoadSubsetOrderBy(orderBy: LoadSubsetOptions["orderBy"]): string | undefined;
|
|
10
|
+
export declare function buildSoqlQuery(opts: {
|
|
11
|
+
objectType: string;
|
|
12
|
+
selectedProperties: string[];
|
|
13
|
+
where?: CompiledSoqlFilter;
|
|
14
|
+
orderBy?: string;
|
|
15
|
+
limit?: number;
|
|
16
|
+
offset?: number;
|
|
17
|
+
}): string;
|
|
18
|
+
//# sourceMappingURL=convertLoadSubsetOptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convertLoadSubsetOptions.d.ts","sourceRoot":"","sources":["../../src/adapter/convertLoadSubsetOptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,iBAAiB,EAGpB,MAAM,cAAc,CAAC;AAKtB,MAAM,MAAM,kBAAkB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACxB,CAAC;AAoBF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CA6B3D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,kBAAkB,GAAG,SAAS,GAAG,OAAO,CAEnF;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,kBAAkB,GAAG,SAAS,CA8D1G;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,iBAAiB,CAAC,SAAS,CAAC,GAAG,MAAM,GAAG,SAAS,CAOlG;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAkBT"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { parseOrderByExpression, parseWhereExpression, } from "@tanstack/db";
|
|
2
|
+
import { Temporal } from "temporal-polyfill";
|
|
3
|
+
const ALWAYS_FALSE = "__ALWAYS_FALSE__";
|
|
4
|
+
function quoteIdentifier(identifier) {
|
|
5
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) {
|
|
6
|
+
throw new Error(`Invalid Salesforce identifier "${identifier}".`);
|
|
7
|
+
}
|
|
8
|
+
return identifier;
|
|
9
|
+
}
|
|
10
|
+
function fieldPathToSoql(fieldPath) {
|
|
11
|
+
if (fieldPath.length === 0) {
|
|
12
|
+
throw new Error("Empty field path.");
|
|
13
|
+
}
|
|
14
|
+
return fieldPath.map((segment) => quoteIdentifier(String(segment))).join(".");
|
|
15
|
+
}
|
|
16
|
+
function escapeSoqlString(value) {
|
|
17
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
18
|
+
}
|
|
19
|
+
export function serializeSoqlLiteral(value) {
|
|
20
|
+
if (value === null || value === undefined) {
|
|
21
|
+
return "null";
|
|
22
|
+
}
|
|
23
|
+
if (typeof value === "boolean") {
|
|
24
|
+
return value ? "true" : "false";
|
|
25
|
+
}
|
|
26
|
+
if (typeof value === "number") {
|
|
27
|
+
if (!Number.isFinite(value)) {
|
|
28
|
+
throw new Error(`Cannot serialize non-finite number ${value} to SOQL.`);
|
|
29
|
+
}
|
|
30
|
+
return String(value);
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === "bigint") {
|
|
33
|
+
return String(value);
|
|
34
|
+
}
|
|
35
|
+
if (value instanceof Date) {
|
|
36
|
+
return value.toISOString();
|
|
37
|
+
}
|
|
38
|
+
if (value instanceof Temporal.Instant) {
|
|
39
|
+
return value.toString();
|
|
40
|
+
}
|
|
41
|
+
if (value instanceof Temporal.PlainDate || value instanceof Temporal.PlainDateTime) {
|
|
42
|
+
return value.toString();
|
|
43
|
+
}
|
|
44
|
+
if (typeof value === "string") {
|
|
45
|
+
return `'${escapeSoqlString(value)}'`;
|
|
46
|
+
}
|
|
47
|
+
throw new Error(`Unsupported SOQL literal type: ${typeof value}`);
|
|
48
|
+
}
|
|
49
|
+
export function isAlwaysFalseFilter(filter) {
|
|
50
|
+
return Boolean(filter?.alwaysFalse);
|
|
51
|
+
}
|
|
52
|
+
export function convertLoadSubsetFilter(filter) {
|
|
53
|
+
if (!filter)
|
|
54
|
+
return undefined;
|
|
55
|
+
const compiled = parseWhereExpression(filter, {
|
|
56
|
+
handlers: {
|
|
57
|
+
and: (...filters) => {
|
|
58
|
+
if (filters.includes(ALWAYS_FALSE))
|
|
59
|
+
return ALWAYS_FALSE;
|
|
60
|
+
const clauses = filters.filter((clause) => clause.length > 0);
|
|
61
|
+
if (clauses.length === 0)
|
|
62
|
+
return "";
|
|
63
|
+
if (clauses.length === 1)
|
|
64
|
+
return clauses[0];
|
|
65
|
+
return `(${clauses.join(" AND ")})`;
|
|
66
|
+
},
|
|
67
|
+
or: (...filters) => {
|
|
68
|
+
if (filters.every((clause) => clause === ALWAYS_FALSE))
|
|
69
|
+
return ALWAYS_FALSE;
|
|
70
|
+
const clauses = filters.filter((clause) => clause !== ALWAYS_FALSE && clause.length > 0);
|
|
71
|
+
if (clauses.length === 0)
|
|
72
|
+
return "";
|
|
73
|
+
if (clauses.length === 1)
|
|
74
|
+
return clauses[0];
|
|
75
|
+
return `(${clauses.join(" OR ")})`;
|
|
76
|
+
},
|
|
77
|
+
not: (inner) => {
|
|
78
|
+
if (inner === ALWAYS_FALSE)
|
|
79
|
+
return "";
|
|
80
|
+
if (!inner)
|
|
81
|
+
return ALWAYS_FALSE;
|
|
82
|
+
return `(NOT ${inner})`;
|
|
83
|
+
},
|
|
84
|
+
eq: (field, value) => value == null
|
|
85
|
+
? `${fieldPathToSoql(field)} = null`
|
|
86
|
+
: `${fieldPathToSoql(field)} = ${serializeSoqlLiteral(value)}`,
|
|
87
|
+
gt: (field, value) => value == null ? ALWAYS_FALSE : `${fieldPathToSoql(field)} > ${serializeSoqlLiteral(value)}`,
|
|
88
|
+
gte: (field, value) => value == null ? ALWAYS_FALSE : `${fieldPathToSoql(field)} >= ${serializeSoqlLiteral(value)}`,
|
|
89
|
+
lt: (field, value) => value == null ? ALWAYS_FALSE : `${fieldPathToSoql(field)} < ${serializeSoqlLiteral(value)}`,
|
|
90
|
+
lte: (field, value) => value == null ? ALWAYS_FALSE : `${fieldPathToSoql(field)} <= ${serializeSoqlLiteral(value)}`,
|
|
91
|
+
isNull: (field) => `${fieldPathToSoql(field)} = null`,
|
|
92
|
+
in: (field, values) => {
|
|
93
|
+
const literals = values
|
|
94
|
+
.filter((entry) => entry !== null && entry !== undefined)
|
|
95
|
+
.map(serializeSoqlLiteral);
|
|
96
|
+
if (literals.length === 0)
|
|
97
|
+
return ALWAYS_FALSE;
|
|
98
|
+
return `${fieldPathToSoql(field)} IN (${literals.join(", ")})`;
|
|
99
|
+
},
|
|
100
|
+
like: (field, value) => value === "%"
|
|
101
|
+
? ""
|
|
102
|
+
: `${fieldPathToSoql(field)} LIKE ${serializeSoqlLiteral(value)}`,
|
|
103
|
+
ilike: (field, value) =>
|
|
104
|
+
// Salesforce LIKE is case-insensitive for most text fields.
|
|
105
|
+
value === "%"
|
|
106
|
+
? ""
|
|
107
|
+
: `${fieldPathToSoql(field)} LIKE ${serializeSoqlLiteral(value)}`,
|
|
108
|
+
},
|
|
109
|
+
}) ?? undefined;
|
|
110
|
+
if (compiled === undefined)
|
|
111
|
+
return undefined;
|
|
112
|
+
if (compiled === ALWAYS_FALSE) {
|
|
113
|
+
return { alwaysFalse: true };
|
|
114
|
+
}
|
|
115
|
+
return { clause: compiled || undefined, alwaysFalse: false };
|
|
116
|
+
}
|
|
117
|
+
export function convertLoadSubsetOrderBy(orderBy) {
|
|
118
|
+
if (!orderBy)
|
|
119
|
+
return undefined;
|
|
120
|
+
const fields = parseOrderByExpression(orderBy).map((ordering) => {
|
|
121
|
+
const field = fieldPathToSoql(ordering.field);
|
|
122
|
+
return `${field} ${ordering.direction.toUpperCase()}`;
|
|
123
|
+
});
|
|
124
|
+
return fields.length > 0 ? fields.join(", ") : undefined;
|
|
125
|
+
}
|
|
126
|
+
export function buildSoqlQuery(opts) {
|
|
127
|
+
const objectType = quoteIdentifier(opts.objectType);
|
|
128
|
+
const select = opts.selectedProperties.map(quoteIdentifier).join(", ");
|
|
129
|
+
const clauses = [`SELECT ${select} FROM ${objectType}`];
|
|
130
|
+
if (opts.where?.clause) {
|
|
131
|
+
clauses.push(`WHERE ${opts.where.clause}`);
|
|
132
|
+
}
|
|
133
|
+
if (opts.orderBy) {
|
|
134
|
+
clauses.push(`ORDER BY ${opts.orderBy}`);
|
|
135
|
+
}
|
|
136
|
+
if (opts.limit !== undefined) {
|
|
137
|
+
clauses.push(`LIMIT ${Math.max(0, Math.trunc(opts.limit))}`);
|
|
138
|
+
}
|
|
139
|
+
if (opts.offset !== undefined && opts.offset > 0) {
|
|
140
|
+
clauses.push(`OFFSET ${Math.max(0, Math.trunc(opts.offset))}`);
|
|
141
|
+
}
|
|
142
|
+
return clauses.join(" ");
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=convertLoadSubsetOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convertLoadSubsetOptions.js","sourceRoot":"","sources":["../../src/adapter/convertLoadSubsetOptions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convertLoadSubsetOptions.test.d.ts","sourceRoot":"","sources":["../../src/adapter/convertLoadSubsetOptions.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { eq, gt, IR, lt } from "@tanstack/db";
|
|
2
|
+
import { Temporal } from "temporal-polyfill";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { buildSoqlQuery, convertLoadSubsetFilter, isAlwaysFalseFilter, serializeSoqlLiteral, } from "./convertLoadSubsetOptions.js";
|
|
5
|
+
describe("serializeSoqlLiteral", () => {
|
|
6
|
+
it("escapes strings and serializes temporal values", () => {
|
|
7
|
+
expect(serializeSoqlLiteral("O'Brien")).toBe("'O\\'Brien'");
|
|
8
|
+
expect(serializeSoqlLiteral(true)).toBe("true");
|
|
9
|
+
expect(serializeSoqlLiteral(Temporal.PlainDate.from("2026-08-06"))).toBe("2026-08-06");
|
|
10
|
+
expect(serializeSoqlLiteral(Temporal.Instant.from("2026-08-06T12:00:00Z"))).toBe("2026-08-06T12:00:00Z");
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
describe("convertLoadSubsetFilter", () => {
|
|
14
|
+
it("converts equality and null checks", () => {
|
|
15
|
+
expect(convertLoadSubsetFilter(eq(new IR.PropRef(["Name"]), "Acme"))).toEqual({
|
|
16
|
+
clause: "Name = 'Acme'",
|
|
17
|
+
alwaysFalse: false,
|
|
18
|
+
});
|
|
19
|
+
expect(convertLoadSubsetFilter(eq(new IR.PropRef(["DeletedDate"]), null))).toEqual({
|
|
20
|
+
clause: "DeletedDate = null",
|
|
21
|
+
alwaysFalse: false,
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
it("treats null range comparisons as always false", () => {
|
|
25
|
+
const filter = convertLoadSubsetFilter(lt(new IR.PropRef(["Amount"]), null));
|
|
26
|
+
expect(isAlwaysFalseFilter(filter)).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
it("serializes Temporal values", () => {
|
|
29
|
+
const filter = convertLoadSubsetFilter(gt(new IR.PropRef(["CreatedDate"]), Temporal.Instant.from("2026-07-27T12:00:00Z")));
|
|
30
|
+
expect(filter).toEqual({
|
|
31
|
+
clause: "CreatedDate > 2026-07-27T12:00:00Z",
|
|
32
|
+
alwaysFalse: false,
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe("buildSoqlQuery", () => {
|
|
37
|
+
it("builds SELECT/FROM/WHERE/ORDER BY/LIMIT/OFFSET queries", () => {
|
|
38
|
+
expect(buildSoqlQuery({
|
|
39
|
+
objectType: "Account",
|
|
40
|
+
selectedProperties: ["Id", "Name"],
|
|
41
|
+
where: { clause: "Name = 'Acme'", alwaysFalse: false },
|
|
42
|
+
orderBy: "Name ASC",
|
|
43
|
+
limit: 50,
|
|
44
|
+
offset: 10,
|
|
45
|
+
})).toBe("SELECT Id, Name FROM Account WHERE Name = 'Acme' ORDER BY Name ASC LIMIT 50 OFFSET 10");
|
|
46
|
+
});
|
|
47
|
+
it("rejects unsafe identifiers", () => {
|
|
48
|
+
expect(() => buildSoqlQuery({
|
|
49
|
+
objectType: "Account; DROP TABLE",
|
|
50
|
+
selectedProperties: ["Id"],
|
|
51
|
+
})).toThrow(/Invalid Salesforce identifier/);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=convertLoadSubsetOptions.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convertLoadSubsetOptions.test.js","sourceRoot":"","sources":["../../src/adapter/convertLoadSubsetOptions.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type OntologyBackendAdapter, type OntologyBackendAdapterProvider, type OntologyIR } from "@party-stack/ontology";
|
|
2
|
+
import { type SalesforceClient } from "@party-stack/salesforce-client";
|
|
3
|
+
export declare function createSalesforceOntologyBackendAdapter(opts: {
|
|
4
|
+
client: SalesforceClient;
|
|
5
|
+
ir: OntologyIR;
|
|
6
|
+
}): OntologyBackendAdapter;
|
|
7
|
+
export type CreateSalesforceOntologyBackendOptions<Context extends Record<string, unknown> = Record<string, unknown>> = {
|
|
8
|
+
client: SalesforceClient;
|
|
9
|
+
} | {
|
|
10
|
+
createClient: (ir: OntologyIR, context: Context) => SalesforceClient | Promise<SalesforceClient>;
|
|
11
|
+
};
|
|
12
|
+
export declare function createSalesforceOntologyBackend<Context extends Record<string, unknown> = Record<string, unknown>>(opts: CreateSalesforceOntologyBackendOptions<Context>): OntologyBackendAdapterProvider<Context>;
|
|
13
|
+
//# sourceMappingURL=createSalesforceOntologyBackendAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createSalesforceOntologyBackendAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/createSalesforceOntologyBackendAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,KAAK,sBAAsB,EAC3B,KAAK,8BAA8B,EACnC,KAAK,UAAU,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAqC3F,wBAAgB,sCAAsC,CAAC,IAAI,EAAE;IACzD,MAAM,EAAE,gBAAgB,CAAC;IACzB,EAAE,EAAE,UAAU,CAAC;CAClB,GAAG,sBAAsB,CAqEzB;AAED,MAAM,MAAM,sCAAsC,CAC9C,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAE/D;IACI,MAAM,EAAE,gBAAgB,CAAC;CAC5B,GACD;IACI,YAAY,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,KAAK,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACpG,CAAC;AAER,wBAAgB,+BAA+B,CAC3C,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnE,IAAI,EAAE,sCAAsC,CAAC,OAAO,CAAC,GAAG,8BAA8B,CAAC,OAAO,CAAC,CAMhG"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { NonRetryableError, } from "@party-stack/ontology";
|
|
2
|
+
import { SalesforceApiError } from "@party-stack/salesforce-client";
|
|
3
|
+
import { getSalesforceFlowApiNameFromActionType } from "../meta/convertMetaActionType.js";
|
|
4
|
+
import { objectCollectionOptions } from "./objectCollectionOptions.js";
|
|
5
|
+
import { createSalesforceCodec } from "./salesforceCodec.js";
|
|
6
|
+
function isNonRetryableSalesforceError(error) {
|
|
7
|
+
if (!(error instanceof SalesforceApiError)) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
if (error.statusCode === 400 || error.statusCode === 403 || error.statusCode === 404) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
const code = error.errorCode?.toUpperCase();
|
|
14
|
+
return (code === "INVALID_INPUT" ||
|
|
15
|
+
code === "REQUIRED_FIELD_MISSING" ||
|
|
16
|
+
code === "FIELD_CUSTOM_VALIDATION_EXCEPTION" ||
|
|
17
|
+
code === "INSUFFICIENT_ACCESS" ||
|
|
18
|
+
code === "INSUFFICIENT_ACCESS_OR_READONLY" ||
|
|
19
|
+
code === "NOT_FOUND");
|
|
20
|
+
}
|
|
21
|
+
function invalidateEditedCollections(objects) {
|
|
22
|
+
for (const collection of Object.values(objects)) {
|
|
23
|
+
const utils = collection.utils;
|
|
24
|
+
utils?.invalidate?.();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function createSalesforceOntologyBackendAdapter(opts) {
|
|
28
|
+
const codec = createSalesforceCodec(opts.ir);
|
|
29
|
+
return {
|
|
30
|
+
name: "salesforce",
|
|
31
|
+
getCollectionOptions: (objectType) => {
|
|
32
|
+
const objectTypeDef = opts.ir.objectTypes.find((candidate) => candidate.name === objectType);
|
|
33
|
+
if (!objectTypeDef) {
|
|
34
|
+
throw new Error(`Unknown Salesforce object type "${objectType}".`);
|
|
35
|
+
}
|
|
36
|
+
return objectCollectionOptions({
|
|
37
|
+
client: opts.client,
|
|
38
|
+
objectType,
|
|
39
|
+
primaryKeyProperty: objectTypeDef.primaryKey,
|
|
40
|
+
selectedProperties: objectTypeDef.properties.map((property) => property.name),
|
|
41
|
+
decodeObject: (object) => codec.decodeObject(objectType, object),
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
applyAction: async (name, parameters, live) => {
|
|
45
|
+
const actionType = opts.ir.actionTypes.find((candidate) => candidate.name === name);
|
|
46
|
+
if (!actionType) {
|
|
47
|
+
throw new NonRetryableError(`Unknown Salesforce action type "${name}".`);
|
|
48
|
+
}
|
|
49
|
+
const parameterTypes = new Map(actionType.parameters.map((parameter) => [parameter.name, parameter.type]));
|
|
50
|
+
const requestParameters = {};
|
|
51
|
+
for (const [parameterName, value] of Object.entries(parameters)) {
|
|
52
|
+
if (value === undefined)
|
|
53
|
+
continue;
|
|
54
|
+
const parameterType = parameterTypes.get(parameterName);
|
|
55
|
+
requestParameters[parameterName] = parameterType
|
|
56
|
+
? codec.encodeValue(parameterType, value)
|
|
57
|
+
: value;
|
|
58
|
+
}
|
|
59
|
+
const flowApiName = getSalesforceFlowApiNameFromActionType(actionType);
|
|
60
|
+
let results;
|
|
61
|
+
try {
|
|
62
|
+
results = await opts.client.invokeFlowAction(flowApiName, [requestParameters]);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (isNonRetryableSalesforceError(error)) {
|
|
66
|
+
throw new NonRetryableError(error instanceof Error ? error.message : "Salesforce Flow invocation failed.", { cause: error });
|
|
67
|
+
}
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
const result = results[0];
|
|
71
|
+
if (!result) {
|
|
72
|
+
throw new Error(`Salesforce Flow "${flowApiName}" returned no results.`);
|
|
73
|
+
}
|
|
74
|
+
if (!result.isSuccess) {
|
|
75
|
+
const message = result.errors?.map((entry) => entry.message).filter(Boolean).join("; ") ||
|
|
76
|
+
`Salesforce Flow "${flowApiName}" failed.`;
|
|
77
|
+
throw new NonRetryableError(message);
|
|
78
|
+
}
|
|
79
|
+
invalidateEditedCollections(live.objects);
|
|
80
|
+
},
|
|
81
|
+
runQueryFunction: (name) => Promise.reject(new Error(`Salesforce ontology adapter cannot run query function type "${name}". Query functions are not supported in this slice.`)),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export function createSalesforceOntologyBackend(opts) {
|
|
85
|
+
return async (ir, context) => createSalesforceOntologyBackendAdapter({
|
|
86
|
+
ir,
|
|
87
|
+
client: "client" in opts ? opts.client : await opts.createClient(ir, context),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=createSalesforceOntologyBackendAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createSalesforceOntologyBackendAdapter.js","sourceRoot":"","sources":["../../src/adapter/createSalesforceOntologyBackendAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createSalesforceOntologyBackendAdapter.test.d.ts","sourceRoot":"","sources":["../../src/adapter/createSalesforceOntologyBackendAdapter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { NonRetryableError, o } from "@party-stack/ontology";
|
|
2
|
+
import { SalesforceApiError } from "@party-stack/salesforce-client";
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { createSalesforceOntologyBackend, createSalesforceOntologyBackendAdapter, } from "./createSalesforceOntologyBackendAdapter.js";
|
|
5
|
+
describe("createSalesforceOntologyBackendAdapter", () => {
|
|
6
|
+
const ir = {
|
|
7
|
+
types: [],
|
|
8
|
+
objectTypes: [
|
|
9
|
+
{
|
|
10
|
+
name: "Account",
|
|
11
|
+
displayName: "Account",
|
|
12
|
+
pluralDisplayName: "Accounts",
|
|
13
|
+
primaryKey: "Id",
|
|
14
|
+
properties: [
|
|
15
|
+
{ name: "Id", displayName: "Id", type: o.string({}) },
|
|
16
|
+
{ name: "Name", displayName: "Name", type: o.string({}) },
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
linkTypes: [],
|
|
21
|
+
actionTypes: [
|
|
22
|
+
{
|
|
23
|
+
name: "Create_Account",
|
|
24
|
+
displayName: "Create Account",
|
|
25
|
+
parameters: [
|
|
26
|
+
{
|
|
27
|
+
name: "accountName",
|
|
28
|
+
displayName: "Account Name",
|
|
29
|
+
type: o.string({}),
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
logic: [],
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
queryFunctionTypes: [],
|
|
36
|
+
};
|
|
37
|
+
// Preserve the Flow API name via the meta id convention at runtime.
|
|
38
|
+
ir.actionTypes[0].id = "salesforce:flow:Create_Account";
|
|
39
|
+
const invokeFlowAction = vi.fn();
|
|
40
|
+
const query = vi.fn();
|
|
41
|
+
const queryMore = vi.fn();
|
|
42
|
+
const invalidate = vi.fn();
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
vi.clearAllMocks();
|
|
45
|
+
});
|
|
46
|
+
function createAdapter() {
|
|
47
|
+
return createSalesforceOntologyBackendAdapter({
|
|
48
|
+
ir,
|
|
49
|
+
client: {
|
|
50
|
+
instanceUrl: "https://example.my.salesforce.com",
|
|
51
|
+
apiVersion: "61.0",
|
|
52
|
+
tokenProvider: () => "token",
|
|
53
|
+
fetch: vi.fn(),
|
|
54
|
+
connection: {},
|
|
55
|
+
request: vi.fn(),
|
|
56
|
+
describeGlobal: vi.fn(),
|
|
57
|
+
describeSObject: vi.fn(),
|
|
58
|
+
query,
|
|
59
|
+
queryMore,
|
|
60
|
+
createRecord: vi.fn(),
|
|
61
|
+
updateRecord: vi.fn(),
|
|
62
|
+
deleteRecord: vi.fn(),
|
|
63
|
+
subscribeToChangeEvents: vi.fn(),
|
|
64
|
+
listFlowActions: vi.fn(),
|
|
65
|
+
describeFlowAction: vi.fn(),
|
|
66
|
+
invokeFlowAction,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
it("invokes Flows by API name and invalidates collections on success", async () => {
|
|
71
|
+
invokeFlowAction.mockResolvedValue([
|
|
72
|
+
{
|
|
73
|
+
isSuccess: true,
|
|
74
|
+
outputValues: { Flow__InterviewStatus: "Finished" },
|
|
75
|
+
},
|
|
76
|
+
]);
|
|
77
|
+
const adapter = createAdapter();
|
|
78
|
+
await adapter.applyAction("Create_Account", { accountName: "Acme" }, {
|
|
79
|
+
objects: {
|
|
80
|
+
Account: {
|
|
81
|
+
utils: { invalidate },
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
expect(invokeFlowAction).toHaveBeenCalledWith("Create_Account", [
|
|
86
|
+
{ accountName: "Acme" },
|
|
87
|
+
]);
|
|
88
|
+
expect(invalidate).toHaveBeenCalledOnce();
|
|
89
|
+
});
|
|
90
|
+
it("maps validation failures to NonRetryableError", async () => {
|
|
91
|
+
invokeFlowAction.mockRejectedValue(new SalesforceApiError("Required fields are missing", {
|
|
92
|
+
statusCode: 400,
|
|
93
|
+
errorCode: "REQUIRED_FIELD_MISSING",
|
|
94
|
+
}));
|
|
95
|
+
const adapter = createAdapter();
|
|
96
|
+
await expect(adapter.applyAction("Create_Account", {}, { objects: {} })).rejects.toBeInstanceOf(NonRetryableError);
|
|
97
|
+
});
|
|
98
|
+
it("maps unsuccessful Flow results to NonRetryableError", async () => {
|
|
99
|
+
invokeFlowAction.mockResolvedValue([
|
|
100
|
+
{
|
|
101
|
+
isSuccess: false,
|
|
102
|
+
errors: [{ message: "Flow failed validation" }],
|
|
103
|
+
},
|
|
104
|
+
]);
|
|
105
|
+
const adapter = createAdapter();
|
|
106
|
+
await expect(adapter.applyAction("Create_Account", { accountName: "Acme" }, { objects: {} })).rejects.toEqual(expect.objectContaining({
|
|
107
|
+
name: "NonRetryableError",
|
|
108
|
+
message: "Flow failed validation",
|
|
109
|
+
}));
|
|
110
|
+
});
|
|
111
|
+
it("rejects query functions", async () => {
|
|
112
|
+
const adapter = createAdapter();
|
|
113
|
+
await expect(adapter.runQueryFunction("currentUser", {}, { objects: {} })).rejects.toThrow(/cannot run query function/);
|
|
114
|
+
});
|
|
115
|
+
it("creates a provider that builds adapters from a client factory", async () => {
|
|
116
|
+
const provider = createSalesforceOntologyBackend({
|
|
117
|
+
createClient: () => ({
|
|
118
|
+
instanceUrl: "https://example.my.salesforce.com",
|
|
119
|
+
apiVersion: "61.0",
|
|
120
|
+
tokenProvider: () => "token",
|
|
121
|
+
fetch: vi.fn(),
|
|
122
|
+
connection: {},
|
|
123
|
+
request: vi.fn(),
|
|
124
|
+
describeGlobal: vi.fn(),
|
|
125
|
+
describeSObject: vi.fn(),
|
|
126
|
+
query,
|
|
127
|
+
queryMore,
|
|
128
|
+
createRecord: vi.fn(),
|
|
129
|
+
updateRecord: vi.fn(),
|
|
130
|
+
deleteRecord: vi.fn(),
|
|
131
|
+
subscribeToChangeEvents: vi.fn(),
|
|
132
|
+
listFlowActions: vi.fn(),
|
|
133
|
+
describeFlowAction: vi.fn(),
|
|
134
|
+
invokeFlowAction,
|
|
135
|
+
}),
|
|
136
|
+
});
|
|
137
|
+
const adapter = await provider(ir, {});
|
|
138
|
+
expect(adapter.name).toBe("salesforce");
|
|
139
|
+
expect(adapter.getCollectionOptions("Account").syncMode).toBe("on-demand");
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
//# sourceMappingURL=createSalesforceOntologyBackendAdapter.test.js.map
|