import { IHttpService } from '@kentico/kontent-core'; import { IDeliveryClientConfig } from '../config'; import { Contracts } from '../contracts'; import { Responses, IContentItem, IContentTypeQueryConfig, IGroupedNetworkResponse, IItemQueryConfig, IDeliveryNetworkResponse, ILanguagesQueryConfig, ITaxonomyQueryConfig } from '../models'; import { IKontentListAllResponse, IKontentListResponse, IListAllQueryConfig, ISDKInfo } from '../models/common/common-models'; import { BaseDeliveryQueryService } from './base-delivery-query.service'; import { IMappingService } from './mapping.service'; export class QueryService extends BaseDeliveryQueryService { constructor( config: IDeliveryClientConfig, httpService: IHttpService, sdkInfo: ISDKInfo, mappingService: IMappingService ) { super(config, httpService, sdkInfo, mappingService); } /** * Gets single item from given url * @param url Url used to get single item * @param queryConfig Query configuration */ async getSingleItemAsync( url: string, queryConfig: IItemQueryConfig ): Promise< IDeliveryNetworkResponse, Contracts.IViewContentItemContract> > { const response = await this.getResponseAsync(url, queryConfig); return this.mapNetworkResponse< Responses.IViewContentItemResponse, Contracts.IViewContentItemContract >(this.mappingService.viewContentItemResponse(response.data), response); } /** * Gets single feed response. Might not contain all items in your project. * @param url Url * @param queryConfig Query configuration */ async getItemsFeed( url: string, queryConfig: IItemQueryConfig ): Promise, Contracts.IItemsFeedContract>> { const response = await this.getResponseAsync(url, queryConfig); return this.mapNetworkResponse(this.mappingService.itemsFeedResponse(response.data), response); } /** * Gets multiple items from given url * @param url Url used to get multiple items * @param queryConfig Query configuration */ async getMultipleItems( url: string, queryConfig: IItemQueryConfig ): Promise< IDeliveryNetworkResponse, Contracts.IListContentItemsContract> > { const response = await this.getResponseAsync(url, queryConfig); return this.mapNetworkResponse(this.mappingService.listContentItemsResponse(response.data), response); } /** * Gets single content type from given url * @param url Url used to get single type * @param queryConfig Query configuration */ async getSingleType( url: string, queryConfig: IContentTypeQueryConfig ): Promise> { const response = await this.getResponseAsync(url, queryConfig); return this.mapNetworkResponse(this.mappingService.viewContentTypeResponse(response.data), response); } /** * Gets multiple content types from given url * @param url Url used to get multiple types * @param queryConfig Query configuration */ async getMultipleTypes( url: string, queryConfig: IContentTypeQueryConfig ): Promise> { const response = await this.getResponseAsync(url, queryConfig); return this.mapNetworkResponse(this.mappingService.listContentTypesResponse(response.data), response); } /** * Gets languages * @param url Url * @param queryConfig Query configuration */ async getLanguages( url: string, queryConfig: ILanguagesQueryConfig ): Promise> { const response = await this.getResponseAsync(url, queryConfig); return this.mapNetworkResponse(this.mappingService.listLanguagesResponse(response.data), response); } /** * Gets single taxonomy from given url * @param url Url used to get single taxonomy * @param queryConfig Query configuration */ async getTaxonomy( url: string, queryConfig: ITaxonomyQueryConfig ): Promise> { const response = await this.getResponseAsync(url, queryConfig); return this.mapNetworkResponse(this.mappingService.viewTaxonomyResponse(response.data), response); } /** * Gets multiple taxonomies from given url * @param url Url used to get multiple taxonomies * @param queryConfig Query configuration */ async getTaxonomies( url: string, queryConfig: ITaxonomyQueryConfig ): Promise> { const response = await this.getResponseAsync(url, queryConfig); return this.mapNetworkResponse(this.mappingService.listTaxonomiesResponse(response.data), response); } /** * Gets single content type element from given url * @param url Url used to get single content type element * @param queryConfig Query configuration */ async getElementAsync( url: string, queryConfig: ITaxonomyQueryConfig ): Promise< IDeliveryNetworkResponse > { const response = await this.getResponseAsync( url, queryConfig ); return this.mapNetworkResponse(this.mappingService.viewContentTypeElementResponse(response.data), response); } async getListAllResponse< TResponse extends IKontentListResponse, TAllResponse extends IKontentListAllResponse, TContract >(data: { page: number; getResponse: ( nextPageUrl?: string, continuationToken?: string ) => Promise>; allResponseFactory: ( items: any[], responses: IDeliveryNetworkResponse[] ) => IGroupedNetworkResponse; listQueryConfig?: IListAllQueryConfig; }): Promise> { const responses = await this.getListAllResponseInternalAsync({ page: data.page, resolvedResponses: [], getResponse: data.getResponse, nextPageUrl: undefined, continuationToken: undefined, listQueryConfig: data.listQueryConfig }); return data.allResponseFactory( responses.reduce((prev: any[], current) => { prev.push(...current.data.items); return prev; }, []), responses ); } private async getListAllResponseInternalAsync(data: { page: number; nextPageUrl?: string; continuationToken?: string; getResponse: ( nextPageUrl?: string, continuationToken?: string ) => Promise>; resolvedResponses: IDeliveryNetworkResponse[]; listQueryConfig?: IListAllQueryConfig; }): Promise[]> { if (data.listQueryConfig?.pages) { if (data.page > data.listQueryConfig.pages) { // page limit reached, return result return data.resolvedResponses; } } const response = await data.getResponse(data.nextPageUrl, data.continuationToken); if (data.listQueryConfig?.delayBetweenRequests) { await this.sleep(data.listQueryConfig.delayBetweenRequests); } data.resolvedResponses.push(response); if (data.listQueryConfig?.responseFetched) { data.listQueryConfig.responseFetched(response, data.nextPageUrl, data.continuationToken); } const nextPage = response.data.pagination?.nextPage; const continuationToken = response.xContinuationToken; if (nextPage || continuationToken) { // recursively fetch next page data return await this.getListAllResponseInternalAsync({ page: data.page + 1, nextPageUrl: nextPage, continuationToken: continuationToken, listQueryConfig: data.listQueryConfig, getResponse: data.getResponse, resolvedResponses: data.resolvedResponses }); } return data.resolvedResponses; } private sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } }