Add get rules api
This commit is contained in:
@ -9,6 +9,18 @@ import {
|
||||
} from 'typeorm'
|
||||
import { User } from './user'
|
||||
|
||||
export enum RuleActionType {
|
||||
AddLabel = 'ADD_LABEL',
|
||||
Archive = 'ARCHIVE',
|
||||
MarkAsRead = 'MARK_AS_READ',
|
||||
SendNotification = 'SEND_NOTIFICATION',
|
||||
}
|
||||
|
||||
export interface RuleAction {
|
||||
type: RuleActionType
|
||||
params: string[]
|
||||
}
|
||||
|
||||
@Entity({ name: 'rules' })
|
||||
export class Rule {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
@ -25,7 +37,7 @@ export class Rule {
|
||||
filter!: string
|
||||
|
||||
@Column('simple-json')
|
||||
actions!: { type: string; params: string[] }[]
|
||||
actions!: RuleAction[]
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
description?: string | null
|
||||
|
||||
@ -1486,6 +1486,11 @@ export type QueryReminderArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type QueryRulesArgs = {
|
||||
enabled?: InputMaybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
|
||||
export type QuerySearchArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
@ -4309,7 +4314,7 @@ export type QueryResolvers<ContextType = ResolverContext, ParentType extends Res
|
||||
newsletterEmails?: Resolver<ResolversTypes['NewsletterEmailsResult'], ParentType, ContextType>;
|
||||
recentSearches?: Resolver<ResolversTypes['RecentSearchesResult'], ParentType, ContextType>;
|
||||
reminder?: Resolver<ResolversTypes['ReminderResult'], ParentType, ContextType, RequireFields<QueryReminderArgs, 'linkId'>>;
|
||||
rules?: Resolver<ResolversTypes['RulesResult'], ParentType, ContextType>;
|
||||
rules?: Resolver<ResolversTypes['RulesResult'], ParentType, ContextType, Partial<QueryRulesArgs>>;
|
||||
search?: Resolver<ResolversTypes['SearchResult'], ParentType, ContextType, Partial<QuerySearchArgs>>;
|
||||
sendInstallInstructions?: Resolver<ResolversTypes['SendInstallInstructionsResult'], ParentType, ContextType>;
|
||||
sharedArticle?: Resolver<ResolversTypes['SharedArticleResult'], ParentType, ContextType, RequireFields<QuerySharedArticleArgs, 'slug' | 'username'>>;
|
||||
|
||||
@ -1039,7 +1039,7 @@ type Query {
|
||||
newsletterEmails: NewsletterEmailsResult!
|
||||
recentSearches: RecentSearchesResult!
|
||||
reminder(linkId: ID!): ReminderResult!
|
||||
rules: RulesResult!
|
||||
rules(enabled: Boolean): RulesResult!
|
||||
search(after: String, first: Int, query: String): SearchResult!
|
||||
sendInstallInstructions: SendInstallInstructionsResult!
|
||||
sharedArticle(selectedHighlightId: String, slug: String!, username: String!): SharedArticleResult!
|
||||
|
||||
@ -59,6 +59,7 @@ import {
|
||||
reminderResolver,
|
||||
reportItemResolver,
|
||||
revokeApiKeyResolver,
|
||||
rulesResolver,
|
||||
saveArticleReadingProgressResolver,
|
||||
saveFileResolver,
|
||||
savePageResolver,
|
||||
@ -72,6 +73,7 @@ import {
|
||||
setLabelsForHighlightResolver,
|
||||
setLabelsResolver,
|
||||
setLinkArchivedResolver,
|
||||
setRuleResolver,
|
||||
setShareArticleResolver,
|
||||
setShareHighlightResolver,
|
||||
setUserPersonalizationResolver,
|
||||
@ -102,7 +104,6 @@ import {
|
||||
import { getPageByParam } from '../elastic/pages'
|
||||
import { recentSearchesResolver } from './recent_searches'
|
||||
import { optInFeatureResolver } from './features'
|
||||
import { setRuleResolver } from './rules'
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
type ResultResolveType = {
|
||||
@ -202,6 +203,7 @@ export const functionResolvers = {
|
||||
updatesSince: updatesSinceResolver,
|
||||
integrations: integrationsResolver,
|
||||
recentSearches: recentSearchesResolver,
|
||||
rules: rulesResolver,
|
||||
},
|
||||
User: {
|
||||
async sharedArticles(
|
||||
@ -613,4 +615,5 @@ export const functionResolvers = {
|
||||
...resultResolveTypeResolver('RecentSearches'),
|
||||
...resultResolveTypeResolver('OptInFeature'),
|
||||
...resultResolveTypeResolver('SetRule'),
|
||||
...resultResolveTypeResolver('Rules'),
|
||||
}
|
||||
|
||||
@ -21,3 +21,4 @@ export * from './popular_reads'
|
||||
export * from './webhooks'
|
||||
export * from './api_key'
|
||||
export * from './integrations'
|
||||
export * from './rules'
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
import { authorized } from '../../utils/helpers'
|
||||
import {
|
||||
MutationSetRuleArgs,
|
||||
QueryRulesArgs,
|
||||
RulesError,
|
||||
RulesErrorCode,
|
||||
RulesSuccess,
|
||||
SetRuleError,
|
||||
SetRuleErrorCode,
|
||||
SetRuleSuccess,
|
||||
@ -55,3 +59,49 @@ export const setRuleResolver = authorized<
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const rulesResolver = authorized<
|
||||
RulesSuccess,
|
||||
RulesError,
|
||||
QueryRulesArgs
|
||||
>(async (_, { enabled }, { claims, log }) => {
|
||||
log.info('Getting rules', {
|
||||
enabled,
|
||||
labels: {
|
||||
source: 'resolver',
|
||||
resolver: 'rulesResolver',
|
||||
uid: claims.uid,
|
||||
},
|
||||
})
|
||||
|
||||
try {
|
||||
const user = await getRepository(User).findOneBy({ id: claims.uid })
|
||||
if (!user) {
|
||||
return {
|
||||
errorCodes: [RulesErrorCode.Unauthorized],
|
||||
}
|
||||
}
|
||||
|
||||
const rules = await getRepository(Rule).findBy({
|
||||
user: { id: claims.uid },
|
||||
enabled: enabled === null ? undefined : enabled,
|
||||
})
|
||||
|
||||
return {
|
||||
rules,
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('Error getting rules', {
|
||||
error,
|
||||
labels: {
|
||||
source: 'resolver',
|
||||
resolver: 'rulesResolver',
|
||||
uid: claims.uid,
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
errorCodes: [RulesErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -2154,7 +2154,7 @@ const schema = gql`
|
||||
updatesSince(after: String, first: Int, since: Date!): UpdatesSinceResult!
|
||||
integrations: IntegrationsResult!
|
||||
recentSearches: RecentSearchesResult!
|
||||
rules: RulesResult!
|
||||
rules(enabled: Boolean): RulesResult!
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import { graphqlRequest, request } from '../util'
|
||||
import { User } from '../../src/entity/user'
|
||||
import { createTestUser, deleteTestUser } from '../db'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { Rule } from '../../src/entity/rule'
|
||||
import { Rule, RuleAction, RuleActionType } from '../../src/entity/rule'
|
||||
|
||||
describe('Rules Resolver', () => {
|
||||
const username = 'fakeUser'
|
||||
@ -31,7 +31,7 @@ describe('Rules Resolver', () => {
|
||||
const setRulesQuery = (
|
||||
name: string,
|
||||
filter: string,
|
||||
actions: { type: string; params: string[] }[],
|
||||
actions: RuleAction[],
|
||||
enabled: boolean,
|
||||
id?: string
|
||||
) => `
|
||||
@ -77,7 +77,7 @@ describe('Rules Resolver', () => {
|
||||
const query = setRulesQuery(
|
||||
'test rule',
|
||||
'test filter',
|
||||
[{ type: 'ADD_LABEL', params: [] }],
|
||||
[{ type: RuleActionType.SendNotification, params: [] }],
|
||||
true
|
||||
)
|
||||
|
||||
@ -85,4 +85,49 @@ describe('Rules Resolver', () => {
|
||||
expect(res.body.data.setRule.rule.filter).to.equal('test filter')
|
||||
})
|
||||
})
|
||||
|
||||
describe('get rules', () => {
|
||||
before(async () => {
|
||||
await getRepository(Rule).save({
|
||||
user: { id: user.id },
|
||||
name: 'test rule',
|
||||
filter: 'test filter',
|
||||
actions: [{ type: RuleActionType.SendNotification, params: [] }],
|
||||
enabled: true,
|
||||
})
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
await getRepository(Rule).delete({ user: { id: user.id } })
|
||||
})
|
||||
|
||||
const getRulesQuery = (enabled: boolean | null = null) => `
|
||||
query {
|
||||
rules (enabled: ${enabled}) {
|
||||
... on RulesSuccess {
|
||||
rules {
|
||||
id
|
||||
name
|
||||
filter
|
||||
actions {
|
||||
type
|
||||
params
|
||||
}
|
||||
enabled
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
... on RulesError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
it('should get rules', async () => {
|
||||
const res = await graphqlRequest(getRulesQuery(), authToken).expect(200)
|
||||
expect(res.body.data.rules.rules.length).to.equal(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
import { DataSource, EntityTarget, Repository } from 'typeorm'
|
||||
import { SnakeNamingStrategy } from 'typeorm-naming-strategies'
|
||||
import * as dotenv from 'dotenv'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
const AppDataSource = new DataSource({
|
||||
type: 'postgres',
|
||||
host: process.env.PG_HOST,
|
||||
port: Number(process.env.PG_PORT),
|
||||
schema: 'omnivore',
|
||||
username: process.env.PG_USER,
|
||||
password: process.env.PG_PASSWORD,
|
||||
database: process.env.PG_DB,
|
||||
logging: ['query', 'info'],
|
||||
entities: [__dirname + '/entity/**/*{.js,.ts}'],
|
||||
namingStrategy: new SnakeNamingStrategy(),
|
||||
})
|
||||
|
||||
export const createDBConnection = async () => {
|
||||
await AppDataSource.initialize()
|
||||
}
|
||||
|
||||
export const closeDBConnection = async () => {
|
||||
await AppDataSource.destroy()
|
||||
}
|
||||
|
||||
export const getRepository = <T>(entity: EntityTarget<T>): Repository<T> => {
|
||||
return AppDataSource.getRepository(entity)
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm'
|
||||
import { User } from './user'
|
||||
|
||||
@Entity({ name: 'rules' })
|
||||
export class Rules {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string
|
||||
|
||||
@ManyToOne(() => User, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user!: User
|
||||
|
||||
@Column('text')
|
||||
name!: string
|
||||
|
||||
@Column('text')
|
||||
filter!: string
|
||||
|
||||
@Column('simple-json')
|
||||
actions!: { type: string; params: string[] }[]
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
description?: string | null
|
||||
|
||||
@Column('boolean', { default: true })
|
||||
enabled!: boolean
|
||||
|
||||
@CreateDateColumn({ default: () => 'CURRENT_TIMESTAMP' })
|
||||
createdAt!: Date
|
||||
|
||||
@UpdateDateColumn({ default: () => 'CURRENT_TIMESTAMP' })
|
||||
updatedAt!: Date
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm'
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string
|
||||
|
||||
@Column('text')
|
||||
name!: string
|
||||
|
||||
@Column('text')
|
||||
email!: string
|
||||
|
||||
@Column('text')
|
||||
sourceUserId!: string
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt!: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt!: Date
|
||||
|
||||
@Column('varchar', { length: 255, nullable: true })
|
||||
password?: string
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm'
|
||||
import { User } from './user'
|
||||
|
||||
@Entity({ name: 'user_device_tokens' })
|
||||
export class UserDeviceToken {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string
|
||||
|
||||
@Column('text')
|
||||
token!: string
|
||||
|
||||
@ManyToOne(() => User)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user!: User
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt!: Date
|
||||
}
|
||||
Reference in New Issue
Block a user