diff --git a/ghost/collections/package.json b/ghost/collections/package.json index 4c6d54299b..0c3d686667 100644 --- a/ghost/collections/package.json +++ b/ghost/collections/package.json @@ -10,7 +10,7 @@ "dev": "tsc --watch --preserveWatchOutput --sourceMap", "build": "tsc", "prepare": "tsc", - "test:unit": "NODE_ENV=testing c8 --src src --all --exclude 'src/Collection.ts' --exclude 'test/' --check-coverage --100 --reporter text --reporter cobertura mocha -r ts-node/register './test/**/*.test.ts'", + "test:unit": "NODE_ENV=testing c8 --src src --all --exclude 'test/' --check-coverage --100 --reporter text --reporter cobertura mocha -r ts-node/register './test/**/*.test.ts'", "test": "yarn test:types && yarn test:unit", "test:types": "tsc --noEmit", "lint:code": "eslint src/ --ext .ts --cache", diff --git a/ghost/collections/src/Collection.ts b/ghost/collections/src/Collection.ts index eb49586c59..76227efddd 100644 --- a/ghost/collections/src/Collection.ts +++ b/ghost/collections/src/Collection.ts @@ -1,15 +1,79 @@ -// @NOTE: file names having only type declarations should also -// be uppercased -/* eslint-disable ghost/filenames/match-regex */ +// have to use requires until there are type definitions for these modules -export type Collection = { +const {ValidationError} = require('@tryghost/errors'); +const tpl = require('@tryghost/tpl'); + +import ObjectID from 'bson-objectid'; + +const messages = { + invalidIDProvided: 'Invalid ID provided for Collection', + invalidDateProvided: 'Invalid date provided for {fieldName}' +}; + +export class Collection { id: string; - // @NOTE: this field feels out of place here and needs clarification - // it's here for now to implement the InMemoryRepository pattern - deleted: boolean; title: string; - description: string, + slug: string; + description: string; type: 'manual' | 'automatic'; filter: string | null; - feature_image: string | null; + featureImage: string | null; + createdAt: Date; + updatedAt: Date; + deleted: boolean; + + private constructor(data: any) { + this.id = data.id; + this.title = data.title; + this.slug = data.slug; + this.description = data.description; + this.type = data.type; + this.filter = data.filter; + this.featureImage = data.featureImage; + this.createdAt = data.createdAt; + this.updatedAt = data.updatedAt; + this.deleted = data.deleted; + } + + static validateDateField(date: any, fieldName: string): Date { + if (!date) { + return new Date(); + } + + if (date instanceof Date) { + return date; + } + + throw new ValidationError({ + message: tpl(messages.invalidDateProvided, {fieldName}) + }); + } + + static async create(data: any): Promise { + let id; + + if (!data.id) { + id = new ObjectID(); + } else if (typeof data.id === 'string') { + id = ObjectID.createFromHexString(data.id); + } else if (data.id instanceof ObjectID) { + id = data.id; + } else { + throw new ValidationError({ + message: tpl(messages.invalidIDProvided) + }); + } + + return new Collection({ + id: id.toHexString(), + title: data.title, + description: data.description, + type: data.type, + filter: data.filter, + featureImage: data.feature_image, + createdAt: Collection.validateDateField(data.created_at, 'created_at'), + updatedAt: Collection.validateDateField(data.updated_at, 'updated_at'), + deleted: data.deleted || false + }); + } } diff --git a/ghost/collections/src/CollectionsRepositoryInMemory.ts b/ghost/collections/src/CollectionsRepositoryInMemory.ts index e7eac2eb8e..0904ccf507 100644 --- a/ghost/collections/src/CollectionsRepositoryInMemory.ts +++ b/ghost/collections/src/CollectionsRepositoryInMemory.ts @@ -1,51 +1,16 @@ -// have to use requires until there are type definitions for these modules -const {ValidationError} = require('@tryghost/errors'); -const tpl = require('@tryghost/tpl'); - -import ObjectID from 'bson-objectid'; import {InMemoryRepository} from '@tryghost/in-memory-repository'; import {Collection} from './Collection'; -const messages = { - invalidIDProvided: 'Invalid ID provided for Collection' -}; - export class CollectionsRepositoryInMemory extends InMemoryRepository { constructor() { super(); } - async create(data: any): Promise { - let id; - - if (!data.id) { - id = new ObjectID(); - } else if (typeof data.id === 'string') { - id = ObjectID.createFromHexString(data.id); - } else if (data.id instanceof ObjectID) { - id = data.id; - } else { - throw new ValidationError({ - message: tpl(messages.invalidIDProvided) - }); - } - - return { - id: id.toHexString(), - title: data.title, - description: data.description, - type: data.type, - filter: data.filter, - feature_image: data.feature_image, - deleted: data.deleted || false - }; - } - protected toPrimitive(entity: Collection): object { return { title: entity.title, description: entity.description, - feature_image: entity.feature_image + feature_image: entity.featureImage }; } } diff --git a/ghost/collections/src/CollectionsService.ts b/ghost/collections/src/CollectionsService.ts index 39efa1e21a..26a7196c6d 100644 --- a/ghost/collections/src/CollectionsService.ts +++ b/ghost/collections/src/CollectionsService.ts @@ -12,7 +12,7 @@ export class CollectionsService { } async save(data: any): Promise { - const collection = await this.repository.create(data); + const collection = await Collection.create(data); await this.repository.save(collection); return collection; } diff --git a/ghost/collections/src/index.ts b/ghost/collections/src/index.ts index 79533181fb..626ea081dc 100644 --- a/ghost/collections/src/index.ts +++ b/ghost/collections/src/index.ts @@ -1,2 +1,3 @@ export * from './CollectionsService'; export * from './CollectionsRepositoryInMemory'; +export * from './Collection'; diff --git a/ghost/collections/test/Collection.test.ts b/ghost/collections/test/Collection.test.ts new file mode 100644 index 0000000000..89404a687b --- /dev/null +++ b/ghost/collections/test/Collection.test.ts @@ -0,0 +1,72 @@ +import assert from 'assert'; +import ObjectID from 'bson-objectid'; +import {Collection} from '../src/index'; + +describe('Collection', function () { + it('Create Collection entity', async function () { + const collection = await Collection.create({ + title: 'Test Collection' + }); + + assert.ok(collection instanceof Collection); + assert.ok(collection.id, 'generated id should be set'); + assert.ok(ObjectID.isValid(collection.id), 'generated id should be valid ObjectID'); + + assert.equal(collection.title, 'Test Collection'); + assert.ok(collection.createdAt instanceof Date); + assert.ok(collection.updatedAt instanceof Date); + assert.ok((collection.deleted === false), 'deleted should be false'); + }); + + it('Can create a Collection with predefined ID', async function () { + const id = new ObjectID(); + const savedCollection = await Collection.create({ + id: id.toHexString() + }); + + assert.equal(savedCollection.id, id.toHexString(), 'Collection should have same id'); + }); + + it('Can create a Collection with predefined ObjectID instance', async function () { + const id = new ObjectID(); + const savedCollection = await Collection.create({ + id: id + }); + + assert.equal(savedCollection.id, id.toHexString(), 'Collection should have same id'); + }); + + it('Can create a Collection with predefined created_at and updated_at values', async function () { + const createdAt = new Date(); + const updatedAt = new Date(); + const savedCollection = await Collection.create({ + created_at: createdAt, + updated_at: updatedAt + }); + + assert.equal(savedCollection.createdAt, createdAt, 'Collection should have same created_at'); + assert.equal(savedCollection.updatedAt, updatedAt, 'Collection should have same updated_at'); + }); + + it('Throws an error when trying to create a Collection with an invalid ID', async function () { + assert.rejects(async () => { + await Collection.create({ + id: 12345 + }); + }, (err: any) => { + assert.equal(err.message, 'Invalid ID provided for Collection', 'Error message should match'); + return true; + }); + }); + + it('Throws an error when trying to create a Collection with invalid created_at date', async function () { + assert.rejects(async () => { + await Collection.create({ + created_at: 'invalid date' + }); + }, (err: any) => { + assert.equal(err.message, 'Invalid date provided for created_at', 'Error message should match'); + return true; + }); + }); +}); diff --git a/ghost/collections/test/collections.test.ts b/ghost/collections/test/collections.test.ts index 36e85986e3..fd24ce9325 100644 --- a/ghost/collections/test/collections.test.ts +++ b/ghost/collections/test/collections.test.ts @@ -40,34 +40,6 @@ describe('collections', function () { assert.equal(deletedCollection, null, 'Collection should be deleted'); }); - it('Can create a collection with predefined ID', async function () { - const id = new ObjectID(); - const savedCollection = await collectionsService.save({ - id: id.toHexString() - }); - - assert.equal(savedCollection.id, id.toHexString(), 'Collection should have same id'); - }); - - it('Can create a collection with predefined ObjectID instance', async function () { - const id = new ObjectID(); - const savedCollection = await collectionsService.save({ - id: id - }); - - assert.equal(savedCollection.id, id.toHexString(), 'Collection should have same id'); - }); - - it('Throws an error when trying to save a collection with an invalid ID', async function () { - try { - await collectionsService.save({ - id: 12345 - }); - } catch (error: any) { - assert.equal(error.message, 'Invalid ID provided for Collection', 'Error message should match'); - } - }); - describe('edit', function () { it('Can edit existing collection', async function () { const savedCollection = await collectionsService.save({