From 70ef4a48458c68aacb033e95b8dd3f18bb3b1082 Mon Sep 17 00:00:00 2001 From: mthmcalixto <121470728+mthmcalixto@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:26:23 -0300 Subject: [PATCH 1/3] feat(/api/v1/contents): add return results for comments add with_children and with_root query params to filter content between posts and comments. --- models/content.js | 2 +- pages/api/v1/contents/index.public.js | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/models/content.js b/models/content.js index c166ff939..db3cb779e 100644 --- a/models/content.js +++ b/models/content.js @@ -236,7 +236,7 @@ async function findWithStrategy(options = {}) { const results = {}; const options = {}; - if (!values?.where?.owner_username) { + if (!values?.where?.owner_username && values?.where?.parent_id === null) { options.strategy = 'relevant_global'; } values.order = 'published_at DESC'; diff --git a/pages/api/v1/contents/index.public.js b/pages/api/v1/contents/index.public.js index cf5eae3f0..a384b1411 100644 --- a/pages/api/v1/contents/index.public.js +++ b/pages/api/v1/contents/index.public.js @@ -10,6 +10,7 @@ import controller from 'models/controller.js'; import event from 'models/event.js'; import firewall from 'models/firewall.js'; import notification from 'models/notification.js'; +import removeMarkdown from 'models/remove-markdown'; import user from 'models/user.js'; import validator from 'models/validator.js'; @@ -35,6 +36,8 @@ function getValidationHandler(request, response, next) { page: 'optional', per_page: 'optional', strategy: 'optional', + with_root: 'optional', + with_children: 'optional', }); request.query = cleanValues; @@ -48,11 +51,12 @@ async function getHandler(request, response) { const results = await content.findWithStrategy({ strategy: request.query.strategy, where: { - parent_id: null, + parent_id: request.query.with_children ? undefined : null, status: 'published', + $not_null: request.query.with_root === false ? ['parent_id'] : undefined, }, attributes: { - exclude: ['body'], + exclude: request.query.with_children ? undefined : ['body'], }, page: request.query.page, per_page: request.query.per_page, @@ -62,6 +66,16 @@ async function getHandler(request, response) { const secureOutputValues = authorization.filterOutput(userTryingToList, 'read:content:list', contentList); + if (request.query.with_children) { + for (const content of secureOutputValues) { + if (content.parent_id) { + content.body = removeMarkdown(content.body, { maxLength: 255 }); + } else { + delete content.body; + } + } + } + controller.injectPaginationHeaders(results.pagination, '/api/v1/contents', response); return response.status(200).json(secureOutputValues); From a2e30763e54c8fbe8c76217d968514198ae47af7 Mon Sep 17 00:00:00 2001 From: Rafael Tavares <26308880+Rafatcb@users.noreply.github.com> Date: Sun, 21 Jan 2024 22:24:02 -0300 Subject: [PATCH 2/3] test(api contents): add expected values on .each objects --- tests/integration/api/v1/contents/get.test.js | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/tests/integration/api/v1/contents/get.test.js b/tests/integration/api/v1/contents/get.test.js index 9bf99b2e5..d3ecc9230 100644 --- a/tests/integration/api/v1/contents/get.test.js +++ b/tests/integration/api/v1/contents/get.test.js @@ -1246,5 +1246,154 @@ describe('GET /api/v1/contents', () => { expect(uuidVersion(responseBody.error_id)).toEqual(4); expect(uuidVersion(responseBody.request_id)).toEqual(4); }); + + describe('with_children and with_root using different strategies', () => { + let firstRootContentExpect; + let secondRootContentExpect; + let firstCommentExpect; + let secondCommentExpect; + + beforeEach(async () => { + const createUser = async () => orchestrator.createUser(); + const createContent = async (user, options) => orchestrator.createContent({ owner_id: user.id, ...options }); + const createComment = async (user, parent, body) => + createContent(user, { body, status: 'published', parent_id: parent.id }); + + const [firstUser, secondUser, thirdUser] = await Promise.all(Array.from({ length: 3 }, () => createUser())); + + await createContent(firstUser, { title: 'Contéudo "Draft"', status: 'draft' }); + + const firstRootContent = await createContent(secondUser, { + title: 'Primeiro conteúdo criado', + status: 'published', + }); + firstRootContentExpect = { + id: firstRootContent.id, + owner_id: secondUser.id, + parent_id: null, + slug: 'primeiro-conteudo-criado', + title: 'Primeiro conteúdo criado', + status: 'published', + source_url: null, + created_at: firstRootContent.created_at.toISOString(), + updated_at: firstRootContent.updated_at.toISOString(), + published_at: firstRootContent.published_at.toISOString(), + deleted_at: null, + owner_username: secondUser.username, + tabcoins: 2, + children_deep_count: 1, + }; + await orchestrator.createRate(firstRootContent, 1); + + const secondRootContent = await createContent(thirdUser, { + title: 'Segundo conteúdo criado', + status: 'published', + }); + secondRootContentExpect = { + id: secondRootContent.id, + owner_id: thirdUser.id, + parent_id: null, + slug: 'segundo-conteudo-criado', + title: 'Segundo conteúdo criado', + status: 'published', + source_url: null, + created_at: secondRootContent.created_at.toISOString(), + updated_at: secondRootContent.updated_at.toISOString(), + published_at: secondRootContent.published_at.toISOString(), + deleted_at: null, + owner_username: thirdUser.username, + tabcoins: 1, + children_deep_count: 1, + }; + + const firstComment = await createComment(firstUser, secondRootContent, 'Comentário #1'); + const secondComment = await createComment(thirdUser, firstRootContent, 'Comentário #2'); + await orchestrator.createRate(firstComment, 1); + + function createCommentExpect(comment, owner, parent, tabcoins = 0) { + return { + id: comment.id, + owner_id: owner.id, + parent_id: parent.id, + slug: comment.slug, + title: null, + body: comment.body, + status: 'published', + source_url: null, + created_at: comment.created_at.toISOString(), + updated_at: comment.updated_at.toISOString(), + published_at: comment.published_at.toISOString(), + deleted_at: null, + owner_username: owner.username, + tabcoins, + children_deep_count: 0, + }; + } + + firstCommentExpect = createCommentExpect(firstComment, firstUser, secondRootContent, 1); + secondCommentExpect = createCommentExpect(secondComment, thirdUser, firstRootContent); + }); + + test.each([ + { + content: 'relevant root', + params: [], + getExpected: () => [firstRootContentExpect, secondRootContentExpect], + }, + { + content: 'relevant root', + params: ['with_children=false'], + getExpected: () => [firstRootContentExpect, secondRootContentExpect], + }, + { + content: 'relevant root and children', + params: ['with_children=true'], + getExpected: () => [firstRootContentExpect, firstCommentExpect, secondRootContentExpect, secondCommentExpect], + }, + { + content: 'new root', + params: ['with_children=false', 'with_root=true', 'strategy=new'], + getExpected: () => [secondRootContentExpect, firstRootContentExpect], + }, + { + content: 'new root', + params: ['with_children=false', 'with_root=true', 'strategy=new'], + getExpected: () => [secondRootContentExpect, firstRootContentExpect], + }, + { + content: 'new root', + params: ['with_children=false', 'strategy=new'], + getExpected: () => [secondRootContentExpect, firstRootContentExpect], + }, + { + content: 'new children', + params: ['with_children=true', 'with_root=false', 'strategy=new'], + getExpected: () => [secondCommentExpect, firstCommentExpect], + }, + { + content: 'new root and children', + params: ['with_children=true', 'strategy=new'], + getExpected: () => [secondCommentExpect, firstCommentExpect, secondRootContentExpect, firstRootContentExpect], + }, + { + content: 'new root and children', + params: ['with_children=true', 'with_root=true', 'strategy=new'], + getExpected: () => [secondCommentExpect, firstCommentExpect, secondRootContentExpect, firstRootContentExpect], + }, + { + content: 'new root', + params: ['with_root=true', 'strategy=new'], + getExpected: () => [secondRootContentExpect, firstRootContentExpect], + }, + ])('get $content with params: $params', async ({ params, getExpected }) => { + const url = `${orchestrator.webserverUrl}/api/v1/contents?${params.join('&')}`; + + const response = await fetch(url); + const responseBody = await response.json(); + + expect(response.status).toEqual(200); + expect(responseBody).toStrictEqual(getExpected()); + }); + }); }); }); From 6a822f7dba04b7c4e46b3c094f9408ca2a26f5d0 Mon Sep 17 00:00:00 2001 From: aprendendofelipe <77860630+aprendendofelipe@users.noreply.github.com> Date: Tue, 23 Jan 2024 21:51:47 -0300 Subject: [PATCH 3/3] test(api contents): add tests with dropAllTables only beforeAll --- tests/integration/api/v1/contents/get.test.js | 80 ++++++++++++------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/tests/integration/api/v1/contents/get.test.js b/tests/integration/api/v1/contents/get.test.js index d3ecc9230..592b5e46b 100644 --- a/tests/integration/api/v1/contents/get.test.js +++ b/tests/integration/api/v1/contents/get.test.js @@ -11,12 +11,12 @@ beforeAll(async () => { }); describe('GET /api/v1/contents', () => { - beforeEach(async () => { - await orchestrator.dropAllTables(); - await orchestrator.runPendingMigrations(); - }); + describe('Anonymous user (dropAllTables beforeEach)', () => { + beforeEach(async () => { + await orchestrator.dropAllTables(); + await orchestrator.runPendingMigrations(); + }); - describe('Anonymous user', () => { test('With CORS and Security Headers enabled', async () => { const response = await fetch(`${orchestrator.webserverUrl}/api/v1/contents`); @@ -1246,14 +1246,21 @@ describe('GET /api/v1/contents', () => { expect(uuidVersion(responseBody.error_id)).toEqual(4); expect(uuidVersion(responseBody.request_id)).toEqual(4); }); + }); + describe('Anonymous user (dropAllTables beforeAll)', () => { describe('with_children and with_root using different strategies', () => { - let firstRootContentExpect; - let secondRootContentExpect; - let firstCommentExpect; - let secondCommentExpect; + const rootSortedByOld = []; + const childSortedByOld = []; + const rootSortedByNew = []; + const childSortedByNew = []; + const rootSortedByTabCoins = []; + const childSortedByTabCoins = []; + + beforeAll(async () => { + await orchestrator.dropAllTables(); + await orchestrator.runPendingMigrations(); - beforeEach(async () => { const createUser = async () => orchestrator.createUser(); const createContent = async (user, options) => orchestrator.createContent({ owner_id: user.id, ...options }); const createComment = async (user, parent, body) => @@ -1261,13 +1268,14 @@ describe('GET /api/v1/contents', () => { const [firstUser, secondUser, thirdUser] = await Promise.all(Array.from({ length: 3 }, () => createUser())); - await createContent(firstUser, { title: 'Contéudo "Draft"', status: 'draft' }); + await createContent(firstUser, { title: 'Conteúdo "Draft"', status: 'draft' }); const firstRootContent = await createContent(secondUser, { title: 'Primeiro conteúdo criado', status: 'published', }); - firstRootContentExpect = { + + rootSortedByOld.push({ id: firstRootContent.id, owner_id: secondUser.id, parent_id: null, @@ -1282,14 +1290,16 @@ describe('GET /api/v1/contents', () => { owner_username: secondUser.username, tabcoins: 2, children_deep_count: 1, - }; + }); + await orchestrator.createRate(firstRootContent, 1); const secondRootContent = await createContent(thirdUser, { title: 'Segundo conteúdo criado', status: 'published', }); - secondRootContentExpect = { + + rootSortedByOld.push({ id: secondRootContent.id, owner_id: thirdUser.id, parent_id: null, @@ -1304,7 +1314,7 @@ describe('GET /api/v1/contents', () => { owner_username: thirdUser.username, tabcoins: 1, children_deep_count: 1, - }; + }); const firstComment = await createComment(firstUser, secondRootContent, 'Comentário #1'); const secondComment = await createComment(thirdUser, firstRootContent, 'Comentário #2'); @@ -1330,60 +1340,68 @@ describe('GET /api/v1/contents', () => { }; } - firstCommentExpect = createCommentExpect(firstComment, firstUser, secondRootContent, 1); - secondCommentExpect = createCommentExpect(secondComment, thirdUser, firstRootContent); + childSortedByOld.push( + createCommentExpect(firstComment, firstUser, secondRootContent, 1), + createCommentExpect(secondComment, thirdUser, firstRootContent) + ); + + rootSortedByNew.push(...rootSortedByOld.slice().reverse()); + childSortedByNew.push(...childSortedByOld.slice().reverse()); + + rootSortedByTabCoins.push(...rootSortedByNew.slice().sort((a, b) => b.tabcoins - a.tabcoins)); + childSortedByTabCoins.push(...childSortedByNew.slice().sort((a, b) => b.tabcoins - a.tabcoins)); }); test.each([ { content: 'relevant root', params: [], - getExpected: () => [firstRootContentExpect, secondRootContentExpect], + getExpected: () => rootSortedByTabCoins, }, { content: 'relevant root', params: ['with_children=false'], - getExpected: () => [firstRootContentExpect, secondRootContentExpect], + getExpected: () => rootSortedByTabCoins, }, { content: 'relevant root and children', params: ['with_children=true'], - getExpected: () => [firstRootContentExpect, firstCommentExpect, secondRootContentExpect, secondCommentExpect], + getExpected: () => [ + rootSortedByTabCoins[0], + childSortedByTabCoins[0], + rootSortedByTabCoins[1], + childSortedByTabCoins[1], + ], }, { content: 'new root', params: ['with_children=false', 'with_root=true', 'strategy=new'], - getExpected: () => [secondRootContentExpect, firstRootContentExpect], + getExpected: () => rootSortedByNew, }, { content: 'new root', params: ['with_children=false', 'with_root=true', 'strategy=new'], - getExpected: () => [secondRootContentExpect, firstRootContentExpect], - }, - { - content: 'new root', - params: ['with_children=false', 'strategy=new'], - getExpected: () => [secondRootContentExpect, firstRootContentExpect], + getExpected: () => rootSortedByNew, }, { content: 'new children', params: ['with_children=true', 'with_root=false', 'strategy=new'], - getExpected: () => [secondCommentExpect, firstCommentExpect], + getExpected: () => childSortedByNew, }, { content: 'new root and children', params: ['with_children=true', 'strategy=new'], - getExpected: () => [secondCommentExpect, firstCommentExpect, secondRootContentExpect, firstRootContentExpect], + getExpected: () => [...childSortedByNew, ...rootSortedByNew], }, { content: 'new root and children', params: ['with_children=true', 'with_root=true', 'strategy=new'], - getExpected: () => [secondCommentExpect, firstCommentExpect, secondRootContentExpect, firstRootContentExpect], + getExpected: () => [...childSortedByNew, ...rootSortedByNew], }, { content: 'new root', params: ['with_root=true', 'strategy=new'], - getExpected: () => [secondRootContentExpect, firstRootContentExpect], + getExpected: () => rootSortedByNew, }, ])('get $content with params: $params', async ({ params, getExpected }) => { const url = `${orchestrator.webserverUrl}/api/v1/contents?${params.join('&')}`;