diff --git a/api-reference.md b/api-reference.md index 65a09ad..8e8b5ae 100644 --- a/api-reference.md +++ b/api-reference.md @@ -67,6 +67,7 @@ with GraphQL endpoint, GraphQL serviceURL and auth if needed * [.listPersistedQueries([options], [retryOptions])](#AEMHeadless+listPersistedQueries) ⇒ Promise.<any> * [.runPersistedQuery(path, [variables], [options], [retryOptions])](#AEMHeadless+runPersistedQuery) ⇒ Promise.<any> * [.runPaginatedQuery(model, fields, [config], [args], [options], [retryOptions])](#AEMHeadless+runPaginatedQuery) + * [.runModelQuery(model, fields, [config], [args], [options], [retryOptions])](#AEMHeadless+runModelQuery) ⇒ Promise.<any> * [.buildQuery(model, fields, [config], [args])](#AEMHeadless+buildQuery) ⇒ [QueryBuilderResult](#QueryBuilderResult) @@ -246,6 +247,41 @@ Returns a Generator Function. + + +### aemHeadless.runModelQuery(model, fields, [config], [args], [options], [retryOptions]) ⇒ Promise.<any> +Returns a Promise that resolves with a filtered POST request JSON data. + +**Kind**: instance method of [AEMHeadless](#AEMHeadless) +**Returns**: Promise.<any> - - the response data wrapped inside a Promise + + + + + + + + + + + + + + + + + + + + +
ParamTypeDefaultDescription
modelstring

contentFragment model name

+
fieldsstring

The query string for item fields

+
[config]ModelConfig{}

Pagination config

+
[args]ModelArgs{}

Query arguments

+
[options]object{}

additional POST request options

+
[retryOptions]object{}

retry options for @adobe/aio-lib-core-networking

+
+ ### aemHeadless.buildQuery(model, fields, [config], [args]) ⇒ [QueryBuilderResult](#QueryBuilderResult) diff --git a/src/index.js b/src/index.js index 6e26f65..14f3be1 100644 --- a/src/index.js +++ b/src/index.js @@ -144,7 +144,6 @@ class AEMHeadless { let hasNext = true let after = args.after || '' const limit = args.limit - const size = args.first || limit let pagingArgs = args while (hasNext) { const offset = pagingArgs.offset || 0 @@ -153,27 +152,55 @@ class AEMHeadless { } isInitial = false + const filteredData = await this.runModelQuery(model, fields, config, pagingArgs, options, retryOptions) - const { query, type } = this.buildQuery(model, fields, config, pagingArgs) - const { data } = await this.runQuery(query, options, retryOptions) + hasNext = filteredData.hasNext + after = filteredData.endCursor - let filteredData = {} - try { - filteredData = this.__filterData(model, type, data, size) - } catch (e) { - throw new API_ERROR({ - sdkDetails: { - serviceURL: this.serviceURL - }, - messageValues: `Error while filtering response data. ${e.message}` - }) + yield { + data: filteredData.data, + hasNext } + } + } - hasNext = filteredData.hasNext - after = filteredData.endCursor + /** + * Returns a Promise that resolves with a filtered POST request JSON data. + * + * @param {string} model - contentFragment model name + * @param {string} fields - The query string for item fields + * @param {ModelConfig} [config={}] - Pagination config + * @param {ModelArgs} [args={}] - Query arguments + * @param {object} [options={}] - additional POST request options + * @param {object} [retryOptions={}] - retry options for @adobe/aio-lib-core-networking + * @returns {Promise} - the response data wrapped inside a Promise + */ + async runModelQuery (model, fields, config, args, options, retryOptions) { + if (!model || !fields) { + throw new INVALID_PARAM({ + sdkDetails: { + serviceURL: this.serviceURL + }, + messageValues: 'Required param missing: @param {string} fields - query string for item fields' + }) + } - yield filteredData.data + const size = args.first || args.limit + const { query, type } = this.buildQuery(model, fields, config, args) + const { data } = await this.runQuery(query, options, retryOptions) + let filteredData = {} + try { + filteredData = this.__filterData(model, type, data, size) + } catch (e) { + throw new API_ERROR({ + sdkDetails: { + serviceURL: this.serviceURL + }, + messageValues: `Error while filtering response data. ${e.message}` + }) } + + return filteredData } /** diff --git a/test/shared/index.test.js b/test/shared/index.test.js index 3b159a8..8ad3291 100644 --- a/test/shared/index.test.js +++ b/test/shared/index.test.js @@ -241,12 +241,12 @@ describe('runPaginatedQuery', () => { const mockRetryOptions = {} it('should throw an error if model is missing', async () => { - const gen = sdk.runPaginatedQuery(null, mockFields, mockConfig, mockArgs, mockOptions, mockRetryOptions) + const gen = await sdk.runPaginatedQuery(null, mockFields, mockConfig, mockArgs, mockOptions, mockRetryOptions) await expect(gen.next()).rejects.toThrow(ErrorCodes.INVALID_PARAM) }) it('should throw an error if fields are missing', async () => { - const gen = sdk.runPaginatedQuery(mockModel, null, mockConfig, mockArgs, mockOptions, mockRetryOptions) + const gen = await sdk.runPaginatedQuery(mockModel, null, mockConfig, mockArgs, mockOptions, mockRetryOptions) await expect(gen.next()).rejects.toThrow(ErrorCodes.INVALID_PARAM) }) @@ -268,9 +268,150 @@ describe('runPaginatedQuery', () => { }) }) - const gen = sdk.runPaginatedQuery(mockModel, mockFields, mockConfig, mockArgs, mockOptions, mockRetryOptions) + const gen = await sdk.runPaginatedQuery(mockModel, mockFields, mockConfig, mockArgs, mockOptions, mockRetryOptions) + const { value } = await gen.next() + + expect(value.data).toEqual([mockData]) + }) + + it('should yield done true and value false, at the last iteration', async () => { + const mockData = { id: '1', name: 'foo' } + fetch.resetMocks() + fetch.mockResolvedValue({ + ok: true, + status: 200, + json: async () => ({ + data: { + mockModelPaginated: { + edges: [ + { node: mockData } + ], + pageInfo: { + hasNextPage: false + } + } + } + }) + }) + + const gen = await sdk.runPaginatedQuery(mockModel, mockFields, mockConfig, mockArgs, mockOptions, mockRetryOptions) const result = await gen.next() - expect(result).toEqual({ done: false, value: [mockData] }) + expect(result.done).toBeFalsy() + expect(result.value.data).toEqual([mockData]) + + const { done, value } = await gen.next() + + expect(done).toBeTruthy() + expect(value).toBeFalsy() + }) + + it('should yield only first item - cursor query', async () => { + const mockData = [ + { id: '1', name: 'foo' }, + { id: '2', name: 'bar' } + ] + fetch.resetMocks() + fetch.mockResolvedValue({ + ok: true, + status: 200, + json: async () => ({ + data: { + mockModelPaginated: { + edges: [ + { node: mockData[0] } + ], + pageInfo: { + hasNextPage: true + } + } + } + }) + }) + + const gen = await sdk.runPaginatedQuery(mockModel, mockFields, mockConfig, { first: 1 }, mockOptions, mockRetryOptions) + const result = await gen.next() + + expect(result).toEqual({ done: false, value: { data: [mockData[0]], hasNext: true } }) + }) + + it('should yield only first item - offset query', async () => { + const mockData = [ + { id: '1', name: 'foo' }, + { id: '2', name: 'bar' } + ] + fetch.resetMocks() + fetch.mockResolvedValue({ + ok: true, + status: 200, + json: async () => ({ + data: { + mockModelList: { + items: [ + mockData[0] + ] + } + } + }) + }) + + const gen = await sdk.runPaginatedQuery('mockModel', mockFields, { useLimitOffset: true }, { limit: 1 }, mockOptions, mockRetryOptions) + const { value } = await gen.next() + + expect(value.data).toEqual([mockData[0]]) + }) + + it('should yield item - path query', async () => { + const mockData = { id: '1', name: 'foo' } + fetch.resetMocks() + fetch.mockResolvedValue({ + ok: true, + status: 200, + json: async () => ({ + data: { + mockModelByPath: { + item: mockData + } + } + }) + }) + + const gen = await sdk.runPaginatedQuery(mockModel, mockFields, {}, { _path: 'path' }, mockOptions, mockRetryOptions) + const { value } = await gen.next() + + expect(value.data).toEqual(mockData) + }) + + it('should update pagingArgs', async () => { + const mockData = { id: '1', name: 'foo' } + fetch.resetMocks() + fetch.mockResolvedValue({ + ok: true, + status: 200, + json: async () => ({ + data: { + mockModel2Paginated: { + edges: [ + { node: mockData }, + { node: mockData } + ], + pageInfo: { + hasNextPage: true + } + } + } + }) + }) + + const gen = await sdk.runPaginatedQuery('mockModel2', mockFields, { pageSize: 1 }, { limit: 1 }, mockOptions, mockRetryOptions) + const result = await gen.next() + + expect(result.done).toBeFalsy() + expect(result.value.data).toEqual([mockData, mockData]) + + const { done, value } = await gen.next() + + expect(done).toBeFalsy() + expect(value.data).toEqual([mockData, mockData]) }) })