1
- import { createPrivateTaskDefinition , TaskDataStatus } from "@webiny/tasks" ;
1
+ import { createTaskDefinition } from "@webiny/tasks" ;
2
+ import { createDeleteEntry , createListDeletedEntries } from "~/useCases" ;
2
3
import {
3
4
HcmsBulkActionsContext ,
4
- IBulkActionOperationByModelInput ,
5
- TrashBinCleanUpParams
5
+ IEmptyTrashBinsInput ,
6
+ IEmptyTrashBinsOutput ,
7
+ IEmptyTrashBinsTaskParams
6
8
} from "~/types" ;
7
- import { ChildTasksCleanup } from "~/useCases/internals" ;
8
9
9
10
const calculateDateTimeString = ( ) => {
10
11
// Retrieve the retention period from the environment variable WEBINY_TRASH_BIN_RETENTION_PERIOD_DAYS,
@@ -23,122 +24,95 @@ const calculateDateTimeString = () => {
23
24
return currentDate . toISOString ( ) ;
24
25
} ;
25
26
26
- const cleanup = async ( { context, task } : TrashBinCleanUpParams ) => {
27
- // We want to clean all child tasks and logs, which have no errors.
28
- const childTasksCleanup = new ChildTasksCleanup ( ) ;
29
- try {
30
- await childTasksCleanup . execute ( {
31
- context,
32
- task
33
- } ) ;
34
- } catch ( ex ) {
35
- console . error ( `Error while cleaning "EmptyTrashBins" child tasks.` , ex ) ;
36
- }
37
- } ;
38
-
39
27
export const createEmptyTrashBinsTask = ( ) => {
40
- return createPrivateTaskDefinition < HcmsBulkActionsContext > ( {
28
+ return createTaskDefinition <
29
+ HcmsBulkActionsContext ,
30
+ IEmptyTrashBinsInput ,
31
+ IEmptyTrashBinsOutput
32
+ > ( {
33
+ isPrivate : true ,
41
34
id : "hcmsEntriesEmptyTrashBins" ,
42
35
title : "Headless CMS - Empty all trash bins" ,
43
- description :
44
- "Delete all entries found in the trash bin, for each model found in the system." ,
45
- maxIterations : 24 ,
36
+ description : "Delete all entries in the trash bin for each model in the system." ,
37
+ maxIterations : 120 ,
46
38
disableDatabaseLogs : true ,
47
- run : async params => {
48
- const { response, isAborted, isCloseToTimeout, context, trigger, input, store } =
49
- params ;
39
+ run : async ( params : IEmptyTrashBinsTaskParams ) => {
40
+ const { response, isAborted, context, input, isCloseToTimeout } = params ;
41
+
42
+ // Abort the task if needed.
50
43
if ( isAborted ( ) ) {
51
44
return response . aborted ( ) ;
52
- } else if ( isCloseToTimeout ( ) ) {
53
- return response . continue (
54
- {
55
- ...input
56
- } ,
57
- {
58
- seconds : 30
59
- }
60
- ) ;
61
45
}
62
46
63
- if ( input . triggered ) {
64
- const { items } = await context . tasks . listTasks ( {
65
- where : {
66
- parentId : store . getTask ( ) . id ,
67
- taskStatus_in : [ TaskDataStatus . RUNNING , TaskDataStatus . PENDING ]
68
- } ,
69
- limit : 100000
70
- } ) ;
47
+ // Fetch all tenants, excluding those already processed.
48
+ const baseTenants = await context . tenancy . listTenants ( ) ;
49
+ const executedTenantIds = input . executedTenantIds || [ ] ;
50
+ const tenants = baseTenants . filter ( tenant => ! executedTenantIds . includes ( tenant . id ) ) ;
51
+ let shouldContinue = false ; // Flag to check if task should continue.
71
52
72
- if ( items . length === 0 ) {
73
- return response . done (
74
- "Task done: emptying the trash bin for all registered models."
75
- ) ;
53
+ // Iterate over each tenant.
54
+ await context . tenancy . withEachTenant ( tenants , async tenant => {
55
+ if ( isCloseToTimeout ( ) ) {
56
+ shouldContinue = true ;
57
+ return ;
76
58
}
77
59
78
- for ( const item of items ) {
79
- const status = await context . tasks . fetchServiceInfo ( item . id ) ;
80
-
81
- if ( status ?. status === "FAILED" || status ?. status === "TIMED_OUT" ) {
82
- await context . tasks . updateTask ( item . id , {
83
- taskStatus : TaskDataStatus . FAILED
84
- } ) ;
85
- continue ;
86
- }
87
-
88
- if ( status ?. status === "ABORTED" ) {
89
- await context . tasks . updateTask ( item . id , {
90
- taskStatus : TaskDataStatus . ABORTED
91
- } ) ;
60
+ // Fetch all locales for the tenant.
61
+ const locales = context . i18n . getLocales ( ) ;
62
+ await context . i18n . withEachLocale ( locales , async ( ) => {
63
+ if ( isCloseToTimeout ( ) ) {
64
+ shouldContinue = true ;
65
+ return ;
92
66
}
93
- }
94
67
95
- return response . continue (
96
- {
97
- ...input
98
- } ,
99
- {
100
- seconds : 3600
101
- }
102
- ) ;
103
- }
68
+ // List all non-private models for the current locale.
69
+ const models = await context . security . withoutAuthorization ( async ( ) =>
70
+ ( await context . cms . listModels ( ) ) . filter ( m => ! m . isPrivate )
71
+ ) ;
104
72
105
- try {
106
- const locales = context . i18n . getLocales ( ) ;
73
+ // Process each model to delete trashed entries.
74
+ for ( const model of models ) {
75
+ const list = createListDeletedEntries ( context ) ; // List trashed entries.
76
+ const mutation = createDeleteEntry ( context ) ; // Mutation to delete entries.
107
77
108
- await context . i18n . withEachLocale ( locales , async ( ) => {
109
- const models = await context . security . withoutAuthorization ( async ( ) => {
110
- return ( await context . cms . listModels ( ) ) . filter ( model => ! model . isPrivate ) ;
111
- } ) ;
78
+ // Query parameters for fetching deleted entries older than a minute ago.
79
+ const listEntriesParams = {
80
+ where : { deletedOn_lt : calculateDateTimeString ( ) } ,
81
+ limit : 50
82
+ } ;
112
83
113
- for ( const model of models ) {
114
- await trigger < IBulkActionOperationByModelInput > ( {
115
- name : `Headless CMS - Empty trash bin for "${ model . name } " model.` ,
116
- definition : "hcmsBulkListDeleteEntries" ,
117
- input : {
118
- modelId : model . modelId ,
119
- where : {
120
- deletedOn_lt : calculateDateTimeString ( )
84
+ let result ;
85
+ // Continue deleting entries while there are entries left to delete.
86
+ while (
87
+ ( result = await list . execute ( model . modelId , listEntriesParams ) ) &&
88
+ result . meta . totalCount > 0
89
+ ) {
90
+ if ( isCloseToTimeout ( ) ) {
91
+ shouldContinue = true ;
92
+ break ;
93
+ }
94
+ for ( const entry of result . entries ) {
95
+ if ( isCloseToTimeout ( ) ) {
96
+ shouldContinue = true ;
97
+ break ;
121
98
}
99
+ // Delete each entry individually.
100
+ await mutation . execute ( model , entry . id ) ;
122
101
}
123
- } ) ;
102
+ }
124
103
}
125
104
} ) ;
126
105
127
- return response . continue (
128
- {
129
- triggered : true
130
- } ,
131
- {
132
- seconds : 120
133
- }
134
- ) ;
135
- } catch ( ex ) {
136
- return response . error ( ex . message ?? "Error while executing EmptyTrashBins task" ) ;
137
- }
138
- } ,
139
- onMaxIterations : cleanup ,
140
- onDone : cleanup ,
141
- onError : cleanup ,
142
- onAbort : cleanup
106
+ // If the task isn't continuing, add the tenant to the executed list.
107
+ if ( ! shouldContinue ) {
108
+ executedTenantIds . push ( tenant . id ) ;
109
+ }
110
+ } ) ;
111
+
112
+ // Continue the task or mark it as done based on the `shouldContinue` flag.
113
+ return shouldContinue
114
+ ? response . continue ( { ...input , executedTenantIds } )
115
+ : response . done ( "Task done: emptied the trash bin for all registered models." ) ;
116
+ }
143
117
} ) ;
144
118
} ;
0 commit comments