diff --git a/packages/api/src/entity/entity_label.ts b/packages/api/src/entity/entity_label.ts index 794275128..392a4e121 100644 --- a/packages/api/src/entity/entity_label.ts +++ b/packages/api/src/entity/entity_label.ts @@ -1,4 +1,21 @@ -import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm' +import { + Column, + Entity, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, +} from 'typeorm' +import { Label } from './label' + +// for labels created by rules, we use the rule name as the source, for example: 'rule:my-rule' +// for labels created by users, we use 'user' +// for labels created by system, we use 'system' +type RuleSourceType = `rule:${string}` +export type LabelSource = 'user' | 'system' | RuleSourceType + +export const isLabelSource = (source: string): source is LabelSource => { + return ['user', 'system'].indexOf(source) !== -1 || source.startsWith('rule:') +} @Entity({ name: 'entity_labels' }) export class EntityLabel { @@ -8,9 +25,16 @@ export class EntityLabel { @Column('uuid') labelId!: string + @ManyToOne(() => Label) + @JoinColumn({ name: 'label_id' }) + label!: Label + @Column('uuid') libraryItemId?: string | null @Column('uuid') highlightId?: string | null + + @Column('text', { default: 'user' }) + source!: LabelSource } diff --git a/packages/api/src/generated/graphql.ts b/packages/api/src/generated/graphql.ts index 57bd61997..a3d814b9f 100644 --- a/packages/api/src/generated/graphql.ts +++ b/packages/api/src/generated/graphql.ts @@ -1091,6 +1091,7 @@ export type Label = { internal?: Maybe; name: Scalars['String']; position?: Maybe; + source?: Maybe; }; export type LabelsError = { @@ -2539,6 +2540,7 @@ export type SetLabelsInput = { labelIds?: InputMaybe>; labels?: InputMaybe>; pageId: Scalars['ID']; + source?: InputMaybe; }; export type SetLabelsResult = SetLabelsError | SetLabelsSuccess; @@ -5180,6 +5182,7 @@ export type LabelResolvers, ParentType, ContextType>; name?: Resolver; position?: Resolver, ParentType, ContextType>; + source?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; diff --git a/packages/api/src/generated/schema.graphql b/packages/api/src/generated/schema.graphql index 8f64a27b7..e57928d48 100644 --- a/packages/api/src/generated/schema.graphql +++ b/packages/api/src/generated/schema.graphql @@ -975,6 +975,7 @@ type Label { internal: Boolean name: String! position: Int + source: String } type LabelsError { @@ -1971,6 +1972,7 @@ input SetLabelsInput { labelIds: [ID!] labels: [CreateLabelInput!] pageId: ID! + source: String } union SetLabelsResult = SetLabelsError | SetLabelsSuccess diff --git a/packages/api/src/resolvers/article/index.ts b/packages/api/src/resolvers/article/index.ts index 08f156690..cc373cbfc 100644 --- a/packages/api/src/resolvers/article/index.ts +++ b/packages/api/src/resolvers/article/index.ts @@ -359,8 +359,7 @@ export const createArticleResolver = authorized< libraryItemToReturn.id, uid, inputLabels, - rssFeedUrl, - pubsub + rssFeedUrl ) log.info( diff --git a/packages/api/src/resolvers/labels/index.ts b/packages/api/src/resolvers/labels/index.ts index 27cd0ab25..5e6e77c5a 100644 --- a/packages/api/src/resolvers/labels/index.ts +++ b/packages/api/src/resolvers/labels/index.ts @@ -1,4 +1,5 @@ import { Between } from 'typeorm' +import { isLabelSource, LabelSource } from '../../entity/entity_label' import { Label } from '../../entity/label' import { env } from '../../env' import { @@ -160,7 +161,7 @@ export const setLabelsResolver = authorized< >( async ( _, - { input: { pageId, labelIds, labels } }, + { input: { pageId, labelIds, labels, source } }, { uid, log, authTrx, pubsub } ) => { if (!labelIds && !labels) { @@ -170,6 +171,21 @@ export const setLabelsResolver = authorized< } } + let labelSource: LabelSource | undefined + + // check if source is valid + if (source) { + if (!isLabelSource(source)) { + log.error('invalid source', source) + + return { + errorCodes: [SetLabelsErrorCode.BadRequest], + } + } + + labelSource = source + } + try { let labelsSet: Label[] = [] @@ -191,7 +207,7 @@ export const setLabelsResolver = authorized< } // save labels in the library item - await saveLabelsInLibraryItem(labelsSet, pageId, uid, pubsub) + await saveLabelsInLibraryItem(labelsSet, pageId, uid, labelSource, pubsub) analytics.track({ userId: uid, diff --git a/packages/api/src/routers/svc/following.ts b/packages/api/src/routers/svc/following.ts index 73d8558ac..d2f31d1b6 100644 --- a/packages/api/src/routers/svc/following.ts +++ b/packages/api/src/routers/svc/following.ts @@ -71,9 +71,7 @@ export function followingServiceRouter() { result.identifiers[0].id, userId, [{ name: 'RSS' }], - undefined, - undefined, - true + req.body.addedToFollowingBy ) logger.info('RSS label added to the item') diff --git a/packages/api/src/schema.ts b/packages/api/src/schema.ts index 065e1739b..fc6828362 100755 --- a/packages/api/src/schema.ts +++ b/packages/api/src/schema.ts @@ -1446,6 +1446,7 @@ const schema = gql` createdAt: Date position: Int internal: Boolean + source: String } type LabelsSuccess { @@ -1532,6 +1533,7 @@ const schema = gql` pageId: ID! labelIds: [ID!] labels: [CreateLabelInput!] + source: String } union SetLabelsResult = SetLabelsSuccess | SetLabelsError diff --git a/packages/api/src/services/labels.ts b/packages/api/src/services/labels.ts index cb4c18310..9e8a508c8 100644 --- a/packages/api/src/services/labels.ts +++ b/packages/api/src/services/labels.ts @@ -1,6 +1,6 @@ import { DeepPartial, FindOptionsWhere, In } from 'typeorm' import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity' -import { EntityLabel } from '../entity/entity_label' +import { EntityLabel, LabelSource } from '../entity/entity_label' import { Label } from '../entity/label' import { LibraryItem } from '../entity/library_item' import { createPubSubClient, EntityType, PubsubClient } from '../pubsub' @@ -11,6 +11,7 @@ import { libraryItemRepository } from '../repository/library_item' type AddLabelsToLibraryItemEvent = { pageId: string labels: DeepPartial