Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
5 changes: 4 additions & 1 deletion lib/client/delivery-client.factory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ClientTypes } from '../models';
import { IDeliveryClientConfig } from '../config/delivery-configs';
import { DeliveryClient } from './delivery-client';

export function createDeliveryClient(config: IDeliveryClientConfig): DeliveryClient {
export function createDeliveryClient<TClientTypes extends ClientTypes = ClientTypes>(
config: IDeliveryClientConfig
): DeliveryClient<TClientTypes> {
return new DeliveryClient(config);
}
50 changes: 30 additions & 20 deletions lib/client/delivery-client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { HttpService } from '@kontent-ai/core-sdk';

import { IDeliveryClientConfig } from '../config';
import { IContentItem } from '../models';
import { ClientTypes, IContentItem } from '../models';
import {
ElementQuery,
ItemsFeedQuery,
Expand All @@ -19,9 +18,9 @@ import { sdkInfo } from '../sdk-info.generated';
import { IMappingService, MappingService, QueryService } from '../services';
import { IDeliveryClient } from './idelivery-client.interface';

export class DeliveryClient implements IDeliveryClient {
private queryService: QueryService;
public mappingService: IMappingService;
export class DeliveryClient<TClientTypes extends ClientTypes = ClientTypes> implements IDeliveryClient {
private queryService: QueryService<TClientTypes>;
public mappingService: IMappingService<TClientTypes>;

/**
* Delivery client used to fetch data from Kontent.ai
Expand Down Expand Up @@ -49,82 +48,93 @@ export class DeliveryClient implements IDeliveryClient {
/**
* Gets query for multiple languages
*/
languages(): LanguagesQuery {
languages(): LanguagesQuery<TClientTypes> {
return new LanguagesQuery(this.config, this.queryService);
}

/**
* Gets query for multiple types
*/
types(): MultipleTypeQuery {
types(): MultipleTypeQuery<TClientTypes> {
return new MultipleTypeQuery(this.config, this.queryService);
}

/**
* Gets query for single type
* @param {string} typeCodename - Codename of the type to fetch
*/
type(typeCodename: string): SingleTypeQuery {
type(typeCodename: TClientTypes['contentTypeCodenames']): SingleTypeQuery<TClientTypes> {
return new SingleTypeQuery(this.config, this.queryService, typeCodename);
}

/**
* Gets query for multiple items
*/
items<TContentItem extends IContentItem = IContentItem>(): MultipleItemsQuery<TContentItem> {
return new MultipleItemsQuery<TContentItem>(this.config, this.queryService);
items<TContentItem extends IContentItem = TClientTypes['contentItemType']>(): MultipleItemsQuery<
TClientTypes,
TContentItem
> {
return new MultipleItemsQuery<TClientTypes, TContentItem>(this.config, this.queryService);
}

/**
* Gets query for single item
* @param {string} codename - Codename of item to fetch
*/
item<TContentItem extends IContentItem = IContentItem>(codename: string): SingleItemQuery<TContentItem> {
return new SingleItemQuery<TContentItem>(this.config, this.queryService, codename);
item<TContentItem extends IContentItem = TClientTypes['contentItemType']>(
codename: string
): SingleItemQuery<TClientTypes, TContentItem> {
return new SingleItemQuery<TClientTypes, TContentItem>(this.config, this.queryService, codename);
}

/**
* Gets query for items feed. Executes single HTTP request only
*/
itemsFeed<TContentItem extends IContentItem = IContentItem>(): ItemsFeedQuery<TContentItem> {
return new ItemsFeedQuery<TContentItem>(this.config, this.queryService);
itemsFeed<TContentItem extends IContentItem = TClientTypes['contentItemType']>(): ItemsFeedQuery<
TClientTypes,
TContentItem
> {
return new ItemsFeedQuery<TClientTypes, TContentItem>(this.config, this.queryService);
}

/**
* Gets query for single taxonomy
* @param {string} codename - Codename of taxonomy to fetch
*/
taxonomy(codename: string): TaxonomyQuery {
taxonomy(codename: TClientTypes['taxonomyCodenames']): TaxonomyQuery<TClientTypes> {
return new TaxonomyQuery(this.config, this.queryService, codename);
}

/**
* Gets query for multiple taxonomies
*/
taxonomies(): TaxonomiesQuery {
return new TaxonomiesQuery(this.config, this.queryService);
taxonomies(): TaxonomiesQuery<TClientTypes> {
return new TaxonomiesQuery<TClientTypes>(this.config, this.queryService);
}

/**
* Gets query for an element within a type
* @param {string} typeCodename - Codename of the type
* @param {string} elementCodename - Codename of the element
*/
element(typeCodename: string, elementCodename: string): ElementQuery {
element(
typeCodename: TClientTypes['contentTypeCodenames'],
elementCodename: TClientTypes['elementCodenames']
): ElementQuery<TClientTypes> {
return new ElementQuery(this.config, this.queryService, typeCodename, elementCodename);
}

/**
* Gets query for initializing sync
*/
initializeSync(): InitializeSyncQuery {
initializeSync(): InitializeSyncQuery<TClientTypes> {
return new InitializeSyncQuery(this.config, this.queryService);
}

/**
* Gets query fetching delta updates of content items
*/
syncChanges(): SyncChangesQuery {
syncChanges(): SyncChangesQuery<TClientTypes> {
return new SyncChangesQuery(this.config, this.queryService);
}
}
39 changes: 25 additions & 14 deletions lib/client/idelivery-client.interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IContentItem } from '../models';
import { ClientTypes, IContentItem } from '../models';
import {
ElementQuery,
ItemsFeedQuery,
Expand All @@ -14,67 +14,78 @@ import {
} from '../query';
import { IMappingService } from '../services';

export interface IDeliveryClient {
export interface IDeliveryClient<TClientTypes extends ClientTypes = ClientTypes> {
/**
* Mapping service - can be used to get strongly typed responses from json result
*/
mappingService: IMappingService;
mappingService: IMappingService<TClientTypes>;

/**
* Gets query for languages
*/
languages(): LanguagesQuery;
languages(): LanguagesQuery<TClientTypes>;

/**
* Gets query for multiple types
*/
types(): MultipleTypeQuery;
types(): MultipleTypeQuery<TClientTypes>;

/**
* Gets query for single type
* @param {string} typeCodename - Codename of the type to retrieve
*/
type(typeCodename: string): SingleTypeQuery;
type(typeCodename: TClientTypes['contentTypeCodenames']): SingleTypeQuery<TClientTypes>;

/**
* Gets query for multiple items
*/
items<TContentItem extends IContentItem = IContentItem>(): MultipleItemsQuery<TContentItem>;
items<TContentItem extends IContentItem = TClientTypes['contentItemType']>(): MultipleItemsQuery<
TClientTypes,
TContentItem
>;

/**
* Gets query for items feed. Executes single HTTP request only
*/
itemsFeed<TContentItem extends IContentItem = IContentItem>(): ItemsFeedQuery<TContentItem>;
itemsFeed<TContentItem extends IContentItem = TClientTypes['contentItemType']>(): ItemsFeedQuery<
TClientTypes,
TContentItem
>;

/**
* Gets query for single item
* @param {string} codename - Codename of item to retrieve
*/
item<TContentItem extends IContentItem = IContentItem>(codename: string): SingleItemQuery<TContentItem>;
item<TContentItem extends IContentItem = TClientTypes['contentItemType']>(
codename: string
): SingleItemQuery<TClientTypes, TContentItem>;

/**
* Gets query for multiple taxonomies
*/
taxonomies(): TaxonomiesQuery;
taxonomies(): TaxonomiesQuery<TClientTypes>;

/**
* Gets query for single item
* @param {string} codename - Codename of taxonomy to retrieve
*/
taxonomy(codename: string): TaxonomyQuery;
taxonomy(codename: TClientTypes['taxonomyCodenames']): TaxonomyQuery<TClientTypes>;

/**
* Gets query for an element within a type
*/
element(typeCodename: string, elementCodename: string): ElementQuery;
element(
typeCodename: TClientTypes['contentTypeCodenames'],
elementCodename: TClientTypes['elementCodenames']
): ElementQuery<TClientTypes>;

/**
* Gets query for initializing sync
*/
initializeSync(): InitializeSyncQuery;
initializeSync(): InitializeSyncQuery<TClientTypes>;

/**
* Gets query fetching delta updates of content items
*/
syncChanges(): SyncChangesQuery;
syncChanges(): SyncChangesQuery<TClientTypes>;
}
17 changes: 9 additions & 8 deletions lib/mappers/element.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IDeliveryClientConfig } from '../config';
import { Contracts } from '../contracts';
import { ElementModels, Elements, ElementType } from '../elements';
import {
ClientTypes,
IContentItem,
IContentItemsContainer,
IContentItemWithRawDataContainer,
Expand All @@ -18,15 +19,15 @@ interface IRichTextImageUrlRecord {
newUrl: string;
}

export class ElementMapper {
export class ElementMapper<TClientTypes extends ClientTypes> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this class doesn't need to send the whole type of object TClientTypes it just needs need type TClientTypes['contentItemType'].

It would be easier to test this class if you need to pass only the necessary stuff

Check also the other classes for this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't, the only reason I passed all types is for consistency and ease of use. Do you think it's worth splitting?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand the point of ease of use, I am not sure about consistency. These generic types are new after all, so they are only consistent with the new code you are writing :).
For me there are two options:

  1. Pass only the required type, which is more correct of system modelling. This improves the type dependency coupling.
    • If I wanted to unit test only ElementMapper I would need to define the whole type to the generic. That would be bothering as the class really only needs type of contentItemType. So for me as a tester of ElementMapper would be nice to pass just ElementMapper<TContentItemType>
  2. If you just want to pass the object everywhere, I guess there is not as elegant workaround to satisfy both ends (ease of use and creating only part of the object)
    ElementMapper<TClientTypes extends Pick<ClientTypes, 'contentItemType'>>

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me it seemed more straightforward to just pass the TClientTypes as a whole, but I changed it so the mappers now need only required generic arguments :)

constructor(private readonly config: IDeliveryClientConfig) {}

mapElements<TContentItem extends IContentItem = IContentItem>(data: {
mapElements<TContentItem extends IContentItem = TClientTypes['contentItemType']>(data: {
dataToMap: IContentItemWithRawElements;
processedItems: IContentItemsContainer;
processedItems: IContentItemsContainer<TClientTypes['contentItemType']>;
processingStartedForCodenames: string[];
preparedItems: IContentItemWithRawDataContainer;
}): IMapElementsResult<TContentItem> | undefined {
}): IMapElementsResult<TContentItem, TClientTypes['contentItemType']> | undefined {
// return processed item to avoid infinite recursion
const processedItem = data.processedItems[
codenameHelper.escapeCodenameInCodenameIndexer(data.dataToMap.item.system.codename)
Expand Down Expand Up @@ -83,7 +84,7 @@ export class ElementMapper {
private mapElement(data: {
elementWrapper: ElementModels.IElementWrapper;
item: IContentItem;
processedItems: IContentItemsContainer;
processedItems: IContentItemsContainer<TClientTypes['contentItemType']>;
processingStartedForCodenames: string[];
preparedItems: IContentItemWithRawDataContainer;
}): ElementModels.IElement<any> {
Expand Down Expand Up @@ -146,7 +147,7 @@ export class ElementMapper {

private mapRichTextElement(
elementWrapper: ElementModels.IElementWrapper,
processedItems: IContentItemsContainer,
processedItems: IContentItemsContainer<TClientTypes['contentItemType']>,
processingStartedForCodenames: string[],
preparedItems: IContentItemWithRawDataContainer
): Elements.RichTextElement {
Expand Down Expand Up @@ -336,7 +337,7 @@ export class ElementMapper {

private mapLinkedItemsElement(data: {
elementWrapper: ElementModels.IElementWrapper;
processedItems: IContentItemsContainer;
processedItems: IContentItemsContainer<TClientTypes['contentItemType']>;
processingStartedForCodenames: string[];
preparedItems: IContentItemWithRawDataContainer;
}): Elements.LinkedItemsElement<any> {
Expand Down Expand Up @@ -371,7 +372,7 @@ export class ElementMapper {
private getOrSaveLinkedItemForElement(
codename: string,
element: Contracts.IElementContract,
processedItems: IContentItemsContainer,
processedItems: IContentItemsContainer<TClientTypes['contentItemType']>,
mappingStartedForCodenames: string[],
preparedItems: IContentItemWithRawDataContainer
): IContentItem | undefined {
Expand Down
Loading