Progres towards moving mongo logic into its adapter
This commit is contained in:
@@ -27,6 +27,7 @@
|
|||||||
"deepcopy": "^0.6.1",
|
"deepcopy": "^0.6.1",
|
||||||
"express": "^4.13.4",
|
"express": "^4.13.4",
|
||||||
"intersect": "^1.0.1",
|
"intersect": "^1.0.1",
|
||||||
|
"lodash": "^4.8.2",
|
||||||
"lru-cache": "^4.0.0",
|
"lru-cache": "^4.0.0",
|
||||||
"mailgun-js": "^0.7.7",
|
"mailgun-js": "^0.7.7",
|
||||||
"mime": "^1.3.4",
|
"mime": "^1.3.4",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ MockController.prototype = Object.create(AdaptableController.prototype);
|
|||||||
MockController.prototype.constructor = AdaptableController;
|
MockController.prototype.constructor = AdaptableController;
|
||||||
|
|
||||||
describe("AdaptableController", ()=>{
|
describe("AdaptableController", ()=>{
|
||||||
|
|
||||||
it("should use the provided adapter", (done) => {
|
it("should use the provided adapter", (done) => {
|
||||||
var adapter = new FilesAdapter();
|
var adapter = new FilesAdapter();
|
||||||
var controller = new FilesController(adapter);
|
var controller = new FilesController(adapter);
|
||||||
@@ -22,7 +22,7 @@ describe("AdaptableController", ()=>{
|
|||||||
expect(controller.adapter).toBe(adapter);
|
expect(controller.adapter).toBe(adapter);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw when creating a new mock controller", (done) => {
|
it("should throw when creating a new mock controller", (done) => {
|
||||||
var adapter = new FilesAdapter();
|
var adapter = new FilesAdapter();
|
||||||
expect(() => {
|
expect(() => {
|
||||||
@@ -30,7 +30,7 @@ describe("AdaptableController", ()=>{
|
|||||||
}).toThrow();
|
}).toThrow();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fail setting the wrong adapter to the controller", (done) => {
|
it("should fail setting the wrong adapter to the controller", (done) => {
|
||||||
function WrongAdapter() {};
|
function WrongAdapter() {};
|
||||||
var adapter = new FilesAdapter();
|
var adapter = new FilesAdapter();
|
||||||
@@ -41,7 +41,7 @@ describe("AdaptableController", ()=>{
|
|||||||
}).toThrow();
|
}).toThrow();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fail to instantiate a controller with wrong adapter", (done) => {
|
it("should fail to instantiate a controller with wrong adapter", (done) => {
|
||||||
function WrongAdapter() {};
|
function WrongAdapter() {};
|
||||||
var adapter = new WrongAdapter();
|
var adapter = new WrongAdapter();
|
||||||
@@ -50,14 +50,14 @@ describe("AdaptableController", ()=>{
|
|||||||
}).toThrow();
|
}).toThrow();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fail to instantiate a controller without an adapter", (done) => {
|
it("should fail to instantiate a controller without an adapter", (done) => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
new FilesController();
|
new FilesController();
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should accept an object adapter", (done) => {
|
it("should accept an object adapter", (done) => {
|
||||||
var adapter = {
|
var adapter = {
|
||||||
createFile: function(config, filename, data) { },
|
createFile: function(config, filename, data) { },
|
||||||
@@ -70,18 +70,18 @@ describe("AdaptableController", ()=>{
|
|||||||
}).not.toThrow();
|
}).not.toThrow();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should accept an object adapter", (done) => {
|
it("should accept an object adapter", (done) => {
|
||||||
function AGoodAdapter() {};
|
function AGoodAdapter() {};
|
||||||
AGoodAdapter.prototype.createFile = function(config, filename, data) { };
|
AGoodAdapter.prototype.createFile = function(config, filename, data) { };
|
||||||
AGoodAdapter.prototype.deleteFile = function(config, filename) { };
|
AGoodAdapter.prototype.deleteFile = function(config, filename) { };
|
||||||
AGoodAdapter.prototype.getFileData = function(config, filename) { };
|
AGoodAdapter.prototype.getFileData = function(config, filename) { };
|
||||||
AGoodAdapter.prototype.getFileLocation = function(config, filename) { };
|
AGoodAdapter.prototype.getFileLocation = function(config, filename) { };
|
||||||
|
|
||||||
var adapter = new AGoodAdapter();
|
var adapter = new AGoodAdapter();
|
||||||
expect(() => {
|
expect(() => {
|
||||||
new FilesController(adapter);
|
new FilesController(adapter);
|
||||||
}).not.toThrow();
|
}).not.toThrow();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
55
spec/MongoSchemaCollectionAdapter.spec.js
Normal file
55
spec/MongoSchemaCollectionAdapter.spec.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const MongoSchemaCollection = require('../src/Adapters/Storage/Mongo/MongoSchemaCollection').default;
|
||||||
|
|
||||||
|
describe('MongoSchemaCollection', () => {
|
||||||
|
it('can transform legacy _client_permissions keys to parse format', done => {
|
||||||
|
expect(MongoSchemaCollection._TESTmongoSchemaToParseSchema({
|
||||||
|
"_id":"_Installation",
|
||||||
|
"_client_permissions":{
|
||||||
|
"get":true,
|
||||||
|
"find":true,
|
||||||
|
"update":true,
|
||||||
|
"create":true,
|
||||||
|
"delete":true,
|
||||||
|
},
|
||||||
|
"_metadata":{
|
||||||
|
"class_permissions":{
|
||||||
|
"get":{"*":true},
|
||||||
|
"find":{"*":true},
|
||||||
|
"update":{"*":true},
|
||||||
|
"create":{"*":true},
|
||||||
|
"delete":{"*":true},
|
||||||
|
"addField":{"*":true},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installationId":"string",
|
||||||
|
"deviceToken":"string",
|
||||||
|
"deviceType":"string",
|
||||||
|
"channels":"array",
|
||||||
|
"user":"*_User",
|
||||||
|
})).toEqual({
|
||||||
|
className: '_Installation',
|
||||||
|
fields: {
|
||||||
|
installationId: { type: 'String' },
|
||||||
|
deviceToken: { type: 'String' },
|
||||||
|
deviceType: { type: 'String' },
|
||||||
|
channels: { type: 'Array' },
|
||||||
|
user: { type: 'Pointer', targetClass: '_User' },
|
||||||
|
ACL: { type: 'ACL' },
|
||||||
|
createdAt: { type: 'Date' },
|
||||||
|
updatedAt: { type: 'Date' },
|
||||||
|
objectId: { type: 'String' },
|
||||||
|
},
|
||||||
|
classLevelPermissions: {
|
||||||
|
find: { '*': true },
|
||||||
|
get: { '*': true },
|
||||||
|
create: { '*': true },
|
||||||
|
update: { '*': true },
|
||||||
|
delete: { '*': true },
|
||||||
|
addField: { '*': true },
|
||||||
|
}
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -242,22 +242,21 @@ describe('OAuth', function() {
|
|||||||
it("should only create a single user with REST API", (done) => {
|
it("should only create a single user with REST API", (done) => {
|
||||||
var objectId;
|
var objectId;
|
||||||
createOAuthUser((error, response, body) => {
|
createOAuthUser((error, response, body) => {
|
||||||
|
expect(error).toBe(null);
|
||||||
|
var b = JSON.parse(body);
|
||||||
|
expect(b.objectId).not.toBeNull();
|
||||||
|
expect(b.objectId).not.toBeUndefined();
|
||||||
|
objectId = b.objectId;
|
||||||
|
|
||||||
|
createOAuthUser((error, response, body) => {
|
||||||
expect(error).toBe(null);
|
expect(error).toBe(null);
|
||||||
var b = JSON.parse(body);
|
var b = JSON.parse(body);
|
||||||
expect(b.objectId).not.toBeNull();
|
expect(b.objectId).not.toBeNull();
|
||||||
expect(b.objectId).not.toBeUndefined();
|
expect(b.objectId).not.toBeUndefined();
|
||||||
objectId = b.objectId;
|
expect(b.objectId).toBe(objectId);
|
||||||
|
done();
|
||||||
createOAuthUser((error, response, body) => {
|
|
||||||
expect(error).toBe(null);
|
|
||||||
var b = JSON.parse(body);
|
|
||||||
expect(b.objectId).not.toBeNull();
|
|
||||||
expect(b.objectId).not.toBeUndefined();
|
|
||||||
expect(b.objectId).toBe(objectId);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("unlink and link with custom provider", (done) => {
|
it("unlink and link with custom provider", (done) => {
|
||||||
|
|||||||
@@ -163,14 +163,26 @@ describe('Schema', () => {
|
|||||||
.then(schema => schema.addClassIfNotExists('NewClass', {
|
.then(schema => schema.addClassIfNotExists('NewClass', {
|
||||||
foo: {type: 'String'}
|
foo: {type: 'String'}
|
||||||
}))
|
}))
|
||||||
.then(result => {
|
.then(actualSchema => {
|
||||||
expect(result).toEqual({
|
const expectedSchema = {
|
||||||
_id: 'NewClass',
|
className: 'NewClass',
|
||||||
objectId: 'string',
|
fields: {
|
||||||
updatedAt: 'string',
|
objectId: { type: 'String' },
|
||||||
createdAt: 'string',
|
updatedAt: { type: 'Date' },
|
||||||
foo: 'string',
|
createdAt: { type: 'Date' },
|
||||||
})
|
ACL: { type: 'ACL' },
|
||||||
|
foo: { type: 'String' },
|
||||||
|
},
|
||||||
|
classLevelPermissions: {
|
||||||
|
find: { '*': true },
|
||||||
|
get: { '*': true },
|
||||||
|
create: { '*': true },
|
||||||
|
update: { '*': true },
|
||||||
|
delete: { '*': true },
|
||||||
|
addField: { '*': true },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -201,15 +213,27 @@ describe('Schema', () => {
|
|||||||
.then(schema => {
|
.then(schema => {
|
||||||
var p1 = schema.addClassIfNotExists('NewClass', {foo: {type: 'String'}});
|
var p1 = schema.addClassIfNotExists('NewClass', {foo: {type: 'String'}});
|
||||||
var p2 = schema.addClassIfNotExists('NewClass', {foo: {type: 'String'}});
|
var p2 = schema.addClassIfNotExists('NewClass', {foo: {type: 'String'}});
|
||||||
Promise.race([p1, p2]) //Use race because we expect the first completed promise to be the successful one
|
Promise.race([p1, p2])
|
||||||
.then(response => {
|
.then(actualSchema => {
|
||||||
expect(response).toEqual({
|
const expectedSchema = {
|
||||||
_id: 'NewClass',
|
className: 'NewClass',
|
||||||
objectId: 'string',
|
fields: {
|
||||||
updatedAt: 'string',
|
objectId: { type: 'String' },
|
||||||
createdAt: 'string',
|
updatedAt: { type: 'Date' },
|
||||||
foo: 'string',
|
createdAt: { type: 'Date' },
|
||||||
});
|
ACL: { type: 'ACL' },
|
||||||
|
foo: { type: 'String' },
|
||||||
|
},
|
||||||
|
classLevelPermissions: {
|
||||||
|
find: { '*': true },
|
||||||
|
get: { '*': true },
|
||||||
|
create: { '*': true },
|
||||||
|
update: { '*': true },
|
||||||
|
delete: { '*': true },
|
||||||
|
addField: { '*': true },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
||||||
});
|
});
|
||||||
Promise.all([p1,p2])
|
Promise.all([p1,p2])
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -373,23 +397,36 @@ describe('Schema', () => {
|
|||||||
aPointer: {type: 'Pointer', targetClass: 'ThisClassDoesNotExistYet'},
|
aPointer: {type: 'Pointer', targetClass: 'ThisClassDoesNotExistYet'},
|
||||||
aRelation: {type: 'Relation', targetClass: 'NewClass'},
|
aRelation: {type: 'Relation', targetClass: 'NewClass'},
|
||||||
}))
|
}))
|
||||||
.then(mongoObj => {
|
.then(actualSchema => {
|
||||||
expect(mongoObj).toEqual({
|
const expectedSchema = {
|
||||||
_id: 'NewClass',
|
className: 'NewClass',
|
||||||
objectId: 'string',
|
fields: {
|
||||||
createdAt: 'string',
|
objectId: { type: 'String' },
|
||||||
updatedAt: 'string',
|
updatedAt: { type: 'Date' },
|
||||||
aNumber: 'number',
|
createdAt: { type: 'Date' },
|
||||||
aString: 'string',
|
ACL: { type: 'ACL' },
|
||||||
aBool: 'boolean',
|
aString: { type: 'String' },
|
||||||
aDate: 'date',
|
aNumber: { type: 'Number' },
|
||||||
aObject: 'object',
|
aString: { type: 'String' },
|
||||||
aArray: 'array',
|
aBool: { type: 'Boolean' },
|
||||||
aGeoPoint: 'geopoint',
|
aDate: { type: 'Date' },
|
||||||
aFile: 'file',
|
aObject: { type: 'Object' },
|
||||||
aPointer: '*ThisClassDoesNotExistYet',
|
aArray: { type: 'Array' },
|
||||||
aRelation: 'relation<NewClass>',
|
aGeoPoint: { type: 'GeoPoint' },
|
||||||
});
|
aFile: { type: 'File' },
|
||||||
|
aPointer: { type: 'Pointer', targetClass: 'ThisClassDoesNotExistYet' },
|
||||||
|
aRelation: { type: 'Relation', targetClass: 'NewClass' },
|
||||||
|
},
|
||||||
|
classLevelPermissions: {
|
||||||
|
find: { '*': true },
|
||||||
|
get: { '*': true },
|
||||||
|
create: { '*': true },
|
||||||
|
update: { '*': true },
|
||||||
|
delete: { '*': true },
|
||||||
|
addField: { '*': true },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -399,23 +436,35 @@ describe('Schema', () => {
|
|||||||
.then(schema => schema.addClassIfNotExists('_Installation', {
|
.then(schema => schema.addClassIfNotExists('_Installation', {
|
||||||
foo: {type: 'Number'},
|
foo: {type: 'Number'},
|
||||||
}))
|
}))
|
||||||
.then(mongoObj => {
|
.then(actualSchema => {
|
||||||
expect(mongoObj).toEqual({
|
const expectedSchema = {
|
||||||
_id: '_Installation',
|
className: '_Installation',
|
||||||
createdAt: 'string',
|
fields: {
|
||||||
updatedAt: 'string',
|
objectId: { type: 'String' },
|
||||||
objectId: 'string',
|
updatedAt: { type: 'Date' },
|
||||||
foo: 'number',
|
createdAt: { type: 'Date' },
|
||||||
installationId: 'string',
|
ACL: { type: 'ACL' },
|
||||||
deviceToken: 'string',
|
foo: { type: 'Number' },
|
||||||
channels: 'array',
|
installationId: { type: 'String' },
|
||||||
deviceType: 'string',
|
deviceToken: { type: 'String' },
|
||||||
pushType: 'string',
|
channels: { type: 'Array' },
|
||||||
GCMSenderId: 'string',
|
deviceType: { type: 'String' },
|
||||||
timeZone: 'string',
|
pushType: { type: 'String' },
|
||||||
localeIdentifier: 'string',
|
GCMSenderId: { type: 'String' },
|
||||||
badge: 'number',
|
timeZone: { type: 'String' },
|
||||||
});
|
localeIdentifier: { type: 'String' },
|
||||||
|
badge: { type: 'Number' },
|
||||||
|
},
|
||||||
|
classLevelPermissions: {
|
||||||
|
find: { '*': true },
|
||||||
|
get: { '*': true },
|
||||||
|
create: { '*': true },
|
||||||
|
update: { '*': true },
|
||||||
|
delete: { '*': true },
|
||||||
|
addField: { '*': true },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -423,16 +472,28 @@ describe('Schema', () => {
|
|||||||
it('creates non-custom classes which include relation field', done => {
|
it('creates non-custom classes which include relation field', done => {
|
||||||
config.database.loadSchema()
|
config.database.loadSchema()
|
||||||
.then(schema => schema.addClassIfNotExists('_Role', {}))
|
.then(schema => schema.addClassIfNotExists('_Role', {}))
|
||||||
.then(mongoObj => {
|
.then(actualSchema => {
|
||||||
expect(mongoObj).toEqual({
|
const expectedSchema = {
|
||||||
_id: '_Role',
|
className: '_Role',
|
||||||
createdAt: 'string',
|
fields: {
|
||||||
updatedAt: 'string',
|
objectId: { type: 'String' },
|
||||||
objectId: 'string',
|
updatedAt: { type: 'Date' },
|
||||||
name: 'string',
|
createdAt: { type: 'Date' },
|
||||||
users: 'relation<_User>',
|
ACL: { type: 'ACL' },
|
||||||
roles: 'relation<_Role>',
|
name: { type: 'String' },
|
||||||
});
|
users: { type: 'Relation', targetClass: '_User' },
|
||||||
|
roles: { type: 'Relation', targetClass: '_Role' },
|
||||||
|
},
|
||||||
|
classLevelPermissions: {
|
||||||
|
find: { '*': true },
|
||||||
|
get: { '*': true },
|
||||||
|
create: { '*': true },
|
||||||
|
update: { '*': true },
|
||||||
|
delete: { '*': true },
|
||||||
|
addField: { '*': true },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -440,19 +501,31 @@ describe('Schema', () => {
|
|||||||
it('creates non-custom classes which include pointer field', done => {
|
it('creates non-custom classes which include pointer field', done => {
|
||||||
config.database.loadSchema()
|
config.database.loadSchema()
|
||||||
.then(schema => schema.addClassIfNotExists('_Session', {}))
|
.then(schema => schema.addClassIfNotExists('_Session', {}))
|
||||||
.then(mongoObj => {
|
.then(actualSchema => {
|
||||||
expect(mongoObj).toEqual({
|
const expectedSchema = {
|
||||||
_id: '_Session',
|
className: '_Session',
|
||||||
createdAt: 'string',
|
fields: {
|
||||||
updatedAt: 'string',
|
objectId: { type: 'String' },
|
||||||
objectId: 'string',
|
updatedAt: { type: 'Date' },
|
||||||
restricted: 'boolean',
|
createdAt: { type: 'Date' },
|
||||||
user: '*_User',
|
restricted: { type: 'Boolean' },
|
||||||
installationId: 'string',
|
user: { type: 'Pointer', targetClass: '_User' },
|
||||||
sessionToken: 'string',
|
installationId: { type: 'String' },
|
||||||
expiresAt: 'date',
|
sessionToken: { type: 'String' },
|
||||||
createdWith: 'object'
|
expiresAt: { type: 'Date' },
|
||||||
});
|
createdWith: { type: 'Object' },
|
||||||
|
ACL: { type: 'ACL' },
|
||||||
|
},
|
||||||
|
classLevelPermissions: {
|
||||||
|
find: { '*': true },
|
||||||
|
get: { '*': true },
|
||||||
|
create: { '*': true },
|
||||||
|
update: { '*': true },
|
||||||
|
delete: { '*': true },
|
||||||
|
addField: { '*': true },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -583,14 +656,26 @@ describe('Schema', () => {
|
|||||||
schema.addClassIfNotExists('NewClass', {
|
schema.addClassIfNotExists('NewClass', {
|
||||||
relationField: {type: 'Relation', targetClass: '_User'}
|
relationField: {type: 'Relation', targetClass: '_User'}
|
||||||
})
|
})
|
||||||
.then(mongoObj => {
|
.then(actualSchema => {
|
||||||
expect(mongoObj).toEqual({
|
const expectedSchema = {
|
||||||
_id: 'NewClass',
|
className: 'NewClass',
|
||||||
objectId: 'string',
|
fields: {
|
||||||
updatedAt: 'string',
|
objectId: { type: 'String' },
|
||||||
createdAt: 'string',
|
updatedAt: { type: 'Date' },
|
||||||
relationField: 'relation<_User>',
|
createdAt: { type: 'Date' },
|
||||||
});
|
ACL: { type: 'ACL' },
|
||||||
|
relationField: { type: 'Relation', targetClass: '_User' },
|
||||||
|
},
|
||||||
|
classLevelPermissions: {
|
||||||
|
find: { '*': true },
|
||||||
|
get: { '*': true },
|
||||||
|
create: { '*': true },
|
||||||
|
update: { '*': true },
|
||||||
|
delete: { '*': true },
|
||||||
|
addField: { '*': true },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
||||||
})
|
})
|
||||||
.then(() => config.database.collectionExists('_Join:relationField:NewClass'))
|
.then(() => config.database.collectionExists('_Join:relationField:NewClass'))
|
||||||
.then(exist => {
|
.then(exist => {
|
||||||
@@ -703,33 +788,4 @@ describe('Schema', () => {
|
|||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles legacy _client_permissions keys without crashing', done => {
|
|
||||||
Schema.mongoSchemaToSchemaAPIResponse({
|
|
||||||
"_id":"_Installation",
|
|
||||||
"_client_permissions":{
|
|
||||||
"get":true,
|
|
||||||
"find":true,
|
|
||||||
"update":true,
|
|
||||||
"create":true,
|
|
||||||
"delete":true,
|
|
||||||
},
|
|
||||||
"_metadata":{
|
|
||||||
"class_permissions":{
|
|
||||||
"get":{"*":true},
|
|
||||||
"find":{"*":true},
|
|
||||||
"update":{"*":true},
|
|
||||||
"create":{"*":true},
|
|
||||||
"delete":{"*":true},
|
|
||||||
"addField":{"*":true},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"installationId":"string",
|
|
||||||
"deviceToken":"string",
|
|
||||||
"deviceType":"string",
|
|
||||||
"channels":"array",
|
|
||||||
"user":"*_User",
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,67 @@
|
|||||||
|
|
||||||
import MongoCollection from './MongoCollection';
|
import MongoCollection from './MongoCollection';
|
||||||
|
|
||||||
|
function mongoFieldToParseSchemaField(type) {
|
||||||
|
if (type[0] === '*') {
|
||||||
|
return {
|
||||||
|
type: 'Pointer',
|
||||||
|
targetClass: type.slice(1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (type.startsWith('relation<')) {
|
||||||
|
return {
|
||||||
|
type: 'Relation',
|
||||||
|
targetClass: type.slice('relation<'.length, type.length - 1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case 'number': return {type: 'Number'};
|
||||||
|
case 'string': return {type: 'String'};
|
||||||
|
case 'boolean': return {type: 'Boolean'};
|
||||||
|
case 'date': return {type: 'Date'};
|
||||||
|
case 'map':
|
||||||
|
case 'object': return {type: 'Object'};
|
||||||
|
case 'array': return {type: 'Array'};
|
||||||
|
case 'geopoint': return {type: 'GeoPoint'};
|
||||||
|
case 'file': return {type: 'File'};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonFieldSchemaKeys = ['_id', '_metadata', '_client_permissions'];
|
||||||
|
function mongoSchemaFieldsToParseSchemaFields(schema) {
|
||||||
|
var fieldNames = Object.keys(schema).filter(key => nonFieldSchemaKeys.indexOf(key) === -1);
|
||||||
|
var response = fieldNames.reduce((obj, fieldName) => {
|
||||||
|
obj[fieldName] = mongoFieldToParseSchemaField(schema[fieldName])
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
response.ACL = {type: 'ACL'};
|
||||||
|
response.createdAt = {type: 'Date'};
|
||||||
|
response.updatedAt = {type: 'Date'};
|
||||||
|
response.objectId = {type: 'String'};
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultCLPS = Object.freeze({
|
||||||
|
find: {'*': true},
|
||||||
|
get: {'*': true},
|
||||||
|
create: {'*': true},
|
||||||
|
update: {'*': true},
|
||||||
|
delete: {'*': true},
|
||||||
|
addField: {'*': true},
|
||||||
|
});
|
||||||
|
|
||||||
|
function mongoSchemaToParseSchema(mongoSchema) {
|
||||||
|
let clpsFromMongoObject = {};
|
||||||
|
if (mongoSchema._metadata && mongoSchema._metadata.class_permissions) {
|
||||||
|
clpsFromMongoObject = mongoSchema._metadata.class_permissions;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
className: mongoSchema._id,
|
||||||
|
fields: mongoSchemaFieldsToParseSchemaFields(mongoSchema),
|
||||||
|
classLevelPermissions: {...defaultCLPS, ...clpsFromMongoObject},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function _mongoSchemaQueryFromNameQuery(name: string, query) {
|
function _mongoSchemaQueryFromNameQuery(name: string, query) {
|
||||||
return _mongoSchemaObjectFromNameFields(name, query);
|
return _mongoSchemaObjectFromNameFields(name, query);
|
||||||
}
|
}
|
||||||
@@ -15,20 +76,31 @@ function _mongoSchemaObjectFromNameFields(name: string, fields) {
|
|||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MongoSchemaCollection {
|
class MongoSchemaCollection {
|
||||||
_collection: MongoCollection;
|
_collection: MongoCollection;
|
||||||
|
|
||||||
constructor(collection: MongoCollection) {
|
constructor(collection: MongoCollection) {
|
||||||
this._collection = collection;
|
this._collection = collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return a promise for all schemas known to this adapter, in Parse format. In case the
|
||||||
|
// schemas cannot be retrieved, returns a promise that rejects. Requirements fot the
|
||||||
|
// rejection reason are TBD.
|
||||||
getAllSchemas() {
|
getAllSchemas() {
|
||||||
return this._collection._rawFind({});
|
return this._collection._rawFind({})
|
||||||
|
.then(schemas => schemas.map(mongoSchemaToParseSchema));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return a promise for the schema with the given name, in Parse format. If
|
||||||
|
// this adapter doesn't know about the schema, return a promise that rejects with
|
||||||
|
// undefined as the reason.
|
||||||
findSchema(name: string) {
|
findSchema(name: string) {
|
||||||
return this._collection._rawFind(_mongoSchemaQueryFromNameQuery(name), { limit: 1 }).then(results => {
|
return this._collection._rawFind(_mongoSchemaQueryFromNameQuery(name), { limit: 1 }).then(results => {
|
||||||
return results[0];
|
if (results.length === 1) {
|
||||||
|
return mongoSchemaToParseSchema(results[0]);
|
||||||
|
} else {
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,3 +128,13 @@ export default class MongoSchemaCollection {
|
|||||||
return this._collection.upsertOne(_mongoSchemaQueryFromNameQuery(name, query), update);
|
return this._collection.upsertOne(_mongoSchemaQueryFromNameQuery(name, query), update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exported for testing reasons and because we haven't moved all mongo schema format
|
||||||
|
// related logic into the database adapter yet.
|
||||||
|
MongoSchemaCollection._TESTmongoSchemaToParseSchema = mongoSchemaToParseSchema
|
||||||
|
|
||||||
|
// Exported because we haven't moved all mongo schema format related logic
|
||||||
|
// into the database adapter yet. We will remove this before too long.
|
||||||
|
MongoSchemaCollection._DONOTUSEmongoFieldToParseSchemaField = mongoFieldToParseSchemaField
|
||||||
|
|
||||||
|
export default MongoSchemaCollection
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ function classNameMismatchResponse(bodyClass, pathClass) {
|
|||||||
function getAllSchemas(req) {
|
function getAllSchemas(req) {
|
||||||
return req.config.database.schemaCollection()
|
return req.config.database.schemaCollection()
|
||||||
.then(collection => collection.getAllSchemas())
|
.then(collection => collection.getAllSchemas())
|
||||||
.then(schemas => schemas.map(Schema.mongoSchemaToSchemaAPIResponse))
|
|
||||||
.then(schemas => ({ response: { results: schemas } }));
|
.then(schemas => ({ response: { results: schemas } }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,11 +24,13 @@ function getOneSchema(req) {
|
|||||||
const className = req.params.className;
|
const className = req.params.className;
|
||||||
return req.config.database.schemaCollection()
|
return req.config.database.schemaCollection()
|
||||||
.then(collection => collection.findSchema(className))
|
.then(collection => collection.findSchema(className))
|
||||||
.then(mongoSchema => {
|
.then(schema => ({ response: schema }))
|
||||||
if (!mongoSchema) {
|
.catch(error => {
|
||||||
|
if (error === undefined) {
|
||||||
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} does not exist.`);
|
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} does not exist.`);
|
||||||
|
} else {
|
||||||
|
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error.');
|
||||||
}
|
}
|
||||||
return { response: Schema.mongoSchemaToSchemaAPIResponse(mongoSchema) };
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ function createSchema(req) {
|
|||||||
|
|
||||||
return req.config.database.loadSchema()
|
return req.config.database.loadSchema()
|
||||||
.then(schema => schema.addClassIfNotExists(className, req.body.fields, req.body.classLevelPermissions))
|
.then(schema => schema.addClassIfNotExists(className, req.body.fields, req.body.classLevelPermissions))
|
||||||
.then(result => ({ response: Schema.mongoSchemaToSchemaAPIResponse(result) }));
|
.then(schema => ({ response: schema }));
|
||||||
}
|
}
|
||||||
|
|
||||||
function modifySchema(req) {
|
function modifySchema(req) {
|
||||||
@@ -55,8 +56,8 @@ function modifySchema(req) {
|
|||||||
return classNameMismatchResponse(req.body.className, req.params.className);
|
return classNameMismatchResponse(req.body.className, req.params.className);
|
||||||
}
|
}
|
||||||
|
|
||||||
var submittedFields = req.body.fields || {};
|
let submittedFields = req.body.fields || {};
|
||||||
var className = req.params.className;
|
let className = req.params.className;
|
||||||
|
|
||||||
return req.config.database.loadSchema()
|
return req.config.database.loadSchema()
|
||||||
.then(schema => {
|
.then(schema => {
|
||||||
|
|||||||
180
src/Schema.js
180
src/Schema.js
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
var Parse = require('parse/node').Parse;
|
var Parse = require('parse/node').Parse;
|
||||||
var transform = require('./transform');
|
var transform = require('./transform');
|
||||||
|
import MongoSchemaCollection from './Adapters/Storage/Mongo/MongoSchemaCollection';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
const defaultColumns = Object.freeze({
|
const defaultColumns = Object.freeze({
|
||||||
// Contain the default columns for every parse object type (except _Join collection)
|
// Contain the default columns for every parse object type (except _Join collection)
|
||||||
@@ -113,15 +115,6 @@ function verifyPermissionKey(key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CLPValidKeys = Object.freeze(['find', 'get', 'create', 'update', 'delete', 'addField']);
|
const CLPValidKeys = Object.freeze(['find', 'get', 'create', 'update', 'delete', 'addField']);
|
||||||
let DefaultClassLevelPermissions = () => {
|
|
||||||
return CLPValidKeys.reduce((perms, key) => {
|
|
||||||
perms[key] = {
|
|
||||||
'*': true
|
|
||||||
};
|
|
||||||
return perms;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateCLP(perms) {
|
function validateCLP(perms) {
|
||||||
if (!perms) {
|
if (!perms) {
|
||||||
return;
|
return;
|
||||||
@@ -220,11 +213,8 @@ function schemaAPITypeToMongoFieldType(type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a schema from a Mongo collection and the exported schema format.
|
// Stores the entire schema of the app in a weird hybrid format somewhere between
|
||||||
// mongoSchema should be a list of objects, each with:
|
// the mongo format and the Parse format. Soon, this will all be Parse format.
|
||||||
// '_id' indicates the className
|
|
||||||
// '_metadata' is ignored for now
|
|
||||||
// Everything else is expected to be a userspace field.
|
|
||||||
class Schema {
|
class Schema {
|
||||||
_collection;
|
_collection;
|
||||||
data;
|
data;
|
||||||
@@ -233,7 +223,8 @@ class Schema {
|
|||||||
constructor(collection) {
|
constructor(collection) {
|
||||||
this._collection = collection;
|
this._collection = collection;
|
||||||
|
|
||||||
// this.data[className][fieldName] tells you the type of that field
|
// this.data[className][fieldName] tells you the type of that field, in mongo format
|
||||||
|
// TODO: use Parse format
|
||||||
this.data = {};
|
this.data = {};
|
||||||
// this.perms[className][operation] tells you the acl-style permissions
|
// this.perms[className][operation] tells you the acl-style permissions
|
||||||
this.perms = {};
|
this.perms = {};
|
||||||
@@ -242,43 +233,24 @@ class Schema {
|
|||||||
reloadData() {
|
reloadData() {
|
||||||
this.data = {};
|
this.data = {};
|
||||||
this.perms = {};
|
this.perms = {};
|
||||||
return this._collection.getAllSchemas().then(results => {
|
return this._collection.getAllSchemas().then(allSchemas => {
|
||||||
for (let obj of results) {
|
allSchemas.forEach(schema => {
|
||||||
let className = null;
|
const parseFormatSchema = {
|
||||||
let classData = {};
|
...defaultColumns._Default,
|
||||||
let permsData = null;
|
...(defaultColumns[schema.className] || {}),
|
||||||
Object.keys(obj).forEach(key => {
|
...schema.fields,
|
||||||
let value = obj[key];
|
|
||||||
switch (key) {
|
|
||||||
case '_id':
|
|
||||||
className = value;
|
|
||||||
break;
|
|
||||||
case '_metadata':
|
|
||||||
if (value && value['class_permissions']) {
|
|
||||||
permsData = value['class_permissions'];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
classData[key] = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (className) {
|
|
||||||
// merge with the default schema
|
|
||||||
let defaultClassData = Object.assign({}, defaultColumns._Default, defaultColumns[className]);
|
|
||||||
defaultClassData = Object.keys(defaultClassData).reduce((memo, key) => {
|
|
||||||
let type = schemaAPITypeToMongoFieldType(defaultClassData[key]).result;
|
|
||||||
if (type) {
|
|
||||||
memo[key] = type;
|
|
||||||
}
|
|
||||||
return memo;
|
|
||||||
}, {});
|
|
||||||
classData = Object.assign({}, defaultClassData, classData);
|
|
||||||
this.data[className] = classData;
|
|
||||||
if (permsData) {
|
|
||||||
this.perms[className] = permsData;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
// ACL doesn't show up in mongo, it's implicit
|
||||||
|
delete parseFormatSchema.ACL;
|
||||||
|
// createdAt and updatedAt are wacky and have legacy baggage
|
||||||
|
parseFormatSchema.createdAt = { type: 'String' };
|
||||||
|
parseFormatSchema.updatedAt = { type: 'String' };
|
||||||
|
this.data[schema.className] = _.mapValues(parseFormatSchema, parseField =>
|
||||||
|
schemaAPITypeToMongoFieldType(parseField).result
|
||||||
|
);
|
||||||
|
|
||||||
|
this.perms[schema.className] = schema.classLevelPermissions;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +272,8 @@ class Schema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this._collection.addSchema(className, mongoObject.result)
|
return this._collection.addSchema(className, mongoObject.result)
|
||||||
.then(result => result.ops[0])
|
//TODO: Move this logic into the database adapter
|
||||||
|
.then(result => MongoSchemaCollection._TESTmongoSchemaToParseSchema(result.ops[0]))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
if (error.code === 11000) { //Mongo's duplicate key error
|
if (error.code === 11000) { //Mongo's duplicate key error
|
||||||
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`);
|
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`);
|
||||||
@@ -354,7 +327,8 @@ class Schema {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
return this.setPermissions(className, classLevelPermissions)
|
return this.setPermissions(className, classLevelPermissions)
|
||||||
})
|
})
|
||||||
.then(() => { return mongoSchemaToSchemaAPIResponse(mongoObject.result) });
|
//TODO: Move this logic into the database adapter
|
||||||
|
.then(() => MongoSchemaCollection._TESTmongoSchemaToParseSchema(mongoObject.result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -384,7 +358,7 @@ class Schema {
|
|||||||
'schema is frozen, cannot add: ' + className);
|
'schema is frozen, cannot add: ' + className);
|
||||||
}
|
}
|
||||||
// We don't have this class. Update the schema
|
// We don't have this class. Update the schema
|
||||||
return this._collection.addSchema(className).then(() => {
|
return this.addClassIfNotExists(className, []).then(() => {
|
||||||
// The schema update succeeded. Reload the schema
|
// The schema update succeeded. Reload the schema
|
||||||
return this.reloadData();
|
return this.reloadData();
|
||||||
}, () => {
|
}, () => {
|
||||||
@@ -421,20 +395,20 @@ class Schema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns a promise that resolves successfully to the new schema
|
// Returns a promise that resolves successfully to the new schema
|
||||||
// object if the provided className-key-type tuple is valid.
|
// object if the provided className-fieldName-type tuple is valid.
|
||||||
// The className must already be validated.
|
// The className must already be validated.
|
||||||
// If 'freeze' is true, refuse to update the schema for this field.
|
// If 'freeze' is true, refuse to update the schema for this field.
|
||||||
validateField(className, key, type, freeze) {
|
validateField(className, fieldName, type, freeze) {
|
||||||
// Just to check that the key is valid
|
// Just to check that the fieldName is valid
|
||||||
transform.transformKey(this, className, key);
|
transform.transformKey(this, className, fieldName);
|
||||||
|
|
||||||
if( key.indexOf(".") > 0 ) {
|
if( fieldName.indexOf(".") > 0 ) {
|
||||||
// subdocument key (x.y) => ok if x is of type 'object'
|
// subdocument key (x.y) => ok if x is of type 'object'
|
||||||
key = key.split(".")[ 0 ];
|
fieldName = fieldName.split(".")[ 0 ];
|
||||||
type = 'object';
|
type = 'object';
|
||||||
}
|
}
|
||||||
|
|
||||||
var expected = this.data[className][key];
|
let expected = this.data[className][fieldName];
|
||||||
if (expected) {
|
if (expected) {
|
||||||
expected = (expected === 'map' ? 'object' : expected);
|
expected = (expected === 'map' ? 'object' : expected);
|
||||||
if (expected === type) {
|
if (expected === type) {
|
||||||
@@ -442,14 +416,13 @@ class Schema {
|
|||||||
} else {
|
} else {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.INCORRECT_TYPE,
|
Parse.Error.INCORRECT_TYPE,
|
||||||
'schema mismatch for ' + className + '.' + key +
|
`schema mismatch for ${className}.${fieldName}; expected ${expected} but got ${type}`
|
||||||
'; expected ' + expected + ' but got ' + type);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (freeze) {
|
if (freeze) {
|
||||||
throw new Parse.Error(Parse.Error.INVALID_JSON,
|
throw new Parse.Error(Parse.Error.INVALID_JSON, `schema is frozen, cannot add ${fieldName} field`);
|
||||||
'schema is frozen, cannot add ' + key + ' field');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't have this field, but if the value is null or undefined,
|
// We don't have this field, but if the value is null or undefined,
|
||||||
@@ -473,9 +446,9 @@ class Schema {
|
|||||||
// Note that we use the $exists guard and $set to avoid race
|
// Note that we use the $exists guard and $set to avoid race
|
||||||
// conditions in the database. This is important!
|
// conditions in the database. This is important!
|
||||||
let query = {};
|
let query = {};
|
||||||
query[key] = { '$exists': false };
|
query[fieldName] = { '$exists': false };
|
||||||
var update = {};
|
var update = {};
|
||||||
update[key] = type;
|
update[fieldName] = type;
|
||||||
update = {'$set': update};
|
update = {'$set': update};
|
||||||
return this._collection.upsertSchema(className, query, update).then(() => {
|
return this._collection.upsertSchema(className, query, update).then(() => {
|
||||||
// The update succeeded. Reload the schema
|
// The update succeeded. Reload the schema
|
||||||
@@ -487,7 +460,7 @@ class Schema {
|
|||||||
return this.reloadData();
|
return this.reloadData();
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// Ensure that the schema now validates
|
// Ensure that the schema now validates
|
||||||
return this.validateField(className, key, type, true);
|
return this.validateField(className, fieldName, type, true);
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
// The schema still doesn't validate. Give up
|
// The schema still doesn't validate. Give up
|
||||||
throw new Parse.Error(Parse.Error.INVALID_JSON,
|
throw new Parse.Error(Parse.Error.INVALID_JSON,
|
||||||
@@ -557,11 +530,11 @@ class Schema {
|
|||||||
validateObject(className, object, query) {
|
validateObject(className, object, query) {
|
||||||
var geocount = 0;
|
var geocount = 0;
|
||||||
var promise = this.validateClassName(className);
|
var promise = this.validateClassName(className);
|
||||||
for (var key in object) {
|
for (let fieldName in object) {
|
||||||
if (object[key] === undefined) {
|
if (object[fieldName] === undefined) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var expected = getType(object[key]);
|
var expected = getType(object[fieldName]);
|
||||||
if (expected === 'geopoint') {
|
if (expected === 'geopoint') {
|
||||||
geocount++;
|
geocount++;
|
||||||
}
|
}
|
||||||
@@ -576,7 +549,12 @@ class Schema {
|
|||||||
if (!expected) {
|
if (!expected) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
promise = thenValidateField(promise, className, key, expected);
|
if (fieldName === 'ACL') {
|
||||||
|
// Every object has ACL implicitly.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
promise = thenValidateField(promise, className, fieldName, expected);
|
||||||
}
|
}
|
||||||
promise = thenValidateRequiredColumns(promise, className, object, query);
|
promise = thenValidateRequiredColumns(promise, className, object, query);
|
||||||
return promise;
|
return promise;
|
||||||
@@ -735,32 +713,6 @@ function mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPe
|
|||||||
return { result: mongoObject };
|
return { result: mongoObject };
|
||||||
}
|
}
|
||||||
|
|
||||||
function mongoFieldTypeToSchemaAPIType(type) {
|
|
||||||
if (type[0] === '*') {
|
|
||||||
return {
|
|
||||||
type: 'Pointer',
|
|
||||||
targetClass: type.slice(1),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (type.startsWith('relation<')) {
|
|
||||||
return {
|
|
||||||
type: 'Relation',
|
|
||||||
targetClass: type.slice('relation<'.length, type.length - 1),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
switch (type) {
|
|
||||||
case 'number': return {type: 'Number'};
|
|
||||||
case 'string': return {type: 'String'};
|
|
||||||
case 'boolean': return {type: 'Boolean'};
|
|
||||||
case 'date': return {type: 'Date'};
|
|
||||||
case 'map':
|
|
||||||
case 'object': return {type: 'Object'};
|
|
||||||
case 'array': return {type: 'Array'};
|
|
||||||
case 'geopoint': return {type: 'GeoPoint'};
|
|
||||||
case 'file': return {type: 'File'};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Builds a new schema (in schema API response format) out of an
|
// Builds a new schema (in schema API response format) out of an
|
||||||
// existing mongo schema + a schemas API put request. This response
|
// existing mongo schema + a schemas API put request. This response
|
||||||
// does not include the default fields, as it is intended to be passed
|
// does not include the default fields, as it is intended to be passed
|
||||||
@@ -776,7 +728,7 @@ function buildMergedSchemaObject(mongoObject, putRequest) {
|
|||||||
}
|
}
|
||||||
var fieldIsDeleted = putRequest[oldField] && putRequest[oldField].__op === 'Delete'
|
var fieldIsDeleted = putRequest[oldField] && putRequest[oldField].__op === 'Delete'
|
||||||
if (!fieldIsDeleted) {
|
if (!fieldIsDeleted) {
|
||||||
newSchema[oldField] = mongoFieldTypeToSchemaAPIType(mongoObject[oldField]);
|
newSchema[oldField] = MongoSchemaCollection._DONOTUSEmongoFieldToParseSchemaField(mongoObject[oldField]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -891,41 +843,11 @@ function getObjectType(obj) {
|
|||||||
return 'object';
|
return 'object';
|
||||||
}
|
}
|
||||||
|
|
||||||
const nonFieldSchemaKeys = ['_id', '_metadata', '_client_permissions'];
|
|
||||||
function mongoSchemaAPIResponseFields(schema) {
|
|
||||||
var fieldNames = Object.keys(schema).filter(key => nonFieldSchemaKeys.indexOf(key) === -1);
|
|
||||||
var response = fieldNames.reduce((obj, fieldName) => {
|
|
||||||
obj[fieldName] = mongoFieldTypeToSchemaAPIType(schema[fieldName])
|
|
||||||
return obj;
|
|
||||||
}, {});
|
|
||||||
response.ACL = {type: 'ACL'};
|
|
||||||
response.createdAt = {type: 'Date'};
|
|
||||||
response.updatedAt = {type: 'Date'};
|
|
||||||
response.objectId = {type: 'String'};
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mongoSchemaToSchemaAPIResponse(schema) {
|
|
||||||
let result = {
|
|
||||||
className: schema._id,
|
|
||||||
fields: mongoSchemaAPIResponseFields(schema),
|
|
||||||
};
|
|
||||||
|
|
||||||
let classLevelPermissions = DefaultClassLevelPermissions();
|
|
||||||
if (schema._metadata && schema._metadata.class_permissions) {
|
|
||||||
classLevelPermissions = Object.assign({}, classLevelPermissions, schema._metadata.class_permissions);
|
|
||||||
}
|
|
||||||
result.classLevelPermissions = classLevelPermissions;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
load,
|
load,
|
||||||
classNameIsValid,
|
classNameIsValid,
|
||||||
invalidClassNameMessage,
|
invalidClassNameMessage,
|
||||||
schemaAPITypeToMongoFieldType,
|
schemaAPITypeToMongoFieldType,
|
||||||
buildMergedSchemaObject,
|
buildMergedSchemaObject,
|
||||||
mongoFieldTypeToSchemaAPIType,
|
|
||||||
mongoSchemaToSchemaAPIResponse,
|
|
||||||
systemClasses,
|
systemClasses,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import cache from './cache';
|
import cache from './cache';
|
||||||
import log from './logger';
|
import log from './logger';
|
||||||
|
|
||||||
var Parse = require('parse/node').Parse;
|
var Parse = require('parse/node').Parse;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user