diff --git a/ghost/collections/src/Collection.ts b/ghost/collections/src/Collection.ts index baa29848cd..ba44f9dd7e 100644 --- a/ghost/collections/src/Collection.ts +++ b/ghost/collections/src/Collection.ts @@ -1,10 +1,8 @@ -// have to use requires until there are type definitions for these modules - import {ValidationError} from '@tryghost/errors'; import tpl from '@tryghost/tpl'; import ObjectID from 'bson-objectid'; -import {PostDTO} from './PostDTO'; +import {CollectionPost} from './CollectionPost'; const messages = { invalidIDProvided: 'Invalid ID provided for Collection', @@ -23,7 +21,7 @@ export class Collection { updatedAt: Date; deleted: boolean; - posts: PostDTO[]; + posts: CollectionPost[]; private constructor(data: any) { this.id = data.id; @@ -52,12 +50,26 @@ export class Collection { updatedAt: this.updatedAt, posts: this.posts.map(post => ({ id: post.id, - title: post.title, - slug: post.slug + postId: post.postId, + sortOrder: post.sortOrder })) }; } + async canAddPost(newCollectionPost: CollectionPost): Promise { + const existingCollectionPost = this.posts.find(p => p.id === newCollectionPost.id); + + if (existingCollectionPost) { + return false; + } + + return true; + } + + addPost(collectionPost: CollectionPost) { + this.posts.push(collectionPost); + } + static validateDateField(date: any, fieldName: string): Date { if (!date) { return new Date(); diff --git a/ghost/collections/src/CollectionPost.ts b/ghost/collections/src/CollectionPost.ts new file mode 100644 index 0000000000..9b89e69316 --- /dev/null +++ b/ghost/collections/src/CollectionPost.ts @@ -0,0 +1,47 @@ +import {ValidationError} from '@tryghost/errors'; +import tpl from '@tryghost/tpl'; +import ObjectID from 'bson-objectid'; + +const messages = { + invalidIDProvided: 'Invalid ID provided for CollectionPost' +}; + +export class CollectionPost { + id: string; + postId: string; + collectionId: string; + sortOrder: number; + deleted: boolean; + + constructor(data: any) { + this.id = data.id; + this.postId = data.postId; + this.collectionId = data.collectionId; + this.sortOrder = data.sortOder; + this.deleted = data.deleted; + } + + 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 CollectionPost({ + id: id.toHexString(), + postId: data.post_id, + collectionId: data.collection_id, + sortOrder: data.sort_order, // NOTE: make sort_order required during creation + deleted: false + }); + } +} diff --git a/ghost/collections/src/CollectionsPostsRepositoryInMemory.ts b/ghost/collections/src/CollectionsPostsRepositoryInMemory.ts new file mode 100644 index 0000000000..99317b8fa8 --- /dev/null +++ b/ghost/collections/src/CollectionsPostsRepositoryInMemory.ts @@ -0,0 +1,17 @@ +import {InMemoryRepository} from '@tryghost/in-memory-repository'; +import {CollectionPost} from './CollectionPost'; + +export class CollectionsPostsRepositoryInMemory extends InMemoryRepository { + constructor() { + super(); + } + + protected toPrimitive(entity: CollectionPost): object { + return { + id: entity.id, + post_id: entity.postId, + collection_id: entity.collectionId, + sort_order: entity.sortOrder + }; + } +} diff --git a/ghost/collections/src/CollectionsRepositoryInMemory.ts b/ghost/collections/src/CollectionsRepositoryInMemory.ts index 0904ccf507..ad3a8295ce 100644 --- a/ghost/collections/src/CollectionsRepositoryInMemory.ts +++ b/ghost/collections/src/CollectionsRepositoryInMemory.ts @@ -1,9 +1,18 @@ import {InMemoryRepository} from '@tryghost/in-memory-repository'; import {Collection} from './Collection'; +import {CollectionPost} from './CollectionPost'; export class CollectionsRepositoryInMemory extends InMemoryRepository { - constructor() { + collectionsPostsRepository: any; + + constructor(deps: any) { super(); + + this.collectionsPostsRepository = deps.collectionsPostsRepository; + } + + async saveCollectionPost(collectionPost: CollectionPost) { + await this.collectionsPostsRepository.save(collectionPost); } protected toPrimitive(entity: Collection): object { diff --git a/ghost/collections/src/CollectionsService.ts b/ghost/collections/src/CollectionsService.ts index 38b0e96555..165a050a0d 100644 --- a/ghost/collections/src/CollectionsService.ts +++ b/ghost/collections/src/CollectionsService.ts @@ -1,5 +1,6 @@ import {Collection} from './Collection'; import {PostDTO} from './PostDTO'; +import {CollectionPost} from './CollectionPost'; type CollectionsServiceDeps = { collectionsRepository: any; @@ -8,6 +9,13 @@ type CollectionsServiceDeps = { } }; +type CollectionPostDTO = { + id?: string; + post_id: string; + collection_id: string; + sort_order?: number; +}; + export class CollectionsService { collectionsRepository: any; postsRepository: any; @@ -32,11 +40,36 @@ export class CollectionsService { const postIds = postDTOs.map(post => post.id); const posts = await this.postsRepository.getBulk(postIds); - collection.posts = posts.map((post: any) => post.id); + for (const post of posts) { + const collectionPost = await CollectionPost.create({ + post_id: post.id, + collection_id: collection.id + }); + + collection.addPost(collectionPost); + } return collection; } + async addPost(collectionPost: CollectionPostDTO): Promise { + const collection = await this.collectionsRepository.getById(collectionPost.collection_id); + + if (!collection) { + return null; + } + + if (!collection.canAddPost(collectionPost)) { + return null; + } + + const collectionPostEntity = await CollectionPost.create(collectionPost); + collection.addPost(collectionPostEntity); + const savedPostCollection = await this.collectionsRepository.saveCollectionPost(collectionPostEntity); + + return savedPostCollection; + } + async edit(data: any): Promise { const collection = await this.collectionsRepository.getById(data.id);