From 64fd0cfdc6fd75207f5ea41231f08c0dcfb56bfb Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 25 Nov 2022 11:44:55 +0800 Subject: [PATCH] Add sort in updatesince api and default sorting by updatedAt in descending order --- packages/api/src/generated/graphql.ts | 1 + packages/api/src/generated/schema.graphql | 2 +- packages/api/src/resolvers/article/index.ts | 168 ++++++++++++-------- packages/api/src/schema.ts | 7 +- packages/api/test/resolvers/article.test.ts | 6 +- 5 files changed, 114 insertions(+), 70 deletions(-) diff --git a/packages/api/src/generated/graphql.ts b/packages/api/src/generated/graphql.ts index dc609a3b7..49474771b 100644 --- a/packages/api/src/generated/graphql.ts +++ b/packages/api/src/generated/graphql.ts @@ -1538,6 +1538,7 @@ export type QueryUpdatesSinceArgs = { after?: InputMaybe; first?: InputMaybe; since: Scalars['Date']; + sort?: InputMaybe; }; diff --git a/packages/api/src/generated/schema.graphql b/packages/api/src/generated/schema.graphql index e2548394a..0d396749c 100644 --- a/packages/api/src/generated/schema.graphql +++ b/packages/api/src/generated/schema.graphql @@ -1061,7 +1061,7 @@ type Query { sharedArticle(selectedHighlightId: String, slug: String!, username: String!): SharedArticleResult! subscriptions(sort: SortParams): SubscriptionsResult! typeaheadSearch(first: Int, query: String!): TypeaheadSearchResult! - updatesSince(after: String, first: Int, since: Date!): UpdatesSinceResult! + updatesSince(after: String, first: Int, since: Date!, sort: SortParams): UpdatesSinceResult! user(userId: ID, username: String): UserResult! users: UsersResult! validateUsername(username: String!): Boolean! diff --git a/packages/api/src/resolvers/article/index.ts b/packages/api/src/resolvers/article/index.ts index 9d02d9d26..3c5b22b6f 100644 --- a/packages/api/src/resolvers/article/index.ts +++ b/packages/api/src/resolvers/article/index.ts @@ -14,6 +14,7 @@ import { CreateArticleErrorCode, CreateArticleSuccess, FeedArticle, + InputMaybe, MutationCreateArticleArgs, MutationSaveArticleReadingProgressArgs, MutationSetBookmarkArticleArgs, @@ -37,6 +38,7 @@ import { SetShareArticleError, SetShareArticleErrorCode, SetShareArticleSuccess, + SortParams, TypeaheadSearchError, TypeaheadSearchErrorCode, TypeaheadSearchSuccess, @@ -966,74 +968,84 @@ export const updatesSinceResolver = authorized< UpdatesSinceSuccess, UpdatesSinceError, QueryUpdatesSinceArgs ->(async (_obj, { since, first, after }, { claims: { uid } }) => { - if (!uid) { - return { errorCodes: [UpdatesSinceErrorCode.Unauthorized] } - } - - analytics.track({ - userId: uid, - event: 'updatesSince', - properties: { - env: env.server.apiEnv, - since, - first, - after, - }, - }) - - const startCursor = after || '' - const size = first || 10 - const startDate = new Date(since) - const [pages, totalCount] = (await searchPages( - { - from: Number(startCursor), - size: size + 1, // fetch one more item to get next cursor - includeDeleted: true, - dateFilters: [{ field: 'updatedAt', startDate }], - sort: { by: SortBy.UPDATED, order: SortOrder.ASCENDING }, - }, - uid - )) || [[], 0] - - const start = - startCursor && !isNaN(Number(startCursor)) ? Number(startCursor) : 0 - const hasNextPage = pages.length > size - const endCursor = String(start + pages.length - (hasNextPage ? 1 : 0)) - - //TODO: refactor so that the lastCursor included - if (hasNextPage) { - // remove an extra if exists - pages.pop() - } - - const edges = pages.map((p) => { - const updateReason = getUpdateReason(p, startDate) - return { - node: { - ...p, - image: p.image && createImageProxyUrl(p.image, 260, 260), - isArchived: !!p.archivedAt, - contentReader: - p.pageType === PageType.File ? ContentReader.Pdf : ContentReader.Web, - } as SearchItem, - cursor: endCursor, - itemID: p.id, - updateReason, +>( + async ( + _obj, + { since, first, after, sort: sortParams }, + { claims: { uid } } + ) => { + if (!uid) { + return { errorCodes: [UpdatesSinceErrorCode.Unauthorized] } } - }) - return { - edges, - pageInfo: { - hasPreviousPage: false, - startCursor, - hasNextPage, - endCursor, - totalCount, - }, + analytics.track({ + userId: uid, + event: 'updatesSince', + properties: { + env: env.server.apiEnv, + since, + first, + after, + }, + }) + + const sort = sortParamsToElasticSort(sortParams) + + const startCursor = after || '' + const size = first || 10 + const startDate = new Date(since) + const [pages, totalCount] = (await searchPages( + { + from: Number(startCursor), + size: size + 1, // fetch one more item to get next cursor + includeDeleted: true, + dateFilters: [{ field: 'updatedAt', startDate }], + sort, + }, + uid + )) || [[], 0] + + const start = + startCursor && !isNaN(Number(startCursor)) ? Number(startCursor) : 0 + const hasNextPage = pages.length > size + const endCursor = String(start + pages.length - (hasNextPage ? 1 : 0)) + + //TODO: refactor so that the lastCursor included + if (hasNextPage) { + // remove an extra if exists + pages.pop() + } + + const edges = pages.map((p) => { + const updateReason = getUpdateReason(p, startDate) + return { + node: { + ...p, + image: p.image && createImageProxyUrl(p.image, 260, 260), + isArchived: !!p.archivedAt, + contentReader: + p.pageType === PageType.File + ? ContentReader.Pdf + : ContentReader.Web, + } as SearchItem, + cursor: endCursor, + itemID: p.id, + updateReason, + } + }) + + return { + edges, + pageInfo: { + hasPreviousPage: false, + startCursor, + hasNextPage, + endCursor, + totalCount, + }, + } } -}) +) const getUpdateReason = (page: Page, since: Date) => { if (page.state === ArticleSavingRequestStatus.Deleted) { @@ -1044,3 +1056,29 @@ const getUpdateReason = (page: Page, since: Date) => { } return UpdateReason.Updated } + +const sortParamsToElasticSort = ( + sortParams: InputMaybe | undefined +) => { + const sort = { by: SortBy.UPDATED, order: SortOrder.DESCENDING } + + if (sortParams) { + sortParams.order === 'ASCENDING' && (sort.order = SortOrder.ASCENDING) + switch (sortParams.by) { + case 'UPDATED_TIME': + sort.by = SortBy.UPDATED + break + case 'SCORE': + sort.by = SortBy.SCORE + break + case 'PUBLISHED_AT': + sort.by = SortBy.PUBLISHED + break + case 'SAVED_AT': + sort.by = SortBy.SAVED + break + } + } + + return sort +} diff --git a/packages/api/src/schema.ts b/packages/api/src/schema.ts index c8ef50ce2..cee9f881e 100755 --- a/packages/api/src/schema.ts +++ b/packages/api/src/schema.ts @@ -2167,7 +2167,12 @@ const schema = gql` webhook(id: ID!): WebhookResult! apiKeys: ApiKeysResult! typeaheadSearch(query: String!, first: Int): TypeaheadSearchResult! - updatesSince(after: String, first: Int, since: Date!): UpdatesSinceResult! + updatesSince( + after: String + first: Int + since: Date! + sort: SortParams + ): UpdatesSinceResult! integrations: IntegrationsResult! recentSearches: RecentSearchesResult! rules(enabled: Boolean): RulesResult! diff --git a/packages/api/test/resolvers/article.test.ts b/packages/api/test/resolvers/article.test.ts index b62dc0a02..a539101a4 100644 --- a/packages/api/test/resolvers/article.test.ts +++ b/packages/api/test/resolvers/article.test.ts @@ -1071,15 +1071,15 @@ describe('Article API', () => { ).length ).to.eql(3) expect(res.body.data.updatesSince.edges[0].itemID).to.eq( - deletedPages[0].id + deletedPages[2].id ) expect(res.body.data.updatesSince.edges[1].itemID).to.eq( deletedPages[1].id ) expect(res.body.data.updatesSince.edges[2].itemID).to.eq( - deletedPages[2].id + deletedPages[0].id ) - expect(res.body.data.updatesSince.edges[2].updateReason).to.eq( + expect(res.body.data.updatesSince.edges[0].updateReason).to.eq( UpdateReason.Deleted ) })