1
- import { Injectable , Logger } from '@nestjs/common' ;
2
- import { isArray , get , set } from 'lodash' ;
1
+ import { HttpException , Injectable , InternalServerErrorException , Logger } from '@nestjs/common' ;
2
+ import { get , isArray , set } from 'lodash' ;
3
3
import { Database } from 'src/modules/database/models/database' ;
4
4
import { plainToClass } from 'class-transformer' ;
5
5
import { ConnectionType } from 'src/modules/database/entities/database.entity' ;
6
6
import { DatabaseRepository } from 'src/modules/database/repositories/database.repository' ;
7
- import { DatabaseImportResponse } from 'src/modules/database-import/dto/database-import.response' ;
8
- import { Validator } from 'class-validator' ;
7
+ import {
8
+ DatabaseImportResponse ,
9
+ DatabaseImportResult ,
10
+ DatabaseImportStatus ,
11
+ } from 'src/modules/database-import/dto/database-import.response' ;
12
+ import { ValidationError , Validator } from 'class-validator' ;
9
13
import { ImportDatabaseDto } from 'src/modules/database-import/dto/import.database.dto' ;
10
14
import { classToClass } from 'src/utils' ;
11
15
import { DatabaseImportAnalytics } from 'src/modules/database-import/database-import.analytics' ;
12
16
import {
17
+ NoDatabaseImportFileProvidedException ,
13
18
SizeLimitExceededDatabaseImportFileException ,
14
- NoDatabaseImportFileProvidedException , UnableToParseDatabaseImportFileException ,
19
+ UnableToParseDatabaseImportFileException ,
15
20
} from 'src/modules/database-import/exceptions' ;
21
+ import { ValidationException } from 'src/common/exceptions' ;
16
22
17
23
@Injectable ( )
18
24
export class DatabaseImportService {
@@ -61,28 +67,33 @@ export class DatabaseImportService {
61
67
62
68
let response = {
63
69
total : items . length ,
64
- success : 0 ,
65
- errors : [ ] ,
70
+ success : [ ] ,
71
+ partial : [ ] ,
72
+ fail : [ ] ,
66
73
} ;
67
74
68
75
// it is very important to insert databases on-by-one to avoid db constraint errors
69
- await items . reduce ( ( prev , item ) => prev . finally ( ( ) => this . createDatabase ( item )
70
- . then ( ( ) => {
71
- response . success += 1 ;
72
- } )
73
- . catch ( ( e ) => {
74
- let error = e ;
75
- if ( isArray ( e ) ) {
76
- [ error ] = e ;
76
+ await items . reduce ( ( prev , item , index ) => prev . finally ( ( ) => this . createDatabase ( item , index )
77
+ . then ( ( result ) => {
78
+ switch ( result . status ) {
79
+ case DatabaseImportStatus . Fail :
80
+ response . fail . push ( result ) ;
81
+ break ;
82
+ case DatabaseImportStatus . Partial :
83
+ response . partial . push ( result ) ;
84
+ break ;
85
+ case DatabaseImportStatus . Success :
86
+ response . success . push ( result ) ;
87
+ break ;
88
+ default :
89
+ // do not include into repost, since some unexpected behaviour
77
90
}
78
- this . logger . warn ( `Unable to import database: ${ error ?. constructor ?. name || 'UncaughtError' } ` , error ) ;
79
- response . errors . push ( error ) ;
80
91
} ) ) , Promise . resolve ( ) ) ;
81
92
82
- this . analytics . sendImportResults ( response ) ;
83
-
84
93
response = plainToClass ( DatabaseImportResponse , response ) ;
85
94
95
+ this . analytics . sendImportResults ( response ) ;
96
+
86
97
return response ;
87
98
} catch ( e ) {
88
99
this . logger . warn ( `Unable to import databases: ${ e ?. constructor ?. name || 'UncaughtError' } ` , e ) ;
@@ -97,51 +108,84 @@ export class DatabaseImportService {
97
108
* Map data to known model, validate it and create database if possible
98
109
* Note: will not create connection, simply create database
99
110
* @param item
111
+ * @param index
100
112
* @private
101
113
*/
102
- private async createDatabase ( item : any ) : Promise < Database > {
103
- const data : any = { } ;
114
+ private async createDatabase ( item : any , index : number ) : Promise < DatabaseImportResult > {
115
+ try {
116
+ const data : any = { } ;
104
117
105
- this . fieldsMapSchema . forEach ( ( [ field , paths ] ) => {
106
- let value ;
118
+ this . fieldsMapSchema . forEach ( ( [ field , paths ] ) => {
119
+ let value ;
107
120
108
- paths . every ( ( path ) => {
109
- value = get ( item , path ) ;
110
- return value === undefined ;
121
+ paths . every ( ( path ) => {
122
+ value = get ( item , path ) ;
123
+ return value === undefined ;
124
+ } ) ;
125
+
126
+ set ( data , field , value ) ;
111
127
} ) ;
112
128
113
- set ( data , field , value ) ;
114
- } ) ;
129
+ // set database name if needed
130
+ if ( ! data . name ) {
131
+ data . name = `${ data . host } :${ data . port } ` ;
132
+ }
115
133
116
- // set database name if needed
117
- if ( ! data . name ) {
118
- data . name = `${ data . host } :${ data . port } ` ;
119
- }
134
+ // determine database type
135
+ if ( data . isCluster ) {
136
+ data . connectionType = ConnectionType . CLUSTER ;
137
+ } else {
138
+ data . connectionType = ConnectionType . STANDALONE ;
139
+ }
120
140
121
- // determine database type
122
- if ( data . isCluster ) {
123
- data . connectionType = ConnectionType . CLUSTER ;
124
- } else {
125
- data . connectionType = ConnectionType . STANDALONE ;
126
- }
141
+ const dto = plainToClass (
142
+ ImportDatabaseDto ,
143
+ // additionally replace empty strings ("") with null
144
+ Object . keys ( data )
145
+ . reduce ( ( acc , key ) => {
146
+ acc [ key ] = data [ key ] === '' ? null : data [ key ] ;
147
+ return acc ;
148
+ } , { } ) ,
149
+ ) ;
150
+
151
+ await this . validator . validateOrReject ( dto , {
152
+ whitelist : true ,
153
+ } ) ;
154
+
155
+ const database = classToClass ( Database , dto ) ;
127
156
128
- const dto = plainToClass (
129
- ImportDatabaseDto ,
130
- // additionally replace empty strings ("") with null
131
- Object . keys ( data )
132
- . reduce ( ( acc , key ) => {
133
- acc [ key ] = data [ key ] === '' ? null : data [ key ] ;
134
- return acc ;
135
- } , { } ) ,
136
- ) ;
157
+ await this . databaseRepository . create ( database ) ;
137
158
138
- await this . validator . validateOrReject ( dto , {
139
- whitelist : true ,
140
- } ) ;
159
+ return {
160
+ index,
161
+ status : DatabaseImportStatus . Success ,
162
+ host : database . host ,
163
+ port : database . port ,
164
+ } ;
165
+ } catch ( e ) {
166
+ let error = e ;
167
+ if ( isArray ( e ) ) {
168
+ [ error ] = e ;
169
+ }
141
170
142
- const database = classToClass ( Database , dto ) ;
171
+ if ( error instanceof ValidationError ) {
172
+ error = new ValidationException ( Object . values ( error ?. constraints || { } ) || 'Bad request' ) ;
173
+ }
143
174
144
- return this . databaseRepository . create ( database ) ;
175
+ if ( ! ( error instanceof HttpException ) ) {
176
+ error = new InternalServerErrorException ( error ?. message ) ;
177
+ }
178
+
179
+ this . logger . warn ( `Unable to import database: ${ error ?. constructor ?. name || 'UncaughtError' } ` , error ) ;
180
+
181
+ return {
182
+ index,
183
+ status : DatabaseImportStatus . Fail ,
184
+ host : item ?. host ,
185
+ port : item ?. port ,
186
+ error,
187
+ } ;
188
+ }
145
189
}
146
190
147
191
/**
0 commit comments