diff --git a/API_DOCS.md b/API_DOCS.md index 8b7d4c0..dbdf1df 100644 --- a/API_DOCS.md +++ b/API_DOCS.md @@ -176,20 +176,20 @@ Describes the shape and behaviour of the resources object you will pass to `getL #### `resources` Parameters -| Key | Value Description | -| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `isBatchResource` | Is this a batch resource? (Can you pass it a list of keys and get a list of results back?) | -| `docsLink` | The URL for the documentation of the resource. Useful for others to verify information is correct, and may be used in stack traces. | -| `batchKey` | The argument to the resource that represents the list of entities we want to fetch. (e.g. 'user_ids') | -| `newKey` | The argument we'll replace the batchKey with - should be a singular version of the `batchKey` (e.g. 'user_id') | -| `reorderResultsByKey` | (Optional) If the resource itself does not guarantee ordering, use this to specify which key in the response objects corresponds to an element in `batchKey`. Transforms and re-order the response to the same order as requested from the DataLoaders. | -| `nestedPath` | (Optional) If the resource returns the list of results in a nested path (e.g. `{ results: [ 1, 2, 3 ] }`), this tells the DataLoader where in the response to find the results. (e.g. 'results'). | -| `commaSeparatedBatchKey` | (Optional) Set to true if the interface of the resource takes the batch key as a comma separated list (rather than an array of IDs, as is more common). Default: false | -| `isResponseDictionary` | (Optional) Set to true if the batch resource returns the results as a dictionary with key mapped to values (instead of a list of items). If this option is supplied `reorderResultsByKey` should not be. Default: false | -| `isBatchKeyASet` | (Optional) Set to true if the interface of the resource takes the batch key as a set (rather than an array). For example, when using a generated clientlib based on swagger where `uniqueItems: true` is set for the batchKey parameter. Default: false. | -| `propertyBatchKey` | (Optional) The argument to the resource that represents the optional properties we want to fetch. (e.g. usually 'properties' or 'features'). | -| `maxBatchSize` | (Optional) Limits the number of items that can be batched together in a single request. When more items are requested than this limit, multiple requests will be made. This can help prevent hitting URI length limits or timeouts for large batches. | -| `responseKey` | (Non-optional when propertyBatchKey is used) The key in the response objects corresponds to `batchKey`. This should be the only field that are marked as required in your swagger endpoint response, except nestedPath. | +| Key | Value Description | +| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `isBatchResource` | Is this a batch resource? (Can you pass it a list of keys and get a list of results back?) | +| `docsLink` | The URL for the documentation of the resource. Useful for others to verify information is correct, and may be used in stack traces. | +| `batchKey` | The argument to the resource that represents the list of entities we want to fetch. (e.g. 'user_ids') | +| `newKey` | The argument we'll replace the batchKey with - should be a singular version of the `batchKey` (e.g. 'user_id') | +| `reorderResultsByKey` | (Optional) If the resource itself does not guarantee ordering, use this to specify which key in the response objects corresponds to an element in `batchKey`. Transforms and re-order the response to the same order as requested from the DataLoaders. Can be a dot separated nested path. | +| `nestedPath` | (Optional) If the resource returns the list of results in a nested path (e.g. `{ results: [ 1, 2, 3 ] }`), this tells the DataLoader where in the response to find the results. (e.g. 'results'). | +| `commaSeparatedBatchKey` | (Optional) Set to true if the interface of the resource takes the batch key as a comma separated list (rather than an array of IDs, as is more common). Default: false | +| `isResponseDictionary` | (Optional) Set to true if the batch resource returns the results as a dictionary with key mapped to values (instead of a list of items). If this option is supplied `reorderResultsByKey` should not be. Default: false | +| `isBatchKeyASet` | (Optional) Set to true if the interface of the resource takes the batch key as a set (rather than an array). For example, when using a generated clientlib based on swagger where `uniqueItems: true` is set for the batchKey parameter. Default: false. | +| `propertyBatchKey` | (Optional) The argument to the resource that represents the optional properties we want to fetch. (e.g. usually 'properties' or 'features'). | +| `maxBatchSize` | (Optional) Limits the number of items that can be batched together in a single request. When more items are requested than this limit, multiple requests will be made. This can help prevent hitting URI length limits or timeouts for large batches. | +| `responseKey` | (Non-optional when propertyBatchKey is used) The key in the response objects corresponds to `batchKey`. This should be the only field that are marked as required in your swagger endpoint response, except nestedPath. | ### `typings` diff --git a/__tests__/implementation.test.js b/__tests__/implementation.test.js index b17b9f2..9a7dd44 100644 --- a/__tests__/implementation.test.js +++ b/__tests__/implementation.test.js @@ -196,6 +196,42 @@ test('batch endpoint (with reorderResultsByKey)', async () => { }); }); +test('batch endpoint (with nested reorderResultsByKey path)', async () => { + const config = { + resources: { + foo: { + isBatchResource: true, + docsLink: 'example.com/docs/bar', + batchKey: 'foo_ids', + newKey: 'foo_id', + reorderResultsByKey: 'nested.foo_id', + }, + }, + }; + + const resources = { + foo: ({ foo_ids }) => { + expect(foo_ids).toEqual([1, 2, 3]); + return Promise.resolve([ + { nested: { foo_id: 2, foo_value: 'world' } }, + { nested: { foo_id: 1, foo_value: 'hello' } }, + { nested: { foo_id: 3, foo_value: '!' } }, + ]); + }, + }; + + await createDataLoaders(config, async (getLoaders) => { + const loaders = getLoaders(resources); + + const results = await loaders.foo.loadMany([{ foo_id: 1 }, { foo_id: 2 }, { foo_id: 3 }]); + expect(results).toEqual([ + { nested: { foo_id: 1, foo_value: 'hello' } }, + { nested: { foo_id: 2, foo_value: 'world' } }, + { nested: { foo_id: 3, foo_value: '!' } }, + ]); + }); +}); + test('batch endpoint (with nestedPath)', async () => { const config = { resources: { diff --git a/schema.json b/schema.json index 54cdfa1..401fb1f 100644 --- a/schema.json +++ b/schema.json @@ -80,7 +80,7 @@ }, "reorderResultsByKey": { "type": "string", - "description": "(Optional) If the resource itself does not guarantee ordering, this key specifies which key in the response objects corresponds to an element in `batchKey`. We use this to transfrom and re-order the response to the same order as in batchKey." + "description": "(Optional) If the resource itself does not guarantee ordering, this key specifies which key in the response objects corresponds to an element in `batchKey`. We use this to transfrom and re-order the response to the same order as in batchKey. Can be a dot separated nested path." }, "nestedPath": { "type": "string", diff --git a/src/runtimeHelpers.ts b/src/runtimeHelpers.ts index 2cd4dfb..7ab7aea 100644 --- a/src/runtimeHelpers.ts +++ b/src/runtimeHelpers.ts @@ -169,7 +169,7 @@ export function sortByKeys({ items, /** The IDs we originally requested from the endpoint */ keys, - /** The attribute of each element in `items` that maps it to an element in `keys`. */ + /** The attribute of each element in `items` that maps it to an element in `keys`. Can be a dot-separated nested path. */ prop, /** Some path that indicates what resource this is being used on. Used for stack traces. */ resourcePath, @@ -212,10 +212,9 @@ export function sortByKeys({ itemsMap.set(String(reorderResultsByValue), item); } else { - // @ts-ignore: TODO: Work how to tell typescript item[prop] exists - invariant(item[prop] != null, `${errorPrefix(resourcePath)} Could not find property "${prop}" in item`); - // @ts-ignore: TODO: Work how to tell typescript item[prop] exists - itemsMap.set(String(item[prop]), item); + const sortKey = _.get(item, prop); + invariant(sortKey != null, `${errorPrefix(resourcePath)} Could not find property "${prop}" in item`); + itemsMap.set(String(sortKey), item); } });