From 317381114a642594ef6b4107c90cda065d96f0f5 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 12 Nov 2024 20:17:40 +0300 Subject: [PATCH 01/20] allow unkown tags eg @state --- tools/parse-jsdoc/jsdoc/jsdoc.conf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/parse-jsdoc/jsdoc/jsdoc.conf.json b/tools/parse-jsdoc/jsdoc/jsdoc.conf.json index 27679f88f..9c1925354 100644 --- a/tools/parse-jsdoc/jsdoc/jsdoc.conf.json +++ b/tools/parse-jsdoc/jsdoc/jsdoc.conf.json @@ -1,6 +1,6 @@ { "tags": { - "allowUnknownTags": false + "allowUnknownTags": true }, "plugins": ["./lookup-tags.cjs"] } From 535379439327f7626bd2e75cefcbe8c2a93d19e3 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 12 Nov 2024 20:30:59 +0300 Subject: [PATCH 02/20] update examples --- packages/salesforce/ast.json | 80 ++++++++++++++++++++++++++---- packages/salesforce/src/Adaptor.js | 67 ++++++++++++++++++------- 2 files changed, 120 insertions(+), 27 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index 152355eca..f6726e464 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -26,6 +26,11 @@ "description": "bulk(\n \"vera__Beneficiary__c\",\n \"upsert\",\n [\n {\n vera__Reporting_Period__c: 2023,\n vera__Geographic_Area__c: \"Uganda\",\n \"vera__Indicator__r.vera__ExtId__c\": 1001,\n vera__Result_UID__c: \"1001_2023_Uganda\",\n },\n ],\n { extIdField: \"vera__Result_UID__c\" }\n);", "caption": "Bulk upsert" }, + { + "title": "example", + "description": "fn((state) => {\n state.accounts = state.data.map((a) => ({ Id: a.id, Name: a.name }));\n return state;\n});\nbulk(\"Account\", \"update\", { failOnError: true }, $.accounts);", + "caption": "Bulk update Account records using a lazy state reference" + }, { "title": "function", "description": null, @@ -131,6 +136,10 @@ "name": "options.pollTimeout", "default": "240000" }, + { + "title": "state", + "description": "{SalesforceState}" + }, { "title": "returns", "description": null, @@ -159,12 +168,18 @@ }, { "title": "example", - "description": "bulkQuery(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.field1}'`);", - "caption": "The results will be available on `state.data`" + "description": "bulkQuery(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.healthId}'`);", + "caption": "Bulk query patient records where `Health_ID__c` is equal to the value in `state.data.healthId`" + }, + { + "title": "example", + "description": "bulkQuery(`SELECT Id FROM Patient__c WHERE Health_ID__c = '${$.data.healthId}'`);", + "caption": "Bulk query patient records using a lazy state reference" }, { "title": "example", - "description": "bulkQuery(\n (state) =>\n `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.field1}'`,\n { pollTimeout: 10000, pollInterval: 6000 }\n);" + "description": "bulkQuery(\n (state) =>\n `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.field1}'`,\n { pollTimeout: 10000, pollInterval: 6000 }\n);", + "caption": "Bulk query with custom polling options" }, { "title": "function", @@ -215,6 +230,10 @@ "name": "options.pollInterval", "default": "3000" }, + { + "title": "state", + "description": "{SalesforceState}" + }, { "title": "returns", "description": null, @@ -274,6 +293,10 @@ }, "name": "records" }, + { + "title": "state", + "description": "{SalesforceState}" + }, { "title": "returns", "description": null, @@ -326,6 +349,10 @@ }, "name": "sObjectName" }, + { + "title": "state", + "description": "{SalesforceState}" + }, { "title": "returns", "description": null, @@ -355,7 +382,8 @@ }, { "title": "example", - "description": "destroy('obj_name', [\n '0060n00000JQWHYAA5',\n '0090n00000JQEWHYAA5'\n], { failOnError: true })" + "description": "destroy(\"Account\", [\"001XXXXXXXXXXXXXXX\", \"001YYYYYYYYYYYYYYY\"], {\n failOnError: true,\n});", + "caption": "Delete multiple Account records" }, { "title": "function", @@ -389,6 +417,10 @@ }, "name": "options" }, + { + "title": "state", + "description": "{SalesforceState}" + }, { "title": "returns", "description": null, @@ -448,6 +480,10 @@ }, "name": "records" }, + { + "title": "state", + "description": "{SalesforceState}" + }, { "title": "returns", "description": null, @@ -477,12 +513,18 @@ }, { "title": "example", - "description": "query(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.field1}'`);" + "description": "query('SELECT Id FROM Patient__c', { autoFetch: true });", + "caption": "Query more records if next records are available" }, { "title": "example", - "description": "query(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.field1}'`, { autoFetch: true });", - "caption": "Query more records if next records are available" + "description": "query(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.healthId}'`);", + "caption": "Query patients by Health ID" + }, + { + "title": "example", + "description": "query(`SELECT Id FROM Patient__c WHERE Health_ID__c = '${$.data.healthId}'`);", + "caption": "Query patients by Health ID using a lazy state reference" }, { "title": "function", @@ -522,13 +564,17 @@ }, { "title": "param", - "description": "A callback to execute once the record is retrieved", + "description": "A callback to execute once the record is retrieved\n<<<<<<< HEAD\n=======", "type": { "type": "NameExpression", "name": "function" }, "name": "callback" }, + { + "title": "state", + "description": "{SalesforceState}\n>>>>>>> 1ece1ff1 (update examples)" + }, { "title": "returns", "description": null, @@ -628,6 +674,10 @@ "title": "magic", "description": "records - $.children[?(@.name==\"{{args.sObject}}\")].children[?(!@.meta.externalId)]" }, + { + "title": "state", + "description": "{SalesforceState}" + }, { "title": "returns", "description": null, @@ -705,6 +755,10 @@ }, "name": "records" }, + { + "title": "state", + "description": "{SalesforceState}" + }, { "title": "returns", "description": null, @@ -732,7 +786,8 @@ }, { "title": "example", - "description": "fn((state) => {\n const s = toUTF8(\"άνθρωποι\");\n console.log(s); // anthropoi\n return state;\n});" + "description": "fn((state) => {\n const s = toUTF8(\"άνθρωποι\");\n console.log(s); // anthropoi\n return state;\n});", + "caption": "Transliterate `άνθρωποι` to `anthropoi`" }, { "title": "param", @@ -771,7 +826,8 @@ }, { "title": "example", - "description": "retrieve('ContentVersion', '0684K0000020Au7QAE/VersionData');" + "description": "retrieve('ContentVersion', '0684K0000020Au7QAE/VersionData');", + "caption": "Retrieve a specific ContentVersion record" }, { "title": "function", @@ -796,6 +852,10 @@ }, "name": "id" }, + { + "title": "state", + "description": "{SalesforceState}" + }, { "title": "returns", "description": null, diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 9d96cbf63..1e69d2962 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -11,6 +11,13 @@ * @ignore */ +/** + * State object + * @typedef {Object} SalesforceState + * @property data - Operation results + * @property references - History of all previous operations results + **/ + import { execute as commonExecute, composeNextState, @@ -80,6 +87,12 @@ export function execute(...operations) { * ], * { extIdField: "vera__Result_UID__c" } * ); + * @example Bulk update Account records using a lazy state reference + * fn((state) => { + * state.accounts = state.data.map((a) => ({ Id: a.id, Name: a.name })); + * return state; + * }); + * bulk("Account", "update", { failOnError: true }, $.accounts); * @function * @param {string} sObjectName - API name of the sObject. * @param {string} operation - The bulk operation to be performed.Eg "insert" | "update" | "upsert" @@ -90,6 +103,7 @@ export function execute(...operations) { * @param {boolean} [options.failOnError=false] - Fail the operation on error. * @param {integer} [options.pollInterval=6000] - Polling interval in milliseconds. * @param {integer} [options.pollTimeout=240000] - Polling timeout in milliseconds. + * @state {SalesforceState} * @returns {Operation} */ export function bulk(sObjectName, operation, records, options = {}) { @@ -192,10 +206,11 @@ export function bulk(sObjectName, operation, records, options = {}) { * `bulkQuery()` uses {@link https://sforce.co/4azgczz Bulk API v.2.0 Query} which is available in API version 47.0 and later. * This API is subject to {@link https://sforce.co/4b6kn6z rate limits}. * @public - * @example - * The results will be available on `state.data` - * bulkQuery(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.field1}'`); - * @example + * @example Bulk query patient records where `Health_ID__c` is equal to the value in `state.data.healthId` + * bulkQuery(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.healthId}'`); + * @example Bulk query patient records using a lazy state reference + * bulkQuery(`SELECT Id FROM Patient__c WHERE Health_ID__c = '${$.data.healthId}'`); + * @example Bulk query with custom polling options * bulkQuery( * (state) => * `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.field1}'`, @@ -206,6 +221,7 @@ export function bulk(sObjectName, operation, records, options = {}) { * @param {object} options - Options passed to the bulk api. * @param {integer} [options.pollTimeout=90000] - Polling timeout in milliseconds. * @param {integer} [options.pollInterval=3000] - Polling interval in milliseconds. + * @state {SalesforceState} * @returns {Operation} */ export function bulkQuery(qs, options = {}) { @@ -253,6 +269,7 @@ export function bulkQuery(qs, options = {}) { * @function * @param {string} sObjectName - API name of the sObject. * @param {object} records - Field attributes for the new record. + * @state {SalesforceState} * @returns {Operation} */ export function create(sObjectName, records) { @@ -284,6 +301,7 @@ export function create(sObjectName, records) { * describe('Account') * @function * @param {string} [sObjectName] - The API name of the sObject. If omitted, fetches metadata for all sObjects. + * @state {SalesforceState} * @returns {Operation} */ export function describe(sObjectName) { @@ -313,15 +331,15 @@ export function describe(sObjectName) { /** * Delete records of an object. * @public - * @example - * destroy('obj_name', [ - * '0060n00000JQWHYAA5', - * '0090n00000JQEWHYAA5' - * ], { failOnError: true }) + * @example Delete multiple Account records + * destroy("Account", ["001XXXXXXXXXXXXXXX", "001YYYYYYYYYYYYYYY"], { + * failOnError: true, + * }); * @function * @param {string} sObjectName - API name of the sObject. * @param {object} ids - Array of IDs of records to delete. * @param {object} options - Options for the destroy delete operation. + * @state {SalesforceState} * @returns {Operation} */ export function destroy(sObjectName, ids, options = {}) { @@ -360,10 +378,13 @@ export function destroy(sObjectName, ids, options = {}) { /** * Send a GET HTTP request using connected session information. - * @example + * @example Make a GET request to a custom Salesforce flow * get('/actions/custom/flow/POC_OpenFN_Test_Flow'); * @param {string} path - The Salesforce API endpoint, Relative to request from - * @param {object} options - Request query parameters and headers + * @param {object} options - Request options + * @param {object} [options.headers] - Object of request headers + * @param {object} [options.query] - A JSON Object request body + * @state {SalesforceState} * @returns {Operation} */ export function get(path, options = {}) { @@ -398,6 +419,7 @@ export function get(path, options = {}) { * @function * @param {string} sObjectName - API name of the sObject. * @param {object} records - Field attributes for the new record. + * @state {SalesforceState} * @returns {Operation} */ export function insert(sObjectName, records) { @@ -407,13 +429,14 @@ export function insert(sObjectName, records) { /** * Send a POST HTTP request using connected session information. * - * @example + * @example Make a POST request to a custom Salesforce flow * post('/actions/custom/flow/POC_OpenFN_Test_Flow', { inputs: [{}] }); * @param {string} path - The Salesforce API endpoint, Relative to request from * @param {object} data - A JSON Object request body * @param {object} options - Request options * @param {object} [options.headers] - Object of request headers * @param {object} [options.query] - A JSON Object request body + * @state {SalesforceState} * @returns {Operation} */ export function post(path, data, options = {}) { @@ -450,15 +473,21 @@ export function post(path, data, options = {}) { * * The Salesforce query API is subject to rate limits, {@link https://sforce.co/3W9zyaQ See for more details}. * @public - * @example - * query(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.field1}'`); * @example Query more records if next records are available - * query(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.field1}'`, { autoFetch: true }); + * query('SELECT Id FROM Patient__c', { autoFetch: true }); + * @example Query patients by Health ID + * query(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.healthId}'`); + * @example Query patients by Health ID using a lazy state reference + * query(`SELECT Id FROM Patient__c WHERE Health_ID__c = '${$.data.healthId}'`); * @function * @param {string} qs - A query string. Must be less than `4000` characters in WHERE clause * @param {object} options - Options passed to the bulk api. * @param {boolean} [options.autoFetch=false] - Fetch next records if available. * @param {function} callback - A callback to execute once the record is retrieved +<<<<<<< HEAD +======= + * @state {SalesforceState} +>>>>>>> 1ece1ff1 (update examples) * @returns {Operation} */ export function query(qs, options = {}, callback = s => s) { @@ -548,6 +577,7 @@ export function query(qs, options = {}, callback = s => s) { * @magic externalId - $.children[?(@.name=="{{args.sObject}}")].children[?(@.meta.externalId)].name * @param {(object|object[])} records - Field attributes for the new object. * @magic records - $.children[?(@.name=="{{args.sObject}}")].children[?(!@.meta.externalId)] + * @state {SalesforceState} * @returns {Operation} */ export function upsert(sObjectName, externalId, records) { @@ -588,6 +618,7 @@ export function upsert(sObjectName, externalId, records) { * @function * @param {string} sObjectName - API name of the sObject. * @param {(object|object[])} records - Field attributes for the new object. + * @state {SalesforceState} * @returns {Operation} */ export function update(sObjectName, records) { @@ -612,7 +643,7 @@ export function update(sObjectName, records) { /** * Transliterates unicode characters to their best ASCII representation * @public - * @example + * @example Transliterate `άνθρωποι` to `anthropoi` * fn((state) => { * const s = toUTF8("άνθρωποι"); * console.log(s); // anthropoi @@ -639,6 +670,7 @@ export function toUTF8(input) { * @param {object} [options.headers] - Object of request headers * @param {object} [options.json] - A JSON object to send as the request body. * @param {string} [options.body] - HTTP body (in POST/PUT/PATCH methods) + * @state {SalesforceState} * @returns {Operation} */ export function request(path, options = {}) { @@ -669,11 +701,12 @@ export function request(path, options = {}) { /** * Retrieves a Salesforce sObject(s). * @public - * @example + * @example Retrieve a specific ContentVersion record * retrieve('ContentVersion', '0684K0000020Au7QAE/VersionData'); * @function * @param {string} sObjectName - The sObject to retrieve * @param {string} id - The id of the record + * @state {SalesforceState} * @returns {Operation} */ export function retrieve(sObjectName, id) { From 3a86bd769906c8e0fccb3c1486f5ce81d8e83a1e Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 12 Nov 2024 20:38:07 +0300 Subject: [PATCH 03/20] fix merge conflicts --- packages/salesforce/ast.json | 43 ++++++++++++++++++++---------- packages/salesforce/src/Adaptor.js | 24 ++++++++--------- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index f6726e464..bfc597ce2 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -504,7 +504,7 @@ "callback" ], "docs": { - "description": "Execute an SOQL query.\nNote that in an event of a query error,\nerror logs will be printed but the operation will not throw the error.\n\nThe Salesforce query API is subject to rate limits, {@link https://sforce.co/3W9zyaQ See for more details}.", + "description": "Executes an SOQL (Salesforce Object Query Language) query to retrieve records from Salesforce.\nThis operation allows querying Salesforce objects using SOQL syntax and handles pagination.\nNote that in an event of a query error, error logs will be printed but the operation will not throw the error.\n\nThe Salesforce query API is subject to rate limits, {@link https://sforce.co/3W9zyaQ See for more details}.", "tags": [ { "title": "public", @@ -518,7 +518,7 @@ }, { "title": "example", - "description": "query(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.healthId}'`);", + "description": "query(state => `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.healthId}'`);", "caption": "Query patients by Health ID" }, { @@ -533,25 +533,37 @@ }, { "title": "param", - "description": "A query string. Must be less than `4000` characters in WHERE clause", + "description": "A SOQL query string or a function that returns a query string. Must be less than 4000 characters in WHERE clause", "type": { - "type": "NameExpression", - "name": "string" + "type": "UnionType", + "elements": [ + { + "type": "NameExpression", + "name": "string" + }, + { + "type": "NameExpression", + "name": "function" + } + ] }, "name": "qs" }, { "title": "param", - "description": "Options passed to the bulk api.", + "description": "Optional configuration for the query operation", "type": { - "type": "NameExpression", - "name": "object" + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "object" + } }, "name": "options" }, { "title": "param", - "description": "Fetch next records if available.", + "description": "When true, automatically fetches next batch of records if available", "type": { "type": "OptionalType", "expression": { @@ -564,20 +576,23 @@ }, { "title": "param", - "description": "A callback to execute once the record is retrieved\n<<<<<<< HEAD\n=======", + "description": "Optional callback function to execute for each retrieved record", "type": { - "type": "NameExpression", - "name": "function" + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "function" + } }, "name": "callback" }, { "title": "state", - "description": "{SalesforceState}\n>>>>>>> 1ece1ff1 (update examples)" + "description": "{SalesforceState} - The Salesforce connection state" }, { "title": "returns", - "description": null, + "description": "Returns an Operation object containing the query results and metadata", "type": { "type": "NameExpression", "name": "Operation" diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 1e69d2962..586d74327 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -467,28 +467,26 @@ export function post(path, data, options = {}) { } /** - * Execute an SOQL query. - * Note that in an event of a query error, - * error logs will be printed but the operation will not throw the error. + * Executes an SOQL (Salesforce Object Query Language) query to retrieve records from Salesforce. + * This operation allows querying Salesforce objects using SOQL syntax and handles pagination. + * Note that in an event of a query error, error logs will be printed but the operation will not throw the error. * * The Salesforce query API is subject to rate limits, {@link https://sforce.co/3W9zyaQ See for more details}. + * * @public * @example Query more records if next records are available * query('SELECT Id FROM Patient__c', { autoFetch: true }); * @example Query patients by Health ID - * query(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.healthId}'`); + * query(state => `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.healthId}'`); * @example Query patients by Health ID using a lazy state reference * query(`SELECT Id FROM Patient__c WHERE Health_ID__c = '${$.data.healthId}'`); * @function - * @param {string} qs - A query string. Must be less than `4000` characters in WHERE clause - * @param {object} options - Options passed to the bulk api. - * @param {boolean} [options.autoFetch=false] - Fetch next records if available. - * @param {function} callback - A callback to execute once the record is retrieved -<<<<<<< HEAD -======= - * @state {SalesforceState} ->>>>>>> 1ece1ff1 (update examples) - * @returns {Operation} + * @param {string|function} qs - A SOQL query string or a function that returns a query string. Must be less than 4000 characters in WHERE clause + * @param {object} [options] - Optional configuration for the query operation + * @param {boolean} [options.autoFetch=false] - When true, automatically fetches next batch of records if available + * @param {function} [callback] - Optional callback function to execute for each retrieved record + * @state {SalesforceState} - The Salesforce connection state + * @returns {Operation} Returns an Operation object containing the query results and metadata */ export function query(qs, options = {}, callback = s => s) { return async state => { From d1cac65413cf91e5f5cb3489a84201bfe3babc7f Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 12 Nov 2024 21:31:27 +0300 Subject: [PATCH 04/20] add typedef for function options --- packages/salesforce/ast.json | 303 ++++++++++++++++++----------- packages/salesforce/src/Adaptor.js | 95 ++++++--- 2 files changed, 255 insertions(+), 143 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index bfc597ce2..72a92a5af 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -68,74 +68,10 @@ "description": "Options passed to the bulk api.", "type": { "type": "NameExpression", - "name": "object" + "name": "BulkOptions" }, "name": "options" }, - { - "title": "param", - "description": "External id field.", - "type": { - "type": "OptionalType", - "expression": { - "type": "NameExpression", - "name": "string" - } - }, - "name": "options.extIdField" - }, - { - "title": "param", - "description": "Skipping bulk operation if no records.", - "type": { - "type": "OptionalType", - "expression": { - "type": "NameExpression", - "name": "boolean" - } - }, - "name": "options.allowNoOp", - "default": "false" - }, - { - "title": "param", - "description": "Fail the operation on error.", - "type": { - "type": "OptionalType", - "expression": { - "type": "NameExpression", - "name": "boolean" - } - }, - "name": "options.failOnError", - "default": "false" - }, - { - "title": "param", - "description": "Polling interval in milliseconds.", - "type": { - "type": "OptionalType", - "expression": { - "type": "NameExpression", - "name": "integer" - } - }, - "name": "options.pollInterval", - "default": "6000" - }, - { - "title": "param", - "description": "Polling timeout in milliseconds.", - "type": { - "type": "OptionalType", - "expression": { - "type": "NameExpression", - "name": "integer" - } - }, - "name": "options.pollTimeout", - "default": "240000" - }, { "title": "state", "description": "{SalesforceState}" @@ -150,7 +86,7 @@ } ] }, - "valid": false + "valid": true }, { "name": "bulkQuery", @@ -200,36 +136,10 @@ "description": "Options passed to the bulk api.", "type": { "type": "NameExpression", - "name": "object" + "name": "BulkQueryOptions" }, "name": "options" }, - { - "title": "param", - "description": "Polling timeout in milliseconds.", - "type": { - "type": "OptionalType", - "expression": { - "type": "NameExpression", - "name": "integer" - } - }, - "name": "options.pollTimeout", - "default": "90000" - }, - { - "title": "param", - "description": "Polling interval in milliseconds.", - "type": { - "type": "OptionalType", - "expression": { - "type": "NameExpression", - "name": "integer" - } - }, - "name": "options.pollInterval", - "default": "3000" - }, { "title": "state", "description": "{SalesforceState}" @@ -244,7 +154,7 @@ } ] }, - "valid": false + "valid": true }, { "name": "create", @@ -433,6 +343,64 @@ }, "valid": true }, + { + "name": "get", + "params": [ + "path", + "options" + ], + "docs": { + "description": "Send a GET HTTP request using connected session information.", + "tags": [ + { + "title": "public", + "description": null, + "type": null + }, + { + "title": "example", + "description": "get('/actions/custom/flow/POC_OpenFN_Test_Flow');", + "caption": "Make a GET request to a custom Salesforce flow" + }, + { + "title": "function", + "description": null, + "name": null + }, + { + "title": "param", + "description": "The Salesforce API endpoint, Relative to request from", + "type": { + "type": "NameExpression", + "name": "string" + }, + "name": "path" + }, + { + "title": "param", + "description": "Request options", + "type": { + "type": "NameExpression", + "name": "RequestOptions" + }, + "name": "options" + }, + { + "title": "state", + "description": "{SalesforceState}" + }, + { + "title": "returns", + "description": null, + "type": { + "type": "NameExpression", + "name": "Operation" + } + } + ] + }, + "valid": true + }, { "name": "insert", "params": [ @@ -496,6 +464,74 @@ }, "valid": true }, + { + "name": "post", + "params": [ + "path", + "data", + "options" + ], + "docs": { + "description": "Send a POST HTTP request using connected session information.", + "tags": [ + { + "title": "public", + "description": null, + "type": null + }, + { + "title": "example", + "description": "post('/actions/custom/flow/POC_OpenFN_Test_Flow', { inputs: [{}] });", + "caption": "Make a POST request to a custom Salesforce flow" + }, + { + "title": "function", + "description": null, + "name": null + }, + { + "title": "param", + "description": "The Salesforce API endpoint, Relative to request from", + "type": { + "type": "NameExpression", + "name": "string" + }, + "name": "path" + }, + { + "title": "param", + "description": "A JSON Object request body", + "type": { + "type": "NameExpression", + "name": "object" + }, + "name": "data" + }, + { + "title": "param", + "description": "Request options", + "type": { + "type": "NameExpression", + "name": "RequestOptions" + }, + "name": "options" + }, + { + "title": "state", + "description": "{SalesforceState}" + }, + { + "title": "returns", + "description": null, + "type": { + "type": "NameExpression", + "name": "Operation" + } + } + ] + }, + "valid": true + }, { "name": "query", "params": [ @@ -556,24 +592,11 @@ "type": "OptionalType", "expression": { "type": "NameExpression", - "name": "object" + "name": "QueryOptions" } }, "name": "options" }, - { - "title": "param", - "description": "When true, automatically fetches next batch of records if available", - "type": { - "type": "OptionalType", - "expression": { - "type": "NameExpression", - "name": "boolean" - } - }, - "name": "options.autoFetch", - "default": "false" - }, { "title": "param", "description": "Optional callback function to execute for each retrieved record", @@ -588,11 +611,11 @@ }, { "title": "state", - "description": "{SalesforceState} - The Salesforce connection state" + "description": "{SalesforceState}" }, { "title": "returns", - "description": "Returns an Operation object containing the query results and metadata", + "description": null, "type": { "type": "NameExpression", "name": "Operation" @@ -600,7 +623,7 @@ } ] }, - "valid": false + "valid": true }, { "name": "upsert", @@ -825,6 +848,64 @@ }, "valid": true }, + { + "name": "request", + "params": [ + "path", + "options" + ], + "docs": { + "description": "Send a HTTP request using connected session information.", + "tags": [ + { + "title": "public", + "description": null, + "type": null + }, + { + "title": "example", + "description": "request(\"/actions/custom/flow/POC_OpenFN_Test_Flow\", {\n method: \"POST\",\n json: { inputs: [{}] },\n});", + "caption": "Make a POST request to a custom Salesforce flow" + }, + { + "title": "function", + "description": null, + "name": null + }, + { + "title": "param", + "description": "The Salesforce API endpoint, Relative to request from", + "type": { + "type": "NameExpression", + "name": "string" + }, + "name": "path" + }, + { + "title": "param", + "description": "Request options", + "type": { + "type": "NameExpression", + "name": "SalesforceRequestOptions" + }, + "name": "options" + }, + { + "title": "state", + "description": "{SalesforceState}" + }, + { + "title": "returns", + "description": null, + "type": { + "type": "NameExpression", + "name": "Operation" + } + } + ] + }, + "valid": true + }, { "name": "retrieve", "params": [ diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 586d74327..0098d8d82 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -18,6 +18,48 @@ * @property references - History of all previous operations results **/ +/** + * Options provided to the Salesforce HTTP request + * @typedef {Object} SalesforceRequestOptions + * @public + * @property {string} [method=GET] - HTTP method to use. Defaults to GET + * @property {object} headers - Object of request headers + * @property {object} query - Object request query + * @property {object} json - Object request body + * @property {string} body - A string request body + */ + +/** + * @typedef {Object} RequestOptions + * @public + * @property {object} headers - Object of request headers + * @property {object} query - Object of request query + * */ + +/** + * Options provided to the Salesforce bulk API request + * @typedef {Object} BulkOptions + * @public + * @property {string} extIdField - External id field. + * @property {boolean} [allowNoOp=false] - Skipping bulk operation if no records. Default: false + * @property {boolean} [failOnError=false] - Fail the operation on error. Default: false + * @property {integer} [pollTimeout=240000] - Polling timeout in milliseconds. + * @property {integer} [pollInterval=6000] - Polling interval in milliseconds. + */ + +/** + * Options provided to the Salesforce bulk query API request + * @typedef {Object} BulkQueryOptions + * @property {integer} [pollTimeout=90000] - Polling timeout in milliseconds. + * @property {integer} [pollInterval=3000] - Polling interval in milliseconds. + * */ + +/** + * @typedef {Object} QueryOptions + * @public + * @property {boolean} [autoFetch=false] - When true, automatically fetches next batch of records if available + * */ + import { execute as commonExecute, composeNextState, @@ -97,12 +139,7 @@ export function execute(...operations) { * @param {string} sObjectName - API name of the sObject. * @param {string} operation - The bulk operation to be performed.Eg "insert" | "update" | "upsert" * @param {array} records - an array of records, or a function which returns an array. - * @param {object} options - Options passed to the bulk api. - * @param {string} [options.extIdField] - External id field. - * @param {boolean} [options.allowNoOp=false] - Skipping bulk operation if no records. - * @param {boolean} [options.failOnError=false] - Fail the operation on error. - * @param {integer} [options.pollInterval=6000] - Polling interval in milliseconds. - * @param {integer} [options.pollTimeout=240000] - Polling timeout in milliseconds. + * @param {BulkOptions} options - Options passed to the bulk api. * @state {SalesforceState} * @returns {Operation} */ @@ -218,9 +255,7 @@ export function bulk(sObjectName, operation, records, options = {}) { * ); * @function * @param {string} qs - A query string. - * @param {object} options - Options passed to the bulk api. - * @param {integer} [options.pollTimeout=90000] - Polling timeout in milliseconds. - * @param {integer} [options.pollInterval=3000] - Polling interval in milliseconds. + * @param {BulkQueryOptions} options - Options passed to the bulk api. * @state {SalesforceState} * @returns {Operation} */ @@ -378,12 +413,12 @@ export function destroy(sObjectName, ids, options = {}) { /** * Send a GET HTTP request using connected session information. + * @public * @example Make a GET request to a custom Salesforce flow * get('/actions/custom/flow/POC_OpenFN_Test_Flow'); + * @function * @param {string} path - The Salesforce API endpoint, Relative to request from - * @param {object} options - Request options - * @param {object} [options.headers] - Object of request headers - * @param {object} [options.query] - A JSON Object request body + * @param {RequestOptions} options - Request options * @state {SalesforceState} * @returns {Operation} */ @@ -428,14 +463,13 @@ export function insert(sObjectName, records) { /** * Send a POST HTTP request using connected session information. - * + * @public * @example Make a POST request to a custom Salesforce flow * post('/actions/custom/flow/POC_OpenFN_Test_Flow', { inputs: [{}] }); + * @function * @param {string} path - The Salesforce API endpoint, Relative to request from * @param {object} data - A JSON Object request body - * @param {object} options - Request options - * @param {object} [options.headers] - Object of request headers - * @param {object} [options.query] - A JSON Object request body + * @param {RequestOptions} options - Request options * @state {SalesforceState} * @returns {Operation} */ @@ -481,12 +515,11 @@ export function post(path, data, options = {}) { * @example Query patients by Health ID using a lazy state reference * query(`SELECT Id FROM Patient__c WHERE Health_ID__c = '${$.data.healthId}'`); * @function - * @param {string|function} qs - A SOQL query string or a function that returns a query string. Must be less than 4000 characters in WHERE clause - * @param {object} [options] - Optional configuration for the query operation - * @param {boolean} [options.autoFetch=false] - When true, automatically fetches next batch of records if available + * @param {(string|function)} qs - A SOQL query string or a function that returns a query string. Must be less than 4000 characters in WHERE clause + * @param {QueryOptions} [options] - Optional configuration for the query operation * @param {function} [callback] - Optional callback function to execute for each retrieved record - * @state {SalesforceState} - The Salesforce connection state - * @returns {Operation} Returns an Operation object containing the query results and metadata + * @state {SalesforceState} + * @returns {Operation} */ export function query(qs, options = {}, callback = s => s) { return async state => { @@ -656,18 +689,15 @@ export function toUTF8(input) { /** * Send a HTTP request using connected session information. - * - * @example - * request('/actions/custom/flow/POC_OpenFN_Test_Flow', { - * method: 'POST', + * @public + * @example Make a POST request to a custom Salesforce flow + * request("/actions/custom/flow/POC_OpenFN_Test_Flow", { + * method: "POST", * json: { inputs: [{}] }, * }); - * @param {string} url - Relative to request from - * @param {object} options - The options for the request. - * @param {string} [options.method=GET] - HTTP method to use. Defaults to GET - * @param {object} [options.headers] - Object of request headers - * @param {object} [options.json] - A JSON object to send as the request body. - * @param {string} [options.body] - HTTP body (in POST/PUT/PATCH methods) + * @function + * @param {string} path - The Salesforce API endpoint, Relative to request from + * @param {SalesforceRequestOptions} options - Request options * @state {SalesforceState} * @returns {Operation} */ @@ -679,11 +709,12 @@ export function request(path, options = {}) { path, options ); - const { method = 'GET', json, body, headers } = resolvedOptions; + const { method = 'GET', json, body, headers, query } = resolvedOptions; const requestOptions = { url: resolvedPath, method, + query, headers: json ? { 'content-type': 'application/json', ...headers } : headers, From b1227a266539ebe020760b672ba52edc75a6840e Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 12 Nov 2024 21:37:20 +0300 Subject: [PATCH 05/20] add changeset --- .changeset/ten-mayflies-nail.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/ten-mayflies-nail.md diff --git a/.changeset/ten-mayflies-nail.md b/.changeset/ten-mayflies-nail.md new file mode 100644 index 000000000..c8bcda42c --- /dev/null +++ b/.changeset/ten-mayflies-nail.md @@ -0,0 +1,6 @@ +--- +'@openfn/language-salesforce': minor +--- + +- add `query` option in `request` function +- update function examples and improve options documentation From 79167dac866e9743adf8f6a2772de2d46dfe7be7 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Wed, 20 Nov 2024 12:58:12 +0300 Subject: [PATCH 06/20] remove spread operator in query option --- packages/salesforce/src/Adaptor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 0098d8d82..60e445940 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -430,7 +430,7 @@ export function get(path, options = {}) { path, options ); - const { headers, ...query } = resolvedOptions; + const { headers, query } = resolvedOptions; console.log(`GET: ${resolvedPath}`); const requestOptions = { url: resolvedPath, From 8ed4abecb4c688010893d0dd4e6b22c2b74356e6 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Thu, 21 Nov 2024 15:48:56 +0300 Subject: [PATCH 07/20] add unit test --- packages/salesforce/test/Adaptor.test.js | 30 +++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/salesforce/test/Adaptor.test.js b/packages/salesforce/test/Adaptor.test.js index 7274fb416..410ccf03d 100644 --- a/packages/salesforce/test/Adaptor.test.js +++ b/packages/salesforce/test/Adaptor.test.js @@ -1,10 +1,38 @@ import chai from 'chai'; import sinon from 'sinon'; -import { create, upsert, toUTF8, execute, query } from '../src/Adaptor'; +import { create, upsert, toUTF8, execute, query, get } from '../src/Adaptor'; const { expect } = chai; describe('Adaptor', () => { + describe('get', () => { + it('fetches an account record', done => { + const fakeConnection = { + request: function () { + return Promise.resolve({ Id: 10 }); + }, + }; + let state = { connection: fakeConnection, references: [] }; + + let spy = sinon.spy(fakeConnection, 'request'); + + get('/services/data/v58.0/sobjects/Account/10')(state) + .then(state => { + expect(spy.args[0]).to.eql([ + { + url: '/services/data/v58.0/sobjects/Account/10', + method: 'GET', + query: undefined, + headers: { 'content-type': 'application/json' }, + }, + ]); + expect(spy.called).to.eql(true); + expect(state.data).to.eql({ Id: 10 }); + }) + .then(done) + .catch(done); + }); + }); describe('create', () => { it('makes a new sObject', done => { const fakeConnection = { From 8ba4528f720e01cdd5fc28d89e4a18db82592088 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Thu, 21 Nov 2024 16:26:54 +0300 Subject: [PATCH 08/20] use composeNextState in query --- packages/salesforce/src/Adaptor.js | 6 +----- packages/salesforce/test/Adaptor.test.js | 8 ++++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 60e445940..6ffa03fc3 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -582,11 +582,7 @@ export function query(qs, options = {}, callback = s => s) { 'Results retrieved and pushed to position [0] of the references array.' ); - const nextState = { - ...state, - references: [result, ...state.references], - }; - return callback(nextState); + return composeNextState(state, result); }; } diff --git a/packages/salesforce/test/Adaptor.test.js b/packages/salesforce/test/Adaptor.test.js index 410ccf03d..c764b80e0 100644 --- a/packages/salesforce/test/Adaptor.test.js +++ b/packages/salesforce/test/Adaptor.test.js @@ -180,7 +180,7 @@ describe('Adaptor', () => { query('select Name from Account')(state) .then(state => { - expect(state.references[0]).to.eql({ + expect(state.data).to.eql({ done: true, totalSize: 0, records: [], @@ -205,7 +205,7 @@ describe('Adaptor', () => { query('select Name from Account')(state) .then(state => { expect(spy.called).to.eql(true); - expect(state.references[0]).to.eql({ + expect(state.data).to.eql({ done: true, totalSize: 1, records: [{ Name: 'OpenFn' }], @@ -240,7 +240,7 @@ describe('Adaptor', () => { .then(state => { expect(spy.called).to.eql(true); expect(spyReq.called).to.eql(true); - expect(state.references[0]).to.eql({ + expect(state.data).to.eql({ done: true, totalSize: 5713, records: [{ Name: 'Open' }, { Name: 'Fn' }], @@ -275,7 +275,7 @@ describe('Adaptor', () => { .then(state => { expect(spy.called).to.eql(true); expect(spyReq.called).to.eql(false); - expect(state.references[0]).to.eql({ + expect(state.data).to.eql({ done: false, totalSize: 5713, nextRecordsUrl: From 2f31124ce93a9075b89c16521965acbcafa0314a Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Fri, 22 Nov 2024 11:42:13 +0300 Subject: [PATCH 09/20] update docs for bulk function --- packages/salesforce/ast.json | 6 +++--- packages/salesforce/src/Adaptor.js | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index 72a92a5af..e93412588 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -9,7 +9,7 @@ "options" ], "docs": { - "description": "Create and execute a bulk job.", + "description": "Create and execute a bulk job.\nThis function uses {@link https://sforce.co/4fDLJnk Bulk API},\nwhich is subject to {@link https://sforce.co/4b6kn6z rate limits}.", "tags": [ { "title": "public", @@ -47,7 +47,7 @@ }, { "title": "param", - "description": "The bulk operation to be performed.Eg \"insert\" | \"update\" | \"upsert\"", + "description": "The bulk operation to be performed.Eg `insert`, `update` or `upsert`", "type": { "type": "NameExpression", "name": "string" @@ -95,7 +95,7 @@ "options" ], "docs": { - "description": "Execute an SOQL Bulk Query.\nThis function uses bulk query to efficiently query large data sets and reduce the number of API requests.\n`bulkQuery()` uses {@link https://sforce.co/4azgczz Bulk API v.2.0 Query} which is available in API version 47.0 and later.\nThis API is subject to {@link https://sforce.co/4b6kn6z rate limits}.", + "description": "Execute an SOQL Bulk Query.\nThis function uses bulk query to efficiently query large data sets and reduce the number of API requests.\n`bulkQuery()` uses {@link https://sforce.co/4azgczz Bulk API v2.0 Query} which is available in API version 47.0 and later.\nThis API is subject to {@link https://sforce.co/4b6kn6z rate limits}.", "tags": [ { "title": "public", diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 6ffa03fc3..97914bb05 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -40,7 +40,7 @@ * Options provided to the Salesforce bulk API request * @typedef {Object} BulkOptions * @public - * @property {string} extIdField - External id field. + * @property {string} extIdField - External id field. Required for upsert. * @property {boolean} [allowNoOp=false] - Skipping bulk operation if no records. Default: false * @property {boolean} [failOnError=false] - Fail the operation on error. Default: false * @property {integer} [pollTimeout=240000] - Polling timeout in milliseconds. @@ -107,7 +107,10 @@ export function execute(...operations) { /** * Create and execute a bulk job. + * This function uses {@link https://sforce.co/4fDLJnk Bulk API}, + * which is subject to {@link https://sforce.co/4b6kn6z rate limits}. * @public + * * @example Bulk insert * bulk( * "Patient__c", @@ -137,7 +140,7 @@ export function execute(...operations) { * bulk("Account", "update", { failOnError: true }, $.accounts); * @function * @param {string} sObjectName - API name of the sObject. - * @param {string} operation - The bulk operation to be performed.Eg "insert" | "update" | "upsert" + * @param {string} operation - The bulk operation to be performed.Eg `insert`, `update` or `upsert` * @param {array} records - an array of records, or a function which returns an array. * @param {BulkOptions} options - Options passed to the bulk api. * @state {SalesforceState} @@ -240,7 +243,7 @@ export function bulk(sObjectName, operation, records, options = {}) { /** * Execute an SOQL Bulk Query. * This function uses bulk query to efficiently query large data sets and reduce the number of API requests. - * `bulkQuery()` uses {@link https://sforce.co/4azgczz Bulk API v.2.0 Query} which is available in API version 47.0 and later. + * `bulkQuery()` uses {@link https://sforce.co/4azgczz Bulk API v2.0 Query} which is available in API version 47.0 and later. * This API is subject to {@link https://sforce.co/4b6kn6z rate limits}. * @public * @example Bulk query patient records where `Health_ID__c` is equal to the value in `state.data.healthId` From 62879ef9742d029ad2ee6ff15f57ee5076cbb3fd Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 10 Dec 2024 12:23:38 +0300 Subject: [PATCH 10/20] update bulk docs --- packages/salesforce/ast.json | 22 +++++++++++++++++++--- packages/salesforce/src/Adaptor.js | 10 +++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index e93412588..dcde82afc 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -28,7 +28,7 @@ }, { "title": "example", - "description": "fn((state) => {\n state.accounts = state.data.map((a) => ({ Id: a.id, Name: a.name }));\n return state;\n});\nbulk(\"Account\", \"update\", { failOnError: true }, $.accounts);", + "description": "fn((state) => {\n state.accounts = state.data.map((a) => ({ Id: a.id, Name: a.name }));\n return state;\n});\nbulk(\"Account\", \"update\", $.accounts, { failOnError: true });", "caption": "Bulk update Account records using a lazy state reference" }, { @@ -65,10 +65,10 @@ }, { "title": "param", - "description": "Options passed to the bulk api.", + "description": "Options to configure the request. In addition to these, you can pass any of the options supported by the {@link https://bit.ly/41tyvVU jsforce API}.", "type": { "type": "NameExpression", - "name": "BulkOptions" + "name": "Options" }, "name": "options" }, @@ -76,6 +76,22 @@ "title": "state", "description": "{SalesforceState}" }, + { + "title": "state", + "description": "{Object[]} data - An array of result objects." + }, + { + "title": "state", + "description": "{string} data[].id - The unique identifier of the result." + }, + { + "title": "state", + "description": "{boolean} data[].success - Indicates whether the operation was successful." + }, + { + "title": "state", + "description": "{Array} data[].errors - An array of error messages, if any." + }, { "title": "returns", "description": null, diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 97914bb05..430d5d492 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -38,7 +38,7 @@ /** * Options provided to the Salesforce bulk API request - * @typedef {Object} BulkOptions + * @typedef {Object} Options * @public * @property {string} extIdField - External id field. Required for upsert. * @property {boolean} [allowNoOp=false] - Skipping bulk operation if no records. Default: false @@ -137,13 +137,17 @@ export function execute(...operations) { * state.accounts = state.data.map((a) => ({ Id: a.id, Name: a.name })); * return state; * }); - * bulk("Account", "update", { failOnError: true }, $.accounts); + * bulk("Account", "update", $.accounts, { failOnError: true }); * @function * @param {string} sObjectName - API name of the sObject. * @param {string} operation - The bulk operation to be performed.Eg `insert`, `update` or `upsert` * @param {array} records - an array of records, or a function which returns an array. - * @param {BulkOptions} options - Options passed to the bulk api. + * @param {Options} options - Options to configure the request. In addition to these, you can pass any of the options supported by the {@link https://bit.ly/41tyvVU jsforce API}. * @state {SalesforceState} + * @state {Object[]} data - An array of result objects. + * @state {string} data[].id - The unique identifier of the result. + * @state {boolean} data[].success - Indicates whether the operation was successful. + * @state {Array} data[].errors - An array of error messages, if any. * @returns {Operation} */ export function bulk(sObjectName, operation, records, options = {}) { From 70b1586fa928c1aa6950f6d37464ac8c4336722f Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 10 Dec 2024 13:16:48 +0300 Subject: [PATCH 11/20] update bulkQuery example --- packages/salesforce/ast.json | 9 ++------- packages/salesforce/src/Adaptor.js | 6 ++---- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index dcde82afc..dbb5ad92a 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -111,22 +111,17 @@ "options" ], "docs": { - "description": "Execute an SOQL Bulk Query.\nThis function uses bulk query to efficiently query large data sets and reduce the number of API requests.\n`bulkQuery()` uses {@link https://sforce.co/4azgczz Bulk API v2.0 Query} which is available in API version 47.0 and later.\nThis API is subject to {@link https://sforce.co/4b6kn6z rate limits}.", + "description": "Execute an SOQL Bulk Query.\nThis function query large data sets and reduce the number of API requests.\n`bulkQuery()` uses {@link https://sforce.co/4azgczz Bulk API v2.0 Query} which is available in API version 47.0 and later.\nThis API is subject to {@link https://sforce.co/4b6kn6z rate limits}.", "tags": [ { "title": "public", "description": null, "type": null }, - { - "title": "example", - "description": "bulkQuery(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.healthId}'`);", - "caption": "Bulk query patient records where `Health_ID__c` is equal to the value in `state.data.healthId`" - }, { "title": "example", "description": "bulkQuery(`SELECT Id FROM Patient__c WHERE Health_ID__c = '${$.data.healthId}'`);", - "caption": "Bulk query patient records using a lazy state reference" + "caption": "Bulk query patient records where \"Health_ID__c\" is equal to the value in \"state.data.healthId\"" }, { "title": "example", diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 430d5d492..6d419f1dd 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -246,13 +246,11 @@ export function bulk(sObjectName, operation, records, options = {}) { } /** * Execute an SOQL Bulk Query. - * This function uses bulk query to efficiently query large data sets and reduce the number of API requests. + * This function query large data sets and reduce the number of API requests. * `bulkQuery()` uses {@link https://sforce.co/4azgczz Bulk API v2.0 Query} which is available in API version 47.0 and later. * This API is subject to {@link https://sforce.co/4b6kn6z rate limits}. * @public - * @example Bulk query patient records where `Health_ID__c` is equal to the value in `state.data.healthId` - * bulkQuery(state=> `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.healthId}'`); - * @example Bulk query patient records using a lazy state reference + * @example Bulk query patient records where "Health_ID__c" is equal to the value in "state.data.healthId" * bulkQuery(`SELECT Id FROM Patient__c WHERE Health_ID__c = '${$.data.healthId}'`); * @example Bulk query with custom polling options * bulkQuery( From ab3d5d6e58dbe8275065edec3ca3ee9823254cea Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 10 Dec 2024 13:24:49 +0300 Subject: [PATCH 12/20] change qs to query --- packages/salesforce/ast.json | 8 ++++---- packages/salesforce/src/Adaptor.js | 31 +++++++++++++++++++----------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index dbb5ad92a..280928f3d 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -107,7 +107,7 @@ { "name": "bulkQuery", "params": [ - "qs", + "query", "options" ], "docs": { @@ -140,7 +140,7 @@ "type": "NameExpression", "name": "string" }, - "name": "qs" + "name": "query" }, { "title": "param", @@ -546,7 +546,7 @@ { "name": "query", "params": [ - "qs", + "query", "options", "callback" ], @@ -594,7 +594,7 @@ } ] }, - "name": "qs" + "name": "query" }, { "title": "param", diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 6d419f1dd..11e4520cb 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -50,6 +50,7 @@ /** * Options provided to the Salesforce bulk query API request * @typedef {Object} BulkQueryOptions + * @public * @property {integer} [pollTimeout=90000] - Polling timeout in milliseconds. * @property {integer} [pollInterval=3000] - Polling interval in milliseconds. * */ @@ -259,29 +260,33 @@ export function bulk(sObjectName, operation, records, options = {}) { * { pollTimeout: 10000, pollInterval: 6000 } * ); * @function - * @param {string} qs - A query string. + * @param {string} query - A query string. * @param {BulkQueryOptions} options - Options passed to the bulk api. * @state {SalesforceState} * @returns {Operation} */ -export function bulkQuery(qs, options = {}) { +export function bulkQuery(query, options = {}) { return async state => { const { connection } = state; - const [resolvedQs, resolvedOptions] = expandReferences(state, qs, options); + const [resolvedQuery, resolvedOptions] = expandReferences( + state, + query, + options + ); if (parseFloat(connection.version) < 47.0) throw new Error('bulkQuery requires API version 47.0 and later'); const { pollTimeout = 90000, pollInterval = 3000 } = resolvedOptions; - console.log(`Executing query: ${resolvedQs}`); + console.log(`Executing query: ${resolvedQuery}`); const queryJob = await connection.request({ method: 'POST', url: `/services/data/v${connection.version}/jobs/query`, body: JSON.stringify({ operation: 'query', - query: resolvedQs, + query: resolvedQuery, }), headers: { 'Content-Type': 'application/json', @@ -520,17 +525,21 @@ export function post(path, data, options = {}) { * @example Query patients by Health ID using a lazy state reference * query(`SELECT Id FROM Patient__c WHERE Health_ID__c = '${$.data.healthId}'`); * @function - * @param {(string|function)} qs - A SOQL query string or a function that returns a query string. Must be less than 4000 characters in WHERE clause + * @param {(string|function)} query - A SOQL query string or a function that returns a query string. Must be less than 4000 characters in WHERE clause * @param {QueryOptions} [options] - Optional configuration for the query operation * @param {function} [callback] - Optional callback function to execute for each retrieved record * @state {SalesforceState} * @returns {Operation} */ -export function query(qs, options = {}, callback = s => s) { +export function query(query, options = {}, callback = s => s) { return async state => { const { connection } = state; - const [resolvedQs, resolvedOptions] = expandReferences(state, qs, options); - console.log(`Executing query: ${resolvedQs}`); + const [resolvedQuery, resolvedOptions] = expandReferences( + state, + query, + options + ); + console.log(`Executing query: ${resolvedQuery}`); const autoFetch = resolvedOptions.autoFetch || resolvedOptions.autofetch; if (autoFetch) { @@ -569,7 +578,7 @@ export function query(qs, options = {}, callback = s => s) { }; try { - const qResult = await connection.query(resolvedQs); + const qResult = await connection.query(resolvedQuery); if (qResult.totalSize > 0) { console.log('Total records:', qResult.totalSize); await processRecords(qResult); @@ -587,7 +596,7 @@ export function query(qs, options = {}, callback = s => s) { 'Results retrieved and pushed to position [0] of the references array.' ); - return composeNextState(state, result); + return callback(composeNextState(state, result)); }; } From 216d30db60e9d2901d858dbff5a77d8c1aa9b076 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 10 Dec 2024 13:53:20 +0300 Subject: [PATCH 13/20] update query state.data docs --- packages/salesforce/ast.json | 76 ++++++++++++++++++++++++++---- packages/salesforce/src/Adaptor.js | 21 +++++++-- 2 files changed, 85 insertions(+), 12 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index 280928f3d..a5ea51892 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -191,6 +191,11 @@ "description": "create(\"Account\",[{ Name: \"My Account #1\" }, { Name: \"My Account #2\" }]);", "caption": "Multiple records creation" }, + { + "title": "example", + "description": "fn((state) => {\n state.data = [{ Name: \"My Account #1\" }, { Name: \"My Account #2\" }];\n return state;\n});\ncreate(\"Account\", $.data);", + "caption": "Create a records from a state variable" + }, { "title": "function", "description": null, @@ -207,10 +212,28 @@ }, { "title": "param", - "description": "Field attributes for the new record.", + "description": "Field attributes for the new record, or an array of field attributes.", "type": { - "type": "NameExpression", - "name": "object" + "type": "UnionType", + "elements": [ + { + "type": "NameExpression", + "name": "Object" + }, + { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "NameExpression", + "name": "Object" + } + ] + } + ] }, "name": "records" }, @@ -436,6 +459,11 @@ "description": "insert(\"Account\",[{ Name: \"My Account #1\" }, { Name: \"My Account #2\" }]);", "caption": "Multiple records creation" }, + { + "title": "example", + "description": "fn((state) => {\n state.data = [{ Name: \"My Account #1\" }, { Name: \"My Account #2\" }];\n return state;\n});\ninsert(\"Account\", $.data);", + "caption": "Using a state variable" + }, { "title": "function", "description": null, @@ -452,10 +480,28 @@ }, { "title": "param", - "description": "Field attributes for the new record.", + "description": "Field attributes for the new record, or an array of field attributes.", "type": { - "type": "NameExpression", - "name": "object" + "type": "UnionType", + "elements": [ + { + "type": "NameExpression", + "name": "Object" + }, + { + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "NameExpression", + "name": "Object" + } + ] + } + ] }, "name": "records" }, @@ -624,6 +670,18 @@ "title": "state", "description": "{SalesforceState}" }, + { + "title": "state", + "description": "{boolean} data.done - Indicates whether the operation is complete." + }, + { + "title": "state", + "description": "{number} data.totalSize - The total number of items returned by the query." + }, + { + "title": "state", + "description": "{Object[]} data.records - The records returned by the query." + }, { "title": "returns", "description": null, @@ -694,13 +752,13 @@ }, { "title": "param", - "description": "Field attributes for the new object.", + "description": "Field attributes for the records to upsert, or an array of field attributes.", "type": { "type": "UnionType", "elements": [ { "type": "NameExpression", - "name": "object" + "name": "Object" }, { "type": "TypeApplication", @@ -711,7 +769,7 @@ "applications": [ { "type": "NameExpression", - "name": "object" + "name": "Object" } ] } diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 11e4520cb..4b188aa74 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -311,9 +311,15 @@ export function bulkQuery(query, options = {}) { * create("Account", { Name: "My Account #1" }); * @example Multiple records creation * create("Account",[{ Name: "My Account #1" }, { Name: "My Account #2" }]); + * @example Create a records from a state variable + * fn((state) => { + * state.data = [{ Name: "My Account #1" }, { Name: "My Account #2" }]; + * return state; + * }); + * create("Account", $.data); * @function * @param {string} sObjectName - API name of the sObject. - * @param {object} records - Field attributes for the new record. + * @param {(Object|Object[])} records - Field attributes for the new record, or an array of field attributes. * @state {SalesforceState} * @returns {Operation} */ @@ -461,9 +467,15 @@ export function get(path, options = {}) { * insert("Account", { Name: "My Account #1" }); * @example Multiple records creation * insert("Account",[{ Name: "My Account #1" }, { Name: "My Account #2" }]); + * @example Using a state variable + * fn((state) => { + * state.data = [{ Name: "My Account #1" }, { Name: "My Account #2" }]; + * return state; + * }); + * insert("Account", $.data); * @function * @param {string} sObjectName - API name of the sObject. - * @param {object} records - Field attributes for the new record. + * @param {(Object|Object[])} records - Field attributes for the new record, or an array of field attributes. * @state {SalesforceState} * @returns {Operation} */ @@ -529,6 +541,9 @@ export function post(path, data, options = {}) { * @param {QueryOptions} [options] - Optional configuration for the query operation * @param {function} [callback] - Optional callback function to execute for each retrieved record * @state {SalesforceState} + * @state {boolean} data.done - Indicates whether the operation is complete. + * @state {number} data.totalSize - The total number of items returned by the query. + * @state {Object[]} data.records - The records returned by the query. * @returns {Operation} */ export function query(query, options = {}, callback = s => s) { @@ -616,7 +631,7 @@ export function query(query, options = {}, callback = s => s) { * @magic sObjectName - $.children[?(!@.meta.system)].name * @param {string} externalId - The external ID of the sObject. * @magic externalId - $.children[?(@.name=="{{args.sObject}}")].children[?(@.meta.externalId)].name - * @param {(object|object[])} records - Field attributes for the new object. + * @param {(Object|Object[])} records - Field attributes for the records to upsert, or an array of field attributes. * @magic records - $.children[?(@.name=="{{args.sObject}}")].children[?(!@.meta.externalId)] * @state {SalesforceState} * @returns {Operation} From 4d8c333e45b791b34968be9573c077893bc02ba4 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 10 Dec 2024 14:04:54 +0300 Subject: [PATCH 14/20] update destroy docs --- packages/salesforce/ast.json | 49 ++++++++++++++++++++++++------ packages/salesforce/src/Adaptor.js | 18 +++++++++-- 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index a5ea51892..4164a1b3b 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -259,7 +259,7 @@ "sObjectName" ], "docs": { - "description": "Fetches and prints metadata for an sObject and pushes the result to `state.data`.\nIf `sObjectName` is not specified, it will print the total number of all available sObjects and push the result to `state.data`.", + "description": "Fetches and logs metadata for an sObject and pushes the result to `state.data`.\nIf `sObjectName` is not specified, it will print the total number of all available sObjects and push the result to `state.data`.", "tags": [ { "title": "public", @@ -285,11 +285,8 @@ "title": "param", "description": "The API name of the sObject. If omitted, fetches metadata for all sObjects.", "type": { - "type": "OptionalType", - "expression": { - "type": "NameExpression", - "name": "string" - } + "type": "NameExpression", + "name": "string" }, "name": "sObjectName" }, @@ -317,7 +314,7 @@ "options" ], "docs": { - "description": "Delete records of an object.", + "description": "Delete records of an object.\nThis functions uses {@link https://jsforce.github.io/document/#delete|jsforce delete} under the hood.", "tags": [ { "title": "public", @@ -329,6 +326,16 @@ "description": "destroy(\"Account\", [\"001XXXXXXXXXXXXXXX\", \"001YYYYYYYYYYYYYYY\"], {\n failOnError: true,\n});", "caption": "Delete multiple Account records" }, + { + "title": "example", + "description": "fn((state) => {\n state.data = [\"001XXXXXXXXXXXXXXX\", \"001YYYYYYYYYYYYYYY\"];\n return state;\n});\ndestroy(\"Account\", $.data);", + "caption": "Using a state variable" + }, + { + "title": "example", + "description": "destroy(\"Account\", [\"001XXXXXXXXXXXXXXX\", \"001YYYYYYYYYYYYYYY\"], {\n failOnError: true,\n});", + "caption": "Using options" + }, { "title": "function", "description": null, @@ -347,8 +354,17 @@ "title": "param", "description": "Array of IDs of records to delete.", "type": { - "type": "NameExpression", - "name": "object" + "type": "TypeApplication", + "expression": { + "type": "NameExpression", + "name": "Array" + }, + "applications": [ + { + "type": "NameExpression", + "name": "string" + } + ] }, "name": "ids" }, @@ -361,6 +377,19 @@ }, "name": "options" }, + { + "title": "param", + "description": "If true, the operation will fail if any record fails to delete.", + "type": { + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "boolean" + } + }, + "name": "options.failOnError", + "default": "false" + }, { "title": "state", "description": "{SalesforceState}" @@ -375,7 +404,7 @@ } ] }, - "valid": true + "valid": false }, { "name": "get", diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 4b188aa74..8b548dcc5 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -343,7 +343,7 @@ export function create(sObjectName, records) { } /** - * Fetches and prints metadata for an sObject and pushes the result to `state.data`. + * Fetches and logs metadata for an sObject and pushes the result to `state.data`. * If `sObjectName` is not specified, it will print the total number of all available sObjects and push the result to `state.data`. * @public * @example Fetch metadata for all available sObjects @@ -351,7 +351,7 @@ export function create(sObjectName, records) { * @example Fetch metadata for Account sObject * describe('Account') * @function - * @param {string} [sObjectName] - The API name of the sObject. If omitted, fetches metadata for all sObjects. + * @param {string} sObjectName - The API name of the sObject. If omitted, fetches metadata for all sObjects. * @state {SalesforceState} * @returns {Operation} */ @@ -381,15 +381,27 @@ export function describe(sObjectName) { /** * Delete records of an object. + * This functions uses {@link https://jsforce.github.io/document/#delete|jsforce delete} under the hood. * @public * @example Delete multiple Account records * destroy("Account", ["001XXXXXXXXXXXXXXX", "001YYYYYYYYYYYYYYY"], { * failOnError: true, * }); + * @example Using a state variable + * fn((state) => { + * state.data = ["001XXXXXXXXXXXXXXX", "001YYYYYYYYYYYYYYY"]; + * return state; + * }); + * destroy("Account", $.data); + * @example Using options + * destroy("Account", ["001XXXXXXXXXXXXXXX", "001YYYYYYYYYYYYYYY"], { + * failOnError: true, + * }); * @function * @param {string} sObjectName - API name of the sObject. - * @param {object} ids - Array of IDs of records to delete. + * @param {string[]} ids - Array of IDs of records to delete. * @param {object} options - Options for the destroy delete operation. + * @param {boolean} [options.failOnError=false] - If true, the operation will fail if any record fails to delete. * @state {SalesforceState} * @returns {Operation} */ From 1987c8fca082e9477fd703a4378f1c429cc4d82e Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 10 Dec 2024 14:09:47 +0300 Subject: [PATCH 15/20] update example --- packages/salesforce/ast.json | 7 +------ packages/salesforce/src/Adaptor.js | 6 +----- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index 4164a1b3b..e695772fc 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -324,18 +324,13 @@ { "title": "example", "description": "destroy(\"Account\", [\"001XXXXXXXXXXXXXXX\", \"001YYYYYYYYYYYYYYY\"], {\n failOnError: true,\n});", - "caption": "Delete multiple Account records" + "caption": "Allow operation to fail if any record fails to delete" }, { "title": "example", "description": "fn((state) => {\n state.data = [\"001XXXXXXXXXXXXXXX\", \"001YYYYYYYYYYYYYYY\"];\n return state;\n});\ndestroy(\"Account\", $.data);", "caption": "Using a state variable" }, - { - "title": "example", - "description": "destroy(\"Account\", [\"001XXXXXXXXXXXXXXX\", \"001YYYYYYYYYYYYYYY\"], {\n failOnError: true,\n});", - "caption": "Using options" - }, { "title": "function", "description": null, diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 8b548dcc5..ba3eb0e02 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -383,7 +383,7 @@ export function describe(sObjectName) { * Delete records of an object. * This functions uses {@link https://jsforce.github.io/document/#delete|jsforce delete} under the hood. * @public - * @example Delete multiple Account records + * @example Allow operation to fail if any record fails to delete * destroy("Account", ["001XXXXXXXXXXXXXXX", "001YYYYYYYYYYYYYYY"], { * failOnError: true, * }); @@ -393,10 +393,6 @@ export function describe(sObjectName) { * return state; * }); * destroy("Account", $.data); - * @example Using options - * destroy("Account", ["001XXXXXXXXXXXXXXX", "001YYYYYYYYYYYYYYY"], { - * failOnError: true, - * }); * @function * @param {string} sObjectName - API name of the sObject. * @param {string[]} ids - Array of IDs of records to delete. From e1e1dfecad3e604e7662ec8981bd8b5c79b3d94e Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 10 Dec 2024 14:18:32 +0300 Subject: [PATCH 16/20] update destroy state.data docs --- packages/salesforce/ast.json | 18 +++++++++++++++++- packages/salesforce/src/Adaptor.js | 6 +++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index e695772fc..6b4ccf19b 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -314,7 +314,7 @@ "options" ], "docs": { - "description": "Delete records of an object.\nThis functions uses {@link https://jsforce.github.io/document/#delete|jsforce delete} under the hood.", + "description": "Delete records of an sObject.\nThis functions uses {@link https://jsforce.github.io/document/#delete|jsforce delete} under the hood.", "tags": [ { "title": "public", @@ -389,6 +389,22 @@ "title": "state", "description": "{SalesforceState}" }, + { + "title": "state", + "description": "{Object[]} data - An array of result objects." + }, + { + "title": "state", + "description": "{string} data[].id - The unique identifier of the result." + }, + { + "title": "state", + "description": "{boolean} data[].success - Indicates whether the operation was successful." + }, + { + "title": "state", + "description": "{Array} data[].errors - An array of error messages, if any." + }, { "title": "returns", "description": null, diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index ba3eb0e02..7cf68031d 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -380,7 +380,7 @@ export function describe(sObjectName) { } /** - * Delete records of an object. + * Delete records of an sObject. * This functions uses {@link https://jsforce.github.io/document/#delete|jsforce delete} under the hood. * @public * @example Allow operation to fail if any record fails to delete @@ -399,6 +399,10 @@ export function describe(sObjectName) { * @param {object} options - Options for the destroy delete operation. * @param {boolean} [options.failOnError=false] - If true, the operation will fail if any record fails to delete. * @state {SalesforceState} + * @state {Object[]} data - An array of result objects. + * @state {string} data[].id - The unique identifier of the result. + * @state {boolean} data[].success - Indicates whether the operation was successful. + * @state {Array} data[].errors - An array of error messages, if any. * @returns {Operation} */ export function destroy(sObjectName, ids, options = {}) { From ec3920980acae533f7e28d88b39d386e0f081732 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 10 Dec 2024 14:54:03 +0300 Subject: [PATCH 17/20] improvements --- packages/salesforce/ast.json | 48 ++++++++++++++++++++---------- packages/salesforce/src/Adaptor.js | 42 ++++++++++++++++++-------- 2 files changed, 62 insertions(+), 28 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index 6b4ccf19b..b1e23eee3 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -174,7 +174,7 @@ "records" ], "docs": { - "description": "Create a new sObject record(s).", + "description": "Create a new sObject record(s).\nThis function uses {@link https://jsforce.github.io/document/#create|jsforce create} under the hood.", "tags": [ { "title": "public", @@ -424,7 +424,7 @@ "options" ], "docs": { - "description": "Send a GET HTTP request using connected session information.", + "description": "Send a GET request on salesforce server configured in `state.configuration`.", "tags": [ { "title": "public", @@ -436,6 +436,16 @@ "description": "get('/actions/custom/flow/POC_OpenFN_Test_Flow');", "caption": "Make a GET request to a custom Salesforce flow" }, + { + "title": "example", + "description": "get('/actions/custom/flow/POC_OpenFN_Test_Flow', { query: { Status: 'Active' } });", + "caption": "Make a GET request to a custom Salesforce flow with query parameters" + }, + { + "title": "example", + "description": "get('/jobs/query/v1/jobs/001XXXXXXXXXXXXXXX/results', (state) => {\n // Mapping the response\n state.mapping = state.data.map(d => ({ name: d.name, id: d.extId }));\n return state;\n});", + "caption": "Make a GET request then map the response" + }, { "title": "function", "description": null, @@ -443,7 +453,7 @@ }, { "title": "param", - "description": "The Salesforce API endpoint, Relative to request from", + "description": "The Salesforce API endpoint.", "type": { "type": "NameExpression", "name": "string" @@ -452,7 +462,7 @@ }, { "title": "param", - "description": "Request options", + "description": "Configure headers and query parameters for the request.", "type": { "type": "NameExpression", "name": "RequestOptions" @@ -569,7 +579,7 @@ "options" ], "docs": { - "description": "Send a POST HTTP request using connected session information.", + "description": "Send a POST request to salesforce server configured in `state.configuration`.", "tags": [ { "title": "public", @@ -578,7 +588,7 @@ }, { "title": "example", - "description": "post('/actions/custom/flow/POC_OpenFN_Test_Flow', { inputs: [{}] });", + "description": "post(\"/actions/custom/flow/POC_OpenFN_Test_Flow\", {\n body: {\n inputs: [\n {\n CommentCount: 6,\n FeedItemId: \"0D5D0000000cfMY\",\n },\n ],\n },\n});", "caption": "Make a POST request to a custom Salesforce flow" }, { @@ -588,7 +598,7 @@ }, { "title": "param", - "description": "The Salesforce API endpoint, Relative to request from", + "description": "The Salesforce API endpoint.", "type": { "type": "NameExpression", "name": "string" @@ -597,7 +607,7 @@ }, { "title": "param", - "description": "A JSON Object request body", + "description": "A JSON Object request body.", "type": { "type": "NameExpression", "name": "object" @@ -606,10 +616,13 @@ }, { "title": "param", - "description": "Request options", + "description": "Configure headers and query parameters for the request.", "type": { - "type": "NameExpression", - "name": "RequestOptions" + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "RequestOptions" + } }, "name": "options" }, @@ -964,7 +977,7 @@ "options" ], "docs": { - "description": "Send a HTTP request using connected session information.", + "description": "Send a request to salesforce server configured in `state.configuration`.", "tags": [ { "title": "public", @@ -983,7 +996,7 @@ }, { "title": "param", - "description": "The Salesforce API endpoint, Relative to request from", + "description": "The Salesforce API endpoint.", "type": { "type": "NameExpression", "name": "string" @@ -992,10 +1005,13 @@ }, { "title": "param", - "description": "Request options", + "description": "Configure headers, query and body parameters for the request.", "type": { - "type": "NameExpression", - "name": "SalesforceRequestOptions" + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "SalesforceRequestOptions" + } }, "name": "options" }, diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 7cf68031d..4a84a0bb3 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -306,6 +306,7 @@ export function bulkQuery(query, options = {}) { /** * Create a new sObject record(s). + * This function uses {@link https://jsforce.github.io/document/#create|jsforce create} under the hood. * @public * @example Single record creation * create("Account", { Name: "My Account #1" }); @@ -440,13 +441,21 @@ export function destroy(sObjectName, ids, options = {}) { } /** - * Send a GET HTTP request using connected session information. + * Send a GET request on salesforce server configured in `state.configuration`. * @public * @example Make a GET request to a custom Salesforce flow * get('/actions/custom/flow/POC_OpenFN_Test_Flow'); + * @example Make a GET request to a custom Salesforce flow with query parameters + * get('/actions/custom/flow/POC_OpenFN_Test_Flow', { query: { Status: 'Active' } }); + * @example Make a GET request then map the response + * get('/jobs/query/v1/jobs/001XXXXXXXXXXXXXXX/results', (state) => { + * // Mapping the response + * state.mapping = state.data.map(d => ({ name: d.name, id: d.extId })); + * return state; + * }); * @function - * @param {string} path - The Salesforce API endpoint, Relative to request from - * @param {RequestOptions} options - Request options + * @param {string} path - The Salesforce API endpoint. + * @param {RequestOptions} options - Configure headers and query parameters for the request. * @state {SalesforceState} * @returns {Operation} */ @@ -480,7 +489,7 @@ export function get(path, options = {}) { * @example Multiple records creation * insert("Account",[{ Name: "My Account #1" }, { Name: "My Account #2" }]); * @example Using a state variable - * fn((state) => { + * fn((state) => { * state.data = [{ Name: "My Account #1" }, { Name: "My Account #2" }]; * return state; * }); @@ -496,14 +505,23 @@ export function insert(sObjectName, records) { } /** - * Send a POST HTTP request using connected session information. + * Send a POST request to salesforce server configured in `state.configuration`. * @public * @example Make a POST request to a custom Salesforce flow - * post('/actions/custom/flow/POC_OpenFN_Test_Flow', { inputs: [{}] }); + * post("/actions/custom/flow/POC_OpenFN_Test_Flow", { + * body: { + * inputs: [ + * { + * CommentCount: 6, + * FeedItemId: "0D5D0000000cfMY", + * }, + * ], + * }, + * }); * @function - * @param {string} path - The Salesforce API endpoint, Relative to request from - * @param {object} data - A JSON Object request body - * @param {RequestOptions} options - Request options + * @param {string} path - The Salesforce API endpoint. + * @param {object} data - A JSON Object request body. + * @param {RequestOptions} [options] - Configure headers and query parameters for the request. * @state {SalesforceState} * @returns {Operation} */ @@ -725,7 +743,7 @@ export function toUTF8(input) { } /** - * Send a HTTP request using connected session information. + * Send a request to salesforce server configured in `state.configuration`. * @public * @example Make a POST request to a custom Salesforce flow * request("/actions/custom/flow/POC_OpenFN_Test_Flow", { @@ -733,8 +751,8 @@ export function toUTF8(input) { * json: { inputs: [{}] }, * }); * @function - * @param {string} path - The Salesforce API endpoint, Relative to request from - * @param {SalesforceRequestOptions} options - Request options + * @param {string} path - The Salesforce API endpoint. + * @param {SalesforceRequestOptions} [options] - Configure headers, query and body parameters for the request. * @state {SalesforceState} * @returns {Operation} */ From 8b62452f69a52b718a40d547222094695e4f890d Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 10 Dec 2024 15:00:23 +0300 Subject: [PATCH 18/20] add link to soql-query --- packages/salesforce/ast.json | 2 +- packages/salesforce/src/Adaptor.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index b1e23eee3..48abd46f5 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -650,7 +650,7 @@ "callback" ], "docs": { - "description": "Executes an SOQL (Salesforce Object Query Language) query to retrieve records from Salesforce.\nThis operation allows querying Salesforce objects using SOQL syntax and handles pagination.\nNote that in an event of a query error, error logs will be printed but the operation will not throw the error.\n\nThe Salesforce query API is subject to rate limits, {@link https://sforce.co/3W9zyaQ See for more details}.", + "description": "Executes an SOQL (Salesforce Object Query Language) query to retrieve records from Salesforce.\nThis operation uses {@link https://jsforce.github.io/document/#using-soql for querying salesforce records} using SOQL query and handles pagination.\nNote that in an event of a query error, error logs will be printed but the operation will not throw the error.\n\nThe Salesforce query API is subject to rate limits, {@link https://sforce.co/3W9zyaQ See for more details}.", "tags": [ { "title": "public", diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 4a84a0bb3..f246d5a0f 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -554,7 +554,7 @@ export function post(path, data, options = {}) { /** * Executes an SOQL (Salesforce Object Query Language) query to retrieve records from Salesforce. - * This operation allows querying Salesforce objects using SOQL syntax and handles pagination. + * This operation uses {@link https://jsforce.github.io/document/#using-soql for querying salesforce records} using SOQL query and handles pagination. * Note that in an event of a query error, error logs will be printed but the operation will not throw the error. * * The Salesforce query API is subject to rate limits, {@link https://sforce.co/3W9zyaQ See for more details}. From d2b8851f1ad67b99d378874ef1118647d26847ee Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 10 Dec 2024 15:06:54 +0300 Subject: [PATCH 19/20] remove callback --- .changeset/ten-mayflies-nail.md | 3 ++- packages/salesforce/ast.json | 19 +++---------------- packages/salesforce/src/Adaptor.js | 9 ++++----- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/.changeset/ten-mayflies-nail.md b/.changeset/ten-mayflies-nail.md index c8bcda42c..00e40c028 100644 --- a/.changeset/ten-mayflies-nail.md +++ b/.changeset/ten-mayflies-nail.md @@ -1,6 +1,7 @@ --- -'@openfn/language-salesforce': minor +'@openfn/language-salesforce': major --- - add `query` option in `request` function +- remove `callback` option in `query` function - update function examples and improve options documentation diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index 48abd46f5..bcccfadac 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -646,11 +646,10 @@ "name": "query", "params": [ "query", - "options", - "callback" + "options" ], "docs": { - "description": "Executes an SOQL (Salesforce Object Query Language) query to retrieve records from Salesforce.\nThis operation uses {@link https://jsforce.github.io/document/#using-soql for querying salesforce records} using SOQL query and handles pagination.\nNote that in an event of a query error, error logs will be printed but the operation will not throw the error.\n\nThe Salesforce query API is subject to rate limits, {@link https://sforce.co/3W9zyaQ See for more details}.", + "description": "Executes an SOQL (Salesforce Object Query Language) query to retrieve records from Salesforce.\nThis operation uses {@link https://jsforce.github.io/document/#using-soql for querying salesforce records} using SOQL query and handles pagination.\nNote that in an event of a query error, error logs will be printed but the operation will not throw the error.\n\nThe Salesforce query API is subject to rate limits, {@link https://sforce.co/3W9zyaQ Learn more here}.", "tags": [ { "title": "public", @@ -660,7 +659,7 @@ { "title": "example", "description": "query('SELECT Id FROM Patient__c', { autoFetch: true });", - "caption": "Query more records if next records are available" + "caption": "Run a query and download all matching records" }, { "title": "example", @@ -707,18 +706,6 @@ }, "name": "options" }, - { - "title": "param", - "description": "Optional callback function to execute for each retrieved record", - "type": { - "type": "OptionalType", - "expression": { - "type": "NameExpression", - "name": "function" - } - }, - "name": "callback" - }, { "title": "state", "description": "{SalesforceState}" diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index f246d5a0f..6bad34aa0 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -557,10 +557,10 @@ export function post(path, data, options = {}) { * This operation uses {@link https://jsforce.github.io/document/#using-soql for querying salesforce records} using SOQL query and handles pagination. * Note that in an event of a query error, error logs will be printed but the operation will not throw the error. * - * The Salesforce query API is subject to rate limits, {@link https://sforce.co/3W9zyaQ See for more details}. + * The Salesforce query API is subject to rate limits, {@link https://sforce.co/3W9zyaQ Learn more here}. * * @public - * @example Query more records if next records are available + * @example Run a query and download all matching records * query('SELECT Id FROM Patient__c', { autoFetch: true }); * @example Query patients by Health ID * query(state => `SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.healthId}'`); @@ -569,14 +569,13 @@ export function post(path, data, options = {}) { * @function * @param {(string|function)} query - A SOQL query string or a function that returns a query string. Must be less than 4000 characters in WHERE clause * @param {QueryOptions} [options] - Optional configuration for the query operation - * @param {function} [callback] - Optional callback function to execute for each retrieved record * @state {SalesforceState} * @state {boolean} data.done - Indicates whether the operation is complete. * @state {number} data.totalSize - The total number of items returned by the query. * @state {Object[]} data.records - The records returned by the query. * @returns {Operation} */ -export function query(query, options = {}, callback = s => s) { +export function query(query, options = {}) { return async state => { const { connection } = state; const [resolvedQuery, resolvedOptions] = expandReferences( @@ -641,7 +640,7 @@ export function query(query, options = {}, callback = s => s) { 'Results retrieved and pushed to position [0] of the references array.' ); - return callback(composeNextState(state, result)); + return composeNextState(state, result); }; } From d9bb89cf72a8ccffd61be97dc561b7b015e1d43f Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 10 Dec 2024 15:13:14 +0300 Subject: [PATCH 20/20] update upsert docs --- packages/salesforce/ast.json | 2 +- packages/salesforce/src/Adaptor.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index bcccfadac..e00212347 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -742,7 +742,7 @@ "records" ], "docs": { - "description": "Create a new sObject record, or updates it if it already exists\nExternal ID field name must be specified in second argument.", + "description": "Create a new sObject record, or updates it if it already exists.\nThis function uses {@link https://jsforce.github.io/document/#upsert|jsforce upsert} under the hood.", "tags": [ { "title": "public", diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 6bad34aa0..5daab3309 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -645,8 +645,8 @@ export function query(query, options = {}) { } /** - * Create a new sObject record, or updates it if it already exists - * External ID field name must be specified in second argument. + * Create a new sObject record, or updates it if it already exists. + * This function uses {@link https://jsforce.github.io/document/#upsert|jsforce upsert} under the hood. * @public * @example Single record upsert * upsert("UpsertTable__c", "ExtId__c", { Name: "Record #1", ExtId__c : 'ID-0000001' });