From 8026f7e1ccd4e878e55fde0fd52937d672886fd4 Mon Sep 17 00:00:00 2001 From: Sejin Jeon Date: Sun, 1 Aug 2021 04:26:33 +0900 Subject: [PATCH] [#9] Modify `PostService.createPost` Fixed `createPost` to make a default thumbnailContent from the content when thumbnailContent is not given. --- backend/.nycrc | 2 +- .../MarkedContentRenderingUtil.ts} | 10 +++++----- .../src/common/marked/MarkedMetaDataUtil.ts | 16 ++++++++++++++++ backend/src/post/PostService.ts | 10 +++++----- backend/src/post/dto/PostParamDto.ts | 2 +- backend/src/post/dto/PostRequestDto.ts | 6 +++--- backend/test/data/testData.ts | 4 ---- backend/test/post/PostRepositoryTest.ts | 6 ++++-- backend/test/post/PostRouterTest.ts | 3 --- backend/test/post/PostServiceTest.ts | 19 +++++++++++++++---- 10 files changed, 50 insertions(+), 28 deletions(-) rename backend/src/common/{markdown/MarkedUtil.ts => marked/MarkedContentRenderingUtil.ts} (93%) create mode 100644 backend/src/common/marked/MarkedMetaDataUtil.ts diff --git a/backend/.nycrc b/backend/.nycrc index 8efa849..e285de6 100644 --- a/backend/.nycrc +++ b/backend/.nycrc @@ -6,6 +6,6 @@ "exclude": [ "test/", "coverage/", - "src/common/markdown/MarkedUtil.ts" + "src/common/marked/MarkedContentRenderingUtil.ts" ] } diff --git a/backend/src/common/markdown/MarkedUtil.ts b/backend/src/common/marked/MarkedContentRenderingUtil.ts similarity index 93% rename from backend/src/common/markdown/MarkedUtil.ts rename to backend/src/common/marked/MarkedContentRenderingUtil.ts index 6f46c95..686db69 100644 --- a/backend/src/common/markdown/MarkedUtil.ts +++ b/backend/src/common/marked/MarkedContentRenderingUtil.ts @@ -3,8 +3,9 @@ import marked, { Tokenizer, TokensList } from 'marked'; import hljs from 'highlight.js'; import katex from 'katex'; import { Heading } from '@src/post/model/Post'; +import { makeDefaultThumbnailContent, makeToc } from '@src/common/marked/MarkedMetaDataUtil'; -export const renderContent = (rawContent: string): { renderedContent: string, toc: Heading[] } => { +export const renderContent = (rawContent: string): { renderedContent: string, toc: Heading[], defaultThumbnailContent: string } => { const MATH: string = 'Math'; const renderer = { @@ -147,10 +148,9 @@ export const renderContent = (rawContent: string): { renderedContent: string, to } }); - const toc: Heading[] = (tokenList - .filter((token) => token.type === 'heading') as Heading[]) - .map((token) => ({ depth: token.depth, text: token.text })); const renderedContent: string = marked.parser(tokenList); + const toc: Heading[] = makeToc(tokenList); + const defaultThumbnailContent: string = makeDefaultThumbnailContent(tokenList); - return { renderedContent, toc }; + return { renderedContent, toc, defaultThumbnailContent }; }; diff --git a/backend/src/common/marked/MarkedMetaDataUtil.ts b/backend/src/common/marked/MarkedMetaDataUtil.ts new file mode 100644 index 0000000..dbdcef2 --- /dev/null +++ b/backend/src/common/marked/MarkedMetaDataUtil.ts @@ -0,0 +1,16 @@ +import { Heading } from '@src/post/model/Post'; +import { TokensList } from 'marked'; + +export const makeToc = (tokenList: TokensList): Heading[] => (tokenList + .filter((token) => token.type === 'heading') as Heading[]) + .map((token) => ({ depth: token.depth, text: token.text })); + +export const makeDefaultThumbnailContent = (tokenList: TokensList): string => tokenList + .filter((token) => token.type === 'paragraph') + // @ts-ignore + .map((token) => token.tokens + .map((nestedToken) => (['br', 'space'].includes(nestedToken.type) ? ' ' : nestedToken.text)) + .join('')) + .join(' ') + .slice(0, 300) + .concat('...'); diff --git a/backend/src/post/PostService.ts b/backend/src/post/PostService.ts index ea8caac..f2ddbd2 100644 --- a/backend/src/post/PostService.ts +++ b/backend/src/post/PostService.ts @@ -7,7 +7,7 @@ import PostRepository from '@src/post/repository/PostRepository'; import CategoryRepository from '@src/category/CategoryRepository'; import SeriesRepository from '@src/series/SeriesRepository'; import TagRepository from '@src/tag/TagRepository'; -import { renderContent } from '@src/common/markdown/MarkedUtil'; +import { renderContent } from '@src/common/marked/MarkedContentRenderingUtil'; import { CreatePostMetaRepoParamDto } from '@src/post/dto/PostMetaRepoParamDto'; import { CreateNewPostParamDto } from '@src/post/dto/PostParamDto'; import { CreatePostRepoParamDto } from '@src/post/dto/PostRepoParamDto'; @@ -71,17 +71,17 @@ export default class PostService { } private makeCreatePostRepoParamDto(postNo: number, paramDto: CreateNewPostParamDto, currentDate: Date): CreatePostRepoParamDto { - const { post } = paramDto; + const { post, language, thumbnailContent } = paramDto; const rawContent: string = this.readPostContent(post.path); - const { renderedContent, toc } = renderContent(rawContent); + const { renderedContent, toc, defaultThumbnailContent } = renderContent(rawContent); const createPostRepoParamDto: CreatePostRepoParamDto = { postNo, title: post.name as string, rawContent, renderedContent, toc, - language: paramDto.language, - thumbnailContent: paramDto.thumbnailContent, + language, + thumbnailContent: _.isNil(thumbnailContent) ? defaultThumbnailContent : thumbnailContent, lastUpdatedDate: currentDate, isLatestVersion: true, }; diff --git a/backend/src/post/dto/PostParamDto.ts b/backend/src/post/dto/PostParamDto.ts index cecf2c0..507725a 100644 --- a/backend/src/post/dto/PostParamDto.ts +++ b/backend/src/post/dto/PostParamDto.ts @@ -12,7 +12,7 @@ export interface CreateNewPostParamDto { // post post: File; language: Language; - thumbnailContent: string; + thumbnailContent?: string; thumbnailImageId?: string; } diff --git a/backend/src/post/dto/PostRequestDto.ts b/backend/src/post/dto/PostRequestDto.ts index edc1ac1..458c3c9 100644 --- a/backend/src/post/dto/PostRequestDto.ts +++ b/backend/src/post/dto/PostRequestDto.ts @@ -10,7 +10,7 @@ export interface CreateNewPostRequestDto { // post language: Language; - thumbnailContent: string; + thumbnailContent?: string; thumbnailImageId?: string; } @@ -24,8 +24,8 @@ export const CreateNewPostRequestSchema: JSONSchemaType isPrivate: { type: 'boolean', nullable: true }, language: { type: 'string', nullable: false }, - thumbnailContent: { type: 'string', nullable: false }, + thumbnailContent: { type: 'string', nullable: true }, thumbnailImageId: { type: 'string', nullable: true }, }, - required: ['language', 'thumbnailContent'], + required: ['language'], }; diff --git a/backend/test/data/testData.ts b/backend/test/data/testData.ts index dd0e5b7..9ebac5f 100644 --- a/backend/test/data/testData.ts +++ b/backend/test/data/testData.ts @@ -97,7 +97,6 @@ export const common = { renderedContent: '

온 김에 스프링하기

왜 우리는 노드만으로 행복할 수 없는가

', toc: [{ depth: 2, text: '온 김에 스프링하기' }], language: Language.KO, - thumbnailContent: '고양이', isLatestVersion: true, }, post1DataToBeUpdatedFirst: { @@ -115,7 +114,6 @@ export const common = { renderedContent: '

심슨가족 시리즈의 무대가 되는 도시다. Pivotal의 Spring과는 별 관계가 없다.

', toc: [], language: Language.KO, - thumbnailContent: '강아지', isLatestVersion: true, }, post2En: { @@ -124,7 +122,6 @@ export const common = { rawContent: 'It\'s the city set for Simpson series. It has little related points with Spring of Pivotal.', renderedContent: '

It\'s the city set for Simpson series. It has little related points with Spring of Pivotal.

', language: Language.EN, - thumbnailContent: 'sob', isLatestVersion: true, }, post3: { @@ -133,7 +130,6 @@ export const common = { rawContent: '걔가 누군지는 잘 모르겠는데 우시앞무선은 장난 아니게 좋더라', renderedContent: '

걔가 누군지는 잘 모르겠는데 우시앞무선은 장난 아니게 좋더라

', language: Language.KO, - thumbnailContent: '너무 빨리 지나가', isLatestVersion: true, }, postMetaIdList: ['507f1f77bcf86cd799439011', '507f1f77bcf86cd799439022', '507f1f77bcf86cd799439033'], diff --git a/backend/test/post/PostRepositoryTest.ts b/backend/test/post/PostRepositoryTest.ts index b8d3ba3..a7f2ee4 100644 --- a/backend/test/post/PostRepositoryTest.ts +++ b/backend/test/post/PostRepositoryTest.ts @@ -49,11 +49,13 @@ describe('PostRepository test', () => { it('createPost', async () => { const paramDto1: CreatePostRepoParamDto = { ...commonTestData.post1, + thumbnailContent: commonTestData.simpleText, thumbnailImageId: gifImage, lastUpdatedDate: commonTestData.dateList[0], }; const paramDto2: CreatePostRepoParamDto = { ...commonTestData.post2, + thumbnailContent: commonTestData.simpleText, lastUpdatedDate: commonTestData.dateList[1], }; @@ -71,7 +73,7 @@ describe('PostRepository test', () => { post1.rawContent.should.equal(commonTestData.post1.rawContent); post1.renderedContent.should.equal(commonTestData.post1.renderedContent); post1.language.should.equal(commonTestData.post1.language); - post1.thumbnailContent.should.equal(commonTestData.post1.thumbnailContent); + post1.thumbnailContent.should.equal(commonTestData.simpleText); post1.thumbnailImage!.should.deep.equal(gifImage._id); post1.lastUpdatedDate.should.deep.equal(commonTestData.dateList[0]); post1.isLatestVersion.should.equal(commonTestData.post1.isLatestVersion); @@ -80,7 +82,7 @@ describe('PostRepository test', () => { post2.rawContent.should.equal(commonTestData.post2.rawContent); post2.renderedContent.should.equal(commonTestData.post2.renderedContent); post2.language.should.equal(commonTestData.post2.language); - post2.thumbnailContent.should.equal(commonTestData.post2.thumbnailContent); + post2.thumbnailContent.should.equal(commonTestData.simpleText); (post2.thumbnailImage === null).should.be.true; post2.lastUpdatedDate.should.deep.equal(commonTestData.dateList[1]); post2.isLatestVersion.should.equal(commonTestData.post2.isLatestVersion); diff --git a/backend/test/post/PostRouterTest.ts b/backend/test/post/PostRouterTest.ts index 50b6730..aaa3bc6 100644 --- a/backend/test/post/PostRouterTest.ts +++ b/backend/test/post/PostRouterTest.ts @@ -34,7 +34,6 @@ describe('Post router test', () => { const requestDto: CreateNewPostRequestDto = { seriesName: commonTestData.series1.name, language: Language.KO, - thumbnailContent: '뚜샤!', }; when(postService.createNewPost(anything())) @@ -55,7 +54,6 @@ describe('Post router test', () => { const requestDto: CreateNewPostRequestDto = { seriesName: commonTestData.series1.name, language: Language.KO, - thumbnailContent: '뚜샤!', }; await request @@ -90,7 +88,6 @@ describe('Post router test', () => { const typeDistortedRequestDto = { seriesName: commonTestData.series1.name, language: Language.KO, - thumbnailContent: '뚜샤!', }; await request diff --git a/backend/test/post/PostServiceTest.ts b/backend/test/post/PostServiceTest.ts index fee26eb..961e064 100644 --- a/backend/test/post/PostServiceTest.ts +++ b/backend/test/post/PostServiceTest.ts @@ -57,10 +57,11 @@ describe('PostService test', () => { ({ file, fileContent } = extractFileInfoFromRawFile('gfm+.md')); }); - it('createNewPost test - categoryName: X, seriesName: X, tagNameList: X, isPrivate: X, thumbnailImageId: X', async () => { + it('createNewPost test - categoryName: X, seriesName: X, tagNameList: X, isPrivate: X, thumbnailImageId: X, thumbnailContent: O', async () => { const serviceParamDto: CreateNewPostParamDto = { post: file, ...commonTestData.post1, + thumbnailContent: commonTestData.simpleText, // Absence of thumbnailContent will be tested in marked rendering section }; when(postMetaRepository.createPostMeta(anything())) .thenResolve(commonTestData.post1.postNo); @@ -85,17 +86,18 @@ describe('PostService test', () => { createPostRepoParamDto.renderedContent.should.not.be.empty; createPostRepoParamDto.renderedContent.should.not.equal(fileContent); createPostRepoParamDto.language.should.equal(commonTestData.post1.language); - createPostRepoParamDto.thumbnailContent.should.equal(commonTestData.post1.thumbnailContent); + createPostRepoParamDto.thumbnailContent.should.equal(commonTestData.simpleText); (createPostRepoParamDto.thumbnailImageId === undefined).should.be.true; createPostRepoParamDto.isLatestVersion.should.be.true; (createPostRepoParamDto.lastVersionPost === undefined).should.be.true; createPostRepoParamDto.lastUpdatedDate!.should.within(date1, date2); }); - it('createNewPost test - categoryName: O, seriesName: O, tagNameList: O, isPrivate: O, thumbnailImageId: O', async () => { + it('createNewPost test - categoryName: O, seriesName: O, tagNameList: O, isPrivate: O, thumbnailImageId: O, thumbnailContent: O', async () => { const serviceParamDto: CreateNewPostParamDto = { post: file, ...commonTestData.post1, + thumbnailContent: commonTestData.simpleText, thumbnailImageId: gifImageId, categoryName: commonTestData.category1.name, seriesName: commonTestData.series1.name, @@ -139,7 +141,7 @@ describe('PostService test', () => { createPostRepoParamDto.renderedContent.should.not.be.empty; createPostRepoParamDto.renderedContent.should.not.equal(fileContent); createPostRepoParamDto.language.should.equal(commonTestData.post1.language); - createPostRepoParamDto.thumbnailContent.should.equal(commonTestData.post1.thumbnailContent); + createPostRepoParamDto.thumbnailContent.should.equal(commonTestData.simpleText); (createPostRepoParamDto.thumbnailImageId!.equals(gifImageId)).should.be.true; createPostRepoParamDto.isLatestVersion.should.be.true; (createPostRepoParamDto.lastVersionPost === undefined).should.be.true; @@ -150,6 +152,7 @@ describe('PostService test', () => { const serviceParamDto: CreateNewPostParamDto = { post: file, ...commonTestData.post1, + thumbnailContent: commonTestData.simpleText, categoryName: commonTestData.category1.name, seriesName: commonTestData.series1.name, tagNameList: [commonTestData.tag1.name, commonTestData.tag2.name], @@ -180,6 +183,7 @@ describe('PostService test', () => { const serviceParamDto: CreateNewPostParamDto = { post: file, ...commonTestData.post1, + thumbnailContent: commonTestData.simpleText, categoryName: commonTestData.category1.name, seriesName: commonTestData.series1.name, tagNameList: [commonTestData.tag1.name, commonTestData.tag2.name], @@ -210,6 +214,7 @@ describe('PostService test', () => { const serviceParamDto: CreateNewPostParamDto = { post: file, ...commonTestData.post1, + thumbnailContent: commonTestData.simpleText, categoryName: commonTestData.category1.name, seriesName: commonTestData.series1.name, tagNameList: [commonTestData.tag1.name, commonTestData.tag2.name], @@ -288,6 +293,8 @@ describe('PostService test', () => { { depth: 3, text: 'two new lines between normal lines' }, { depth: 3, text: 'three new lines between normal lines' }, ]); + createPostRepoParamDto.thumbnailContent.length.should.be.greaterThan(0); + createPostRepoParamDto.thumbnailContent.length.should.be.lessThanOrEqual(303); }); it('content rendering test - code blocks', async () => { @@ -316,6 +323,8 @@ describe('PostService test', () => { { depth: 3, text: 'list indentation 2단계 (tab & 2 spaces mixed)' }, { depth: 3, text: 'inline code' }, ]); + createPostRepoParamDto.thumbnailContent.length.should.be.greaterThan(0); + createPostRepoParamDto.thumbnailContent.length.should.be.lessThanOrEqual(303); }); it('content rendering test - mathematical expressions', async () => { @@ -352,6 +361,8 @@ describe('PostService test', () => { { depth: 3, text: 'list indentation 2단계 (2 spaces)' }, { depth: 3, text: 'list indentation 2단계 (4 spaces)' }, ]); + createPostRepoParamDto.thumbnailContent.length.should.be.greaterThan(0); + createPostRepoParamDto.thumbnailContent.length.should.be.lessThanOrEqual(303); }); }); });