Merge pull request #4041 from omnivore-app/feature/folder-policy-api
feat: folder expiration policy api
This commit is contained in:
49
packages/api/src/entity/folder_policy.ts
Normal file
49
packages/api/src/entity/folder_policy.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm'
|
||||
import { User } from './user'
|
||||
|
||||
export enum FolderPolicyAction {
|
||||
Delete = 'DELETE',
|
||||
Archive = 'ARCHIVE',
|
||||
}
|
||||
|
||||
@Entity({ name: 'folder_policy' })
|
||||
export class FolderPolicy {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string
|
||||
|
||||
@Column('uuid')
|
||||
userId!: string
|
||||
|
||||
@ManyToOne(() => User, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user!: User
|
||||
|
||||
@Column('text')
|
||||
folder!: string
|
||||
|
||||
@Column('enum', { enum: FolderPolicyAction })
|
||||
action!: FolderPolicyAction
|
||||
|
||||
@Column('int')
|
||||
afterDays!: number
|
||||
|
||||
@CreateDateColumn({
|
||||
type: 'timestamptz',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
createdAt!: Date
|
||||
|
||||
@UpdateDateColumn({
|
||||
type: 'timestamptz',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
updatedAt!: Date
|
||||
}
|
||||
@ -349,6 +349,29 @@ export type CreateArticleSuccess = {
|
||||
user: User;
|
||||
};
|
||||
|
||||
export type CreateFolderPolicyError = {
|
||||
__typename?: 'CreateFolderPolicyError';
|
||||
errorCodes: Array<CreateFolderPolicyErrorCode>;
|
||||
};
|
||||
|
||||
export enum CreateFolderPolicyErrorCode {
|
||||
BadRequest = 'BAD_REQUEST',
|
||||
Unauthorized = 'UNAUTHORIZED'
|
||||
}
|
||||
|
||||
export type CreateFolderPolicyInput = {
|
||||
action: FolderPolicyAction;
|
||||
afterDays: Scalars['Int'];
|
||||
folder: Scalars['String'];
|
||||
};
|
||||
|
||||
export type CreateFolderPolicyResult = CreateFolderPolicyError | CreateFolderPolicySuccess;
|
||||
|
||||
export type CreateFolderPolicySuccess = {
|
||||
__typename?: 'CreateFolderPolicySuccess';
|
||||
policy: FolderPolicy;
|
||||
};
|
||||
|
||||
export type CreateGroupError = {
|
||||
__typename?: 'CreateGroupError';
|
||||
errorCodes: Array<CreateGroupErrorCode>;
|
||||
@ -619,6 +642,22 @@ export type DeleteFilterSuccess = {
|
||||
filter: Filter;
|
||||
};
|
||||
|
||||
export type DeleteFolderPolicyError = {
|
||||
__typename?: 'DeleteFolderPolicyError';
|
||||
errorCodes: Array<DeleteFolderPolicyErrorCode>;
|
||||
};
|
||||
|
||||
export enum DeleteFolderPolicyErrorCode {
|
||||
Unauthorized = 'UNAUTHORIZED'
|
||||
}
|
||||
|
||||
export type DeleteFolderPolicyResult = DeleteFolderPolicyError | DeleteFolderPolicySuccess;
|
||||
|
||||
export type DeleteFolderPolicySuccess = {
|
||||
__typename?: 'DeleteFolderPolicySuccess';
|
||||
success: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type DeleteHighlightError = {
|
||||
__typename?: 'DeleteHighlightError';
|
||||
errorCodes: Array<DeleteHighlightErrorCode>;
|
||||
@ -1081,6 +1120,38 @@ export type FiltersSuccess = {
|
||||
filters: Array<Filter>;
|
||||
};
|
||||
|
||||
export type FolderPoliciesError = {
|
||||
__typename?: 'FolderPoliciesError';
|
||||
errorCodes: Array<FolderPoliciesErrorCode>;
|
||||
};
|
||||
|
||||
export enum FolderPoliciesErrorCode {
|
||||
BadRequest = 'BAD_REQUEST',
|
||||
Unauthorized = 'UNAUTHORIZED'
|
||||
}
|
||||
|
||||
export type FolderPoliciesResult = FolderPoliciesError | FolderPoliciesSuccess;
|
||||
|
||||
export type FolderPoliciesSuccess = {
|
||||
__typename?: 'FolderPoliciesSuccess';
|
||||
policies: Array<FolderPolicy>;
|
||||
};
|
||||
|
||||
export type FolderPolicy = {
|
||||
__typename?: 'FolderPolicy';
|
||||
action: FolderPolicyAction;
|
||||
afterDays: Scalars['Int'];
|
||||
createdAt: Scalars['Date'];
|
||||
folder: Scalars['String'];
|
||||
id: Scalars['ID'];
|
||||
updatedAt: Scalars['Date'];
|
||||
};
|
||||
|
||||
export enum FolderPolicyAction {
|
||||
Archive = 'ARCHIVE',
|
||||
Delete = 'DELETE'
|
||||
}
|
||||
|
||||
export type GenerateApiKeyError = {
|
||||
__typename?: 'GenerateApiKeyError';
|
||||
errorCodes: Array<GenerateApiKeyErrorCode>;
|
||||
@ -1723,6 +1794,7 @@ export type Mutation = {
|
||||
bulkAction: BulkActionResult;
|
||||
createArticle: CreateArticleResult;
|
||||
createArticleSavingRequest: CreateArticleSavingRequestResult;
|
||||
createFolderPolicy: CreateFolderPolicyResult;
|
||||
createGroup: CreateGroupResult;
|
||||
createHighlight: CreateHighlightResult;
|
||||
createLabel: CreateLabelResult;
|
||||
@ -1731,6 +1803,7 @@ export type Mutation = {
|
||||
deleteDiscoverArticle: DeleteDiscoverArticleResult;
|
||||
deleteDiscoverFeed: DeleteDiscoverFeedResult;
|
||||
deleteFilter: DeleteFilterResult;
|
||||
deleteFolderPolicy: DeleteFolderPolicyResult;
|
||||
deleteHighlight: DeleteHighlightResult;
|
||||
deleteIntegration: DeleteIntegrationResult;
|
||||
deleteLabel: DeleteLabelResult;
|
||||
@ -1780,6 +1853,7 @@ export type Mutation = {
|
||||
unsubscribe: UnsubscribeResult;
|
||||
updateEmail: UpdateEmailResult;
|
||||
updateFilter: UpdateFilterResult;
|
||||
updateFolderPolicy: UpdateFolderPolicyResult;
|
||||
updateHighlight: UpdateHighlightResult;
|
||||
updateLabel: UpdateLabelResult;
|
||||
updateNewsletterEmail: UpdateNewsletterEmailResult;
|
||||
@ -1822,6 +1896,11 @@ export type MutationCreateArticleSavingRequestArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateFolderPolicyArgs = {
|
||||
input: CreateFolderPolicyInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateGroupArgs = {
|
||||
input: CreateGroupInput;
|
||||
};
|
||||
@ -1862,6 +1941,11 @@ export type MutationDeleteFilterArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteFolderPolicyArgs = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteHighlightArgs = {
|
||||
highlightId: Scalars['ID'];
|
||||
};
|
||||
@ -2095,6 +2179,11 @@ export type MutationUpdateFilterArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationUpdateFolderPolicyArgs = {
|
||||
input: UpdateFolderPolicyInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationUpdateHighlightArgs = {
|
||||
input: UpdateHighlightInput;
|
||||
};
|
||||
@ -2280,6 +2369,7 @@ export type Query = {
|
||||
discoverTopics: GetDiscoverTopicResults;
|
||||
feeds: FeedsResult;
|
||||
filters: FiltersResult;
|
||||
folderPolicies: FolderPoliciesResult;
|
||||
getDiscoverFeedArticles: GetDiscoverFeedArticleResults;
|
||||
getUserPersonalization: GetUserPersonalizationResult;
|
||||
groups: GroupsResult;
|
||||
@ -3555,6 +3645,29 @@ export type UpdateFilterSuccess = {
|
||||
filter: Filter;
|
||||
};
|
||||
|
||||
export type UpdateFolderPolicyError = {
|
||||
__typename?: 'UpdateFolderPolicyError';
|
||||
errorCodes: Array<UpdateFolderPolicyErrorCode>;
|
||||
};
|
||||
|
||||
export enum UpdateFolderPolicyErrorCode {
|
||||
BadRequest = 'BAD_REQUEST',
|
||||
Unauthorized = 'UNAUTHORIZED'
|
||||
}
|
||||
|
||||
export type UpdateFolderPolicyInput = {
|
||||
action?: InputMaybe<FolderPolicyAction>;
|
||||
afterDays?: InputMaybe<Scalars['Int']>;
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type UpdateFolderPolicyResult = UpdateFolderPolicyError | UpdateFolderPolicySuccess;
|
||||
|
||||
export type UpdateFolderPolicySuccess = {
|
||||
__typename?: 'UpdateFolderPolicySuccess';
|
||||
policy: FolderPolicy;
|
||||
};
|
||||
|
||||
export type UpdateHighlightError = {
|
||||
__typename?: 'UpdateHighlightError';
|
||||
errorCodes: Array<UpdateHighlightErrorCode>;
|
||||
@ -4180,6 +4293,11 @@ export type ResolversTypes = {
|
||||
CreateArticleSavingRequestResult: ResolversTypes['CreateArticleSavingRequestError'] | ResolversTypes['CreateArticleSavingRequestSuccess'];
|
||||
CreateArticleSavingRequestSuccess: ResolverTypeWrapper<CreateArticleSavingRequestSuccess>;
|
||||
CreateArticleSuccess: ResolverTypeWrapper<CreateArticleSuccess>;
|
||||
CreateFolderPolicyError: ResolverTypeWrapper<CreateFolderPolicyError>;
|
||||
CreateFolderPolicyErrorCode: CreateFolderPolicyErrorCode;
|
||||
CreateFolderPolicyInput: CreateFolderPolicyInput;
|
||||
CreateFolderPolicyResult: ResolversTypes['CreateFolderPolicyError'] | ResolversTypes['CreateFolderPolicySuccess'];
|
||||
CreateFolderPolicySuccess: ResolverTypeWrapper<CreateFolderPolicySuccess>;
|
||||
CreateGroupError: ResolverTypeWrapper<CreateGroupError>;
|
||||
CreateGroupErrorCode: CreateGroupErrorCode;
|
||||
CreateGroupInput: CreateGroupInput;
|
||||
@ -4234,6 +4352,10 @@ export type ResolversTypes = {
|
||||
DeleteFilterErrorCode: DeleteFilterErrorCode;
|
||||
DeleteFilterResult: ResolversTypes['DeleteFilterError'] | ResolversTypes['DeleteFilterSuccess'];
|
||||
DeleteFilterSuccess: ResolverTypeWrapper<DeleteFilterSuccess>;
|
||||
DeleteFolderPolicyError: ResolverTypeWrapper<DeleteFolderPolicyError>;
|
||||
DeleteFolderPolicyErrorCode: DeleteFolderPolicyErrorCode;
|
||||
DeleteFolderPolicyResult: ResolversTypes['DeleteFolderPolicyError'] | ResolversTypes['DeleteFolderPolicySuccess'];
|
||||
DeleteFolderPolicySuccess: ResolverTypeWrapper<DeleteFolderPolicySuccess>;
|
||||
DeleteHighlightError: ResolverTypeWrapper<DeleteHighlightError>;
|
||||
DeleteHighlightErrorCode: DeleteHighlightErrorCode;
|
||||
DeleteHighlightReplyError: ResolverTypeWrapper<DeleteHighlightReplyError>;
|
||||
@ -4324,6 +4446,12 @@ export type ResolversTypes = {
|
||||
FiltersResult: ResolversTypes['FiltersError'] | ResolversTypes['FiltersSuccess'];
|
||||
FiltersSuccess: ResolverTypeWrapper<FiltersSuccess>;
|
||||
Float: ResolverTypeWrapper<Scalars['Float']>;
|
||||
FolderPoliciesError: ResolverTypeWrapper<FolderPoliciesError>;
|
||||
FolderPoliciesErrorCode: FolderPoliciesErrorCode;
|
||||
FolderPoliciesResult: ResolversTypes['FolderPoliciesError'] | ResolversTypes['FolderPoliciesSuccess'];
|
||||
FolderPoliciesSuccess: ResolverTypeWrapper<FolderPoliciesSuccess>;
|
||||
FolderPolicy: ResolverTypeWrapper<FolderPolicy>;
|
||||
FolderPolicyAction: FolderPolicyAction;
|
||||
GenerateApiKeyError: ResolverTypeWrapper<GenerateApiKeyError>;
|
||||
GenerateApiKeyErrorCode: GenerateApiKeyErrorCode;
|
||||
GenerateApiKeyInput: GenerateApiKeyInput;
|
||||
@ -4659,6 +4787,11 @@ export type ResolversTypes = {
|
||||
UpdateFilterInput: UpdateFilterInput;
|
||||
UpdateFilterResult: ResolversTypes['UpdateFilterError'] | ResolversTypes['UpdateFilterSuccess'];
|
||||
UpdateFilterSuccess: ResolverTypeWrapper<UpdateFilterSuccess>;
|
||||
UpdateFolderPolicyError: ResolverTypeWrapper<UpdateFolderPolicyError>;
|
||||
UpdateFolderPolicyErrorCode: UpdateFolderPolicyErrorCode;
|
||||
UpdateFolderPolicyInput: UpdateFolderPolicyInput;
|
||||
UpdateFolderPolicyResult: ResolversTypes['UpdateFolderPolicyError'] | ResolversTypes['UpdateFolderPolicySuccess'];
|
||||
UpdateFolderPolicySuccess: ResolverTypeWrapper<UpdateFolderPolicySuccess>;
|
||||
UpdateHighlightError: ResolverTypeWrapper<UpdateHighlightError>;
|
||||
UpdateHighlightErrorCode: UpdateHighlightErrorCode;
|
||||
UpdateHighlightInput: UpdateHighlightInput;
|
||||
@ -4794,6 +4927,10 @@ export type ResolversParentTypes = {
|
||||
CreateArticleSavingRequestResult: ResolversParentTypes['CreateArticleSavingRequestError'] | ResolversParentTypes['CreateArticleSavingRequestSuccess'];
|
||||
CreateArticleSavingRequestSuccess: CreateArticleSavingRequestSuccess;
|
||||
CreateArticleSuccess: CreateArticleSuccess;
|
||||
CreateFolderPolicyError: CreateFolderPolicyError;
|
||||
CreateFolderPolicyInput: CreateFolderPolicyInput;
|
||||
CreateFolderPolicyResult: ResolversParentTypes['CreateFolderPolicyError'] | ResolversParentTypes['CreateFolderPolicySuccess'];
|
||||
CreateFolderPolicySuccess: CreateFolderPolicySuccess;
|
||||
CreateGroupError: CreateGroupError;
|
||||
CreateGroupInput: CreateGroupInput;
|
||||
CreateGroupResult: ResolversParentTypes['CreateGroupError'] | ResolversParentTypes['CreateGroupSuccess'];
|
||||
@ -4837,6 +4974,9 @@ export type ResolversParentTypes = {
|
||||
DeleteFilterError: DeleteFilterError;
|
||||
DeleteFilterResult: ResolversParentTypes['DeleteFilterError'] | ResolversParentTypes['DeleteFilterSuccess'];
|
||||
DeleteFilterSuccess: DeleteFilterSuccess;
|
||||
DeleteFolderPolicyError: DeleteFolderPolicyError;
|
||||
DeleteFolderPolicyResult: ResolversParentTypes['DeleteFolderPolicyError'] | ResolversParentTypes['DeleteFolderPolicySuccess'];
|
||||
DeleteFolderPolicySuccess: DeleteFolderPolicySuccess;
|
||||
DeleteHighlightError: DeleteHighlightError;
|
||||
DeleteHighlightReplyError: DeleteHighlightReplyError;
|
||||
DeleteHighlightReplyResult: ResolversParentTypes['DeleteHighlightReplyError'] | ResolversParentTypes['DeleteHighlightReplySuccess'];
|
||||
@ -4906,6 +5046,10 @@ export type ResolversParentTypes = {
|
||||
FiltersResult: ResolversParentTypes['FiltersError'] | ResolversParentTypes['FiltersSuccess'];
|
||||
FiltersSuccess: FiltersSuccess;
|
||||
Float: Scalars['Float'];
|
||||
FolderPoliciesError: FolderPoliciesError;
|
||||
FolderPoliciesResult: ResolversParentTypes['FolderPoliciesError'] | ResolversParentTypes['FolderPoliciesSuccess'];
|
||||
FolderPoliciesSuccess: FolderPoliciesSuccess;
|
||||
FolderPolicy: FolderPolicy;
|
||||
GenerateApiKeyError: GenerateApiKeyError;
|
||||
GenerateApiKeyInput: GenerateApiKeyInput;
|
||||
GenerateApiKeyResult: ResolversParentTypes['GenerateApiKeyError'] | ResolversParentTypes['GenerateApiKeySuccess'];
|
||||
@ -5166,6 +5310,10 @@ export type ResolversParentTypes = {
|
||||
UpdateFilterInput: UpdateFilterInput;
|
||||
UpdateFilterResult: ResolversParentTypes['UpdateFilterError'] | ResolversParentTypes['UpdateFilterSuccess'];
|
||||
UpdateFilterSuccess: UpdateFilterSuccess;
|
||||
UpdateFolderPolicyError: UpdateFolderPolicyError;
|
||||
UpdateFolderPolicyInput: UpdateFolderPolicyInput;
|
||||
UpdateFolderPolicyResult: ResolversParentTypes['UpdateFolderPolicyError'] | ResolversParentTypes['UpdateFolderPolicySuccess'];
|
||||
UpdateFolderPolicySuccess: UpdateFolderPolicySuccess;
|
||||
UpdateHighlightError: UpdateHighlightError;
|
||||
UpdateHighlightInput: UpdateHighlightInput;
|
||||
UpdateHighlightReplyError: UpdateHighlightReplyError;
|
||||
@ -5469,6 +5617,20 @@ export type CreateArticleSuccessResolvers<ContextType = ResolverContext, ParentT
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type CreateFolderPolicyErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateFolderPolicyError'] = ResolversParentTypes['CreateFolderPolicyError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['CreateFolderPolicyErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type CreateFolderPolicyResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateFolderPolicyResult'] = ResolversParentTypes['CreateFolderPolicyResult']> = {
|
||||
__resolveType: TypeResolveFn<'CreateFolderPolicyError' | 'CreateFolderPolicySuccess', ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type CreateFolderPolicySuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateFolderPolicySuccess'] = ResolversParentTypes['CreateFolderPolicySuccess']> = {
|
||||
policy?: Resolver<ResolversTypes['FolderPolicy'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type CreateGroupErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateGroupError'] = ResolversParentTypes['CreateGroupError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['CreateGroupErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
@ -5627,6 +5789,20 @@ export type DeleteFilterSuccessResolvers<ContextType = ResolverContext, ParentTy
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type DeleteFolderPolicyErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['DeleteFolderPolicyError'] = ResolversParentTypes['DeleteFolderPolicyError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['DeleteFolderPolicyErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type DeleteFolderPolicyResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['DeleteFolderPolicyResult'] = ResolversParentTypes['DeleteFolderPolicyResult']> = {
|
||||
__resolveType: TypeResolveFn<'DeleteFolderPolicyError' | 'DeleteFolderPolicySuccess', ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type DeleteFolderPolicySuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['DeleteFolderPolicySuccess'] = ResolversParentTypes['DeleteFolderPolicySuccess']> = {
|
||||
success?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type DeleteHighlightErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['DeleteHighlightError'] = ResolversParentTypes['DeleteHighlightError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['DeleteHighlightErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
@ -5992,6 +6168,30 @@ export type FiltersSuccessResolvers<ContextType = ResolverContext, ParentType ex
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type FolderPoliciesErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['FolderPoliciesError'] = ResolversParentTypes['FolderPoliciesError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['FolderPoliciesErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type FolderPoliciesResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['FolderPoliciesResult'] = ResolversParentTypes['FolderPoliciesResult']> = {
|
||||
__resolveType: TypeResolveFn<'FolderPoliciesError' | 'FolderPoliciesSuccess', ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type FolderPoliciesSuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['FolderPoliciesSuccess'] = ResolversParentTypes['FolderPoliciesSuccess']> = {
|
||||
policies?: Resolver<Array<ResolversTypes['FolderPolicy']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type FolderPolicyResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['FolderPolicy'] = ResolversParentTypes['FolderPolicy']> = {
|
||||
action?: Resolver<ResolversTypes['FolderPolicyAction'], ParentType, ContextType>;
|
||||
afterDays?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
createdAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
folder?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type GenerateApiKeyErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['GenerateApiKeyError'] = ResolversParentTypes['GenerateApiKeyError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['GenerateApiKeyErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
@ -6486,6 +6686,7 @@ export type MutationResolvers<ContextType = ResolverContext, ParentType extends
|
||||
bulkAction?: Resolver<ResolversTypes['BulkActionResult'], ParentType, ContextType, RequireFields<MutationBulkActionArgs, 'action' | 'query'>>;
|
||||
createArticle?: Resolver<ResolversTypes['CreateArticleResult'], ParentType, ContextType, RequireFields<MutationCreateArticleArgs, 'input'>>;
|
||||
createArticleSavingRequest?: Resolver<ResolversTypes['CreateArticleSavingRequestResult'], ParentType, ContextType, RequireFields<MutationCreateArticleSavingRequestArgs, 'input'>>;
|
||||
createFolderPolicy?: Resolver<ResolversTypes['CreateFolderPolicyResult'], ParentType, ContextType, RequireFields<MutationCreateFolderPolicyArgs, 'input'>>;
|
||||
createGroup?: Resolver<ResolversTypes['CreateGroupResult'], ParentType, ContextType, RequireFields<MutationCreateGroupArgs, 'input'>>;
|
||||
createHighlight?: Resolver<ResolversTypes['CreateHighlightResult'], ParentType, ContextType, RequireFields<MutationCreateHighlightArgs, 'input'>>;
|
||||
createLabel?: Resolver<ResolversTypes['CreateLabelResult'], ParentType, ContextType, RequireFields<MutationCreateLabelArgs, 'input'>>;
|
||||
@ -6494,6 +6695,7 @@ export type MutationResolvers<ContextType = ResolverContext, ParentType extends
|
||||
deleteDiscoverArticle?: Resolver<ResolversTypes['DeleteDiscoverArticleResult'], ParentType, ContextType, RequireFields<MutationDeleteDiscoverArticleArgs, 'input'>>;
|
||||
deleteDiscoverFeed?: Resolver<ResolversTypes['DeleteDiscoverFeedResult'], ParentType, ContextType, RequireFields<MutationDeleteDiscoverFeedArgs, 'input'>>;
|
||||
deleteFilter?: Resolver<ResolversTypes['DeleteFilterResult'], ParentType, ContextType, RequireFields<MutationDeleteFilterArgs, 'id'>>;
|
||||
deleteFolderPolicy?: Resolver<ResolversTypes['DeleteFolderPolicyResult'], ParentType, ContextType, RequireFields<MutationDeleteFolderPolicyArgs, 'id'>>;
|
||||
deleteHighlight?: Resolver<ResolversTypes['DeleteHighlightResult'], ParentType, ContextType, RequireFields<MutationDeleteHighlightArgs, 'highlightId'>>;
|
||||
deleteIntegration?: Resolver<ResolversTypes['DeleteIntegrationResult'], ParentType, ContextType, RequireFields<MutationDeleteIntegrationArgs, 'id'>>;
|
||||
deleteLabel?: Resolver<ResolversTypes['DeleteLabelResult'], ParentType, ContextType, RequireFields<MutationDeleteLabelArgs, 'id'>>;
|
||||
@ -6543,6 +6745,7 @@ export type MutationResolvers<ContextType = ResolverContext, ParentType extends
|
||||
unsubscribe?: Resolver<ResolversTypes['UnsubscribeResult'], ParentType, ContextType, RequireFields<MutationUnsubscribeArgs, 'name'>>;
|
||||
updateEmail?: Resolver<ResolversTypes['UpdateEmailResult'], ParentType, ContextType, RequireFields<MutationUpdateEmailArgs, 'input'>>;
|
||||
updateFilter?: Resolver<ResolversTypes['UpdateFilterResult'], ParentType, ContextType, RequireFields<MutationUpdateFilterArgs, 'input'>>;
|
||||
updateFolderPolicy?: Resolver<ResolversTypes['UpdateFolderPolicyResult'], ParentType, ContextType, RequireFields<MutationUpdateFolderPolicyArgs, 'input'>>;
|
||||
updateHighlight?: Resolver<ResolversTypes['UpdateHighlightResult'], ParentType, ContextType, RequireFields<MutationUpdateHighlightArgs, 'input'>>;
|
||||
updateLabel?: Resolver<ResolversTypes['UpdateLabelResult'], ParentType, ContextType, RequireFields<MutationUpdateLabelArgs, 'input'>>;
|
||||
updateNewsletterEmail?: Resolver<ResolversTypes['UpdateNewsletterEmailResult'], ParentType, ContextType, RequireFields<MutationUpdateNewsletterEmailArgs, 'input'>>;
|
||||
@ -6639,6 +6842,7 @@ export type QueryResolvers<ContextType = ResolverContext, ParentType extends Res
|
||||
discoverTopics?: Resolver<ResolversTypes['GetDiscoverTopicResults'], ParentType, ContextType>;
|
||||
feeds?: Resolver<ResolversTypes['FeedsResult'], ParentType, ContextType, RequireFields<QueryFeedsArgs, 'input'>>;
|
||||
filters?: Resolver<ResolversTypes['FiltersResult'], ParentType, ContextType>;
|
||||
folderPolicies?: Resolver<ResolversTypes['FolderPoliciesResult'], ParentType, ContextType>;
|
||||
getDiscoverFeedArticles?: Resolver<ResolversTypes['GetDiscoverFeedArticleResults'], ParentType, ContextType, RequireFields<QueryGetDiscoverFeedArticlesArgs, 'discoverTopicId'>>;
|
||||
getUserPersonalization?: Resolver<ResolversTypes['GetUserPersonalizationResult'], ParentType, ContextType>;
|
||||
groups?: Resolver<ResolversTypes['GroupsResult'], ParentType, ContextType>;
|
||||
@ -7391,6 +7595,20 @@ export type UpdateFilterSuccessResolvers<ContextType = ResolverContext, ParentTy
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type UpdateFolderPolicyErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['UpdateFolderPolicyError'] = ResolversParentTypes['UpdateFolderPolicyError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['UpdateFolderPolicyErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type UpdateFolderPolicyResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['UpdateFolderPolicyResult'] = ResolversParentTypes['UpdateFolderPolicyResult']> = {
|
||||
__resolveType: TypeResolveFn<'UpdateFolderPolicyError' | 'UpdateFolderPolicySuccess', ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type UpdateFolderPolicySuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['UpdateFolderPolicySuccess'] = ResolversParentTypes['UpdateFolderPolicySuccess']> = {
|
||||
policy?: Resolver<ResolversTypes['FolderPolicy'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type UpdateHighlightErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['UpdateHighlightError'] = ResolversParentTypes['UpdateHighlightError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['UpdateHighlightErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
@ -7734,6 +7952,9 @@ export type Resolvers<ContextType = ResolverContext> = {
|
||||
CreateArticleSavingRequestResult?: CreateArticleSavingRequestResultResolvers<ContextType>;
|
||||
CreateArticleSavingRequestSuccess?: CreateArticleSavingRequestSuccessResolvers<ContextType>;
|
||||
CreateArticleSuccess?: CreateArticleSuccessResolvers<ContextType>;
|
||||
CreateFolderPolicyError?: CreateFolderPolicyErrorResolvers<ContextType>;
|
||||
CreateFolderPolicyResult?: CreateFolderPolicyResultResolvers<ContextType>;
|
||||
CreateFolderPolicySuccess?: CreateFolderPolicySuccessResolvers<ContextType>;
|
||||
CreateGroupError?: CreateGroupErrorResolvers<ContextType>;
|
||||
CreateGroupResult?: CreateGroupResultResolvers<ContextType>;
|
||||
CreateGroupSuccess?: CreateGroupSuccessResolvers<ContextType>;
|
||||
@ -7768,6 +7989,9 @@ export type Resolvers<ContextType = ResolverContext> = {
|
||||
DeleteFilterError?: DeleteFilterErrorResolvers<ContextType>;
|
||||
DeleteFilterResult?: DeleteFilterResultResolvers<ContextType>;
|
||||
DeleteFilterSuccess?: DeleteFilterSuccessResolvers<ContextType>;
|
||||
DeleteFolderPolicyError?: DeleteFolderPolicyErrorResolvers<ContextType>;
|
||||
DeleteFolderPolicyResult?: DeleteFolderPolicyResultResolvers<ContextType>;
|
||||
DeleteFolderPolicySuccess?: DeleteFolderPolicySuccessResolvers<ContextType>;
|
||||
DeleteHighlightError?: DeleteHighlightErrorResolvers<ContextType>;
|
||||
DeleteHighlightReplyError?: DeleteHighlightReplyErrorResolvers<ContextType>;
|
||||
DeleteHighlightReplyResult?: DeleteHighlightReplyResultResolvers<ContextType>;
|
||||
@ -7833,6 +8057,10 @@ export type Resolvers<ContextType = ResolverContext> = {
|
||||
FiltersError?: FiltersErrorResolvers<ContextType>;
|
||||
FiltersResult?: FiltersResultResolvers<ContextType>;
|
||||
FiltersSuccess?: FiltersSuccessResolvers<ContextType>;
|
||||
FolderPoliciesError?: FolderPoliciesErrorResolvers<ContextType>;
|
||||
FolderPoliciesResult?: FolderPoliciesResultResolvers<ContextType>;
|
||||
FolderPoliciesSuccess?: FolderPoliciesSuccessResolvers<ContextType>;
|
||||
FolderPolicy?: FolderPolicyResolvers<ContextType>;
|
||||
GenerateApiKeyError?: GenerateApiKeyErrorResolvers<ContextType>;
|
||||
GenerateApiKeyResult?: GenerateApiKeyResultResolvers<ContextType>;
|
||||
GenerateApiKeySuccess?: GenerateApiKeySuccessResolvers<ContextType>;
|
||||
@ -8054,6 +8282,9 @@ export type Resolvers<ContextType = ResolverContext> = {
|
||||
UpdateFilterError?: UpdateFilterErrorResolvers<ContextType>;
|
||||
UpdateFilterResult?: UpdateFilterResultResolvers<ContextType>;
|
||||
UpdateFilterSuccess?: UpdateFilterSuccessResolvers<ContextType>;
|
||||
UpdateFolderPolicyError?: UpdateFolderPolicyErrorResolvers<ContextType>;
|
||||
UpdateFolderPolicyResult?: UpdateFolderPolicyResultResolvers<ContextType>;
|
||||
UpdateFolderPolicySuccess?: UpdateFolderPolicySuccessResolvers<ContextType>;
|
||||
UpdateHighlightError?: UpdateHighlightErrorResolvers<ContextType>;
|
||||
UpdateHighlightReplyError?: UpdateHighlightReplyErrorResolvers<ContextType>;
|
||||
UpdateHighlightReplyResult?: UpdateHighlightReplyResultResolvers<ContextType>;
|
||||
|
||||
@ -307,6 +307,27 @@ type CreateArticleSuccess {
|
||||
user: User!
|
||||
}
|
||||
|
||||
type CreateFolderPolicyError {
|
||||
errorCodes: [CreateFolderPolicyErrorCode!]!
|
||||
}
|
||||
|
||||
enum CreateFolderPolicyErrorCode {
|
||||
BAD_REQUEST
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
input CreateFolderPolicyInput {
|
||||
action: FolderPolicyAction!
|
||||
afterDays: Int!
|
||||
folder: String!
|
||||
}
|
||||
|
||||
union CreateFolderPolicyResult = CreateFolderPolicyError | CreateFolderPolicySuccess
|
||||
|
||||
type CreateFolderPolicySuccess {
|
||||
policy: FolderPolicy!
|
||||
}
|
||||
|
||||
type CreateGroupError {
|
||||
errorCodes: [CreateGroupErrorCode!]!
|
||||
}
|
||||
@ -557,6 +578,20 @@ type DeleteFilterSuccess {
|
||||
filter: Filter!
|
||||
}
|
||||
|
||||
type DeleteFolderPolicyError {
|
||||
errorCodes: [DeleteFolderPolicyErrorCode!]!
|
||||
}
|
||||
|
||||
enum DeleteFolderPolicyErrorCode {
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union DeleteFolderPolicyResult = DeleteFolderPolicyError | DeleteFolderPolicySuccess
|
||||
|
||||
type DeleteFolderPolicySuccess {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type DeleteHighlightError {
|
||||
errorCodes: [DeleteHighlightErrorCode!]!
|
||||
}
|
||||
@ -972,6 +1007,35 @@ type FiltersSuccess {
|
||||
filters: [Filter!]!
|
||||
}
|
||||
|
||||
type FolderPoliciesError {
|
||||
errorCodes: [FolderPoliciesErrorCode!]!
|
||||
}
|
||||
|
||||
enum FolderPoliciesErrorCode {
|
||||
BAD_REQUEST
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union FolderPoliciesResult = FolderPoliciesError | FolderPoliciesSuccess
|
||||
|
||||
type FolderPoliciesSuccess {
|
||||
policies: [FolderPolicy!]!
|
||||
}
|
||||
|
||||
type FolderPolicy {
|
||||
action: FolderPolicyAction!
|
||||
afterDays: Int!
|
||||
createdAt: Date!
|
||||
folder: String!
|
||||
id: ID!
|
||||
updatedAt: Date!
|
||||
}
|
||||
|
||||
enum FolderPolicyAction {
|
||||
ARCHIVE
|
||||
DELETE
|
||||
}
|
||||
|
||||
type GenerateApiKeyError {
|
||||
errorCodes: [GenerateApiKeyErrorCode!]!
|
||||
}
|
||||
@ -1555,6 +1619,7 @@ type Mutation {
|
||||
bulkAction(action: BulkActionType!, arguments: JSON, async: Boolean, expectedCount: Int, labelIds: [ID!], query: String!): BulkActionResult!
|
||||
createArticle(input: CreateArticleInput!): CreateArticleResult!
|
||||
createArticleSavingRequest(input: CreateArticleSavingRequestInput!): CreateArticleSavingRequestResult!
|
||||
createFolderPolicy(input: CreateFolderPolicyInput!): CreateFolderPolicyResult!
|
||||
createGroup(input: CreateGroupInput!): CreateGroupResult!
|
||||
createHighlight(input: CreateHighlightInput!): CreateHighlightResult!
|
||||
createLabel(input: CreateLabelInput!): CreateLabelResult!
|
||||
@ -1563,6 +1628,7 @@ type Mutation {
|
||||
deleteDiscoverArticle(input: DeleteDiscoverArticleInput!): DeleteDiscoverArticleResult!
|
||||
deleteDiscoverFeed(input: DeleteDiscoverFeedInput!): DeleteDiscoverFeedResult!
|
||||
deleteFilter(id: ID!): DeleteFilterResult!
|
||||
deleteFolderPolicy(id: ID!): DeleteFolderPolicyResult!
|
||||
deleteHighlight(highlightId: ID!): DeleteHighlightResult!
|
||||
deleteIntegration(id: ID!): DeleteIntegrationResult!
|
||||
deleteLabel(id: ID!): DeleteLabelResult!
|
||||
@ -1612,6 +1678,7 @@ type Mutation {
|
||||
unsubscribe(name: String!, subscriptionId: ID): UnsubscribeResult!
|
||||
updateEmail(input: UpdateEmailInput!): UpdateEmailResult!
|
||||
updateFilter(input: UpdateFilterInput!): UpdateFilterResult!
|
||||
updateFolderPolicy(input: UpdateFolderPolicyInput!): UpdateFolderPolicyResult!
|
||||
updateHighlight(input: UpdateHighlightInput!): UpdateHighlightResult!
|
||||
updateLabel(input: UpdateLabelInput!): UpdateLabelResult!
|
||||
updateNewsletterEmail(input: UpdateNewsletterEmailInput!): UpdateNewsletterEmailResult!
|
||||
@ -1754,6 +1821,7 @@ type Query {
|
||||
discoverTopics: GetDiscoverTopicResults!
|
||||
feeds(input: FeedsInput!): FeedsResult!
|
||||
filters: FiltersResult!
|
||||
folderPolicies: FolderPoliciesResult!
|
||||
getDiscoverFeedArticles(after: String, discoverTopicId: String!, feedId: ID, first: Int): GetDiscoverFeedArticleResults!
|
||||
getUserPersonalization: GetUserPersonalizationResult!
|
||||
groups: GroupsResult!
|
||||
@ -2835,6 +2903,27 @@ type UpdateFilterSuccess {
|
||||
filter: Filter!
|
||||
}
|
||||
|
||||
type UpdateFolderPolicyError {
|
||||
errorCodes: [UpdateFolderPolicyErrorCode!]!
|
||||
}
|
||||
|
||||
enum UpdateFolderPolicyErrorCode {
|
||||
BAD_REQUEST
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
input UpdateFolderPolicyInput {
|
||||
action: FolderPolicyAction
|
||||
afterDays: Int
|
||||
id: ID!
|
||||
}
|
||||
|
||||
union UpdateFolderPolicyResult = UpdateFolderPolicyError | UpdateFolderPolicySuccess
|
||||
|
||||
type UpdateFolderPolicySuccess {
|
||||
policy: FolderPolicy!
|
||||
}
|
||||
|
||||
type UpdateHighlightError {
|
||||
errorCodes: [UpdateHighlightErrorCode!]!
|
||||
}
|
||||
|
||||
7
packages/api/src/jobs/expire_folders.ts
Normal file
7
packages/api/src/jobs/expire_folders.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { appDataSource } from '../data_source'
|
||||
|
||||
export const EXPIRE_FOLDERS_JOB_NAME = 'expire-folders'
|
||||
|
||||
export const expireFoldersJob = async () => {
|
||||
await appDataSource.query('CALL omnivore.expire_folders()')
|
||||
}
|
||||
@ -31,6 +31,10 @@ import {
|
||||
SAVE_NEWSLETTER_JOB,
|
||||
} from './jobs/email/inbound_emails'
|
||||
import { sendEmailJob, SEND_EMAIL_JOB } from './jobs/email/send_email'
|
||||
import {
|
||||
expireFoldersJob,
|
||||
EXPIRE_FOLDERS_JOB_NAME,
|
||||
} from './jobs/expire_folders'
|
||||
import { findThumbnail, THUMBNAIL_JOB } from './jobs/find_thumbnail'
|
||||
import {
|
||||
generatePreviewContent,
|
||||
@ -217,6 +221,8 @@ export const createWorker = (connection: ConnectionOptions) =>
|
||||
return generatePreviewContent(job.data)
|
||||
case PRUNE_TRASH_JOB:
|
||||
return pruneTrashJob(job.data)
|
||||
case EXPIRE_FOLDERS_JOB_NAME:
|
||||
return expireFoldersJob()
|
||||
default:
|
||||
logger.warning(`[queue-processor] unhandled job: ${job.name}`)
|
||||
}
|
||||
|
||||
129
packages/api/src/resolvers/folder_policy/index.ts
Normal file
129
packages/api/src/resolvers/folder_policy/index.ts
Normal file
@ -0,0 +1,129 @@
|
||||
import { FolderPolicy, FolderPolicyAction } from '../../entity/folder_policy'
|
||||
import {
|
||||
CreateFolderPolicyError,
|
||||
CreateFolderPolicyErrorCode,
|
||||
CreateFolderPolicySuccess,
|
||||
DeleteFolderPolicyError,
|
||||
DeleteFolderPolicySuccess,
|
||||
FolderPoliciesError,
|
||||
FolderPoliciesSuccess,
|
||||
MutationCreateFolderPolicyArgs,
|
||||
MutationDeleteFolderPolicyArgs,
|
||||
MutationUpdateFolderPolicyArgs,
|
||||
UpdateFolderPolicyError,
|
||||
UpdateFolderPolicyErrorCode,
|
||||
UpdateFolderPolicySuccess,
|
||||
} from '../../generated/graphql'
|
||||
import {
|
||||
createFolderPolicy,
|
||||
deleteFolderPolicy,
|
||||
findFolderPoliciesByUserId,
|
||||
findFolderPolicyById,
|
||||
updateFolderPolicy,
|
||||
} from '../../services/folder_policy'
|
||||
import { Merge } from '../../util'
|
||||
import { authorized } from '../../utils/gql-utils'
|
||||
|
||||
type PartialFolderPoliciesSuccess = Merge<
|
||||
FolderPoliciesSuccess,
|
||||
{ policies: Array<FolderPolicy> }
|
||||
>
|
||||
export const folderPoliciesResolver = authorized<
|
||||
PartialFolderPoliciesSuccess,
|
||||
FolderPoliciesError
|
||||
>(async (_, __, { uid }) => {
|
||||
const policies = await findFolderPoliciesByUserId(uid)
|
||||
|
||||
return {
|
||||
policies,
|
||||
}
|
||||
})
|
||||
|
||||
export const createFolderPolicyResolver = authorized<
|
||||
Merge<CreateFolderPolicySuccess, { policy: FolderPolicy }>,
|
||||
CreateFolderPolicyError,
|
||||
MutationCreateFolderPolicyArgs
|
||||
>(async (_, { input }, { uid, log }) => {
|
||||
const { folder, action, afterDays } = input
|
||||
|
||||
if (afterDays < 0) {
|
||||
log.error('Invalid values')
|
||||
|
||||
return {
|
||||
errorCodes: [CreateFolderPolicyErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
|
||||
const policy = await createFolderPolicy({
|
||||
userId: uid,
|
||||
folder,
|
||||
action: action as unknown as FolderPolicyAction,
|
||||
afterDays,
|
||||
})
|
||||
|
||||
return {
|
||||
policy,
|
||||
}
|
||||
})
|
||||
|
||||
export const updateFolderPolicyResolver = authorized<
|
||||
Merge<UpdateFolderPolicySuccess, { policy: FolderPolicy }>,
|
||||
UpdateFolderPolicyError,
|
||||
MutationUpdateFolderPolicyArgs
|
||||
>(async (_, { input }, { log, uid }) => {
|
||||
const { id, action, afterDays } = input
|
||||
|
||||
if (!action && !afterDays) {
|
||||
log.error('No fields to update')
|
||||
|
||||
return {
|
||||
errorCodes: [UpdateFolderPolicyErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
|
||||
if (afterDays && afterDays < 0) {
|
||||
log.error('Invalid values')
|
||||
|
||||
return {
|
||||
errorCodes: [UpdateFolderPolicyErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
|
||||
const result = await updateFolderPolicy(uid, id, {
|
||||
action: action ? (action as unknown as FolderPolicyAction) : undefined,
|
||||
afterDays: afterDays ?? undefined,
|
||||
})
|
||||
|
||||
if (!result.affected) {
|
||||
log.error('Policy not found')
|
||||
|
||||
return {
|
||||
errorCodes: [UpdateFolderPolicyErrorCode.Unauthorized],
|
||||
}
|
||||
}
|
||||
|
||||
const policy = await findFolderPolicyById(uid, id)
|
||||
if (!policy) {
|
||||
log.error('Policy not found')
|
||||
|
||||
return {
|
||||
errorCodes: [UpdateFolderPolicyErrorCode.Unauthorized],
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
policy,
|
||||
}
|
||||
})
|
||||
|
||||
export const deleteFolderPolicyResolver = authorized<
|
||||
DeleteFolderPolicySuccess,
|
||||
DeleteFolderPolicyError,
|
||||
MutationDeleteFolderPolicyArgs
|
||||
>(async (_, { id }, { uid }) => {
|
||||
const result = await deleteFolderPolicy(uid, id)
|
||||
|
||||
return {
|
||||
success: !!result.affected,
|
||||
}
|
||||
})
|
||||
@ -52,6 +52,12 @@ import {
|
||||
saveDiscoverArticleResolver,
|
||||
} from './discover_feeds'
|
||||
import { optInFeatureResolver } from './features'
|
||||
import {
|
||||
createFolderPolicyResolver,
|
||||
deleteFolderPolicyResolver,
|
||||
folderPoliciesResolver,
|
||||
updateFolderPolicyResolver,
|
||||
} from './folder_policy'
|
||||
import { highlightsResolver } from './highlight'
|
||||
import {
|
||||
hiddenHomeSectionResolver,
|
||||
@ -307,6 +313,9 @@ export const functionResolvers = {
|
||||
exportToIntegration: exportToIntegrationResolver,
|
||||
replyToEmail: replyToEmailResolver,
|
||||
refreshHome: refreshHomeResolver,
|
||||
createFolderPolicy: createFolderPolicyResolver,
|
||||
updateFolderPolicy: updateFolderPolicyResolver,
|
||||
deleteFolderPolicy: deleteFolderPolicyResolver,
|
||||
},
|
||||
Query: {
|
||||
me: getMeUserResolver,
|
||||
@ -342,6 +351,7 @@ export const functionResolvers = {
|
||||
subscription: subscriptionResolver,
|
||||
hiddenHomeSection: hiddenHomeSectionResolver,
|
||||
highlights: highlightsResolver,
|
||||
folderPolicies: folderPoliciesResolver,
|
||||
},
|
||||
User: {
|
||||
async intercomHash(user: User) {
|
||||
@ -882,4 +892,8 @@ export const functionResolvers = {
|
||||
...resultResolveTypeResolver('RefreshHome'),
|
||||
...resultResolveTypeResolver('HiddenHomeSection'),
|
||||
...resultResolveTypeResolver('Highlights'),
|
||||
...resultResolveTypeResolver('FolderPolicies'),
|
||||
...resultResolveTypeResolver('CreateFolderPolicy'),
|
||||
...resultResolveTypeResolver('UpdateFolderPolicy'),
|
||||
...resultResolveTypeResolver('DeleteFolderPolicy'),
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import { readPushSubscription } from '../../pubsub'
|
||||
import { userRepository } from '../../repository/user'
|
||||
import { createPageSaveRequest } from '../../services/create_page_save_request'
|
||||
import { enqueuePruneTrashJob } from '../../utils/createTask'
|
||||
import { enqueueExpireFoldersJob } from '../../utils/createTask'
|
||||
import { logger } from '../../utils/logger'
|
||||
|
||||
interface CreateLinkRequestMessage {
|
||||
@ -92,5 +93,25 @@ export function linkServiceRouter() {
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/expireFolders', async (req, res) => {
|
||||
const { expired } = readPushSubscription(req)
|
||||
|
||||
if (expired) {
|
||||
logger.info('discarding expired message')
|
||||
return res.status(200).send('Expired')
|
||||
}
|
||||
|
||||
try {
|
||||
const job = await enqueueExpireFoldersJob()
|
||||
logger.info('enqueue job', { id: job?.id })
|
||||
|
||||
return res.sendStatus(200)
|
||||
} catch (error) {
|
||||
logger.error('error expire folders', error)
|
||||
|
||||
return res.sendStatus(500)
|
||||
}
|
||||
})
|
||||
|
||||
return router
|
||||
}
|
||||
|
||||
@ -3247,6 +3247,97 @@ const schema = gql`
|
||||
BAD_REQUEST
|
||||
}
|
||||
|
||||
type FolderPolicy {
|
||||
id: ID!
|
||||
folder: String!
|
||||
action: FolderPolicyAction!
|
||||
afterDays: Int!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
}
|
||||
|
||||
enum FolderPolicyAction {
|
||||
ARCHIVE
|
||||
DELETE
|
||||
}
|
||||
|
||||
union FolderPoliciesResult = FolderPoliciesSuccess | FolderPoliciesError
|
||||
|
||||
type FolderPoliciesSuccess {
|
||||
policies: [FolderPolicy!]!
|
||||
}
|
||||
|
||||
type FolderPoliciesError {
|
||||
errorCodes: [FolderPoliciesErrorCode!]!
|
||||
}
|
||||
|
||||
enum FolderPoliciesErrorCode {
|
||||
UNAUTHORIZED
|
||||
BAD_REQUEST
|
||||
}
|
||||
|
||||
input CreateFolderPolicyInput {
|
||||
folder: String! @sanitize(minLength: 1, maxLength: 255)
|
||||
action: FolderPolicyAction!
|
||||
afterDays: Int!
|
||||
}
|
||||
|
||||
union CreateFolderPolicyResult =
|
||||
CreateFolderPolicySuccess
|
||||
| CreateFolderPolicyError
|
||||
|
||||
type CreateFolderPolicySuccess {
|
||||
policy: FolderPolicy!
|
||||
}
|
||||
|
||||
type CreateFolderPolicyError {
|
||||
errorCodes: [CreateFolderPolicyErrorCode!]!
|
||||
}
|
||||
|
||||
enum CreateFolderPolicyErrorCode {
|
||||
UNAUTHORIZED
|
||||
BAD_REQUEST
|
||||
}
|
||||
|
||||
union DeleteFolderPolicyResult =
|
||||
DeleteFolderPolicySuccess
|
||||
| DeleteFolderPolicyError
|
||||
|
||||
type DeleteFolderPolicySuccess {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type DeleteFolderPolicyError {
|
||||
errorCodes: [DeleteFolderPolicyErrorCode!]!
|
||||
}
|
||||
|
||||
enum DeleteFolderPolicyErrorCode {
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union UpdateFolderPolicyResult =
|
||||
UpdateFolderPolicySuccess
|
||||
| UpdateFolderPolicyError
|
||||
|
||||
type UpdateFolderPolicySuccess {
|
||||
policy: FolderPolicy!
|
||||
}
|
||||
|
||||
type UpdateFolderPolicyError {
|
||||
errorCodes: [UpdateFolderPolicyErrorCode!]!
|
||||
}
|
||||
|
||||
enum UpdateFolderPolicyErrorCode {
|
||||
UNAUTHORIZED
|
||||
BAD_REQUEST
|
||||
}
|
||||
|
||||
input UpdateFolderPolicyInput {
|
||||
id: ID!
|
||||
action: FolderPolicyAction
|
||||
afterDays: Int
|
||||
}
|
||||
|
||||
# Mutations
|
||||
type Mutation {
|
||||
googleLogin(input: GoogleLoginInput!): LoginResult!
|
||||
@ -3373,6 +3464,13 @@ const schema = gql`
|
||||
editDiscoverFeed(input: EditDiscoverFeedInput!): EditDiscoverFeedResult!
|
||||
emptyTrash: EmptyTrashResult!
|
||||
refreshHome: RefreshHomeResult!
|
||||
createFolderPolicy(
|
||||
input: CreateFolderPolicyInput!
|
||||
): CreateFolderPolicyResult!
|
||||
updateFolderPolicy(
|
||||
input: UpdateFolderPolicyInput!
|
||||
): UpdateFolderPolicyResult!
|
||||
deleteFolderPolicy(id: ID!): DeleteFolderPolicyResult!
|
||||
}
|
||||
|
||||
# FIXME: remove sort from feedArticles after all cached tabs are closed
|
||||
@ -3447,6 +3545,7 @@ const schema = gql`
|
||||
subscription(id: ID!): SubscriptionResult!
|
||||
hiddenHomeSection: HiddenHomeSectionResult!
|
||||
highlights(after: String, first: Int, query: String): HighlightsResult!
|
||||
folderPolicies: FolderPoliciesResult!
|
||||
}
|
||||
|
||||
schema {
|
||||
|
||||
46
packages/api/src/services/folder_policy.ts
Normal file
46
packages/api/src/services/folder_policy.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { FolderPolicy, FolderPolicyAction } from '../entity/folder_policy'
|
||||
import { getRepository } from '../repository'
|
||||
|
||||
export const createFolderPolicy = async (folderPolicy: {
|
||||
userId: string
|
||||
folder: string
|
||||
action: FolderPolicyAction
|
||||
afterDays: number
|
||||
}) => {
|
||||
return getRepository(FolderPolicy).save(folderPolicy)
|
||||
}
|
||||
|
||||
export const findFolderPoliciesByUserId = async (userId: string) => {
|
||||
return getRepository(FolderPolicy).find({
|
||||
where: { userId },
|
||||
order: { folder: 'ASC' },
|
||||
})
|
||||
}
|
||||
|
||||
export const updateFolderPolicy = async (
|
||||
userId: string,
|
||||
folderPolicyId: string,
|
||||
update: Partial<FolderPolicy>
|
||||
) => {
|
||||
return getRepository(FolderPolicy).update(
|
||||
{ id: folderPolicyId, userId },
|
||||
update
|
||||
)
|
||||
}
|
||||
|
||||
export const deleteFolderPolicy = async (
|
||||
userId: string,
|
||||
folderPolicyId: string
|
||||
) => {
|
||||
return getRepository(FolderPolicy).delete({
|
||||
id: folderPolicyId,
|
||||
userId,
|
||||
})
|
||||
}
|
||||
|
||||
export const findFolderPolicyById = async (
|
||||
userId: string,
|
||||
folderPolicyId: string
|
||||
) => {
|
||||
return getRepository(FolderPolicy).findOneBy({ id: folderPolicyId, userId })
|
||||
}
|
||||
@ -28,6 +28,7 @@ import {
|
||||
import { BulkActionData, BULK_ACTION_JOB_NAME } from '../jobs/bulk_action'
|
||||
import { CallWebhookJobData, CALL_WEBHOOK_JOB_NAME } from '../jobs/call_webhook'
|
||||
import { SendEmailJobData, SEND_EMAIL_JOB } from '../jobs/email/send_email'
|
||||
import { EXPIRE_FOLDERS_JOB_NAME } from '../jobs/expire_folders'
|
||||
import { THUMBNAIL_JOB } from '../jobs/find_thumbnail'
|
||||
import { GENERATE_PREVIEW_CONTENT_JOB } from '../jobs/generate_preview_content'
|
||||
import { EXPORT_ALL_ITEMS_JOB_NAME } from '../jobs/integration/export_all_items'
|
||||
@ -114,6 +115,7 @@ export const getJobPriority = (jobName: string): number => {
|
||||
case THUMBNAIL_JOB:
|
||||
case GENERATE_PREVIEW_CONTENT_JOB:
|
||||
case PRUNE_TRASH_JOB:
|
||||
case EXPIRE_FOLDERS_JOB_NAME:
|
||||
return 100
|
||||
|
||||
default:
|
||||
@ -1072,4 +1074,23 @@ export const enqueuePruneTrashJob = async (numDays: number) => {
|
||||
)
|
||||
}
|
||||
|
||||
export const enqueueExpireFoldersJob = async () => {
|
||||
const queue = await getBackendQueue()
|
||||
if (!queue) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return queue.add(
|
||||
EXPIRE_FOLDERS_JOB_NAME,
|
||||
{},
|
||||
{
|
||||
jobId: `${EXPIRE_FOLDERS_JOB_NAME}_${JOB_VERSION}`,
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
priority: getJobPriority(EXPIRE_FOLDERS_JOB_NAME),
|
||||
attempts: 3,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export default createHttpTaskWithToken
|
||||
|
||||
212
packages/api/test/resolvers/folder_policy.test.ts
Normal file
212
packages/api/test/resolvers/folder_policy.test.ts
Normal file
@ -0,0 +1,212 @@
|
||||
import { expect } from 'chai'
|
||||
import {
|
||||
FolderPolicy as FolderPolicyEntity,
|
||||
FolderPolicyAction,
|
||||
} from '../../src/entity/folder_policy'
|
||||
import { User } from '../../src/entity/user'
|
||||
import {
|
||||
DeleteFolderPolicySuccess,
|
||||
FolderPolicy,
|
||||
} from '../../src/generated/graphql'
|
||||
import {
|
||||
createFolderPolicy,
|
||||
deleteFolderPolicy,
|
||||
findFolderPolicyById,
|
||||
} from '../../src/services/folder_policy'
|
||||
import { deleteUser } from '../../src/services/user'
|
||||
import { createTestUser } from '../db'
|
||||
import { graphqlRequest, loginAndGetAuthToken } from '../util'
|
||||
|
||||
describe('Folder Policy API', () => {
|
||||
let loginUser: User
|
||||
let authToken: string
|
||||
|
||||
before(async () => {
|
||||
// create test user and login
|
||||
loginUser = await createTestUser('loginUser')
|
||||
authToken = await loginAndGetAuthToken(loginUser.email)
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
await deleteUser(loginUser.id)
|
||||
})
|
||||
|
||||
describe('List Folder Policy', () => {
|
||||
const query = `
|
||||
query {
|
||||
folderPolicies {
|
||||
... on FolderPoliciesSuccess {
|
||||
policies {
|
||||
id
|
||||
folder
|
||||
action
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
... on FolderPoliciesError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
it('should return a list of folder policy of the user in ascending order', async () => {
|
||||
const existingPolicy = await createFolderPolicy({
|
||||
userId: loginUser.id,
|
||||
folder: 'inbox',
|
||||
action: FolderPolicyAction.Archive,
|
||||
afterDays: 30,
|
||||
})
|
||||
const existingPolicy1 = await createFolderPolicy({
|
||||
userId: loginUser.id,
|
||||
folder: 'following',
|
||||
action: FolderPolicyAction.Archive,
|
||||
afterDays: 30,
|
||||
})
|
||||
|
||||
const res = await graphqlRequest(query, authToken).expect(200)
|
||||
|
||||
const policies = res.body.data.folderPolicies
|
||||
.policies as Array<FolderPolicy>
|
||||
expect(policies).to.have.lengthOf(2)
|
||||
expect(policies[0].id).to.equal(existingPolicy1.id)
|
||||
expect(policies[1].id).to.equal(existingPolicy.id)
|
||||
|
||||
await deleteFolderPolicy(loginUser.id, existingPolicy.id)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Create Folder Policy', () => {
|
||||
const mutation = `
|
||||
mutation CreateFolderPolicy($input: CreateFolderPolicyInput!) {
|
||||
createFolderPolicy(input: $input) {
|
||||
... on CreateFolderPolicySuccess {
|
||||
policy {
|
||||
id
|
||||
folder
|
||||
action
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
... on CreateFolderPolicyError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
it('should create a folder policy', async () => {
|
||||
const input = {
|
||||
folder: 'test-folder',
|
||||
action: FolderPolicyAction.Archive,
|
||||
afterDays: 30,
|
||||
}
|
||||
|
||||
const res = await graphqlRequest(mutation, authToken, { input }).expect(
|
||||
200
|
||||
)
|
||||
|
||||
const createdPolicy = res.body.data.createFolderPolicy
|
||||
.policy as FolderPolicy
|
||||
|
||||
const policy = await findFolderPolicyById(loginUser.id, createdPolicy.id)
|
||||
expect(policy).to.exist
|
||||
expect(policy?.folder).to.equal(input.folder)
|
||||
|
||||
await deleteFolderPolicy(loginUser.id, createdPolicy.id)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Update Folder Policy', () => {
|
||||
let existingPolicy: FolderPolicyEntity
|
||||
|
||||
before(async () => {
|
||||
existingPolicy = await createFolderPolicy({
|
||||
userId: loginUser.id,
|
||||
folder: 'test-folder',
|
||||
action: FolderPolicyAction.Archive,
|
||||
afterDays: 30,
|
||||
})
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
await deleteFolderPolicy(loginUser.id, existingPolicy.id)
|
||||
})
|
||||
|
||||
const mutation = `
|
||||
mutation UpdateFolderPolicy($input: UpdateFolderPolicyInput!) {
|
||||
updateFolderPolicy(input: $input) {
|
||||
... on UpdateFolderPolicySuccess {
|
||||
policy {
|
||||
id
|
||||
folder
|
||||
action
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
... on UpdateFolderPolicyError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
it('should update a folder policy', async () => {
|
||||
const input = {
|
||||
id: existingPolicy.id,
|
||||
action: FolderPolicyAction.Delete,
|
||||
afterDays: 30,
|
||||
}
|
||||
|
||||
const res = await graphqlRequest(mutation, authToken, { input }).expect(
|
||||
200
|
||||
)
|
||||
|
||||
const updatedPolicy = res.body.data.updateFolderPolicy
|
||||
.policy as FolderPolicy
|
||||
|
||||
const policy = await findFolderPolicyById(loginUser.id, updatedPolicy.id)
|
||||
expect(policy).to.exist
|
||||
expect(policy?.action).to.equal(input.action)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Delete Folder Policy', () => {
|
||||
let existingPolicy: FolderPolicyEntity
|
||||
|
||||
before(async () => {
|
||||
existingPolicy = await createFolderPolicy({
|
||||
userId: loginUser.id,
|
||||
folder: 'test-folder',
|
||||
action: FolderPolicyAction.Archive,
|
||||
afterDays: 30,
|
||||
})
|
||||
})
|
||||
|
||||
const mutation = `
|
||||
mutation DeleteFolderPolicy($id: ID!) {
|
||||
deleteFolderPolicy(id: $id) {
|
||||
... on DeleteFolderPolicySuccess {
|
||||
success
|
||||
}
|
||||
... on DeleteFolderPolicyError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
it('should delete a folder policy', async () => {
|
||||
const res = await graphqlRequest(mutation, authToken, {
|
||||
id: existingPolicy.id,
|
||||
}).expect(200)
|
||||
|
||||
const result = res.body.data
|
||||
.deleteFolderPolicy as DeleteFolderPolicySuccess
|
||||
expect(result.success).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -60,3 +60,11 @@ export const generateFakeUuid = () => {
|
||||
export const generateFakeShortId = () => {
|
||||
return nanoid(8)
|
||||
}
|
||||
|
||||
export const loginAndGetAuthToken = async (email: string) => {
|
||||
const res = await request
|
||||
.post('/local/debug/fake-user-login')
|
||||
.send({ fakeEmail: email })
|
||||
|
||||
return res.body.authToken as string
|
||||
}
|
||||
|
||||
68
packages/db/migrations/0182.do.folder_policy.sql
Executable file
68
packages/db/migrations/0182.do.folder_policy.sql
Executable file
@ -0,0 +1,68 @@
|
||||
-- Type: DO
|
||||
-- Name: folder_policy
|
||||
-- Description: Create a folder_policy table to contain the folder expiration policies for user and folder
|
||||
|
||||
BEGIN;
|
||||
|
||||
CREATE TYPE folder_action AS ENUM ('DELETE', 'ARCHIVE');
|
||||
|
||||
CREATE TABLE omnivore.folder_policy (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v1mc(),
|
||||
user_id UUID NOT NULL REFERENCES omnivore.user(id) ON DELETE CASCADE,
|
||||
folder TEXT NOT NULL, -- folder name in lowercase
|
||||
action folder_action NOT NULL, -- delete or archive
|
||||
after_days INT NOT NULL, -- number of days after which the action should be taken
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE (user_id, folder, action) -- only one policy per user and folder action
|
||||
);
|
||||
|
||||
CREATE TRIGGER update_folder_policy_modtime BEFORE UPDATE ON omnivore.folder_policy FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON omnivore.folder_policy TO omnivore_user;
|
||||
|
||||
CREATE PROCEDURE omnivore.expire_folders()
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
folder_record RECORD;
|
||||
folder_name TEXT;
|
||||
folder_action folder_action;
|
||||
folder_user_id UUID;
|
||||
folder_after_days INT;
|
||||
old_states library_item_state[];
|
||||
new_state library_item_state;
|
||||
column_name TEXT;
|
||||
folder_policy_cursor CURSOR FOR SELECT id, user_id, folder, action, after_days FROM omnivore.folder_policy;
|
||||
BEGIN
|
||||
FOR folder_record IN folder_policy_cursor LOOP
|
||||
folder_user_id := folder_record.user_id;
|
||||
folder_name := folder_record.folder;
|
||||
folder_action := folder_record.action;
|
||||
folder_after_days := folder_record.after_days;
|
||||
|
||||
IF folder_action = 'DELETE' THEN
|
||||
old_states := ARRAY['SUCCEEDED', 'FAILED', 'ARCHIVED', 'PROCESSING', 'CONTENT_NOT_FETCHED'::library_item_state];
|
||||
new_state := 'DELETED';
|
||||
column_name := 'deleted_at';
|
||||
ELSIF folder_action = 'ARCHIVE' THEN
|
||||
old_states := ARRAY['SUCCEEDED', 'FAILED', 'PROCESSING', 'CONTENT_NOT_FETCHED'::library_item_state];
|
||||
new_state := 'ARCHIVED';
|
||||
column_name := 'archived_at';
|
||||
END IF;
|
||||
|
||||
BEGIN
|
||||
PERFORM omnivore.set_claims(folder_user_id, 'omnivore_user');
|
||||
|
||||
EXECUTE format('UPDATE omnivore.library_item '
|
||||
'SET state = $1, %I = CURRENT_TIMESTAMP '
|
||||
'WHERE user_id = $2 AND state = ANY ($3) AND folder = $4 AND created_at < CURRENT_TIMESTAMP - INTERVAL ''$5 days''', column_name)
|
||||
USING new_state, folder_user_id, old_states, folder_name, folder_after_days;
|
||||
|
||||
COMMIT;
|
||||
END;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
COMMIT;
|
||||
13
packages/db/migrations/0182.undo.folder_policy.sql
Executable file
13
packages/db/migrations/0182.undo.folder_policy.sql
Executable file
@ -0,0 +1,13 @@
|
||||
-- Type: UNDO
|
||||
-- Name: folder_policy
|
||||
-- Description: Create a folder_policy table to contain the folder expiration policies for user and folder
|
||||
|
||||
BEGIN;
|
||||
|
||||
DROP TABLE omnivore.folder_policy;
|
||||
|
||||
DROP TYPE folder_action;
|
||||
|
||||
DROP PROCEDURE omnivore.expire_folders();
|
||||
|
||||
COMMIT;
|
||||
Reference in New Issue
Block a user