From f111685c5886f3e77edfd3621c98d8ac1b9bcce4 Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Fri, 24 Nov 2023 20:00:08 +0100 Subject: refactor(services, types): reorganize GraphQL fetchers and data types The Typescript mapped types was useful for autocompletion in fetchers but their are harder to maintain. I think it's better to keep each query close to its fetcher to have a better understanding of the fetched data. So I: * colocate queries with their own fetcher * colocate mutations with their own mutator * remove Typescript mapped types for queries and mutations * move data convertors inside graphql services * rename most of data types and fetchers --- .../graphql/helpers/build-comments-tree.test.ts | 67 +++++++++++ .../graphql/helpers/build-comments-tree.ts | 30 +++++ ...convert-post-preview-to-article-preview.test.ts | 130 +++++++++++++++++++++ .../convert-post-preview-to-article-preview.ts | 36 ++++++ .../helpers/convert-post-to-article.test.ts | 125 ++++++++++++++++++++ .../graphql/helpers/convert-post-to-article.ts | 43 +++++++ .../convert-recent-post-to-recent-article.test.ts | 48 ++++++++ .../convert-recent-post-to-recent-article.ts | 24 ++++ .../helpers/convert-taxonomy-to-page-link.test.ts | 52 +++++++++ .../helpers/convert-taxonomy-to-page-link.ts | 23 ++++ .../helpers/convert-wp-comment-to-comment.test.ts | 93 +++++++++++++++ .../helpers/convert-wp-comment-to-comment.ts | 35 ++++++ .../helpers/convert-wp-image-to-img.test.ts | 41 +++++++ .../graphql/helpers/convert-wp-image-to-img.ts | 16 +++ src/services/graphql/helpers/index.ts | 7 ++ 15 files changed, 770 insertions(+) create mode 100644 src/services/graphql/helpers/build-comments-tree.test.ts create mode 100644 src/services/graphql/helpers/build-comments-tree.ts create mode 100644 src/services/graphql/helpers/convert-post-preview-to-article-preview.test.ts create mode 100644 src/services/graphql/helpers/convert-post-preview-to-article-preview.ts create mode 100644 src/services/graphql/helpers/convert-post-to-article.test.ts create mode 100644 src/services/graphql/helpers/convert-post-to-article.ts create mode 100644 src/services/graphql/helpers/convert-recent-post-to-recent-article.test.ts create mode 100644 src/services/graphql/helpers/convert-recent-post-to-recent-article.ts create mode 100644 src/services/graphql/helpers/convert-taxonomy-to-page-link.test.ts create mode 100644 src/services/graphql/helpers/convert-taxonomy-to-page-link.ts create mode 100644 src/services/graphql/helpers/convert-wp-comment-to-comment.test.ts create mode 100644 src/services/graphql/helpers/convert-wp-comment-to-comment.ts create mode 100644 src/services/graphql/helpers/convert-wp-image-to-img.test.ts create mode 100644 src/services/graphql/helpers/convert-wp-image-to-img.ts create mode 100644 src/services/graphql/helpers/index.ts (limited to 'src/services/graphql/helpers') diff --git a/src/services/graphql/helpers/build-comments-tree.test.ts b/src/services/graphql/helpers/build-comments-tree.test.ts new file mode 100644 index 0000000..cd9fa40 --- /dev/null +++ b/src/services/graphql/helpers/build-comments-tree.test.ts @@ -0,0 +1,67 @@ +import { describe, expect, it } from '@jest/globals'; +import type { SingleComment } from '../../../types'; +import { buildCommentsTree } from './build-comments-tree'; + +describe('build-comments-tree', () => { + it('transforms a flat comments array to a comments tree', () => { + const firstComment = { + content: 'Non non provident mollitia a.', + id: 1, + isApproved: true, + meta: { author: { name: 'Emma_Muller' }, date: '2022-11-02' }, + replies: [], + } satisfies SingleComment; + const firstCommentReplies = [ + { + content: 'Et omnis voluptatem est atque.', + id: 3, + isApproved: true, + meta: { author: { name: 'Patrick.Goodwin44' }, date: '2022-11-05' }, + replies: [], + parentId: 1, + }, + ] satisfies SingleComment[]; + const secondComment = { + content: 'Vero iure architecto modi iusto qui.', + id: 2, + isApproved: true, + meta: { author: { name: 'Dominique13' }, date: '2022-11-04' }, + replies: [], + } satisfies SingleComment; + const secondCommentReplies = [ + { + content: 'Qui quaerat quas quia praesentium quasi.', + id: 4, + isApproved: true, + meta: { author: { name: 'Patrick.Goodwin44' }, date: '2022-11-05' }, + replies: [], + parentId: 2, + }, + { + content: 'Ut officia aliquid harum voluptas molestiae quo.', + id: 5, + isApproved: true, + meta: { author: { name: 'Ariel.Braun6' }, date: '2022-11-06' }, + replies: [], + parentId: 2, + }, + ] satisfies SingleComment[]; + const comments: SingleComment[] = [ + firstComment, + secondComment, + ...firstCommentReplies, + ...secondCommentReplies, + ]; + const result = buildCommentsTree(comments); + + expect(result).toHaveLength(2); + expect(result[0]).toStrictEqual({ + ...firstComment, + replies: firstCommentReplies, + }); + expect(result[1]).toStrictEqual({ + ...secondComment, + replies: secondCommentReplies, + }); + }); +}); diff --git a/src/services/graphql/helpers/build-comments-tree.ts b/src/services/graphql/helpers/build-comments-tree.ts new file mode 100644 index 0000000..1534cfe --- /dev/null +++ b/src/services/graphql/helpers/build-comments-tree.ts @@ -0,0 +1,30 @@ +import type { SingleComment } from '../../../types'; + +/** + * Create a comments tree with replies. + * + * @param {SingleComment[]} comments - A flatten comments list. + * @returns {SingleComment[]} An array of comments with replies. + */ +export const buildCommentsTree = ( + comments: SingleComment[] +): SingleComment[] => { + type CommentsHashTable = Record; + + const hashTable: CommentsHashTable = Object.create(null); + const commentsTree: SingleComment[] = []; + + comments.forEach((comment) => { + hashTable[comment.id] = { ...comment, replies: [] }; + }); + + comments.forEach((comment) => { + if (comment.parentId) { + hashTable[comment.parentId].replies.push(hashTable[comment.id]); + } else { + commentsTree.push(hashTable[comment.id]); + } + }); + + return commentsTree; +}; diff --git a/src/services/graphql/helpers/convert-post-preview-to-article-preview.test.ts b/src/services/graphql/helpers/convert-post-preview-to-article-preview.test.ts new file mode 100644 index 0000000..c13684f --- /dev/null +++ b/src/services/graphql/helpers/convert-post-preview-to-article-preview.test.ts @@ -0,0 +1,130 @@ +import { describe, expect, it } from '@jest/globals'; +import type { WPPostPreview, WPThematicPreview } from '../../../types'; +import { convertPostPreviewToArticlePreview } from './convert-post-preview-to-article-preview'; +import { convertTaxonomyToPageLink } from './convert-taxonomy-to-page-link'; +import { convertWPImgToImg } from './convert-wp-image-to-img'; + +describe('convert-post-preview-to-article-preview', () => { + /* eslint-disable max-statements */ + it('converts a RecentWPPost object to a RecentArticle object', () => { + const post: WPPostPreview = { + acfPosts: null, + commentCount: 6, + contentParts: { + beforeMore: + 'Et quos fuga molestias. Voluptatum nobis mollitia eaque dolorem sunt. Dolores dignissimos consequuntur mollitia. Enim molestias quibusdam sequi. Dolore ut quo. Libero iure non vel reprehenderit.', + }, + databaseId: 5, + date: '2021-04-28', + featuredImage: null, + info: { + wordsCount: 450, + }, + modified: '2021-04-29', + slug: '/the-post-slug', + title: 'et tempore sint', + }; + const result = convertPostPreviewToArticlePreview(post); + + expect(result.id).toBe(post.databaseId); + expect(result.intro).toBe(post.contentParts.beforeMore); + expect(result.meta.commentsCount).toBe(post.commentCount); + expect(result.meta.cover).toBeUndefined(); + expect(result.meta.dates.publication).toBe(post.date); + expect(result.meta.dates.update).toBe(post.modified); + expect(result.meta.thematics).toBeUndefined(); + expect(result.meta.wordsCount).toBe(post.info.wordsCount); + expect(result.slug).toBe(post.slug); + expect(result.title).toBe(post.title); + }); + /* eslint-enable max-statements */ + + it('can return 0 as comment count if not defined', () => { + const post: WPPostPreview = { + acfPosts: null, + commentCount: null, + contentParts: { + beforeMore: + 'Et quos fuga molestias. Voluptatum nobis mollitia eaque dolorem sunt. Dolores dignissimos consequuntur mollitia. Enim molestias quibusdam sequi. Dolore ut quo. Libero iure non vel reprehenderit.', + }, + databaseId: 5, + date: '2021-04-28', + featuredImage: null, + info: { + wordsCount: 450, + }, + modified: '2021-04-29', + slug: '/the-post-slug', + title: 'et tempore sint', + }; + const result = convertPostPreviewToArticlePreview(post); + + expect(result.meta.commentsCount).toBe(0); + }); + + it('can convert the cover', () => { + const post = { + acfPosts: null, + commentCount: null, + contentParts: { + beforeMore: + 'Et quos fuga molestias. Voluptatum nobis mollitia eaque dolorem sunt. Dolores dignissimos consequuntur mollitia. Enim molestias quibusdam sequi. Dolore ut quo. Libero iure non vel reprehenderit.', + }, + databaseId: 5, + date: '2021-04-28', + featuredImage: { + node: { + altText: 'molestiae praesentium animi', + mediaDetails: { + height: 480, + width: 640, + }, + sourceUrl: 'https://picsum.photos/640/480', + title: 'ullam deserunt perspiciatis', + }, + }, + info: { + wordsCount: 450, + }, + modified: '2021-04-29', + slug: '/the-post-slug', + title: 'et tempore sint', + } satisfies WPPostPreview; + const result = convertPostPreviewToArticlePreview(post); + + expect(result.meta.cover).toStrictEqual( + convertWPImgToImg(post.featuredImage.node) + ); + }); + + it('can convert the thematics', () => { + const thematics: WPThematicPreview[] = [ + { databaseId: 2, slug: '/thematic1', title: 'aut quis vel' }, + { databaseId: 8, slug: '/thematic2', title: 'vel sint autem' }, + ]; + const post: WPPostPreview = { + acfPosts: { + postsInThematic: thematics, + }, + commentCount: 6, + contentParts: { + beforeMore: + 'Et quos fuga molestias. Voluptatum nobis mollitia eaque dolorem sunt. Dolores dignissimos consequuntur mollitia. Enim molestias quibusdam sequi. Dolore ut quo. Libero iure non vel reprehenderit.', + }, + databaseId: 5, + date: '2021-04-28', + featuredImage: null, + info: { + wordsCount: 450, + }, + modified: '2021-04-29', + slug: '/the-post-slug', + title: 'et tempore sint', + }; + const result = convertPostPreviewToArticlePreview(post); + + expect(result.meta.thematics).toStrictEqual( + thematics.map(convertTaxonomyToPageLink) + ); + }); +}); diff --git a/src/services/graphql/helpers/convert-post-preview-to-article-preview.ts b/src/services/graphql/helpers/convert-post-preview-to-article-preview.ts new file mode 100644 index 0000000..78777eb --- /dev/null +++ b/src/services/graphql/helpers/convert-post-preview-to-article-preview.ts @@ -0,0 +1,36 @@ +import type { ArticlePreview, WPPostPreview } from '../../../types'; +import { convertTaxonomyToPageLink } from './convert-taxonomy-to-page-link'; +import { convertWPImgToImg } from './convert-wp-image-to-img'; + +export const convertPostPreviewToArticlePreview = ({ + acfPosts, + commentCount, + contentParts, + databaseId, + date, + featuredImage, + info, + modified, + slug, + title, +}: WPPostPreview): ArticlePreview => { + return { + id: databaseId, + intro: contentParts.beforeMore, + meta: { + commentsCount: typeof commentCount === 'number' ? commentCount : 0, + cover: featuredImage ? convertWPImgToImg(featuredImage.node) : undefined, + dates: { + publication: date, + update: modified, + }, + thematics: + acfPosts && 'postsInThematic' in acfPosts + ? acfPosts.postsInThematic?.map(convertTaxonomyToPageLink) + : undefined, + wordsCount: info.wordsCount, + }, + slug, + title, + }; +}; diff --git a/src/services/graphql/helpers/convert-post-to-article.test.ts b/src/services/graphql/helpers/convert-post-to-article.test.ts new file mode 100644 index 0000000..0a1c359 --- /dev/null +++ b/src/services/graphql/helpers/convert-post-to-article.test.ts @@ -0,0 +1,125 @@ +import { describe, expect, it } from '@jest/globals'; +import type { WPPost } from '../../../types'; +import { convertPostToArticle } from './convert-post-to-article'; +import { convertWPImgToImg } from './convert-wp-image-to-img'; + +describe('convert-post-to-article', () => { + /* eslint-disable max-statements */ + it('converts a WPPost object to an Article object', async () => { + const post: WPPost = { + acfPosts: null, + author: { node: { name: 'Vince5' } }, + commentCount: 10, + contentParts: { + afterMore: + 'Eum est rerum neque placeat iure veniam enim consequatur assumenda. Quos eos placeat ea et vel sit ratione fugit. Modi qui sint iure beatae illo voluptas.', + beforeMore: + 'Omnis ab qui dolorem praesentium voluptas asperiores officiis. Id nostrum minus quae ducimus tenetur eum a rem eum. Aut odio libero sit soluta ullam odit.', + }, + databaseId: 8, + date: '2022-05-04', + featuredImage: null, + info: { wordsCount: 300 }, + modified: '2022-06-01', + seo: { + metaDesc: 'Est non debitis quas harum quasi voluptatem qui.', + title: 'consequuntur molestiae amet', + }, + slug: '/the-post-slug', + title: 'ea vero repellat', + }; + const result = await convertPostToArticle(post); + + // eslint-disable-next-line @typescript-eslint/no-magic-numbers + expect.assertions(15); + + expect(result.content).toBe(post.contentParts.afterMore); + expect(result.id).toBe(post.databaseId); + expect(result.intro).toBe(post.contentParts.beforeMore); + expect(result.meta.author).toBe(post.author.node.name); + expect(result.meta.commentsCount).toBe(post.commentCount); + expect(result.meta.cover).toBeUndefined(); + expect(result.meta.dates.publication).toBe(post.date); + expect(result.meta.dates.update).toBe(post.modified); + expect(result.meta.seo.description).toBe(post.seo.metaDesc); + expect(result.meta.seo.title).toBe(post.seo.title); + expect(result.meta.thematics).toBeUndefined(); + expect(result.meta.topics).toBeUndefined(); + expect(result.meta.wordsCount).toBe(post.info.wordsCount); + expect(result.slug).toBe(post.slug); + expect(result.title).toBe(post.title); + }); + /* eslint-enable max-statements */ + + it('can convert the cover', async () => { + const post = { + acfPosts: null, + author: { node: { name: 'Vince5' } }, + commentCount: null, + contentParts: { + afterMore: + 'Eum est rerum neque placeat iure veniam enim consequatur assumenda. Quos eos placeat ea et vel sit ratione fugit. Modi qui sint iure beatae illo voluptas.', + beforeMore: + 'Omnis ab qui dolorem praesentium voluptas asperiores officiis. Id nostrum minus quae ducimus tenetur eum a rem eum. Aut odio libero sit soluta ullam odit.', + }, + databaseId: 8, + date: '2022-05-04', + featuredImage: { + node: { + altText: 'molestiae praesentium animi', + mediaDetails: { + height: 480, + width: 640, + }, + sourceUrl: 'https://picsum.photos/640/480', + title: 'ullam deserunt perspiciatis', + }, + }, + info: { wordsCount: 300 }, + modified: '2022-06-01', + seo: { + metaDesc: 'Est non debitis quas harum quasi voluptatem qui.', + title: 'consequuntur molestiae amet', + }, + slug: '/the-post-slug', + title: 'ea vero repellat', + } satisfies WPPost; + const result = await convertPostToArticle(post); + + expect.assertions(1); + + expect(result.meta.cover).toStrictEqual( + convertWPImgToImg(post.featuredImage.node) + ); + }); + + it('can return 0 as comment count when not defined', async () => { + const post: WPPost = { + acfPosts: null, + author: { node: { name: 'Vince5' } }, + commentCount: null, + contentParts: { + afterMore: + 'Eum est rerum neque placeat iure veniam enim consequatur assumenda. Quos eos placeat ea et vel sit ratione fugit. Modi qui sint iure beatae illo voluptas.', + beforeMore: + 'Omnis ab qui dolorem praesentium voluptas asperiores officiis. Id nostrum minus quae ducimus tenetur eum a rem eum. Aut odio libero sit soluta ullam odit.', + }, + databaseId: 8, + date: '2022-05-04', + featuredImage: null, + info: { wordsCount: 300 }, + modified: '2022-06-01', + seo: { + metaDesc: 'Est non debitis quas harum quasi voluptatem qui.', + title: 'consequuntur molestiae amet', + }, + slug: '/the-post-slug', + title: 'ea vero repellat', + }; + const result = await convertPostToArticle(post); + + expect.assertions(1); + + expect(result.meta.commentsCount).toBe(0); + }); +}); diff --git a/src/services/graphql/helpers/convert-post-to-article.ts b/src/services/graphql/helpers/convert-post-to-article.ts new file mode 100644 index 0000000..b540a77 --- /dev/null +++ b/src/services/graphql/helpers/convert-post-to-article.ts @@ -0,0 +1,43 @@ +import type { Article, WPPost } from '../../../types'; +import { updateContentTree } from '../../../utils/helpers'; +import { convertTaxonomyToPageLink } from './convert-taxonomy-to-page-link'; +import { convertWPImgToImg } from './convert-wp-image-to-img'; + +export const convertPostToArticle = async ({ + acfPosts, + author, + commentCount, + contentParts, + databaseId, + date, + featuredImage, + info, + modified, + seo, + slug, + title, +}: WPPost): Promise
=> { + return { + content: await updateContentTree(contentParts.afterMore), + id: databaseId, + intro: contentParts.beforeMore, + meta: { + author: author.node.name, + commentsCount: commentCount ?? 0, + cover: featuredImage ? convertWPImgToImg(featuredImage.node) : undefined, + dates: { + publication: date, + update: modified, + }, + seo: { + description: seo.metaDesc, + title: seo.title, + }, + thematics: acfPosts?.postsInThematic?.map(convertTaxonomyToPageLink), + topics: acfPosts?.postsInTopic?.map(convertTaxonomyToPageLink), + wordsCount: info.wordsCount, + }, + slug, + title, + }; +}; diff --git a/src/services/graphql/helpers/convert-recent-post-to-recent-article.test.ts b/src/services/graphql/helpers/convert-recent-post-to-recent-article.test.ts new file mode 100644 index 0000000..8acf753 --- /dev/null +++ b/src/services/graphql/helpers/convert-recent-post-to-recent-article.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from '@jest/globals'; +import type { RecentWPPost } from '../../../types'; +import { convertRecentPostToRecentArticle } from './convert-recent-post-to-recent-article'; +import { convertWPImgToImg } from './convert-wp-image-to-img'; + +describe('convert-recent-post-to-recent-article', () => { + it('converts a RecentWPPost object to a RecentArticle object', () => { + const post: RecentWPPost = { + databaseId: 5, + date: '2022-03-20', + featuredImage: null, + slug: '/the-post-slug', + title: 'veritatis ex autem', + }; + const result = convertRecentPostToRecentArticle(post); + + expect(result.cover).toBeUndefined(); + expect(result.id).toBe(post.databaseId); + expect(result.publicationDate).toBe(post.date); + expect(result.slug).toBe(post.slug); + expect(result.title).toBe(post.title); + }); + + it('can convert the cover', () => { + const post = { + databaseId: 5, + date: '2022-03-20', + featuredImage: { + node: { + altText: 'molestiae praesentium animi', + mediaDetails: { + height: 480, + width: 640, + }, + sourceUrl: 'https://picsum.photos/640/480', + title: 'ullam deserunt perspiciatis', + }, + }, + slug: '/the-post-slug', + title: 'veritatis ex autem', + } satisfies RecentWPPost; + const result = convertRecentPostToRecentArticle(post); + + expect(result.cover).toStrictEqual( + convertWPImgToImg(post.featuredImage.node) + ); + }); +}); diff --git a/src/services/graphql/helpers/convert-recent-post-to-recent-article.ts b/src/services/graphql/helpers/convert-recent-post-to-recent-article.ts new file mode 100644 index 0000000..ff5eb67 --- /dev/null +++ b/src/services/graphql/helpers/convert-recent-post-to-recent-article.ts @@ -0,0 +1,24 @@ +import type { RecentArticle, RecentWPPost } from '../../../types'; +import { convertWPImgToImg } from './convert-wp-image-to-img'; + +/** + * Convert a WordPress post to an article. + * + * @param {RecentWPPost} post - A post. + * @returns {RecentArticle} An article. + */ +export const convertRecentPostToRecentArticle = ({ + databaseId, + date, + featuredImage, + slug, + title, +}: RecentWPPost): RecentArticle => { + return { + cover: featuredImage ? convertWPImgToImg(featuredImage.node) : undefined, + id: databaseId, + publicationDate: date, + slug, + title, + }; +}; diff --git a/src/services/graphql/helpers/convert-taxonomy-to-page-link.test.ts b/src/services/graphql/helpers/convert-taxonomy-to-page-link.test.ts new file mode 100644 index 0000000..b687ccb --- /dev/null +++ b/src/services/graphql/helpers/convert-taxonomy-to-page-link.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from '@jest/globals'; +import type { WPThematicPreview, WPTopicPreview } from '../../../types'; +import { convertTaxonomyToPageLink } from './convert-taxonomy-to-page-link'; + +describe('convert-taxonomy-to-page-link', () => { + it('can convert a WPThematicPreview object to a Thematic object', () => { + const thematic: WPThematicPreview = { + databaseId: 42, + slug: '/the-thematic-slug', + title: 'et ut alias', + }; + const result = convertTaxonomyToPageLink(thematic); + + expect(result.id).toBe(thematic.databaseId); + expect(result.logo).toBeUndefined(); + expect(result.name).toBe(thematic.title); + expect(result.url).toBe(thematic.slug); + }); + + it('can convert a WPTopicPreview object to a Topic object', () => { + const topic: WPTopicPreview = { + databaseId: 42, + featuredImage: { + node: { + altText: 'dolorem quia possimus', + mediaDetails: { + height: 480, + width: 640, + }, + sourceUrl: 'https://picsum.photos/640/480', + title: 'eos', + }, + }, + slug: '/the-topic-slug', + title: 'et ut alias', + }; + const result = convertTaxonomyToPageLink(topic); + + expect(result.id).toBe(topic.databaseId); + expect(result.logo?.alt).toBe(topic.featuredImage?.node.altText); + expect(result.logo?.height).toBe( + topic.featuredImage?.node.mediaDetails.height + ); + expect(result.logo?.src).toBe(topic.featuredImage?.node.sourceUrl); + expect(result.logo?.title).toBe(topic.featuredImage?.node.title); + expect(result.logo?.width).toBe( + topic.featuredImage?.node.mediaDetails.width + ); + expect(result.name).toBe(topic.title); + expect(result.url).toBe(topic.slug); + }); +}); diff --git a/src/services/graphql/helpers/convert-taxonomy-to-page-link.ts b/src/services/graphql/helpers/convert-taxonomy-to-page-link.ts new file mode 100644 index 0000000..2294fb7 --- /dev/null +++ b/src/services/graphql/helpers/convert-taxonomy-to-page-link.ts @@ -0,0 +1,23 @@ +import type { + PageLink, + WPThematicPreview, + WPTopicPreview, +} from '../../../types'; +import { convertWPImgToImg } from './convert-wp-image-to-img'; + +export const convertTaxonomyToPageLink = ({ + databaseId, + slug, + title, + ...props +}: WPThematicPreview | WPTopicPreview): PageLink => { + return { + id: databaseId, + logo: + 'featuredImage' in props && props.featuredImage + ? convertWPImgToImg(props.featuredImage.node) + : undefined, + name: title, + url: slug, + }; +}; diff --git a/src/services/graphql/helpers/convert-wp-comment-to-comment.test.ts b/src/services/graphql/helpers/convert-wp-comment-to-comment.test.ts new file mode 100644 index 0000000..4b385b4 --- /dev/null +++ b/src/services/graphql/helpers/convert-wp-comment-to-comment.test.ts @@ -0,0 +1,93 @@ +import { describe, expect, it } from '@jest/globals'; +import type { WPComment } from '../../../types'; +import { convertWPCommentToComment } from './convert-wp-comment-to-comment'; + +describe('convert-wp-comment-to-comment', () => { + it('converts a WPComment object to a Comment object', () => { + const comment: WPComment = { + approved: true, + author: { + node: { + avatar: null, + name: 'Maribel.Roberts', + url: null, + }, + }, + content: 'Aliquam qui et facere consequatur quia.', + databaseId: 4, + date: '2023-10-15', + parentDatabaseId: 1, + status: 'HOLD', + }; + + const transformedComment = convertWPCommentToComment(comment); + + expect(transformedComment.content).toBe(comment.content); + expect(transformedComment.id).toBe(comment.databaseId); + expect(transformedComment.isApproved).toBe(comment.approved); + expect(transformedComment.meta.author.avatar).toBeUndefined(); + expect(transformedComment.meta.author.name).toBe(comment.author.node.name); + expect(transformedComment.meta.author.website).toBeUndefined(); + expect(transformedComment.parentId).toBe(comment.parentDatabaseId); + expect(transformedComment.replies).toStrictEqual([]); + }); + + it('can convert the avatar', () => { + const comment: WPComment = { + approved: true, + author: { + node: { + avatar: { + height: 80, + url: 'https://cloudflare-ipfs.com/ipfs/Qmd3W5DuhgHirLHGVixi6V76LhCkZUz6pnFt5AJBiyvHye/avatar/426.jpg', + width: 80, + }, + name: 'Maribel.Roberts', + url: null, + }, + }, + content: 'Aliquam qui et facere consequatur quia.', + databaseId: 4, + date: '2023-10-15', + parentDatabaseId: 1, + status: 'HOLD', + }; + + const transformedComment = convertWPCommentToComment(comment); + + expect(transformedComment.meta.author.avatar?.alt).toBe( + `${comment.author.node.name} avatar` + ); + expect(transformedComment.meta.author.avatar?.height).toBe( + comment.author.node.avatar?.height + ); + expect(transformedComment.meta.author.avatar?.src).toBe( + comment.author.node.avatar?.url + ); + expect(transformedComment.meta.author.avatar?.width).toBe( + comment.author.node.avatar?.width + ); + }); + + it('can remove the parentId when not meaningful', () => { + const comment: WPComment = { + approved: true, + author: { + node: { + avatar: null, + name: 'Maribel.Roberts', + url: null, + }, + }, + content: 'Aliquam qui et facere consequatur quia.', + databaseId: 4, + date: '2023-10-15', + parentDatabaseId: 0, + status: 'HOLD', + }; + + const transformedComment = convertWPCommentToComment(comment); + + expect(transformedComment.parentId).toBeUndefined(); + }); +}); diff --git a/src/services/graphql/helpers/convert-wp-comment-to-comment.ts b/src/services/graphql/helpers/convert-wp-comment-to-comment.ts new file mode 100644 index 0000000..7a7e2ca --- /dev/null +++ b/src/services/graphql/helpers/convert-wp-comment-to-comment.ts @@ -0,0 +1,35 @@ +import type { SingleComment, WPComment } from '../../../types'; + +/** + * Convert a comment from WordPress type to SingleComment. + * + * @param {WPComment} comment - A raw comment from WordPress. + * @returns {SingleComment} A comment. + */ +export const convertWPCommentToComment = ( + comment: WPComment +): SingleComment => { + return { + content: comment.content, + isApproved: comment.approved, + id: comment.databaseId, + meta: { + author: { + name: comment.author.node.name, + avatar: comment.author.node.avatar + ? { + alt: `${comment.author.node.name} avatar`, + height: comment.author.node.avatar.height, + src: comment.author.node.avatar.url, + width: comment.author.node.avatar.width, + } + : undefined, + website: comment.author.node.url ?? undefined, + }, + date: comment.date, + }, + parentId: + comment.parentDatabaseId === 0 ? undefined : comment.parentDatabaseId, + replies: [], + }; +}; diff --git a/src/services/graphql/helpers/convert-wp-image-to-img.test.ts b/src/services/graphql/helpers/convert-wp-image-to-img.test.ts new file mode 100644 index 0000000..ca58a4f --- /dev/null +++ b/src/services/graphql/helpers/convert-wp-image-to-img.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from '@jest/globals'; +import type { WPImage } from '../../../types'; +import { convertWPImgToImg } from './convert-wp-image-to-img'; + +describe('convert-wp-image-to-img', () => { + it('converts a WPImage object to an Img object', () => { + const img: WPImage = { + altText: 'molestiae praesentium animi', + mediaDetails: { + height: 480, + width: 640, + }, + sourceUrl: 'https://picsum.photos/640/480', + title: null, + }; + + const transformedImg = convertWPImgToImg(img); + + expect(transformedImg.alt).toBe(img.altText); + expect(transformedImg.height).toBe(img.mediaDetails.height); + expect(transformedImg.src).toBe(img.sourceUrl); + expect(transformedImg.title).toBeUndefined(); + expect(transformedImg.width).toBe(img.mediaDetails.width); + }); + + it('can return an empty string if altText is missing', () => { + const img: WPImage = { + altText: null, + mediaDetails: { + height: 480, + width: 640, + }, + sourceUrl: 'https://picsum.photos/640/480', + title: null, + }; + + const transformedImg = convertWPImgToImg(img); + + expect(transformedImg.alt).toBe(''); + }); +}); diff --git a/src/services/graphql/helpers/convert-wp-image-to-img.ts b/src/services/graphql/helpers/convert-wp-image-to-img.ts new file mode 100644 index 0000000..392aaf9 --- /dev/null +++ b/src/services/graphql/helpers/convert-wp-image-to-img.ts @@ -0,0 +1,16 @@ +import type { Img, WPImage } from '../../../types'; + +export const convertWPImgToImg = ({ + altText, + mediaDetails, + sourceUrl, + title, +}: WPImage): Img => { + return { + alt: altText ?? '', + height: mediaDetails.height, + src: sourceUrl, + title: title ?? undefined, + width: mediaDetails.width, + }; +}; diff --git a/src/services/graphql/helpers/index.ts b/src/services/graphql/helpers/index.ts new file mode 100644 index 0000000..16e93d2 --- /dev/null +++ b/src/services/graphql/helpers/index.ts @@ -0,0 +1,7 @@ +export * from './build-comments-tree'; +export * from './convert-post-preview-to-article-preview'; +export * from './convert-post-to-article'; +export * from './convert-recent-post-to-recent-article'; +export * from './convert-taxonomy-to-page-link'; +export * from './convert-wp-comment-to-comment'; +export * from './convert-wp-image-to-img'; -- cgit v1.2.3