Skip to content

Commit 47b016a

Browse files
committed
Added validations,
1 parent 879a3d5 commit 47b016a

File tree

6 files changed

+104
-35
lines changed

6 files changed

+104
-35
lines changed

src/LiveQuery/ParseLiveQueryServer.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -851,9 +851,8 @@ class ParseLiveQueryServer {
851851
const parseQuery = new Parse.Query(className);
852852
parseQuery.withJSON(request.query);
853853
request.query = subscription.query;
854-
request.requestId = request.requestId;
855-
request.sessionToken = subscriptionInfo.sessionToken;
856-
request.useMasterKey = client.hasMasterKey;
854+
request.sessionToken = subscriptionInfo.sessionToken;
855+
request.useMasterKey = client.hasMasterKey;
857856
request.installationId = client.installationId;
858857

859858
await runTrigger(trigger, `beforeUnsubscribe.${className}`, request, auth);

src/Options/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { MailAdapter } from '../Adapters/Email/MailAdapter';
88
import { PubSubAdapter } from '../Adapters/PubSub/PubSubAdapter';
99
import { WSSAdapter } from '../Adapters/WebSocketServer/WSSAdapter';
1010
import { CheckGroup } from '../Security/CheckGroup';
11+
import type { MigrationsOptions } from '../SchemaMigrations/Migrations';
1112

1213
type Adapter<T> = string | any | T;
1314
type NumberOrBoolean = number | boolean;
@@ -244,7 +245,7 @@ export interface ParseServerOptions {
244245
/* Callback when server has started and before running schemas migration operations if schemas key provided */
245246
beforeSchemasMigration: ?() => void | Promise<void>;
246247
/* Rest representation on Parse.Schema https://docs.parseplatform.org/rest/guide/#adding-a-schema */
247-
schemas: ?(JSONSchema[]);
248+
migrations: ?MigrationsOptions;
248249
/* Callback when server has closed */
249250
serverCloseComplete: ?() => void;
250251
/* The security options to identify and report weak security settings.

src/ParseServer.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import { ParseGraphQLServer } from './GraphQL/ParseGraphQLServer';
4444
import { SecurityRouter } from './Routers/SecurityRouter';
4545
import CheckRunner from './Security/CheckRunner';
4646
import Deprecator from './Deprecator/Deprecator';
47-
import { DefinedSchemas } from './DefinedSchemas';
47+
import { DefinedSchemas } from './SchemaMigrations/DefinedSchemas';
4848

4949
// Mutate the Parse object to add the Cloud Code handlers
5050
addParseCloud();
@@ -70,7 +70,7 @@ class ParseServer {
7070
serverURL = requiredParameter('You must provide a serverURL!'),
7171
serverStartComplete,
7272
beforeSchemasMigration,
73-
schemas,
73+
migrations,
7474
} = options;
7575
// Initialize the node client SDK automatically
7676
Parse.initialize(appId, javascriptKey || 'unused', masterKey);
@@ -91,8 +91,8 @@ class ParseServer {
9191
if (beforeSchemasMigration) {
9292
await Promise.resolve(beforeSchemasMigration());
9393
}
94-
if (schemas) {
95-
await new DefinedSchemas(schemas, this.config).execute();
94+
if (migrations) {
95+
await new DefinedSchemas(migrations, this.config).execute();
9696
}
9797
if (serverStartComplete) {
9898
serverStartComplete();

src/SchemaMigrations/DefinedSchemas.js

Lines changed: 87 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
1-
import Parse from 'parse/node';
1+
// @flow
2+
// import Parse from 'parse/node';
3+
const Parse = require('parse/node');
24
import { logger } from '../logger';
35
import Config from '../Config';
46
import { internalCreateSchema, internalUpdateSchema } from '../Routers/SchemasRouter';
5-
import { defaultColumns } from '../Controllers/SchemaController';
7+
import { defaultColumns, systemClasses } from '../Controllers/SchemaController';
68
import { ParseServerOptions } from '../Options';
79
import * as Migrations from './Migrations';
810

911
export 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);

src/SchemaMigrations/Migrations.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ export interface IndexesInterface {
4242
[key: string]: IndexInterface;
4343
}
4444

45+
export interface MigrationsOptions {
46+
schemas: JSONSchema[];
47+
strict: ?boolean;
48+
deleteExtraFields: ?boolean;
49+
recreateModifiedFields: ?boolean;
50+
}
51+
4552
export interface JSONSchema {
4653
className: ClassNameType;
4754
fields?: { [key: string]: FieldType };
@@ -74,26 +81,21 @@ function CLP(ops: CLPType, value: CLPInterface): CLPInterface {
7481

7582
export class CLPHelper {
7683
static requiresAuthentication(ops: CLPType): CLPInterface {
77-
return CLPHelper(ops, { requiresAuthentication: true });
84+
return CLP(ops, { requiresAuthentication: true });
7885
}
7986

8087
static requiresAnonymous(ops: CLPType): CLPInterface {
81-
return CLPHelper(ops, { '*': true });
88+
return CLP(ops, { '*': true });
8289
}
8390
}
8491

8592
export function makeSchema(className: ClassNameType, schema: JSONSchema): JSONSchema {
8693
return {
8794
className,
8895
fields: {
89-
objectId: { type: 'String' },
90-
createdAt: { type: 'Date' },
91-
updatedAt: { type: 'Date' },
92-
ACL: { type: 'ACL' },
9396
...schema.fields,
9497
},
9598
indexes: {
96-
objectId: { objectId: 1 },
9799
...schema.indexes,
98100
},
99101
classLevelPermissions: {

src/cloud-code/Parse.Cloud.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -677,8 +677,6 @@ ParseCloud.onLiveQueryEvent = function (handler) {
677677
triggers.addLiveQueryEventHandler(handler, Parse.applicationId);
678678
};
679679

680-
681-
682680
/**
683681
* Registers a before live query subscription function.
684682
*
@@ -719,8 +717,6 @@ ParseCloud.onLiveQueryEvent = function (handler) {
719717
triggers.addLiveQueryEventHandler(handler, Parse.applicationId);
720718
};
721719

722-
723-
724720
/**
725721
* Registers an after live query server event function.
726722
*

0 commit comments

Comments
 (0)