Skip to content

Fix #1591 #1626

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 13 additions & 29 deletions spec/MongoTransform.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,11 @@ var dummySchema = {
};


describe('parseObjectToMongoObjectForCreate', () => {
describe('parseObjectToMongoObject', () => {

it('a basic number', (done) => {
var input = {five: 5};
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, {
fields: {five: {type: 'Number'}}
});
var output = transform.parseObjectToMongoObject(dummySchema, null, input);
jequal(input, output);
done();
});
Expand All @@ -39,7 +37,7 @@ describe('parseObjectToMongoObjectForCreate', () => {
createdAt: "2015-10-06T21:24:50.332Z",
updatedAt: "2015-10-06T21:24:50.332Z"
};
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
var output = transform.parseObjectToMongoObject(dummySchema, null, input);
expect(output._created_at instanceof Date).toBe(true);
expect(output._updated_at instanceof Date).toBe(true);
done();
Expand All @@ -51,53 +49,43 @@ describe('parseObjectToMongoObjectForCreate', () => {
objectId: 'myId',
className: 'Blah',
};
var out = transform.parseObjectToMongoObjectForCreate(dummySchema, null, {pointers: [pointer]},{
fields: {pointers: {type: 'Array'}}
});
var out = transform.parseObjectToMongoObject(dummySchema, null, {pointers: [pointer]});
jequal([pointer], out.pointers);
done();
});

//TODO: object creation requests shouldn't be seeing __op delete, it makes no sense to
//have __op delete in a new object. Figure out what this should actually be testing.
notWorking('a delete op', (done) => {
it('a delete op', (done) => {
var input = {deleteMe: {__op: 'Delete'}};
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
var output = transform.parseObjectToMongoObject(dummySchema, null, input);
jequal(output, {});
done();
});

it('basic ACL', (done) => {
var input = {ACL: {'0123': {'read': true, 'write': true}}};
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
var output = transform.parseObjectToMongoObject(dummySchema, null, input);
// This just checks that it doesn't crash, but it should check format.
done();
});

describe('GeoPoints', () => {
it('plain', (done) => {
var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180};
var out = transform.parseObjectToMongoObjectForCreate(dummySchema, null, {location: geoPoint},{
fields: {location: {type: 'GeoPoint'}}
});
var out = transform.parseObjectToMongoObject(dummySchema, null, {location: geoPoint});
expect(out.location).toEqual([180, -180]);
done();
});

it('in array', (done) => {
var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180};
var out = transform.parseObjectToMongoObjectForCreate(dummySchema, null, {locations: [geoPoint, geoPoint]},{
fields: {locations: {type: 'Array'}}
});
var out = transform.parseObjectToMongoObject(dummySchema, null, {locations: [geoPoint, geoPoint]});
expect(out.locations).toEqual([geoPoint, geoPoint]);
done();
});

it('in sub-object', (done) => {
var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180};
var out = transform.parseObjectToMongoObjectForCreate(dummySchema, null, { locations: { start: geoPoint }},{
fields: {locations: {type: 'Object'}}
});
var out = transform.parseObjectToMongoObject(dummySchema, null, { locations: { start: geoPoint }});
expect(out).toEqual({ locations: { start: geoPoint } });
done();
});
Expand Down Expand Up @@ -208,9 +196,7 @@ describe('transform schema key changes', () => {
var input = {
somePointer: {__type: 'Pointer', className: 'Micro', objectId: 'oft'}
};
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, {
fields: {somePointer: {type: 'Pointer'}}
});
var output = transform.parseObjectToMongoObject(dummySchema, null, input);
expect(typeof output._p_somePointer).toEqual('string');
expect(output._p_somePointer).toEqual('Micro$oft');
done();
Expand All @@ -220,9 +206,7 @@ describe('transform schema key changes', () => {
var input = {
userPointer: {__type: 'Pointer', className: '_User', objectId: 'qwerty'}
};
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, {
fields: {userPointer: {type: 'Pointer'}}
});
var output = transform.parseObjectToMongoObject(dummySchema, null, input);
expect(typeof output._p_userPointer).toEqual('string');
expect(output._p_userPointer).toEqual('_User$qwerty');
done();
Expand All @@ -235,7 +219,7 @@ describe('transform schema key changes', () => {
"Kevin": { "write": true }
}
};
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
var output = transform.parseObjectToMongoObject(dummySchema, null, input);
expect(typeof output._rperm).toEqual('object');
expect(typeof output._wperm).toEqual('object');
expect(output.ACL).toBeUndefined();
Expand Down
54 changes: 0 additions & 54 deletions spec/Parse.Push.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict';

let request = require('request');

describe('Parse.Push', () => {

Expand Down Expand Up @@ -90,57 +89,4 @@ describe('Parse.Push', () => {
done();
});
});

it('should not allow clients to query _PushStatus', done => {
setup()
.then(() => Parse.Push.send({
where: {
deviceType: 'ios'
},
data: {
badge: 'increment',
alert: 'Hello world!'
}
}, {useMasterKey: true}))
.then(() => {
request.get({
url: 'http://localhost:8378/1/classes/_PushStatus',
json: true,
headers: {
'X-Parse-Application-Id': 'test',
},
}, (error, response, body) => {
expect(body.results.length).toEqual(0);
done();
});
});
});

it('should allow master key to query _PushStatus', done => {
setup()
.then(() => Parse.Push.send({
where: {
deviceType: 'ios'
},
data: {
badge: 'increment',
alert: 'Hello world!'
}
}, {useMasterKey: true}))
.then(() => {
request.get({
url: 'http://localhost:8378/1/classes/_PushStatus',
json: true,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key': 'test',
},
}, (error, response, body) => {
expect(body.results.length).toEqual(1);
expect(body.results[0].query).toEqual('{"deviceType":"ios"}');
expect(body.results[0].payload).toEqual('{"badge":"increment","alert":"Hello world!"}');
done();
});
});
});
});
14 changes: 6 additions & 8 deletions src/Adapters/Storage/Mongo/MongoStorageAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,16 +145,14 @@ export class MongoStorageAdapter {
// this adapter doesn't know about the schema, return a promise that rejects with
// undefined as the reason.
getOneSchema(className) {
return this.schemaCollection()
.then(schemasCollection => schemasCollection._fechOneSchemaFrom_SCHEMA(className));
return this.schemaCollection().then(schemasCollection => schemasCollection._fechOneSchemaFrom_SCHEMA(className));
}

// TODO: As yet not particularly well specified. Creates an object. Shouldn't need the
// schemaController, but MongoTransform still needs it :( maybe shouldn't even need the schema,
// and should infer from the type. Or maybe does need the schema for validations. Or maybe needs
// the schem only for the legacy mongo format. We'll figure that out later.
createObject(className, object, schemaController, parseFormatSchema) {
const mongoObject = transform.parseObjectToMongoObjectForCreate(schemaController, className, object, parseFormatSchema);
// TODO: As yet not particularly well specified. Creates an object. Does it really need the schema?
// or can it fetch the schema itself? Also the schema is not currently a Parse format schema, and it
// should be, if we are passing it at all.
createObject(className, object, schema) {
const mongoObject = transform.parseObjectToMongoObject(schema, className, object);
return this.adaptiveCollection(className)
.then(collection => collection.insertOne(mongoObject));
}
Expand Down
51 changes: 21 additions & 30 deletions src/Adapters/Storage/Mongo/MongoTransform.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,9 @@ var Parse = require('parse/node').Parse;
// validate: true indicates that key names are to be validated.
//
// Returns an object with {key: key, value: value}.
export function transformKeyValue(schema, className, restKey, restValue, {
inArray,
inObject,
query,
update,
validate,
} = {}) {
export function transformKeyValue(schema, className, restKey, restValue, options) {
options = options || {};

// Check if the schema is known since it's a built-in field.
var key = restKey;
var timeField = false;
Expand Down Expand Up @@ -66,7 +62,7 @@ export function transformKeyValue(schema, className, restKey, restValue, {
return {key: key, value: restValue};
break;
case '$or':
if (!query) {
if (!options.query) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME,
'you can only use $or in queries');
}
Expand All @@ -79,7 +75,7 @@ export function transformKeyValue(schema, className, restKey, restValue, {
});
return {key: '$or', value: mongoSubqueries};
case '$and':
if (!query) {
if (!options.query) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME,
'you can only use $and in queries');
}
Expand All @@ -95,7 +91,7 @@ export function transformKeyValue(schema, className, restKey, restValue, {
// Other auth data
var authDataMatch = key.match(/^authData\.([a-zA-Z0-9_]+)\.id$/);
if (authDataMatch) {
if (query) {
if (options.query) {
var provider = authDataMatch[1];
// Special-case auth data.
return {key: '_auth_data_'+provider+'.id', value: restValue};
Expand All @@ -104,7 +100,7 @@ export function transformKeyValue(schema, className, restKey, restValue, {
'can only query on ' + key);
break;
};
if (validate && !key.match(/^[a-zA-Z][a-zA-Z0-9_\.]*$/)) {
if (options.validate && !key.match(/^[a-zA-Z][a-zA-Z0-9_\.]*$/)) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME,
'invalid key name: ' + key);
}
Expand All @@ -121,24 +117,24 @@ export function transformKeyValue(schema, className, restKey, restValue, {
(!expected && restValue && restValue.__type == 'Pointer')) {
key = '_p_' + key;
}
var expectedTypeIsArray = (expected && expected.type === 'Array');
var inArray = (expected && expected.type === 'Array');

// Handle query constraints
if (query) {
value = transformConstraint(restValue, expectedTypeIsArray);
if (options.query) {
value = transformConstraint(restValue, inArray);
if (value !== CannotTransform) {
return {key: key, value: value};
}
}

if (expectedTypeIsArray && query && !(restValue instanceof Array)) {
if (inArray && options.query && !(restValue instanceof Array)) {
return {
key: key, value: { '$all' : [restValue] }
};
}

// Handle atomic values
var value = transformAtom(restValue, false, { inArray, inObject });
var value = transformAtom(restValue, false, options);
if (value !== CannotTransform) {
if (timeField && (typeof value === 'string')) {
value = new Date(value);
Expand All @@ -154,7 +150,7 @@ export function transformKeyValue(schema, className, restKey, restValue, {

// Handle arrays
if (restValue instanceof Array) {
if (query) {
if (options.query) {
throw new Parse.Error(Parse.Error.INVALID_JSON,
'cannot use array as query param');
}
Expand All @@ -166,7 +162,7 @@ export function transformKeyValue(schema, className, restKey, restValue, {
}

// Handle update operators
value = transformUpdateOperator(restValue, !update);
value = transformUpdateOperator(restValue, !options.update);
if (value !== CannotTransform) {
return {key: key, value: value};
}
Expand Down Expand Up @@ -290,21 +286,16 @@ const parseObjectKeyValueToMongoObjectKeyValue = (

// Main exposed method to create new objects.
// restCreate is the "create" clause in REST API form.
function parseObjectToMongoObjectForCreate(schema, className, restCreate, parseFormatSchema) {
// Returns the mongo form of the object.
function parseObjectToMongoObject(schema, className, restCreate) {
if (className == '_User') {
restCreate = transformAuthData(restCreate);
}
var mongoCreate = transformACL(restCreate);
for (let restKey in restCreate) {
let { key, value } = parseObjectKeyValueToMongoObjectKeyValue(
schema,
className,
restKey,
restCreate[restKey],
parseFormatSchema
);
if (value !== undefined) {
mongoCreate[key] = value;
for (var restKey in restCreate) {
var out = transformKeyValue(schema, className, restKey, restCreate[restKey]);
if (out.value !== undefined) {
mongoCreate[out.key] = out.value;
}
}
return mongoCreate;
Expand Down Expand Up @@ -1006,7 +997,7 @@ var FileCoder = {

module.exports = {
transformKey,
parseObjectToMongoObjectForCreate,
parseObjectToMongoObject,
transformUpdate,
transformWhere,
transformSelect,
Expand Down
21 changes: 13 additions & 8 deletions src/Controllers/DatabaseController.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,19 +336,24 @@ DatabaseController.prototype.create = function(className, object, { acl } = {})
let originalObject = object;
object = deepcopy(object);

var schema;
var isMaster = acl === undefined;
var aclGroup = acl || [];

return this.validateClassName(className)
.then(() => this.loadSchema())
.then(schemaController => {
return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, 'create'))
.then(() => this.loadSchema())
.then(s => {
schema = s;
if (!isMaster) {
return schema.validatePermission(className, aclGroup, 'create');
}
return Promise.resolve();
})
.then(() => this.handleRelationUpdates(className, null, object))
.then(() => schemaController.enforceClassExists(className))
.then(() => schemaController.getOneSchema(className))
.then(schema => this.adapter.createObject(className, object, schemaController, schema))
.then(result => sanitizeDatabaseResult(originalObject, result.ops[0]));
})
.then(() => this.adapter.createObject(className, object, schema))
.then(result => {
return sanitizeDatabaseResult(originalObject, result.ops[0]);
});
};

DatabaseController.prototype.canAddField = function(schema, className, object, aclGroup) {
Expand Down
Loading