Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: #59 add runModelQuery method to use functionality from Paginated query #60

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ with GraphQL endpoint, GraphQL serviceURL and auth if needed
* [.listPersistedQueries([options], [retryOptions])](#AEMHeadless+listPersistedQueries) ⇒ <code>Promise.&lt;any&gt;</code>
* [.runPersistedQuery(path, [variables], [options], [retryOptions])](#AEMHeadless+runPersistedQuery) ⇒ <code>Promise.&lt;any&gt;</code>
* [.runPaginatedQuery(model, fields, [config], [args], [options], [retryOptions])](#AEMHeadless+runPaginatedQuery)
* [.runModelQuery(model, fields, [config], [args], [options], [retryOptions])](#AEMHeadless+runModelQuery) ⇒ <code>Promise.&lt;any&gt;</code>
* [.buildQuery(model, fields, [config], [args])](#AEMHeadless+buildQuery) ⇒ [<code>QueryBuilderResult</code>](#QueryBuilderResult)

<a name="new_AEMHeadless_new"></a>
Expand Down Expand Up @@ -246,6 +247,41 @@ Returns a Generator Function.
</tr> </tbody>
</table>

<a name="AEMHeadless+runModelQuery"></a>

### aemHeadless.runModelQuery(model, fields, [config], [args], [options], [retryOptions]) ⇒ <code>Promise.&lt;any&gt;</code>
Returns a Promise that resolves with a filtered POST request JSON data.

**Kind**: instance method of [<code>AEMHeadless</code>](#AEMHeadless)
**Returns**: <code>Promise.&lt;any&gt;</code> - - the response data wrapped inside a Promise
<table>
<thead>
<tr>
<th>Param</th><th>Type</th><th>Default</th><th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>model</td><td><code>string</code></td><td></td><td><p>contentFragment model name</p>
</td>
</tr><tr>
<td>fields</td><td><code>string</code></td><td></td><td><p>The query string for item fields</p>
</td>
</tr><tr>
<td>[config]</td><td><code><a href="#ModelConfig">ModelConfig</a></code></td><td><code>{}</code></td><td><p>Pagination config</p>
</td>
</tr><tr>
<td>[args]</td><td><code><a href="#ModelArgs">ModelArgs</a></code></td><td><code>{}</code></td><td><p>Query arguments</p>
</td>
</tr><tr>
<td>[options]</td><td><code>object</code></td><td><code>{}</code></td><td><p>additional POST request options</p>
</td>
</tr><tr>
<td>[retryOptions]</td><td><code>object</code></td><td><code>{}</code></td><td><p>retry options for @adobe/aio-lib-core-networking</p>
</td>
</tr> </tbody>
</table>

<a name="AEMHeadless+buildQuery"></a>

### aemHeadless.buildQuery(model, fields, [config], [args]) ⇒ [<code>QueryBuilderResult</code>](#QueryBuilderResult)
Expand Down
59 changes: 43 additions & 16 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<any>} - 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
}

/**
Expand Down
149 changes: 145 additions & 4 deletions test/shared/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})

Expand All @@ -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])
})
})