diff --git a/packages/collections/CHANGELOG.md b/packages/collections/CHANGELOG.md index 5bfaab445..335174dec 100644 --- a/packages/collections/CHANGELOG.md +++ b/packages/collections/CHANGELOG.md @@ -1,5 +1,20 @@ # @openfn/language-collections +## 0.7.0 + +### Minor Changes + +- c95ccba: BREAKING: Pass state into the keygen function on `set()`. This allows + state to be used to calculate keys. + + When calling `collections.set(collection, keygen, values)`, the keygen + function signature has changed from `(value, index) => key` to + `(value, state, index) => key`. + +### Patch Changes + +- cdb01db: Better error handling if the keygen function is invalid + ## 0.6.2 ### Patch Changes diff --git a/packages/collections/package.json b/packages/collections/package.json index dfb4d03d0..896d12e86 100644 --- a/packages/collections/package.json +++ b/packages/collections/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/language-collections", - "version": "0.6.2", + "version": "0.7.0", "description": "OpenFn collections adaptor", "type": "module", "exports": { diff --git a/packages/collections/src/collections.js b/packages/collections/src/collections.js index 63f99e41d..79f580d57 100644 --- a/packages/collections/src/collections.js +++ b/packages/collections/src/collections.js @@ -173,10 +173,20 @@ export function set(name, keyGen, values) { // Otherwise we convert the incoming values into an array of key/value pairs // Note that we may need to serialize json to string // the hardest bit is knowing when to deserialize - kvPairs = dataArray.map((value, index) => ({ - key: keyGenFn(value, index), - value: JSON.stringify(value), - })); + kvPairs = dataArray.map((value, index) => { + const key = keyGenFn(value, state, index); + if (typeof key !== 'string') { + const e = new Error('KEYGEN_ERROR'); + e.description = + 'The key generator function returned a non-string value which is not a valid key'; + e.fix = `The second argument to set() is a key or key-generator function. If you pass a function, make sure the function returns a string (a key). Note that this function is NOT a state reference and does not support lazy state ($)`; + throw e; + } + return { + key, + value: JSON.stringify(value), + }; + }); } while (kvPairs.length) { diff --git a/packages/collections/test/Adaptor.test.js b/packages/collections/test/Adaptor.test.js index 6f8f16496..16fd26f7b 100644 --- a/packages/collections/test/Adaptor.test.js +++ b/packages/collections/test/Adaptor.test.js @@ -427,6 +427,32 @@ describe('set', () => { expect(err.message).to.eql('ILLEGAL_ARGUMENTS'); }); + it("should throw keygen doesn't return a string", async () => { + const { state } = init(); + state.configuration = {}; + + let err; + try { + await collections.set(COLLECTION, () => true, { x: 1 })(state); + } catch (e) { + err = e; + } + expect(err.message).to.eql('KEYGEN_ERROR'); + }); + + it('should throw keygen looks like a state reference', async () => { + const { state } = init(); + state.configuration = {}; + + let err; + try { + await collections.set(COLLECTION, state => state, { x: 1 })(state); + } catch (e) { + err = e; + } + expect(err.message).to.eql('KEYGEN_ERROR'); + }); + it('should set a single item', async () => { const { state } = init(); @@ -463,6 +489,18 @@ describe('set', () => { expect(result).to.eql(item); }); + it('should pass state to the keygen function', async () => { + const { state } = init(); + state.key = 'x'; + + const item = { id: 'x' }; + + await collections.set(COLLECTION, (_key, state) => state.key, item)(state); + + const result = api.asJSON(COLLECTION, 'x'); + expect(result).to.eql(item); + }); + it('should set a single array-type item with a reference', async () => { const { state } = init();