From e5276d73e7f1b25cb2db0612a29fcadc34634190 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 23 Jul 2024 16:48:24 +0300 Subject: [PATCH 01/18] add result to reference[0] --- packages/salesforce/src/Adaptor.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 77cf09a10..97426c496 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -164,7 +164,7 @@ export function retrieve(sObject, id, callback) { * @param {function} callback - A callback to execute once the record is retrieved * @returns {Operation} */ -export function query(qs, options, callback = s => s) { +export function query(qs, options = {}, callback = s => s) { return async state => { let done = false; let qResult = null; @@ -176,7 +176,7 @@ export function query(qs, options, callback = s => s) { qs, options ); - const { autoFetch } = { ...{ autoFetch: false }, ...resolvedOptions }; + const { autoFetch = false } = resolvedOptions; console.log(`Executing query: ${resolvedQs}`); try { @@ -228,7 +228,7 @@ export function query(qs, options, callback = s => s) { const nextState = { ...state, - references: [result, ...state.references], + references: [...result, ...state.references], }; return callback(nextState); }; From 813e85be3811e363701d27d11571fdef02d3420e Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Mon, 29 Jul 2024 10:42:38 +0300 Subject: [PATCH 02/18] improved autoFetch --- packages/salesforce/src/Adaptor.js | 53 ++++++++++++++---------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 97426c496..682acdb85 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -166,9 +166,9 @@ export function retrieve(sObject, id, callback) { */ export function query(qs, options = {}, callback = s => s) { return async state => { - let done = false; - let qResult = null; let result = []; + let qResult = null; + let fetchedRecords = []; const { connection } = state; const [resolvedQs, resolvedOptions] = newExpandReferences( @@ -187,36 +187,31 @@ export function query(qs, options = {}, callback = s => s) { throw err; } - if (qResult.totalSize > 0) { - console.log('Total records', qResult.totalSize); - - while (!done) { - result.push(qResult); - - if (qResult.done) { - done = true; - } else if (autoFetch) { - console.log( - 'Fetched records so far', - result.map(ref => ref.records).flat().length - ); - console.log('Fetching next records...'); - try { - qResult = await connection.request({ url: qResult.nextRecordsUrl }); - } catch (err) { - const { message, errorCode } = err; - console.log(`Error ${errorCode}: ${message}`); - throw err; - } - } else { - done = true; + const processRecords = async qResult => { + const { done, totalSize, records, nextRecordsUrl } = qResult; + + fetchedRecords.push(...records); + result = [{ done, totalSize, nextRecordsUrl, records: fetchedRecords }]; + + if (!qResult.done && autoFetch) { + console.log('Fetched records so far:', fetchedRecords.length); + console.log('Fetching next records...'); + + try { + qResult = await connection.request({ url: qResult.nextRecordsUrl }); + await processRecords(qResult); + } catch (err) { + const { message, errorCode } = err; + console.error(`Error ${errorCode}: ${message}`); + throw err; } } + }; - console.log( - 'Done ✔ retrieved records', - result.map(ref => ref.records).flat().length - ); + if (qResult.totalSize > 0) { + console.log('Total records:', qResult.totalSize); + await processRecords(qResult); + console.log('Done ✔ retrieved records:', fetchedRecords.length); } else { result.push(qResult); console.log('No records found.'); From 16d017f6b5a940889c0c1aa77d773f80bc9e2068 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Mon, 29 Jul 2024 11:11:09 +0300 Subject: [PATCH 03/18] add unit test --- packages/salesforce/src/Adaptor.js | 9 ++- packages/salesforce/test/Adaptor.test.js | 83 ++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 682acdb85..204344634 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -191,7 +191,14 @@ export function query(qs, options = {}, callback = s => s) { const { done, totalSize, records, nextRecordsUrl } = qResult; fetchedRecords.push(...records); - result = [{ done, totalSize, nextRecordsUrl, records: fetchedRecords }]; + result = [ + { + done, + totalSize, + records: fetchedRecords, + ...(nextRecordsUrl && { nextRecordsUrl }), + }, + ]; if (!qResult.done && autoFetch) { console.log('Fetched records so far:', fetchedRecords.length); diff --git a/packages/salesforce/test/Adaptor.test.js b/packages/salesforce/test/Adaptor.test.js index 46120a1cf..67ea70837 100644 --- a/packages/salesforce/test/Adaptor.test.js +++ b/packages/salesforce/test/Adaptor.test.js @@ -8,6 +8,7 @@ import { upsertIf, toUTF8, execute, + query, } from '../src/Adaptor'; const { expect } = chai; @@ -194,4 +195,86 @@ describe('Adaptor', () => { expect(result).to.eql('Nhamaonha 6a Classe 2023-10-09'); }); }); + + describe('query', () => { + it('should fetch all records', done => { + const fakeConnection = { + query: function () { + return Promise.resolve({ + done: true, + totalSize: 1, + records: [{ Name: 'OpenFn' }], + }); + }, + }; + let state = { connection: fakeConnection, references: [] }; + let spy = sinon.spy(fakeConnection, 'query'); + + query('select Name from Account', { autoFetch: true })(state) + .then(state => { + expect(spy.called).to.eql(true); + expect(state.references[0]).to.eql({ + done: true, + totalSize: 1, + records: [{ Name: 'OpenFn' }], + }); + }) + .then(done) + .catch(done); + }); + it('should fetch all records if autoFetch is true and totalSize > 2000', done => { + const fakeConnection = { + query: function () { + return Promise.resolve({ + done: true, + totalSize: 5713, + records: [{ Name: 'OpenFn' }], + }); + }, + }; + let state = { connection: fakeConnection, references: [] }; + let spy = sinon.spy(fakeConnection, 'query'); + + query('select Name from Account', { autoFetch: true })(state) + .then(state => { + expect(spy.called).to.eql(true); + expect(state.references[0]).to.eql({ + done: true, + totalSize: 5713, + records: [{ Name: 'OpenFn' }], + }); + }) + .then(done) + .catch(done); + }); + it('should return nextRecordsUrl if autoFetch is false and totalSize > 2000', done => { + const fakeConnection = { + query: function () { + return Promise.resolve({ + done: true, + totalSize: 5713, + nextRecordsUrl: + '/services/data/v47.0/query/0r8yy3Dlrs3Ol9EACO-2000', + records: [{ Name: 'OpenFn' }], + }); + }, + }; + let state = { connection: fakeConnection, references: [] }; + let spy = sinon.spy(fakeConnection, 'query'); + + query('select Name from Account', { autoFetch: true })(state) + .then(state => { + expect(spy.called).to.eql(true); + expect(state.references[0]).to.eql({ + done: true, + totalSize: 5713, + nextRecordsUrl: + '/services/data/v47.0/query/0r8yy3Dlrs3Ol9EACO-2000', + records: [{ Name: 'OpenFn' }], + }); + }) + .then(done) + .catch(done); + }); + }); }); From ce08e7f060039e14a6b21fb856173aa90031825b Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Mon, 29 Jul 2024 11:15:21 +0300 Subject: [PATCH 04/18] add changeset --- .changeset/ten-crews-complain.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/ten-crews-complain.md diff --git a/.changeset/ten-crews-complain.md b/.changeset/ten-crews-complain.md new file mode 100644 index 000000000..33fc46eb6 --- /dev/null +++ b/.changeset/ten-crews-complain.md @@ -0,0 +1,8 @@ +--- +'@openfn/language-salesforce': major +--- + +Fix `autoFetch` in `query()` function + +- All records are merged in a single`records` array +- Results are pushed to `[0]` in `state.references` From 728de2b5326ddc6e56cc0e2fa729b51ee41a2d93 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Mon, 29 Jul 2024 11:20:49 +0300 Subject: [PATCH 05/18] fix eslint error --- packages/salesforce/src/Adaptor.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 204344634..a398d0eb1 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -200,13 +200,13 @@ export function query(qs, options = {}, callback = s => s) { }, ]; - if (!qResult.done && autoFetch) { + if (!done && autoFetch) { console.log('Fetched records so far:', fetchedRecords.length); console.log('Fetching next records...'); try { - qResult = await connection.request({ url: qResult.nextRecordsUrl }); - await processRecords(qResult); + const newResult = await connection.request({ url: nextRecordsUrl }); + await processRecords(newResult); } catch (err) { const { message, errorCode } = err; console.error(`Error ${errorCode}: ${message}`); From 4811a2f49006a418bc11dffb97f603c1636dc4a8 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 30 Jul 2024 11:25:17 +0300 Subject: [PATCH 06/18] improve tests --- packages/salesforce/test/Adaptor.test.js | 27 +++++++++++++++++------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/salesforce/test/Adaptor.test.js b/packages/salesforce/test/Adaptor.test.js index 67ea70837..a966ee98e 100644 --- a/packages/salesforce/test/Adaptor.test.js +++ b/packages/salesforce/test/Adaptor.test.js @@ -210,7 +210,7 @@ describe('Adaptor', () => { let state = { connection: fakeConnection, references: [] }; let spy = sinon.spy(fakeConnection, 'query'); - query('select Name from Account', { autoFetch: true })(state) + query('select Name from Account')(state) .then(state => { expect(spy.called).to.eql(true); expect(state.references[0]).to.eql({ @@ -222,36 +222,47 @@ describe('Adaptor', () => { .then(done) .catch(done); }); - it('should fetch all records if autoFetch is true and totalSize > 2000', done => { + it('should fetch all page if autoFetch is true', done => { const fakeConnection = { query: function () { + return Promise.resolve({ + done: false, + totalSize: 5713, + nextRecordsUrl: + '/services/data/v47.0/query/0r8yy3Dlrs3Ol9EACO-2000', + records: [{ Name: 'Open' }], + }); + }, + request: function () { return Promise.resolve({ done: true, totalSize: 5713, - records: [{ Name: 'OpenFn' }], + records: [{ Name: 'Fn' }], }); }, }; let state = { connection: fakeConnection, references: [] }; let spy = sinon.spy(fakeConnection, 'query'); + let spyReq = sinon.spy(fakeConnection, 'request'); query('select Name from Account', { autoFetch: true })(state) .then(state => { expect(spy.called).to.eql(true); + expect(spyReq.called).to.eql(true); expect(state.references[0]).to.eql({ done: true, totalSize: 5713, - records: [{ Name: 'OpenFn' }], + records: [{ Name: 'Open' }, { Name: 'Fn' }], }); }) .then(done) .catch(done); }); - it('should return nextRecordsUrl if autoFetch is false and totalSize > 2000', done => { + it('should not fetch another page if autofetch is false', done => { const fakeConnection = { query: function () { return Promise.resolve({ - done: true, + done: false, totalSize: 5713, nextRecordsUrl: '/services/data/v47.0/query/0r8yy3Dlrs3Ol9EACO-2000', @@ -262,11 +273,11 @@ describe('Adaptor', () => { let state = { connection: fakeConnection, references: [] }; let spy = sinon.spy(fakeConnection, 'query'); - query('select Name from Account', { autoFetch: true })(state) + query('select Name from Account')(state) .then(state => { expect(spy.called).to.eql(true); expect(state.references[0]).to.eql({ - done: true, + done: false, totalSize: 5713, nextRecordsUrl: '/services/data/v47.0/query/0r8yy3Dlrs3Ol9EACO-2000', From 130491f7f68da51fe1a64b03ea5f0f5bcc4b84d2 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 30 Jul 2024 11:35:12 +0300 Subject: [PATCH 07/18] check if query string is properly passed --- packages/salesforce/test/Adaptor.test.js | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/salesforce/test/Adaptor.test.js b/packages/salesforce/test/Adaptor.test.js index a966ee98e..6dea91adc 100644 --- a/packages/salesforce/test/Adaptor.test.js +++ b/packages/salesforce/test/Adaptor.test.js @@ -197,6 +197,33 @@ describe('Adaptor', () => { }); describe('query', () => { + it('should properly pass the query string', async () => { + const fakeConnection = { + query: function () { + return Promise.resolve({ + done: true, + totalSize: 1, + records: [{ Name: 'OpenFn' }], + }); + }, + }; + + let state = { + connection: fakeConnection, + references: [], + qs: 'select Name from Account', + }; + let spy = sinon.spy(fakeConnection, 'query'); + + query('select Name from Account')(state).then(state => { + expect(spy.calledWith('select Name from Account')).to.eql(true); + }); + + query(state => state.qs)(state).then(state => { + expect(spy.calledWith('select Name from Account')).to.eql(true); + }); + }); + it('should fetch all records', done => { const fakeConnection = { query: function () { From 474fff4bff08b808717fffa6c99ca54f5780ba64 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Tue, 30 Jul 2024 11:54:00 +0300 Subject: [PATCH 08/18] update test description --- packages/salesforce/test/Adaptor.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/salesforce/test/Adaptor.test.js b/packages/salesforce/test/Adaptor.test.js index 6dea91adc..2653b599d 100644 --- a/packages/salesforce/test/Adaptor.test.js +++ b/packages/salesforce/test/Adaptor.test.js @@ -224,7 +224,7 @@ describe('Adaptor', () => { }); }); - it('should fetch all records', done => { + it('should fetch up to 2000 records', done => { const fakeConnection = { query: function () { return Promise.resolve({ From 7c2679718ce477b30fc8d6b7939a2bf576cd8345 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Wed, 31 Jul 2024 11:27:49 +0300 Subject: [PATCH 09/18] improve query result --- packages/salesforce/src/Adaptor.js | 31 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index a398d0eb1..7a2b3f84e 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -166,9 +166,12 @@ export function retrieve(sObject, id, callback) { */ export function query(qs, options = {}, callback = s => s) { return async state => { - let result = []; let qResult = null; - let fetchedRecords = []; + let result = { + done: true, + totalSize: 0, + records: [], + }; const { connection } = state; const [resolvedQs, resolvedOptions] = newExpandReferences( @@ -190,18 +193,16 @@ export function query(qs, options = {}, callback = s => s) { const processRecords = async qResult => { const { done, totalSize, records, nextRecordsUrl } = qResult; - fetchedRecords.push(...records); - result = [ - { - done, - totalSize, - records: fetchedRecords, - ...(nextRecordsUrl && { nextRecordsUrl }), - }, - ]; + result.done = done; + result.totalSize = totalSize; + result.records.push(...records); + + if (!done && nextRecordsUrl) { + result.nextRecordsUrl = nextRecordsUrl; + } if (!done && autoFetch) { - console.log('Fetched records so far:', fetchedRecords.length); + console.log('Fetched records so far:', result.records.length); console.log('Fetching next records...'); try { @@ -218,9 +219,9 @@ export function query(qs, options = {}, callback = s => s) { if (qResult.totalSize > 0) { console.log('Total records:', qResult.totalSize); await processRecords(qResult); - console.log('Done ✔ retrieved records:', fetchedRecords.length); + console.log('Done ✔ retrieved records:', result.records.length); } else { - result.push(qResult); + result = qResult; console.log('No records found.'); } @@ -230,7 +231,7 @@ export function query(qs, options = {}, callback = s => s) { const nextState = { ...state, - references: [...result, ...state.references], + references: [result, ...state.references], }; return callback(nextState); }; From 65f346d7f52ec9d359cbbd83cc67e64ff96a4b31 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Wed, 31 Jul 2024 12:19:33 +0300 Subject: [PATCH 10/18] remove .catch(done) and fix broken test --- packages/salesforce/src/Adaptor.js | 3 +-- packages/salesforce/test/Adaptor.test.js | 28 ++++++++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 7a2b3f84e..67d4e16bf 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -197,10 +197,9 @@ export function query(qs, options = {}, callback = s => s) { result.totalSize = totalSize; result.records.push(...records); - if (!done && nextRecordsUrl) { + if (!done && !autoFetch && nextRecordsUrl) { result.nextRecordsUrl = nextRecordsUrl; } - if (!done && autoFetch) { console.log('Fetched records so far:', result.records.length); console.log('Fetching next records...'); diff --git a/packages/salesforce/test/Adaptor.test.js b/packages/salesforce/test/Adaptor.test.js index 2653b599d..8c6037851 100644 --- a/packages/salesforce/test/Adaptor.test.js +++ b/packages/salesforce/test/Adaptor.test.js @@ -211,13 +211,30 @@ describe('Adaptor', () => { let state = { connection: fakeConnection, references: [], - qs: 'select Name from Account', }; let spy = sinon.spy(fakeConnection, 'query'); query('select Name from Account')(state).then(state => { expect(spy.calledWith('select Name from Account')).to.eql(true); }); + }); + it('should properly pass the query string from state', async () => { + const fakeConnection = { + query: function () { + return Promise.resolve({ + done: true, + totalSize: 1, + records: [{ Name: 'OpenFn' }], + }); + }, + }; + + let state = { + connection: fakeConnection, + references: [], + qs: 'select Name from Account', + }; + let spy = sinon.spy(fakeConnection, 'query'); query(state => state.qs)(state).then(state => { expect(spy.calledWith('select Name from Account')).to.eql(true); @@ -246,8 +263,7 @@ describe('Adaptor', () => { records: [{ Name: 'OpenFn' }], }); }) - .then(done) - .catch(done); + .then(done); }); it('should fetch all page if autoFetch is true', done => { const fakeConnection = { @@ -282,8 +298,7 @@ describe('Adaptor', () => { records: [{ Name: 'Open' }, { Name: 'Fn' }], }); }) - .then(done) - .catch(done); + .then(done); }); it('should not fetch another page if autofetch is false', done => { const fakeConnection = { @@ -311,8 +326,7 @@ describe('Adaptor', () => { records: [{ Name: 'OpenFn' }], }); }) - .then(done) - .catch(done); + .then(done); }); }); }); From e44b6cc5c24962ec2a531cf2bc12d1b4556ce49e Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Wed, 31 Jul 2024 13:03:23 +0300 Subject: [PATCH 11/18] add log if autofetch is enabled --- packages/salesforce/src/Adaptor.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 67d4e16bf..dfc40d62c 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -179,7 +179,13 @@ export function query(qs, options = {}, callback = s => s) { qs, options ); - const { autoFetch = false } = resolvedOptions; + + const { autoFetch: autoFetchOption, autofetch: autoFetchOptionAlias } = + resolvedOptions; + const autoFetch = autoFetchOption || autoFetchOptionAlias || false; + if (autoFetch) { + console.log('AutoFetch is enabled'); + } console.log(`Executing query: ${resolvedQs}`); try { From c4775925439fe080ceeffaf773bb3e1e0fc98565 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Wed, 31 Jul 2024 14:37:06 +0300 Subject: [PATCH 12/18] improve autofetch variable --- packages/salesforce/ast.json | 178 ++++++++++++----------- packages/salesforce/src/Adaptor.js | 5 +- packages/salesforce/test/Adaptor.test.js | 2 +- 3 files changed, 97 insertions(+), 88 deletions(-) diff --git a/packages/salesforce/ast.json b/packages/salesforce/ast.json index 19f0db47c..73e1dc224 100644 --- a/packages/salesforce/ast.json +++ b/packages/salesforce/ast.json @@ -1132,15 +1132,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "fn(state => {\n // do some things to state\n return state;\n});" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "fn(state => {\n // do some things to state\n return state;\n});" + }, { "title": "param", "description": "is the function", @@ -1176,15 +1176,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "fnIf((state) => state?.data?.name, get(\"https://example.com\"));" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "fnIf((state) => state?.data?.name, get(\"https://example.com\"));" + }, { "title": "param", "description": "The condition that returns true", @@ -1229,15 +1229,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "jsonValue({ a:1 }, 'a')" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "jsonValue({ a:1 }, 'a')" + }, { "title": "param", "description": "A valid JSON object.", @@ -1281,15 +1281,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "sourceValue('$.key')" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "sourceValue('$.key')" + }, { "title": "param", "description": "JSONPath referencing a point in `state`.", @@ -1324,15 +1324,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "source('$.key')" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "source('$.key')" + }, { "title": "param", "description": "JSONPath referencing a point in `state`.", @@ -1385,15 +1385,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "dataPath('key')" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "dataPath('key')" + }, { "title": "param", "description": "JSONPath referencing a point in `data`.", @@ -1428,15 +1428,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "dataValue('key')" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "dataValue('key')" + }, { "title": "param", "description": "JSONPath referencing a point in `data`.", @@ -1471,15 +1471,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "referencePath('key')" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "referencePath('key')" + }, { "title": "param", "description": "JSONPath referencing a point in `references`.", @@ -1514,15 +1514,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "lastReferenceValue('key')" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "lastReferenceValue('key')" + }, { "title": "param", "description": "JSONPath referencing a point in `references`.", @@ -1558,15 +1558,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "each(\"$.[*]\",\n create(\"SObject\",\n field(\"FirstName\", sourceValue(\"$.firstName\"))\n )\n)" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "each(\"$.[*]\",\n create(\"SObject\",\n field(\"FirstName\", sourceValue(\"$.firstName\"))\n )\n)" + }, { "title": "param", "description": "JSONPath referencing a point in `state`.", @@ -1610,15 +1610,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "combine(\n create('foo'),\n delete('bar')\n)" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "combine(\n create('foo'),\n delete('bar')\n)" + }, { "title": "param", "description": "Operations to be performed.", @@ -1655,15 +1655,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "join('$.key','$.data','newKey')" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "join('$.key','$.data','newKey')" + }, { "title": "param", "description": "Target path", @@ -1769,15 +1769,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "field('destination_field_name__c', 'value')" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "field('destination_field_name__c', 'value')" + }, { "title": "param", "description": "Name of the field", @@ -1821,15 +1821,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "fields(list_of_fields)" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "fields(list_of_fields)" + }, { "title": "param", "description": "a list of fields", @@ -1874,6 +1874,11 @@ "description": null, "name": null }, + { + "title": "public", + "description": null, + "type": null + }, { "title": "param", "description": null, @@ -1928,6 +1933,11 @@ "description": null, "name": null }, + { + "title": "public", + "description": null, + "type": null + }, { "title": "param", "description": "The array of objects to be grouped.", @@ -1987,15 +1997,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "index()" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "index()" + }, { "title": "returns", "description": null, @@ -2022,15 +2032,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "field(\"destination_string__c\", function(state) {\n return arrayToString(dataValue(\"path_of_array\")(state), ', ')\n})" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "field(\"destination_string__c\", function(state) {\n return arrayToString(dataValue(\"path_of_array\")(state), ', ')\n})" + }, { "title": "param", "description": "Array of toString'able primatives.", @@ -2074,15 +2084,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "each(function(state) {\n return toArray( dataValue(\"path_of_array\")(state) )\n}, ...)" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "each(function(state) {\n return toArray( dataValue(\"path_of_array\")(state) )\n}, ...)" + }, { "title": "param", "description": "Data required to be in an array", @@ -2117,15 +2127,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "field(\"destination_string__c\", humanProper(state.data.path_to_string))" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "field(\"destination_string__c\", humanProper(state.data.path_to_string))" + }, { "title": "param", "description": "String that needs converting", @@ -2161,15 +2171,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "scrubEmojis('Dove🕊️⭐ 29')" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "scrubEmojis('Dove🕊️⭐ 29')" + }, { "title": "param", "description": "String that needs to be cleaned", @@ -2214,15 +2224,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "chunk([1,2,3,4,5], 2)" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "chunk([1,2,3,4,5], 2)" + }, { "title": "param", "description": "Array to be chunked", @@ -2268,15 +2278,15 @@ "description": null, "type": null }, - { - "title": "example", - "description": "map(\"$.[*]\",\n create(\"SObject\",\n field(\"FirstName\", sourceValue(\"$.firstName\"))\n )\n)" - }, { "title": "function", "description": null, "name": null }, + { + "title": "example", + "description": "map(\"$.[*]\",\n create(\"SObject\",\n field(\"FirstName\", sourceValue(\"$.firstName\"))\n )\n)" + }, { "title": "param", "description": "JSONPath referencing a point in `state.data`.", diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index dfc40d62c..0039ee70f 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -180,9 +180,8 @@ export function query(qs, options = {}, callback = s => s) { options ); - const { autoFetch: autoFetchOption, autofetch: autoFetchOptionAlias } = - resolvedOptions; - const autoFetch = autoFetchOption || autoFetchOptionAlias || false; + const autoFetch = resolvedOptions.autoFetch || resolvedOptions.autofetch; + if (autoFetch) { console.log('AutoFetch is enabled'); } diff --git a/packages/salesforce/test/Adaptor.test.js b/packages/salesforce/test/Adaptor.test.js index 8c6037851..bb43e8061 100644 --- a/packages/salesforce/test/Adaptor.test.js +++ b/packages/salesforce/test/Adaptor.test.js @@ -241,7 +241,7 @@ describe('Adaptor', () => { }); }); - it('should fetch up to 2000 records', done => { + it('should fetch 1 record', done => { const fakeConnection = { query: function () { return Promise.resolve({ From ff05f887ed53aba9702687961fc72258de111792 Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Wed, 31 Jul 2024 14:41:45 +0300 Subject: [PATCH 13/18] add request mock --- packages/salesforce/test/Adaptor.test.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/salesforce/test/Adaptor.test.js b/packages/salesforce/test/Adaptor.test.js index bb43e8061..87d9701d1 100644 --- a/packages/salesforce/test/Adaptor.test.js +++ b/packages/salesforce/test/Adaptor.test.js @@ -308,22 +308,31 @@ describe('Adaptor', () => { totalSize: 5713, nextRecordsUrl: '/services/data/v47.0/query/0r8yy3Dlrs3Ol9EACO-2000', - records: [{ Name: 'OpenFn' }], + records: [{ Name: 'Open' }], + }); + }, + request: function () { + return Promise.resolve({ + done: true, + totalSize: 5713, + records: [{ Name: 'Fn' }], }); }, }; let state = { connection: fakeConnection, references: [] }; let spy = sinon.spy(fakeConnection, 'query'); + let spyReq = sinon.spy(fakeConnection, 'request'); query('select Name from Account')(state) .then(state => { expect(spy.called).to.eql(true); + expect(spyReq.called).to.eql(false); expect(state.references[0]).to.eql({ done: false, totalSize: 5713, nextRecordsUrl: '/services/data/v47.0/query/0r8yy3Dlrs3Ol9EACO-2000', - records: [{ Name: 'OpenFn' }], + records: [{ Name: 'Open' }], }); }) .then(done); From 813413a3e3d92fc1a124c6dc51721903367c4bad Mon Sep 17 00:00:00 2001 From: Emmanuel Evance Date: Wed, 31 Jul 2024 16:09:20 +0300 Subject: [PATCH 14/18] remove assignment for qResult --- packages/salesforce/src/Adaptor.js | 48 ++++++++++++++---------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index 0039ee70f..ddd201552 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -166,37 +166,26 @@ export function retrieve(sObject, id, callback) { */ export function query(qs, options = {}, callback = s => s) { return async state => { - let qResult = null; - let result = { - done: true, - totalSize: 0, - records: [], - }; - const { connection } = state; const [resolvedQs, resolvedOptions] = newExpandReferences( state, qs, options ); - + console.log(`Executing query: ${resolvedQs}`); const autoFetch = resolvedOptions.autoFetch || resolvedOptions.autofetch; if (autoFetch) { console.log('AutoFetch is enabled'); } - console.log(`Executing query: ${resolvedQs}`); - try { - qResult = await connection.query(resolvedQs); - } catch (err) { - const { message, errorCode } = err; - console.log(`Error ${errorCode}: ${message}`); - throw err; - } - - const processRecords = async qResult => { - const { done, totalSize, records, nextRecordsUrl } = qResult; + let result = { + done: true, + totalSize: 0, + records: [], + }; + const processRecords = async res => { + const { done, totalSize, records, nextRecordsUrl } = res; result.done = done; result.totalSize = totalSize; @@ -220,13 +209,20 @@ export function query(qs, options = {}, callback = s => s) { } }; - if (qResult.totalSize > 0) { - console.log('Total records:', qResult.totalSize); - await processRecords(qResult); - console.log('Done ✔ retrieved records:', result.records.length); - } else { - result = qResult; - console.log('No records found.'); + try { + const qResult = await connection.query(resolvedQs); + if (qResult.totalSize > 0) { + console.log('Total records:', qResult.totalSize); + await processRecords(qResult); + console.log('Done ✔ retrieved records:', result.records.length); + } else { + result = qResult; + console.log('No records found.'); + } + } catch (err) { + const { message, errorCode } = err; + console.log(`Error ${errorCode}: ${message}`); + throw err; } console.log( From 21ba57ca07a3e0e3a128470537580da5c64266f3 Mon Sep 17 00:00:00 2001 From: jclark Date: Wed, 31 Jul 2024 14:24:31 +0100 Subject: [PATCH 15/18] salesforce: add test for 0 records --- packages/salesforce/test/Adaptor.test.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/salesforce/test/Adaptor.test.js b/packages/salesforce/test/Adaptor.test.js index 87d9701d1..5c39b3c6c 100644 --- a/packages/salesforce/test/Adaptor.test.js +++ b/packages/salesforce/test/Adaptor.test.js @@ -241,6 +241,29 @@ describe('Adaptor', () => { }); }); + it('should fetch 0 records', done => { + const fakeConnection = { + query: function () { + return Promise.resolve({ + done: true, + totalSize: 0, + records: [], + }); + }, + }; + const state = { connection: fakeConnection, references: [] }; + + query('select Name from Account')(state) + .then(state => { + expect(state.references[0]).to.eql({ + done: true, + totalSize: 0, + records: [], + }); + }) + .then(done); + }); + it('should fetch 1 record', done => { const fakeConnection = { query: function () { From fcc93f09c846a4f7cb01fac527e857d843b9eedd Mon Sep 17 00:00:00 2001 From: jclark Date: Wed, 31 Jul 2024 14:26:31 +0100 Subject: [PATCH 16/18] salesforce: don't needlessly reassign result --- packages/salesforce/src/Adaptor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/salesforce/src/Adaptor.js b/packages/salesforce/src/Adaptor.js index ddd201552..225ba0cd8 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -179,11 +179,12 @@ export function query(qs, options = {}, callback = s => s) { console.log('AutoFetch is enabled'); } - let result = { + const result = { done: true, totalSize: 0, records: [], }; + const processRecords = async res => { const { done, totalSize, records, nextRecordsUrl } = res; @@ -216,7 +217,6 @@ export function query(qs, options = {}, callback = s => s) { await processRecords(qResult); console.log('Done ✔ retrieved records:', result.records.length); } else { - result = qResult; console.log('No records found.'); } } catch (err) { From 0f48998bdf7b7cd3b9101afcfebc73b98ebe8ec8 Mon Sep 17 00:00:00 2001 From: jclark Date: Wed, 31 Jul 2024 14:27:27 +0100 Subject: [PATCH 17/18] salesforce: tweak logging --- 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 225ba0cd8..eb79c37e8 100644 --- a/packages/salesforce/src/Adaptor.js +++ b/packages/salesforce/src/Adaptor.js @@ -176,7 +176,7 @@ export function query(qs, options = {}, callback = s => s) { const autoFetch = resolvedOptions.autoFetch || resolvedOptions.autofetch; if (autoFetch) { - console.log('AutoFetch is enabled'); + console.log('autoFetch is enabled: all records will be downloaded'); } const result = { From 01773ae73b51ed433e04ae21574a8954272dbae8 Mon Sep 17 00:00:00 2001 From: jclark Date: Wed, 31 Jul 2024 14:31:35 +0100 Subject: [PATCH 18/18] version: salesforce@4.8.1 --- .changeset/ten-crews-complain.md | 8 -------- packages/salesforce/CHANGELOG.md | 11 +++++++++++ packages/salesforce/package.json | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) delete mode 100644 .changeset/ten-crews-complain.md diff --git a/.changeset/ten-crews-complain.md b/.changeset/ten-crews-complain.md deleted file mode 100644 index 33fc46eb6..000000000 --- a/.changeset/ten-crews-complain.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@openfn/language-salesforce': major ---- - -Fix `autoFetch` in `query()` function - -- All records are merged in a single`records` array -- Results are pushed to `[0]` in `state.references` diff --git a/packages/salesforce/CHANGELOG.md b/packages/salesforce/CHANGELOG.md index 9e73b7178..7624bb288 100644 --- a/packages/salesforce/CHANGELOG.md +++ b/packages/salesforce/CHANGELOG.md @@ -1,5 +1,16 @@ # @openfn/language-salesforce +## 4.8.1 + +### Patch Changes + +- ce08e7f: Fix `autoFetch` behaviour in `query()` function. All records are + merged into a single `records` array, and pushed to `[0]` in + `state.references`. + + For jobs which use `references[0][0]` to read query results, this is a + breaking fix. + ## 4.8.0 ### Minor Changes diff --git a/packages/salesforce/package.json b/packages/salesforce/package.json index dff6475c5..95422cfbb 100644 --- a/packages/salesforce/package.json +++ b/packages/salesforce/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/language-salesforce", - "version": "4.8.0", + "version": "4.8.1", "description": "Salesforce Language Pack for OpenFn", "homepage": "https://docs.openfn.org", "exports": {