mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
fix highlight tests
This commit is contained in:
parent
b036149fe2
commit
4b95b732cf
34 changed files with 489 additions and 429 deletions
|
|
@ -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<Highlight>,
|
||||
libraryItemId: string,
|
||||
userId: string
|
||||
createAndSave(highlight: DeepPartial<Highlight>) {
|
||||
return this.save(unescapeHighlight(highlight))
|
||||
},
|
||||
|
||||
updateAndSave(
|
||||
highlightId: string,
|
||||
highlight: QueryDeepPartialEntity<Highlight>
|
||||
) {
|
||||
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,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
|
|
|
|||
|
|
@ -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<
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Highlight> & { pageId: string }
|
||||
type HighlightEvent = { id: string; pageId: string }
|
||||
type CreateHighlightEvent = DeepPartial<Highlight> & HighlightEvent
|
||||
type UpdateHighlightEvent = QueryDeepPartialEntity<Highlight> & 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<HighlightEvent>(
|
||||
await pubsub.entityCreated<CreateHighlightEvent>(
|
||||
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<HighlightEvent>(
|
||||
await pubsub.entityCreated<CreateHighlightEvent>(
|
||||
EntityType.HIGHLIGHT,
|
||||
{ ...newHighlight, pageId: libraryItemId },
|
||||
userId
|
||||
|
|
@ -68,23 +68,20 @@ export const mergeHighlights = async (
|
|||
|
||||
export const updateHighlight = async (
|
||||
highlightId: string,
|
||||
highlight: DeepPartial<Highlight>,
|
||||
highlight: QueryDeepPartialEntity<Highlight>,
|
||||
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<HighlightEvent>(
|
||||
await pubsub.entityUpdated<UpdateHighlightEvent>(
|
||||
EntityType.HIGHLIGHT,
|
||||
{ ...highlight, id: highlightId, pageId: updatedHighlight.libraryItem.id },
|
||||
userId
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export const findIntegrations = async (
|
|||
)
|
||||
}
|
||||
|
||||
export const createIntegration = async (
|
||||
export const saveIntegration = async (
|
||||
integration: DeepPartial<Integration>,
|
||||
userId: string
|
||||
) => {
|
||||
|
|
|
|||
|
|
@ -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<NewsletterEmail[]> => {
|
||||
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<boolean> => {
|
||||
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<boolean> => {
|
||||
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<NewsletterEmail | null> => {
|
||||
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<NewsletterEmail | null> => {
|
||||
return authTrx(
|
||||
(t) => t.getRepository(NewsletterEmail).findOneBy({ id }),
|
||||
undefined,
|
||||
userId
|
||||
)
|
||||
return getRepository(NewsletterEmail).findOneBy({ id, user: { id: userId } })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,22 +10,46 @@ export const saveReceivedEmail = async (
|
|||
userId: string,
|
||||
type: 'article' | 'non-article' = 'non-article'
|
||||
): Promise<ReceivedEmail> => {
|
||||
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
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<SaveResult> => {
|
||||
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<boolean> => {
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -19,10 +19,13 @@ export const findDeviceTokenByToken = async (
|
|||
export const findDeviceTokensByUserId = async (
|
||||
userId: string
|
||||
): Promise<UserDeviceToken[]> => {
|
||||
return authTrx((t) =>
|
||||
t.getRepository(UserDeviceToken).findBy({
|
||||
user: { id: userId },
|
||||
})
|
||||
return authTrx(
|
||||
(t) =>
|
||||
t.getRepository(UserDeviceToken).findBy({
|
||||
user: { id: userId },
|
||||
}),
|
||||
undefined,
|
||||
userId
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: '<p>test content</p>',
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = '<p>test</p>'
|
||||
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 () => {
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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<Highlight> = {
|
||||
|
|
@ -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<Highlight> = {
|
||||
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<Highlight> = {
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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 <newsletter@omnivore.app>',
|
||||
subject: 'fake subject 3',
|
||||
text: 'fake text 3',
|
||||
html: '<html><body>fake html 3</body></html>',
|
||||
to: newsletterEmail.address,
|
||||
type: 'non-article',
|
||||
})
|
||||
recentEmail = await saveReceivedEmail(
|
||||
'Omnivore Newsletter <newsletter@omnivore.app>',
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue