allow send data to webhook with rules

This commit is contained in:
Hongbo Wu
2024-03-21 18:13:42 +08:00
parent 07cee98ff9
commit c551d9fe6b
9 changed files with 80 additions and 53 deletions

View File

@ -21,7 +21,7 @@ export enum RuleActionType {
export enum RuleEventType {
PageCreated = 'PAGE_CREATED',
PageUpdated = 'PAGE_UPDATED',
LabelCreated = 'PAGE_CREATED',
LabelCreated = 'LABEL_CREATED',
HighlightCreated = 'HIGHLIGHT_CREATED',
}

View File

@ -2502,10 +2502,13 @@ export enum RuleActionType {
Archive = 'ARCHIVE',
Delete = 'DELETE',
MarkAsRead = 'MARK_AS_READ',
SendNotification = 'SEND_NOTIFICATION'
SendNotification = 'SEND_NOTIFICATION',
Webhook = 'WEBHOOK'
}
export enum RuleEventType {
HighlightCreated = 'HIGHLIGHT_CREATED',
LabelCreated = 'LABEL_CREATED',
PageCreated = 'PAGE_CREATED',
PageUpdated = 'PAGE_UPDATED'
}

View File

@ -1879,9 +1879,12 @@ enum RuleActionType {
DELETE
MARK_AS_READ
SEND_NOTIFICATION
WEBHOOK
}
enum RuleEventType {
HIGHLIGHT_CREATED
LABEL_CREATED
PAGE_CREATED
PAGE_UPDATED
}

View File

@ -1,4 +1,5 @@
import { LiqeQuery } from '@omnivore/liqe'
import axios, { Method } from 'axios'
import { ReadingProgressDataSource } from '../datasources/reading_progress_data_source'
import { LibraryItem, LibraryItemState } from '../entity/library_item'
import { Rule, RuleAction, RuleActionType, RuleEventType } from '../entity/rule'
@ -28,6 +29,7 @@ interface RuleActionObj {
userId: string
action: RuleAction
data: ItemEvent | LibraryItem
ruleEventType: RuleEventType
}
type RuleActionFunc = (obj: RuleActionObj) => Promise<unknown>
@ -86,6 +88,29 @@ const sendNotification = async (obj: RuleActionObj) => {
return sendPushNotifications(obj.userId, message, 'rule', data)
}
const sendToWebhook = async (obj: RuleActionObj) => {
const [url, method, contentType] = obj.action.params
const [type, action] = obj.ruleEventType.split('_')
const body = {
action,
userId: obj.userId,
[type]: obj.data,
}
logger.info('triggering webhook', { url, method })
return axios.request({
url,
method: method as Method,
headers: {
'Content-Type': contentType,
},
data: body,
timeout: 5000, // 5s
})
}
const getRuleAction = (
actionType: RuleActionType
): RuleActionFunc | undefined => {
@ -100,6 +125,8 @@ const getRuleAction = (
return markPageAsRead
case RuleActionType.SendNotification:
return sendNotification
case RuleActionType.Webhook:
return sendToWebhook
default:
logger.error('Unknown rule action type', actionType)
return undefined
@ -110,7 +137,8 @@ const triggerActions = async (
libraryItemId: string,
userId: string,
rules: Rule[],
data: ItemEvent
data: ItemEvent,
ruleEventType: RuleEventType
) => {
const actionPromises: Promise<unknown>[] = []
@ -165,6 +193,7 @@ const triggerActions = async (
userId,
action,
data: results[0],
ruleEventType,
}
actionPromises.push(actionFunc(actionObj))
@ -188,7 +217,7 @@ export const triggerRule = async (jobData: TriggerRuleJobData) => {
return false
}
await triggerActions(libraryItemId, userId, rules, data)
await triggerActions(libraryItemId, userId, rules, data, ruleEventType)
return true
}

View File

@ -12,6 +12,8 @@ import {
import { buildLogger } from './utils/logger'
import { isYouTubeVideoURL } from './utils/youtube'
export type BaseEntityEvent = { id: string; userId: string }
const logger = buildLogger('pubsub')
const client = new PubSub()
@ -46,7 +48,7 @@ export const createPubSubClient = (): PubsubClient => {
Buffer.from(JSON.stringify({ userId, email, name, username }))
)
},
entityCreated: async <T extends Record<string, any>>(
entityCreated: async <T extends BaseEntityEvent>(
type: EntityType,
data: T,
userId: string,
@ -54,10 +56,10 @@ export const createPubSubClient = (): PubsubClient => {
): Promise<void> => {
// queue trigger rule job
await enqueueTriggerRuleJob({
userId,
ruleEventType: `${type.toUpperCase()}_CREATED` as RuleEventType,
libraryItemId,
data,
userId,
libraryItemId,
})
// queue export item job
await enqueueExportItem({
@ -92,21 +94,20 @@ export const createPubSubClient = (): PubsubClient => {
}
}
},
entityUpdated: async <T extends Record<string, any>>(
entityUpdated: async <T extends BaseEntityEvent>(
type: EntityType,
data: T,
userId: string,
libraryItemId: string
): Promise<void> => {
// queue trigger rule job
if (type === EntityType.PAGE) {
await enqueueTriggerRuleJob({
userId,
ruleEventType: RuleEventType.PageUpdated,
libraryItemId,
data,
})
}
await enqueueTriggerRuleJob({
userId,
ruleEventType: RuleEventType.PageUpdated,
libraryItemId,
data,
})
// queue export item job
await enqueueExportItem({
userId,
@ -145,7 +146,7 @@ export const createPubSubClient = (): PubsubClient => {
}
export enum EntityType {
PAGE = 'page',
ITEM = 'page',
HIGHLIGHT = 'highlight',
LABEL = 'label',
RSS_FEED = 'feed',
@ -158,13 +159,13 @@ export interface PubsubClient {
name: string,
username: string
) => Promise<void>
entityCreated: <T extends Record<string, any>>(
entityCreated: <T extends BaseEntityEvent>(
type: EntityType,
data: T,
userId: string,
libraryItemId: string
) => Promise<void>
entityUpdated: <T extends Record<string, any>>(
entityUpdated: <T extends BaseEntityEvent>(
type: EntityType,
data: T,
userId: string,

View File

@ -2167,6 +2167,7 @@ const schema = gql`
DELETE
MARK_AS_READ
SEND_NOTIFICATION
WEBHOOK
}
type RulesError {
@ -2181,6 +2182,8 @@ const schema = gql`
enum RuleEventType {
PAGE_CREATED
PAGE_UPDATED
LABEL_CREATED
HIGHLIGHT_CREATED
}
input SetRuleInput {

View File

@ -59,7 +59,7 @@ export const createHighlight = async (
await pubsub.entityCreated<UpdateItemEvent>(
EntityType.HIGHLIGHT,
{ id: libraryItemId, highlights: [newHighlight] },
{ id: libraryItemId, highlights: [newHighlight], userId },
userId,
libraryItemId
)
@ -107,7 +107,7 @@ export const mergeHighlights = async (
await pubsub.entityCreated<UpdateItemEvent>(
EntityType.HIGHLIGHT,
{ id: libraryItemId, highlights: [newHighlight] },
{ id: libraryItemId, highlights: [newHighlight], userId },
userId,
libraryItemId
)
@ -142,7 +142,7 @@ export const updateHighlight = async (
const libraryItemId = updatedHighlight.libraryItem.id
await pubsub.entityUpdated<UpdateItemEvent>(
EntityType.HIGHLIGHT,
{ id: libraryItemId, highlights: [highlight] },
{ id: libraryItemId, highlights: [highlight], userId },
userId,
libraryItemId
)

View File

@ -146,7 +146,7 @@ export const saveLabelsInLibraryItem = async (
// create pubsub event
await pubsub.entityCreated<UpdateItemEvent>(
EntityType.LABEL,
{ id: libraryItemId, labels },
{ id: libraryItemId, labels, userId },
userId,
libraryItemId
)
@ -217,7 +217,7 @@ export const saveLabelsInHighlight = async (
// create pubsub event
await pubsub.entityCreated<UpdateItemEvent>(
EntityType.LABEL,
{ id: libraryItemId, highlights: [{ id: highlightId, labels }] },
{ id: libraryItemId, highlights: [{ id: highlightId, labels }], userId },
userId,
libraryItemId
)

View File

@ -15,7 +15,7 @@ import { Highlight } from '../entity/highlight'
import { Label } from '../entity/label'
import { LibraryItem, LibraryItemState } from '../entity/library_item'
import { BulkActionType, InputMaybe, SortParams } from '../generated/graphql'
import { createPubSubClient, EntityType } from '../pubsub'
import { BaseEntityEvent, createPubSubClient, EntityType } from '../pubsub'
import { redisDataSource } from '../redis_data_source'
import {
authTrx,
@ -37,10 +37,13 @@ type IgnoredFields =
| 'links'
| 'textContentHash'
export type ItemEvent = CreateItemEvent | UpdateItemEvent
export type CreateItemEvent = Omit<DeepPartial<LibraryItem>, IgnoredFields>
export type UpdateItemEvent = Omit<
QueryDeepPartialEntity<LibraryItem>,
IgnoredFields
export type CreateItemEvent = Merge<
Omit<DeepPartial<LibraryItem>, IgnoredFields>,
BaseEntityEvent
>
export type UpdateItemEvent = Merge<
Omit<QueryDeepPartialEntity<LibraryItem>, IgnoredFields>,
BaseEntityEvent
>
export class RequiresSearchQueryError extends Error {
@ -841,7 +844,7 @@ export const softDeleteLibraryItem = async (
userId
)
await pubsub.entityDeleted(EntityType.PAGE, id, userId)
await pubsub.entityDeleted(EntityType.ITEM, id, userId)
return deletedLibraryItem
}
@ -883,13 +886,8 @@ export const updateLibraryItem = async (
if (libraryItem.state === LibraryItemState.Succeeded) {
// send create event if the item was created
await pubsub.entityCreated<CreateItemEvent>(
EntityType.PAGE,
{
...updatedLibraryItem,
originalContent: undefined,
readableContent: undefined,
feedContent: undefined,
},
EntityType.ITEM,
{ ...updatedLibraryItem, userId },
userId,
id
)
@ -898,13 +896,8 @@ export const updateLibraryItem = async (
}
await pubsub.entityUpdated<UpdateItemEvent>(
EntityType.PAGE,
{
...libraryItem,
originalContent: undefined,
readableContent: undefined,
feedContent: undefined,
},
EntityType.ITEM,
{ ...libraryItem, id, userId },
userId,
id
)
@ -966,8 +959,8 @@ export const updateLibraryItemReadingProgress = async (
const updatedItem = result[0][0]
await pubsub.entityUpdated<UpdateItemEvent>(
EntityType.PAGE,
updatedItem,
EntityType.ITEM,
{ ...updatedItem, id, userId },
userId,
id
)
@ -1067,13 +1060,8 @@ export const createOrUpdateLibraryItem = async (
}
await pubsub.entityCreated<CreateItemEvent>(
EntityType.PAGE,
{
...newLibraryItem,
originalContent: undefined,
readableContent: undefined,
feedContent: undefined,
},
EntityType.ITEM,
{ ...newLibraryItem, userId },
userId,
newLibraryItem.id
)