Skip to content

Commit

Permalink
Contentful: custom content model (#1900)
Browse files Browse the repository at this point in the history
* feat(contentful): add custom content model

* feat(contentful): change naming

* feat(plugin-contentful): allow fetching all kind of unknown models from contentful, not just the ones named 'custom'

* style(contentful): refactor types in order to not break tests and to remove custom type from TopContentType

Co-authored-by: Adrià Sastre <adria.sastre@gmail.com>
  • Loading branch information
AlbertGom and asastre authored Oct 1, 2021
1 parent 62db1b2 commit a1e17f9
Show file tree
Hide file tree
Showing 15 changed files with 148 additions and 6,463 deletions.
6,456 changes: 0 additions & 6,456 deletions packages/botonic-plugin-contentful/package-lock.json

This file was deleted.

1 change: 1 addition & 0 deletions packages/botonic-plugin-contentful/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"scripts": {
"build": "rm -rf lib && ../../node_modules/.bin/tsc",
"build:watch": "../../node_modules/.bin/tsc --watch",
"build_unit_tests": "tsc -b tests/tsconfig.json",
"build:watch": "npm run build -- --watch",
"test": "../../node_modules/.bin/jest --coverage",
Expand Down
4 changes: 3 additions & 1 deletion packages/botonic-plugin-contentful/src/cms/callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import escapeStringRegexp from 'escape-string-regexp'

import { isOfType } from '../util/enums'
import { Equatable, ValueObject } from '../util/objects'
import { CMS, ContentType, TopContentType } from './cms'
import { CMS, ContentType, CustomContentType, TopContentType } from './cms'
import { Content, TopContent } from './contents'
import { Context } from './context'
import { CmsException } from './exceptions'
Expand Down Expand Up @@ -151,6 +151,8 @@ export class ContentId extends ResourceId {
return cms.button(this.id, context)
case ContentType.ELEMENT:
return cms.element(this.id, context)
case CustomContentType.CUSTOM:
return cms.custom(this.id, context)
default:
return new TopContentId(this.model, this.id).deliver(cms, context)
}
Expand Down
5 changes: 5 additions & 0 deletions packages/botonic-plugin-contentful/src/cms/cms-dummy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Chitchat,
CommonFields,
Content,
Custom,
DateRangeContent,
Document,
Element,
Expand Down Expand Up @@ -78,6 +79,10 @@ export class DummyCMS implements CMS {
)
}

async custom(id: string, {} = DEFAULT_CONTEXT): Promise<Custom> {
return Promise.resolve(new Custom(id, id, {}))
}

chitchat(id: string, context = DEFAULT_CONTEXT): Promise<Chitchat> {
return this.text(id, context)
}
Expand Down
18 changes: 17 additions & 1 deletion packages/botonic-plugin-contentful/src/cms/cms-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,21 @@ import { SearchCandidate } from '../search'
import { Measure } from '../util'
import { reduceMultiError } from '../util/async'
import { AssetId, ContentId, ResourceId } from './callback'
import { CMS, ContentType, PagingOptions, TopContentType } from './cms'
import {
CMS,
ContentType,
CustomContentType,
PagingOptions,
TopContentType,
} from './cms'
import {
Asset,
Button,
Carousel,
Chitchat,
CommonFields,
Content,
Custom,
DateRangeContent,
Document,
Element,
Expand Down Expand Up @@ -99,6 +106,15 @@ export class ErrorReportingCMS implements CMS {
this.cms.handoff(id, context)
)
}

custom(id: string, context?: Context): Promise<Custom> {
return this.catchAndValidate(
id,
context,
CustomContentType.CUSTOM,
this.cms.custom(id, context)
)
}
text(id: string, context?: Context): Promise<Text> {
return this.catchAndValidate(
id,
Expand Down
14 changes: 13 additions & 1 deletion packages/botonic-plugin-contentful/src/cms/cms-log.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { ContentfulOptions } from '../plugin'
import { SearchCandidate } from '../search'
import { CMS, ContentType, PagingOptions, TopContentType } from './cms'
import {
CMS,
ContentType,
CustomContentType,
PagingOptions,
TopContentType,
} from './cms'
import {
Asset,
Button,
Carousel,
Chitchat,
CommonFields,
Content,
Custom,
DateRangeContent,
Document,
Element,
Expand Down Expand Up @@ -92,6 +99,11 @@ export class LogCMS implements CMS {
return this.cms.handoff(id, context)
}

custom(id: string, context?: Context): Promise<Custom> {
this.logContentDelivery(CustomContentType.CUSTOM, id, context)
return this.cms.custom(id, context)
}

content(id: string, context?: Context): Promise<Content> {
this.logContentDelivery('content' as ContentType, id, context)
return this.cms.content(id, context)
Expand Down
5 changes: 5 additions & 0 deletions packages/botonic-plugin-contentful/src/cms/cms-multilocale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Chitchat,
CommonFields,
Content,
Custom,
DateRangeContent,
Document,
Element,
Expand Down Expand Up @@ -104,6 +105,10 @@ export class MultiContextCms implements CMS {
return this.cmsFromContext(context).handoff(id, context)
}

custom(id: string, context?: Context): Promise<Custom> {
return this.cmsFromContext(context).custom(id, context)
}

topContents<T extends TopContent>(
model: TopContentType,
context?: Context,
Expand Down
21 changes: 20 additions & 1 deletion packages/botonic-plugin-contentful/src/cms/cms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Chitchat,
CommonFields,
Content,
Custom,
DateRangeContent,
Document,
Element,
Expand Down Expand Up @@ -58,7 +59,12 @@ export enum SubContentType {
BUTTON = 'button',
ELEMENT = 'element',
}
export type ContentType = TopContentType | SubContentType

export enum CustomContentType {
CUSTOM = 'custom',
}

export type ContentType = TopContentType | SubContentType | CustomContentType
export const ContentType = { ...TopContentType, ...SubContentType }
export const CONTENT_TYPES: ContentType[] = [
...TOP_CONTENT_TYPES,
Expand All @@ -84,6 +90,17 @@ export const BOTONIC_CONTENT_TYPES: BotonicContentType[] = [
...Object.values(SubContentType),
]

export function isCustomModel(
cmsModelType: ContentType,
localModelType: ContentType
): boolean {
return (
localModelType === CustomContentType.CUSTOM &&
(isSameModel(cmsModelType, CustomContentType.CUSTOM) ||
!CONTENT_TYPES.includes(cmsModelType))
)
}

export function isSameModel(model1: ContentType, model2: ContentType): boolean {
switch (model1) {
case ContentType.TEXT:
Expand Down Expand Up @@ -119,6 +136,8 @@ export interface CMS {
element(id: string, context?: Context): Promise<Element>

handoff(id: string, context?: Context): Promise<Handoff>

custom(id: string, context?: Context): Promise<Custom>
/** Even if ContentfulOptions.resumeErrors is set, if the asset is not available
* the method will fail. */
image(id: string, context?: Context): Promise<Image>
Expand Down
19 changes: 18 additions & 1 deletion packages/botonic-plugin-contentful/src/cms/contents.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import * as time from '../time'
import { shallowClone, Stringable } from '../util/objects'
import { Callback, ContentCallback, ContentId, TopContentId } from './callback'
import { ContentType, MessageContentType, TopContentType } from './cms'
import {
ContentType,
CustomContentType,
MessageContentType,
TopContentType,
} from './cms'
import { SearchableBy } from './fields'

export enum ButtonStyle {
Expand Down Expand Up @@ -268,6 +273,18 @@ export class Button extends Content {
}
}

export type CustomFields = Record<string, any>

export class Custom extends Content {
constructor(
readonly id: string,
readonly name: string,
readonly fields: CustomFields = {}
) {
super(CustomContentType.CUSTOM)
}
}

export class StartUp extends MessageContent {
constructor(
readonly common: CommonFields,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Content,
ContentType,
Context,
Custom,
DateRangeContent,
Document,
Element,
Expand Down Expand Up @@ -98,6 +99,10 @@ export class FilteredCMS implements CMS {
return this.cms.handoff(id, context)
}

custom(id: string, context?: Context): Promise<Custom> {
return this.cms.custom(id, context)
}

url(id: string, context?: Context): Promise<Url> {
return this.cms.url(id, context)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { AssetDelivery } from './contents/asset'
import { ButtonDelivery } from './contents/button'
import { CarouselDelivery } from './contents/carousel'
import { ContentsDelivery } from './contents/contents'
import { CustomDelivery } from './contents/custom'
import { DateRangeDelivery } from './contents/date-range'
import { DocumentDelivery } from './contents/document'
import { FollowUpDelivery } from './contents/follow-up'
Expand Down Expand Up @@ -56,6 +57,7 @@ export class Contentful implements cms.CMS {
private readonly _dateRange: DateRangeDelivery
private readonly _image: ImageDelivery
private readonly _handoff: HandoffDelivery
private readonly _custom: CustomDelivery
private readonly _asset: AssetDelivery
private readonly _queue: QueueDelivery
private readonly _button: ButtonDelivery
Expand Down Expand Up @@ -108,6 +110,7 @@ export class Contentful implements cms.CMS {
this._schedule = new ScheduleDelivery(delivery, resumeErrors)
this._queue = new QueueDelivery(delivery, this._schedule, resumeErrors)
this._handoff = new HandoffDelivery(delivery, this._queue, resumeErrors)
this._custom = new CustomDelivery(delivery, resumeErrors)
const followUp = new FollowUpDelivery(
this._delivery,
this._carousel,
Expand Down Expand Up @@ -180,6 +183,10 @@ export class Contentful implements cms.CMS {
return this._handoff.handoff(id, context)
}

async custom(id: string, context = DEFAULT_CONTEXT): Promise<cms.Custom> {
return this._custom.custom(id, context)
}

topContents<T extends TopContent>(
model: TopContentType,
context = DEFAULT_CONTEXT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ContentId,
ContentType,
Context,
isCustomModel,
isSameModel,
ResourceId,
} from '../cms'
Expand Down Expand Up @@ -113,7 +114,10 @@ export abstract class ContentDelivery extends ResourceDelivery {
): Promise<contentful.Entry<T>> {
const entry = await this.delivery.getEntry<T>(id, context, query)
const gotType = ContentfulEntryUtils.getContentModel(entry)
if (!isSameModel(gotType, this.modelType)) {
if (
!isCustomModel(gotType, this.modelType) &&
!isSameModel(gotType, this.modelType)
) {
throw new Error(
`Requested model with id '${id}' of type '${this.modelType}' but got '${gotType}'`
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as contentful from 'contentful'

import * as cms from '../../cms'
import { CustomFields } from '../../cms'
import { ContentDelivery } from '../content-delivery'
import { ContentWithNameFields } from '../delivery-utils'
import { DeliveryApi } from '../index'

export class CustomDelivery extends ContentDelivery {
constructor(delivery: DeliveryApi, resumeErrors: boolean) {
super(cms.CustomContentType.CUSTOM, delivery, resumeErrors)
}

public async custom(id: string, context: cms.Context): Promise<cms.Custom> {
const entry = await this.getEntry<ContentWithCustomFields>(id, context)
return this.fromEntry(entry)
}

public fromEntry(
customEntry: contentful.Entry<ContentWithCustomFields>
): cms.Custom {
return new cms.Custom(
customEntry.sys.id,
customEntry.fields.name,
this.getCustomFields(customEntry.fields)
)
}

private getCustomFields(entryFields: ContentWithCustomFields): CustomFields {
const { name, ...fields } = entryFields
return fields ? fields : {}
}
}

export type ContentWithCustomFields = ContentWithNameFields & CustomFields
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export class ImportContentUpdater {
}

async contentTypes(): Promise<ContentType[]> {
return andArrays(await this.info.contentTypes(), EXPORTABLE_CONTENT_TYPES)
return andArrays(EXPORTABLE_CONTENT_TYPES, await this.info.contentTypes())
}

async warnMissingFields(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { testContentful, testContext } from '../contentful.helper'

const CUSTOM_TEST = '5kqsVkXbN7ZnXXbUIiaWso'

test('TEST: contentful custom with custom fields', async () => {
const sut = testContentful()
const custom = await sut.custom(CUSTOM_TEST, testContext()) // actually returns the fallback language (es)
expect(custom.fields).toEqual({
customJson: { width: '100' },
customText: "Hi, I'm a custom text!",
customBoolean: true,
})
})

0 comments on commit a1e17f9

Please sign in to comment.