Skip to content

Commit b2f36e1

Browse files
committed
Merge pull request #1644 from drew-gross/refactor-query-transform
Break dependency of deleteObjectsByQuery on schemaController
2 parents 10d2988 + 71ae7be commit b2f36e1

File tree

10 files changed

+195
-149
lines changed

10 files changed

+195
-149
lines changed

spec/MongoTransform.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ describe('parseObjectToMongoObjectForCreate', () => {
106106

107107
describe('transformWhere', () => {
108108
it('objectId', (done) => {
109-
var out = transform.transformWhere(dummySchema, null, {objectId: 'foo'});
109+
var out = transform.transformWhere(null, {objectId: 'foo'});
110110
expect(out._id).toEqual('foo');
111111
done();
112112
});
@@ -115,7 +115,7 @@ describe('transformWhere', () => {
115115
var input = {
116116
objectId: {'$in': ['one', 'two', 'three']},
117117
};
118-
var output = transform.transformWhere(dummySchema, null, input);
118+
var output = transform.transformWhere(null, input);
119119
jequal(input.objectId, output._id);
120120
done();
121121
});

spec/ParseHooks.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ describe('Hooks', () => {
7676
})
7777
});
7878

79-
it("should CRUD a trigger registration", (done) => {
79+
it("should CRUD a trigger registration", (done) => {
8080
// Create
8181
Parse.Hooks.createTrigger("MyClass","beforeDelete", "http://someurl").then((res) => {
8282
expect(res.className).toBe("MyClass");

spec/Schema.spec.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ describe('SchemaController', () => {
124124
var obj;
125125
createTestUser()
126126
.then(user => {
127-
console.log(user);
128127
return config.database.loadSchema()
129128
// Create a valid class
130129
.then(schema => schema.validateObject('Stuff', {foo: 'bar'}))

src/Adapters/Storage/Mongo/MongoStorageAdapter.js

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -173,16 +173,11 @@ export class MongoStorageAdapter {
173173
// If no objects match, reject with OBJECT_NOT_FOUND. If objects are found and deleted, resolve with undefined.
174174
// If there is some other error, reject with INTERNAL_SERVER_ERROR.
175175

176-
// Currently accepts the schemaController, and validate for lecacy reasons
177-
deleteObjectsByQuery(className, query, schemaController, validate) {
176+
// Currently accepts validate for legacy reasons. Currently accepts the schema, that may not actually be necessary.
177+
deleteObjectsByQuery(className, query, validate, schema) {
178178
return this.adaptiveCollection(className)
179179
.then(collection => {
180-
let mongoWhere = transform.transformWhere(
181-
schemaController,
182-
className,
183-
query,
184-
{ validate }
185-
);
180+
let mongoWhere = transform.transformWhere(className, query, { validate }, schema);
186181
return collection.deleteMany(mongoWhere)
187182
})
188183
.then(({ result }) => {

src/Adapters/Storage/Mongo/MongoTransform.js

Lines changed: 95 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,16 @@ var Parse = require('parse/node').Parse;
1111
//
1212
// There are several options that can help transform:
1313
//
14-
// query: true indicates that query constraints like $lt are allowed in
15-
// the value.
16-
//
1714
// update: true indicates that __op operators like Add and Delete
1815
// in the value are converted to a mongo update form. Otherwise they are
1916
// converted to static data.
2017
//
2118
// validate: true indicates that key names are to be validated.
2219
//
2320
// Returns an object with {key: key, value: value}.
24-
export function transformKeyValue(schema, className, restKey, restValue, {
21+
function transformKeyValue(schema, className, restKey, restValue, {
2522
inArray,
2623
inObject,
27-
query,
2824
update,
2925
validate,
3026
} = {}) {
@@ -66,47 +62,17 @@ export function transformKeyValue(schema, className, restKey, restValue, {
6662
return {key: key, value: restValue};
6763
break;
6864
case '$or':
69-
if (!query) {
70-
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME,
71-
'you can only use $or in queries');
72-
}
73-
if (!(restValue instanceof Array)) {
74-
throw new Parse.Error(Parse.Error.INVALID_QUERY,
75-
'bad $or format - use an array value');
76-
}
77-
var mongoSubqueries = restValue.map((s) => {
78-
return transformWhere(schema, className, s);
79-
});
80-
return {key: '$or', value: mongoSubqueries};
65+
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'you can only use $or in queries');
8166
case '$and':
82-
if (!query) {
83-
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME,
84-
'you can only use $and in queries');
85-
}
86-
if (!(restValue instanceof Array)) {
87-
throw new Parse.Error(Parse.Error.INVALID_QUERY,
88-
'bad $and format - use an array value');
89-
}
90-
var mongoSubqueries = restValue.map((s) => {
91-
return transformWhere(schema, className, s);
92-
});
93-
return {key: '$and', value: mongoSubqueries};
67+
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'you can only use $and in queries');
9468
default:
9569
// Other auth data
9670
var authDataMatch = key.match(/^authData\.([a-zA-Z0-9_]+)\.id$/);
9771
if (authDataMatch) {
98-
if (query) {
99-
var provider = authDataMatch[1];
100-
// Special-case auth data.
101-
return {key: '_auth_data_'+provider+'.id', value: restValue};
102-
}
103-
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME,
104-
'can only query on ' + key);
105-
break;
106-
};
72+
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'can only query on ' + key);
73+
}
10774
if (validate && !key.match(/^[a-zA-Z][a-zA-Z0-9_\.]*$/)) {
108-
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME,
109-
'invalid key name: ' + key);
75+
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'invalid key name: ' + key);
11076
}
11177
}
11278

@@ -123,20 +89,6 @@ export function transformKeyValue(schema, className, restKey, restValue, {
12389
}
12490
var expectedTypeIsArray = (expected && expected.type === 'Array');
12591

126-
// Handle query constraints
127-
if (query) {
128-
value = transformConstraint(restValue, expectedTypeIsArray);
129-
if (value !== CannotTransform) {
130-
return {key: key, value: value};
131-
}
132-
}
133-
134-
if (expectedTypeIsArray && query && !(restValue instanceof Array)) {
135-
return {
136-
key: key, value: { '$all' : [restValue] }
137-
};
138-
}
139-
14092
// Handle atomic values
14193
var value = transformAtom(restValue, false, { inArray, inObject });
14294
if (value !== CannotTransform) {
@@ -154,10 +106,6 @@ export function transformKeyValue(schema, className, restKey, restValue, {
154106

155107
// Handle arrays
156108
if (restValue instanceof Array) {
157-
if (query) {
158-
throw new Parse.Error(Parse.Error.INVALID_JSON,
159-
'cannot use array as query param');
160-
}
161109
value = restValue.map((restObj) => {
162110
var out = transformKeyValue(schema, className, restKey, restObj, { inArray: true });
163111
return out.value;
@@ -182,20 +130,105 @@ export function transformKeyValue(schema, className, restKey, restValue, {
182130
return {key: key, value: value};
183131
}
184132

133+
const valueAsDate = value => {
134+
if (typeof value === 'string') {
135+
return new Date(value);
136+
} else if (value instanceof Date) {
137+
return value;
138+
}
139+
return false;
140+
}
141+
142+
function transformQueryKeyValue(className, key, value, { validate } = {}, schema) {
143+
switch(key) {
144+
case 'createdAt':
145+
if (valueAsDate(value)) {
146+
return {key: '_created_at', value: valueAsDate(value)}
147+
}
148+
key = '_created_at';
149+
break;
150+
case 'updatedAt':
151+
if (valueAsDate(value)) {
152+
return {key: '_updated_at', value: valueAsDate(value)}
153+
}
154+
key = '_updated_at';
155+
break;
156+
case 'expiresAt':
157+
if (valueAsDate(value)) {
158+
return {key: 'expiresAt', value: valueAsDate(value)}
159+
}
160+
break;
161+
case 'objectId': return {key: '_id', value}
162+
case 'sessionToken': return {key: '_session_token', value}
163+
case '_rperm':
164+
case '_wperm':
165+
case '_perishable_token':
166+
case '_email_verify_token': return {key, value}
167+
case '$or':
168+
if (!(value instanceof Array)) {
169+
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'bad $or format - use an array value');
170+
}
171+
return {key: '$or', value: value.map(subQuery => transformWhere(className, subQuery, {}, schema))};
172+
case '$and':
173+
if (!(value instanceof Array)) {
174+
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'bad $and format - use an array value');
175+
}
176+
return {key: '$and', value: value.map(subQuery => transformWhere(className, subQuery, {}, schema))};
177+
default:
178+
// Other auth data
179+
const authDataMatch = key.match(/^authData\.([a-zA-Z0-9_]+)\.id$/);
180+
if (authDataMatch) {
181+
const provider = authDataMatch[1];
182+
// Special-case auth data.
183+
return {key: `_auth_data_${provider}.id`, value};
184+
}
185+
if (validate && !key.match(/^[a-zA-Z][a-zA-Z0-9_\.]*$/)) {
186+
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'invalid key name: ' + key);
187+
}
188+
}
189+
190+
const expectedTypeIsArray =
191+
schema &&
192+
schema.fields[key] &&
193+
schema.fields[key].type === 'Array';
194+
195+
const expectedTypeIsPointer =
196+
schema &&
197+
schema.fields[key] &&
198+
schema.fields[key].type === 'Pointer';
199+
200+
if (expectedTypeIsPointer || !schema && value && value.__type === 'Pointer') {
201+
key = '_p_' + key;
202+
}
203+
204+
// Handle query constraints
205+
if (transformConstraint(value, expectedTypeIsArray) !== CannotTransform) {
206+
return {key, value: transformConstraint(value, expectedTypeIsArray)};
207+
}
208+
209+
if (expectedTypeIsArray && !(value instanceof Array)) {
210+
return {key, value: { '$all' : [value] }};
211+
}
212+
213+
// Handle atomic values
214+
if (transformAtom(value, false) !== CannotTransform) {
215+
return {key, value: transformAtom(value, false)};
216+
} else {
217+
throw new Parse.Error(Parse.Error.INVALID_JSON, `You cannot use ${value} as a query parameter.`);
218+
}
219+
}
185220

186221
// Main exposed method to help run queries.
187222
// restWhere is the "where" clause in REST API form.
188223
// Returns the mongo form of the query.
189224
// Throws a Parse.Error if the input query is invalid.
190-
function transformWhere(schema, className, restWhere, options = {validate: true}) {
225+
function transformWhere(className, restWhere, { validate = true } = {}, schema) {
191226
let mongoWhere = {};
192227
if (restWhere['ACL']) {
193228
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Cannot query on ACL.');
194229
}
195-
let transformKeyOptions = {query: true};
196-
transformKeyOptions.validate = options.validate;
197230
for (let restKey in restWhere) {
198-
let out = transformKeyValue(schema, className, restKey, restWhere[restKey], transformKeyOptions);
231+
let out = transformQueryKeyValue(className, restKey, restWhere[restKey], { validate }, schema);
199232
mongoWhere[out.key] = out.value;
200233
}
201234
return mongoWhere;

src/Auth.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,7 @@ var getAuthForSessionToken = function({ config, sessionToken, installationId } =
5252
limit: 1,
5353
include: 'user'
5454
};
55-
var restWhere = {
56-
_session_token: sessionToken
57-
};
58-
var query = new RestQuery(config, master(config), '_Session',
59-
restWhere, restOptions);
55+
var query = new RestQuery(config, master(config), '_Session', { sessionToken }, restOptions);
6056
return query.execute().then((response) => {
6157
var results = response.results;
6258
if (results.length !== 1 || !results[0]['user']) {

0 commit comments

Comments
 (0)