From 425864ce010faddfd6bfb39b8b20a0706fd3c058 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Tue, 22 Aug 2023 22:00:48 +0800 Subject: [PATCH] remove knex --- packages/api/src/apollo.ts | 21 +- .../api/src/entity/{utils.ts => index.ts} | 7 + packages/api/src/entity/label.ts | 7 - packages/api/src/entity/library_item.ts | 64 +- packages/api/src/entity/profile.ts | 8 +- packages/api/src/entity/user.ts | 19 +- packages/api/src/events/user/user_created.ts | 2 +- packages/api/src/resolvers/api_key/index.ts | 2 +- packages/api/src/resolvers/article/index.ts | 157 ++--- .../resolvers/article_saving_request/index.ts | 2 +- packages/api/src/resolvers/filters/index.ts | 11 +- .../api/src/resolvers/function_resolvers.ts | 398 ++++++------ .../importers/uploadImportFileResolver.ts | 2 +- .../api/src/resolvers/integrations/index.ts | 2 +- packages/api/src/resolvers/labels/index.ts | 2 +- packages/api/src/resolvers/links/index.ts | 71 +-- .../api/src/resolvers/newsletters/index.ts | 2 +- packages/api/src/resolvers/reaction/index.ts | 228 ++++--- .../api/src/resolvers/recent_emails/index.ts | 2 +- .../src/resolvers/recent_searches/index.ts | 10 +- .../src/resolvers/recommendations/index.ts | 2 +- packages/api/src/resolvers/reminders/index.ts | 571 +++++++++--------- packages/api/src/resolvers/rules/index.ts | 8 +- packages/api/src/resolvers/save/index.ts | 35 +- .../api/src/resolvers/subscriptions/index.ts | 2 +- packages/api/src/resolvers/types.ts | 38 +- .../api/src/resolvers/upload_files/index.ts | 50 +- packages/api/src/resolvers/user/index.ts | 181 +++--- .../src/resolvers/user_feed_article/index.ts | 256 ++++---- .../api/src/resolvers/user_friends/index.ts | 199 +++--- .../resolvers/user_personalization/index.ts | 14 +- packages/api/src/resolvers/webhooks/index.ts | 10 +- packages/api/src/routers/article_router.ts | 2 +- packages/api/src/routers/auth/auth_router.ts | 2 +- packages/api/src/routers/page_router.ts | 10 +- packages/api/src/routers/svc/content.ts | 11 +- .../api/src/routers/svc/email_attachment.ts | 18 +- packages/api/src/routers/svc/integrations.ts | 2 +- packages/api/src/routers/svc/reminders.ts | 199 +++--- packages/api/src/routers/svc/rss_feed.ts | 2 +- packages/api/src/routers/svc/webhooks.ts | 2 +- packages/api/src/routers/text_to_speech.ts | 2 +- packages/api/src/routers/user_router.ts | 2 +- packages/api/src/schema.ts | 70 +-- packages/api/src/server.ts | 32 +- packages/api/src/services/archive_link.ts | 2 +- .../src/services/create_page_save_request.ts | 2 +- packages/api/src/services/create_user.ts | 28 +- packages/api/src/services/features.ts | 2 +- packages/api/src/services/followers.ts | 4 +- packages/api/src/services/groups.ts | 2 +- .../api/src/services/integrations/readwise.ts | 2 +- packages/api/src/services/labels.ts | 14 +- packages/api/src/services/newsletters.ts | 6 +- packages/api/src/services/received_emails.ts | 2 +- packages/api/src/services/reminders.ts | 24 +- packages/api/src/services/reports.ts | 2 +- packages/api/src/services/rules.ts | 4 +- packages/api/src/services/save_file.ts | 35 +- packages/api/src/services/save_page.ts | 42 +- packages/api/src/services/save_url.ts | 4 +- packages/api/src/services/search_history.ts | 2 +- packages/api/src/services/subscriptions.ts | 2 +- .../api/src/services/user_device_tokens.ts | 8 +- packages/api/src/utils/auth.ts | 2 +- packages/api/src/utils/helpers.ts | 11 +- packages/api/src/utils/parser.ts | 2 +- packages/api/test/db.ts | 2 +- packages/api/test/resolvers/api_key.test.ts | 2 +- packages/api/test/resolvers/article.test.ts | 2 +- packages/api/test/resolvers/features.test.ts | 2 +- .../api/test/resolvers/integrations.test.ts | 2 +- packages/api/test/resolvers/labels.test.ts | 2 +- .../api/test/resolvers/newsletters.test.ts | 2 +- .../api/test/resolvers/recent_emails.test.ts | 2 +- .../test/resolvers/recent_searches.test.ts | 2 +- packages/api/test/resolvers/report.test.ts | 2 +- packages/api/test/resolvers/rules.test.ts | 2 +- .../api/test/resolvers/subscriptions.test.ts | 2 +- .../test/resolvers/user_device_tokens.test.ts | 2 +- .../test/resolvers/user_feed_article.test.ts | 2 +- packages/api/test/resolvers/webhooks.test.ts | 2 +- packages/api/test/routers/auth.test.ts | 2 +- packages/api/test/routers/emails.test.ts | 2 +- .../api/test/routers/integrations.test.ts | 2 +- packages/api/test/routers/webhooks.test.ts | 2 +- .../create_content_display_report.test.ts | 5 +- .../api/test/services/create_user.test.ts | 2 +- packages/api/test/services/labels.test.ts | 2 +- .../services/save_newsletter_email.test.ts | 2 +- 90 files changed, 1431 insertions(+), 1557 deletions(-) rename packages/api/src/entity/{utils.ts => index.ts} (61%) diff --git a/packages/api/src/apollo.ts b/packages/api/src/apollo.ts index 4ea00c319..ea6f75d7f 100644 --- a/packages/api/src/apollo.ts +++ b/packages/api/src/apollo.ts @@ -10,9 +10,8 @@ import { ContextFunction } from 'apollo-server-core' import { ApolloServer } from 'apollo-server-express' import { ExpressContext } from 'apollo-server-express/dist/ApolloServer' import * as jwt from 'jsonwebtoken' -import { Knex } from 'knex' +import { EntityManager } from 'typeorm' import { promisify } from 'util' -import { kx } from './datalayer/knex_config' import { createPubSubClient } from './datalayer/pubsub' import { sanitizeDirectiveTransformer } from './directives' import { env } from './env' @@ -20,7 +19,7 @@ import { functionResolvers } from './resolvers/function_resolvers' import { ClaimsToSet, ResolverContext } from './resolvers/types' import ScalarResolvers from './scalars' import typeDefs from './schema' -import { initModels } from './server' +import { AppDataSource } from './server' import { tracer } from './tracing' import { getClaimsByToken, setAuthInCookie } from './utils/auth' import { SetClaimsRole } from './utils/dictionary' @@ -47,7 +46,7 @@ const contextFunc: ContextFunction = async ({ const claims = await getClaimsByToken(token) async function setClaims( - tx: Knex.Transaction, + em: EntityManager, uuid?: string, userRole?: string ): Promise { @@ -55,18 +54,14 @@ const contextFunc: ContextFunction = async ({ (claims && claims.uid) || uuid || '00000000-0000-0000-0000-000000000000' const dbRole = userRole === SetClaimsRole.ADMIN ? 'omnivore_admin' : 'omnivore_user' - return tx.raw('SELECT * from omnivore.set_claims(?, ?)', [uid, dbRole]) + return em.query('SELECT * from omnivore.set_claims($1, $2)', [uid, dbRole]) } const ctx = { log: logger, claims, - kx, pubsub, // no caching for subscriptions - // TODO: create per request caching for connections - // eslint-disable-next-line @typescript-eslint/no-explicit-any - models: initModels(kx, true), clearAuth: () => { res.clearCookie('auth') res.clearCookie('pendingUserAuth') @@ -78,12 +73,12 @@ const contextFunc: ContextFunction = async ({ ) => await setAuthInCookie(claims, res, secret), setClaims, authTrx: ( - cb: (tx: Knex.Transaction) => TResult, + cb: (em: EntityManager) => TResult, userRole?: string ): Promise => - kx.transaction(async (tx) => { - await setClaims(tx, undefined, userRole) - return cb(tx) + AppDataSource.transaction(async (em) => { + await setClaims(em, undefined, userRole) + return cb(em) }), tracingSpan: tracer.startSpan('apollo.request'), } diff --git a/packages/api/src/entity/utils.ts b/packages/api/src/entity/index.ts similarity index 61% rename from packages/api/src/entity/utils.ts rename to packages/api/src/entity/index.ts index 1ee7933cc..0c87ae2d2 100644 --- a/packages/api/src/entity/utils.ts +++ b/packages/api/src/entity/index.ts @@ -1,5 +1,8 @@ import { EntityManager, EntityTarget, Repository } from 'typeorm' import { AppDataSource } from '../server' +import { Reminder } from './reminder' +import { UploadFile } from './upload_file' +import { User } from './user' export const setClaims = async ( t: EntityManager, @@ -14,3 +17,7 @@ export const setClaims = async ( export const getRepository = (entity: EntityTarget): Repository => { return AppDataSource.getRepository(entity) } + +export const userRepository = getRepository(User) +export const uploadFileRepository = getRepository(UploadFile) +export const reminderRepository = getRepository(Reminder) diff --git a/packages/api/src/entity/label.ts b/packages/api/src/entity/label.ts index 384cf4ed9..dd0c65079 100644 --- a/packages/api/src/entity/label.ts +++ b/packages/api/src/entity/label.ts @@ -3,12 +3,9 @@ import { CreateDateColumn, Entity, JoinColumn, - JoinTable, - ManyToMany, ManyToOne, PrimaryGeneratedColumn, } from 'typeorm' -import { LibraryItem } from './library_item' import { User } from './user' @Entity({ name: 'labels' }) @@ -37,8 +34,4 @@ export class Label { @Column('boolean', { default: false }) internal!: boolean - - @ManyToMany(() => LibraryItem, (libraryItem) => libraryItem.labels) - @JoinTable() - libraryItems?: LibraryItem[] } diff --git a/packages/api/src/entity/library_item.ts b/packages/api/src/entity/library_item.ts index 04228c67f..6a8325a9b 100644 --- a/packages/api/src/entity/library_item.ts +++ b/packages/api/src/entity/library_item.ts @@ -64,7 +64,7 @@ export class LibraryItem { originalUrl!: string @Column('text', { nullable: true }) - downloadUrl?: string + downloadUrl?: string | null @Column('text') slug!: string @@ -73,10 +73,10 @@ export class LibraryItem { title!: string @Column('text', { nullable: true }) - author?: string + author?: string | null @Column('text', { nullable: true }) - description?: string + description?: string | null @Column('timestamptz') savedAt?: Date @@ -85,49 +85,49 @@ export class LibraryItem { createdAt?: Date @Column('timestamptz', { nullable: true }) - publishedAt?: Date + publishedAt?: Date | null @Column('timestamptz') - archivedAt?: Date + archivedAt?: Date | null @Column('timestamptz') - deletedAt?: Date + deletedAt?: Date | null @Column('timestamptz') - readAt?: Date + readAt?: Date | null @UpdateDateColumn() updatedAt?: Date @Column('text', { nullable: true }) - itemLanguage?: string + itemLanguage?: string | null @Column('integer', { nullable: true }) - wordCount?: number + wordCount?: number | null @Column('text', { nullable: true }) - siteName?: string + siteName?: string | null @Column('text', { nullable: true }) - siteIcon?: string + siteIcon?: string | null @Column('json', { nullable: true }) - metadata?: Record + metadata?: Record | null @Column('integer', { nullable: true }) - readingProgressLastReadAnchor?: number + readingProgressLastReadAnchor?: number | null @Column('integer', { nullable: true }) - readingProgressHighestReadAnchor?: number + readingProgressHighestReadAnchor?: number | null @Column('real', { nullable: true }) - readingProgressTopPercent?: number + readingProgressTopPercent?: number | null @Column('real', { nullable: true }) - readingProgressBottomPercent?: number + readingProgressBottomPercent?: number | null @Column('text', { nullable: true }) - thumbnail?: string + thumbnail?: string | null @Column('enum', { enum: LibraryItemType, default: LibraryItemType.Unknown }) itemType?: LibraryItemType @@ -140,47 +140,29 @@ export class LibraryItem { contentReader?: ContentReaderType @Column('text', { nullable: true }) - originalContent?: string + originalContent?: string | null @Column('text', { nullable: true }) - readableContent?: string - - @Column('tsvector', { nullable: true }) - contentTsv?: string - - @Column('tsvector', { nullable: true }) - siteTsv?: string - - @Column('tsvector', { nullable: true }) - titleTsv?: string - - @Column('tsvector', { nullable: true }) - authorTsv?: string - - @Column('tsvector', { nullable: true }) - descriptionTsv?: string - - @Column('tsvector', { nullable: true }) - searchTsv?: string + readableContent?: string | null @Column('text', { nullable: true }) - modelName?: string + modelName?: string | null // NOT SUPPORTED IN TYPEORM // @Column('vector', { nullable: true }) // embedding?: number[] @Column('text', { nullable: true }) - textContentHash?: string + textContentHash?: string | null @Column('text', { nullable: true }) - gcsArchiveId?: string + gcsArchiveId?: string | null @OneToOne(() => Subscription, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'subscription_id' }) subscription?: Subscription - @ManyToMany(() => Label, (label) => label.libraryItems) + @ManyToMany(() => Label) @JoinTable() labels?: Label[] diff --git a/packages/api/src/entity/profile.ts b/packages/api/src/entity/profile.ts index b5f04b48d..9fdc96b76 100644 --- a/packages/api/src/entity/profile.ts +++ b/packages/api/src/entity/profile.ts @@ -18,11 +18,11 @@ export class Profile { @Column('text') username!: string - @Column('text') - bio!: string + @Column('text', { nullable: true }) + bio?: string | null - @Column('text') - pictureUrl!: string + @Column('text', { nullable: true }) + pictureUrl?: string | null @OneToOne(() => User, (user) => user.profile) @JoinColumn({ name: 'user_id' }) diff --git a/packages/api/src/entity/user.ts b/packages/api/src/entity/user.ts index 60859dc91..a926b5a89 100644 --- a/packages/api/src/entity/user.ts +++ b/packages/api/src/entity/user.ts @@ -7,13 +7,23 @@ import { PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm' -import { RegistrationType, StatusType } from '../datalayer/user/model' +import { Label } from './label' import { NewsletterEmail } from './newsletter_email' import { Profile } from './profile' -import { Label } from './label' import { Subscription } from './subscription' import { UserPersonalization } from './user_personalization' +export enum RegistrationType { + Google = 'GOOGLE', + Apple = 'APPLE', + Email = 'EMAIL', +} + +export enum StatusType { + Active = 'ACTIVE', + Pending = 'PENDING', +} + @Entity() export class User { @PrimaryGeneratedColumn('uuid') @@ -40,7 +50,10 @@ export class User { @OneToMany(() => NewsletterEmail, (newsletterEmail) => newsletterEmail.user) newsletterEmails?: NewsletterEmail[] - @OneToOne(() => Profile, (profile) => profile.user, { eager: true }) + @OneToOne(() => Profile, (profile) => profile.user, { + eager: true, + cascade: true, + }) profile!: Profile @Column('varchar', { length: 255, nullable: true }) diff --git a/packages/api/src/events/user/user_created.ts b/packages/api/src/events/user/user_created.ts index 13c46aae0..d73383dae 100644 --- a/packages/api/src/events/user/user_created.ts +++ b/packages/api/src/events/user/user_created.ts @@ -26,7 +26,7 @@ export class CreateIntercomAccount email: profile.user.email, externalId: profile.user.id, name: profile.user.name, - avatar: profile.pictureUrl, + avatar: profile.pictureUrl || undefined, customAttributes: customAttributes, signedUpAt: Math.floor(Date.now() / 1000), }) diff --git a/packages/api/src/resolvers/api_key/index.ts b/packages/api/src/resolvers/api_key/index.ts index 94ec3c753..8b5cc6a5b 100644 --- a/packages/api/src/resolvers/api_key/index.ts +++ b/packages/api/src/resolvers/api_key/index.ts @@ -1,6 +1,6 @@ +import { getRepository } from '../../entity' import { ApiKey } from '../../entity/api_key' import { User } from '../../entity/user' -import { getRepository } from '../../entity/utils' import { env } from '../../env' import { ApiKeysError, diff --git a/packages/api/src/resolvers/article/index.ts b/packages/api/src/resolvers/article/index.ts index b05a80643..d50532d44 100644 --- a/packages/api/src/resolvers/article/index.ts +++ b/packages/api/src/resolvers/article/index.ts @@ -20,6 +20,9 @@ import { PageType, SearchItem as SearchItemData, } from '../../elastic/types' +import { getRepository } from '../../entity' +import { UploadFile } from '../../entity/upload_file' +import { User } from '../../entity/user' import { env } from '../../env' import { Article, @@ -42,7 +45,6 @@ import { MutationSaveArticleReadingProgressArgs, MutationSetBookmarkArticleArgs, MutationSetFavoriteArticleArgs, - MutationSetShareArticleArgs, PageInfo, QueryArticleArgs, QueryArticlesArgs, @@ -63,8 +65,6 @@ import { SetFavoriteArticleError, SetFavoriteArticleErrorCode, SetFavoriteArticleSuccess, - SetShareArticleError, - SetShareArticleErrorCode, SetShareArticleSuccess, SortParams, TypeaheadSearchError, @@ -82,6 +82,7 @@ import { createLabels, getLabelsByIds, } from '../../services/labels' +import { setFileUploadComplete } from '../../services/save_file' import { parsedContentToPage } from '../../services/save_page' import { traceAs } from '../../tracing' import { Merge } from '../../util' @@ -167,12 +168,7 @@ export const createArticleResolver = authorized< }, ctx ) => { - const { - models, - authTrx, - claims: { uid }, - log, - } = ctx + const { authTrx, log, uid } = ctx analytics.track({ userId: uid, @@ -184,7 +180,18 @@ export const createArticleResolver = authorized< }, }) - const user = userDataToUser(await models.user.get(uid)) + const userData = await getRepository(User).findOneBy({ id: uid }) + if (!userData) { + return pageError( + { + errorCodes: [CreateArticleErrorCode.Unauthorized], + }, + ctx, + pageId + ) + } + const user = userDataToUser(userData) + try { if (isSiteBlockedForParse(url)) { return pageError( @@ -235,14 +242,17 @@ export const createArticleResolver = authorized< }, } // save state - let archivedAt = + const archivedAt = state === ArticleSavingRequestStatus.Archived ? new Date() : null - if (pageId) { - const reminder = await models.reminder.getByRequestId(uid, pageId) - if (reminder && reminder.archiveUntil) { - archivedAt = new Date() - } - } + // if (pageId) { + // const reminder = await getRepository(Reminder).findOneBy({ + // articleSavingRequest: pageId, + // user: { id: uid }, + // }) + // if (reminder && reminder.archiveUntil) { + // archivedAt = new Date() + // } + // } // add labels to page const labels = inputLabels ? await createLabels(ctx, inputLabels) @@ -252,9 +262,9 @@ export const createArticleResolver = authorized< /* We do not trust the values from client, lookup upload file by querying * with filtering on user ID and URL to verify client's uploadFileId is valid. */ - const uploadFile = await models.uploadFile.getWhere({ + const uploadFile = await getRepository(UploadFile).findOneBy({ id: uploadFileId, - userId: uid, + user: { id: uid }, }) if (!uploadFile) { return pageError( @@ -330,7 +340,7 @@ export const createArticleResolver = authorized< if (uploadFileId) { const uploadFileData = await authTrx(async (tx) => { - return models.uploadFile.setFileUploadComplete(uploadFileId, tx) + return setFileUploadComplete(uploadFileId, tx) }) if (!uploadFileData || !uploadFileData.id || !uploadFileData.fileName) { return pageError( @@ -579,64 +589,65 @@ export type SetShareArticleSuccessPartial = Merge< } > -export const setShareArticleResolver = authorized< - SetShareArticleSuccessPartial, - SetShareArticleError, - MutationSetShareArticleArgs ->( - async ( - _, - { input: { articleID, share, sharedComment, sharedWithHighlights } }, - { models, authTrx, claims: { uid }, log } - ) => { - const article = await models.article.get(articleID) - if (!article) { - return { errorCodes: [SetShareArticleErrorCode.NotFound] } - } +// TODO: not implemented yet +// export const setShareArticleResolver = authorized< +// SetShareArticleSuccessPartial, +// SetShareArticleError, +// MutationSetShareArticleArgs +// >( +// async ( +// _, +// { input: { articleID, share, sharedComment, sharedWithHighlights } }, +// { models, authTrx, claims: { uid }, log } +// ) => { +// const article = await models.article.get(articleID) +// if (!article) { +// return { errorCodes: [SetShareArticleErrorCode.NotFound] } +// } - const sharedAt = share ? new Date() : null +// const sharedAt = share ? new Date() : null - log.info(`${share ? 'S' : 'Uns'}haring an article`, { - article: Object.assign({}, article, { - content: undefined, - originalHtml: undefined, - sharedAt, - }), - labels: { - source: 'resolver', - resolver: 'setShareArticleResolver', - articleId: article.id, - userId: uid, - }, - }) +// log.info(`${share ? 'S' : 'Uns'}haring an article`, { +// article: Object.assign({}, article, { +// content: undefined, +// originalHtml: undefined, +// sharedAt, +// }), +// labels: { +// source: 'resolver', +// resolver: 'setShareArticleResolver', +// articleId: article.id, +// userId: uid, +// }, +// }) - const result = await authTrx((tx) => - models.userArticle.updateByArticleId( - uid, - articleID, - { sharedAt, sharedComment, sharedWithHighlights }, - tx - ) - ) +// const result = await authTrx((tx) => +// models.userArticle.updateByArticleId( +// uid, +// articleID, +// { sharedAt, sharedComment, sharedWithHighlights }, +// tx +// ) +// ) - if (!result) { - return { errorCodes: [SetShareArticleErrorCode.NotFound] } - } +// if (!result) { +// return { errorCodes: [SetShareArticleErrorCode.NotFound] } +// } - // Make sure article.id instead of userArticle.id has passed. We use it for cache updates - const updatedArticle = { - ...result, - ...article, - postedByViewer: !!sharedAt, - } - const updatedFeedArticle = sharedAt ? { ...result, sharedAt } : undefined - return { - updatedFeedArticleId: result.id, - updatedFeedArticle, - updatedArticle, - } - } -) +// // Make sure article.id instead of userArticle.id has passed. We use it for cache updates +// const updatedArticle = { +// ...result, +// ...article, +// postedByViewer: !!sharedAt, +// } +// const updatedFeedArticle = sharedAt ? { ...result, sharedAt } : undefined +// return { +// updatedFeedArticleId: result.id, +// updatedFeedArticle, +// updatedArticle, +// } +// } +// ) export type SetBookmarkArticleSuccessPartial = Merge< SetBookmarkArticleSuccess, diff --git a/packages/api/src/resolvers/article_saving_request/index.ts b/packages/api/src/resolvers/article_saving_request/index.ts index 55f669093..ee935665b 100644 --- a/packages/api/src/resolvers/article_saving_request/index.ts +++ b/packages/api/src/resolvers/article_saving_request/index.ts @@ -1,7 +1,7 @@ /* eslint-disable prefer-const */ import { getPageByParam } from '../../elastic/pages' +import { getRepository } from '../../entity' import { User } from '../../entity/user' -import { getRepository } from '../../entity/utils' import { env } from '../../env' import { ArticleSavingRequestError, diff --git a/packages/api/src/resolvers/filters/index.ts b/packages/api/src/resolvers/filters/index.ts index fd6193a6c..483b86902 100644 --- a/packages/api/src/resolvers/filters/index.ts +++ b/packages/api/src/resolvers/filters/index.ts @@ -1,4 +1,8 @@ -import { authorized } from '../../utils/helpers' +import { Between } from 'typeorm' +import { getRepository, setClaims } from '../../entity' +import { Filter } from '../../entity/filter' +import { User } from '../../entity/user' +import { env } from '../../env' import { DeleteFilterError, DeleteFilterErrorCode, @@ -20,14 +24,11 @@ import { UpdateFilterSuccess, UpdateFilterErrorCode, } from '../../generated/graphql' -import { Filter } from '../../entity/filter' -import { getRepository, setClaims } from '../../entity/utils' -import { User } from '../../entity/user' import { AppDataSource } from '../../server' -import { Between } from 'typeorm' import { analytics } from '../../utils/analytics' import { env } from '../../env' import { isNil, mergeWith } from 'lodash' +import { authorized } from '../../utils/helpers' export const saveFilterResolver = authorized< SaveFilterSuccess, diff --git a/packages/api/src/resolvers/function_resolvers.ts b/packages/api/src/resolvers/function_resolvers.ts index 9c62aeccf..e635ba08a 100644 --- a/packages/api/src/resolvers/function_resolvers.ts +++ b/packages/api/src/resolvers/function_resolvers.ts @@ -3,23 +3,21 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { getShareInfoForArticle } from '../datalayer/links/share_info' import { getPageByParam } from '../elastic/pages' +import { getRepository } from '../entity' import { Subscription } from '../entity/subscription' +import { UploadFile } from '../entity/upload_file' +import { User } from '../entity/user' import { Article, ArticleHighlightsInput, Highlight, HighlightType, - LinkShareInfo, PageType, - Reaction, SearchItem, - User, } from '../generated/graphql' import { userDataToUser, validatedDate, wordsCount } from '../utils/helpers' import { createImageProxyUrl } from '../utils/imageproxy' -import { logger } from '../utils/logger' import { contentReaderForPage, generateDownloadSignedUrl, @@ -38,14 +36,14 @@ import { createHighlightResolver, createLabelResolver, createNewsletterEmailResolver, - createReminderResolver, + // createReminderResolver, deleteAccountResolver, deleteFilterResolver, deleteHighlightResolver, deleteIntegrationResolver, deleteLabelResolver, deleteNewsletterEmailResolver, - deleteReminderResolver, + // deleteReminderResolver, deleteRuleResolver, deleteWebhookResolver, deviceTokensResolver, @@ -54,11 +52,11 @@ import { getAllUsersResolver, getArticleResolver, getArticlesResolver, - getFollowersResolver, - getFollowingResolver, + // getFollowersResolver, + // getFollowingResolver, getMeUserResolver, - getSharedArticleResolver, - getUserFeedArticlesResolver, + // getSharedArticleResolver, + // getUserFeedArticlesResolver, getUserPersonalizationResolver, getUserResolver, googleLoginResolver, @@ -76,7 +74,7 @@ import { newsletterEmailsResolver, recommendHighlightsResolver, recommendResolver, - reminderResolver, + // reminderResolver, reportItemResolver, revokeApiKeyResolver, rulesResolver, @@ -90,13 +88,13 @@ import { setBookmarkArticleResolver, setDeviceTokenResolver, setFavoriteArticleResolver, - setFollowResolver, + // setFollowResolver, setIntegrationResolver, setLabelsForHighlightResolver, setLabelsResolver, setLinkArchivedResolver, setRuleResolver, - setShareArticleResolver, + // setShareArticleResolver, setShareHighlightResolver, setUserPersonalizationResolver, setWebhookResolver, @@ -107,10 +105,10 @@ import { updateFilterResolver, updateHighlightResolver, updateLabelResolver, - updateLinkShareInfoResolver, + // updateLinkShareInfoResolver, updatePageResolver, - updateReminderResolver, - updateSharedCommentResolver, + // updateReminderResolver, + // updateSharedCommentResolver, updatesSinceResolver, updateSubscriptionResolver, updateUserProfileResolver, @@ -120,7 +118,6 @@ import { webhookResolver, webhooksResolver, } from './index' -import { createReactionResolver, deleteReactionResolver } from './reaction' import { markEmailAsItemResolver, recentEmailsResolver } from './recent_emails' import { recentSearchesResolver } from './recent_searches' import { Claims, WithDataSourcesContext } from './types' @@ -154,30 +151,30 @@ export const functionResolvers = { updateUserProfile: updateUserProfileResolver, createArticle: createArticleResolver, createHighlight: createHighlightResolver, - createReaction: createReactionResolver, - deleteReaction: deleteReactionResolver, + // createReaction: createReactionResolver, + // deleteReaction: deleteReactionResolver, mergeHighlight: mergeHighlightResolver, updateHighlight: updateHighlightResolver, deleteHighlight: deleteHighlightResolver, uploadFileRequest: uploadFileRequestResolver, - setShareArticle: setShareArticleResolver, - updateSharedComment: updateSharedCommentResolver, - setFollow: setFollowResolver, + // setShareArticle: setShareArticleResolver, + // updateSharedComment: updateSharedCommentResolver, + // setFollow: setFollowResolver, setBookmarkArticle: setBookmarkArticleResolver, setUserPersonalization: setUserPersonalizationResolver, createArticleSavingRequest: createArticleSavingRequestResolver, setShareHighlight: setShareHighlightResolver, reportItem: reportItemResolver, - updateLinkShareInfo: updateLinkShareInfoResolver, + // updateLinkShareInfo: updateLinkShareInfoResolver, setLinkArchived: setLinkArchivedResolver, createNewsletterEmail: createNewsletterEmailResolver, deleteNewsletterEmail: deleteNewsletterEmailResolver, saveUrl: saveUrlResolver, savePage: savePageResolver, saveFile: saveFileResolver, - createReminder: createReminderResolver, - updateReminder: updateReminderResolver, - deleteReminder: deleteReminderResolver, + // createReminder: createReminderResolver, + // updateReminder: updateReminderResolver, + // deleteReminder: deleteReminderResolver, setDeviceToken: setDeviceTokenResolver, createLabel: createLabelResolver, updateLabel: updateLabelResolver, @@ -221,15 +218,15 @@ export const functionResolvers = { users: getAllUsersResolver, validateUsername: validateUsernameResolver, article: getArticleResolver, - sharedArticle: getSharedArticleResolver, + // sharedArticle: getSharedArticleResolver, articles: getArticlesResolver, - feedArticles: getUserFeedArticlesResolver, - getFollowers: getFollowersResolver, - getFollowing: getFollowingResolver, + // feedArticles: getUserFeedArticlesResolver, + // getFollowers: getFollowersResolver, + // getFollowing: getFollowingResolver, getUserPersonalization: getUserPersonalizationResolver, articleSavingRequest: articleSavingRequestResolver, newsletterEmails: newsletterEmailsResolver, - reminder: reminderResolver, + // reminder: reminderResolver, labels: labelsResolver, search: searchResolver, subscriptions: subscriptionsResolver, @@ -247,141 +244,141 @@ export const functionResolvers = { groups: groupsResolver, recentEmails: recentEmailsResolver, }, - User: { - async sharedArticles( - user: User, - __: Record, - ctx: WithDataSourcesContext - ) { - return ctx.models.userArticle.getUserSharedArticles(user.id, ctx.kx) - }, - async sharedArticlesCount( - user: { id: string; sharedArticlesCount?: number }, - __: Record, - ctx: WithDataSourcesContext - ) { - if (user.sharedArticlesCount) return user.sharedArticlesCount - return ctx.models.userArticle.getSharedArticlesCount(user.id, ctx.kx) - }, - async sharedHighlightsCount( - user: { id: string; sharedHighlightsCount?: number }, - _: unknown, - ctx: WithDataSourcesContext - ) { - // #TODO: restructure highlightStats and sharedArticlesCount in order to get it within a single query - if (user.sharedHighlightsCount) return user.sharedHighlightsCount - const { sharedHighlightsCount } = - await ctx.models.user.getSharedHighlightsStats(user.id) - return sharedHighlightsCount - }, - async sharedNotesCount( - user: User, - _: unknown, - ctx: WithDataSourcesContext - ) { - if (user.sharedNotesCount) return user.sharedNotesCount - const { sharedNotesCount } = - await ctx.models.user.getSharedHighlightsStats(user.id) - return sharedNotesCount - }, - }, - FeedArticle: { - async article( - feedArticle: { articleId: string; userId: string; article?: Article }, - __: unknown, - ctx: WithDataSourcesContext - ) { - if (feedArticle.article) return feedArticle.article + // User: { + // async sharedArticles( + // user: User, + // __: Record, + // ctx: WithDataSourcesContext + // ) { + // return ctx.models.userArticle.getUserSharedArticles(user.id, ctx.kx) + // }, + // async sharedArticlesCount( + // user: { id: string; sharedArticlesCount?: number }, + // __: Record, + // ctx: WithDataSourcesContext + // ) { + // if (user.sharedArticlesCount) return user.sharedArticlesCount + // return ctx.models.userArticle.getSharedArticlesCount(user.id, ctx.kx) + // }, + // async sharedHighlightsCount( + // user: { id: string; sharedHighlightsCount?: number }, + // _: unknown, + // ctx: WithDataSourcesContext + // ) { + // // #TODO: restructure highlightStats and sharedArticlesCount in order to get it within a single query + // if (user.sharedHighlightsCount) return user.sharedHighlightsCount + // const { sharedHighlightsCount } = + // await ctx.models.user.getSharedHighlightsStats(user.id) + // return sharedHighlightsCount + // }, + // async sharedNotesCount( + // user: User, + // _: unknown, + // ctx: WithDataSourcesContext + // ) { + // if (user.sharedNotesCount) return user.sharedNotesCount + // const { sharedNotesCount } = + // await ctx.models.user.getSharedHighlightsStats(user.id) + // return sharedNotesCount + // }, + // }, + // FeedArticle: { + // async article( + // feedArticle: { articleId: string; userId: string; article?: Article }, + // __: unknown, + // ctx: WithDataSourcesContext + // ) { + // if (feedArticle.article) return feedArticle.article - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let a: any + // // eslint-disable-next-line @typescript-eslint/no-explicit-any + // let a: any - const savedArticle = - ctx.claims?.uid && - (await ctx.models.userArticle.getForUser( - ctx.claims?.uid, - feedArticle.articleId, - ctx.kx - )) + // const savedArticle = + // ctx.claims?.uid && + // (await ctx.models.userArticle.getForUser( + // ctx.claims?.uid, + // feedArticle.articleId, + // ctx.kx + // )) - if (savedArticle) { - // If user has saved the article, use his version (slug) then - a = { - ...savedArticle, - savedByViewer: true, - postedByViewer: !!savedArticle.sharedAt, - } - } else { - a = await ctx.models.userArticle.getForUser( - feedArticle.userId, - feedArticle.articleId, - ctx.kx - ) - } + // if (savedArticle) { + // // If user has saved the article, use his version (slug) then + // a = { + // ...savedArticle, + // savedByViewer: true, + // postedByViewer: !!savedArticle.sharedAt, + // } + // } else { + // a = await ctx.models.userArticle.getForUser( + // feedArticle.userId, + // feedArticle.articleId, + // ctx.kx + // ) + // } - if (a && a.image) { - a.image = createImageProxyUrl(a.image, 0, 180) - } else { - logger.info( - 'error getting article for feedItem', - feedArticle.userId, - feedArticle.articleId - ) - } + // if (a && a.image) { + // a.image = createImageProxyUrl(a.image, 0, 180) + // } else { + // logger.info( + // 'error getting article for feedItem', + // feedArticle.userId, + // feedArticle.articleId + // ) + // } - return a - }, - async sharedBy( - feedArticle: { userId: string; sharedBy?: User }, - __: unknown, - ctx: WithDataSourcesContext - ) { - if (feedArticle.sharedBy) return feedArticle.sharedBy - return userDataToUser(await ctx.models.user.get(feedArticle.userId)) - }, - async highlight( - feedArticle: { highlightId?: string; highlight?: Highlight }, - _: unknown, - ctx: WithDataSourcesContext - ) { - if (feedArticle.highlight) return feedArticle.highlight - return feedArticle.highlightId - ? await ctx.models.highlight.get(feedArticle.highlightId) - : null - }, - async reactions( - feedArticle: { id: string; reactions?: Reaction[] }, - _: unknown, - ctx: WithDataSourcesContext - ) { - const { reactions, id } = feedArticle - if (reactions) return reactions + // return a + // }, + // async sharedBy( + // feedArticle: { userId: string; sharedBy?: User }, + // __: unknown, + // ctx: WithDataSourcesContext + // ) { + // if (feedArticle.sharedBy) return feedArticle.sharedBy + // return userDataToUser(await ctx.models.user.get(feedArticle.userId)) + // }, + // async highlight( + // feedArticle: { highlightId?: string; highlight?: Highlight }, + // _: unknown, + // ctx: WithDataSourcesContext + // ) { + // if (feedArticle.highlight) return feedArticle.highlight + // return feedArticle.highlightId + // ? await ctx.models.highlight.get(feedArticle.highlightId) + // : null + // }, + // async reactions( + // feedArticle: { id: string; reactions?: Reaction[] }, + // _: unknown, + // ctx: WithDataSourcesContext + // ) { + // const { reactions, id } = feedArticle + // if (reactions) return reactions - return await ctx.models.reaction.batchGetFromArticle(id) - }, - async highlightsCount( - feedArticle: { id: string; highlightsCount?: number }, - _: unknown, - ctx: WithDataSourcesContext - ) { - if (feedArticle.highlightsCount) return feedArticle.highlightsCount - const { highlightsCount } = await ctx.models.userArticle.getStats( - feedArticle.id - ) - return highlightsCount - }, - async annotationsCount( - feedArticle: { id: string; annotationsCount?: number }, - _: unknown, - ctx: WithDataSourcesContext - ) { - if (feedArticle.annotationsCount) return feedArticle.annotationsCount - const { annotationsCount } = await ctx.models.userArticle.getStats( - feedArticle.id - ) - return annotationsCount - }, - }, + // return await ctx.models.reaction.batchGetFromArticle(id) + // }, + // async highlightsCount( + // feedArticle: { id: string; highlightsCount?: number }, + // _: unknown, + // ctx: WithDataSourcesContext + // ) { + // if (feedArticle.highlightsCount) return feedArticle.highlightsCount + // const { highlightsCount } = await ctx.models.userArticle.getStats( + // feedArticle.id + // ) + // return highlightsCount + // }, + // async annotationsCount( + // feedArticle: { id: string; annotationsCount?: number }, + // _: unknown, + // ctx: WithDataSourcesContext + // ) { + // if (feedArticle.annotationsCount) return feedArticle.annotationsCount + // const { annotationsCount } = await ctx.models.userArticle.getStats( + // feedArticle.id + // ) + // return annotationsCount + // }, + // }, Article: { async url(article: Article, _: unknown, ctx: WithDataSourcesContext) { if ( @@ -390,7 +387,9 @@ export const functionResolvers = { ctx.claims && article.uploadFileId ) { - const upload = await ctx.models.uploadFile.get(article.uploadFileId) + const upload = await getRepository(UploadFile).findOneBy({ + id: article.uploadFileId, + }) if (!upload || !upload.fileName) { return undefined } @@ -490,20 +489,20 @@ export const functionResolvers = { ) { return article.highlights || [] }, - async shareInfo( - article: { id: string; sharedBy?: User; shareInfo?: LinkShareInfo }, - __: unknown, - ctx: WithDataSourcesContext - ): Promise { - if (article.shareInfo) return article.shareInfo - if (!ctx.claims?.uid) return undefined - return getShareInfoForArticle( - ctx.kx, - ctx.claims?.uid, - article.id, - ctx.models - ) - }, + // async shareInfo( + // article: { id: string; sharedBy?: User; shareInfo?: LinkShareInfo }, + // __: unknown, + // ctx: WithDataSourcesContext + // ): Promise { + // if (article.shareInfo) return article.shareInfo + // if (!ctx.claims?.uid) return undefined + // return getShareInfoForArticle( + // ctx.kx, + // ctx.claims?.uid, + // article.id, + // ctx.models + // ) + // }, image(article: { image?: string }): string | undefined { return article.image && createImageProxyUrl(article.image, 320, 320) }, @@ -528,18 +527,23 @@ export const functionResolvers = { __: unknown, ctx: WithDataSourcesContext ) { - return userDataToUser(await ctx.models.user.get(highlight.userId)) - }, - async reactions( - highlight: { id: string; reactions?: Reaction[] }, - _: unknown, - ctx: WithDataSourcesContext - ) { - const { reactions, id } = highlight - if (reactions) return reactions + const userData = await getRepository(User).findOneBy({ + id: highlight.userId, + }) + if (!userData) return null - return await ctx.models.reaction.batchGetFromHighlight(id) + return userDataToUser(userData) }, + // async reactions( + // highlight: { id: string; reactions?: Reaction[] }, + // _: unknown, + // ctx: WithDataSourcesContext + // ) { + // const { reactions, id } = highlight + // if (reactions) return reactions + + // return await ctx.models.reaction.batchGetFromHighlight(id) + // }, async createdByMe( highlight: { userId: string; createdByMe?: boolean }, __: unknown, @@ -551,15 +555,15 @@ export const functionResolvers = { return highlight.type || HighlightType.Highlight }, }, - Reaction: { - async user( - reaction: { userId: string }, - __: unknown, - ctx: WithDataSourcesContext - ) { - return userDataToUser(await ctx.models.user.get(reaction.userId)) - }, - }, + // Reaction: { + // async user( + // reaction: { userId: string }, + // __: unknown, + // ctx: WithDataSourcesContext + // ) { + // return userDataToUser(await ctx.models.user.get(reaction.userId)) + // }, + // }, SearchItem: { async url(item: SearchItem, _: unknown, ctx: WithDataSourcesContext) { if ( @@ -567,7 +571,9 @@ export const functionResolvers = { ctx.claims && item.uploadFileId ) { - const upload = await ctx.models.uploadFile.get(item.uploadFileId) + const upload = await getRepository(UploadFile).findOneBy({ + id: item.uploadFileId, + }) if (!upload || !upload.fileName) { return undefined } diff --git a/packages/api/src/resolvers/importers/uploadImportFileResolver.ts b/packages/api/src/resolvers/importers/uploadImportFileResolver.ts index 122ef69ee..175942813 100644 --- a/packages/api/src/resolvers/importers/uploadImportFileResolver.ts +++ b/packages/api/src/resolvers/importers/uploadImportFileResolver.ts @@ -1,7 +1,7 @@ import { DateTime } from 'luxon' import { v4 as uuidv4 } from 'uuid' +import { getRepository } from '../../entity' import { User } from '../../entity/user' -import { getRepository } from '../../entity/utils' import { env } from '../../env' import { MutationUploadImportFileArgs, diff --git a/packages/api/src/resolvers/integrations/index.ts b/packages/api/src/resolvers/integrations/index.ts index 35f697d39..1a480f020 100644 --- a/packages/api/src/resolvers/integrations/index.ts +++ b/packages/api/src/resolvers/integrations/index.ts @@ -1,6 +1,6 @@ +import { getRepository } from '../../entity' import { Integration, IntegrationType } from '../../entity/integration' import { User } from '../../entity/user' -import { getRepository } from '../../entity/utils' import { env } from '../../env' import { DeleteIntegrationError, diff --git a/packages/api/src/resolvers/labels/index.ts b/packages/api/src/resolvers/labels/index.ts index b58a1128e..530793914 100644 --- a/packages/api/src/resolvers/labels/index.ts +++ b/packages/api/src/resolvers/labels/index.ts @@ -8,9 +8,9 @@ import { updateLabelsInPage, } from '../../elastic/labels' import { getPageById } from '../../elastic/pages' +import { getRepository, setClaims } from '../../entity' import { Label } from '../../entity/label' import { User } from '../../entity/user' -import { getRepository, setClaims } from '../../entity/utils' import { env } from '../../env' import { CreateLabelError, diff --git a/packages/api/src/resolvers/links/index.ts b/packages/api/src/resolvers/links/index.ts index f0105554a..da3afa3bd 100644 --- a/packages/api/src/resolvers/links/index.ts +++ b/packages/api/src/resolvers/links/index.ts @@ -1,4 +1,3 @@ -import { createOrUpdateLinkShareInfo } from '../../datalayer/links/share_info' import { updatePage } from '../../elastic/pages' import { env } from '../../env' import { @@ -6,52 +5,48 @@ import { ArchiveLinkErrorCode, ArchiveLinkSuccess, MutationSetLinkArchivedArgs, - MutationUpdateLinkShareInfoArgs, - UpdateLinkShareInfoError, - UpdateLinkShareInfoErrorCode, - UpdateLinkShareInfoSuccess, } from '../../generated/graphql' import { analytics } from '../../utils/analytics' import { authorized } from '../../utils/helpers' -export const updateLinkShareInfoResolver = authorized< - UpdateLinkShareInfoSuccess, - UpdateLinkShareInfoError, - MutationUpdateLinkShareInfoArgs ->(async (_obj, args, { models, claims, authTrx, log }) => { - const { title, description } = args.input +// export const updateLinkShareInfoResolver = authorized< +// UpdateLinkShareInfoSuccess, +// UpdateLinkShareInfoError, +// MutationUpdateLinkShareInfoArgs +// >(async (_obj, args, { models, claims, authTrx, log }) => { +// const { title, description } = args.input - log.info('updateLinkShareInfoResolver', args.input.linkId, title, description) +// log.info('updateLinkShareInfoResolver', args.input.linkId, title, description) - // TEMP: because the old API uses articles instead of Links, we are actually - // getting an article ID here and need to map it to a link ID. When the API - // is updated to use Links instead of Articles this will be removed. - const link = await authTrx((tx) => - models.userArticle.getByArticleId(claims.uid, args.input.linkId, tx) - ) +// // TEMP: because the old API uses articles instead of Links, we are actually +// // getting an article ID here and need to map it to a link ID. When the API +// // is updated to use Links instead of Articles this will be removed. +// const link = await authTrx((tx) => +// models.userArticle.getByArticleId(claims.uid, args.input.linkId, tx) +// ) - if (!link?.id) { - return { - __typename: 'UpdateLinkShareInfoError', - errorCodes: [UpdateLinkShareInfoErrorCode.Unauthorized], - } - } +// if (!link?.id) { +// return { +// __typename: 'UpdateLinkShareInfoError', +// errorCodes: [UpdateLinkShareInfoErrorCode.Unauthorized], +// } +// } - const result = await authTrx((tx) => - createOrUpdateLinkShareInfo(tx, link.id, title, description) - ) - if (!result) { - return { - __typename: 'UpdateLinkShareInfoError', - errorCodes: [UpdateLinkShareInfoErrorCode.BadRequest], - } - } +// const result = await authTrx((tx) => +// createOrUpdateLinkShareInfo(tx, link.id, title, description) +// ) +// if (!result) { +// return { +// __typename: 'UpdateLinkShareInfoError', +// errorCodes: [UpdateLinkShareInfoErrorCode.BadRequest], +// } +// } - return { - __typename: 'UpdateLinkShareInfoSuccess', - message: 'Updated Share Information', - } -}) +// return { +// __typename: 'UpdateLinkShareInfoSuccess', +// message: 'Updated Share Information', +// } +// }) export const setLinkArchivedResolver = authorized< ArchiveLinkSuccess, diff --git a/packages/api/src/resolvers/newsletters/index.ts b/packages/api/src/resolvers/newsletters/index.ts index 58919c985..af7d72dd1 100644 --- a/packages/api/src/resolvers/newsletters/index.ts +++ b/packages/api/src/resolvers/newsletters/index.ts @@ -1,6 +1,6 @@ +import { getRepository } from '../../entity' import { NewsletterEmail } from '../../entity/newsletter_email' import { User } from '../../entity/user' -import { getRepository } from '../../entity/utils' import { env } from '../../env' import { CreateNewsletterEmailError, diff --git a/packages/api/src/resolvers/reaction/index.ts b/packages/api/src/resolvers/reaction/index.ts index 25687a55f..aec16962a 100644 --- a/packages/api/src/resolvers/reaction/index.ts +++ b/packages/api/src/resolvers/reaction/index.ts @@ -1,15 +1,5 @@ import { Merge } from '../../util' -import { authorized } from '../../utils/helpers' -import { - CreateReactionError, - CreateReactionErrorCode, - CreateReactionSuccess, - DeleteReactionError, - DeleteReactionErrorCode, - MutationCreateReactionArgs, - MutationDeleteReactionArgs, - Reaction, -} from './../../generated/graphql' +import { CreateReactionSuccess, Reaction } from './../../generated/graphql' export type PartialReaction = Omit @@ -18,125 +8,125 @@ export type PartialCreateReactionSuccess = Merge< { reaction: PartialReaction } > -export const createReactionResolver = authorized< - PartialCreateReactionSuccess, - CreateReactionError, - MutationCreateReactionArgs ->(async (_, { input }, { models, claims, log, authTrx }) => { - const { userArticleId, highlightId } = input +// export const createReactionResolver = authorized< +// PartialCreateReactionSuccess, +// CreateReactionError, +// MutationCreateReactionArgs +// >(async (_, { input }, { models, claims, log, authTrx }) => { +// const { userArticleId, highlightId } = input - if ((!userArticleId && !highlightId) || (userArticleId && highlightId)) { - // One reaction target is required - // Higlight replies hasn't supported yet - return { - errorCodes: [CreateReactionErrorCode.BadTarget], - } - } +// if ((!userArticleId && !highlightId) || (userArticleId && highlightId)) { +// // One reaction target is required +// // Higlight replies hasn't supported yet +// return { +// errorCodes: [CreateReactionErrorCode.BadTarget], +// } +// } - if (input.code && input.code.length > 50) { - return { - errorCodes: [CreateReactionErrorCode.BadCode], - } - } +// if (input.code && input.code.length > 50) { +// return { +// errorCodes: [CreateReactionErrorCode.BadCode], +// } +// } - if (userArticleId) { - if (!(await models.userArticle.get(userArticleId))) { - return { - errorCodes: [CreateReactionErrorCode.BadTarget], - } - } - } else if (highlightId) { - if (!(await models.highlight.get(highlightId))) { - return { - errorCodes: [CreateReactionErrorCode.BadTarget], - } - } - } +// if (userArticleId) { +// if (!(await models.userArticle.get(userArticleId))) { +// return { +// errorCodes: [CreateReactionErrorCode.BadTarget], +// } +// } +// } else if (highlightId) { +// if (!(await models.highlight.get(highlightId))) { +// return { +// errorCodes: [CreateReactionErrorCode.BadTarget], +// } +// } +// } - try { - const previousReaction = userArticleId - ? await models.reaction.getByUserAndParam(claims.uid, { userArticleId }) - : await models.reaction.getByUserAndParam(claims.uid, { highlightId }) +// try { +// const previousReaction = userArticleId +// ? await models.reaction.getByUserAndParam(claims.uid, { userArticleId }) +// : await models.reaction.getByUserAndParam(claims.uid, { highlightId }) - let reaction - if (!previousReaction) { - reaction = await authTrx((tx) => - models.reaction.create({ ...input, userId: claims.uid }, tx) - ) - } else { - reaction = await authTrx((tx) => - models.reaction.update( - previousReaction.id, - { - code: input.code, - }, - tx - ) - ) - } +// let reaction +// if (!previousReaction) { +// reaction = await authTrx((tx) => +// models.reaction.create({ ...input, userId: claims.uid }, tx) +// ) +// } else { +// reaction = await authTrx((tx) => +// models.reaction.update( +// previousReaction.id, +// { +// code: input.code, +// }, +// tx +// ) +// ) +// } - if (!reaction) { - return { - errorCodes: [CreateReactionErrorCode.NotFound], - } - } - log.info(`${previousReaction ? 'Updating' : 'Creating'} a new reaction`, { - reaction, - labels: { - source: 'resolver', - resolver: 'createReactionResolver', - uid: claims.uid, - }, - }) +// if (!reaction) { +// return { +// errorCodes: [CreateReactionErrorCode.NotFound], +// } +// } +// log.info(`${previousReaction ? 'Updating' : 'Creating'} a new reaction`, { +// reaction, +// labels: { +// source: 'resolver', +// resolver: 'createReactionResolver', +// uid: claims.uid, +// }, +// }) - return { - reaction: reaction as PartialReaction, - } - } catch (err) { - log.info(err) - return { - errorCodes: [CreateReactionErrorCode.NotFound], - } - } -}) +// return { +// reaction: reaction as PartialReaction, +// } +// } catch (err) { +// log.info(err) +// return { +// errorCodes: [CreateReactionErrorCode.NotFound], +// } +// } +// }) -export const deleteReactionResolver = authorized< - PartialCreateReactionSuccess, - DeleteReactionError, - MutationDeleteReactionArgs ->(async (_, { id }, { authTrx, models, claims, log }) => { - const reaction = await models.reaction.get(id) +// export const deleteReactionResolver = authorized< +// PartialCreateReactionSuccess, +// DeleteReactionError, +// MutationDeleteReactionArgs +// >(async (_, { id }, { authTrx, models, claims, log }) => { +// const reaction = await models.reaction.get(id) - if (!reaction?.id) { - return { - errorCodes: [DeleteReactionErrorCode.NotFound], - } - } +// if (!reaction?.id) { +// return { +// errorCodes: [DeleteReactionErrorCode.NotFound], +// } +// } - if (reaction.userId !== claims.uid) { - return { - errorCodes: [DeleteReactionErrorCode.Forbidden], - } - } +// if (reaction.userId !== claims.uid) { +// return { +// errorCodes: [DeleteReactionErrorCode.Forbidden], +// } +// } - const deleted = await authTrx((tx) => models.reaction.delete(id, tx)) +// const deleted = await authTrx((tx) => models.reaction.delete(id, tx)) - if ('error' in deleted) { - return { - errorCodes: [DeleteReactionErrorCode.NotFound], - } - } +// if ('error' in deleted) { +// return { +// errorCodes: [DeleteReactionErrorCode.NotFound], +// } +// } - log.info('Deleting a highlight', { - deleted, - labels: { - source: 'resolver', - resolver: 'deleteHighlightResolver', - uid: claims.uid, - }, - }) +// log.info('Deleting a highlight', { +// deleted, +// labels: { +// source: 'resolver', +// resolver: 'deleteHighlightResolver', +// uid: claims.uid, +// }, +// }) - return { - reaction: reaction as PartialReaction, - } -}) +// return { +// reaction: reaction as PartialReaction, +// } +// }) diff --git a/packages/api/src/resolvers/recent_emails/index.ts b/packages/api/src/resolvers/recent_emails/index.ts index ef4955ec0..0c0266a1e 100644 --- a/packages/api/src/resolvers/recent_emails/index.ts +++ b/packages/api/src/resolvers/recent_emails/index.ts @@ -1,7 +1,7 @@ import { ILike } from 'typeorm' +import { getRepository } from '../../entity' import { NewsletterEmail } from '../../entity/newsletter_email' import { ReceivedEmail } from '../../entity/received_email' -import { getRepository } from '../../entity/utils' import { env } from '../../env' import { MarkEmailAsItemError, diff --git a/packages/api/src/resolvers/recent_searches/index.ts b/packages/api/src/resolvers/recent_searches/index.ts index d8efadf9e..5f109e5b6 100644 --- a/packages/api/src/resolvers/recent_searches/index.ts +++ b/packages/api/src/resolvers/recent_searches/index.ts @@ -1,14 +1,14 @@ -import { authorized } from '../../utils/helpers' +import { getRepository } from '../../entity' +import { User } from '../../entity/user' +import { env } from '../../env' import { RecentSearchesError, RecentSearchesErrorCode, RecentSearchesSuccess, } from '../../generated/graphql' -import { analytics } from '../../utils/analytics' -import { env } from '../../env' -import { getRepository } from '../../entity/utils' -import { User } from '../../entity/user' import { getRecentSearches } from '../../services/search_history' +import { analytics } from '../../utils/analytics' +import { authorized } from '../../utils/helpers' export const recentSearchesResolver = authorized< RecentSearchesSuccess, diff --git a/packages/api/src/resolvers/recommendations/index.ts b/packages/api/src/resolvers/recommendations/index.ts index 68765d194..09d43eb61 100644 --- a/packages/api/src/resolvers/recommendations/index.ts +++ b/packages/api/src/resolvers/recommendations/index.ts @@ -1,8 +1,8 @@ import { In } from 'typeorm' import { getPageByParam } from '../../elastic/pages' +import { getRepository } from '../../entity' import { Group } from '../../entity/groups/group' import { User } from '../../entity/user' -import { getRepository } from '../../entity/utils' import { env } from '../../env' import { CreateGroupError, diff --git a/packages/api/src/resolvers/reminders/index.ts b/packages/api/src/resolvers/reminders/index.ts index 9b33134b6..4f311e6e8 100644 --- a/packages/api/src/resolvers/reminders/index.ts +++ b/packages/api/src/resolvers/reminders/index.ts @@ -1,31 +1,4 @@ import { DateTime } from 'luxon' -import { getPageById } from '../../elastic/pages' -import { Page } from '../../elastic/types' -import { env } from '../../env' -import { - CreateReminderError, - CreateReminderErrorCode, - CreateReminderSuccess, - DeleteReminderError, - DeleteReminderErrorCode, - DeleteReminderSuccess, - MutationCreateReminderArgs, - MutationDeleteReminderArgs, - MutationUpdateReminderArgs, - QueryReminderArgs, - ReminderError, - ReminderErrorCode, - ReminderSuccess, - UpdateReminderError, - UpdateReminderErrorCode, - UpdateReminderSuccess, -} from '../../generated/graphql' -import { setLinkArchived } from '../../services/archive_link' -import { analytics } from '../../utils/analytics' -import { deleteTask, enqueueReminder } from '../../utils/createTask' -import { authorized } from '../../utils/helpers' -import { logger } from '../../utils/logger' -import { DataModels } from '../types' const validScheduleTime = (str: string): Date | undefined => { const scheduleTime = DateTime.fromISO(str, { setZone: true }).set({ @@ -39,320 +12,320 @@ const validScheduleTime = (str: string): Date | undefined => { return scheduleTime.toJSDate() } -export const createReminderResolver = authorized< - CreateReminderSuccess, - CreateReminderError, - MutationCreateReminderArgs ->(async (_, { input }, { models, claims: { uid }, log }) => { - log.info('createReminderResolver') +// export const createReminderResolver = authorized< +// CreateReminderSuccess, +// CreateReminderError, +// MutationCreateReminderArgs +// >(async (_, { input }, { models, claims: { uid }, log }) => { +// log.info('createReminderResolver') - const { clientRequestId, linkId, archiveUntil, sendNotification } = input +// const { clientRequestId, linkId, archiveUntil, sendNotification } = input - const scheduledTime = validScheduleTime(input.remindAt) - if (!scheduledTime) { - log.error('Invalid scheduled time', input.remindAt) - return { - errorCodes: [CreateReminderErrorCode.BadRequest], - } - } +// const scheduledTime = validScheduleTime(input.remindAt) +// if (!scheduledTime) { +// log.error('Invalid scheduled time', input.remindAt) +// return { +// errorCodes: [CreateReminderErrorCode.BadRequest], +// } +// } - const pageId = linkId || clientRequestId - if (!pageId) { - log.error('client request id or link id is required') - return { - errorCodes: [CreateReminderErrorCode.BadRequest], - } - } +// const pageId = linkId || clientRequestId +// if (!pageId) { +// log.error('client request id or link id is required') +// return { +// errorCodes: [CreateReminderErrorCode.BadRequest], +// } +// } - analytics.track({ - userId: uid, - event: 'reminder_created', - properties: { - clientRequestId, - remindAt: scheduledTime, - archiveUntil, - sendNotification, - linkId, - env: env.server.apiEnv, - }, - }) +// analytics.track({ +// userId: uid, +// event: 'reminder_created', +// properties: { +// clientRequestId, +// remindAt: scheduledTime, +// archiveUntil, +// sendNotification, +// linkId, +// env: env.server.apiEnv, +// }, +// }) - try { - // saving from web - const page = await getPageById(pageId) - if (!page) { - log.error('page not found', pageId) +// try { +// // saving from web +// const page = await getPageById(pageId) +// if (!page) { +// log.error('page not found', pageId) - return { - errorCodes: [CreateReminderErrorCode.NotFound], - } - } +// return { +// errorCodes: [CreateReminderErrorCode.NotFound], +// } +// } - if (page.userId !== uid) { - log.error('user not authorized', uid) +// if (page.userId !== uid) { +// log.error('user not authorized', uid) - return { - errorCodes: [CreateReminderErrorCode.Unauthorized], - } - } - if (archiveUntil) { - await archivePage(uid, page) - } +// return { +// errorCodes: [CreateReminderErrorCode.Unauthorized], +// } +// } +// if (archiveUntil) { +// await archivePage(uid, page) +// } - const taskName = await groupReminders(scheduledTime, uid, models) - log.info('scheduled task name', taskName) +// const taskName = await groupReminders(scheduledTime, uid, models) +// log.info('scheduled task name', taskName) - // insert reminder to db - const reminder = await models.reminder.create({ - userId: uid, - taskName: taskName, - archiveUntil: archiveUntil, - sendNotification: sendNotification, - createdAt: new Date(), - remindAt: scheduledTime, - elasticPageId: pageId, - }) - log.info('created reminder', reminder) +// // insert reminder to db +// const reminder = await models.reminder.create({ +// userId: uid, +// taskName: taskName, +// archiveUntil: archiveUntil, +// sendNotification: sendNotification, +// createdAt: new Date(), +// remindAt: scheduledTime, +// elasticPageId: pageId, +// }) +// log.info('created reminder', reminder) - return { - reminder: { - id: reminder.id, - archiveUntil, - sendNotification, - remindAt: scheduledTime, - }, - } - } catch (e) { - log.info('error creating reminder', e) +// return { +// reminder: { +// id: reminder.id, +// archiveUntil, +// sendNotification, +// remindAt: scheduledTime, +// }, +// } +// } catch (e) { +// log.info('error creating reminder', e) - return { - errorCodes: [CreateReminderErrorCode.BadRequest], - } - } -}) +// return { +// errorCodes: [CreateReminderErrorCode.BadRequest], +// } +// } +// }) -// Attempts to find a link and archive it if it exists. -// It is possible that the link has not been created -// yet if it is still in the saving process. In that -// case it will be archived when the link is created. -const archivePage = async (uid: string, page: Page) => { - try { - await setLinkArchived(uid, page.id, true) - } catch (e) { - logger.info('error archiving link', e) - } -} +// // Attempts to find a link and archive it if it exists. +// // It is possible that the link has not been created +// // yet if it is still in the saving process. In that +// // case it will be archived when the link is created. +// const archivePage = async (uid: string, page: Page) => { +// try { +// await setLinkArchived(uid, page.id, true) +// } catch (e) { +// logger.info('error archiving link', e) +// } +// } -export const reminderResolver = authorized< - ReminderSuccess, - ReminderError, - QueryReminderArgs ->(async (_, { linkId: pageId }, { models, claims: { uid }, log }) => { - log.info('reminderResolver') +// export const reminderResolver = authorized< +// ReminderSuccess, +// ReminderError, +// QueryReminderArgs +// >(async (_, { linkId: pageId }, { models, claims: { uid }, log }) => { +// log.info('reminderResolver') - analytics.track({ - userId: uid, - event: 'reminder', - properties: { - linkId: pageId, - env: env.server.apiEnv, - }, - }) +// analytics.track({ +// userId: uid, +// event: 'reminder', +// properties: { +// linkId: pageId, +// env: env.server.apiEnv, +// }, +// }) - try { - // get page from articleId - const page = await getPageById(pageId) - if (!page) { - return { - errorCodes: [ReminderErrorCode.NotFound], - } - } - if (page.userId !== uid) { - return { - errorCodes: [ReminderErrorCode.Unauthorized], - } - } - const reminder = await models.reminder.getCreatedByParameters(uid, { - elasticPageId: page.id, - }) +// try { +// // get page from articleId +// const page = await getPageById(pageId) +// if (!page) { +// return { +// errorCodes: [ReminderErrorCode.NotFound], +// } +// } +// if (page.userId !== uid) { +// return { +// errorCodes: [ReminderErrorCode.Unauthorized], +// } +// } +// const reminder = await models.reminder.getCreatedByParameters(uid, { +// elasticPageId: page.id, +// }) - if (!reminder) { - log.error('reminder not found: pageId: ', pageId) +// if (!reminder) { +// log.error('reminder not found: pageId: ', pageId) - return { - errorCodes: [ReminderErrorCode.NotFound], - } - } +// return { +// errorCodes: [ReminderErrorCode.NotFound], +// } +// } - return { - reminder: { - id: reminder.id, - archiveUntil: reminder.archiveUntil || false, - sendNotification: reminder.sendNotification || true, - remindAt: reminder.remindAt, - }, - } - } catch (e) { - log.error(e) +// return { +// reminder: { +// id: reminder.id, +// archiveUntil: reminder.archiveUntil || false, +// sendNotification: reminder.sendNotification || true, +// remindAt: reminder.remindAt, +// }, +// } +// } catch (e) { +// log.error(e) - return { - errorCodes: [ReminderErrorCode.BadRequest], - } - } -}) +// return { +// errorCodes: [ReminderErrorCode.BadRequest], +// } +// } +// }) -export const updateReminderResolver = authorized< - UpdateReminderSuccess, - UpdateReminderError, - MutationUpdateReminderArgs ->(async (_, { input }, { models, claims: { uid }, log, authTrx }) => { - log.info('updateReminderResolver') +// export const updateReminderResolver = authorized< +// UpdateReminderSuccess, +// UpdateReminderError, +// MutationUpdateReminderArgs +// >(async (_, { input }, { models, claims: { uid }, log, authTrx }) => { +// log.info('updateReminderResolver') - const { id, archiveUntil, sendNotification } = input +// const { id, archiveUntil, sendNotification } = input - const scheduledTime = validScheduleTime(input.remindAt) - if (!scheduledTime) { - log.error('Invalid scheduled time', input.remindAt) - return { - errorCodes: [UpdateReminderErrorCode.BadRequest], - } - } +// const scheduledTime = validScheduleTime(input.remindAt) +// if (!scheduledTime) { +// log.error('Invalid scheduled time', input.remindAt) +// return { +// errorCodes: [UpdateReminderErrorCode.BadRequest], +// } +// } - analytics.track({ - userId: uid, - event: 'reminder_updated', - properties: { - id, - remindAt: scheduledTime, - archiveUntil, - sendNotification, - env: env.server.apiEnv, - }, - }) +// analytics.track({ +// userId: uid, +// event: 'reminder_updated', +// properties: { +// id, +// remindAt: scheduledTime, +// archiveUntil, +// sendNotification, +// env: env.server.apiEnv, +// }, +// }) - try { - const reminder = await models.reminder.getCreated(id) +// try { +// const reminder = await models.reminder.getCreated(id) - if (!reminder) { - log.error('reminder not found:', id) +// if (!reminder) { +// log.error('reminder not found:', id) - return { - errorCodes: [UpdateReminderErrorCode.NotFound], - } - } +// return { +// errorCodes: [UpdateReminderErrorCode.NotFound], +// } +// } - if (reminder.userId !== uid) { - return { - errorCodes: [UpdateReminderErrorCode.Unauthorized], - } - } +// if (reminder.userId !== uid) { +// return { +// errorCodes: [UpdateReminderErrorCode.Unauthorized], +// } +// } - // delete old google cloud task - if (reminder.taskName) { - await deleteTask(reminder.taskName) - } +// // delete old google cloud task +// if (reminder.taskName) { +// await deleteTask(reminder.taskName) +// } - const taskName = await groupReminders(scheduledTime, uid, models) +// const taskName = await groupReminders(scheduledTime, uid, models) - // update db - await authTrx((tx) => - models.reminder.update( - id, - { - taskName: taskName, - archiveUntil, - sendNotification, - remindAt: scheduledTime, - }, - tx - ) - ) +// // update db +// await authTrx((tx) => +// models.reminder.update( +// id, +// { +// taskName: taskName, +// archiveUntil, +// sendNotification, +// remindAt: scheduledTime, +// }, +// tx +// ) +// ) - return { - reminder: { - id: reminder.id, - archiveUntil, - sendNotification, - remindAt: scheduledTime, - }, - } - } catch (e) { - log.error(e) +// return { +// reminder: { +// id: reminder.id, +// archiveUntil, +// sendNotification, +// remindAt: scheduledTime, +// }, +// } +// } catch (e) { +// log.error(e) - return { - errorCodes: [UpdateReminderErrorCode.BadRequest], - } - } -}) +// return { +// errorCodes: [UpdateReminderErrorCode.BadRequest], +// } +// } +// }) -export const deleteReminderResolver = authorized< - DeleteReminderSuccess, - DeleteReminderError, - MutationDeleteReminderArgs ->(async (_, { id }, { models, claims: { uid }, log, authTrx }) => { - log.info('deleteReminderResolver') +// export const deleteReminderResolver = authorized< +// DeleteReminderSuccess, +// DeleteReminderError, +// MutationDeleteReminderArgs +// >(async (_, { id }, { models, claims: { uid }, log, authTrx }) => { +// log.info('deleteReminderResolver') - analytics.track({ - userId: uid, - event: 'reminder_deleted', - properties: { - id: id, - env: env.server.apiEnv, - }, - }) +// analytics.track({ +// userId: uid, +// event: 'reminder_deleted', +// properties: { +// id: id, +// env: env.server.apiEnv, +// }, +// }) - try { - const reminder = await models.reminder.getCreated(id) +// try { +// const reminder = await models.reminder.getCreated(id) - if (!reminder) { - log.error('reminder not found:', id) +// if (!reminder) { +// log.error('reminder not found:', id) - return { - errorCodes: [DeleteReminderErrorCode.NotFound], - } - } +// return { +// errorCodes: [DeleteReminderErrorCode.NotFound], +// } +// } - if (reminder.userId !== uid) { - return { - errorCodes: [DeleteReminderErrorCode.Unauthorized], - } - } +// if (reminder.userId !== uid) { +// return { +// errorCodes: [DeleteReminderErrorCode.Unauthorized], +// } +// } - // update db - await authTrx((tx) => models.reminder.delete(id, tx)) +// // update db +// await authTrx((tx) => models.reminder.delete(id, tx)) - return { - reminder: { - id: reminder.id, - archiveUntil: reminder.archiveUntil || false, - sendNotification: reminder.sendNotification || true, - remindAt: reminder.remindAt, - }, - } - } catch (e) { - log.error(e) +// return { +// reminder: { +// id: reminder.id, +// archiveUntil: reminder.archiveUntil || false, +// sendNotification: reminder.sendNotification || true, +// remindAt: reminder.remindAt, +// }, +// } +// } catch (e) { +// log.error(e) - return { - errorCodes: [DeleteReminderErrorCode.BadRequest], - } - } -}) +// return { +// errorCodes: [DeleteReminderErrorCode.BadRequest], +// } +// } +// }) -// check if there exists reminders for the same user at the same time -// create a Google cloud task if no existing task and return task name -const groupReminders = async ( - scheduledTime: Date, - userId: string, - models: DataModels -): Promise => { - const exists = await models.reminder.existByUserAndRemindAt( - userId, - scheduledTime - ) +// // check if there exists reminders for the same user at the same time +// // create a Google cloud task if no existing task and return task name +// const groupReminders = async ( +// scheduledTime: Date, +// userId: string, +// models: DataModels +// ): Promise => { +// const exists = await models.reminder.existByUserAndRemindAt( +// userId, +// scheduledTime +// ) - if (!exists) { - return enqueueReminder(userId, scheduledTime.getTime()) - } +// if (!exists) { +// return enqueueReminder(userId, scheduledTime.getTime()) +// } - return undefined -} +// return undefined +// } diff --git a/packages/api/src/resolvers/rules/index.ts b/packages/api/src/resolvers/rules/index.ts index 456f127a0..b26e4b569 100644 --- a/packages/api/src/resolvers/rules/index.ts +++ b/packages/api/src/resolvers/rules/index.ts @@ -1,4 +1,6 @@ -import { authorized } from '../../utils/helpers' +import { getRepository } from '../../entity' +import { Rule } from '../../entity/rule' +import { User } from '../../entity/user' import { DeleteRuleError, DeleteRuleErrorCode, @@ -13,9 +15,7 @@ import { SetRuleErrorCode, SetRuleSuccess, } from '../../generated/graphql' -import { getRepository } from '../../entity/utils' -import { User } from '../../entity/user' -import { Rule } from '../../entity/rule' +import { authorized } from '../../utils/helpers' export const setRuleResolver = authorized< SetRuleSuccess, diff --git a/packages/api/src/resolvers/save/index.ts b/packages/api/src/resolvers/save/index.ts index 27e7ace38..3c737d493 100644 --- a/packages/api/src/resolvers/save/index.ts +++ b/packages/api/src/resolvers/save/index.ts @@ -1,5 +1,5 @@ +import { getRepository } from '../../entity' import { User } from '../../entity/user' -import { getRepository } from '../../entity/utils' import { env } from '../../env' import { MutationSaveFileArgs, @@ -13,19 +13,15 @@ import { saveFile } from '../../services/save_file' import { savePage } from '../../services/save_page' import { saveUrl } from '../../services/save_url' import { analytics } from '../../utils/analytics' -import { authorized, userDataToUser } from '../../utils/helpers' +import { authorized } from '../../utils/helpers' export const savePageResolver = authorized< SaveSuccess, SaveError, MutationSavePageArgs >(async (_, { input }, ctx) => { - const { - models, - claims: { uid }, - } = ctx analytics.track({ - userId: uid, + userId: ctx.uid, event: 'link_saved', properties: { url: input.url, @@ -35,16 +31,14 @@ export const savePageResolver = authorized< }, }) - const user = userDataToUser(await models.user.get(uid)) + const user = await getRepository(User).findOneBy({ + id: ctx.uid, + }) if (!user) { return { errorCodes: [SaveErrorCode.Unauthorized] } } - return savePage( - { ...ctx, uid, refresh: true }, - { userId: user.id, username: user.profile.username }, - input - ) + return savePage(ctx, user, input) }) export const saveUrlResolver = authorized< @@ -74,7 +68,7 @@ export const saveUrlResolver = authorized< return { errorCodes: [SaveErrorCode.Unauthorized] } } - return (await saveUrl({ ...ctx, uid }, user, input)) as SaveSuccess + return saveUrl(ctx, user, input) }) export const saveFileResolver = authorized< @@ -82,13 +76,8 @@ export const saveFileResolver = authorized< SaveError, MutationSaveFileArgs >(async (_, { input }, ctx) => { - const { - models, - claims: { uid }, - } = ctx - analytics.track({ - userId: uid, + userId: ctx.uid, event: 'link_saved', properties: { url: input.url, @@ -98,10 +87,12 @@ export const saveFileResolver = authorized< }, }) - const user = userDataToUser(await models.user.get(uid)) + const user = await getRepository(User).findOneBy({ + id: ctx.uid, + }) if (!user) { return { errorCodes: [SaveErrorCode.Unauthorized] } } - return (await saveFile({ ...ctx, uid }, user, input)) as SaveSuccess + return saveFile(ctx, user, input) }) diff --git a/packages/api/src/resolvers/subscriptions/index.ts b/packages/api/src/resolvers/subscriptions/index.ts index 4665fdd4d..9fe846258 100644 --- a/packages/api/src/resolvers/subscriptions/index.ts +++ b/packages/api/src/resolvers/subscriptions/index.ts @@ -1,8 +1,8 @@ import Parser from 'rss-parser' import { Brackets } from 'typeorm' +import { getRepository } from '../../entity' import { Subscription } from '../../entity/subscription' import { User } from '../../entity/user' -import { getRepository } from '../../entity/utils' import { env } from '../../env' import { MutationSubscribeArgs, diff --git a/packages/api/src/resolvers/types.ts b/packages/api/src/resolvers/types.ts index 2a26a3ff4..17cf31258 100644 --- a/packages/api/src/resolvers/types.ts +++ b/packages/api/src/resolvers/types.ts @@ -1,20 +1,10 @@ /* eslint-disable @typescript-eslint/ban-types */ -import { Context as ApolloContext } from 'apollo-server-core' -import winston from 'winston' -import { Knex } from 'knex' -import UserModel from '../datalayer/user' -import ArticleModel from '../datalayer/article' -import UserArticleModel from '../datalayer/links' -import UserFriendModel from '../datalayer/user_friends' -import UserPersonalizationModel from '../datalayer/user_personalization' -import ArticleSavingRequestModel from '../datalayer/article_saving_request' -import UploadFileDataModel from '../datalayer/upload_files' -import * as jwt from 'jsonwebtoken' import { Span } from '@opentelemetry/api' -import HighlightModel from '../datalayer/highlight' -import ReactionModel from '../datalayer/reaction' +import { Context as ApolloContext } from 'apollo-server-core' +import * as jwt from 'jsonwebtoken' +import { EntityManager } from 'typeorm' +import winston from 'winston' import { PubsubClient } from '../datalayer/pubsub' -import ReminderModel from '../datalayer/reminders' export interface Claims { uid: string @@ -30,35 +20,19 @@ export type ClaimsToSet = { userRole?: string | null } -export type DataModels = { - user: UserModel - article: ArticleModel - userArticle: UserArticleModel - userFriends: UserFriendModel - userPersonalization: UserPersonalizationModel - articleSavingRequest: ArticleSavingRequestModel - uploadFile: UploadFileDataModel - highlight: HighlightModel - reaction: ReactionModel - reminder: ReminderModel -} - export interface RequestContext { log: winston.Logger claims: Claims | undefined - kx: Knex pubsub: PubsubClient - models: DataModels setAuth: (claims: ClaimsToSet, secret?: string) => Promise clearAuth: () => void - setClaims: (tx: Knex.Transaction, uuid?: string | undefined) => Promise - // eslint-disable-next-line @typescript-eslint/ban-types + setClaims: (em: EntityManager, uuid?: string | undefined) => Promise signToken: ( arg1: string | object | Buffer, arg2: jwt.Secret ) => Promise authTrx: ( - cb: (tx: Knex.Transaction) => TResult, + cb: (em: EntityManager) => TResult, userRole?: string ) => Promise tracingSpan: Span diff --git a/packages/api/src/resolvers/upload_files/index.ts b/packages/api/src/resolvers/upload_files/index.ts index b529d2412..2fcd49980 100644 --- a/packages/api/src/resolvers/upload_files/index.ts +++ b/packages/api/src/resolvers/upload_files/index.ts @@ -3,24 +3,25 @@ import normalizeUrl from 'normalize-url' import path from 'path' import { createPage, getPageByParam, updatePage } from '../../elastic/pages' import { PageType } from '../../elastic/types' +import { uploadFileRepository } from '../../entity' +import { UploadFile } from '../../entity/upload_file' import { env } from '../../env' import { ArticleSavingRequestStatus, MutationUploadFileRequestArgs, - ResolverFn, + UploadFileRequestError, UploadFileRequestErrorCode, - UploadFileRequestResult, + UploadFileRequestSuccess, UploadFileStatus, } from '../../generated/graphql' import { validateUrl } from '../../services/create_page_save_request' import { analytics } from '../../utils/analytics' -import { generateSlug } from '../../utils/helpers' +import { authorized, generateSlug } from '../../utils/helpers' import { generateUploadFilePathName, generateUploadSignedUrl, getFilePublicUrl, } from '../../utils/uploads' -import { WithDataSourcesContext } from '../types' const isFileUrl = (url: string): boolean => { const parsedUrl = new URL(url) @@ -34,23 +35,18 @@ export const pageTypeForContentType = (contentType: string): PageType => { return PageType.File } -export const uploadFileRequestResolver: ResolverFn< - UploadFileRequestResult, - unknown, - WithDataSourcesContext, +export const uploadFileRequestResolver = authorized< + UploadFileRequestSuccess, + UploadFileRequestError, MutationUploadFileRequestArgs -> = async (_obj, { input }, ctx) => { - const { models, authTrx, claims, log } = ctx +>(async (_, { input }, ctx) => { + const { authTrx, uid, log } = ctx let uploadFileData: { id: string | null } = { id: null, } - if (!claims?.uid) { - return { errorCodes: [UploadFileRequestErrorCode.Unauthorized] } - } - analytics.track({ - userId: claims.uid, + userId: uid, event: 'file_upload_request', properties: { url: input.url, @@ -89,10 +85,10 @@ export const uploadFileRequestResolver: ResolverFn< return { errorCodes: [UploadFileRequestErrorCode.BadInput] } } - uploadFileData = await models.uploadFile.create({ + uploadFileData = await uploadFileRepository.save({ url: input.url, - userId: claims.uid, - fileName: fileName, + userId: uid, + fileName, status: UploadFileStatus.Initialized, contentType: input.contentType, }) @@ -113,14 +109,10 @@ export const uploadFileRequestResolver: ResolverFn< // If this is a file URL, we swap in the GCS public URL if (isFileUrl(input.url)) { await authTrx(async (tx) => { - await models.uploadFile.update( - uploadFileId, - { - url: publicUrl, - status: UploadFileStatus.Initialized, - }, - tx - ) + await tx.getRepository(UploadFile).update(uploadFileId, { + url: publicUrl, + status: UploadFileStatus.Initialized, + }) }) } @@ -131,7 +123,7 @@ export const uploadFileRequestResolver: ResolverFn< // new item. const page = isFileUrl(input.url) ? await getPageByParam({ - userId: claims.uid, + userId: uid, url: input.url, }) : undefined @@ -155,7 +147,7 @@ export const uploadFileRequestResolver: ResolverFn< { url: isFileUrl(input.url) ? publicUrl : input.url, id: input.clientRequestId || '', - userId: claims.uid, + userId: uid, title: title, hash: uploadFilePathName, content: '', @@ -185,4 +177,4 @@ export const uploadFileRequestResolver: ResolverFn< } else { return { errorCodes: [UploadFileRequestErrorCode.FailedCreate] } } -} +}) diff --git a/packages/api/src/resolvers/user/index.ts b/packages/api/src/resolvers/user/index.ts index 62b24a794..2591a6269 100644 --- a/packages/api/src/resolvers/user/index.ts +++ b/packages/api/src/resolvers/user/index.ts @@ -1,8 +1,8 @@ import * as jwt from 'jsonwebtoken' import { RegistrationType } from '../../datalayer/user/model' import { deletePagesByParam } from '../../elastic/pages' +import { setClaims, userRepository } from '../../entity' import { User as UserEntity } from '../../entity/user' -import { getRepository, setClaims } from '../../entity/utils' import { env } from '../../env' import { DeleteAccountError, @@ -39,7 +39,7 @@ import { UsersSuccess, } from '../../generated/graphql' import { AppDataSource } from '../../server' -import { createUser } from '../../services/create_user' +import { createUser, getTopUsers } from '../../services/create_user' import { sendVerificationEmail } from '../../services/send_emails' import { authorized, userDataToUser } from '../../utils/helpers' import { validateUsername } from '../../utils/usernamePolicy' @@ -49,8 +49,10 @@ export const updateUserResolver = authorized< UpdateUserSuccess, UpdateUserError, MutationUpdateUserArgs ->(async (_, { input: { name, bio } }, { models, authTrx, claims }) => { - const user = await models.user.get(claims.uid) +>(async (_, { input: { name, bio } }, { uid }) => { + const user = await userRepository.findOneBy({ + id: uid, + }) if (!user) { return { errorCodes: [UpdateUserErrorCode.UserNotFound] } } @@ -69,16 +71,14 @@ export const updateUserResolver = authorized< return { errorCodes } } - const updatedUser = await authTrx(async (tx) => { - const [updatedRecord, profile] = await Promise.all([ - models.user.update( - claims.uid, - { name, source: user.source, sourceUserId: user.sourceUserId }, - tx - ), - models.user.updateProfile(claims.uid, { bio }, tx), - ]) - return { ...updatedRecord, profile } + const updatedUser = await userRepository.save({ + id: uid, + name, + source: user.source, + sourceUserId: user.sourceUserId, + profile: { + bio, + }, }) return { user: userDataToUser(updatedUser) } @@ -88,69 +88,63 @@ export const updateUserProfileResolver = authorized< UpdateUserProfileSuccess, UpdateUserProfileError, MutationUpdateUserProfileArgs ->( - async ( - _, - { input: { userId, username, pictureUrl } }, - { models, authTrx, claims } - ) => { - const user = await models.user.get(userId) - if (user.id !== claims.uid) { - return { - errorCodes: [UpdateUserProfileErrorCode.Forbidden], - } - } - - if (!(username || pictureUrl)) { - return { - errorCodes: [UpdateUserProfileErrorCode.BadData], - } - } - - const lowerCasedUsername = username?.toLowerCase() - if (lowerCasedUsername) { - const existingUser = await models.user.getWhere({ - username: lowerCasedUsername, - }) - if (existingUser?.id) { - return { - errorCodes: [UpdateUserProfileErrorCode.UsernameExists], - } - } - - if (!validateUsername(lowerCasedUsername)) { - return { - errorCodes: [UpdateUserProfileErrorCode.BadUsername], - } - } - } - - const updatedProfile = await authTrx((tx) => - models.user.updateProfile( - userId, - { - username: lowerCasedUsername, - picture_url: pictureUrl, - }, - tx - ) - ) - - user.profile = { - ...updatedProfile, - picture_url: updatedProfile.pictureUrl, - } - - return { user: userDataToUser(user) } +>(async (_, { input: { userId, username, pictureUrl } }, { uid }) => { + const user = await userRepository.findOneBy({ + id: userId, + }) + if (!user) { + return { errorCodes: [UpdateUserProfileErrorCode.Unauthorized] } } -) + + if (user.id !== uid) { + return { + errorCodes: [UpdateUserProfileErrorCode.Forbidden], + } + } + + if (!(username || pictureUrl)) { + return { + errorCodes: [UpdateUserProfileErrorCode.BadData], + } + } + + const lowerCasedUsername = username?.toLowerCase() + if (lowerCasedUsername) { + const existingUser = await userRepository.findOneBy({ + profile: { + username: lowerCasedUsername, + }, + }) + if (existingUser?.id) { + return { + errorCodes: [UpdateUserProfileErrorCode.UsernameExists], + } + } + + if (!validateUsername(lowerCasedUsername)) { + return { + errorCodes: [UpdateUserProfileErrorCode.BadUsername], + } + } + } + + const updatedUser = await userRepository.save({ + id: uid, + profile: { + username: lowerCasedUsername, + pictureUrl, + }, + }) + + return { user: userDataToUser(updatedUser) } +}) export const googleLoginResolver: ResolverFn< LoginResult, unknown, WithDataSourcesContext, MutationGoogleLoginArgs -> = async (_obj, { input }, { models, setAuth }) => { +> = async (_obj, { input }, { setAuth }) => { const { email, secret } = input try { @@ -159,7 +153,7 @@ export const googleLoginResolver: ResolverFn< return { errorCodes: [LoginErrorCode.AuthFailed] } } - const user = await models.user.getWhere({ + const user = await userRepository.findOneBy({ email, }) if (!user?.id) { @@ -176,13 +170,18 @@ export const validateUsernameResolver: ResolverFn< Record, WithDataSourcesContext, QueryValidateUsernameArgs -> = async (_obj, { username }, { models }) => { +> = async (_obj, { username }) => { const lowerCasedUsername = username.toLowerCase() if (!validateUsername(lowerCasedUsername)) { return false } - return !(await models.user.exists({ username: lowerCasedUsername })) + const user = await userRepository.findOneBy({ + profile: { + username: lowerCasedUsername, + }, + }) + return !user } export const googleSignupResolver: ResolverFn< @@ -245,11 +244,20 @@ export const getMeUserResolver: ResolverFn< unknown, WithDataSourcesContext, unknown -> = async (_obj, __, { models, claims }) => { +> = async (_obj, __, { claims }) => { try { - return claims?.uid - ? userDataToUser(await models.user.get(claims.uid)) - : undefined + if (!claims?.uid) { + return undefined + } + + const user = await userRepository.findOneBy({ + id: claims.uid, + }) + if (!user) { + return undefined + } + + return userDataToUser(user) } catch (error) { return undefined } @@ -260,18 +268,20 @@ export const getUserResolver: ResolverFn< unknown, WithDataSourcesContext, QueryUserArgs -> = async (_obj, { userId: id, username }, { models, claims }) => { +> = async (_obj, { userId: id, username }, { uid }) => { if (!(id || username)) { return { errorCodes: [UserErrorCode.BadRequest] } } const userId = - id || (username && (await models.user.getWhere({ username }))?.id) + id || + (username && + (await userRepository.findOneBy({ profile: { username } }))?.id) if (!userId) { return { errorCodes: [UserErrorCode.UserNotFound] } } - const userRecord = await models.user.getUserDetails(claims?.uid, userId) + const userRecord = await userRepository.findOneBy({ id: userId }) if (!userRecord) { return { errorCodes: [UserErrorCode.UserNotFound] } } @@ -280,9 +290,8 @@ export const getUserResolver: ResolverFn< } export const getAllUsersResolver = authorized( - async (_obj, _params, { models, claims, authTrx }) => { - const users = - (await authTrx((tx) => models.user.getTopUsers(claims.uid, tx))) || [] + async (_obj, _params) => { + const users = await getTopUsers() const result = { users: users.map((userData) => userDataToUser(userData)) } return result } @@ -303,8 +312,10 @@ export const deleteAccountResolver = authorized< DeleteAccountSuccess, DeleteAccountError, MutationDeleteAccountArgs ->(async (_, { userID }, { models, claims, log, pubsub }) => { - const user = await models.user.get(userID) +>(async (_, { userID }, { claims, log, pubsub }) => { + const user = await userRepository.findOneBy({ + id: userID, + }) if (!user) { return { errorCodes: [DeleteAccountErrorCode.UserNotFound], diff --git a/packages/api/src/resolvers/user_feed_article/index.ts b/packages/api/src/resolvers/user_feed_article/index.ts index acfc154a5..724896678 100644 --- a/packages/api/src/resolvers/user_feed_article/index.ts +++ b/packages/api/src/resolvers/user_feed_article/index.ts @@ -1,24 +1,12 @@ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { PartialArticle } from '..' -import { getShareInfoForArticle } from '../../datalayer/links/share_info' import { FeedArticle, - MutationUpdateSharedCommentArgs, PageInfo, - QueryFeedArticlesArgs, - QuerySharedArticleArgs, - ResolverFn, - SharedArticleError, - SharedArticleErrorCode, SharedArticleSuccess, - UpdateSharedCommentError, - UpdateSharedCommentErrorCode, - UpdateSharedCommentSuccess, } from '../../generated/graphql' import { Merge } from '../../util' -import { authorized } from '../../utils/helpers' -import { WithDataSourcesContext } from '../types' export type PartialFeedArticle = Omit< FeedArticle, @@ -35,139 +23,139 @@ export type SharedArticleSuccessPartial = Merge< { article: PartialArticle } > -export const getSharedArticleResolver: ResolverFn< - SharedArticleSuccessPartial | SharedArticleError, - Record, - WithDataSourcesContext, - QuerySharedArticleArgs -> = async (_obj, { username, slug, selectedHighlightId }, { kx, models }) => { - try { - const user = await models.user.getWhere({ username }) - if (!user) { - return { - errorCodes: [SharedArticleErrorCode.NotFound], - } - } +// export const getSharedArticleResolver: ResolverFn< +// SharedArticleSuccessPartial | SharedArticleError, +// Record, +// WithDataSourcesContext, +// QuerySharedArticleArgs +// > = async (_obj, { username, slug, selectedHighlightId }, { kx, models }) => { +// try { +// const user = await models.user.getWhere({ username }) +// if (!user) { +// return { +// errorCodes: [SharedArticleErrorCode.NotFound], +// } +// } - const article = await models.userArticle.getBySlug(username, slug) - if (!article || !article.sharedAt) { - return { - errorCodes: [SharedArticleErrorCode.NotFound], - } - } +// const article = await models.userArticle.getBySlug(username, slug) +// if (!article || !article.sharedAt) { +// return { +// errorCodes: [SharedArticleErrorCode.NotFound], +// } +// } - if (selectedHighlightId) { - const highlightResult = await models.highlight.getWhereIn('shortId', [ - selectedHighlightId, - ]) - if (!highlightResult || !highlightResult[0].sharedAt) { - return { - errorCodes: [SharedArticleErrorCode.NotFound], - } - } - } +// if (selectedHighlightId) { +// const highlightResult = await models.highlight.getWhereIn('shortId', [ +// selectedHighlightId, +// ]) +// if (!highlightResult || !highlightResult[0].sharedAt) { +// return { +// errorCodes: [SharedArticleErrorCode.NotFound], +// } +// } +// } - const shareInfo = await getShareInfoForArticle( - kx, - user.id, - article.id, - models - ) +// const shareInfo = await getShareInfoForArticle( +// kx, +// user.id, +// article.id, +// models +// ) - return { article: { ...article, userId: user.id, shareInfo: shareInfo } } - } catch (error) { - return { errorCodes: [SharedArticleErrorCode.NotFound] } - } -} +// return { article: { ...article, userId: user.id, shareInfo: shareInfo } } +// } catch (error) { +// return { errorCodes: [SharedArticleErrorCode.NotFound] } +// } +// } -export const getUserFeedArticlesResolver: ResolverFn< - PaginatedFeedArticlesSuccessPartial, - unknown, - WithDataSourcesContext, - QueryFeedArticlesArgs -> = async ( - _obj, - { after: _startCursor, first: _first, sharedByUser }, - { models, claims, authTrx } -) => { - if (!(sharedByUser || claims?.uid)) { - return { - edges: [], - pageInfo: { - startCursor: '', - endCursor: '', - hasNextPage: false, - hasPreviousPage: false, - }, - } - } +// export const getUserFeedArticlesResolver: ResolverFn< +// PaginatedFeedArticlesSuccessPartial, +// unknown, +// WithDataSourcesContext, +// QueryFeedArticlesArgs +// > = async ( +// _obj, +// { after: _startCursor, first: _first, sharedByUser }, +// { models, claims, authTrx } +// ) => { +// if (!(sharedByUser || claims?.uid)) { +// return { +// edges: [], +// pageInfo: { +// startCursor: '', +// endCursor: '', +// hasNextPage: false, +// hasPreviousPage: false, +// }, +// } +// } - const first = _first || 0 - const startCursor = _startCursor || '' +// const first = _first || 0 +// const startCursor = _startCursor || '' - const feedArticles = - (await authTrx((tx) => - models.userArticle.getUserFeedArticlesPaginatedWithHighlights( - { cursor: startCursor, first: first + 1, sharedByUser }, // fetch one more item to get next cursor - claims?.uid || '', - tx - ) - )) || [] +// const feedArticles = +// (await authTrx((tx) => +// models.userArticle.getUserFeedArticlesPaginatedWithHighlights( +// { cursor: startCursor, first: first + 1, sharedByUser }, // fetch one more item to get next cursor +// claims?.uid || '', +// tx +// ) +// )) || [] - const endCursor = feedArticles[feedArticles.length - 1]?.sharedAt - .getTime() - ?.toString() - const hasNextPage = feedArticles.length > first +// const endCursor = feedArticles[feedArticles.length - 1]?.sharedAt +// .getTime() +// ?.toString() +// const hasNextPage = feedArticles.length > first - if (hasNextPage) { - // remove an extra if exists - feedArticles.pop() - } +// if (hasNextPage) { +// // remove an extra if exists +// feedArticles.pop() +// } - const edges = feedArticles.map((fa) => { - return { - node: fa, - cursor: fa.sharedAt.getTime()?.toString(), - } - }) +// const edges = feedArticles.map((fa) => { +// return { +// node: fa, +// cursor: fa.sharedAt.getTime()?.toString(), +// } +// }) - return { - edges, - pageInfo: { - hasPreviousPage: false, - startCursor: '', - hasNextPage, - endCursor, - }, - } -} +// return { +// edges, +// pageInfo: { +// hasPreviousPage: false, +// startCursor: '', +// hasNextPage, +// endCursor, +// }, +// } +// } -export const updateSharedCommentResolver = authorized< - UpdateSharedCommentSuccess, - UpdateSharedCommentError, - MutationUpdateSharedCommentArgs ->( - async ( - _, - { input: { articleID, sharedComment } }, - { models, authTrx, claims: { uid } } - ) => { - const ua = await authTrx((tx) => - models.userArticle.getByParameters(uid, { articleId: articleID }, tx) - ) - if (!ua) { - return { errorCodes: [UpdateSharedCommentErrorCode.NotFound] } - } +// export const updateSharedCommentResolver = authorized< +// UpdateSharedCommentSuccess, +// UpdateSharedCommentError, +// MutationUpdateSharedCommentArgs +// >( +// async ( +// _, +// { input: { articleID, sharedComment } }, +// { models, authTrx, claims: { uid } } +// ) => { +// const ua = await authTrx((tx) => +// models.userArticle.getByParameters(uid, { articleId: articleID }, tx) +// ) +// if (!ua) { +// return { errorCodes: [UpdateSharedCommentErrorCode.NotFound] } +// } - await authTrx((tx) => - models.userArticle.updateByArticleId( - uid, - articleID, - { sharedComment }, - tx - ) - ) +// await authTrx((tx) => +// models.userArticle.updateByArticleId( +// uid, +// articleID, +// { sharedComment }, +// tx +// ) +// ) - return { articleID, sharedComment } - } -) +// return { articleID, sharedComment } +// } +// ) diff --git a/packages/api/src/resolvers/user_friends/index.ts b/packages/api/src/resolvers/user_friends/index.ts index 39da6e5d8..823184066 100644 --- a/packages/api/src/resolvers/user_friends/index.ts +++ b/packages/api/src/resolvers/user_friends/index.ts @@ -1,118 +1,101 @@ -import { Knex } from 'knex' -import { UserData } from '../../datalayer/user/model' -import { - GetFollowersResult, - GetFollowingResult, - MutationSetFollowArgs, - QueryGetFollowersArgs, - QueryGetFollowingArgs, - ResolverFn, - SetFollowError, - SetFollowErrorCode, - SetFollowSuccess, - User, -} from '../../generated/graphql' -import { authorized, userDataToUser } from '../../utils/helpers' -import { DataModels, WithDataSourcesContext } from '../types' +// export const setFollowResolver = authorized< +// SetFollowSuccess, +// SetFollowError, +// MutationSetFollowArgs +// >( +// async ( +// _, +// { input: { userId: friendUserId, follow } }, +// { models, authTrx, claims: { uid } } +// ) => { +// const user = await models.user.getUserDetails(uid, friendUserId) +// if (!user) return { errorCodes: [SetFollowErrorCode.NotFound] } -export const setFollowResolver = authorized< - SetFollowSuccess, - SetFollowError, - MutationSetFollowArgs ->( - async ( - _, - { input: { userId: friendUserId, follow } }, - { models, authTrx, claims: { uid } } - ) => { - const user = await models.user.getUserDetails(uid, friendUserId) - if (!user) return { errorCodes: [SetFollowErrorCode.NotFound] } +// const userFriendRecord = await authTrx((tx) => +// models.userFriends.getByUserFriendId(uid, friendUserId, tx) +// ) - const userFriendRecord = await authTrx((tx) => - models.userFriends.getByUserFriendId(uid, friendUserId, tx) - ) +// if (follow) { +// if (!userFriendRecord) { +// await authTrx((tx) => +// models.userFriends.create({ friendUserId, userId: uid }, tx) +// ) +// } +// } else if (userFriendRecord) { +// await authTrx((tx) => models.userFriends.delete(userFriendRecord.id, tx)) +// } - if (follow) { - if (!userFriendRecord) { - await authTrx((tx) => - models.userFriends.create({ friendUserId, userId: uid }, tx) - ) - } - } else if (userFriendRecord) { - await authTrx((tx) => models.userFriends.delete(userFriendRecord.id, tx)) - } +// const updatedUser = await models.user.getUserDetails(uid, friendUserId) +// if (!updatedUser) return { errorCodes: [SetFollowErrorCode.NotFound] } - const updatedUser = await models.user.getUserDetails(uid, friendUserId) - if (!updatedUser) return { errorCodes: [SetFollowErrorCode.NotFound] } +// return { +// updatedUser: { +// ...userDataToUser(updatedUser), +// isFriend: updatedUser.viewerIsFollowing, +// }, +// } +// } +// ) - return { - updatedUser: { - ...userDataToUser(updatedUser), - isFriend: updatedUser.viewerIsFollowing, - }, - } - } -) +// const getUserList = async ( +// uid: string, +// users: UserData[], +// models: DataModels, +// authTrx: ( +// cb: (tx: Knex.Transaction) => TResult, +// userRole?: string +// ) => Promise +// ): Promise => { +// const usersIds = users.map(({ id }) => id) +// const friends = await authTrx((tx) => +// models.userFriends.getByFriendIds(uid, usersIds, tx) +// ) -const getUserList = async ( - uid: string, - users: UserData[], - models: DataModels, - authTrx: ( - cb: (tx: Knex.Transaction) => TResult, - userRole?: string - ) => Promise -): Promise => { - const usersIds = users.map(({ id }) => id) - const friends = await authTrx((tx) => - models.userFriends.getByFriendIds(uid, usersIds, tx) - ) +// const friendsIds = friends.map(({ friendUserId }) => friendUserId) +// users = users.map((f) => ({ +// ...f, +// isFriend: friendsIds.includes(f.id), +// viewerIsFollowing: friendsIds.includes(f.id), +// })) - const friendsIds = friends.map(({ friendUserId }) => friendUserId) - users = users.map((f) => ({ - ...f, - isFriend: friendsIds.includes(f.id), - viewerIsFollowing: friendsIds.includes(f.id), - })) +// return users.map((u) => userDataToUser(u)) +// } - return users.map((u) => userDataToUser(u)) -} +// export const getFollowersResolver: ResolverFn< +// GetFollowersResult, +// unknown, +// WithDataSourcesContext, +// QueryGetFollowersArgs +// > = async (_parent, { userId }, { models, claims, authTrx }) => { +// const followers = userId +// ? await authTrx((tx) => models.user.getUserFollowersList(userId, tx)) +// : [] +// if (!claims?.uid) return { followers: usersWithNoFriends(followers) } +// return { +// followers: await getUserList(claims?.uid, followers, models, authTrx), +// } +// } -export const getFollowersResolver: ResolverFn< - GetFollowersResult, - unknown, - WithDataSourcesContext, - QueryGetFollowersArgs -> = async (_parent, { userId }, { models, claims, authTrx }) => { - const followers = userId - ? await authTrx((tx) => models.user.getUserFollowersList(userId, tx)) - : [] - if (!claims?.uid) return { followers: usersWithNoFriends(followers) } - return { - followers: await getUserList(claims?.uid, followers, models, authTrx), - } -} +// export const getFollowingResolver: ResolverFn< +// GetFollowingResult, +// unknown, +// WithDataSourcesContext, +// QueryGetFollowingArgs +// > = async (_parent, { userId }, { models, claims, authTrx }) => { +// const following = userId +// ? await authTrx((tx) => models.user.getUserFollowingList(userId, tx)) +// : [] +// if (!claims?.uid) return { following: usersWithNoFriends(following) } +// return { +// following: await getUserList(claims?.uid, following, models, authTrx), +// } +// } -export const getFollowingResolver: ResolverFn< - GetFollowingResult, - unknown, - WithDataSourcesContext, - QueryGetFollowingArgs -> = async (_parent, { userId }, { models, claims, authTrx }) => { - const following = userId - ? await authTrx((tx) => models.user.getUserFollowingList(userId, tx)) - : [] - if (!claims?.uid) return { following: usersWithNoFriends(following) } - return { - following: await getUserList(claims?.uid, following, models, authTrx), - } -} - -const usersWithNoFriends = (users: UserData[]): User[] => { - return users.map((f) => - userDataToUser({ - ...f, - isFriend: false, - } as UserData) - ) -} +// const usersWithNoFriends = (users: UserData[]): User[] => { +// return users.map((f) => +// userDataToUser({ +// ...f, +// isFriend: false, +// } as UserData) +// ) +// } diff --git a/packages/api/src/resolvers/user_personalization/index.ts b/packages/api/src/resolvers/user_personalization/index.ts index 955dc16d4..54c179bdf 100644 --- a/packages/api/src/resolvers/user_personalization/index.ts +++ b/packages/api/src/resolvers/user_personalization/index.ts @@ -1,3 +1,5 @@ +import { getRepository, setClaims } from '../../entity' +import { UserPersonalization } from '../../entity/user_personalization' import { GetUserPersonalizationError, GetUserPersonalizationResult, @@ -7,10 +9,8 @@ import { SetUserPersonalizationSuccess, SortOrder, } from '../../generated/graphql' -import { authorized } from '../../utils/helpers' -import { UserPersonalization } from '../../entity/user_personalization' import { AppDataSource } from '../../server' -import { getRepository, setClaims } from '../../entity/utils' +import { authorized } from '../../utils/helpers' export const setUserPersonalizationResolver = authorized< SetUserPersonalizationSuccess, @@ -58,8 +58,12 @@ export const setUserPersonalizationResolver = authorized< export const getUserPersonalizationResolver = authorized< GetUserPersonalizationResult, GetUserPersonalizationError ->(async (_parent, _args, { models, claims: { uid } }) => { - const userPersonalization = await models.userPersonalization.getByUserId(uid) +>(async (_parent, _args, { uid }) => { + const userPersonalization = await getRepository( + UserPersonalization + ).findOneBy({ + user: { id: uid }, + }) // Cast SortOrder from string to enum const librarySortOrder = userPersonalization?.librarySortOrder as diff --git a/packages/api/src/resolvers/webhooks/index.ts b/packages/api/src/resolvers/webhooks/index.ts index 5ec23d9fe..0d2d6b0d4 100644 --- a/packages/api/src/resolvers/webhooks/index.ts +++ b/packages/api/src/resolvers/webhooks/index.ts @@ -1,4 +1,7 @@ -import { authorized } from '../../utils/helpers' +import { getRepository } from '../../entity' +import { User } from '../../entity/user' +import { Webhook } from '../../entity/webhook' +import { env } from '../../env' import { DeleteWebhookError, DeleteWebhookErrorCode, @@ -18,11 +21,8 @@ import { WebhooksSuccess, WebhookSuccess, } from '../../generated/graphql' -import { getRepository } from '../../entity/utils' -import { User } from '../../entity/user' -import { Webhook } from '../../entity/webhook' import { analytics } from '../../utils/analytics' -import { env } from '../../env' +import { authorized } from '../../utils/helpers' export const webhooksResolver = authorized( async (_obj, _params, { claims: { uid }, log }) => { diff --git a/packages/api/src/routers/article_router.ts b/packages/api/src/routers/article_router.ts index 2444f7c8d..f87951765 100644 --- a/packages/api/src/routers/article_router.ts +++ b/packages/api/src/routers/article_router.ts @@ -6,8 +6,8 @@ import express from 'express' import * as jwt from 'jsonwebtoken' import { createPubSubClient } from '../datalayer/pubsub' import { getPageById, updatePage } from '../elastic/pages' +import { getRepository } from '../entity' import { Speech, SpeechState } from '../entity/speech' -import { getRepository } from '../entity/utils' import { env } from '../env' import { CreateArticleErrorCode } from '../generated/graphql' import { Claims } from '../resolvers/types' diff --git a/packages/api/src/routers/auth/auth_router.ts b/packages/api/src/routers/auth/auth_router.ts index a2efc46c2..60d02be9d 100644 --- a/packages/api/src/routers/auth/auth_router.ts +++ b/packages/api/src/routers/auth/auth_router.ts @@ -21,8 +21,8 @@ import { StatusType, UserData, } from '../../datalayer/user/model' +import { getRepository, setClaims } from '../../entity' import { User } from '../../entity/user' -import { getRepository, setClaims } from '../../entity/utils' import { env } from '../../env' import { LoginErrorCode, SignupErrorCode } from '../../generated/graphql' import { isErrorWithCode } from '../../resolvers' diff --git a/packages/api/src/routers/page_router.ts b/packages/api/src/routers/page_router.ts index f321c07d4..b00091c61 100644 --- a/packages/api/src/routers/page_router.ts +++ b/packages/api/src/routers/page_router.ts @@ -5,11 +5,11 @@ import cors from 'cors' import express from 'express' import * as jwt from 'jsonwebtoken' -import { kx } from '../datalayer/knex_config' import { createPubSubClient } from '../datalayer/pubsub' import { createPage, getPageByParam, updatePage } from '../elastic/pages' import { addRecommendation } from '../elastic/recommendation' import { Recommendation } from '../elastic/types' +import { uploadFileRepository } from '../entity' import { env } from '../env' import { ArticleSavingRequestStatus, @@ -17,7 +17,6 @@ import { UploadFileStatus, } from '../generated/graphql' import { Claims } from '../resolvers/types' -import { initModels } from '../server' import { getTokenByRequest } from '../utils/auth' import { corsConfig } from '../utils/corsConfig' import { @@ -76,7 +75,6 @@ export function pageRouter() { return res.status(400).send({ errorCode: 'BAD_DATA' }) } - const models = initModels(kx, false) const ctx = { uid: claims.uid, pubsub: createPubSubClient(), @@ -84,10 +82,10 @@ export function pageRouter() { const title = titleForFilePath(url) const fileName = fileNameForFilePath(url) - const uploadFileData = await models.uploadFile.create({ - url: url, + const uploadFileData = await uploadFileRepository.save({ + url, userId: claims.uid, - fileName: fileName, + fileName, status: UploadFileStatus.Initialized, contentType: 'application/pdf', }) diff --git a/packages/api/src/routers/svc/content.ts b/packages/api/src/routers/svc/content.ts index a991d3062..e52a89700 100644 --- a/packages/api/src/routers/svc/content.ts +++ b/packages/api/src/routers/svc/content.ts @@ -2,16 +2,16 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import express from 'express' -import { setClaims } from '../../datalayer/helpers' -import { kx } from '../../datalayer/knex_config' import { createPubSubClient, readPushSubscription, } from '../../datalayer/pubsub' import { getPageByParam, updatePage } from '../../elastic/pages' import { Page } from '../../elastic/types' +import { setClaims } from '../../entity' import { ArticleSavingRequestStatus } from '../../generated/graphql' -import { initModels } from '../../server' +import { AppDataSource } from '../../server' +import { setFileUploadComplete } from '../../services/save_file' import { logger } from '../../utils/logger' interface UpdateContentMessage { @@ -76,10 +76,9 @@ export function contentServiceRouter() { pageToUpdate.state = ArticleSavingRequestStatus.Succeeded try { - const models = initModels(kx, false) - const uploadFileData = await kx.transaction(async (tx) => { + const uploadFileData = await AppDataSource.transaction(async (tx) => { await setClaims(tx, page.userId) - return models.uploadFile.setFileUploadComplete(fileId, tx) + return setFileUploadComplete(fileId, tx) }) logger.info('updated uploadFileData', uploadFileData) } catch (error) { diff --git a/packages/api/src/routers/svc/email_attachment.ts b/packages/api/src/routers/svc/email_attachment.ts index 86403aa24..62da08e7d 100644 --- a/packages/api/src/routers/svc/email_attachment.ts +++ b/packages/api/src/routers/svc/email_attachment.ts @@ -1,14 +1,14 @@ import express from 'express' -import { setClaims } from '../../datalayer/helpers' -import { kx } from '../../datalayer/knex_config' import { createPubSubClient } from '../../datalayer/pubsub' import { createPage } from '../../elastic/pages' import { ArticleSavingRequestStatus, Page } from '../../elastic/types' +import { setClaims, uploadFileRepository } from '../../entity' import { env } from '../../env' import { PageType, UploadFileStatus } from '../../generated/graphql' -import { initModels } from '../../server' +import { AppDataSource } from '../../server' import { getNewsletterEmail } from '../../services/newsletters' import { updateReceivedEmail } from '../../services/received_emails' +import { setFileUploadComplete } from '../../services/save_file' import { analytics } from '../../utils/analytics' import { getClaimsByToken } from '../../utils/auth' import { generateSlug } from '../../utils/helpers' @@ -54,8 +54,7 @@ export function emailAttachmentRouter() { }) try { - const models = initModels(kx, false) - const uploadFileData = await models.uploadFile.create({ + const uploadFileData = await uploadFileRepository.save({ url: '', userId: user.id, fileName: fileName, @@ -117,10 +116,9 @@ export function emailAttachmentRouter() { }) try { - const models = initModels(kx, false) - const uploadFile = await models.uploadFile.getWhere({ + const uploadFile = await uploadFileRepository.findOneBy({ id: uploadFileId, - userId: user.id, + user: { id: user.id }, }) if (!uploadFile) { return res.status(400).send('BAD REQUEST') @@ -131,9 +129,9 @@ export function emailAttachmentRouter() { uploadFile.fileName ) - const uploadFileData = await kx.transaction(async (tx) => { + const uploadFileData = await AppDataSource.transaction(async (tx) => { await setClaims(tx, user.id) - return models.uploadFile.setFileUploadComplete(uploadFileId, tx) + return setFileUploadComplete(uploadFileId, tx) }) if (!uploadFileData || !uploadFileData.id || !uploadFileData.fileName) { return res.status(400).send('BAD REQUEST') diff --git a/packages/api/src/routers/svc/integrations.ts b/packages/api/src/routers/svc/integrations.ts index bb09b9d8f..67b9840f1 100644 --- a/packages/api/src/routers/svc/integrations.ts +++ b/packages/api/src/routers/svc/integrations.ts @@ -8,8 +8,8 @@ import { v4 as uuidv4 } from 'uuid' import { EntityType, readPushSubscription } from '../../datalayer/pubsub' import { getPageById, searchPages } from '../../elastic/pages' import { Page } from '../../elastic/types' +import { getRepository } from '../../entity' import { Integration, IntegrationType } from '../../entity/integration' -import { getRepository } from '../../entity/utils' import { Claims } from '../../resolvers/types' import { getIntegrationService } from '../../services/integrations' import { getClaimsByToken } from '../../utils/auth' diff --git a/packages/api/src/routers/svc/reminders.ts b/packages/api/src/routers/svc/reminders.ts index 390608376..6212ff721 100644 --- a/packages/api/src/routers/svc/reminders.ts +++ b/packages/api/src/routers/svc/reminders.ts @@ -1,20 +1,12 @@ -import express from 'express' import { MulticastMessage } from 'firebase-admin/messaging' -import { setClaims } from '../../datalayer/helpers' -import { kx } from '../../datalayer/knex_config' import { createPubSubClient } from '../../datalayer/pubsub' import { updatePage } from '../../elastic/pages' +import { setClaims } from '../../entity' import { UserDeviceToken } from '../../entity/user_device_tokens' -import { env, homePageURL } from '../../env' +import { homePageURL } from '../../env' import { ContentReader } from '../../generated/graphql' -import { DataModels } from '../../resolvers/types' -import { initModels } from '../../server' -import { getPagesWithReminder, PageReminder } from '../../services/reminders' -import { getDeviceTokensByUserId } from '../../services/user_device_tokens' -import { analytics } from '../../utils/analytics' -import { logger } from '../../utils/logger' -import { sendEmail } from '../../utils/sendEmail' -import { sendMulticastPushNotifications } from '../../utils/sendNotification' +import { AppDataSource } from '../../server' +import { PageReminder, setRemindersComplete } from '../../services/reminders' interface PageToNotify { title: string @@ -24,116 +16,116 @@ interface PageToNotify { image: string | undefined | null } -export function remindersServiceRouter() { - const router = express.Router() +// export function remindersServiceRouter() { +// const router = express.Router() - // eslint-disable-next-line @typescript-eslint/no-misused-promises - router.post('/trigger', async (req, res) => { - logger.info('reminders/trigger') +// // eslint-disable-next-line @typescript-eslint/no-misused-promises +// router.post('/trigger', async (req, res) => { +// logger.info('reminders/trigger') - const { userId, scheduleTime } = req.body as { - userId?: string - scheduleTime?: number - } +// const { userId, scheduleTime } = req.body as { +// userId?: string +// scheduleTime?: number +// } - analytics.track({ - userId: userId, - event: 'reminder_triggered', - properties: { - env: env.server.apiEnv, - }, - }) +// analytics.track({ +// userId: userId, +// event: 'reminder_triggered', +// properties: { +// env: env.server.apiEnv, +// }, +// }) - if (!userId || !scheduleTime) { - res.status(400).send('Bad Request') - return - } +// if (!userId || !scheduleTime) { +// res.status(400).send('Bad Request') +// return +// } - try { - const remindAt = new Date(scheduleTime) +// try { +// const remindAt = new Date(scheduleTime) - // get all reminders by userid and scheduled time - const models = initModels(kx, false) +// // get all reminders by userid and scheduled time +// const models = initModels(kx, false) - const user = await models.user.get(userId) - if (!user || !user.email) { - logger.info('user not found', userId) - res.status(400).send('User Not Found') - return - } +// const user = await models.user.get(userId) +// if (!user || !user.email) { +// logger.info('user not found', userId) +// res.status(400).send('User Not Found') +// return +// } - const pageReminders = await getPagesWithReminder(userId, remindAt) +// const pageReminders = await getPagesWithReminder(userId, remindAt) - if (!pageReminders) { - logger.info('pages with reminders not found', userId, scheduleTime) - res.status(200).send('Reminders Not Found') - return - } +// if (!pageReminders) { +// logger.info('pages with reminders not found', userId, scheduleTime) +// res.status(200).send('Reminders Not Found') +// return +// } - logger.info('page with reminders:', pageReminders) +// logger.info('page with reminders:', pageReminders) - const [pagesToNotify, pagesToUnarchive] = getPagesToNotifyAndUnarchive( - pageReminders, - user.profile.username - ) +// const [pagesToNotify, pagesToUnarchive] = getPagesToNotifyAndUnarchive( +// pageReminders, +// user.profile.username +// ) - // If none of the fetch reminders have sendNotification - // set to true, then we should not send an email or notification - if (pagesToNotify.length > 0) { - // we have configured Sendgrid to send a template - if (!process.env.SENDGRID_REMINDER_TEMPLATE_ID) { - logger.info('Sendgrid reminder email template_id not set') +// // If none of the fetch reminders have sendNotification +// // set to true, then we should not send an email or notification +// if (pagesToNotify.length > 0) { +// // we have configured Sendgrid to send a template +// if (!process.env.SENDGRID_REMINDER_TEMPLATE_ID) { +// logger.info('Sendgrid reminder email template_id not set') - await updateRemindersStatus( - models, - userId, - pagesToUnarchive, - remindAt - ) - res.status(200).send('Template Id Not Found') - return - } +// await updateRemindersStatus( +// models, +// userId, +// pagesToUnarchive, +// remindAt +// ) +// res.status(200).send('Template Id Not Found') +// return +// } - const dynamicTemplateData = { - subject: `Omnivore Reminder Service`, - title: `Hey ${user.name}, you have ${pagesToNotify.length} article(s) to read on Omnivore`, - articles: pagesToNotify, - } +// const dynamicTemplateData = { +// subject: `Omnivore Reminder Service`, +// title: `Hey ${user.name}, you have ${pagesToNotify.length} article(s) to read on Omnivore`, +// articles: pagesToNotify, +// } - logger.info('dynamic template data:', dynamicTemplateData) +// logger.info('dynamic template data:', dynamicTemplateData) - await sendEmail({ - from: env.sender.message, - dynamicTemplateData: dynamicTemplateData, - templateId: env.sendgrid.reminderTemplateId, - to: user.email, - }) +// await sendEmail({ +// from: env.sender.message, +// dynamicTemplateData: dynamicTemplateData, +// templateId: env.sendgrid.reminderTemplateId, +// to: user.email, +// }) - // send push notifications - const deviceTokens = await getDeviceTokensByUserId(userId) - if (deviceTokens && deviceTokens.length > 0) { - const message = messageForPages(pageReminders, deviceTokens) - await sendMulticastPushNotifications(userId, message, 'reminder') - } +// // send push notifications +// const deviceTokens = await getDeviceTokensByUserId(userId) +// if (deviceTokens && deviceTokens.length > 0) { +// const message = messageForPages(pageReminders, deviceTokens) +// await sendMulticastPushNotifications(userId, message, 'reminder') +// } - if (!deviceTokens) { - logger.info('Device tokens not set:', userId) +// if (!deviceTokens) { +// logger.info('Device tokens not set:', userId) - res.status(400).send('Device token Not Found') - return - } - } +// res.status(400).send('Device token Not Found') +// return +// } +// } - await updateRemindersStatus(models, userId, pagesToUnarchive, remindAt) - res.status(200).send('Reminders triggered') - } catch (e) { - logger.info(e) - res.status(500).send(e) - } - }) +// await updateRemindersStatus(models, userId, pagesToUnarchive, remindAt) +// res.status(200).send('Reminders triggered') +// } catch (e) { +// logger.info(e) +// res.status(500).send(e) +// } +// }) - return router -} +// return router +// } const getPagesToNotifyAndUnarchive = ( pageReminders: PageReminder[], @@ -221,7 +213,6 @@ const messageForPages = ( } const updateRemindersStatus = async ( - models: DataModels, userId: string, pagesToUnarchive: string[], remindAt: Date @@ -243,8 +234,8 @@ const updateRemindersStatus = async ( } // db update - await kx.transaction(async (tx) => { + await AppDataSource.transaction(async (tx) => { await setClaims(tx, userId) - await models.reminder.setRemindersComplete(userId, remindAt, tx) + await setRemindersComplete(tx, userId, remindAt) }) } diff --git a/packages/api/src/routers/svc/rss_feed.ts b/packages/api/src/routers/svc/rss_feed.ts index c53ee726e..78e30cc0e 100644 --- a/packages/api/src/routers/svc/rss_feed.ts +++ b/packages/api/src/routers/svc/rss_feed.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-misused-promises */ import express from 'express' import { readPushSubscription } from '../../datalayer/pubsub' +import { getRepository } from '../../entity' import { Subscription } from '../../entity/subscription' -import { getRepository } from '../../entity/utils' import { SubscriptionStatus, SubscriptionType } from '../../generated/graphql' import { enqueueRssFeedFetch } from '../../utils/createTask' import { logger } from '../../utils/logger' diff --git a/packages/api/src/routers/svc/webhooks.ts b/packages/api/src/routers/svc/webhooks.ts index c2cdd26e0..00937eeea 100644 --- a/packages/api/src/routers/svc/webhooks.ts +++ b/packages/api/src/routers/svc/webhooks.ts @@ -4,7 +4,7 @@ import axios, { Method } from 'axios' import express from 'express' import { readPushSubscription } from '../../datalayer/pubsub' -import { getRepository } from '../../entity/utils' +import { getRepository } from '../../entity' import { Webhook } from '../../entity/webhook' import { logger } from '../../utils/logger' diff --git a/packages/api/src/routers/text_to_speech.ts b/packages/api/src/routers/text_to_speech.ts index 5fee7cc60..1afd8e819 100644 --- a/packages/api/src/routers/text_to_speech.ts +++ b/packages/api/src/routers/text_to_speech.ts @@ -7,9 +7,9 @@ import express from 'express' import { readPushSubscription } from '../datalayer/pubsub' import { getPageById } from '../elastic/pages' import { ArticleSavingRequestStatus } from '../elastic/types' +import { getRepository, setClaims } from '../entity' import { Speech, SpeechState } from '../entity/speech' import { UserPersonalization } from '../entity/user_personalization' -import { getRepository, setClaims } from '../entity/utils' import { AppDataSource } from '../server' import { FeatureName, getFeature } from '../services/features' import { shouldSynthesize } from '../services/speech' diff --git a/packages/api/src/routers/user_router.ts b/packages/api/src/routers/user_router.ts index d2feb11fe..b80ba0bb1 100644 --- a/packages/api/src/routers/user_router.ts +++ b/packages/api/src/routers/user_router.ts @@ -2,8 +2,8 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import cors from 'cors' import express from 'express' +import { getRepository } from '../entity' import { User } from '../entity/user' -import { getRepository } from '../entity/utils' import { env } from '../env' import { getClaimsByToken, getTokenByRequest } from '../utils/auth' import { corsConfig } from '../utils/corsConfig' diff --git a/packages/api/src/schema.ts b/packages/api/src/schema.ts index d0b161eb9..94f719594 100755 --- a/packages/api/src/schema.ts +++ b/packages/api/src/schema.ts @@ -2604,24 +2604,24 @@ const schema = gql` mergeHighlight(input: MergeHighlightInput!): MergeHighlightResult! updateHighlight(input: UpdateHighlightInput!): UpdateHighlightResult! deleteHighlight(highlightId: ID!): DeleteHighlightResult! - createHighlightReply( - input: CreateHighlightReplyInput! - ): CreateHighlightReplyResult! - updateHighlightReply( - input: UpdateHighlightReplyInput! - ): UpdateHighlightReplyResult! - deleteHighlightReply(highlightReplyId: ID!): DeleteHighlightReplyResult! - createReaction(input: CreateReactionInput!): CreateReactionResult! - deleteReaction(id: ID!): DeleteReactionResult! + # createHighlightReply( + # input: CreateHighlightReplyInput! + # ): CreateHighlightReplyResult! + # updateHighlightReply( + # input: UpdateHighlightReplyInput! + # ): UpdateHighlightReplyResult! + # deleteHighlightReply(highlightReplyId: ID!): DeleteHighlightReplyResult! + # createReaction(input: CreateReactionInput!): CreateReactionResult! + # deleteReaction(id: ID!): DeleteReactionResult! uploadFileRequest(input: UploadFileRequestInput!): UploadFileRequestResult! saveArticleReadingProgress( input: SaveArticleReadingProgressInput! ): SaveArticleReadingProgressResult! - setShareArticle(input: SetShareArticleInput!): SetShareArticleResult! - updateSharedComment( - input: UpdateSharedCommentInput! - ): UpdateSharedCommentResult! - setFollow(input: SetFollowInput!): SetFollowResult! + # setShareArticle(input: SetShareArticleInput!): SetShareArticleResult! + # updateSharedComment( + # input: UpdateSharedCommentInput! + # ): UpdateSharedCommentResult! + # setFollow(input: SetFollowInput!): SetFollowResult! setBookmarkArticle( input: SetBookmarkArticleInput! ): SetBookmarkArticleResult! @@ -2631,11 +2631,11 @@ const schema = gql` createArticleSavingRequest( input: CreateArticleSavingRequestInput! ): CreateArticleSavingRequestResult! - setShareHighlight(input: SetShareHighlightInput!): SetShareHighlightResult! + # setShareHighlight(input: SetShareHighlightInput!): SetShareHighlightResult! reportItem(input: ReportItemInput!): ReportItemResult! - updateLinkShareInfo( - input: UpdateLinkShareInfoInput! - ): UpdateLinkShareInfoResult! + # updateLinkShareInfo( + # input: UpdateLinkShareInfoInput! + # ): UpdateLinkShareInfoResult! setLinkArchived(input: ArchiveLinkInput!): ArchiveLinkResult! createNewsletterEmail: CreateNewsletterEmailResult! deleteNewsletterEmail(newsletterEmailId: ID!): DeleteNewsletterEmailResult! @@ -2643,9 +2643,9 @@ const schema = gql` savePage(input: SavePageInput!): SaveResult! updatePage(input: UpdatePageInput!): UpdatePageResult! saveFile(input: SaveFileInput!): SaveResult! - createReminder(input: CreateReminderInput!): CreateReminderResult! - updateReminder(input: UpdateReminderInput!): UpdateReminderResult! - deleteReminder(id: ID!): DeleteReminderResult! + # createReminder(input: CreateReminderInput!): CreateReminderResult! + # updateReminder(input: UpdateReminderInput!): UpdateReminderResult! + # deleteReminder(id: ID!): DeleteReminderResult! setDeviceToken(input: SetDeviceTokenInput!): SetDeviceTokenResult! createLabel(input: CreateLabelInput!): CreateLabelResult! updateLabel(input: UpdateLabelInput!): UpdateLabelResult! @@ -2710,25 +2710,25 @@ const schema = gql` includePending: Boolean ): ArticlesResult! article(username: String!, slug: String!, format: String): ArticleResult! - sharedArticle( - username: String! - slug: String! - selectedHighlightId: String - ): SharedArticleResult! - feedArticles( - after: String - first: Int - sort: SortParams - sharedByUser: ID - ): FeedArticlesResult! + # sharedArticle( + # username: String! + # slug: String! + # selectedHighlightId: String + # ): SharedArticleResult! + # feedArticles( + # after: String + # first: Int + # sort: SortParams + # sharedByUser: ID + # ): FeedArticlesResult! users: UsersResult! validateUsername(username: String!): Boolean! - getFollowers(userId: ID): GetFollowersResult! - getFollowing(userId: ID): GetFollowingResult! + # getFollowers(userId: ID): GetFollowersResult! + # getFollowing(userId: ID): GetFollowingResult! getUserPersonalization: GetUserPersonalizationResult! articleSavingRequest(id: ID, url: String): ArticleSavingRequestResult! newsletterEmails: NewsletterEmailsResult! - reminder(linkId: ID!): ReminderResult! + # reminder(linkId: ID!): ReminderResult! labels: LabelsResult! search( after: String diff --git a/packages/api/src/server.ts b/packages/api/src/server.ts index 7178cb427..77eeaa7df 100755 --- a/packages/api/src/server.ts +++ b/packages/api/src/server.ts @@ -11,24 +11,12 @@ import express, { Express } from 'express' import * as httpContext from 'express-http-context2' import rateLimit from 'express-rate-limit' import { createServer, Server } from 'http' -import { Knex } from 'knex' import { DataSource } from 'typeorm' import { SnakeNamingStrategy } from 'typeorm-naming-strategies' import { config, loggers } from 'winston' import { makeApolloServer } from './apollo' -import ArticleModel from './datalayer/article' -import ArticleSavingRequestModel from './datalayer/article_saving_request' -import HighlightModel from './datalayer/highlight' -import UserArticleModel from './datalayer/links' -import ReactionModel from './datalayer/reaction' -import ReminderModel from './datalayer/reminders' -import UploadFileDataModel from './datalayer/upload_files' -import UserModel from './datalayer/user' -import UserFriendModel from './datalayer/user_friends' -import UserPersonalizationModel from './datalayer/user_personalization' import { initElasticsearch } from './elastic' import { env } from './env' -import { DataModels } from './resolvers/types' import { articleRouter } from './routers/article_router' import { authRouter } from './routers/auth/auth_router' import { mobileAuthRouter } from './routers/auth/mobile/mobile_auth_router' @@ -42,7 +30,7 @@ import { emailAttachmentRouter } from './routers/svc/email_attachment' import { integrationsServiceRouter } from './routers/svc/integrations' import { linkServiceRouter } from './routers/svc/links' import { newsletterServiceRouter } from './routers/svc/newsletters' -import { remindersServiceRouter } from './routers/svc/reminders' +// import { remindersServiceRouter } from './routers/svc/reminders' import { rssFeedRouter } from './routers/svc/rss_feed' import { uploadServiceRouter } from './routers/svc/upload' import { webhooksServiceRouter } from './routers/svc/webhooks' @@ -59,19 +47,6 @@ import { const PORT = process.env.PORT || 4000 -export const initModels = (kx: Knex, cache = true): DataModels => ({ - user: new UserModel(kx, cache), - article: new ArticleModel(kx, cache), - userArticle: new UserArticleModel(kx, cache), - userFriends: new UserFriendModel(kx, cache), - userPersonalization: new UserPersonalizationModel(kx, cache), - articleSavingRequest: new ArticleSavingRequestModel(kx, cache), - uploadFile: new UploadFileDataModel(kx, cache), - highlight: new HighlightModel(kx, cache), - reaction: new ReactionModel(kx, cache), - reminder: new ReminderModel(kx, cache), -}) - export const AppDataSource = new DataSource({ type: 'postgres', host: env.pg.host, @@ -85,6 +60,9 @@ export const AppDataSource = new DataSource({ subscribers: [__dirname + '/events/**/*{.js,.ts}'], namingStrategy: new SnakeNamingStrategy(), logger: new CustomTypeOrmLogger(), + cache: true, + connectTimeoutMS: 60000, // 60 seconds + maxQueryExecutionTime: 60000, // 60 seconds }) export const createApp = (): { @@ -164,7 +142,7 @@ export const createApp = (): { app.use('/svc/pubsub/webhooks', webhooksServiceRouter()) app.use('/svc/pubsub/integrations', integrationsServiceRouter()) app.use('/svc/pubsub/rss-feed', rssFeedRouter()) - app.use('/svc/reminders', remindersServiceRouter()) + // app.use('/svc/reminders', remindersServiceRouter()) app.use('/svc/email-attachment', emailAttachmentRouter()) if (env.dev.isLocal) { diff --git a/packages/api/src/services/archive_link.ts b/packages/api/src/services/archive_link.ts index 226df4672..8d74de5e5 100644 --- a/packages/api/src/services/archive_link.ts +++ b/packages/api/src/services/archive_link.ts @@ -1,5 +1,5 @@ +import { setClaims } from '../entity' import { Link } from '../entity/link' -import { setClaims } from '../entity/utils' import { AppDataSource } from '../server' export const setLinkArchived = async ( diff --git a/packages/api/src/services/create_page_save_request.ts b/packages/api/src/services/create_page_save_request.ts index f51510148..6c7b74408 100644 --- a/packages/api/src/services/create_page_save_request.ts +++ b/packages/api/src/services/create_page_save_request.ts @@ -8,8 +8,8 @@ import { updatePage, } from '../elastic/pages' import { ArticleSavingRequestStatus, Label, PageType } from '../elastic/types' +import { getRepository } from '../entity' import { User } from '../entity/user' -import { getRepository } from '../entity/utils' import { ArticleSavingRequest, CreateArticleSavingRequestErrorCode, diff --git a/packages/api/src/services/create_user.ts b/packages/api/src/services/create_user.ts index 649e04b34..7b49e0f40 100644 --- a/packages/api/src/services/create_user.ts +++ b/packages/api/src/services/create_user.ts @@ -1,10 +1,10 @@ -import { EntityManager } from 'typeorm' +import { EntityManager, In } from 'typeorm' import { StatusType } from '../datalayer/user/model' +import { getRepository, userRepository } from '../entity' import { GroupMembership } from '../entity/groups/group_membership' import { Invite } from '../entity/groups/invite' import { Profile } from '../entity/profile' import { User } from '../entity/user' -import { getRepository } from '../entity/utils' import { SignupErrorCode } from '../generated/graphql' import { AuthProvider } from '../routers/auth/auth_types' import { AppDataSource } from '../server' @@ -15,6 +15,18 @@ import { Filter } from '../entity/filter' import { analytics } from '../utils/analytics' import { env } from '../env' +const TOP_USERS = [ + 'jacksonh', + 'nat', + 'luis', + 'satindar', + 'malandrina', + 'patrick', + 'alexgutjahr', + 'hongbowu', +] +export const MAX_RECORDS_LIMIT = 1000 + export const createUser = async (input: { provider: AuthProvider sourceUserId?: string @@ -166,9 +178,19 @@ const validateInvite = async ( } export const getUserByEmail = async (email: string): Promise => { - return getRepository(User) + return userRepository .createQueryBuilder('user') .leftJoinAndSelect('user.profile', 'profile') .where('LOWER(email) = LOWER(:email)', { email }) // case insensitive .getOne() } + +export const getTopUsers = async (): Promise => { + return userRepository + .createQueryBuilder() + .where({ + profile: { username: In(TOP_USERS) }, + }) + .limit(MAX_RECORDS_LIMIT) + .getMany() +} diff --git a/packages/api/src/services/features.ts b/packages/api/src/services/features.ts index 7d3158d70..36495fc07 100644 --- a/packages/api/src/services/features.ts +++ b/packages/api/src/services/features.ts @@ -1,7 +1,7 @@ import * as jwt from 'jsonwebtoken' import { IsNull, Not } from 'typeorm' +import { getRepository } from '../entity' import { Feature } from '../entity/feature' -import { getRepository } from '../entity/utils' import { env } from '../env' import { AppDataSource } from '../server' import { logger } from '../utils/logger' diff --git a/packages/api/src/services/followers.ts b/packages/api/src/services/followers.ts index 0b93c2e81..84543a24a 100644 --- a/packages/api/src/services/followers.ts +++ b/packages/api/src/services/followers.ts @@ -1,6 +1,6 @@ -import { User } from '../entity/user' +import { getRepository } from '../entity' import { Follower } from '../entity/follower' -import { getRepository } from '../entity/utils' +import { User } from '../entity/user' export const getUserFollowers = async ( user: User, diff --git a/packages/api/src/services/groups.ts b/packages/api/src/services/groups.ts index 3f2979182..a7b49ff6e 100644 --- a/packages/api/src/services/groups.ts +++ b/packages/api/src/services/groups.ts @@ -1,10 +1,10 @@ import { nanoid } from 'nanoid' +import { getRepository } from '../entity' import { Group } from '../entity/groups/group' import { GroupMembership } from '../entity/groups/group_membership' import { Invite } from '../entity/groups/invite' import { RuleActionType } from '../entity/rule' import { User } from '../entity/user' -import { getRepository } from '../entity/utils' import { homePageURL } from '../env' import { RecommendationGroup, User as GraphqlUser } from '../generated/graphql' import { AppDataSource } from '../server' diff --git a/packages/api/src/services/integrations/readwise.ts b/packages/api/src/services/integrations/readwise.ts index b7487c22f..70d7a3151 100644 --- a/packages/api/src/services/integrations/readwise.ts +++ b/packages/api/src/services/integrations/readwise.ts @@ -1,7 +1,7 @@ import axios from 'axios' import { HighlightType, Page } from '../../elastic/types' +import { getRepository } from '../../entity' import { Integration } from '../../entity/integration' -import { getRepository } from '../../entity/utils' import { env } from '../../env' import { wait } from '../../utils/helpers' import { logger } from '../../utils/logger' diff --git a/packages/api/src/services/labels.ts b/packages/api/src/services/labels.ts index 5837ddf72..135514e9c 100644 --- a/packages/api/src/services/labels.ts +++ b/packages/api/src/services/labels.ts @@ -2,10 +2,10 @@ import DataLoader from 'dataloader' import { In } from 'typeorm' import { addLabelInPage } from '../elastic/labels' import { PageContext } from '../elastic/types' +import { getRepository } from '../entity' import { Label } from '../entity/label' import { Link } from '../entity/link' import { User } from '../entity/user' -import { getRepository } from '../entity/utils' import { CreateLabelInput } from '../generated/graphql' import { generateRandomColor } from '../utils/helpers' import { logger } from '../utils/logger' @@ -117,18 +117,10 @@ export const createLabels = async ( ctx: PageContext, labels: CreateLabelInput[] ): Promise => { - const user = await getRepository(User).findOneBy({ - id: ctx.uid, - }) - if (!user) { - logger.error('user not found') - return [] - } - const labelEntities = await getRepository(Label) .createQueryBuilder() .where({ - user: { id: user.id }, + user: { id: ctx.uid }, }) .andWhere('LOWER(name) IN (:...names)', { names: labels.map((l) => l.name.toLowerCase()), @@ -148,7 +140,7 @@ export const createLabels = async ( description: l.description, color: l.color || generateRandomColor(), internal: isLabelInternal(l.name), - user, + user: { id: ctx.uid }, })) ) return [...labelEntities, ...newLabelEntities] diff --git a/packages/api/src/services/newsletters.ts b/packages/api/src/services/newsletters.ts index 79ddef432..20345b1f3 100644 --- a/packages/api/src/services/newsletters.ts +++ b/packages/api/src/services/newsletters.ts @@ -1,12 +1,12 @@ -import { NewsletterEmail } from '../entity/newsletter_email' import { nanoid } from 'nanoid' +import { getRepository } from '../entity' +import { NewsletterEmail } from '../entity/newsletter_email' import { User } from '../entity/user' +import { env } from '../env' import { CreateNewsletterEmailErrorCode, SubscriptionStatus, } from '../generated/graphql' -import { env } from '../env' -import { getRepository } from '../entity/utils' import addressparser = require('nodemailer/lib/addressparser') const parsedAddress = (emailAddress: string): string | undefined => { diff --git a/packages/api/src/services/received_emails.ts b/packages/api/src/services/received_emails.ts index a3891bfcc..2995f7346 100644 --- a/packages/api/src/services/received_emails.ts +++ b/packages/api/src/services/received_emails.ts @@ -1,5 +1,5 @@ +import { getRepository } from '../entity' import { ReceivedEmail } from '../entity/received_email' -import { getRepository } from '../entity/utils' export const saveReceivedEmail = async ( from: string, diff --git a/packages/api/src/services/reminders.ts b/packages/api/src/services/reminders.ts index d94e065a4..95168735c 100644 --- a/packages/api/src/services/reminders.ts +++ b/packages/api/src/services/reminders.ts @@ -1,7 +1,7 @@ -import { IsNull, Not } from 'typeorm' +import { EntityManager, IsNull, Not } from 'typeorm' import { getPageById } from '../elastic/pages' +import { getRepository } from '../entity' import { Reminder } from '../entity/reminder' -import { getRepository } from '../entity/utils' import { logger } from '../utils/logger' export interface PageReminder { @@ -52,3 +52,23 @@ export const getPagesWithReminder = async ( return results } + +export const setRemindersComplete = async ( + tx: EntityManager, + userId: string, + remindAt: Date +): Promise => { + const updateResult = await tx + .createQueryBuilder() + .where({ userId, remindAt }) + .update(Reminder) + .set({ status: 'COMPLETED' }) + .returning('*') + .execute() + + if (updateResult.generatedMaps.length === 0) { + return null + } + + return updateResult.generatedMaps[0] as Reminder +} diff --git a/packages/api/src/services/reports.ts b/packages/api/src/services/reports.ts index 1495af024..96590e94e 100644 --- a/packages/api/src/services/reports.ts +++ b/packages/api/src/services/reports.ts @@ -1,7 +1,7 @@ import { getPageById } from '../elastic/pages' +import { getRepository } from '../entity' import { AbuseReport } from '../entity/reports/abuse_report' import { ContentDisplayReport } from '../entity/reports/content_display_report' -import { getRepository } from '../entity/utils' import { ReportItemInput, ReportType } from '../generated/graphql' import { logger } from '../utils/logger' diff --git a/packages/api/src/services/rules.ts b/packages/api/src/services/rules.ts index c4fa6e2a3..94fd3f4eb 100644 --- a/packages/api/src/services/rules.ts +++ b/packages/api/src/services/rules.ts @@ -1,6 +1,6 @@ -import { Rule, RuleAction } from '../entity/rule' -import { getRepository } from '../entity/utils' import { ILike } from 'typeorm' +import { getRepository } from '../entity' +import { Rule, RuleAction } from '../entity/rule' export const createRule = async ( userId: string, diff --git a/packages/api/src/services/save_file.ts b/packages/api/src/services/save_file.ts index 235670839..5fa56b106 100644 --- a/packages/api/src/services/save_file.ts +++ b/packages/api/src/services/save_file.ts @@ -1,7 +1,7 @@ -import { Knex } from 'knex' -import { PubsubClient } from '../datalayer/pubsub' import { UserData } from '../datalayer/user/model' import { updatePage } from '../elastic/pages' +import { getRepository } from '../entity' +import { UploadFile } from '../entity/upload_file' import { homePageURL } from '../env' import { ArticleSavingRequestStatus, @@ -9,33 +9,30 @@ import { SaveFileInput, SaveResult, } from '../generated/graphql' -import { DataModels } from '../resolvers/types' +import { WithDataSourcesContext } from '../resolvers/types' +import { AppDataSource } from '../server' import { logger } from '../utils/logger' import { getStorageFileDetails } from '../utils/uploads' import { createLabels } from './labels' -type SaveContext = { - pubsub: PubsubClient - models: DataModels - authTrx: ( - cb: (tx: Knex.Transaction) => TResult, - userRole?: string - ) => Promise - uid: string +export const setFileUploadComplete = async ( + id: string, + em = AppDataSource.createEntityManager() +): Promise => { + return em.getRepository(UploadFile).save({ id, status: 'COMPLETED' }) } export const saveFile = async ( - ctx: SaveContext, - saver: UserData, + ctx: WithDataSourcesContext, + user: UserData, input: SaveFileInput ): Promise => { logger.info('saving file with input', input) const pageId = input.clientRequestId - const uploadFile = await ctx.models.uploadFile.getWhere({ + const uploadFile = await getRepository(UploadFile).findOneBy({ id: input.uploadFileId, - userId: saver.id, + user: { id: ctx.uid }, }) - if (!uploadFile) { return { errorCodes: [SaveErrorCode.Unauthorized], @@ -45,7 +42,7 @@ export const saveFile = async ( await getStorageFileDetails(input.uploadFileId, uploadFile.fileName) const uploadFileData = await ctx.authTrx(async (tx) => { - return ctx.models.uploadFile.setFileUploadComplete(input.uploadFileId, tx) + return setFileUploadComplete(input.uploadFileId, tx) }) if (!uploadFileData) { @@ -59,7 +56,7 @@ export const saveFile = async ( input.state === ArticleSavingRequestStatus.Archived ? new Date() : null // add labels to page const labels = input.labels - ? await createLabels({ ...ctx, uid: saver.id }, input.labels) + ? await createLabels(ctx, input.labels) : undefined if (input.state || input.labels) { const updated = await updatePage( @@ -80,7 +77,7 @@ export const saveFile = async ( return { clientRequestId: input.clientRequestId, - url: `${homePageURL()}/${saver.profile.username}/links/${ + url: `${homePageURL()}/${user.profile.username}/links/${ input.clientRequestId }`, } diff --git a/packages/api/src/services/save_page.ts b/packages/api/src/services/save_page.ts index 2acaa6755..1bf439803 100644 --- a/packages/api/src/services/save_page.ts +++ b/packages/api/src/services/save_page.ts @@ -1,8 +1,8 @@ import { Readability } from '@omnivore/readability' -import { PubsubClient } from '../datalayer/pubsub' import { addHighlightToPage } from '../elastic/highlights' import { createPage, getPageByParam, updatePage } from '../elastic/pages' import { ArticleSavingRequestStatus, Page, PageType } from '../elastic/types' +import { User } from '../entity/user' import { homePageURL } from '../env' import { HighlightType, @@ -12,7 +12,7 @@ import { SavePageInput, SaveResult, } from '../generated/graphql' -import { DataModels } from '../resolvers/types' +import { WithDataSourcesContext } from '../resolvers/types' import { enqueueThumbnailTask } from '../utils/createTask' import { cleanUrl, @@ -27,18 +27,6 @@ import { parsePreparedContent } from '../utils/parser' import { createPageSaveRequest } from './create_page_save_request' import { createLabels } from './labels' -type SaveContext = { - pubsub: PubsubClient - models: DataModels - uid: string - refresh?: boolean -} - -type SaverUserData = { - userId: string - username: string -} - // where we can use APIs to fetch their underlying content. const FORCE_PUPPETEER_URLS = [ TWEET_URL_REGEX, @@ -65,19 +53,9 @@ const shouldParseInBackend = (input: SavePageInput): boolean => { ) } -export const createSavingRequest = ( - ctx: SaveContext, - clientRequestId: string -) => { - return ctx.models.articleSavingRequest.create({ - userId: ctx.uid, - id: clientRequestId, - }) -} - export const savePage = async ( - ctx: SaveContext, - saver: SaverUserData, + ctx: WithDataSourcesContext, + user: User, input: SavePageInput ): Promise => { const parseResult = await parsePreparedContent( @@ -97,7 +75,7 @@ export const savePage = async ( const articleToSave = parsedContentToPage({ url: input.url, title: input.title, - userId: saver.userId, + userId: user.id, pageId, slug, croppedPathname, @@ -124,7 +102,7 @@ export const savePage = async ( if (shouldParseInBackend(input)) { try { await createPageSaveRequest({ - userId: saver.userId, + userId: user.id, url: articleToSave.url, pubsub: ctx.pubsub, articleSavingRequestId: input.clientRequestId, @@ -140,7 +118,7 @@ export const savePage = async ( } else { // check if the page already exists const existingPage = await getPageByParam({ - userId: saver.userId, + userId: user.id, url: articleToSave.url, }) if (existingPage) { @@ -151,7 +129,7 @@ export const savePage = async ( ) { return { clientRequestId: pageId, - url: `${homePageURL()}/${saver.username}/${slug}`, + url: `${homePageURL()}/${user.profile.username}/${slug}`, } } @@ -195,7 +173,7 @@ export const savePage = async ( if (!isImported) { try { // create a task to update thumbnail and pre-cache all images - const taskId = await enqueueThumbnailTask(saver.userId, slug) + const taskId = await enqueueThumbnailTask(user.id, slug) logger.info('Created thumbnail task', { taskId }) } catch (e) { logger.error('Failed to create thumbnail task', e) @@ -222,7 +200,7 @@ export const savePage = async ( return { clientRequestId: pageId, - url: `${homePageURL()}/${saver.username}/${slug}`, + url: `${homePageURL()}/${user.profile.username}/${slug}`, } } diff --git a/packages/api/src/services/save_url.ts b/packages/api/src/services/save_url.ts index f42f34115..7c9267867 100644 --- a/packages/api/src/services/save_url.ts +++ b/packages/api/src/services/save_url.ts @@ -1,7 +1,7 @@ import { PubsubClient } from '../datalayer/pubsub' import { ArticleSavingRequestStatus } from '../elastic/types' +import { getRepository } from '../entity' import { User } from '../entity/user' -import { getRepository } from '../entity/utils' import { homePageURL } from '../env' import { SaveErrorCode, SaveResult, SaveUrlInput } from '../generated/graphql' import { logger } from '../utils/logger' @@ -24,7 +24,7 @@ export const saveUrl = async ( input.state === ArticleSavingRequestStatus.Archived ? new Date() : null // add labels to page const labels = input.labels - ? await createLabels({ ...ctx, uid: ctx.uid }, input.labels) + ? await createLabels(ctx, input.labels) : undefined const pageSaveRequest = await createPageSaveRequest({ diff --git a/packages/api/src/services/search_history.ts b/packages/api/src/services/search_history.ts index 59da78836..582048c3c 100644 --- a/packages/api/src/services/search_history.ts +++ b/packages/api/src/services/search_history.ts @@ -1,5 +1,5 @@ +import { getRepository } from '../entity' import { SearchHistory } from '../entity/search_history' -import { getRepository } from '../entity/utils' export const getRecentSearches = async ( userId: string diff --git a/packages/api/src/services/subscriptions.ts b/packages/api/src/services/subscriptions.ts index c950dfee7..ecc0db916 100644 --- a/packages/api/src/services/subscriptions.ts +++ b/packages/api/src/services/subscriptions.ts @@ -1,7 +1,7 @@ import axios from 'axios' +import { getRepository } from '../entity' import { NewsletterEmail } from '../entity/newsletter_email' import { Subscription } from '../entity/subscription' -import { getRepository } from '../entity/utils' import { SubscriptionStatus, SubscriptionType } from '../generated/graphql' import { logger } from '../utils/logger' import { sendEmail } from '../utils/sendEmail' diff --git a/packages/api/src/services/user_device_tokens.ts b/packages/api/src/services/user_device_tokens.ts index 4cc740a4b..f050a5388 100644 --- a/packages/api/src/services/user_device_tokens.ts +++ b/packages/api/src/services/user_device_tokens.ts @@ -1,10 +1,10 @@ -import { UserDeviceToken } from '../entity/user_device_tokens' +import { getRepository, setClaims } from '../entity' import { User } from '../entity/user' -import { SetDeviceTokenErrorCode } from '../generated/graphql' -import { getRepository, setClaims } from '../entity/utils' -import { analytics } from '../utils/analytics' +import { UserDeviceToken } from '../entity/user_device_tokens' import { env } from '../env' +import { SetDeviceTokenErrorCode } from '../generated/graphql' import { AppDataSource } from '../server' +import { analytics } from '../utils/analytics' export const getDeviceToken = async ( id: string diff --git a/packages/api/src/utils/auth.ts b/packages/api/src/utils/auth.ts index 951bf6300..1f410a4a4 100644 --- a/packages/api/src/utils/auth.ts +++ b/packages/api/src/utils/auth.ts @@ -4,8 +4,8 @@ import express from 'express' import * as jwt from 'jsonwebtoken' import { promisify } from 'util' import { v4 as uuidv4 } from 'uuid' +import { getRepository } from '../entity' import { ApiKey } from '../entity/api_key' -import { getRepository } from '../entity/utils' import { env } from '../env' import { Claims, ClaimsToSet } from '../resolvers/types' import { logger } from './logger' diff --git a/packages/api/src/utils/helpers.ts b/packages/api/src/utils/helpers.ts index 74607ab33..ea72d7ffc 100644 --- a/packages/api/src/utils/helpers.ts +++ b/packages/api/src/utils/helpers.ts @@ -5,10 +5,9 @@ import path from 'path' import _ from 'underscore' import slugify from 'voca/slugify' import wordsCounter from 'word-counting' -import { RegistrationType, UserData } from '../datalayer/user/model' import { updatePage } from '../elastic/pages' import { ArticleSavingRequestStatus, Page } from '../elastic/types' -import { User } from '../entity/user' +import { RegistrationType, User } from '../entity/user' import { ArticleSavingRequest, CreateArticleError, @@ -116,7 +115,7 @@ export const findDelimiter = ( // FIXME: Remove this Date stub after nullable types will be fixed export const userDataToUser = ( user: Merge< - UserData, + User, { isFriend?: boolean followersCount?: number @@ -156,15 +155,11 @@ export const userDataToUser = ( followersCount: user.followersCount || 0, isFullUser: true, viewerIsFollowing: user.viewerIsFollowing || user.isFriend || false, - picture: user.profile.picture_url, + picture: user.profile.pictureUrl, sharedArticles: [], sharedArticlesCount: user.sharedArticlesCount || 0, sharedHighlightsCount: user.sharedHighlightsCount || 0, sharedNotesCount: user.sharedNotesCount || 0, - profile: { - ...user.profile, - pictureUrl: user.profile.picture_url, - }, }) export const generateSlug = (title: string): string => { diff --git a/packages/api/src/utils/parser.ts b/packages/api/src/utils/parser.ts index 329bd977a..b280ed784 100644 --- a/packages/api/src/utils/parser.ts +++ b/packages/api/src/utils/parser.ts @@ -16,8 +16,8 @@ import { ILike } from 'typeorm' import { promisify } from 'util' import { v4 as uuid } from 'uuid' import { Highlight } from '../elastic/types' +import { getRepository } from '../entity' import { User } from '../entity/user' -import { getRepository } from '../entity/utils' import { env } from '../env' import { PageType, PreparedDocumentInput } from '../generated/graphql' import { ArticleFormat } from '../resolvers/article' diff --git a/packages/api/test/db.ts b/packages/api/test/db.ts index 6faf1803e..22da1a403 100644 --- a/packages/api/test/db.ts +++ b/packages/api/test/db.ts @@ -1,6 +1,7 @@ import Postgrator from 'postgrator' import { FindOptionsWhere } from 'typeorm' import { SnakeNamingStrategy } from 'typeorm-naming-strategies' +import { getRepository, setClaims } from '../src/entity' import { Integration } from '../src/entity/integration' import { Label } from '../src/entity/label' import { Link } from '../src/entity/link' @@ -11,7 +12,6 @@ import { Reminder } from '../src/entity/reminder' import { Subscription } from '../src/entity/subscription' import { User } from '../src/entity/user' import { UserDeviceToken } from '../src/entity/user_device_tokens' -import { getRepository, setClaims } from '../src/entity/utils' import { SubscriptionStatus, SubscriptionType } from '../src/generated/graphql' import { AppDataSource } from '../src/server' import { createUser } from '../src/services/create_user' diff --git a/packages/api/test/resolvers/api_key.test.ts b/packages/api/test/resolvers/api_key.test.ts index e1f85f3d8..2bdc840fb 100644 --- a/packages/api/test/resolvers/api_key.test.ts +++ b/packages/api/test/resolvers/api_key.test.ts @@ -3,7 +3,7 @@ import { createTestUser, deleteTestUser } from '../db' import { graphqlRequest, request } from '../util' import { expect } from 'chai' import supertest from 'supertest' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' import { ApiKey } from '../../src/entity/api_key' const testAPIKey = (apiKey: string): supertest.Test => { diff --git a/packages/api/test/resolvers/article.test.ts b/packages/api/test/resolvers/article.test.ts index 10a94e06a..43678497e 100644 --- a/packages/api/test/resolvers/article.test.ts +++ b/packages/api/test/resolvers/article.test.ts @@ -22,9 +22,9 @@ import { PageContext, PageType, } from '../../src/elastic/types' +import { getRepository } from '../../src/entity' import { UploadFile } from '../../src/entity/upload_file' import { User } from '../../src/entity/user' -import { getRepository } from '../../src/entity/utils' import { BulkActionType, SyncUpdatedItemEdge, diff --git a/packages/api/test/resolvers/features.test.ts b/packages/api/test/resolvers/features.test.ts index d4781612c..2988cf90c 100644 --- a/packages/api/test/resolvers/features.test.ts +++ b/packages/api/test/resolvers/features.test.ts @@ -2,9 +2,9 @@ import { expect } from 'chai' import * as jwt from 'jsonwebtoken' import 'mocha' import sinon, { SinonFakeTimers } from 'sinon' +import { getRepository } from '../../src/entity' import { Feature } from '../../src/entity/feature' import { User } from '../../src/entity/user' -import { getRepository } from '../../src/entity/utils' import { env } from '../../src/env' import { createTestUser, deleteTestUser } from '../db' import { graphqlRequest, request } from '../util' diff --git a/packages/api/test/resolvers/integrations.test.ts b/packages/api/test/resolvers/integrations.test.ts index 79c130294..a802a7092 100644 --- a/packages/api/test/resolvers/integrations.test.ts +++ b/packages/api/test/resolvers/integrations.test.ts @@ -4,7 +4,7 @@ import { createTestUser, deleteTestIntegrations, deleteTestUser } from '../db' import { generateFakeUuid, graphqlRequest, request } from '../util' import { SetIntegrationErrorCode } from '../../src/generated/graphql' import chai, { expect } from 'chai' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' import { Integration } from '../../src/entity/integration' import nock from 'nock' import { READWISE_API_URL } from '../../src/services/integrations/readwise' diff --git a/packages/api/test/resolvers/labels.test.ts b/packages/api/test/resolvers/labels.test.ts index 4717c820e..9868d6036 100644 --- a/packages/api/test/resolvers/labels.test.ts +++ b/packages/api/test/resolvers/labels.test.ts @@ -20,7 +20,7 @@ import { Page, PageContext, } from '../../src/elastic/types' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' import { deletePage, getPageById } from '../../src/elastic/pages' import { createPubSubClient } from '../../src/datalayer/pubsub' import { diff --git a/packages/api/test/resolvers/newsletters.test.ts b/packages/api/test/resolvers/newsletters.test.ts index 14eee11b7..54690b8f0 100644 --- a/packages/api/test/resolvers/newsletters.test.ts +++ b/packages/api/test/resolvers/newsletters.test.ts @@ -14,7 +14,7 @@ import { SubscriptionStatus, } from '../../src/generated/graphql' import 'mocha' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' describe('Newsletters API', () => { let user: User diff --git a/packages/api/test/resolvers/recent_emails.test.ts b/packages/api/test/resolvers/recent_emails.test.ts index 7d87414be..28c96d2d3 100644 --- a/packages/api/test/resolvers/recent_emails.test.ts +++ b/packages/api/test/resolvers/recent_emails.test.ts @@ -3,7 +3,7 @@ import { expect } from 'chai' import { User } from '../../src/entity/user' import { createTestUser, deleteTestUser } from '../db' import { graphqlRequest, request } from '../util' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' import { ReceivedEmail } from '../../src/entity/received_email' import { NewsletterEmail } from '../../src/entity/newsletter_email' import sinon from 'sinon' diff --git a/packages/api/test/resolvers/recent_searches.test.ts b/packages/api/test/resolvers/recent_searches.test.ts index 8598a2c6e..3217541a0 100644 --- a/packages/api/test/resolvers/recent_searches.test.ts +++ b/packages/api/test/resolvers/recent_searches.test.ts @@ -5,7 +5,7 @@ import { PageContext } from '../../src/elastic/types' import { createTestUser, deleteTestUser } from '../db' import { graphqlRequest, request } from '../util' import { createPubSubClient } from '../../src/datalayer/pubsub' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' import { SearchHistory } from '../../src/entity/search_history' describe('recent_searches resolver', () => { diff --git a/packages/api/test/resolvers/report.test.ts b/packages/api/test/resolvers/report.test.ts index f67b95ae6..d39c67c3f 100644 --- a/packages/api/test/resolvers/report.test.ts +++ b/packages/api/test/resolvers/report.test.ts @@ -5,7 +5,7 @@ import { createTestElasticPage, graphqlRequest, request } from '../util' import { ReportType } from '../../src/generated/graphql' import { ContentDisplayReport } from '../../src/entity/reports/content_display_report' import { expect } from 'chai' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' describe('Report API', () => { let user: User diff --git a/packages/api/test/resolvers/rules.test.ts b/packages/api/test/resolvers/rules.test.ts index 5a55195bb..dc6bbcf9b 100644 --- a/packages/api/test/resolvers/rules.test.ts +++ b/packages/api/test/resolvers/rules.test.ts @@ -3,7 +3,7 @@ import { expect } from 'chai' import { graphqlRequest, request } from '../util' import { User } from '../../src/entity/user' import { createTestUser, deleteTestUser } from '../db' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' import { Rule, RuleAction, RuleActionType } from '../../src/entity/rule' describe('Rules Resolver', () => { diff --git a/packages/api/test/resolvers/subscriptions.test.ts b/packages/api/test/resolvers/subscriptions.test.ts index 5ef6ff8b2..3ddb24bed 100644 --- a/packages/api/test/resolvers/subscriptions.test.ts +++ b/packages/api/test/resolvers/subscriptions.test.ts @@ -5,7 +5,7 @@ import sinonChai from 'sinon-chai' import { NewsletterEmail } from '../../src/entity/newsletter_email' import { Subscription } from '../../src/entity/subscription' import { User } from '../../src/entity/user' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' import { SubscriptionStatus, SubscriptionType, diff --git a/packages/api/test/resolvers/user_device_tokens.test.ts b/packages/api/test/resolvers/user_device_tokens.test.ts index 869969a8c..11e9730dd 100644 --- a/packages/api/test/resolvers/user_device_tokens.test.ts +++ b/packages/api/test/resolvers/user_device_tokens.test.ts @@ -11,7 +11,7 @@ import { UserDeviceToken } from '../../src/entity/user_device_tokens' import { SetDeviceTokenErrorCode } from '../../src/generated/graphql' import 'mocha' import { User } from '../../src/entity/user' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' describe('Device tokens API', () => { let authToken: string diff --git a/packages/api/test/resolvers/user_feed_article.test.ts b/packages/api/test/resolvers/user_feed_article.test.ts index 192a6c9c4..84fafcf4e 100644 --- a/packages/api/test/resolvers/user_feed_article.test.ts +++ b/packages/api/test/resolvers/user_feed_article.test.ts @@ -11,7 +11,7 @@ import { Page } from '../../src/entity/page' import { Link } from '../../src/entity/link' import { Highlight } from '../../src/entity/highlight' import 'mocha' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' import { User } from '../../src/entity/user' describe('User feed article API', () => { diff --git a/packages/api/test/resolvers/webhooks.test.ts b/packages/api/test/resolvers/webhooks.test.ts index a22ca3d8d..d07aa544b 100644 --- a/packages/api/test/resolvers/webhooks.test.ts +++ b/packages/api/test/resolvers/webhooks.test.ts @@ -5,7 +5,7 @@ import 'mocha' import { User } from '../../src/entity/user' import { WebhookEvent } from '../../src/generated/graphql' import { Webhook } from '../../src/entity/webhook' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' describe('Webhooks API', () => { let user: User diff --git a/packages/api/test/routers/auth.test.ts b/packages/api/test/routers/auth.test.ts index 48dcd8df7..a4d2f3b5e 100644 --- a/packages/api/test/routers/auth.test.ts +++ b/packages/api/test/routers/auth.test.ts @@ -5,8 +5,8 @@ import sinonChai from 'sinon-chai' import supertest from 'supertest' import { StatusType } from '../../src/datalayer/user/model' import { searchPages } from '../../src/elastic/pages' +import { getRepository } from '../../src/entity' import { User } from '../../src/entity/user' -import { getRepository } from '../../src/entity/utils' import { AuthProvider } from '../../src/routers/auth/auth_types' import { createPendingUserToken } from '../../src/routers/auth/jwt_helpers' import { diff --git a/packages/api/test/routers/emails.test.ts b/packages/api/test/routers/emails.test.ts index a625fe800..0757ed759 100644 --- a/packages/api/test/routers/emails.test.ts +++ b/packages/api/test/routers/emails.test.ts @@ -11,7 +11,7 @@ import { request } from '../util' import * as parser from '../../src/utils/parser' import * as sendNotification from '../../src/utils/sendNotification' import * as sendEmail from '../../src/utils/sendEmail' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' import { ReceivedEmail } from '../../src/entity/received_email' import * as jwt from 'jsonwebtoken' diff --git a/packages/api/test/routers/integrations.test.ts b/packages/api/test/routers/integrations.test.ts index f05a7e669..f8a1b1e82 100644 --- a/packages/api/test/routers/integrations.test.ts +++ b/packages/api/test/routers/integrations.test.ts @@ -16,9 +16,9 @@ import { Page, PageContext, } from '../../src/elastic/types' +import { getRepository } from '../../src/entity' import { Integration, IntegrationType } from '../../src/entity/integration' import { User } from '../../src/entity/user' -import { getRepository } from '../../src/entity/utils' import { env } from '../../src/env' import { getHighlightUrl } from '../../src/services/highlights' import { READWISE_API_URL } from '../../src/services/integrations/readwise' diff --git a/packages/api/test/routers/webhooks.test.ts b/packages/api/test/routers/webhooks.test.ts index 20e76606f..0dc3bbc56 100644 --- a/packages/api/test/routers/webhooks.test.ts +++ b/packages/api/test/routers/webhooks.test.ts @@ -2,7 +2,7 @@ import { createTestUser, deleteTestUser } from '../db' import { request } from '../util' import { User } from '../../src/entity/user' import 'mocha' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' import { Webhook } from '../../src/entity/webhook' import { expect } from 'chai' import nock from 'nock' diff --git a/packages/api/test/services/create_content_display_report.test.ts b/packages/api/test/services/create_content_display_report.test.ts index 6bf30d488..e9998a6f3 100644 --- a/packages/api/test/services/create_content_display_report.test.ts +++ b/packages/api/test/services/create_content_display_report.test.ts @@ -2,11 +2,8 @@ import 'mocha' import chai, { expect } from 'chai' import { createTestUser, deleteTestUser } from '../db' import sinonChai from 'sinon-chai' -import sinon from 'sinon' -import * as util from '../../src/utils/sendEmail' -import { MailDataRequired } from '@sendgrid/helpers/classes/mail' import { User } from '../../src/entity/user' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' import { ContentDisplayReport } from '../../src/entity/reports/content_display_report' import { saveContentDisplayReport } from '../../src/services/reports' import { ReportType } from '../../src/generated/graphql' diff --git a/packages/api/test/services/create_user.test.ts b/packages/api/test/services/create_user.test.ts index 42cde34ce..514b3ab90 100644 --- a/packages/api/test/services/create_user.test.ts +++ b/packages/api/test/services/create_user.test.ts @@ -11,7 +11,6 @@ import { getUserFollowers, getUserFollowing, } from '../../src/services/followers' -import { StatusType } from '../../src/datalayer/user/model' import sinonChai from 'sinon-chai' import sinon from 'sinon' import * as util from '../../src/utils/sendEmail' @@ -19,6 +18,7 @@ import { MailDataRequired } from '@sendgrid/helpers/classes/mail' import { User } from '../../src/entity/user' import { getRepository } from '../../src/entity/utils' import { Filter } from "../../src/entity/filter" +import { getRepository } from '../../src/entity' chai.use(sinonChai) diff --git a/packages/api/test/services/labels.test.ts b/packages/api/test/services/labels.test.ts index 89d9e92c6..436ab07bd 100644 --- a/packages/api/test/services/labels.test.ts +++ b/packages/api/test/services/labels.test.ts @@ -11,7 +11,7 @@ import { LinkLabel } from '../../src/entity/link_label' import { Label } from '../../src/entity/label' import { Link } from '../../src/entity/link' import { labelsLoader } from '../../src/services/labels' -import { getRepository } from '../../src/entity/utils' +import { getRepository } from '../../src/entity' import { User } from '../../src/entity/user' describe('batch get labels from linkIds', () => { diff --git a/packages/api/test/services/save_newsletter_email.test.ts b/packages/api/test/services/save_newsletter_email.test.ts index 1e369d289..6b9fcd8ff 100644 --- a/packages/api/test/services/save_newsletter_email.test.ts +++ b/packages/api/test/services/save_newsletter_email.test.ts @@ -3,11 +3,11 @@ import 'mocha' import nock from 'nock' import { createPubSubClient } from '../../src/datalayer/pubsub' import { getPageByParam } from '../../src/elastic/pages' +import { getRepository } from '../../src/entity' import { NewsletterEmail } from '../../src/entity/newsletter_email' import { ReceivedEmail } from '../../src/entity/received_email' import { Subscription } from '../../src/entity/subscription' import { User } from '../../src/entity/user' -import { getRepository } from '../../src/entity/utils' import { createNewsletterEmail } from '../../src/services/newsletters' import { SaveContext } from '../../src/services/save_email' import { saveNewsletterEmail } from '../../src/services/save_newsletter_email'