update labels rls

This commit is contained in:
Hongbo Wu
2023-09-07 23:28:24 +08:00
parent 6672ca0109
commit 024d558cfe
13 changed files with 104 additions and 59 deletions

View File

@ -178,9 +178,9 @@ export const updateHighlightResolver = authorized<
const updatedHighlight = await updateHighlight(
input.highlightId,
{
annotation: input.annotation,
html: input.html,
quote: input.quote,
annotation: input.annotation ?? undefined,
html: input.html ?? undefined,
quote: input.quote ?? undefined,
},
uid,
pubsub

View File

@ -20,6 +20,7 @@ import {
WebhookSuccess,
} from '../../generated/graphql'
import { authTrx } from '../../repository'
import { deleteWebhook } from '../../services/webhook'
import { analytics } from '../../utils/analytics'
import { authorized } from '../../utils/helpers'
@ -80,9 +81,9 @@ export const deleteWebhookResolver = authorized<
DeleteWebhookSuccess,
DeleteWebhookError,
MutationDeleteWebhookArgs
>(async (_, { id }, { authTrx, uid, log }) => {
>(async (_, { id }, { uid, log }) => {
try {
await authTrx(async (t) => t.getRepository(Webhook).delete(id))
const webhook = await deleteWebhook(id, uid)
analytics.track({
userId: uid,
@ -94,16 +95,7 @@ export const deleteWebhookResolver = authorized<
})
return {
webhook: {
id,
url: '',
eventTypes: [],
method: 'POST',
contentType: 'application/json',
enabled: false,
createdAt: new Date(),
updatedAt: new Date(),
},
webhook: webhookDataToResponse(webhook),
}
} catch (error) {
log.error('Error deleting webhook', error)

View File

@ -76,8 +76,11 @@ export const updateHighlight = async (
const highlightRepo = tx.withRepository(highlightRepository)
await highlightRepo.updateAndSave(highlightId, highlight)
return highlightRepo.findOneByOrFail({
id: highlightId,
return highlightRepo.findOneOrFail({
where: { id: highlightId },
relations: {
libraryItem: true,
},
})
})

View File

@ -190,3 +190,23 @@ export const updateLabel = async (
userId
)
}
export const findLabelsByUserId = async (userId: string): Promise<Label[]> => {
return authTrx(
async (tx) =>
tx.withRepository(labelRepository).find({
where: { user: { id: userId } },
order: { position: 'ASC' },
}),
undefined,
userId
)
}
export const findLabelById = async (id: string, userId: string) => {
return authTrx(
async (tx) => tx.withRepository(labelRepository).findOneBy({ id }),
undefined,
userId
)
}

View File

@ -12,6 +12,7 @@ import { BulkActionType } from '../generated/graphql'
import { createPubSubClient, EntityType } from '../pubsub'
import { authTrx } from '../repository'
import { libraryItemRepository } from '../repository/library_item'
import { wordsCount } from '../utils/helpers'
import {
DateFilter,
FieldFilter,
@ -387,7 +388,13 @@ export const createLibraryItem = async (
pubsub = createPubSubClient()
): Promise<LibraryItem> => {
const newLibraryItem = await authTrx(
async (tx) => tx.withRepository(libraryItemRepository).save(libraryItem),
async (tx) =>
tx.withRepository(libraryItemRepository).save({
...libraryItem,
wordCount:
libraryItem.wordCount ??
wordsCount(libraryItem.readableContent || ''),
}),
undefined,
userId
)

View File

@ -5,7 +5,7 @@ import { DeepPartial, EntityManager } from 'typeorm'
import { LibraryItem, LibraryItemType } from '../entity/library_item'
import { authTrx, entityManager } from '../repository'
import { libraryItemRepository } from '../repository/library_item'
import { generateSlug, stringToHash } from '../utils/helpers'
import { generateSlug, stringToHash, wordsCount } from '../utils/helpers'
import { logger } from '../utils/logger'
import { createLibraryItem } from './library_item'
@ -75,6 +75,7 @@ const popularReadToLibraryItem = (
publishedAt: pr.publishedAt,
siteName: pr.siteName,
user: { id: userId },
wordCount: wordsCount(pr.content),
}
}

View File

@ -36,9 +36,9 @@ export const deleteRule = async (id: string, userId?: string) => {
return authTrx(
async (t) => {
const repo = t.getRepository(Rule)
const rule = await repo.findOneByOrFail({ id })
await repo.delete(id)
return repo.findOneByOrFail({ id })
return rule
},
undefined,
userId

View File

@ -41,3 +41,16 @@ export const findWebhookById = async (id: string, userId?: string) => {
userId
)
}
export const deleteWebhook = async (id: string, userId?: string) => {
return authTrx(
async (tx) => {
const repo = tx.getRepository(Webhook)
const webhook = await repo.findOneByOrFail({ id })
await repo.delete(id)
return webhook
},
undefined,
userId
)
}

View File

@ -5,12 +5,17 @@ import { Highlight } from '../../src/entity/highlight'
import { Label } from '../../src/entity/label'
import { LibraryItem } from '../../src/entity/library_item'
import { User } from '../../src/entity/user'
import { getRepository } from '../../src/repository'
import {
createHighlight,
findHighlightById,
} from '../../src/services/highlights'
import { createLabel, deleteLabels, saveLabelsInHighlight } from '../../src/services/labels'
import {
createLabel,
deleteLabels,
findLabelById,
findLabelsByUserId,
saveLabelsInHighlight,
} from '../../src/services/labels'
import {
deleteLibraryItemById,
findLibraryItemById,
@ -38,22 +43,17 @@ describe('Labels API', () => {
})
describe('GET labels', () => {
let labels: Label[]
let query: string
before(async () => {
// create testing labels
const label1 = await createLabel('label_1', '#ffffff', user.id)
const label2 = await createLabel('label_2', '#eeeeee', user.id)
labels = [label1, label2]
await createLabel('label_1', '#ffffff', user.id)
await createLabel('label_2', '#eeeeee', user.id)
})
after(async () => {
// clean up
await deleteLabels(
labels.map((l) => l.id),
user.id
)
await deleteLabels({ user: { id: user.id } }, user.id)
})
beforeEach(() => {
@ -80,9 +80,7 @@ describe('Labels API', () => {
it('should return labels', async () => {
const res = await graphqlRequest(query, authToken).expect(200)
const labels = await getRepository(Label).findBy({
user: { id: user.id },
})
const labels = await findLabelsByUserId(user.id)
expect(res.body.data.labels.labels).to.eql(
labels.map((label) => ({
id: label.id,
@ -148,9 +146,10 @@ describe('Labels API', () => {
it('should create label', async () => {
const res = await graphqlRequest(query, authToken).expect(200)
const label = await getRepository(Label).findOneBy({
id: res.body.data.createLabel.label.id,
})
const label = await findLabelById(
res.body.data.createLabel.label.id,
user.id
)
expect(label).to.exist
})
})
@ -164,15 +163,13 @@ describe('Labels API', () => {
})
after(async () => {
await deleteLabels([existingLabel.id], user.id)
await deleteLabels({ id: existingLabel.id }, user.id)
})
it('should return error code BAD_REQUEST', async () => {
const res = await graphqlRequest(query, authToken).expect(200)
expect(res.body.data.createLabel.errorCodes).to.eql([
'BAD_REQUEST',
])
expect(res.body.data.createLabel.errorCodes).to.eql(['BAD_REQUEST'])
})
})
@ -228,7 +225,7 @@ describe('Labels API', () => {
it('should delete label', async () => {
await graphqlRequest(query, authToken).expect(200)
const label = await getRepository(Label).findOneBy({ id: labelId })
const label = await findLabelById(labelId, user.id)
expect(label).not.exist
})
})
@ -335,7 +332,7 @@ describe('Labels API', () => {
after(async () => {
// clean up
await deleteLabels(
labels.map((l) => l.id),
{ user: { id: user.id } },
user.id
)
await deleteLibraryItemById(item.id)
@ -461,7 +458,7 @@ describe('Labels API', () => {
})
after(async () => {
await deleteLabels([toUpdateLabel.id], user.id)
await deleteLabels( { id: toUpdateLabel.id }, user.id)
})
it('should return the updated label', async () => {
@ -475,9 +472,7 @@ describe('Labels API', () => {
it('should update the label in db', async () => {
await graphqlRequest(query, authToken).expect(200)
const updatedLabel = await getRepository(Label).findOne({
where: { id: labelId },
})
const updatedLabel = await findLabelById(labelId, user.id)
expect(updatedLabel?.name).to.eql(name)
expect(updatedLabel?.color).to.eql(color)
@ -538,7 +533,7 @@ describe('Labels API', () => {
after(async () => {
// clean up
await deleteLabels(
labels.map((l) => l.id),
{ user: { id: user.id } },
user.id
)
await deleteLibraryItemById(item.id)
@ -689,12 +684,7 @@ describe('Labels API', () => {
expect(res.body.data.moveLabel.label.position).to.eql(
labels[4].position
)
const reorderedLabels = await getRepository(Label).find({
where: {
user: { id: user.id },
},
order: { position: 'ASC' },
})
const reorderedLabels = await findLabelsByUserId(user.id)
expect(reorderedLabels.map((l) => l.id)).to.eql([
labels[0].id,
labels[2].id,

View File

@ -42,7 +42,7 @@ describe('PopularReads API', () => {
describe('addPopularRead', () => {
it('should add a new article if the readName is valid', async () => {
const readName = 'omnivore_get_started'
const readName = 'omnivore_ios'
const res = await graphqlRequest(
addPopularReadQuery(readName),
authToken
@ -54,14 +54,14 @@ describe('PopularReads API', () => {
user.id
)
expect(item?.originalUrl).to.eq(
'https://blog.omnivore.app/p/getting-started-with-omnivore'
'https://blog.omnivore.app/p/saving-links-from-your-iphone-or'
)
expect(item?.wordCount).to.eq(1155)
expect(item?.wordCount).to.eq(371)
})
it('responds status code 500 when invalid user', async () => {
const invalidAuthToken = 'Fake token'
const readName = 'omnivore_get_started'
const readName = 'omnivore_web'
return graphqlRequest(
addPopularReadQuery(readName),
invalidAuthToken

View File

@ -146,7 +146,10 @@ describe('Recent Emails Resolver', () => {
expect(resp.body.data.markEmailAsItem.success).to.be.true
const updatedRecentEmail = await findReceivedEmailById(recentEmail.id)
const updatedRecentEmail = await findReceivedEmailById(
recentEmail.id,
user.id
)
expect(updatedRecentEmail?.type).to.eql('article')
})
})

View File

@ -16,6 +16,8 @@ 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;
DROP POLICY read_labels ON omnivore.labels;
DROP POLICY create_labels ON omnivore.labels;
CREATE POLICY labels_policy on omnivore.labels
USING (user_id = omnivore.get_current_user_id())
WITH CHECK (user_id = omnivore.get_current_user_id());
@ -38,6 +40,8 @@ CREATE POLICY webhooks_policy on omnivore.webhooks
WITH CHECK (user_id = omnivore.get_current_user_id());
GRANT SELECT, INSERT, UPDATE, DELETE ON omnivore.webhooks TO omnivore_user;
DROP POLICY read_user_device_tokens ON omnivore.user_device_tokens;
DROP POLICY create_user_device_tokens ON omnivore.user_device_tokens;
CREATE POLICY user_device_tokens_policy on omnivore.user_device_tokens
USING (user_id = omnivore.get_current_user_id())
WITH CHECK (user_id = omnivore.get_current_user_id());

View File

@ -11,6 +11,12 @@ ALTER TABLE omnivore.integrations DISABLE ROW LEVEL SECURITY;
DROP POLICY integrations_policy on omnivore.integrations;
DROP POLICY labels_policy on omnivore.labels;
CREATE POLICY read_labels on omnivore.labels
FOR SELECT TO omnivore_user
USING (true);
CREATE POLICY create_labels on omnivore.labels
FOR INSERT TO omnivore_user
WITH CHECK (true);
ALTER TABLE omnivore.received_emails DISABLE ROW LEVEL SECURITY;
DROP POLICY received_emails_policy on omnivore.received_emails;
@ -22,5 +28,11 @@ ALTER TABLE omnivore.webhooks DISABLE ROW LEVEL SECURITY;
DROP POLICY webhooks_policy on omnivore.webhooks;
DROP POLICY user_device_tokens_policy on omnivore.user_device_tokens;
CREATE POLICY read_user_device_tokens on omnivore.user_device_tokens
FOR SELECT TO omnivore_user
USING (true);
CREATE POLICY create_user_device_tokens on omnivore.user_device_tokens
FOR INSERT TO omnivore_user
WITH CHECK (true);
COMMIT;