1- import Parse from 'parse/node' ;
1+ // @flow
2+ // import Parse from 'parse/node';
3+ const Parse = require ( 'parse/node' ) ;
24import { logger } from '../logger' ;
35import Config from '../Config' ;
46import { internalCreateSchema , internalUpdateSchema } from '../Routers/SchemasRouter' ;
5- import { defaultColumns } from '../Controllers/SchemaController' ;
7+ import { defaultColumns , systemClasses } from '../Controllers/SchemaController' ;
68import { ParseServerOptions } from '../Options' ;
79import * as Migrations from './Migrations' ;
810
911export class DefinedSchemas {
10- constructor ( localSchemas : Migrations . JSONSchema [ ] , config : ParseServerOptions ) {
12+ config : ParseServerOptions ;
13+ migrationOptions: Migrations . MigrationsOptions ;
14+ localSchemas: Migrations . JSONSchema [ ] ;
15+ retries: number ;
16+ maxRetries: number ;
17+
18+ constructor ( migrationsOptions : Migrations . MigrationsOptions [ ] , config : ParseServerOptions ) {
19+ this . localSchemas = [ ] ;
1120 this . config = Config . get ( config . appId ) ;
12- this . localSchemas = localSchemas ;
21+ this . migrationsOptions = migrationsOptions ;
22+
23+ if ( migrationsOptions && migrationsOptions . schemas ) {
24+ this . localSchemas = migrationsOptions . schemas ;
25+ }
26+
1327 this . retries = 0 ;
1428 this . maxRetries = 3 ;
1529 }
@@ -65,6 +79,8 @@ export class DefinedSchemas {
6579 this . allCloudSchemas = await Parse . Schema . all ( ) ;
6680 clearTimeout ( timeout ) ;
6781 await Promise . all ( this . localSchemas . map ( async localSchema => this . saveOrUpdate ( localSchema ) ) ) ;
82+
83+ this . checkForMissingSchemas ( ) ;
6884 await this . enforceCLPForNonProvidedClass ( ) ;
6985 } catch ( e ) {
7086 if ( timeout ) clearTimeout ( timeout ) ;
@@ -83,6 +99,26 @@ export class DefinedSchemas {
8399 }
84100 }
85101
102+ checkForMissingSchemas ( ) {
103+ if ( this . migrationsOptions . strict !== true ) {
104+ return ;
105+ }
106+
107+ const cloudSchemas = this . allCloudSchemas . map ( s => s . className ) ;
108+ const localSchemas = this . localSchemas . map ( s => s . className ) ;
109+ const missingSchemas = cloudSchemas . filter (
110+ c => ! localSchemas . includes ( c ) && ! systemClasses . includes ( c )
111+ ) ;
112+
113+ if ( missingSchemas . length ) {
114+ logger . warn (
115+ `The following schemas are currently present in the database, but not explicitly defined in a schema: "${ missingSchemas . join (
116+ '", "'
117+ ) } "`
118+ ) ;
119+ }
120+ }
121+
86122 // Required for testing purpose
87123 async wait ( time ) {
88124 await new Promise ( resolve => setTimeout ( resolve , time ) ) ;
@@ -169,7 +205,11 @@ export class DefinedSchemas {
169205 }
170206
171207 const fieldsToDelete : string [ ] = [ ] ;
172- const fieldsToRecreate : string [ ] = [ ] ;
208+ const fieldsToRecreate : {
209+ fieldName : string ,
210+ from : { type : string , targetClass : string } ,
211+ to : { type : string , targetClass : string } ,
212+ } [ ] = [ ] ;
173213 const fieldsWithChangedParams : string [ ] = [ ] ;
174214
175215 // Check deletion
@@ -190,8 +230,11 @@ export class DefinedSchemas {
190230 { type : localField . type , targetClass : localField . targetClass }
191231 )
192232 ) {
193- fieldsToRecreate . push ( fieldName ) ;
194- fieldsToDelete . push ( fieldName ) ;
233+ fieldsToRecreate . push ( {
234+ fieldName,
235+ from : { type : field . type , targetClass : field . targetClass } ,
236+ to : { type : localField . type , targetClass : localField . targetClass } ,
237+ } ) ;
195238 return ;
196239 }
197240
@@ -201,17 +244,45 @@ export class DefinedSchemas {
201244 }
202245 } ) ;
203246
204- fieldsToDelete . forEach ( fieldName => {
205- newLocalSchema . deleteField ( fieldName ) ;
206- } ) ;
247+ if ( this . migrationsOptions . deleteExtraFields === true ) {
248+ fieldsToDelete . forEach ( fieldName => {
249+ newLocalSchema . deleteField ( fieldName ) ;
250+ } ) ;
207251
208- // Delete fields from the schema then apply changes
209- await this . updateSchemaToDB ( newLocalSchema ) ;
252+ // Delete fields from the schema then apply changes
253+ await this . updateSchemaToDB ( newLocalSchema ) ;
254+ } else if ( this . migrationsOptions . strict === true && fieldsToDelete . length ) {
255+ logger . warn (
256+ `The following fields exist in the database for "${
257+ localSchema . className
258+ } ", but are missing in the schema : "${ fieldsToDelete . join ( '" ,"' ) } "`
259+ ) ;
260+ }
261+
262+ if ( this . migrationsOptions . recreateModifiedFields === true ) {
263+ fieldsToRecreate . forEach ( fieldName => {
264+ newLocalSchema . deleteField ( fieldName ) ;
265+ } ) ;
266+
267+ // Delete fields from the schema then apply changes
268+ await this . updateSchemaToDB ( newLocalSchema ) ;
269+
270+ fieldsToRecreate . forEach ( fieldName => {
271+ const field = localSchema . fields [ fieldName ] ;
272+ this . handleFields ( newLocalSchema , fieldName , field ) ;
273+ } ) ;
274+ } else if ( this . migrationsOptions . strict === true && fieldsToRecreate . length ) {
275+ fieldsToRecreate . forEach ( field => {
276+ const from =
277+ field . from . type + ( field . from . targetClass ? ` (${ field . from . targetClass } )` : '' ) ;
278+ const to = field . to . type + ( field . to . targetClass ? ` (${ field . to . targetClass } )` : '' ) ;
279+
280+ logger . warn (
281+ `The field "${ field . fieldName } " type differ between the schema and the database for "${ localSchema . className } "; Schema is defined as "${ to } " and current database type is "${ from } "`
282+ ) ;
283+ } ) ;
284+ }
210285
211- fieldsToRecreate . forEach ( fieldName => {
212- const field = localSchema . fields [ fieldName ] ;
213- this . handleFields ( newLocalSchema , fieldName , field ) ;
214- } ) ;
215286 fieldsWithChangedParams . forEach ( fieldName => {
216287 const field = localSchema . fields [ fieldName ] ;
217288 this . handleFields ( newLocalSchema , fieldName , field ) ;
0 commit comments