-
Notifications
You must be signed in to change notification settings - Fork 0
EPILIBS-210: Add taskAdapter.query wrapping GET /task/search #166
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,8 @@ | ||||||||||||||||||
| import type { RoutingOptions } from '../utils/router'; | ||||||||||||||||||
| import type { GenericScope, Address } from '../utils/constants'; | ||||||||||||||||||
| import type { RoutingOptions, Page } from '../utils/router'; | ||||||||||||||||||
| import type { GenericScope, GenericSearchOptions, Address } from '../utils/constants'; | ||||||||||||||||||
|
|
||||||||||||||||||
| import Router from '../utils/router'; | ||||||||||||||||||
| import { parseFilterInput } from '../utils/filter-parser'; | ||||||||||||||||||
|
|
||||||||||||||||||
| export enum RETRY_POLICY { | ||||||||||||||||||
| DO_NOTHING = 'DO_NOTHING', // If the task fails, do nothing (this is the default) | ||||||||||||||||||
|
|
@@ -326,3 +327,56 @@ export async function getTaskIn< | |||||||||||||||||
| ) | ||||||||||||||||||
| .then(({ body }) => body); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Queries for tasks | ||||||||||||||||||
| * Base URL: GET `https://forio.com/api/v3/{ACCOUNT}/{PROJECT}/task/search` | ||||||||||||||||||
| * | ||||||||||||||||||
| * Requires facilitator (or higher) privileges. Filterable/sortable fields include | ||||||||||||||||||
| * `task.taskKey`, `task.name`, `task.status`, `task.scopeBoundary`, `task.scopeKey`, | ||||||||||||||||||
| * `task.userKey`, `task.groupName`, `task.episodeName`, `task.nextExecution`, | ||||||||||||||||||
| * `task.failSafeExecution`, and `task.created`. | ||||||||||||||||||
| * | ||||||||||||||||||
| * @example | ||||||||||||||||||
| * import { taskAdapter } from 'epicenter-libs'; | ||||||||||||||||||
| * const page = await taskAdapter.query({ | ||||||||||||||||||
| * filter: [ | ||||||||||||||||||
| * 'task.scopeKey=0000017dd3bf540e5ada5b1e058f08f20461', // tasks scoped to this group | ||||||||||||||||||
| * 'task.status=INITIALIZED', // that have not yet fired | ||||||||||||||||||
| * ], | ||||||||||||||||||
| * sort: ['-task.created'], // newest first | ||||||||||||||||||
| * max: 10, // page should only include the first 10 items | ||||||||||||||||||
| * }); | ||||||||||||||||||
| * | ||||||||||||||||||
| * @param searchOptions Search options for the query | ||||||||||||||||||
| * @param [searchOptions.filter] Filters for searching | ||||||||||||||||||
| * @param [searchOptions.sort] Sorting criteria | ||||||||||||||||||
| * @param [searchOptions.first] The starting index of the page returned | ||||||||||||||||||
| * @param [searchOptions.max] The number of entries per page | ||||||||||||||||||
| * @param [optionals] Optional arguments; pass network call options overrides here. | ||||||||||||||||||
| * @returns promise that resolves to a page of tasks | ||||||||||||||||||
| */ | ||||||||||||||||||
| export async function query< | ||||||||||||||||||
| B extends object = TaskPayloadBody, | ||||||||||||||||||
| H extends object = TaskPayloadHeaders, | ||||||||||||||||||
| >( | ||||||||||||||||||
| searchOptions: GenericSearchOptions, | ||||||||||||||||||
| optionals: RoutingOptions = {}, | ||||||||||||||||||
| ): Promise<Page<TaskReadOutView<B, H>>> { | ||||||||||||||||||
| const { filter, sort = [], first, max } = searchOptions; | ||||||||||||||||||
|
|
||||||||||||||||||
| const searchParams = { | ||||||||||||||||||
| filter: parseFilterInput(filter), | ||||||||||||||||||
| sort: sort.join(';') || undefined, | ||||||||||||||||||
| first, max, | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| return await new Router() | ||||||||||||||||||
| .withSearchParams(searchParams) | ||||||||||||||||||
| .get('/task/search', { | ||||||||||||||||||
| paginated: true, | ||||||||||||||||||
| ...optionals, | ||||||||||||||||||
| }) | ||||||||||||||||||
|
Comment on lines
+377
to
+380
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Placing
Suggested change
|
||||||||||||||||||
| .then(({ body }) => body); | ||||||||||||||||||
| } | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -269,6 +269,58 @@ describe('taskAdapter', () => { | |
| testedMethods.add('getTaskIn'); | ||
| }); | ||
|
|
||
| describe('taskAdapter.query', () => { | ||
| const OPTIONS = { | ||
| filter: [ | ||
| 'task.scopeKey=0000017dd3bf540e5ada5b1e058f08f20461', | ||
| 'task.status=INITIALIZED', | ||
| ], | ||
| sort: ['-task.created'], | ||
| first: 0, | ||
| max: 10, | ||
| }; | ||
|
|
||
| it('Should do a GET', async () => { | ||
| await taskAdapter.query(OPTIONS); | ||
| const req = capturedRequests[capturedRequests.length - 1]; | ||
| expect(req.options.method.toUpperCase()).toBe('GET'); | ||
| }); | ||
|
|
||
| it('Should have authorization', async () => { | ||
| await taskAdapter.query(OPTIONS); | ||
| const req = capturedRequests[capturedRequests.length - 1]; | ||
| expect(getAuthHeader(req.requestHeaders)).toBe(`Bearer ${SESSION.token}`); | ||
| }); | ||
|
|
||
| it('Should use the task search URL', async () => { | ||
| await taskAdapter.query(OPTIONS); | ||
| const req = capturedRequests[capturedRequests.length - 1]; | ||
| const url = req.url.split('?')[0]; | ||
| expect(url).toBe(`https://${config.apiHost}/api/v${config.apiVersion}/${config.accountShortName}/${config.projectShortName}/task/search`); | ||
| }); | ||
|
|
||
| it('Should support generic URL options', async () => { | ||
| await taskAdapter.query(OPTIONS, GENERIC_OPTIONS); | ||
| const req = capturedRequests[capturedRequests.length - 1]; | ||
| const url = req.url.split('?')[0]; | ||
| const { server, accountShortName, projectShortName } = GENERIC_OPTIONS; | ||
| expect(url).toBe(`${server}/api/v${config.apiVersion}/${accountShortName}/${projectShortName}/task/search`); | ||
| }); | ||
|
|
||
| it('Should pass in query options as a part of the search parameters (query string)', async () => { | ||
| await taskAdapter.query(OPTIONS); | ||
| const req = capturedRequests[capturedRequests.length - 1]; | ||
| const search = req.url.split('?')[1]; | ||
| const searchParams = new URLSearchParams(search); | ||
| expect(searchParams.get('filter')).toBe(OPTIONS.filter.join(';')); | ||
| expect(searchParams.get('sort')).toBe(OPTIONS.sort.join(';')); | ||
| expect(searchParams.get('first')).toBe(OPTIONS.first.toString()); | ||
| expect(searchParams.get('max')).toBe(OPTIONS.max.toString()); | ||
| }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To ensure that calling });
it('Should support empty search options', async () => {
await taskAdapter.query();
const req = capturedRequests[capturedRequests.length - 1];
const search = req.url.split('?')[1];
const searchParams = new URLSearchParams(search);
expect(searchParams.get('filter')).toBeNull();
expect(searchParams.get('sort')).toBeNull();
expect(searchParams.get('first')).toBeNull();
expect(searchParams.get('max')).toBeNull();
}); |
||
|
|
||
| testedMethods.add('query'); | ||
| }); | ||
|
|
||
| it('Should not have any untested methods', () => { | ||
| const actualMethods = getFunctionKeys(taskAdapter); | ||
| expect(actualMethods).toEqual(testedMethods); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since all properties of
GenericSearchOptionsare optional, callers should be able to invokequery()without passing any arguments (e.g., to fetch the first page of tasks with default settings). Currently, omittingsearchOptionswill cause a runtimeTypeErrorwhen destructuring properties likefilterandsortfromundefined.Providing a default empty object
{}forsearchOptionsresolves this issue and makes the method safer and more convenient to use, especially in JavaScript environments.