From 83f988bb356339f076f0320dce2abea39633ad18 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 29 Jul 2022 15:53:13 +0800 Subject: [PATCH] Soft delete articles --- packages/api/src/elastic/labels.ts | 2 +- packages/api/src/elastic/pages.ts | 32 ++++++++++----------- packages/api/src/elastic/types.ts | 26 +++++++++++++++++ packages/api/src/resolvers/article/index.ts | 8 ++++-- packages/api/test/elastic/index.test.ts | 24 ++++++---------- packages/api/test/resolvers/article.test.ts | 19 +++++------- packages/api/test/resolvers/labels.test.ts | 4 +-- 7 files changed, 67 insertions(+), 48 deletions(-) diff --git a/packages/api/src/elastic/labels.ts b/packages/api/src/elastic/labels.ts index ce02655e2..3c1423f7c 100644 --- a/packages/api/src/elastic/labels.ts +++ b/packages/api/src/elastic/labels.ts @@ -18,7 +18,7 @@ export const addLabelInPage = async ( ctx._source.labels = [params.label]; ctx._source.updatedAt = params.updatedAt } else if (!ctx._source.labels.any(label -> label.name == params.label.name)) { - ctx._source.labels.add(params.label) ; + ctx._source.labels.add(params.label); ctx._source.updatedAt = params.updatedAt } else { ctx.op = 'none' }`, lang: 'painless', diff --git a/packages/api/src/elastic/pages.ts b/packages/api/src/elastic/pages.ts index 3f0b1538f..4dbc40fbe 100644 --- a/packages/api/src/elastic/pages.ts +++ b/packages/api/src/elastic/pages.ts @@ -2,6 +2,7 @@ import { ArticleSavingRequestStatus, Page, PageContext, + PageSearchArgs, PageType, ParamSet, SearchBody, @@ -17,7 +18,6 @@ import { ReadFilter, SortBy, SortOrder, - SortParams, } from '../utils/search' import { client, INDEX_ALIAS } from './index' import { EntityType } from '../datalayer/pubsub' @@ -337,21 +337,7 @@ export const getPageById = async (id: string): Promise => { } export const searchPages = async ( - args: { - from?: number - size?: number - sort?: SortParams - query?: string - inFilter?: InFilter - readFilter?: ReadFilter - typeFilter?: PageType - labelFilters: LabelFilter[] - hasFilters: HasFilter[] - dateFilters: DateFilter[] - termFilters?: FieldFilter[] - matchFilters?: FieldFilter[] - includePending?: boolean | null - }, + args: PageSearchArgs, userId: string ): Promise<[Page[], number] | undefined> => { try { @@ -448,6 +434,14 @@ export const searchPages = async ( }) } + if (!args.includeDeleted) { + body.query.bool.must_not.push({ + term: { + state: ArticleSavingRequestStatus.Deleted, + }, + }) + } + console.log('searching pages in elastic', JSON.stringify(body)) const response = await client.search, SearchBody>({ @@ -533,6 +527,7 @@ export const deletePagesByParam = async ( const { body } = await client.deleteByQuery({ index: INDEX_ALIAS, body: params, + conflicts: 'proceed', }) if (body.deleted > 0) { @@ -566,6 +561,11 @@ export const searchAsYouType = async ( userId, }, }, + { + term: { + state: ArticleSavingRequestStatus.Succeeded, + }, + }, { multi_match: { query, diff --git a/packages/api/src/elastic/types.ts b/packages/api/src/elastic/types.ts index 0e7fb7a2f..f4bf7b7c4 100644 --- a/packages/api/src/elastic/types.ts +++ b/packages/api/src/elastic/types.ts @@ -1,6 +1,15 @@ // Define the type of the body for the Search request import { PickTuple } from '../util' import { PubsubClient } from '../datalayer/pubsub' +import { + DateFilter, + FieldFilter, + HasFilter, + InFilter, + LabelFilter, + ReadFilter, + SortParams, +} from '../utils/search' export interface SearchBody { query: { @@ -246,3 +255,20 @@ export interface PageContext { refresh?: boolean uid: string } + +export interface PageSearchArgs { + from?: number + size?: number + sort?: SortParams + query?: string + inFilter?: InFilter + readFilter?: ReadFilter + typeFilter?: PageType + labelFilters: LabelFilter[] + hasFilters: HasFilter[] + dateFilters: DateFilter[] + termFilters?: FieldFilter[] + matchFilters?: FieldFilter[] + includePending?: boolean | null + includeDeleted?: boolean +} diff --git a/packages/api/src/resolvers/article/index.ts b/packages/api/src/resolvers/article/index.ts index 28d0f6dd6..3ee5c1c29 100644 --- a/packages/api/src/resolvers/article/index.ts +++ b/packages/api/src/resolvers/article/index.ts @@ -82,7 +82,6 @@ import { } from '../../elastic/types' import { createPage, - deletePage, getPageById, getPageByParam, searchAsYouType, @@ -636,7 +635,12 @@ export const setBookmarkArticleResolver = authorized< return { errorCodes: [SetBookmarkArticleErrorCode.NotFound] } } - await deletePage(pageRemoved.id, { pubsub, uid }) + // delete the page + await updatePage( + pageRemoved.id, + { state: ArticleSavingRequestStatus.Deleted }, + { pubsub, uid } + ) const highlightsUnshared = await authTrx(async (tx) => { return models.highlight.unshareAllHighlights(articleID, uid, tx) diff --git a/packages/api/test/elastic/index.test.ts b/packages/api/test/elastic/index.test.ts index 7d7dc556d..ff415a41d 100644 --- a/packages/api/test/elastic/index.test.ts +++ b/packages/api/test/elastic/index.test.ts @@ -70,11 +70,7 @@ describe('elastic api', () => { ], state: ArticleSavingRequestStatus.Succeeded, } - const pageId = await createPage(page, ctx) - if (!pageId) { - expect.fail('Failed to create page') - } - page.id = pageId + page.id = (await createPage(page, ctx))! }) after(async () => { @@ -83,12 +79,10 @@ describe('elastic api', () => { }) describe('createPage', () => { - let newPageId: string | undefined + let newPageId: string after(async () => { - if (newPageId) { - await deletePage(newPageId, ctx) - } + await deletePage(newPageId, ctx) }) it('creates a page', async () => { @@ -108,9 +102,7 @@ describe('elastic api', () => { url: 'https://blog.omnivore.app/testUrl', state: ArticleSavingRequestStatus.Succeeded, } - - newPageId = await createPage(newPageData, ctx) - + newPageId = (await createPage(newPageData, ctx))! expect(newPageId).to.be.a('string') }) }) @@ -344,9 +336,11 @@ describe('elastic api', () => { }) describe('searchAsYouType', () => { + let pageId: string + before(async () => { // create a testing page - await createPage( + pageId = (await createPage( { content: '', createdAt: new Date(), @@ -363,12 +357,12 @@ describe('elastic api', () => { userId, }, ctx - ) + ))! }) after(async () => { // delete the testing page - await deletePagesByParam({ userId }, ctx) + await deletePage(pageId, ctx) }) it('searches pages', async () => { diff --git a/packages/api/test/resolvers/article.test.ts b/packages/api/test/resolvers/article.test.ts index a7b135406..bb8a8c58e 100644 --- a/packages/api/test/resolvers/article.test.ts +++ b/packages/api/test/resolvers/article.test.ts @@ -649,7 +649,7 @@ describe('Article API', () => { let query = '' let articleId = '' let bookmark = true - let pageId = '' + let pageId: string before(async () => { const page: Page = { @@ -667,16 +667,11 @@ describe('Article API', () => { readingProgressAnchorIndex: 0, state: ArticleSavingRequestStatus.Succeeded, } - const newPageId = await createPage(page, ctx) - if (newPageId) { - pageId = newPageId - } + pageId = (await createPage(page, ctx))! }) after(async () => { - if (pageId) { - await deletePage(pageId, ctx) - } + await deletePage(pageId, ctx) }) beforeEach(() => { @@ -684,7 +679,7 @@ describe('Article API', () => { }) context('when we set a bookmark on an article', () => { - before(async () => { + before(() => { articleId = pageId bookmark = true }) @@ -698,15 +693,15 @@ describe('Article API', () => { }) context('when we unset a bookmark on an article', () => { - before(async () => { + before(() => { articleId = pageId bookmark = false }) it('should delete an article', async () => { await graphqlRequest(query, authToken).expect(200) - const pageId = await getPageById(articleId) - expect(pageId).to.undefined + const page = await getPageById(articleId) + expect(page?.state).to.eql(ArticleSavingRequestStatus.Deleted) }) }) }) diff --git a/packages/api/test/resolvers/labels.test.ts b/packages/api/test/resolvers/labels.test.ts index 9b10b9a6f..afd3a6c85 100644 --- a/packages/api/test/resolvers/labels.test.ts +++ b/packages/api/test/resolvers/labels.test.ts @@ -11,7 +11,7 @@ import 'mocha' import { User } from '../../src/entity/user' import { Highlight, Page, PageContext } from '../../src/elastic/types' import { getRepository } from '../../src/entity/utils' -import { deletePagesByParam, getPageById } from '../../src/elastic/pages' +import { deletePage, getPageById } from '../../src/elastic/pages' import { addLabelInPage } from '../../src/elastic/labels' import { createPubSubClient } from '../../src/datalayer/pubsub' import { @@ -66,7 +66,7 @@ describe('Labels API', () => { after(async () => { // clean up - await deletePagesByParam({ userId: user.id }, ctx) + await deletePage(page.id, ctx) await deleteTestUser(username) })