diff --git a/packages/api/src/repository/highlight.ts b/packages/api/src/repository/highlight.ts index 232037267..e38da3e0b 100644 --- a/packages/api/src/repository/highlight.ts +++ b/packages/api/src/repository/highlight.ts @@ -1,4 +1,5 @@ import { DeepPartial } from 'typeorm' +import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity' import { entityManager } from '.' import { Highlight } from '../entity/highlight' import { unescapeHtml } from '../utils/helpers' @@ -26,15 +27,22 @@ export const highlightRepository = entityManager }) }, - createAndSave( - highlight: DeepPartial, - libraryItemId: string, - userId: string + createAndSave(highlight: DeepPartial) { + return this.save(unescapeHighlight(highlight)) + }, + + updateAndSave( + highlightId: string, + highlight: QueryDeepPartialEntity ) { - return this.save({ - ...unescapeHighlight(highlight), - user: { id: userId }, - libraryItem: { id: libraryItemId }, + return this.update(highlightId, { + ...highlight, + annotation: highlight.annotation + ? unescapeHtml(highlight.annotation.toString()) + : undefined, + quote: highlight.quote + ? unescapeHtml(highlight.quote.toString()) + : undefined, }) }, }) diff --git a/packages/api/src/repository/label.ts b/packages/api/src/repository/label.ts index 1e1f2d057..8a1e615f0 100644 --- a/packages/api/src/repository/label.ts +++ b/packages/api/src/repository/label.ts @@ -57,7 +57,7 @@ export const labelRepository = entityManager.getRepository(Label).extend({ .getMany() }, - findByIds(labelIds: string[]) { + findLabelsById(labelIds: string[]) { return this.find({ where: { id: In(labelIds) }, select: ['id', 'name', 'color', 'description', 'createdAt'], diff --git a/packages/api/src/resolvers/highlight/index.ts b/packages/api/src/resolvers/highlight/index.ts index 7b58fe75c..41b7c8fa2 100644 --- a/packages/api/src/resolvers/highlight/index.ts +++ b/packages/api/src/resolvers/highlight/index.ts @@ -56,7 +56,11 @@ export const createHighlightResolver = authorized< >(async (_, { input }, { log, pubsub, uid }) => { try { const newHighlight = await createHighlight( - input, + { + ...input, + user: { id: uid }, + libraryItem: { id: input.articleId }, + }, input.articleId, uid, pubsub @@ -132,6 +136,8 @@ export const mergeHighlightResolver = authorized< mergedAnnotations.length > 0 ? mergedAnnotations.join('\n') : null, labels: mergedLabels, color, + user: { id: uid }, + libraryItem: { id: input.articleId }, } const newHighlight = await mergeHighlights( @@ -171,7 +177,11 @@ export const updateHighlightResolver = authorized< try { const updatedHighlight = await updateHighlight( input.highlightId, - input, + { + annotation: input.annotation, + html: input.html, + quote: input.quote, + }, uid, pubsub ) diff --git a/packages/api/src/resolvers/integrations/index.ts b/packages/api/src/resolvers/integrations/index.ts index 9fbe6327a..8bf1b396a 100644 --- a/packages/api/src/resolvers/integrations/index.ts +++ b/packages/api/src/resolvers/integrations/index.ts @@ -19,11 +19,11 @@ import { SetIntegrationSuccess, } from '../../generated/graphql' import { - createIntegration, findIntegration, findIntegrations, getIntegrationService, removeIntegration, + saveIntegration, updateIntegration, } from '../../services/integrations' import { analytics } from '../../utils/analytics' @@ -54,11 +54,6 @@ export const setIntegrationResolver = authorized< errorCodes: [SetIntegrationErrorCode.NotFound], } } - if (existingIntegration.user.id !== uid) { - return { - errorCodes: [SetIntegrationErrorCode.Unauthorized], - } - } integrationToSave.id = existingIntegration.id integrationToSave.taskName = existingIntegration.taskName @@ -76,7 +71,7 @@ export const setIntegrationResolver = authorized< } // save integration - const integration = await createIntegration(integrationToSave, uid) + const integration = await saveIntegration(integrationToSave, uid) if ( integrationToSave.type === IntegrationType.Export && diff --git a/packages/api/src/resolvers/labels/index.ts b/packages/api/src/resolvers/labels/index.ts index edded1e98..ad17b4b3d 100644 --- a/packages/api/src/resolvers/labels/index.ts +++ b/packages/api/src/resolvers/labels/index.ts @@ -96,7 +96,7 @@ export const createLabelResolver = authorized< } catch (error) { log.error('createLabelResolver', error) return { - errorCodes: [CreateLabelErrorCode.BadRequest], + errorCodes: [CreateLabelErrorCode.LabelAlreadyExists], } } }) @@ -168,13 +168,8 @@ export const setLabelsResolver = authorized< } else if (labelIds && labelIds.length > 0) { // for old clients that send labelIds labelsSet = await authTrx(async (tx) => { - return tx.withRepository(labelRepository).findByIds(labelIds) + return tx.withRepository(labelRepository).findLabelsById(labelIds) }) - if (labelsSet.length !== labelIds.length) { - return { - errorCodes: [SetLabelsErrorCode.NotFound], - } - } } // save labels in the library item await saveLabelsInLibraryItem(labelsSet, pageId, uid, pubsub) @@ -195,7 +190,7 @@ export const setLabelsResolver = authorized< } catch (error) { log.error('setLabelsResolver error', error) return { - errorCodes: [SetLabelsErrorCode.BadRequest], + errorCodes: [SetLabelsErrorCode.NotFound], } } } @@ -230,7 +225,7 @@ export const updateLabelResolver = authorized< if (!result.affected) { log.error('failed to update') return { - errorCodes: [UpdateLabelErrorCode.BadRequest], + errorCodes: [UpdateLabelErrorCode.NotFound], } } @@ -268,7 +263,7 @@ export const setLabelsForHighlightResolver = authorized< } else if (labelIds && labelIds.length > 0) { // for old clients that send labelIds labelsSet = await authTrx(async (tx) => { - return tx.withRepository(labelRepository).findByIds(labelIds) + return tx.withRepository(labelRepository).findLabelsById(labelIds) }) if (labelsSet.length !== labelIds.length) { return { @@ -296,7 +291,7 @@ export const setLabelsForHighlightResolver = authorized< } catch (error) { log.error('setLabelsForHighlightResolver error', error) return { - errorCodes: [SetLabelsErrorCode.BadRequest], + errorCodes: [SetLabelsErrorCode.NotFound], } } }) diff --git a/packages/api/src/resolvers/newsletters/index.ts b/packages/api/src/resolvers/newsletters/index.ts index eea6bb4f1..49f13b090 100644 --- a/packages/api/src/resolvers/newsletters/index.ts +++ b/packages/api/src/resolvers/newsletters/index.ts @@ -12,6 +12,7 @@ import { NewsletterEmailsErrorCode, NewsletterEmailsSuccess, } from '../../generated/graphql' +import { getRepository } from '../../repository' import { createNewsletterEmail, deleteNewsletterEmail, @@ -78,7 +79,7 @@ export const deleteNewsletterEmailResolver = authorized< DeleteNewsletterEmailSuccess, DeleteNewsletterEmailError, MutationDeleteNewsletterEmailArgs ->(async (_parent, args, { authTrx, uid, log }) => { +>(async (_parent, args, { uid, log }) => { analytics.track({ userId: uid, event: 'newsletter_email_address_deleted', @@ -88,14 +89,13 @@ export const deleteNewsletterEmailResolver = authorized< }) try { - const newsletterEmail = await authTrx((t) => - t.getRepository(NewsletterEmail).findOne({ - where: { - id: args.newsletterEmailId, - }, - relations: ['user', 'subscriptions'], - }) - ) + const newsletterEmail = await getRepository(NewsletterEmail).findOne({ + where: { + id: args.newsletterEmailId, + user: { id: uid }, + }, + relations: ['user', 'subscriptions'], + }) if (!newsletterEmail) { return { @@ -120,8 +120,8 @@ export const deleteNewsletterEmailResolver = authorized< errorCodes: [DeleteNewsletterEmailErrorCode.NotFound], } } - } catch (e) { - log.info(e) + } catch (error) { + log.error('deleteNewsletterEmailResolver', error) return { errorCodes: [DeleteNewsletterEmailErrorCode.BadRequest], diff --git a/packages/api/src/resolvers/save/index.ts b/packages/api/src/resolvers/save/index.ts index 6051ac042..2acf0ce3b 100644 --- a/packages/api/src/resolvers/save/index.ts +++ b/packages/api/src/resolvers/save/index.ts @@ -67,7 +67,7 @@ export const saveUrlResolver = authorized< return { errorCodes: [SaveErrorCode.Unauthorized] } } - return saveUrl(ctx, user, input) + return saveUrl(input, user) }) export const saveFileResolver = authorized< diff --git a/packages/api/src/routers/svc/content.ts b/packages/api/src/routers/svc/content.ts index de6c80b10..712ba7af1 100644 --- a/packages/api/src/routers/svc/content.ts +++ b/packages/api/src/routers/svc/content.ts @@ -7,7 +7,10 @@ import { readPushSubscription } from '../../pubsub' import { authTrx } from '../../repository' import { libraryItemRepository } from '../../repository/library_item' import { updateLibraryItem } from '../../services/library_item' -import { setFileUploadComplete } from '../../services/upload_file' +import { + findUploadFileById, + setFileUploadComplete, +} from '../../services/upload_file' import { logger } from '../../utils/logger' interface UpdateContentMessage { @@ -54,18 +57,27 @@ export function contentServiceRouter() { return } - const libraryItem = await authTrx(async (tx) => - tx - .withRepository(libraryItemRepository) - .createQueryBuilder('item') - .innerJoinAndSelect('item.user', 'user') - .innerJoinAndSelect('item.uploadFile', 'file') - .where('item.fileId = :fileId', { fileId }) - .getOne() + const uploadFile = await findUploadFileById(fileId) + if (!uploadFile) { + logger.info('No file found') + res.status(404).send('No file found') + return + } + + const libraryItem = await authTrx( + async (tx) => + tx + .withRepository(libraryItemRepository) + .createQueryBuilder('item') + .innerJoinAndSelect('item.uploadFile', 'file') + .where('file.id = :fileId', { fileId }) + .getOne(), + undefined, + uploadFile.user.id ) if (!libraryItem) { logger.info('No upload file found for id:', fileId) - res.status(400).send('Bad Request') + res.status(404).send('Bad Request') return } @@ -82,7 +94,7 @@ export function contentServiceRouter() { try { const uploadFileData = await setFileUploadComplete( fileId, - libraryItem.user.id + uploadFile.user.id ) logger.info('updated uploadFileData', uploadFileData) } catch (error) { @@ -92,7 +104,7 @@ export function contentServiceRouter() { const result = await updateLibraryItem( libraryItem.id, itemToUpdate, - libraryItem.user.id + uploadFile.user.id ) logger.info( 'Updating library item text', diff --git a/packages/api/src/routers/svc/email_attachment.ts b/packages/api/src/routers/svc/email_attachment.ts index 84dea297b..255657e79 100644 --- a/packages/api/src/routers/svc/email_attachment.ts +++ b/packages/api/src/routers/svc/email_attachment.ts @@ -61,14 +61,17 @@ export function emailAttachmentRouter() { }) try { - const uploadFileData = await authTrx((tx) => - tx.getRepository(UploadFile).save({ - url: '', - userId: user.id, - fileName: fileName, - status: UploadFileStatus.Initialized, - contentType: contentType, - }) + const uploadFileData = await authTrx( + (tx) => + tx.getRepository(UploadFile).save({ + url: '', + userId: user.id, + fileName: fileName, + status: UploadFileStatus.Initialized, + contentType: contentType, + }), + undefined, + user.id ) if (uploadFileData.id) { @@ -165,7 +168,7 @@ export function emailAttachmentRouter() { const pageId = await createLibraryItem(articleToSave, user.id) // update received email type - await updateReceivedEmail(receivedEmailId, 'article') + await updateReceivedEmail(receivedEmailId, 'article', user.id) res.send({ id: pageId }) } catch (err) { diff --git a/packages/api/src/routers/svc/integrations.ts b/packages/api/src/routers/svc/integrations.ts index 70789406a..3e89b5d13 100644 --- a/packages/api/src/routers/svc/integrations.ts +++ b/packages/api/src/routers/svc/integrations.ts @@ -5,12 +5,15 @@ import { stringify } from 'csv-stringify' import express from 'express' import { DateTime } from 'luxon' import { v4 as uuidv4 } from 'uuid' -import { Integration, IntegrationType } from '../../entity/integration' +import { IntegrationType } from '../../entity/integration' import { LibraryItem } from '../../entity/library_item' import { EntityType, readPushSubscription } from '../../pubsub' -import { getRepository } from '../../repository' import { Claims } from '../../resolvers/types' -import { getIntegrationService } from '../../services/integrations' +import { + findIntegration, + getIntegrationService, + updateIntegration, +} from '../../services/integrations' import { findLibraryItemById, searchLibraryItems, @@ -65,12 +68,14 @@ export function integrationsServiceRouter() { return } - const integration = await getRepository(Integration).findOneBy({ - user: { id: userId }, - name: req.params.integrationName.toUpperCase(), - type: IntegrationType.Export, - enabled: true, - }) + const integration = await findIntegration( + { + name: req.params.integrationName.toUpperCase(), + type: IntegrationType.Export, + enabled: true, + }, + userId + ) if (!integration) { logger.info('No active integration found for user', { userId }) res.status(200).send('No integration found') @@ -158,9 +163,13 @@ export function integrationsServiceRouter() { } } // delete task name if completed - await getRepository(Integration).update(integration.id, { - taskName: null, - }) + await updateIntegration( + integration.id, + { + taskName: null, + }, + userId + ) } else { logger.info('unknown action', { action }) res.status(200).send('Unknown action') @@ -197,12 +206,14 @@ export function integrationsServiceRouter() { let writeStream: NodeJS.WritableStream | undefined try { const userId = claims.uid - const integration = await getRepository(Integration).findOneBy({ - user: { id: userId }, - id: req.body.integrationId, - enabled: true, - type: IntegrationType.Import, - }) + const integration = await findIntegration( + { + id: req.body.integrationId, + enabled: true, + type: IntegrationType.Import, + }, + userId + ) if (!integration) { logger.info('No active integration found for user', { userId }) return res.status(200).send('No integration found') @@ -270,10 +281,14 @@ export function integrationsServiceRouter() { } // update the integration's syncedAt and remove taskName - await getRepository(Integration).update(integration.id, { - syncedAt: new Date(syncedAt), - taskName: null, - }) + await updateIntegration( + integration.id, + { + syncedAt: new Date(syncedAt), + taskName: null, + }, + userId + ) } catch (err) { logger.error('import pages from integration failed', err) return res.status(500).send(err) diff --git a/packages/api/src/routers/svc/newsletters.ts b/packages/api/src/routers/svc/newsletters.ts index a5668d3bc..500f546db 100644 --- a/packages/api/src/routers/svc/newsletters.ts +++ b/packages/api/src/routers/svc/newsletters.ts @@ -1,6 +1,6 @@ import express from 'express' import { SubscriptionStatus } from '../../generated/graphql' -import { createPubSubClient, readPushSubscription } from '../../pubsub' +import { readPushSubscription } from '../../pubsub' import { findNewsletterEmail, updateConfirmationCode, @@ -111,16 +111,12 @@ export function newsletterServiceRouter() { return res.status(200).send('Not Found') } - const saveCtx = { - pubsub: createPubSubClient(), - uid: newsletterEmail.user.id, - } if (isUrl(data.title)) { // save url if the title is a parsable url const result = await saveUrlFromEmail( - saveCtx, data.title, - data.receivedEmailId + data.receivedEmailId, + newsletterEmail.user.id ) if (!result) { return res.status(500).send('Error saving url from email') @@ -146,7 +142,11 @@ export function newsletterServiceRouter() { } // update received email type - await updateReceivedEmail(data.receivedEmailId, 'article') + await updateReceivedEmail( + data.receivedEmailId, + 'article', + newsletterEmail.user.id + ) } catch (e) { logger.error(e) if (e instanceof SyntaxError) { diff --git a/packages/api/src/routers/svc/reminders.ts b/packages/api/src/routers/svc/reminders.ts index 66e68f894..a4a5f206d 100644 --- a/packages/api/src/routers/svc/reminders.ts +++ b/packages/api/src/routers/svc/reminders.ts @@ -1,12 +1,3 @@ -import { MulticastMessage } from 'firebase-admin/messaging' -import { appDataSource } from '../../data_source' -import { updatePage } from '../../elastic/pages' -import { UserDeviceToken } from '../../entity/user_device_tokens' -import { homePageURL } from '../../env' -import { ContentReader } from '../../generated/graphql' -import { createPubSubClient } from '../../pubsub' -import { setClaims } from '../../repository' - interface PageToNotify { title: string url: string diff --git a/packages/api/src/routers/svc/webhooks.ts b/packages/api/src/routers/svc/webhooks.ts index 97d17590c..d11b45b95 100644 --- a/packages/api/src/routers/svc/webhooks.ts +++ b/packages/api/src/routers/svc/webhooks.ts @@ -5,7 +5,7 @@ import axios, { Method } from 'axios' import express from 'express' import { Webhook } from '../../entity/webhook' import { readPushSubscription } from '../../pubsub' -import { getRepository } from '../../repository' +import { authTrx } from '../../repository' import { logger } from '../../utils/logger' export function webhooksServiceRouter() { @@ -38,12 +38,18 @@ export function webhooksServiceRouter() { // example: PAGE_CREATED const eventType = `${type}_${req.params.action}`.toUpperCase() - const webhooks = await getRepository(Webhook) - .createQueryBuilder() - .where('user_id = :userId', { userId }) - .andWhere(':eventType = ANY(event_types)', { eventType }) - .andWhere('enabled = true') - .getMany() + const webhooks = await authTrx( + (t) => + t + .getRepository(Webhook) + .createQueryBuilder() + .where('user_id = :userId', { userId }) + .andWhere(':eventType = ANY(event_types)', { eventType }) + .andWhere('enabled = true') + .getMany(), + undefined, + userId + ) if (webhooks.length <= 0) { logger.info( diff --git a/packages/api/src/routers/user_router.ts b/packages/api/src/routers/user_router.ts index f7e4692fc..688c4b6b8 100644 --- a/packages/api/src/routers/user_router.ts +++ b/packages/api/src/routers/user_router.ts @@ -2,9 +2,8 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import cors from 'cors' import express from 'express' -import { getRepository } from '../repository' -import { User } from '../entity/user' import { env } from '../env' +import { userRepository } from '../repository/user' import { getClaimsByToken, getTokenByRequest } from '../utils/auth' import { corsConfig } from '../utils/corsConfig' import { logger } from '../utils/logger' @@ -40,7 +39,7 @@ export function userRouter() { return } try { - const user = await getRepository(User).findOneBy({ id: claims.uid }) + const user = await userRepository.findOneBy({ id: claims.uid }) if (!user) { res.status(400).send('Bad Request') return diff --git a/packages/api/src/services/highlights.ts b/packages/api/src/services/highlights.ts index 68215663b..0eca20b75 100644 --- a/packages/api/src/services/highlights.ts +++ b/packages/api/src/services/highlights.ts @@ -1,12 +1,15 @@ import { diff_match_patch } from 'diff-match-patch' import { DeepPartial } from 'typeorm' +import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity' import { Highlight } from '../entity/highlight' import { homePageURL } from '../env' import { createPubSubClient, EntityType } from '../pubsub' import { authTrx } from '../repository' import { highlightRepository } from '../repository/highlight' -type HighlightEvent = DeepPartial & { pageId: string } +type HighlightEvent = { id: string; pageId: string } +type CreateHighlightEvent = DeepPartial & HighlightEvent +type UpdateHighlightEvent = QueryDeepPartialEntity & HighlightEvent export const getHighlightLocation = (patch: string): number | undefined => { const dmp = new diff_match_patch() @@ -24,16 +27,13 @@ export const createHighlight = async ( pubsub = createPubSubClient() ) => { const newHighlight = await authTrx( - async (tx) => { - return tx - .withRepository(highlightRepository) - .createAndSave(highlight, libraryItemId, userId) - }, + async (tx) => + tx.withRepository(highlightRepository).createAndSave(highlight), undefined, userId ) - await pubsub.entityCreated( + await pubsub.entityCreated( EntityType.HIGHLIGHT, { ...newHighlight, pageId: libraryItemId }, userId @@ -54,10 +54,10 @@ export const mergeHighlights = async ( await highlightRepo.delete(highlightsToRemove) - return highlightRepo.createAndSave(highlightToAdd, libraryItemId, userId) + return highlightRepo.createAndSave(highlightToAdd) }) - await pubsub.entityCreated( + await pubsub.entityCreated( EntityType.HIGHLIGHT, { ...newHighlight, pageId: libraryItemId }, userId @@ -68,23 +68,20 @@ export const mergeHighlights = async ( export const updateHighlight = async ( highlightId: string, - highlight: DeepPartial, + highlight: QueryDeepPartialEntity, userId: string, pubsub = createPubSubClient() ) => { const updatedHighlight = await authTrx(async (tx) => { const highlightRepo = tx.withRepository(highlightRepository) - await highlightRepo.save({ - ...highlight, - id: highlightId, - }) + await highlightRepo.updateAndSave(highlightId, highlight) return highlightRepo.findOneByOrFail({ id: highlightId, }) }) - await pubsub.entityUpdated( + await pubsub.entityUpdated( EntityType.HIGHLIGHT, { ...highlight, id: highlightId, pageId: updatedHighlight.libraryItem.id }, userId diff --git a/packages/api/src/services/integrations/index.ts b/packages/api/src/services/integrations/index.ts index 1a7f92816..281f75530 100644 --- a/packages/api/src/services/integrations/index.ts +++ b/packages/api/src/services/integrations/index.ts @@ -62,7 +62,7 @@ export const findIntegrations = async ( ) } -export const createIntegration = async ( +export const saveIntegration = async ( integration: DeepPartial, userId: string ) => { diff --git a/packages/api/src/services/newsletters.ts b/packages/api/src/services/newsletters.ts index be3843156..dd57b00e9 100644 --- a/packages/api/src/services/newsletters.ts +++ b/packages/api/src/services/newsletters.ts @@ -5,7 +5,7 @@ import { CreateNewsletterEmailErrorCode, SubscriptionStatus, } from '../generated/graphql' -import { authTrx } from '../repository' +import { getRepository } from '../repository' import { userRepository } from '../repository/user' import addressparser = require('nodemailer/lib/addressparser') @@ -33,44 +33,34 @@ export const createNewsletterEmail = async ( // generate a random email address with username prefix const emailAddress = createRandomEmailAddress(user.profile.username, 8) - return authTrx( - (t) => - t.getRepository(NewsletterEmail).save({ - address: emailAddress, - user, - confirmationCode, - }), - undefined, - userId - ) + return getRepository(NewsletterEmail).save({ + address: emailAddress, + user, + confirmationCode, + }) } export const getNewsletterEmails = async ( userId: string ): Promise => { - return authTrx((t) => - t - .getRepository(NewsletterEmail) - .createQueryBuilder('newsletter_email') - .leftJoinAndSelect('newsletter_email.user', 'user') - .leftJoinAndSelect( - 'newsletter_email.subscriptions', - 'subscriptions', - 'subscriptions.status = :status', - { - status: SubscriptionStatus.Active, - } - ) - .where('newsletter_email.user = :userId', { userId }) - .orderBy('newsletter_email.createdAt', 'DESC') - .getMany() - ) + return getRepository(NewsletterEmail) + .createQueryBuilder('newsletter_email') + .leftJoinAndSelect('newsletter_email.user', 'user') + .leftJoinAndSelect( + 'newsletter_email.subscriptions', + 'subscriptions', + 'subscriptions.status = :status', + { + status: SubscriptionStatus.Active, + } + ) + .where('newsletter_email.user = :userId', { userId }) + .orderBy('newsletter_email.createdAt', 'DESC') + .getMany() } export const deleteNewsletterEmail = async (id: string): Promise => { - const result = await authTrx((t) => - t.getRepository(NewsletterEmail).delete(id) - ) + const result = await getRepository(NewsletterEmail).delete(id) return !!result.affected } @@ -80,16 +70,13 @@ export const updateConfirmationCode = async ( confirmationCode: string ): Promise => { const address = parsedAddress(emailAddress) - const result = await authTrx((t) => - t - .getRepository(NewsletterEmail) - .createQueryBuilder() - .where('address ILIKE :address', { address }) - .update({ - confirmationCode: confirmationCode, - }) - .execute() - ) + const result = await getRepository(NewsletterEmail) + .createQueryBuilder() + .where('address ILIKE :address', { address }) + .update({ + confirmationCode: confirmationCode, + }) + .execute() return !!result.affected } @@ -98,14 +85,11 @@ export const findNewsletterEmail = async ( emailAddress: string ): Promise => { const address = parsedAddress(emailAddress) - return authTrx((t) => - t - .getRepository(NewsletterEmail) - .createQueryBuilder('newsletter_email') - .innerJoinAndSelect('newsletter_email.user', 'user') - .where('address ILIKE :address', { address }) - .getOne() - ) + return getRepository(NewsletterEmail) + .createQueryBuilder('newsletter_email') + .innerJoinAndSelect('newsletter_email.user', 'user') + .where('address ILIKE :address', { address }) + .getOne() } const createRandomEmailAddress = (userName: string, length: number): string => { @@ -126,9 +110,5 @@ export const getNewsletterEmailById = async ( id: string, userId: string ): Promise => { - return authTrx( - (t) => t.getRepository(NewsletterEmail).findOneBy({ id }), - undefined, - userId - ) + return getRepository(NewsletterEmail).findOneBy({ id, user: { id: userId } }) } diff --git a/packages/api/src/services/received_emails.ts b/packages/api/src/services/received_emails.ts index 7c29d573f..98930d9fa 100644 --- a/packages/api/src/services/received_emails.ts +++ b/packages/api/src/services/received_emails.ts @@ -10,22 +10,46 @@ export const saveReceivedEmail = async ( userId: string, type: 'article' | 'non-article' = 'non-article' ): Promise => { - return authTrx((t) => - t.getRepository(ReceivedEmail).save({ - from, - to, - subject, - text, - html, - type, - user: { id: userId }, - }) + return authTrx( + (t) => + t.getRepository(ReceivedEmail).save({ + from, + to, + subject, + text, + html, + type, + user: { id: userId }, + }), + undefined, + userId ) } export const updateReceivedEmail = async ( id: string, - type: 'article' | 'non-article' + type: 'article' | 'non-article', + userId?: string ) => { - return authTrx((t) => t.getRepository(ReceivedEmail).update(id, { type })) + return authTrx( + (t) => t.getRepository(ReceivedEmail).update(id, { type }), + undefined, + userId + ) +} + +export const deleteReceivedEmail = async (id: string, userId?: string) => { + return authTrx( + (t) => t.getRepository(ReceivedEmail).delete(id), + undefined, + userId + ) +} + +export const findReceivedEmailById = async (id: string, userId?: string) => { + return authTrx( + (t) => t.getRepository(ReceivedEmail).findOneBy({ id }), + undefined, + userId + ) } diff --git a/packages/api/src/services/save_url.ts b/packages/api/src/services/save_url.ts index 419c726de..0f14b1658 100644 --- a/packages/api/src/services/save_url.ts +++ b/packages/api/src/services/save_url.ts @@ -1,26 +1,18 @@ import { User } from '../entity/user' import { homePageURL } from '../env' import { SaveErrorCode, SaveResult, SaveUrlInput } from '../generated/graphql' -import { PubsubClient } from '../pubsub' import { userRepository } from '../repository/user' import { logger } from '../utils/logger' import { createPageSaveRequest } from './create_page_save_request' -interface SaveContext { - pubsub: PubsubClient - uid: string -} - export const saveUrl = async ( - ctx: SaveContext, - user: User, - input: SaveUrlInput + input: SaveUrlInput, + user: User ): Promise => { try { const pageSaveRequest = await createPageSaveRequest({ ...input, - userId: ctx.uid, - pubsub: ctx.pubsub, + userId: user.id, articleSavingRequestId: input.clientRequestId, state: input.state || undefined, labels: input.labels || undefined, @@ -46,22 +38,25 @@ export const saveUrl = async ( } export const saveUrlFromEmail = async ( - ctx: SaveContext, url: string, - clientRequestId: string + clientRequestId: string, + userId: string ): Promise => { const user = await userRepository.findOneBy({ - id: ctx.uid, + id: userId, }) if (!user) { return false } - const result = await saveUrl(ctx, user, { - url, - clientRequestId, - source: 'email', - }) + const result = await saveUrl( + { + url, + clientRequestId, + source: 'email', + }, + user + ) if (result.__typename === 'SaveError') { return false } diff --git a/packages/api/src/services/upload_file.ts b/packages/api/src/services/upload_file.ts index 863e457de..c281a3384 100644 --- a/packages/api/src/services/upload_file.ts +++ b/packages/api/src/services/upload_file.ts @@ -2,7 +2,12 @@ import { UploadFile } from '../entity/upload_file' import { authTrx, getRepository } from '../repository' export const findUploadFileById = async (id: string) => { - return getRepository(UploadFile).findOneBy({ id }) + return getRepository(UploadFile).findOne({ + where: { id }, + relations: { + user: true, + }, + }) } export const setFileUploadComplete = async (id: string, userId?: string) => { diff --git a/packages/api/src/services/user_device_tokens.ts b/packages/api/src/services/user_device_tokens.ts index c852262c3..1f991899a 100644 --- a/packages/api/src/services/user_device_tokens.ts +++ b/packages/api/src/services/user_device_tokens.ts @@ -19,10 +19,13 @@ export const findDeviceTokenByToken = async ( export const findDeviceTokensByUserId = async ( userId: string ): Promise => { - return authTrx((t) => - t.getRepository(UserDeviceToken).findBy({ - user: { id: userId }, - }) + return authTrx( + (t) => + t.getRepository(UserDeviceToken).findBy({ + user: { id: userId }, + }), + undefined, + userId ) } diff --git a/packages/api/src/services/webhook.ts b/packages/api/src/services/webhook.ts index 04c88dea5..40152adae 100644 --- a/packages/api/src/services/webhook.ts +++ b/packages/api/src/services/webhook.ts @@ -25,3 +25,19 @@ export const createWebhook = async ( userId ) } + +export const findWebhooks = async (userId?: string) => { + return authTrx( + (tx) => tx.getRepository(Webhook).findBy({ user: { id: userId } }), + undefined, + userId + ) +} + +export const findWebhookById = async (id: string, userId?: string) => { + return authTrx( + (tx) => tx.getRepository(Webhook).findBy({ id }), + undefined, + userId + ) +} diff --git a/packages/api/test/db.ts b/packages/api/test/db.ts index 408d8903f..06f65f75b 100644 --- a/packages/api/test/db.ts +++ b/packages/api/test/db.ts @@ -11,8 +11,10 @@ import { getRepository } from '../src/repository' import { userRepository } from '../src/repository/user' import { createUser } from '../src/services/create_user' import { Filter } from "../src/entity/filter" +import { saveLabelsInLibraryItem } from '../src/services/labels' import { createLibraryItem } from '../src/services/library_item' import { createDeviceToken } from '../src/services/user_device_tokens' +import { generateFakeUuid } from './util' const runMigrations = async () => { const migrationDirectory = __dirname + '/../../db/migrations' @@ -133,10 +135,14 @@ export const createTestLibraryItem = async ( user: { id: userId }, title: 'test title', originalContent: '

test content

', - originalUrl: 'https://blog.omnivore.app/test-url', + originalUrl: `https://blog.omnivore.app/test-url-${generateFakeUuid()}`, slug: 'test-with-omnivore', - labels, } - return createLibraryItem(item, userId) + const createdItem = await createLibraryItem(item, userId) + if (labels) { + await saveLabelsInLibraryItem(labels, createdItem.id, userId) + } + + return createdItem } diff --git a/packages/api/test/resolvers/highlight.test.ts b/packages/api/test/resolvers/highlight.test.ts index 4f8127d3b..5ea84002e 100644 --- a/packages/api/test/resolvers/highlight.test.ts +++ b/packages/api/test/resolvers/highlight.test.ts @@ -1,16 +1,13 @@ -import { createTestLibraryItem, createTestUser } from '../db' -import { - generateFakeUuid, - graphqlRequest, - request, -} from '../util' import * as chai from 'chai' import { expect } from 'chai' +import chaiString from 'chai-string' import 'mocha' import { User } from '../../src/entity/user' -import chaiString from 'chai-string' +import { createHighlight } from '../../src/services/highlights' import { updateLibraryItem } from '../../src/services/library_item' import { deleteUser } from '../../src/services/user' +import { createTestLibraryItem, createTestUser } from '../db' +import { generateFakeUuid, graphqlRequest, request } from '../util' chai.use(chaiString) @@ -140,7 +137,7 @@ const updateHighlightQuery = ({ describe('Highlights API', () => { let authToken: string let user: User - let pageId: string + let itemId: string before(async () => { // create test user and login @@ -150,7 +147,7 @@ describe('Highlights API', () => { .send({ fakeEmail: user.email }) authToken = res.body.authToken - pageId = (await createTestLibraryItem(user.id)).id + itemId = (await createTestLibraryItem(user.id)).id }) after(async () => { @@ -165,7 +162,7 @@ describe('Highlights API', () => { const highlightPositionAnchorIndex = 15 const html = '

test

' const query = createHighlightQuery( - pageId, + itemId, highlightId, shortHighlightId, highlightPositionPercent, @@ -192,7 +189,7 @@ describe('Highlights API', () => { const highlightPositionPercent = 50.0 const highlightPositionAnchorIndex = 25 const query = createHighlightQuery( - pageId, + itemId, newHighlightId, newShortHighlightId, highlightPositionPercent, @@ -214,7 +211,7 @@ describe('Highlights API', () => { // create test highlight highlightId = generateFakeUuid() const shortHighlightId = '_short_id_1' - const query = createHighlightQuery(pageId, highlightId, shortHighlightId) + const query = createHighlightQuery(itemId, highlightId, shortHighlightId) await graphqlRequest(query, authToken).expect(200) }) @@ -224,7 +221,7 @@ describe('Highlights API', () => { const highlightPositionPercent = 50.0 const highlightPositionAnchorIndex = 25 const query = mergeHighlightQuery( - pageId, + itemId, newHighlightId, newShortHighlightId, [highlightId], @@ -248,23 +245,16 @@ describe('Highlights API', () => { before(async () => { // create test highlight - highlightId = generateFakeUuid() - await updateLibraryItem( - pageId, + const highlight = await createHighlight( { - highlights: [ - { - id: highlightId, - shortId: '_short_id_3', - annotation: '', - patch: '', - quote: '', - user, - }, - ], + libraryItem: { id: itemId }, + shortId: '_short_id_3', + user, }, + itemId, user.id ) + highlightId = highlight.id }) it('updates the quote when the quote is in HTML format when the annotation has HTML reserved characters', async () => { diff --git a/packages/api/test/resolvers/integrations.test.ts b/packages/api/test/resolvers/integrations.test.ts index 5dbbc8a54..c170dedb4 100644 --- a/packages/api/test/resolvers/integrations.test.ts +++ b/packages/api/test/resolvers/integrations.test.ts @@ -6,9 +6,9 @@ import { Integration } from '../../src/entity/integration' import { User } from '../../src/entity/user' import { SetIntegrationErrorCode } from '../../src/generated/graphql' import { - createIntegration, deleteIntegrations, findIntegration, + saveIntegration, updateIntegration, } from '../../src/services/integrations' import { READWISE_API_URL } from '../../src/services/integrations/readwise' @@ -168,11 +168,14 @@ describe('Integrations resolvers', () => { before(async () => { otherUser = await createTestUser('otherUser') - existingIntegration = await createIntegration({ - user: { id: otherUser.id }, - name: 'READWISE', - token: 'fakeToken', - }, otherUser.id) + existingIntegration = await saveIntegration( + { + user: { id: otherUser.id }, + name: 'READWISE', + token: 'fakeToken', + }, + otherUser.id + ) integrationId = existingIntegration.id }) @@ -187,18 +190,21 @@ describe('Integrations resolvers', () => { authToken ) expect(res.body.data.setIntegration.errorCodes).to.eql([ - SetIntegrationErrorCode.Unauthorized, + SetIntegrationErrorCode.NotFound, ]) }) }) context('when integration belongs to the user', () => { before(async () => { - existingIntegration = await createIntegration({ - user: { id: loginUser.id }, - name: 'READWISE', - token: 'fakeToken', - }, loginUser.id) + existingIntegration = await saveIntegration( + { + user: { id: loginUser.id }, + name: 'READWISE', + token: 'fakeToken', + }, + loginUser.id + ) integrationId = existingIntegration.id }) @@ -294,11 +300,14 @@ describe('Integrations resolvers', () => { let existingIntegration: Integration before(async () => { - existingIntegration = await createIntegration({ - user: { id: loginUser.id }, - name: 'READWISE', - token: 'fakeToken', - }, loginUser.id) + existingIntegration = await saveIntegration( + { + user: { id: loginUser.id }, + name: 'READWISE', + token: 'fakeToken', + }, + loginUser.id + ) }) after(async () => { @@ -340,12 +349,15 @@ describe('Integrations resolvers', () => { let existingIntegration: Integration beforeEach(async () => { - existingIntegration = await createIntegration({ - user: { id: loginUser.id }, - name: 'READWISE', - token: 'fakeToken', - taskName: 'some task name', - }, loginUser.id) + existingIntegration = await saveIntegration( + { + user: { id: loginUser.id }, + name: 'READWISE', + token: 'fakeToken', + taskName: 'some task name', + }, + loginUser.id + ) }) it('deletes the integration and cloud task', async () => { @@ -383,11 +395,14 @@ describe('Integrations resolvers', () => { context('when integration exists', () => { before(async () => { - existingIntegration = await createIntegration({ - user: { id: loginUser.id }, - name: 'POCKET', - token: 'fakeToken', - }, loginUser.id) + existingIntegration = await saveIntegration( + { + user: { id: loginUser.id }, + name: 'POCKET', + token: 'fakeToken', + }, + loginUser.id + ) }) after(async () => { diff --git a/packages/api/test/resolvers/labels.test.ts b/packages/api/test/resolvers/labels.test.ts index 369f96071..1bc1a3e74 100644 --- a/packages/api/test/resolvers/labels.test.ts +++ b/packages/api/test/resolvers/labels.test.ts @@ -43,8 +43,8 @@ describe('Labels API', () => { before(async () => { // create testing labels - const label1 = await createLabel(user.id, 'label_1', '#ffffff') - const label2 = await createLabel(user.id, 'label_2', '#eeeeee') + const label1 = await createLabel('label_1', '#ffffff', user.id) + const label2 = await createLabel('label_2', '#eeeeee', user.id) labels = [label1, label2] }) @@ -159,7 +159,7 @@ describe('Labels API', () => { let existingLabel: Label before(async () => { - existingLabel = await createLabel(user.id, 'label3', '#ffffff') + existingLabel = await createLabel('label3', '#ffffff', user.id) name = existingLabel.name }) @@ -219,9 +219,9 @@ describe('Labels API', () => { context('when label is not used', () => { before(async () => { toDeleteLabel = await createLabel( - user.id, 'label not in use', - '#ffffff' + '#ffffff', + user.id ) labelId = toDeleteLabel.id }) @@ -237,7 +237,7 @@ describe('Labels API', () => { let item: LibraryItem before(async () => { - toDeleteLabel = await createLabel(user.id, 'page label', '#ffffff') + toDeleteLabel = await createLabel('page label', '#ffffff', user.id) labelId = toDeleteLabel.id item = await createTestLibraryItem(user.id, [toDeleteLabel]) }) @@ -261,9 +261,9 @@ describe('Labels API', () => { before(async () => { item = await createTestLibraryItem(user.id) toDeleteLabel = await createLabel( - user.id, 'highlight label', - '#ffffff' + '#ffffff', + user.id ) labelId = toDeleteLabel.id const highlight: DeepPartial = { @@ -326,8 +326,8 @@ describe('Labels API', () => { before(async () => { // create testing labels - const label1 = await createLabel(user.id, 'label_1', '#ffffff') - const label2 = await createLabel(user.id, 'label_2', '#eeeeee') + const label1 = await createLabel('label_1', '#ffffff', user.id) + const label2 = await createLabel('label_2', '#eeeeee', user.id) labels = [label1, label2] item = await createTestLibraryItem(user.id) }) @@ -454,7 +454,7 @@ describe('Labels API', () => { let toUpdateLabel: Label before(async () => { - toUpdateLabel = await createLabel(user.id, 'label5', '#ffffff') + toUpdateLabel = await createLabel('label5', '#ffffff', user.id) labelId = toUpdateLabel.id name = 'Updated label' color = '#aabbcc' @@ -529,8 +529,8 @@ describe('Labels API', () => { before(async () => { // create testing labels - const label1 = await createLabel(user.id, 'label_1', '#ffffff') - const label2 = await createLabel(user.id, 'label_2', '#eeeeee') + const label1 = await createLabel('label_1', '#ffffff', user.id) + const label2 = await createLabel('label_2', '#eeeeee', user.id) labels = [label1, label2] item = await createTestLibraryItem(user.id) }) @@ -572,15 +572,14 @@ describe('Labels API', () => { context('when labels exists', () => { before(async () => { - highlightId = generateFakeUuid() const highlight: DeepPartial = { id: highlightId, patch: 'test patch', quote: 'test quote', - shortId: 'test shortId', + shortId: generateFakeUuid(), user, } - await createHighlight(highlight, item.id, user.id) + highlightId = (await createHighlight(highlight, item.id, user.id)).id labelIds = [labels[0].id, labels[1].id] }) @@ -594,15 +593,13 @@ describe('Labels API', () => { context('when labels not exist', () => { before(async () => { - highlightId = generateFakeUuid() const highlight: DeepPartial = { - id: highlightId, patch: 'test patch', quote: 'test quote', - shortId: 'test shortId', + shortId: generateFakeUuid(), user, } - await createHighlight(highlight, item.id, user.id) + highlightId = (await createHighlight(highlight, item.id, user.id)).id labelIds = [generateFakeUuid(), generateFakeUuid()] }) @@ -657,7 +654,7 @@ describe('Labels API', () => { before(async () => { // create testing labels for (let i = 0; i < 5; i++) { - const label = await createLabel(user.id, `label_${i}`, '#ffffff') + const label = await createLabel(`label_${i}`, '#ffffff', user.id) labels.push(label) } }) diff --git a/packages/api/test/resolvers/recent_emails.test.ts b/packages/api/test/resolvers/recent_emails.test.ts index 04b2c658c..422f27a37 100644 --- a/packages/api/test/resolvers/recent_emails.test.ts +++ b/packages/api/test/resolvers/recent_emails.test.ts @@ -5,6 +5,11 @@ import { NewsletterEmail } from '../../src/entity/newsletter_email' import { ReceivedEmail } from '../../src/entity/received_email' import { User } from '../../src/entity/user' import { getRepository } from '../../src/repository' +import { + deleteReceivedEmail, + findReceivedEmailById, + saveReceivedEmail, +} from '../../src/services/received_emails' import { deleteUser } from '../../src/services/user' import * as sendEmail from '../../src/utils/sendEmail' import { createTestUser } from '../db' @@ -66,24 +71,24 @@ describe('Recent Emails Resolver', () => { describe('recentEmails', () => { before(async () => { // create fake emails - const recentEmail = await getRepository(ReceivedEmail).save({ - user: { id: user.id }, - from: 'fake from', - subject: 'fake subject', - text: 'fake text', - html: 'fake html', - to: newsletterEmail.address, - type: 'article', - }) - const recentEmail2 = await getRepository(ReceivedEmail).save({ - user: { id: user.id }, - from: 'fake from 2', - subject: 'fake subject 2', - text: 'fake text 2', - html: 'fake html 2', - to: newsletterEmail2.address, - type: 'non-article', - }) + const recentEmail = await saveReceivedEmail( + 'fake from', + newsletterEmail.address, + 'fake subject', + 'fake text', + 'fake html', + user.id, + 'article' + ) + const recentEmail2 = await saveReceivedEmail( + 'fake from 2', + newsletterEmail2.address, + 'fake subject 2', + 'fake text 2', + 'fake html 2', + user.id, + 'non-article' + ) recentEmails = [recentEmail, recentEmail2] }) @@ -115,21 +120,21 @@ describe('Recent Emails Resolver', () => { before(async () => { // create fake email - recentEmail = await getRepository(ReceivedEmail).save({ - user: { id: user.id }, - from: 'Omnivore Newsletter ', - subject: 'fake subject 3', - text: 'fake text 3', - html: 'fake html 3', - to: newsletterEmail.address, - type: 'non-article', - }) + recentEmail = await saveReceivedEmail( + 'Omnivore Newsletter ', + newsletterEmail.address, + 'fake subject 3', + 'fake text 3', + 'fake html 3', + user.id, + 'non-article' + ) sinon.replace(sendEmail, 'sendEmail', sinon.fake.resolves(true)) }) after(async () => { // clean up - await getRepository(ReceivedEmail).delete(recentEmail.id) + await deleteReceivedEmail(recentEmail.id, user.id) sinon.restore() }) @@ -141,9 +146,7 @@ describe('Recent Emails Resolver', () => { expect(resp.body.data.markEmailAsItem.success).to.be.true - const updatedRecentEmail = await getRepository(ReceivedEmail).findOneBy({ - id: recentEmail.id, - }) + const updatedRecentEmail = await findReceivedEmailById(recentEmail.id) expect(updatedRecentEmail?.type).to.eql('article') }) }) @@ -169,24 +172,26 @@ describe('Recent Emails Resolver', () => { before(async () => { // create fake emails - const recentEmail = await getRepository(ReceivedEmail).save({ - user: { id: user2.id }, - from: 'fake from', - subject: 'fake subject', - text: 'fake text', - html: 'fake html', - to: newsletterEmail.address, - type: 'article', - }) - const recentEmail2 = await getRepository(ReceivedEmail).save({ - user: { id: user2.id }, - from: 'fake from 2', - subject: 'fake subject 2', - text: 'fake text 2', - html: 'fake html 2', - to: newsletterEmail2.address, - type: 'non-article', - }) + const recentEmail = await saveReceivedEmail( + 'fake from 4', + newsletterEmail.address, + 'fake subject 4', + 'fake text 4', + 'fake html 4', + user2.id, + 'article' + ) + + const recentEmail2 = await saveReceivedEmail( + 'fake from 4', + newsletterEmail.address, + 'fake subject 4', + 'fake text 4', + 'fake html 4', + user2.id, + 'non-article' + ) + recentEmails = [recentEmail, recentEmail2] }) @@ -198,15 +203,15 @@ describe('Recent Emails Resolver', () => { expect(results[0].id).to.eql(recentEmails[1].id) expect(results[1].id).to.eql(recentEmails[0].id) - await getRepository(ReceivedEmail).save({ - user: { id: user3.id }, - from: 'fake from', - subject: 'fake subject', - text: 'fake text', - html: 'fake html', - to: newsletterEmail.address, - type: 'article', - }) + await saveReceivedEmail( + 'fake from 5', + newsletterEmail.address, + 'fake subject 5', + 'fake text 5', + 'fake html 5', + user3.id, + 'article' + ) const res2 = await graphqlRequest(recentEmailsQuery, user2Auth).expect( 200 diff --git a/packages/api/test/resolvers/rules.test.ts b/packages/api/test/resolvers/rules.test.ts index 7297fc670..1cb96aeef 100644 --- a/packages/api/test/resolvers/rules.test.ts +++ b/packages/api/test/resolvers/rules.test.ts @@ -95,7 +95,7 @@ describe('Rules Resolver', () => { t.getRepository(Rule).save({ user: { id: user.id }, name: 'test rule', - filter: 'test filter', + filter: 'test filter 2', actions: [{ type: RuleActionType.SendNotification, params: [] }], enabled: true, }), @@ -148,7 +148,7 @@ describe('Rules Resolver', () => { t.getRepository(Rule).save({ user: { id: user.id }, name: 'test rule', - filter: 'test filter', + filter: 'test filter 3', actions: [{ type: RuleActionType.SendNotification, params: [] }], enabled: true, }), diff --git a/packages/api/test/resolvers/webhooks.test.ts b/packages/api/test/resolvers/webhooks.test.ts index e6b7bbaa7..0c5cce41f 100644 --- a/packages/api/test/resolvers/webhooks.test.ts +++ b/packages/api/test/resolvers/webhooks.test.ts @@ -3,9 +3,13 @@ import 'mocha' import { User } from '../../src/entity/user' import { Webhook } from '../../src/entity/webhook' import { WebhookEvent } from '../../src/generated/graphql' -import { getRepository } from '../../src/repository' import { deleteUser } from '../../src/services/user' -import { createWebhooks } from '../../src/services/webhook' +import { + createWebhook, + createWebhooks, + findWebhookById, + findWebhooks, +} from '../../src/services/webhook' import { createTestUser } from '../db' import { graphqlRequest, request } from '../util' @@ -50,11 +54,14 @@ describe('Webhooks API', () => { before(async () => { // create test webhooks - webhook = await getRepository(Webhook).save({ - url: 'http://localhost:3000/webhooks/test', - user: { id: user.id }, - eventTypes: [WebhookEvent.PageDeleted], - }) + webhook = await createWebhook( + { + url: 'http://localhost:3000/webhooks/test', + user: { id: user.id }, + eventTypes: [WebhookEvent.PageDeleted], + }, + user.id + ) }) it('should return a webhook', async () => { @@ -102,9 +109,7 @@ describe('Webhooks API', () => { ` const res = await graphqlRequest(query, authToken) - const webhooks = await getRepository(Webhook).findBy({ - user: { id: user.id }, - }) + const webhooks = await findWebhooks(user.id) expect(res.body.data.webhooks.webhooks).to.eql( webhooks.map((w) => ({ @@ -171,11 +176,15 @@ describe('Webhooks API', () => { context('when id is there', () => { before(async () => { - const webhook = await getRepository(Webhook).save({ - url: 'http://localhost:3000/webhooks/test', - user: { id: user.id }, - eventTypes: [WebhookEvent.HighlightUpdated], - }) + const webhook = await createWebhook( + { + url: 'http://localhost:3000/webhooks/test', + user: { id: user.id }, + eventTypes: [WebhookEvent.HighlightUpdated], + }, + user.id + ) + webhookId = webhook.id webhookUrl = 'http://localhost:3000/webhooks/test_2' eventTypes = [ @@ -219,19 +228,20 @@ describe('Webhooks API', () => { context('when webhook exists', () => { before(async () => { - const webhook = await getRepository(Webhook).save({ - url: 'http://localhost:3000/webhooks/test', - user: { id: user.id }, - eventTypes: [WebhookEvent.LabelCreated], - }) + const webhook = await createWebhook( + { + url: 'http://localhost:3000/webhooks/test', + user: { id: user.id }, + eventTypes: [WebhookEvent.LabelCreated], + }, + user.id + ) webhookId = webhook.id }) it('should delete a webhook', async () => { const res = await graphqlRequest(query, authToken) - const webhook = await getRepository(Webhook).findOneBy({ - id: webhookId, - }) + const webhook = await findWebhookById(webhookId, user.id) expect(res.body.data.deleteWebhook.webhook).to.be.an('object') expect(res.body.data.deleteWebhook.webhook.id).to.eql(webhookId) diff --git a/packages/api/test/routers/auth.test.ts b/packages/api/test/routers/auth.test.ts index 1c151602e..d70b5a0f4 100644 --- a/packages/api/test/routers/auth.test.ts +++ b/packages/api/test/routers/auth.test.ts @@ -56,7 +56,7 @@ describe('auth router', () => { }) afterEach(async () => { - const user = await getRepository(User).findOneBy({ name }) + const user = await userRepository.findOneBy({ name }) await deleteUser(user!.id) }) @@ -83,7 +83,7 @@ describe('auth router', () => { it('creates the user with pending status and correct name', async () => { await signupRequest(email, password, name, username).expect(302) - const user = await getRepository(User).findOneBy({ name }) + const user = await userRepository.findOneBy({ name }) expect(user?.status).to.eql(StatusType.Pending) expect(user?.name).to.eql(name) diff --git a/packages/api/test/routers/emails.test.ts b/packages/api/test/routers/emails.test.ts index 71954d3d7..175a3538e 100644 --- a/packages/api/test/routers/emails.test.ts +++ b/packages/api/test/routers/emails.test.ts @@ -4,8 +4,8 @@ import 'mocha' import sinon from 'sinon' import { ReceivedEmail } from '../../src/entity/received_email' import { User } from '../../src/entity/user' -import { getRepository } from '../../src/repository' import { createNewsletterEmail } from '../../src/services/newsletters' +import { saveReceivedEmail } from '../../src/services/received_emails' import { deleteUser } from '../../src/services/user' import * as parser from '../../src/utils/parser' import * as sendEmail from '../../src/utils/sendEmail' @@ -30,15 +30,15 @@ describe('Emails Router', () => { await createNewsletterEmail(user.id, newsletterEmail) token = process.env.PUBSUB_VERIFICATION_TOKEN! - receivedEmail = await getRepository(ReceivedEmail).save({ - user: { id: user.id }, + receivedEmail = await saveReceivedEmail( from, to, subject, text, - html: '', - type: 'non-article', - }) + '', + user.id, + 'non-article' + ) }) after(async () => { diff --git a/packages/api/test/routers/integrations.test.ts b/packages/api/test/routers/integrations.test.ts index 2e0ba273d..5d86923f2 100644 --- a/packages/api/test/routers/integrations.test.ts +++ b/packages/api/test/routers/integrations.test.ts @@ -10,9 +10,12 @@ import { LibraryItem } from '../../src/entity/library_item' import { User } from '../../src/entity/user' import { env } from '../../src/env' import { PubSubRequestBody } from '../../src/pubsub' -import { authTrx, getRepository } from '../../src/repository' import { createHighlight, getHighlightUrl } from '../../src/services/highlights' -import { deleteIntegrations } from '../../src/services/integrations' +import { + deleteIntegrations, + saveIntegration, + updateIntegration, +} from '../../src/services/integrations' import { READWISE_API_URL } from '../../src/services/integrations/readwise' import { deleteLibraryItemById } from '../../src/services/library_item' import { deleteUser } from '../../src/services/user' @@ -127,14 +130,12 @@ describe('Integrations routers', () => { let highlightsData: string before(async () => { - integration = await authTrx( - (t) => - t.getRepository(Integration).save({ - user: { id: user.id }, - name: 'READWISE', - token: 'token', - }), - undefined, + integration = await saveIntegration( + { + user: { id: user.id }, + name: 'READWISE', + token: 'token', + }, user.id ) integrationName = integration.name @@ -309,10 +310,14 @@ describe('Integrations routers', () => { }) .post('/highlights', highlightsData) .reply(200) - await getRepository(Integration).update(integration.id, { - syncedAt: null, - taskName: 'some task name', - }) + await updateIntegration( + integration.id, + { + syncedAt: null, + taskName: 'some task name', + }, + user.id + ) }) it('returns 200 with OK', async () => { @@ -334,15 +339,13 @@ describe('Integrations routers', () => { before(async () => { token = 'test token' // create integration - integration = await authTrx( - (t) => - t.getRepository(Integration).save({ - user: { id: user.id }, - name: 'POCKET', - token, - type: IntegrationType.Import, - }), - undefined, + integration = await saveIntegration( + { + user: { id: user.id }, + name: 'POCKET', + token, + type: IntegrationType.Import, + }, user.id ) diff --git a/packages/db/migrations/0123.do.add_rls.sql b/packages/db/migrations/0123.do.add_rls.sql index c25353b4d..419b6a1de 100755 --- a/packages/db/migrations/0123.do.add_rls.sql +++ b/packages/db/migrations/0123.do.add_rls.sql @@ -16,12 +16,6 @@ CREATE POLICY integrations_policy on omnivore.integrations WITH CHECK (user_id = omnivore.get_current_user_id()); GRANT SELECT, INSERT, UPDATE, DELETE ON omnivore.integrations TO omnivore_user; -ALTER TABLE omnivore.newsletter_emails ENABLE ROW LEVEL SECURITY; -CREATE POLICY newsletter_emails_policy on omnivore.newsletter_emails - USING (user_id = omnivore.get_current_user_id()) - WITH CHECK (user_id = omnivore.get_current_user_id()); -GRANT SELECT, INSERT, DELETE ON omnivore.newsletter_emails TO omnivore_user; - CREATE POLICY labels_policy on omnivore.labels USING (user_id = omnivore.get_current_user_id()) WITH CHECK (user_id = omnivore.get_current_user_id()); @@ -49,12 +43,4 @@ CREATE POLICY user_device_tokens_policy on omnivore.user_device_tokens WITH CHECK (user_id = omnivore.get_current_user_id()); GRANT SELECT, INSERT, DELETE ON omnivore.user_device_tokens TO omnivore_user; -ALTER TABLE omnivore.search_history ENABLE ROW LEVEL SECURITY; -CREATE POLICY search_history_policy on omnivore.search_history - USING (user_id = omnivore.get_current_user_id()) - WITH CHECK (user_id = omnivore.get_current_user_id()); -GRANT SELECT, INSERT, DELETE ON omnivore.search_history TO omnivore_user; - - - COMMIT; diff --git a/packages/db/migrations/0123.undo.add_rls.sql b/packages/db/migrations/0123.undo.add_rls.sql index 8ca5f1857..6b17c40b8 100755 --- a/packages/db/migrations/0123.undo.add_rls.sql +++ b/packages/db/migrations/0123.undo.add_rls.sql @@ -10,9 +10,6 @@ DROP POLICY filters_policy on omnivore.filters; ALTER TABLE omnivore.integrations DISABLE ROW LEVEL SECURITY; DROP POLICY integrations_policy on omnivore.integrations; -ALTER TABLE omnivore.newsletter_emails DISABLE ROW LEVEL SECURITY; -DROP POLICY newsletter_emails_policy on omnivore.newsletter_emails; - DROP POLICY labels_policy on omnivore.labels; ALTER TABLE omnivore.received_emails DISABLE ROW LEVEL SECURITY; @@ -26,7 +23,4 @@ DROP POLICY webhooks_policy on omnivore.webhooks; DROP POLICY user_device_tokens_policy on omnivore.user_device_tokens; -ALTER TABLE omnivore.search_history DISABLE ROW LEVEL SECURITY; -DROP POLICY search_history_policy on omnivore.search_history; - COMMIT;