Add helper to extract collection elements as well as collections

In some cases, we need to access the individual collection pages
as they have things embedded (such as work package schemas).
pull/10358/head
Oliver Günther 3 years ago
parent db973ebdf9
commit 9bd471035a
No known key found for this signature in database
GPG Key ID: 88872239EB414F99
  1. 6
      frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths.ts
  2. 55
      frontend/src/app/core/apiv3/helpers/get-paginated-results.ts
  3. 8
      frontend/src/app/features/hal/services/hal-resource.service.ts

@ -64,6 +64,10 @@ export class ApiV3WorkPackagesPaths extends ApiV3Collection<WorkPackageResource,
* @param ids * @param ids
*/ */
public requireAll(ids:string[]):Promise<unknown> { public requireAll(ids:string[]):Promise<unknown> {
if (ids.length === 0) {
return Promise.resolve();
}
return new Promise<undefined>((resolve, reject) => { return new Promise<undefined>((resolve, reject) => {
this this
.loadCollectionsFor(_.uniq(ids)) .loadCollectionsFor(_.uniq(ids))
@ -157,7 +161,7 @@ export class ApiV3WorkPackagesPaths extends ApiV3Collection<WorkPackageResource,
protected loadCollectionsFor(ids:string[]):Promise<WorkPackageCollectionResource[]> { protected loadCollectionsFor(ids:string[]):Promise<WorkPackageCollectionResource[]> {
return this return this
.halResourceService .halResourceService
.getAllPaginated<WorkPackageCollectionResource>( .getAllPaginated<WorkPackageCollectionResource>(
this.path, this.path,
{ {
filters: ApiV3Filter('id', '=', ids).toJson(), filters: ApiV3Filter('id', '=', ids).toJson(),

@ -27,26 +27,29 @@ type ApiV3CollectionType<T> = CollectionResource<T>|IHALCollection<T>;
* Extract the elements of either a HAL class or an interface * Extract the elements of either a HAL class or an interface
*/ */
function extractCollectionElements<T>(collection:ApiV3CollectionType<T>):T[] { function extractCollectionElements<T>(collection:ApiV3CollectionType<T>):T[] {
// Some API endpoints return an undefined _embedded.elements
// so we ensure we return an array at all times.
if (collection instanceof HalResource) { if (collection instanceof HalResource) {
return collection.elements; return collection.elements || [];
} }
return collection._embedded.elements; return collection._embedded?.elements || [];
} }
/** /**
* Get ALL pages of a potentially paginated APIv3 request. * Get ALL pages of a potentially paginated APIv3 request, returning an array of collections
* *
* @param request The requesting callback to request specific pages * @param request The requesting callback to request specific pages
* @param pageSize The pageSize parameter to request, defaults to -1 (the maximum magic page number) * @param pageSize The pageSize parameter to request, defaults to -1 (the maximum magic page number)
* @return an array of HAL collections
*/ */
export function getPaginatedResults<T>( export function getPaginatedCollections<T, C extends ApiV3CollectionType<T>>(
request:(params:ApiV3PaginationParameters) => Observable<ApiV3CollectionType<T>>, request:(params:ApiV3PaginationParameters) => Observable<C>,
pageSize = MAGIC_PAGE_NUMBER, pageSize = MAGIC_PAGE_NUMBER,
):Observable<T[]> { ):Observable<ApiV3CollectionType<T>[]> {
return request({ pageSize, offset: 1 }) return request({ pageSize, offset: 1 })
.pipe( .pipe(
mergeMap((collection:ApiV3CollectionType<T>) => { mergeMap((collection:C) => {
const resolvedSize = collection.pageSize; const resolvedSize = collection.pageSize;
if (collection.total > collection.count) { if (collection.total > collection.count) {
@ -58,20 +61,36 @@ export function getPaginatedResults<T>(
// Branch out and fetch all remaining pages in parallel. // Branch out and fetch all remaining pages in parallel.
// Afterwards, merge the resulting list // Afterwards, merge the resulting list
return forkJoin(...calls).pipe( return forkJoin(...calls)
map( .pipe(
(results:ApiV3CollectionType<T>[]) => results.reduce( map((results:C[]) => [collection, ...results]),
(acc, next) => acc.concat(extractCollectionElements(next)), );
extractCollectionElements(collection),
),
),
);
} }
// The current page is the only page, return the results. // The current page is the only page, return the results.
return of(extractCollectionElements(collection)); return of([collection]);
}), }),
// Elements may incorrectly be undefined here due to the way the representer works );
map((elements) => elements || []), }
/**
* Get ALL pages of a potentially paginated APIv3 request, returning all concatenated elements.
*
* @param request The requesting callback to request specific pages
* @param pageSize The pageSize parameter to request, defaults to -1 (the maximum magic page number)
* @return an array of plain HAL resources
*/
export function getPaginatedResults<T>(
request:(params:ApiV3PaginationParameters) => Observable<ApiV3CollectionType<T>>,
pageSize = MAGIC_PAGE_NUMBER,
):Observable<T[]> {
return getPaginatedCollections(request, pageSize)
.pipe(
map(
(results:ApiV3CollectionType<T>[]) => results.reduce(
(acc, next) => acc.concat(extractCollectionElements(next)),
[] as T[],
),
),
); );
} }

@ -65,7 +65,7 @@ import {
} from 'core-app/features/hal/resources/hal-resource'; } from 'core-app/features/hal/resources/hal-resource';
import { initializeHalProperties } from '../helpers/hal-resource-builder'; import { initializeHalProperties } from '../helpers/hal-resource-builder';
import { HalError } from 'core-app/features/hal/services/hal-error'; import { HalError } from 'core-app/features/hal/services/hal-error';
import { getPaginatedResults } from 'core-app/core/apiv3/helpers/get-paginated-results'; import { getPaginatedCollections } from 'core-app/core/apiv3/helpers/get-paginated-results';
export interface HalResourceFactoryConfigInterface { export interface HalResourceFactoryConfigInterface {
cls?:any; cls?:any;
@ -153,13 +153,13 @@ export class HalResourceService {
params:Record<string, string|number> = {}, params:Record<string, string|number> = {},
headers:HTTPClientHeaders = {}, headers:HTTPClientHeaders = {},
):Observable<T[]> { ):Observable<T[]> {
return getPaginatedResults( return getPaginatedCollections(
(pageParams) => { (pageParams) => {
const requestParams = { ...params, pageParams }; const requestParams = { ...params, ...pageParams };
return this.request<CollectionResource<T>>('get', href, this.toEprops(requestParams), headers); return this.request<CollectionResource<T>>('get', href, this.toEprops(requestParams), headers);
}, },
(params.pageSize as number|undefined) || -1, (params.pageSize as number|undefined) || -1,
); ) as Observable<T[]>;
} }
/** /**

Loading…
Cancel
Save