Skip to content

Commit c9a138d

Browse files
drew-grosspeterdotjs
authored andcommitted
Break schemaController dependency. (#1901)
* Break dependency on MongoCollection for updateMany * Move transformWhere usage into MongoTransform * Pass parse schema into transformUpdate * break dependency on schemaController * remove schema parameter * move key name validation up one level * Move validation out of mongo adapter * Move validation into Parse Server and transformUpdate in Mongo Adapter * Update mongo adapter * Use adapter API * use and fix mongo adapter api * Remove/rename stuff * Kill transform in DBController * better imports for transform * Tidy ConfigRouter * Remove schemaController in more places * Remove comment
1 parent c4499d2 commit c9a138d

File tree

5 files changed

+89
-116
lines changed

5 files changed

+89
-116
lines changed

spec/MongoTransform.spec.js

+12-27
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,11 @@ let transform = require('../src/Adapters/Storage/Mongo/MongoTransform');
55
let dd = require('deep-diff');
66
let mongodb = require('mongodb');
77

8-
var dummySchema = {
9-
data: {},
10-
getExpectedType: function(className, key) {
11-
if (key == 'userPointer') {
12-
return { type: 'Pointer', targetClass: '_User' };
13-
} else if (key == 'picture') {
14-
return { type: 'File' };
15-
} else if (key == 'location') {
16-
return { type: 'GeoPoint' };
17-
}
18-
return;
19-
},
20-
};
21-
22-
238
describe('parseObjectToMongoObjectForCreate', () => {
249

2510
it('a basic number', (done) => {
2611
var input = {five: 5};
27-
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, {
12+
var output = transform.parseObjectToMongoObjectForCreate(null, input, {
2813
fields: {five: {type: 'Number'}}
2914
});
3015
jequal(input, output);
@@ -36,7 +21,7 @@ describe('parseObjectToMongoObjectForCreate', () => {
3621
createdAt: "2015-10-06T21:24:50.332Z",
3722
updatedAt: "2015-10-06T21:24:50.332Z"
3823
};
39-
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
24+
var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} });
4025
expect(output._created_at instanceof Date).toBe(true);
4126
expect(output._updated_at instanceof Date).toBe(true);
4227
done();
@@ -48,7 +33,7 @@ describe('parseObjectToMongoObjectForCreate', () => {
4833
objectId: 'myId',
4934
className: 'Blah',
5035
};
51-
var out = transform.parseObjectToMongoObjectForCreate(dummySchema, null, {pointers: [pointer]},{
36+
var out = transform.parseObjectToMongoObjectForCreate(null, {pointers: [pointer]},{
5237
fields: {pointers: {type: 'Array'}}
5338
});
5439
jequal([pointer], out.pointers);
@@ -59,22 +44,22 @@ describe('parseObjectToMongoObjectForCreate', () => {
5944
//have __op delete in a new object. Figure out what this should actually be testing.
6045
notWorking('a delete op', (done) => {
6146
var input = {deleteMe: {__op: 'Delete'}};
62-
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
47+
var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} });
6348
jequal(output, {});
6449
done();
6550
});
6651

6752
it('basic ACL', (done) => {
6853
var input = {ACL: {'0123': {'read': true, 'write': true}}};
69-
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
54+
var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} });
7055
// This just checks that it doesn't crash, but it should check format.
7156
done();
7257
});
7358

7459
describe('GeoPoints', () => {
7560
it('plain', (done) => {
7661
var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180};
77-
var out = transform.parseObjectToMongoObjectForCreate(dummySchema, null, {location: geoPoint},{
62+
var out = transform.parseObjectToMongoObjectForCreate(null, {location: geoPoint},{
7863
fields: {location: {type: 'GeoPoint'}}
7964
});
8065
expect(out.location).toEqual([180, -180]);
@@ -83,7 +68,7 @@ describe('parseObjectToMongoObjectForCreate', () => {
8368

8469
it('in array', (done) => {
8570
var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180};
86-
var out = transform.parseObjectToMongoObjectForCreate(dummySchema, null, {locations: [geoPoint, geoPoint]},{
71+
var out = transform.parseObjectToMongoObjectForCreate(null, {locations: [geoPoint, geoPoint]},{
8772
fields: {locations: {type: 'Array'}}
8873
});
8974
expect(out.locations).toEqual([geoPoint, geoPoint]);
@@ -92,7 +77,7 @@ describe('parseObjectToMongoObjectForCreate', () => {
9277

9378
it('in sub-object', (done) => {
9479
var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180};
95-
var out = transform.parseObjectToMongoObjectForCreate(dummySchema, null, { locations: { start: geoPoint }},{
80+
var out = transform.parseObjectToMongoObjectForCreate(null, { locations: { start: geoPoint }},{
9681
fields: {locations: {type: 'Object'}}
9782
});
9883
expect(out).toEqual({ locations: { start: geoPoint } });
@@ -206,7 +191,7 @@ describe('transform schema key changes', () => {
206191
var input = {
207192
somePointer: {__type: 'Pointer', className: 'Micro', objectId: 'oft'}
208193
};
209-
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, {
194+
var output = transform.parseObjectToMongoObjectForCreate(null, input, {
210195
fields: {somePointer: {type: 'Pointer'}}
211196
});
212197
expect(typeof output._p_somePointer).toEqual('string');
@@ -218,7 +203,7 @@ describe('transform schema key changes', () => {
218203
var input = {
219204
userPointer: {__type: 'Pointer', className: '_User', objectId: 'qwerty'}
220205
};
221-
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, {
206+
var output = transform.parseObjectToMongoObjectForCreate(null, input, {
222207
fields: {userPointer: {type: 'Pointer'}}
223208
});
224209
expect(typeof output._p_userPointer).toEqual('string');
@@ -233,7 +218,7 @@ describe('transform schema key changes', () => {
233218
"Kevin": { "write": true }
234219
}
235220
};
236-
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
221+
var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} });
237222
expect(typeof output._rperm).toEqual('object');
238223
expect(typeof output._wperm).toEqual('object');
239224
expect(output.ACL).toBeUndefined();
@@ -250,7 +235,7 @@ describe('transform schema key changes', () => {
250235
}
251236
};
252237

253-
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
238+
var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} });
254239
expect(typeof output._acl).toEqual('object');
255240
expect(output._acl["Kevin"].w).toBeTruthy();
256241
expect(output._acl["Kevin"].r).toBeUndefined();

src/Adapters/Storage/Mongo/MongoStorageAdapter.js

+47-21
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
import MongoCollection from './MongoCollection';
2-
import MongoSchemaCollection from './MongoSchemaCollection';
3-
import {parse as parseUrl, format as formatUrl} from '../../../vendor/mongodbUrl';
4-
import * as transform from './MongoTransform';
5-
import _ from 'lodash';
1+
import MongoCollection from './MongoCollection';
2+
import MongoSchemaCollection from './MongoSchemaCollection';
3+
import {
4+
parse as parseUrl,
5+
format as formatUrl,
6+
} from '../../../vendor/mongodbUrl';
7+
import {
8+
parseObjectToMongoObjectForCreate,
9+
mongoObjectToParseObject,
10+
transformKey,
11+
transformWhere,
12+
transformUpdate,
13+
} from './MongoTransform';
14+
import _ from 'lodash';
615

716
let mongodb = require('mongodb');
817
let MongoClient = mongodb.MongoClient;
@@ -159,12 +168,11 @@ export class MongoStorageAdapter {
159168
.then(schemasCollection => schemasCollection._fechOneSchemaFrom_SCHEMA(className));
160169
}
161170

162-
// TODO: As yet not particularly well specified. Creates an object. Shouldn't need the
163-
// schemaController, but MongoTransform still needs it :( maybe shouldn't even need the schema,
171+
// TODO: As yet not particularly well specified. Creates an object. Maybe shouldn't even need the schema,
164172
// and should infer from the type. Or maybe does need the schema for validations. Or maybe needs
165173
// the schem only for the legacy mongo format. We'll figure that out later.
166-
createObject(className, object, schemaController, parseFormatSchema) {
167-
const mongoObject = transform.parseObjectToMongoObjectForCreate(schemaController, className, object, parseFormatSchema);
174+
createObject(className, object, schema) {
175+
const mongoObject = parseObjectToMongoObjectForCreate(className, object, schema);
168176
return this.adaptiveCollection(className)
169177
.then(collection => collection.insertOne(mongoObject))
170178
.catch(error => {
@@ -176,15 +184,13 @@ export class MongoStorageAdapter {
176184
});
177185
}
178186

179-
// Remove all objects that match the given parse query. Parse Query should be in Parse Format.
187+
// Remove all objects that match the given Parse Query.
180188
// If no objects match, reject with OBJECT_NOT_FOUND. If objects are found and deleted, resolve with undefined.
181189
// If there is some other error, reject with INTERNAL_SERVER_ERROR.
182-
183-
// Currently accepts the schema, that may not actually be necessary.
184190
deleteObjectsByQuery(className, query, schema) {
185191
return this.adaptiveCollection(className)
186192
.then(collection => {
187-
let mongoWhere = transform.transformWhere(className, query, schema);
193+
let mongoWhere = transformWhere(className, query, schema);
188194
return collection.deleteMany(mongoWhere)
189195
})
190196
.then(({ result }) => {
@@ -197,23 +203,43 @@ export class MongoStorageAdapter {
197203
});
198204
}
199205

206+
// Apply the update to all objects that match the given Parse Query.
207+
updateObjectsByQuery(className, query, schema, update) {
208+
const mongoUpdate = transformUpdate(className, update, schema);
209+
const mongoWhere = transformWhere(className, query, schema);
210+
return this.adaptiveCollection(className)
211+
.then(collection => collection.updateMany(mongoWhere, mongoUpdate));
212+
}
213+
214+
// Hopefully we can get rid of this in favor of updateObjectsByQuery.
215+
findOneAndUpdate(className, query, schema, update) {
216+
const mongoUpdate = transformUpdate(className, update, schema);
217+
const mongoWhere = transformWhere(className, query, schema);
218+
return this.adaptiveCollection(className)
219+
.then(collection => collection.findOneAndUpdate(mongoWhere, mongoUpdate));
220+
}
221+
222+
// Hopefully we can get rid of this. It's only used for config and hooks.
223+
upsertOneObject(className, query, schema, update) {
224+
const mongoUpdate = transformUpdate(className, update, schema);
225+
const mongoWhere = transformWhere(className, query, schema);
226+
return this.adaptiveCollection(className)
227+
.then(collection => collection.upsertOne(mongoWhere, mongoUpdate));
228+
}
229+
200230
// Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
201231
find(className, query, schema, { skip, limit, sort }) {
202-
let mongoWhere = this.transform.transformWhere(className, query, schema);
203-
let mongoSort = _.mapKeys(sort, (value, fieldName) => transform.transformKey(className, fieldName, schema));
232+
let mongoWhere = transformWhere(className, query, schema);
233+
let mongoSort = _.mapKeys(sort, (value, fieldName) => transformKey(className, fieldName, schema));
204234
return this.adaptiveCollection(className)
205235
.then(collection => collection.find(mongoWhere, { skip, limit, sort: mongoSort }))
206-
.then(objects => objects.map(object => transform.mongoObjectToParseObject(className, object, schema)));
236+
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)));
207237
}
208238

209239
// Executs a count.
210240
count(className, query, schema) {
211241
return this.adaptiveCollection(className)
212-
.then(collection => collection.count(transform.transformWhere(className, query, schema)));
213-
}
214-
215-
get transform() {
216-
return transform;
242+
.then(collection => collection.count(transformWhere(className, query, schema)));
217243
}
218244
}
219245

src/Adapters/Storage/Mongo/MongoTransform.js

+8-45
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const transformKey = (className, fieldName, schema) => {
1919
return fieldName;
2020
}
2121

22-
const transformKeyValueForUpdate = (schema, className, restKey, restValue) => {
22+
const transformKeyValueForUpdate = (className, restKey, restValue, parseFormatSchema) => {
2323
// Check if the schema is known since it's a built-in field.
2424
var key = restKey;
2525
var timeField = false;
@@ -38,12 +38,6 @@ const transformKeyValueForUpdate = (schema, className, restKey, restValue) => {
3838
key = '_updated_at';
3939
timeField = true;
4040
break;
41-
case '_email_verify_token':
42-
key = "_email_verify_token";
43-
break;
44-
case '_perishable_token':
45-
key = "_perishable_token";
46-
break;
4741
case 'sessionToken':
4842
case '_session_token':
4943
key = '_session_token';
@@ -57,26 +51,9 @@ const transformKeyValueForUpdate = (schema, className, restKey, restValue) => {
5751
case '_wperm':
5852
return {key: key, value: restValue};
5953
break;
60-
case '$or':
61-
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'you can only use $or in queries');
62-
case '$and':
63-
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'you can only use $and in queries');
64-
default:
65-
// Other auth data
66-
var authDataMatch = key.match(/^authData\.([a-zA-Z0-9_]+)\.id$/);
67-
if (authDataMatch) {
68-
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'can only query on ' + key);
69-
}
7054
}
7155

72-
// Handle special schema key changes
73-
// TODO: it seems like this is likely to have edge cases where
74-
// pointer types are missed
75-
var expected = undefined;
76-
if (schema && schema.getExpectedType) {
77-
expected = schema.getExpectedType(className, key);
78-
}
79-
if ((expected && expected.type == 'Pointer') || (!expected && restValue && restValue.__type == 'Pointer')) {
56+
if ((parseFormatSchema.fields[key] && parseFormatSchema.fields[key].type === 'Pointer') || (!parseFormatSchema.fields[key] && restValue && restValue.__type == 'Pointer')) {
8057
key = '_p_' + key;
8158
}
8259

@@ -101,9 +78,6 @@ const transformKeyValueForUpdate = (schema, className, restKey, restValue) => {
10178
}
10279

10380
// Handle normal objects by recursing
104-
if (Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))) {
105-
throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters");
106-
}
10781
value = _.mapValues(restValue, transformInteriorValue);
10882
return {key, value};
10983
}
@@ -223,13 +197,7 @@ function transformWhere(className, restWhere, schema) {
223197
return mongoWhere;
224198
}
225199

226-
const parseObjectKeyValueToMongoObjectKeyValue = (
227-
schema,
228-
className,
229-
restKey,
230-
restValue,
231-
parseFormatSchema
232-
) => {
200+
const parseObjectKeyValueToMongoObjectKeyValue = (className, restKey, restValue, schema) => {
233201
// Check if the schema is known since it's a built-in field.
234202
let transformedValue;
235203
let coercedToDate;
@@ -267,7 +235,7 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
267235
if (restValue && restValue.__type !== 'Bytes') {
268236
//Note: We may not know the type of a field here, as the user could be saving (null) to a field
269237
//That never existed before, meaning we can't infer the type.
270-
if (parseFormatSchema.fields[restKey] && parseFormatSchema.fields[restKey].type == 'Pointer' || restValue.__type == 'Pointer') {
238+
if (schema.fields[restKey] && schema.fields[restKey].type == 'Pointer' || restValue.__type == 'Pointer') {
271239
restKey = '_p_' + restKey;
272240
}
273241
}
@@ -305,18 +273,17 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
305273

306274
// Main exposed method to create new objects.
307275
// restCreate is the "create" clause in REST API form.
308-
function parseObjectToMongoObjectForCreate(schema, className, restCreate, parseFormatSchema) {
276+
function parseObjectToMongoObjectForCreate(className, restCreate, schema) {
309277
if (className == '_User') {
310278
restCreate = transformAuthData(restCreate);
311279
}
312280
var mongoCreate = transformACL(restCreate);
313281
for (let restKey in restCreate) {
314282
let { key, value } = parseObjectKeyValueToMongoObjectKeyValue(
315-
schema,
316283
className,
317284
restKey,
318285
restCreate[restKey],
319-
parseFormatSchema
286+
schema
320287
);
321288
if (value !== undefined) {
322289
mongoCreate[key] = value;
@@ -326,10 +293,7 @@ function parseObjectToMongoObjectForCreate(schema, className, restCreate, parseF
326293
}
327294

328295
// Main exposed method to help update old objects.
329-
function transformUpdate(schema, className, restUpdate) {
330-
if (!restUpdate) {
331-
throw 'got empty restUpdate';
332-
}
296+
const transformUpdate = (className, restUpdate, parseFormatSchema) => {
333297
if (className == '_User') {
334298
restUpdate = transformAuthData(restUpdate);
335299
}
@@ -348,9 +312,8 @@ function transformUpdate(schema, className, restUpdate) {
348312
mongoUpdate['$set']['_acl'] = acl._acl;
349313
}
350314
}
351-
352315
for (var restKey in restUpdate) {
353-
var out = transformKeyValueForUpdate(schema, className, restKey, restUpdate[restKey]);
316+
var out = transformKeyValueForUpdate(className, restKey, restUpdate[restKey], parseFormatSchema);
354317

355318
// If the output value is an object with any $ keys, it's an
356319
// operator that needs to be lifted onto the top level update

0 commit comments

Comments
 (0)