@acorel/contentful-integration
Version:
Contentful integration with SAP Composable Storefront by Acorel
246 lines (225 loc) • 7.35 kB
text/typescript
import {Injectable} from "@angular/core";
import {
CmsComponentType,
convertComponent,
getComponentTypeByTypeName,
GraphqlCmsIntegrationService
} from "@acorel/graphql-integration";
import * as gqlbuilder from 'gql-query-builder'
import {gql, QueryOptions} from "@apollo/client/core";
import {CmsService, CmsStructureModel, PageContext} from "@spartacus/core";
import {ContentSlotData} from "@spartacus/core/src/cms/model/content-slot-data.model";
import {CmsComponent} from "@spartacus/core/src/model/cms.model";
import {ContentfulLivePreviewService} from "./contentful-live-preview.service";
import {CONTENTFUL_ENTITY} from "./contentful-integration.util";
import {ContentfulConfig} from "./contentful-config";
export class ContentfulIntegrationService implements GraphqlCmsIntegrationService {
constructor(
private contentfulLivePreviewService: ContentfulLivePreviewService,
private cmsService: CmsService,
private config: ContentfulConfig
) {
GraphqlCmsIntegrationService.COMPONENTTYPES = this.componentTypes();
}
pageQuery(options: {preview: boolean, slug: string, pageType: string}): QueryOptions {
const query = gqlbuilder.query([{
operation: "webPageCollection",
fields: [{
items: [
...CONTENTFUL_ENTITY,
'slug',
'pageTitle',
'pageType',
'showResults',
{
operation: "contentSlotsCollection",
fields: [{
items: [
...CONTENTFUL_ENTITY,
'name',
{
operation: "componentsCollection",
fields: [{
items: [
...this.pageComponentQuery()
]
}],
variables: {
componentLimit: {
name: "limit",
value: 10
}
}
}
]
}],
variables: {
contentSlotLimit: {
name: "limit",
value: 10
}
}
}
]
}],
variables: {
preview: {
name: "preview",
value: options.preview,
},
limit: {
name: "limit",
value: 1
},
where: {
name: "where",
type: "WebPageFilter",
value: {
"AND": [
{"slug": options.slug},
{"pageType": options.pageType}
]
}
}
}
},]);
return {
query: gql(query.query),
variables: query.variables
}
}
pageComponentQuery() : object[] {
const fragments: object[] = [];
this.componentTypes().forEach(ct => {
fragments.push({
operation: ct.typename,
fields: CONTENTFUL_ENTITY,
fragment: true,
})
})
return fragments;
}
pageResult(pageContext: PageContext, item: any): CmsStructureModel {
console.log('ITEM', item);
if (this.contentfulLivePreviewService.isPreviewEnabled()) {
this.contentfulLivePreviewService.subscribe(pageContext.id, pageContext,{
locale: this.contentfulLivePreviewService.currentLocale(),
data: item,
callback: data => {
this.contentfulLivePreviewService.update(pageContext.id, pageContext, data);
this.cmsService.refreshLatestPage();
}
})
}
return {
page: {
pageId: pageContext.id,
name: item.pageTitle,
title: item.pageTitle,
slots: this.convertSlots(pageContext, item.contentSlotsCollection.items),
properties: {
showResults: item.showResults
}
}
}
}
convertSlots(pageContext: PageContext, items: any[]
): { [key: string]: ContentSlotData; } {
let returnObject = {} as {
[key: string]: ContentSlotData;
};
items.forEach(item => {
let name = (item.name) ? item.name : item.sys.id;
let slot = this.convertSlot(pageContext, item);
if (returnObject[name] && returnObject[name].components && slot.components) {
slot.components.forEach(comp => {
// @ts-ignore
returnObject[name].components.push(comp);
})
} else {
returnObject[name] = slot;
}
});
return returnObject;
}
convertSlot(pageContext: PageContext, item: any
):
ContentSlotData {
return {
// We introduce a slot component, this will make it possible to add multiple contentful slot component in
// a single page slot.
components: [convertComponent(pageContext, item)]
}
}
componentQuery(id: string, options: {preview: boolean, typeCode?: string}): QueryOptions | undefined {
const type = (options.typeCode) ? options.typeCode : GraphqlCmsIntegrationService.COMPONENT_TYPE_MAPPING[id]?.typeName;
const componentType = getComponentTypeByTypeName(type);
if (componentType) {
const query = gqlbuilder.query([{
operation: componentType.collection,
fields: componentType.fields,
variables: {
preview: {
name: "preview",
value: options.preview,
},
id: {
name: "id",
type: "String!",
value: id
}
}
}]);
return {
query: gql(query.query),
variables: query.variables,
fetchPolicy: (options.preview) ? 'network-only' : 'cache-first'
}
} else {
return undefined
}
}
componentResult<T extends CmsComponent>(id: string, pageContext: PageContext, item: any, typeCode?: string): T {
const type = (typeCode) ? typeCode : GraphqlCmsIntegrationService.COMPONENT_TYPE_MAPPING[id]?.typeName;
const componentType = getComponentTypeByTypeName(type);
if (componentType) {
// If live preview enabled:
if (this.contentfulLivePreviewService.isPreviewEnabled()) {
this.contentfulLivePreviewService.subscribe(id, pageContext,{
locale: this.contentfulLivePreviewService.currentLocale(),
data: item,
callback: data => {
this.contentfulLivePreviewService.update(id, pageContext, data);
this.cmsService.refreshComponent(id, pageContext);
}
})
}
return componentType.converter(pageContext, item);
} else {
throw "No component type found for component with id: " + id;
}
}
private componentTypes() : CmsComponentType[] {
if (this.config.contentful?.components) {
return Array.from(new Map(Object.entries(this.config.contentful?.components)).values())
.filter(item => {return item != undefined}).map(item => { return item.component}) as CmsComponentType[];
}
return [];
}
isPreview(pageContext: PageContext): boolean {
if (this.config.contentful?.previewUrl) {
return pageContext.id.startsWith(this.config.contentful.previewUrl);
}
return false;
}
slugFromPage(pageContext: PageContext): string {
if (this.config.contentful?.previewUrl) {
return pageContext.id.replace(this.config.contentful.previewUrl, "")
.replace(/^\/|\/$/g, '');
}
return pageContext.id;
}
}