@@ -11,13 +11,14 @@ import {
11
11
} from 'cypher-query-builder' ;
12
12
import {
13
13
type CalendarDate ,
14
+ CreationFailed ,
14
15
generateId ,
15
16
type ID ,
17
+ NotFoundException ,
16
18
type Range ,
17
19
type UnsecuredDto ,
18
20
} from '~/common' ;
19
21
import { DtoRepository } from '~/core/database' ;
20
- import { type ChangesOf } from '~/core/database/changes' ;
21
22
import {
22
23
ACTIVE ,
23
24
createNode ,
@@ -65,120 +66,139 @@ export class PeriodicReportRepository extends DtoRepository<
65
66
}
66
67
67
68
async merge ( input : MergePeriodicReports ) {
68
- const Report = resolveReportType ( input ) ;
69
+ try {
70
+ const Report = resolveReportType ( input ) ;
69
71
70
- // Create IDs here that will feed into the reports that are new.
71
- // If only neo4j had a nanoid generator natively.
72
- const intervals = await Promise . all (
73
- input . intervals . map ( async ( interval ) => ( {
74
- tempId : await generateId ( ) ,
75
- start : interval . start ,
76
- end : interval . end ,
77
- tempFileId : await generateId ( ) ,
78
- } ) ) ,
79
- ) ;
72
+ // Create IDs here that will feed into the reports that are new.
73
+ // If only neo4j had a nanoid generator natively.
74
+ const intervals = await Promise . all (
75
+ input . intervals . map ( async ( interval ) => ( {
76
+ tempId : await generateId ( ) ,
77
+ start : interval . start ,
78
+ end : interval . end ,
79
+ tempFileId : await generateId ( ) ,
80
+ } ) ) ,
81
+ ) ;
80
82
81
- const isProgress = input . type === ReportType . Progress ;
82
- const extraCreateOptions = isProgress
83
- ? this . progressRepo . getCreateOptions ( input )
84
- : { } ;
83
+ const isProgress = input . type === ReportType . Progress ;
84
+ const extraCreateOptions = isProgress
85
+ ? this . progressRepo . getCreateOptions ( input )
86
+ : { } ;
85
87
86
- const query = this . db
87
- . query ( )
88
- // before interval list, so it's the same time across reports
89
- . with ( 'datetime() as now' )
90
- . matchNode ( 'parent' , 'BaseNode' , { id : input . parent } )
91
- . unwind ( intervals , 'interval' )
92
- . comment ( 'Stop processing this row if the report already exists' )
93
- . subQuery ( 'parent, interval' , ( sub ) =>
94
- sub
95
- . match ( [
96
- [
97
- node ( 'parent' ) ,
98
- relation ( 'out' , '' , 'report' , ACTIVE ) ,
99
- node ( 'node' , `${ input . type } Report` ) ,
100
- ] ,
101
- [
102
- node ( 'node' ) ,
103
- relation ( 'out' , '' , 'start' , ACTIVE ) ,
104
- node ( '' , 'Property' , { value : variable ( 'interval.start' ) } ) ,
105
- ] ,
106
- [
107
- node ( 'node' ) ,
108
- relation ( 'out' , '' , 'end' , ACTIVE ) ,
109
- node ( '' , 'Property' , { value : variable ( 'interval.end' ) } ) ,
110
- ] ,
111
- ] )
112
- // Invert zero rows into one row
113
- // We want to continue out of this sub-query having 1 row when
114
- // the report doesn't exist.
115
- // However, the match above gives us zero rows in this case.
116
- // Use count() to get us back to 1 row, and to create a temp list
117
- // of how many rows we want (0 if report exists, 1 if it doesn't).
118
- // Then use UNWIND to convert this list into rows.
119
- . with ( 'CASE WHEN count(node) = 0 THEN [true] ELSE [] END as rows' )
120
- . raw ( 'UNWIND rows as row' )
121
- // nonsense value, the 1 row returned is what is important, not this column
122
- . return ( 'true as itIsNew' ) ,
123
- )
124
- . apply (
125
- await createNode ( Report as typeof IPeriodicReport , {
126
- baseNodeProps : {
127
- id : variable ( 'interval.tempId' ) ,
128
- createdAt : variable ( 'now' ) ,
129
- ...extraCreateOptions . baseNodeProps ,
130
- } ,
131
- initialProps : {
132
- type : input . type ,
133
- start : variable ( 'interval.start' ) ,
134
- end : variable ( 'interval.end' ) ,
135
- skippedReason : null ,
136
- receivedDate : null ,
137
- reportFile : variable ( 'interval.tempFileId' ) ,
138
- ...extraCreateOptions . initialProps ,
139
- } ,
140
- } ) ,
141
- )
142
- . apply (
143
- createRelationships ( Report , 'in' , {
144
- report : variable ( 'parent' ) ,
145
- } ) ,
146
- )
147
- . apply ( isProgress ? this . progressRepo . amendAfterCreateNode ( ) : undefined )
148
- // rename node to report, so we can call create node again for the file
149
- . with ( 'now, interval, node as report' )
150
- . apply (
151
- await createNode ( File , {
152
- initialProps : {
153
- name : variable ( 'apoc.temporal.format(interval.end, "date")' ) ,
154
- } ,
155
- baseNodeProps : {
156
- id : variable ( 'interval.tempFileId' ) ,
157
- createdAt : variable ( 'now' ) ,
158
- } ,
159
- } ) ,
160
- )
161
- . apply (
162
- createRelationships ( File , {
163
- in : { reportFileNode : variable ( 'report' ) } ,
164
- out : { createdBy : currentUser } ,
165
- } ) ,
166
- )
167
- . return < { id : ID ; interval : Range < CalendarDate > } > (
168
- 'report.id as id, interval' ,
169
- ) ;
170
- return await query . run ( ) ;
88
+ const query = this . db
89
+ . query ( )
90
+ // before interval list, so it's the same time across reports
91
+ . with ( 'datetime() as now' )
92
+ . matchNode ( 'parent' , 'BaseNode' , { id : input . parent } )
93
+ . unwind ( intervals , 'interval' )
94
+ . comment ( 'Stop processing this row if the report already exists' )
95
+ . subQuery ( 'parent, interval' , ( sub ) =>
96
+ sub
97
+ . match ( [
98
+ [
99
+ node ( 'parent' ) ,
100
+ relation ( 'out' , '' , 'report' , ACTIVE ) ,
101
+ node ( 'node' , `${ input . type } Report` ) ,
102
+ ] ,
103
+ [
104
+ node ( 'node' ) ,
105
+ relation ( 'out' , '' , 'start' , ACTIVE ) ,
106
+ node ( '' , 'Property' , { value : variable ( 'interval.start' ) } ) ,
107
+ ] ,
108
+ [
109
+ node ( 'node' ) ,
110
+ relation ( 'out' , '' , 'end' , ACTIVE ) ,
111
+ node ( '' , 'Property' , { value : variable ( 'interval.end' ) } ) ,
112
+ ] ,
113
+ ] )
114
+ // Invert zero rows into one row
115
+ // We want to continue out of this sub-query having 1 row when
116
+ // the report doesn't exist.
117
+ // However, the match above gives us zero rows in this case.
118
+ // Use count() to get us back to 1 row, and to create a temp list
119
+ // of how many rows we want (0 if report exists, 1 if it doesn't).
120
+ // Then use UNWIND to convert this list into rows.
121
+ . with ( 'CASE WHEN count(node) = 0 THEN [true] ELSE [] END as rows' )
122
+ . raw ( 'UNWIND rows as row' )
123
+ // nonsense value, the 1 row returned is what is important, not this column
124
+ . return ( 'true as itIsNew' ) ,
125
+ )
126
+ . apply (
127
+ await createNode ( Report as typeof IPeriodicReport , {
128
+ baseNodeProps : {
129
+ id : variable ( 'interval.tempId' ) ,
130
+ createdAt : variable ( 'now' ) ,
131
+ ...extraCreateOptions . baseNodeProps ,
132
+ } ,
133
+ initialProps : {
134
+ type : input . type ,
135
+ start : variable ( 'interval.start' ) ,
136
+ end : variable ( 'interval.end' ) ,
137
+ skippedReason : null ,
138
+ receivedDate : null ,
139
+ reportFile : variable ( 'interval.tempFileId' ) ,
140
+ ...extraCreateOptions . initialProps ,
141
+ } ,
142
+ } ) ,
143
+ )
144
+ . apply (
145
+ createRelationships ( Report , 'in' , {
146
+ report : variable ( 'parent' ) ,
147
+ } ) ,
148
+ )
149
+ . apply (
150
+ isProgress ? this . progressRepo . amendAfterCreateNode ( ) : undefined ,
151
+ )
152
+ // rename node to report, so we can call create node again for the file
153
+ . with ( 'now, interval, node as report' )
154
+ . apply (
155
+ await createNode ( File , {
156
+ initialProps : {
157
+ name : variable ( 'apoc.temporal.format(interval.end, "date")' ) ,
158
+ } ,
159
+ baseNodeProps : {
160
+ id : variable ( 'interval.tempFileId' ) ,
161
+ createdAt : variable ( 'now' ) ,
162
+ } ,
163
+ } ) ,
164
+ )
165
+ . apply (
166
+ createRelationships ( File , {
167
+ in : { reportFileNode : variable ( 'report' ) } ,
168
+ out : { createdBy : currentUser } ,
169
+ } ) ,
170
+ )
171
+ . return < { id : ID ; interval : Range < CalendarDate > } > (
172
+ 'report.id as id, interval' ,
173
+ ) ;
174
+
175
+ return await query . run ( ) ;
176
+ } catch ( exception ) {
177
+ const Report = resolveReportType ( { type : input . type } ) ;
178
+ throw new CreationFailed ( Report , exception ) ;
179
+ }
171
180
}
172
181
173
- async update < T extends PeriodicReport | UnsecuredDto < PeriodicReport > > (
174
- existing : T ,
175
- simpleChanges : Omit <
176
- ChangesOf < PeriodicReport , UpdatePeriodicReportInput > ,
177
- 'reportFile'
178
- > &
179
- Partial < Pick < PeriodicReport , 'start' | 'end' > > ,
182
+ async update (
183
+ changes : Omit < UpdatePeriodicReportInput , 'reportFile' > &
184
+ Pick < PeriodicReport , 'start' | 'end' > ,
180
185
) {
181
- return await this . updateProperties ( existing , simpleChanges ) ;
186
+ const { id, ...simpleChanges } = changes ;
187
+
188
+ await this . updateProperties ( { id } , simpleChanges ) ;
189
+
190
+ return await this . readOne ( id ) ;
191
+ }
192
+
193
+ async readOne ( id : ID ) {
194
+ if ( ! id ) {
195
+ throw new NotFoundException (
196
+ 'No periodic report id to search for' ,
197
+ 'periodicReport.id' ,
198
+ ) ;
199
+ }
200
+
201
+ return await super . readOne ( id ) ;
182
202
}
183
203
184
204
async list ( input : PeriodicReportListInput ) {
0 commit comments