Skip to content

Commit

Permalink
Update element types and snippet sync types
Browse files Browse the repository at this point in the history
  • Loading branch information
IvanKiral committed Apr 4, 2024
1 parent 418ad1b commit 969af06
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 63 deletions.
7 changes: 1 addition & 6 deletions src/modules/sync/generateSyncModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import {
AssetContracts,
ContentItemContracts,
ContentTypeContracts,
ContentTypeElements,
ContentTypeSnippetContracts,
ManagementClient,
TaxonomyContracts,
} from "@kontent-ai/management-sdk";
Expand All @@ -17,6 +15,7 @@ import { serializeDateForFileName } from "../../utils/files.js";
import { transformContentTypeModel } from "./modelTransfomers/contentTypes.js";
import { transformContentTypeSnippetsModel } from "./modelTransfomers/contentTypeSnippets.js";
import { transformTaxonomyGroupsModel } from "./modelTransfomers/taxonomyGroups.js";
import { ContentTypeSnippetsWithUnionElements } from "./types/contractModels.js";
import { FileContentModel } from "./types/fileContentModel.js";
import { getRequiredIds } from "./utils/contentTypeHelpers.js";
import {
Expand All @@ -27,10 +26,6 @@ import {
fetchTaxonomies,
} from "./utils/fetchers.js";

export type ContentTypeSnippetsWithUnionElements =
& Omit<ContentTypeSnippetContracts.IContentTypeSnippetContract, "elements">
& { elements: ContentTypeElements.Element[] };

export type EnvironmentModel = {
taxonomyGroups: ReadonlyArray<TaxonomyContracts.ITaxonomyContract>;
contentTypeSnippets: ReadonlyArray<ContentTypeSnippetsWithUnionElements>;
Expand Down
72 changes: 41 additions & 31 deletions src/modules/sync/modelTransfomers/contentTypeSnippets.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { omit } from "../../../utils/object.js";
import { EnvironmentModel } from "../generateSyncModel.js";
import { ContentTypeSnippetsWithUnionElements, SnippetElement } from "../types/contractModels.js";
import { SyncSnippetElement } from "../types/syncModel.js";
import {
transformAssetElement,
transformCustomElement,
Expand All @@ -16,41 +18,49 @@ export const transformContentTypeSnippetsModel = (
) =>
environmentModel.contentTypeSnippets.map(snippet => {
const syncSnippetElements = snippet.elements
.map(element => {
switch (element.type) {
case "guidelines":
return transformGuidelinesElement(
element,
environmentModel.assets,
environmentModel.items,
);
case "modular_content":
return transformLinkedItemsElement(
element,
environmentModel.contentTypes,
environmentModel.items,
);
case "taxonomy":
return transformTaxonomyElement(
element,
environmentModel.taxonomyGroups,
);
case "multiple_choice":
return transformMultipleChoiceElement(element);
case "custom":
return transformCustomElement(element, snippet);
case "asset":
return transformAssetElement(element, environmentModel.assets);
case "rich_text":
return transformRichTextElement(element, environmentModel.contentTypes);
default:
return transformDefaultElement(element);
}
});
.map<SyncSnippetElement>(element =>
omit(transformElement(element, snippet, environmentModel), ["content_group"])
);

return {
...omit(snippet, ["id", "last_modified"]),
elements: syncSnippetElements,
external_id: snippet.external_id ?? snippet.id,
};
});

const transformElement = (
element: SnippetElement,
snippet: ContentTypeSnippetsWithUnionElements,
environmentModel: EnvironmentModel,
) => {
switch (element.type) {
case "guidelines":
return transformGuidelinesElement(
element,
environmentModel.assets,
environmentModel.items,
);
case "modular_content":
return transformLinkedItemsElement(
element,
environmentModel.contentTypes,
environmentModel.items,
);
case "taxonomy":
return transformTaxonomyElement(
element,
environmentModel.taxonomyGroups,
);
case "multiple_choice":
return transformMultipleChoiceElement(element);
case "custom":
return transformCustomElement(element, snippet);
case "asset":
return transformAssetElement(element, environmentModel.assets);
case "rich_text":
return transformRichTextElement(element, environmentModel.contentTypes);
default:
return transformDefaultElement(element);
}
};
42 changes: 25 additions & 17 deletions src/modules/sync/modelTransfomers/elementTransformers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,25 @@ import {
} from "../../../constants/richText.js";
import { logWarning } from "../../../log.js";
import { omit } from "../../../utils/object.js";
import { CodenameReference, Replace } from "../../../utils/types.js";
import { customAssetCodenameAttributeName, customItemLinkCodenameAttributeName } from "../../constants/syncRichText.js";
import { ContentTypeSnippetsWithUnionElements } from "../generateSyncModel.js";
import { ContentTypeSnippetsWithUnionElements } from "../types/contractModels.js";
import {
SyncSnippetAssetElement,
SyncSnippetCustomElement,
SyncSnippetGuidelinesElement,
SyncSnippetLinkedItemsElement,
SyncSnippetMultipleChoiceElement,
SyncSnippetRichTextElement,
SyncSnippetTaxonomyElement,
} from "../types/elements.js";
SyncAssetElement,
SyncCustomElement,
SyncGuidelinesElement,
SyncLinkedItemsElement,
SyncMultipleChoiceElement,
SyncRichTextElement,
SyncTaxonomyElement,
} from "../types/syncModel.js";

type ElementWithOldContentGroup<E extends { content_group?: CodenameReference }> = Replace<
E,
"content_group",
SharedContracts.IReferenceObjectContract,
true
>;

const findContentType = (
typeReference: SharedContracts.IReferenceObjectContract,
Expand Down Expand Up @@ -71,7 +79,7 @@ const replaceRichTextReferences = (
export const transformCustomElement = (
element: ContentTypeElements.ICustomElement,
snippet: ContentTypeSnippetsWithUnionElements,
): SyncSnippetCustomElement => {
): ElementWithOldContentGroup<SyncCustomElement> => {
const syncAllowedElements = element.allowed_elements?.map(element => ({
codename: snippet.elements.find(el => el.id === element.id!)?.codename!,
}));
Expand All @@ -86,7 +94,7 @@ export const transformCustomElement = (

export const transformMultipleChoiceElement = (
element: ContentTypeElements.IMultipleChoiceElement,
): SyncSnippetMultipleChoiceElement => {
): ElementWithOldContentGroup<SyncMultipleChoiceElement> => {
const defaultOptionId = element.default?.global.value.map(o => o.id);
const defaultOptionCodename = defaultOptionId?.map(id => element.options.find(option => option.id === id))!;
const defaultOption = { global: { value: [{ codename: defaultOptionCodename[0]?.codename ?? "" }] } };
Expand All @@ -109,7 +117,7 @@ export const transformMultipleChoiceElement = (
export const transformAssetElement = (
element: ContentTypeElements.IAssetElement,
assets: ReadonlyArray<AssetContracts.IAssetModelContract>,
): SyncSnippetAssetElement => {
): ElementWithOldContentGroup<SyncAssetElement> => {
const defaultAssetsIds = element.default?.global.value.map(a => a.id);
const defaultAssetsReferences = defaultAssetsIds?.map(id => {
const asset = assets.find(asset => asset.id === id);
Expand All @@ -133,7 +141,7 @@ export const transformAssetElement = (
export const transformRichTextElement = (
element: ContentTypeElements.IRichTextElement,
contentTypes: ReadonlyArray<ContentTypeContracts.IContentTypeContract>,
): SyncSnippetRichTextElement => {
): ElementWithOldContentGroup<SyncRichTextElement> => {
const allowedContentTypes = element.allowed_content_types?.map(c => findContentType(c, contentTypes));
const allowedItemLinkTypes = element.allowed_item_link_types?.map(c => findContentType(c, contentTypes));

Expand All @@ -155,7 +163,7 @@ type TermType = {
export const transformTaxonomyElement = (
element: ContentTypeElements.ITaxonomyElement,
taxonomies: ReadonlyArray<TaxonomyContracts.ITaxonomyContract>,
): SyncSnippetTaxonomyElement => {
): ElementWithOldContentGroup<SyncTaxonomyElement> => {
const taxonomyGroup = taxonomies.find(t => t.id === element.taxonomy_group.id);
if (!taxonomyGroup) {
throw new Error(`Could not find taxonomy group with id ${element.taxonomy_group.id}`);
Expand Down Expand Up @@ -193,7 +201,7 @@ export const transformLinkedItemsElement = (
element: ContentTypeElements.ILinkedItemsElement,
contentTypes: ReadonlyArray<ContentTypeContracts.IContentTypeContract>,
items: ReadonlyArray<ContentItemContracts.IContentItemModelContract>,
): SyncSnippetLinkedItemsElement => {
): ElementWithOldContentGroup<SyncLinkedItemsElement> => {
const allowedContentTypes = element.allowed_content_types?.map(type => findContentType(type, contentTypes));

const defaultValues = element.default?.global.value.map(itemReference => {
Expand Down Expand Up @@ -224,15 +232,15 @@ export const transformGuidelinesElement = (
element: ContentTypeElements.IGuidelinesElement,
assets: ReadonlyArray<AssetContracts.IAssetModelContract>,
items: ReadonlyArray<ContentItemContracts.IContentItemModelContract>,
): SyncSnippetGuidelinesElement => ({
): ElementWithOldContentGroup<SyncGuidelinesElement> => ({
...omit(element, ["id"]),
guidelines: replaceRichTextReferences(element.guidelines, assets, items),
codename: element.codename as string,
external_id: element.external_id ?? element.id,
});

export const transformDefaultElement = (
element: ContentTypeElements.Element,
element: ContentTypeElements.ITextElement | ContentTypeElements.INumberElement | ContentTypeElements.IDateTimeElement,
) => ({
...omit(element, ["id"]),
codename: element.codename as string,
Expand Down
14 changes: 14 additions & 0 deletions src/modules/sync/types/contractModels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ContentTypeElements, ContentTypeSnippetContracts } from "@kontent-ai/management-sdk";

import { Replace } from "../../../utils/types.js";

export type SnippetElement = Exclude<
ContentTypeElements.Element,
ContentTypeElements.IUrlSlugElement | ContentTypeElements.ISnippetElement | ContentTypeElements.ISubpagesElement
>;

export type ContentTypeSnippetsWithUnionElements = Replace<
ContentTypeSnippetContracts.IContentTypeSnippetContract,
"elements",
SnippetElement[]
>;
4 changes: 2 additions & 2 deletions src/modules/sync/types/fileContentModel.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {
ContentTypeContracts,
ContentTypeElements,
ContentTypeSnippetContracts,
ElementContracts,
TaxonomyContracts,
} from "@kontent-ai/management-sdk";

import { Replace } from "../../../utils/types.js";
import { SyncSnippetElement } from "./syncModel.js";

export type TaxonomySyncModel =
& Omit<TaxonomyContracts.ITaxonomyContract, "id" | "last_modified" | "codename" | "terms">
Expand All @@ -19,7 +19,7 @@ export type ContentTypeSnippetsSyncModel =
& Omit<ContentTypeSnippetContracts.IContentTypeSnippetContract, "id" | "codename" | "last_modified" | "elements">
& Readonly<{
codename: string;
elements: ReadonlyArray<Replace<ContentTypeElements.Element, "codename", string>>;
elements: ReadonlyArray<SyncSnippetElement>;
}>;

export type ContentTypeSyncModel =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { ContentTypeElements } from "@kontent-ai/management-sdk";

import { CodenameReference } from "../../../utils/types.js";

export type ReplaceReferences<T> = T extends { id?: string; codename?: string; externalId?: string } ?
& { codename: string }
& CodenameReference
& {
[K in keyof Omit<T, "id" | "codename">]: ReplaceReferences<T[K]>;
}
: T extends ReadonlyArray<infer R> ? ReadonlyArray<ReplaceReferences<R>>
: T;

type SnippetElement<E> = Omit<E, "content_group">;
type ContentReference = { default?: { global: { value: { codename: string; external_id: string }[] } } };
// content item or asset reference
export type ContentReference = { default?: { global: { value: { codename: string; external_id: string }[] } } };

export type SyncCustomElement = ReplaceReferences<ContentTypeElements.ICustomElement>;
export type SyncMultipleChoiceElement = ReplaceReferences<ContentTypeElements.IMultipleChoiceElement>;
Expand All @@ -24,6 +27,9 @@ export type SyncLinkedItemsElement =
& Omit<ReplaceReferences<ContentTypeElements.ILinkedItemsElement>, "default">
& ContentReference;
export type SyncGuidelinesElement = ReplaceReferences<ContentTypeElements.IGuidelinesElement>;
export type SyncTextElement = ReplaceReferences<ContentTypeElements.ITextElement>;
export type SyncDateTimeElement = ReplaceReferences<ContentTypeElements.IDateTimeElement>;
export type SyncNumberElement = ReplaceReferences<ContentTypeElements.INumberElement>;

export type SyncSnippetCustomElement = SnippetElement<SyncCustomElement>;
export type SyncSnippetMultipleChoiceElement = SnippetElement<SyncMultipleChoiceElement>;
Expand All @@ -32,3 +38,18 @@ export type SyncSnippetRichTextElement = SnippetElement<SyncRichTextElement>;
export type SyncSnippetTaxonomyElement = SnippetElement<SyncTaxonomyElement>;
export type SyncSnippetLinkedItemsElement = SnippetElement<SyncLinkedItemsElement>;
export type SyncSnippetGuidelinesElement = SnippetElement<SyncGuidelinesElement>;
export type SyncSnippetTextElement = SnippetElement<SyncTextElement>;
export type SyncSnippetDateTimeElement = SnippetElement<SyncDateTimeElement>;
export type SyncSnippetNumberElement = SnippetElement<SyncNumberElement>;

export type SyncSnippetElement =
| SyncSnippetCustomElement
| SyncSnippetMultipleChoiceElement
| SyncSnippetAssetElement
| SyncSnippetRichTextElement
| SyncSnippetTaxonomyElement
| SyncSnippetLinkedItemsElement
| SyncSnippetGuidelinesElement
| SyncSnippetTextElement
| SyncSnippetNumberElement
| SyncSnippetDateTimeElement;
2 changes: 1 addition & 1 deletion src/modules/sync/utils/contentTypeHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
transformToJson,
} from "@kontent-ai/rich-text-resolver";

import { ContentTypeSnippetsWithUnionElements } from "../generateSyncModel.js";
import { ContentTypeSnippetsWithUnionElements } from "../types/contractModels.js";

const isAssetElement = (
element: ElementContracts.IContentTypeElementContract | ContentTypeElements.Element,
Expand Down
6 changes: 4 additions & 2 deletions src/utils/object.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export const omit = <T extends object, K extends keyof T>(obj: T, props: K[]): Omit<T, K> =>
Object.fromEntries(Object.entries(obj).filter(([key]) => !props.includes(key as K))) as Omit<T, K>;
import { SuperiorOmit } from "./types.js";

export const omit = <T extends object, K extends keyof T>(obj: T, props: K[]): SuperiorOmit<T, K> =>
Object.fromEntries(Object.entries(obj).filter(([key]) => !props.includes(key as K))) as SuperiorOmit<T, K>;
13 changes: 13 additions & 0 deletions src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,16 @@ export type RequiredCodename<T extends { [key in "codename"]?: string }> = Repla
export type Replace<T, Key extends keyof T, NewType, IsOptional extends boolean = false> =
& Omit<T, Key>
& (IsOptional extends true ? { readonly [key in Key]?: NewType } : { readonly [key in Key]: NewType });

/**
* Original Pick type extended to work on Union type objects
*/
export type SuperiorPick<T, K extends keyof T> = T extends any ? {
[P in K]: T[P];
}
: never;

/**
* Original Omit type extended to work on Union type objects
*/
export type SuperiorOmit<T, K extends keyof any> = T extends any ? SuperiorPick<T, Exclude<keyof T, K>> : never;
4 changes: 2 additions & 2 deletions tests/unit/sync/elementTransformers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
TaxonomyContracts,
} from "@kontent-ai/management-sdk";

import { ContentTypeSnippetsWithUnionElements } from "../../../src/modules/sync/generateSyncModel";
import {
transformAssetElement,
transformCustomElement,
Expand All @@ -18,9 +17,10 @@ import {
transformRichTextElement,
transformTaxonomyElement,
} from "../../../src/modules/sync/modelTransfomers/elementTransformers";
import { ContentTypeSnippetsWithUnionElements, SnippetElement } from "../../../src/modules/sync/types/contractModels";

const createSnippet = (
elements: ContentTypeElements.Element[],
elements: SnippetElement[],
): ContentTypeSnippetsWithUnionElements => ({
id: "snippet",
name: "snippet",
Expand Down

0 comments on commit 969af06

Please sign in to comment.