diff --git a/.travis.yml b/.travis.yml index 28561741c..fc6cec052 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ --- language: node_js node_js: -- '5' +- '6.11.4' branches: only: diff --git a/integration/package.json b/integration/package.json index b5a35b928..5927ccbc9 100644 --- a/integration/package.json +++ b/integration/package.json @@ -3,7 +3,7 @@ "dependencies": { "express": "^4.13.4", "mocha": "^2.4.5", - "parse-server": "^2.6.0" + "parse-server": "^2.7.0" }, "scripts": { "test": "mocha --reporter dot -t 5000" diff --git a/integration/test/ParseSchemaTest.js b/integration/test/ParseSchemaTest.js new file mode 100644 index 000000000..91dfbffc9 --- /dev/null +++ b/integration/test/ParseSchemaTest.js @@ -0,0 +1,262 @@ +const assert = require('assert'); +const clear = require('./clear'); +const Parse = require('../../node'); + +describe('Schema', () => { + before(() => { + Parse.initialize('integration'); + Parse.CoreManager.set('SERVER_URL', 'http://localhost:1337/parse'); + Parse.CoreManager.set('MASTER_KEY', 'notsosecret'); + Parse.Storage._clear(); + }); + + beforeEach((done) => { + clear().then(() => { + done(); + }); + }); + + it('invalid get all no schema', (done) => { + Parse.Schema.all().then(() => {}).fail((e) => { + done(); + }); + }); + + it('invalid get no schema', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + testSchema.get().then(() => {}).fail((e) => { + done(); + }); + }); + + it('save', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + testSchema.save().then((result) => { + assert.equal(result.className, 'SchemaTest'); + done(); + }); + }); + + it('get', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + testSchema + .addField('defaultFieldString') + .addString('stringField') + .addNumber('numberField') + .addBoolean('booleanField') + .addDate('dateField') + .addFile('fileField') + .addGeoPoint('geoPointField') + .addArray('arrayField') + .addObject('objectField') + .addPointer('pointerField', '_User') + .addRelation('relationField', '_User'); + + testSchema.save().then(() => { + return testSchema.get(); + }).then((result) => { + assert.equal(result.fields.defaultFieldString.type, 'String'); + assert.equal(result.fields.stringField.type, 'String'); + assert.equal(result.fields.numberField.type, 'Number'); + assert.equal(result.fields.booleanField.type, 'Boolean'); + assert.equal(result.fields.dateField.type, 'Date'); + assert.equal(result.fields.fileField.type, 'File'); + assert.equal(result.fields.geoPointField.type, 'GeoPoint'); + assert.equal(result.fields.arrayField.type, 'Array'); + assert.equal(result.fields.objectField.type, 'Object'); + assert.equal(result.fields.pointerField.type, 'Pointer'); + assert.equal(result.fields.relationField.type, 'Relation'); + assert.equal(result.fields.pointerField.targetClass, '_User'); + assert.equal(result.fields.relationField.targetClass, '_User'); + done(); + }); + }); + + it('all', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + testSchema.save().then(() => { + return Parse.Schema.all(); + }).then((results) => { + assert.equal(results.length, 1); + done(); + }); + }); + + it('update', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + testSchema.addString('name'); + testSchema.save().then(() => { + testSchema.deleteField('name'); + testSchema.addNumber('quantity'); + testSchema.addBoolean('status'); + return testSchema.update(); + }).then((result) => { + assert.equal(result.fields.status.type, 'Boolean'); + assert.equal(result.fields.quantity.type, 'Number'); + assert.equal(result.fields.name, undefined); + done(); + }); + }); + + it('multiple update', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + testSchema.save().then(() => { + testSchema.addString('name'); + return testSchema.update(); + }).then(() => { + return testSchema.update(); + }).then(() => { + return testSchema.get(); + }).then((result) => { + assert.equal(Object.keys(result.fields).length, 5); + done(); + }); + }); + + it('delete', (done) => { + const testSchema1 = new Parse.Schema('SchemaTest1'); + const testSchema2 = new Parse.Schema('SchemaTest2'); + testSchema1.save().then(() => { + return testSchema2.save(); + }).then(() => { + return Parse.Schema.all(); + }).then((results) => { + assert.equal(results.length, 2); + return testSchema1.delete(); + }).then(() => { + return Parse.Schema.all(); + }).then((results) => { + assert.equal(results.length, 1); + assert.equal(results[0].className, 'SchemaTest2'); + done(); + }); + }); + + it('save index', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + const index = { + name: 1 + }; + testSchema.addString('name'); + testSchema.addIndex('test_index', index); + testSchema.save().then((result) => { + assert.notEqual(result.indexes.test_index, undefined); + done(); + }); + }); + + it('update index', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + testSchema.save().then((result) => { + const index = { + name: 1 + }; + testSchema.addString('name'); + testSchema.addIndex('test_index', index); + return testSchema.update(); + }).then((result) => { + assert.notEqual(result.indexes.test_index, undefined); + done(); + }); + }); + + it('delete index', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + testSchema.save().then((result) => { + const index = { + name: 1 + }; + testSchema.addString('name'); + testSchema.addIndex('test_index', index); + return testSchema.update(); + }).then((result) => { + assert.notEqual(result.indexes.test_index, undefined); + testSchema.deleteIndex('test_index'); + return testSchema.update(); + }).then((result) => { + assert.equal(result.indexes.test_index, undefined); + done(); + }); + }); + + it('invalid field name', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + try { + testSchema.addField(null); + } catch (e) { + done(); + } + }); + + it('invalid field type', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + try { + testSchema.addField('name', 'UnknownType'); + } catch (e) { + done(); + } + }); + + it('invalid index name', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + try { + testSchema.addIndex(null); + } catch (e) { + done(); + } + }); + + it('invalid index', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + try { + testSchema.addIndex('name', null); + } catch (e) { + done(); + } + }); + + it('invalid pointer name', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + try { + testSchema.addPointer(null); + } catch (e) { + done(); + } + }); + + it('invalid pointer class', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + try { + testSchema.addPointer('name', null); + } catch (e) { + done(); + } + }); + + it('invalid relation name', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + try { + testSchema.addRelation(null); + } catch (e) { + done(); + } + }); + + it('invalid relation class', (done) => { + const testSchema = new Parse.Schema('SchemaTest'); + try { + testSchema.addRelation('name', null); + } catch (e) { + done(); + } + }); + + it('assert class name', (done) => { + const testSchema = new Parse.Schema(); + try { + testSchema.assertClassName(); + } catch (e) { + done(); + } + }); +}); diff --git a/src/CoreManager.js b/src/CoreManager.js index 8dac29644..383f93701 100644 --- a/src/CoreManager.js +++ b/src/CoreManager.js @@ -76,6 +76,13 @@ type RESTController = { request: (method: string, path: string, data: mixed) => ParsePromise; ajax: (method: string, url: string, data: any, headers?: any) => ParsePromise; }; +type SchemaController = { + get: (className: string, options: RequestOptions) => ParsePromise; + delete: (className: string, options: RequestOptions) => ParsePromise; + create: (className: string, params: any, options: RequestOptions) => ParsePromise; + update: (className: string, params: any, options: RequestOptions) => ParsePromise; + send(className: string, method: string, params: any, options: RequestOptions): ParsePromise; +}; type SessionController = { getSession: (token: RequestOptions) => ParsePromise; }; @@ -131,6 +138,7 @@ type Config = { PushController?: PushController, QueryController?: QueryController, RESTController?: RESTController, + SchemaController?: SchemaController, SessionController?: SessionController, StorageController?: StorageController, UserController?: UserController, @@ -285,6 +293,15 @@ module.exports = { return config['RESTController']; }, + setSchemaController(controller: SchemaController) { + requireMethods('SchemaController', ['get', 'create', 'update', 'delete', 'send'], controller); + config['SchemaController'] = controller; + }, + + getSchemaController(): SchemaController { + return config['SchemaController']; + }, + setSessionController(controller: SessionController) { requireMethods('SessionController', ['getSession'], controller); config['SessionController'] = controller; diff --git a/src/Parse.js b/src/Parse.js index 872f4ae19..4cae086bc 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -116,6 +116,7 @@ Parse.Push = require('./Push'); Parse.Query = require('./ParseQuery').default; Parse.Relation = require('./ParseRelation').default; Parse.Role = require('./ParseRole').default; +Parse.Schema = require('./ParseSchema').default; Parse.Session = require('./ParseSession').default; Parse.Storage = require('./Storage'); Parse.User = require('./ParseUser').default; diff --git a/src/ParseSchema.js b/src/ParseSchema.js new file mode 100644 index 000000000..4eb8199d0 --- /dev/null +++ b/src/ParseSchema.js @@ -0,0 +1,445 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + */ + +import CoreManager from './CoreManager'; +import ParsePromise from './ParsePromise'; + +import type { RequestOptions, FullOptions } from './RESTController'; + +const FIELD_TYPES = ['String', 'Number', 'Boolean', 'Date', 'File', 'GeoPoint', 'Array', 'Object', 'Pointer', 'Relation']; + +/** + * A Parse.Schema object is for handling schema data from Parse. + *

All the schemas methods require MasterKey. + * + *

+ * const schema = new Parse.Schema('MyClass');
+ * schema.addString('field');
+ * schema.addIndex('index_name', {'field', 1});
+ * schema.save();
+ * 
+ *

+ * @alias Parse.Schema + */ +class ParseSchema { + className: string; + _fields: { [key: string]: mixed }; + _indexes: { [key: string]: mixed }; + + /** + * @param {String} className Parse Class string. + */ + constructor(className: string) { + if (typeof className === 'string') { + if (className === 'User' && CoreManager.get('PERFORM_USER_REWRITE')) { + this.className = '_User'; + } else { + this.className = className; + } + } + + this._fields = {}; + this._indexes = {}; + } + + /** + * Static method to get all schemas + * + * @param {Object} options A Backbone-style options object. + * Valid options are: + * + * @return {Parse.Promise} A promise that is resolved with the result when + * the query completes. + */ + static all(options: FullOptions) { + options = options || {}; + const controller = CoreManager.getSchemaController(); + + return controller.get('', options) + .then((response) => { + if (response.results.length === 0) { + throw new Error('Schema not found.'); + } + return response.results; + })._thenRunCallbacks(options); + } + + /** + * Get the Schema from Parse + * + * @param {Object} options A Backbone-style options object. + * Valid options are: + * + * @return {Parse.Promise} A promise that is resolved with the result when + * the query completes. + */ + get(options: FullOptions) { + this.assertClassName(); + + options = options || {}; + const controller = CoreManager.getSchemaController(); + + return controller.get(this.className, options) + .then((response) => { + if (!response) { + throw new Error('Schema not found.'); + } + return response; + })._thenRunCallbacks(options); + } + + /** + * Create a new Schema on Parse + * + * @param {Object} options A Backbone-style options object. + * Valid options are: + * + * @return {Parse.Promise} A promise that is resolved with the result when + * the query completes. + */ + save(options: FullOptions) { + this.assertClassName(); + + options = options || {}; + const controller = CoreManager.getSchemaController(); + const params = { + className: this.className, + fields: this._fields, + indexes: this._indexes, + }; + + return controller.create(this.className, params, options) + .then((response) => { + return response; + })._thenRunCallbacks(options); + } + + /** + * Update a Schema on Parse + * + * @param {Object} options A Backbone-style options object. + * Valid options are: + * + * @return {Parse.Promise} A promise that is resolved with the result when + * the query completes. + */ + update(options: FullOptions) { + this.assertClassName(); + + options = options || {}; + const controller = CoreManager.getSchemaController(); + const params = { + className: this.className, + fields: this._fields, + indexes: this._indexes, + }; + + this._fields = {}; + this._indexes = {}; + + return controller.update(this.className, params, options) + .then((response) => { + return response; + })._thenRunCallbacks(options); + } + + /** + * Removing a Schema from Parse + * + * @param {Object} options A Backbone-style options object. + * Valid options are: + * + * @return {Parse.Promise} A promise that is resolved with the result when + * the query completes. + */ + delete(options: FullOptions) { + this.assertClassName(); + + options = options || {}; + const controller = CoreManager.getSchemaController(); + + return controller.delete(this.className, options) + .then((response) => { + return response; + })._thenRunCallbacks(options); + } + + /** + * Assert if ClassName has been filled + * @private + */ + assertClassName() { + if (!this.className) { + throw new Error('You must set a Class Name before making any request.'); + } + } + + /** + * Adding a Field to Create / Update a Schema + * + * @param {String} name Name of the field that will be created on Parse + * @param {String} type TheCan be a (String|Number|Boolean|Date|Parse.File|Parse.GeoPoint|Array|Object|Pointer|Parse.Relation) + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + addField(name: string, type: string) { + type = type || 'String'; + + if (!name) { + throw new Error('field name may not be null.'); + } + if (FIELD_TYPES.indexOf(type) === -1) { + throw new Error(`${type} is not a valid type.`); + } + + this._fields[name] = { type }; + + return this; + } + + /** + * Adding an Index to Create / Update a Schema + * + * @param {String} name Name of the field that will be created on Parse + * @param {String} type Can be a (String|Number|Boolean|Date|Parse.File|Parse.GeoPoint|Array|Object|Pointer|Parse.Relation) + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + addIndex(name: string, index: any) { + if (!name) { + throw new Error('index name may not be null.'); + } + if (!index) { + throw new Error('index may not be null.'); + } + + this._indexes[name] = index; + + return this; + } + + /** + * Adding String Field + * + * @param {String} name Name of the field that will be created on Parse + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + addString(name: string) { + return this.addField(name, 'String'); + } + + /** + * Adding Number Field + * + * @param {String} name Name of the field that will be created on Parse + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + addNumber(name: string) { + return this.addField(name, 'Number'); + } + + /** + * Adding Boolean Field + * + * @param {String} name Name of the field that will be created on Parse + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + addBoolean(name: string) { + return this.addField(name, 'Boolean'); + } + + /** + * Adding Date Field + * + * @param {String} name Name of the field that will be created on Parse + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + addDate(name: string) { + return this.addField(name, 'Date'); + } + + /** + * Adding File Field + * + * @param {String} name Name of the field that will be created on Parse + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + addFile(name: string) { + return this.addField(name, 'File'); + } + + /** + * Adding GeoPoint Field + * + * @param {String} name Name of the field that will be created on Parse + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + addGeoPoint(name: string) { + return this.addField(name, 'GeoPoint'); + } + + /** + * Adding Array Field + * + * @param {String} name Name of the field that will be created on Parse + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + addArray(name: string) { + return this.addField(name, 'Array'); + } + + /** + * Adding Object Field + * + * @param {String} name Name of the field that will be created on Parse + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + addObject(name: string) { + return this.addField(name, 'Object'); + } + + /** + * Adding Pointer Field + * + * @param {String} name Name of the field that will be created on Parse + * @param {String} targetClass Name of the target Pointer Class + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + addPointer(name: string, targetClass: string) { + if (!name) { + throw new Error('field name may not be null.'); + } + if (!targetClass) { + throw new Error('You need to set the targetClass of the Pointer.'); + } + + this._fields[name] = { + type: 'Pointer', + targetClass + }; + + return this; + } + + /** + * Adding Relation Field + * + * @param {String} name Name of the field that will be created on Parse + * @param {String} targetClass Name of the target Pointer Class + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + addRelation(name: string, targetClass: string) { + if (!name) { + throw new Error('field name may not be null.'); + } + if (!targetClass) { + throw new Error('You need to set the targetClass of the Relation.'); + } + + this._fields[name] = { + type: 'Relation', + targetClass + }; + + return this; + } + + /** + * Deleting a Field to Update on a Schema + * + * @param {String} name Name of the field that will be created on Parse + * @param {String} targetClass Name of the target Pointer Class + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + deleteField(name: string) { + this._fields[name] = { __op: 'Delete'}; + } + + /** + * Deleting an Index to Update on a Schema + * + * @param {String} name Name of the field that will be created on Parse + * @param {String} targetClass Name of the target Pointer Class + * @return {Parse.Schema} Returns the schema, so you can chain this call. + */ + deleteIndex(name: string) { + this._indexes[name] = { __op: 'Delete'}; + } +} + +const DefaultController = { + send(className: string, method: string, params: any, options: RequestOptions): ParsePromise { + const RESTController = CoreManager.getRESTController(); + const requestOptions = { useMasterKey: true }; + if (options.hasOwnProperty('sessionToken')) { + requestOptions.sessionToken = options.sessionToken; + } + return RESTController.request( + method, + `schemas/${className}`, + params, + requestOptions + ); + }, + + get(className: string, options: RequestOptions): ParsePromise { + return this.send(className, 'GET', {}, options); + }, + + create(className: string, params: any, options: RequestOptions): ParsePromise { + return this.send(className, 'POST', params, options); + }, + + update(className: string, params: any, options: RequestOptions): ParsePromise { + return this.send(className, 'PUT', params, options); + }, + + delete(className: string, options: RequestOptions): ParsePromise { + return this.send(className, 'DELETE', {}, options); + } +}; + +CoreManager.setSchemaController(DefaultController); + +export default ParseSchema; diff --git a/src/__tests__/CoreManager-test.js b/src/__tests__/CoreManager-test.js index 5429907e6..88be5cbd1 100644 --- a/src/__tests__/CoreManager-test.js +++ b/src/__tests__/CoreManager-test.js @@ -315,4 +315,31 @@ describe('CoreManager', () => { CoreManager.setStorageController(controller); expect(CoreManager.getStorageController()).toBe(controller); }); + + it('requires SchemaController to implement certain functionality', () => { + expect(CoreManager.setSchemaController.bind(null, {})).toThrow( + 'SchemaController must implement get()' + ); + + expect(CoreManager.setSchemaController.bind(null, { + send: function() {}, + get: function() {}, + create: function() {}, + update: function() {}, + delete: function() {} + })).not.toThrow(); + }); + + it('can set and get SchemaController', () => { + var controller = { + send: function() {}, + get: function() {}, + create: function() {}, + update: function() {}, + delete: function() {} + }; + + CoreManager.setSchemaController(controller); + expect(CoreManager.getSchemaController()).toBe(controller); + }); }); diff --git a/src/__tests__/ParseSchema-test.js b/src/__tests__/ParseSchema-test.js new file mode 100644 index 000000000..c99311caa --- /dev/null +++ b/src/__tests__/ParseSchema-test.js @@ -0,0 +1,432 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +jest.autoMockOff(); + +var ParseSchema = require('../ParseSchema').default; +var ParsePromise = require('../ParsePromise').default; +var CoreManager = require('../CoreManager'); + +function generateSaveMock(prefix) { + return function(name, payload) { + return ParsePromise.as({ + name: name, + url: prefix + name + }); + }; +} + +var defaultController = CoreManager.getSchemaController(); + +describe('ParseSchema', () => { + it('can create schema', (done) => { + var schema = new ParseSchema('SchemaTest'); + expect(schema.className, 'SchemaTest'); + done(); + }); + + it('can create schema with User Class', (done) => { + var schema = new ParseSchema('User'); + expect(schema.className, '_User'); + done(); + }); + + it('cannot use schema without class', (done) => { + try { + var schema = new ParseSchema(); + schema.assertClassName(); + } catch (e) { + done(); + } + }); + + it('can create schema fields', (done) => { + var schema = new ParseSchema('SchemaTest'); + schema + .addField('defaultFieldString') + .addString('stringField') + .addNumber('numberField') + .addBoolean('booleanField') + .addDate('dateField') + .addFile('fileField') + .addGeoPoint('geoPointField') + .addArray('arrayField') + .addObject('objectField') + .addPointer('pointerField', '_User') + .addRelation('relationField', '_User'); + + expect(schema._fields.defaultFieldString.type, 'String'); + expect(schema._fields.stringField.type, 'String'); + expect(schema._fields.numberField.type, 'Number'); + expect(schema._fields.booleanField.type, 'Boolean'); + expect(schema._fields.dateField.type, 'Date'); + expect(schema._fields.fileField.type, 'File'); + expect(schema._fields.geoPointField.type, 'GeoPoint'); + expect(schema._fields.arrayField.type, 'Array'); + expect(schema._fields.objectField.type, 'Object'); + expect(schema._fields.pointerField.type, 'Pointer'); + expect(schema._fields.relationField.type, 'Relation'); + expect(schema._fields.pointerField.targetClass, '_User'); + expect(schema._fields.relationField.targetClass, '_User'); + done(); + }); + + it('can create schema indexes', (done) => { + var schema = new ParseSchema('SchemaTest'); + schema.addIndex('testIndex', { name: 1 }); + + expect(schema._indexes.name, 1); + done(); + }); + + it('cannot add field with null name', (done) => { + try { + var schema = new ParseSchema('SchemaTest'); + schema.addField(null, 'string'); + } catch (e) { + done(); + } + }); + + it('cannot add field with invalid type', (done) => { + try { + var schema = new ParseSchema('SchemaTest'); + schema.addField('testField', 'unknown'); + } catch (e) { + done(); + } + }); + + it('cannot add index with null name', (done) => { + try { + var schema = new ParseSchema('SchemaTest'); + schema.addIndex(null, {'name': 1}); + } catch (e) { + done(); + } + }); + + it('cannot add index with null index', (done) => { + try { + var schema = new ParseSchema('SchemaTest'); + schema.addIndex('testIndex', null); + } catch (e) { + done(); + } + }); + + it('cannot add pointer with null name', (done) => { + try { + var schema = new ParseSchema('SchemaTest'); + schema.addPointer(null, 'targetClass'); + } catch (e) { + done(); + } + }); + + it('cannot add pointer with null targetClass', (done) => { + try { + var schema = new ParseSchema('SchemaTest'); + schema.addPointer('pointerField', null); + } catch (e) { + done(); + } + }); + + it('cannot add relation with null name', (done) => { + try { + var schema = new ParseSchema('SchemaTest'); + schema.addRelation(null, 'targetClass'); + } catch (e) { + done(); + } + }); + + it('cannot add relation with null targetClass', (done) => { + try { + var schema = new ParseSchema('SchemaTest'); + schema.addRelation('relationField', null); + } catch (e) { + done(); + } + }); + + it('can delete schema field', (done) => { + var schema = new ParseSchema('SchemaTest'); + schema.deleteField('testField'); + expect(schema._fields.testField._op, 'Delete'); + done(); + }); + + it('can delete schema index', (done) => { + var schema = new ParseSchema('SchemaTest'); + schema.deleteIndex('testIndex'); + expect(schema._indexes.testIndex._op, 'Delete'); + done(); + }); + + // CoreManager.setSchemaController({ + // send() {}, + // get() {}, + // create() {}, + // update() {}, + // delete() {}, + // }); + it('can save schema', (done) => { + CoreManager.setSchemaController({ + send() {}, + get() {}, + update() {}, + delete() {}, + create(className, params, options) { + expect(className).toBe('SchemaTest'); + expect(params).toEqual({ + className: 'SchemaTest', + fields: { name: { type: 'String'} }, + indexes: { testIndex: { name: 1 } } + }); + expect(options).toEqual({}); + return ParsePromise.as([]); + }, + }); + + var schema = new ParseSchema('SchemaTest'); + schema.addField('name'); + schema.addIndex('testIndex', {'name': 1}); + schema.save().then((results) => { + expect(results).toEqual([]); + done(); + }); + }); + + it('can update schema', (done) => { + CoreManager.setSchemaController({ + send() {}, + get() {}, + create() {}, + delete() {}, + update(className, params, options) { + expect(className).toBe('SchemaTest'); + expect(params).toEqual({ + className: 'SchemaTest', + fields: { name: { type: 'String'} }, + indexes: { testIndex: { name: 1 } } + }); + expect(options).toEqual({}); + return ParsePromise.as([]); + }, + }); + + var schema = new ParseSchema('SchemaTest'); + schema.addField('name'); + schema.addIndex('testIndex', {'name': 1}); + schema.update().then((results) => { + expect(results).toEqual([]); + done(); + }); + }); + + it('can delete schema', (done) => { + CoreManager.setSchemaController({ + send() {}, + create() {}, + update() {}, + get() {}, + delete(className, options) { + expect(className).toBe('SchemaTest'); + expect(options).toEqual({}); + return ParsePromise.as([]); + }, + }); + + var schema = new ParseSchema('SchemaTest'); + schema.delete().then((results) => { + expect(results).toEqual([]); + done(); + }); + }); + + it('can get schema', (done) => { + CoreManager.setSchemaController({ + send() {}, + create() {}, + update() {}, + delete() {}, + get(className, options) { + expect(className).toBe('SchemaTest'); + expect(options).toEqual({}); + return ParsePromise.as([]); + }, + }); + + var schema = new ParseSchema('SchemaTest'); + schema.get().then((results) => { + expect(results).toEqual([]); + done(); + }); + }); + + it('can get schema with options', (done) => { + CoreManager.setSchemaController({ + send() {}, + create() {}, + update() {}, + delete() {}, + get(className, options) { + expect(className).toBe('SchemaTest'); + expect(options).toEqual({ sessionToken: 1234 }); + return ParsePromise.as([]); + }, + }); + + var schema = new ParseSchema('SchemaTest'); + schema.get({ sessionToken: 1234 }).then((results) => { + expect(results).toEqual([]); + done(); + }); + }); + + it('cannot get empty schema', (done) => { + CoreManager.setSchemaController({ + send() {}, + create() {}, + update() {}, + delete() {}, + get(className, options) { + expect(className).toBe('SchemaTest'); + expect(options).toEqual({}); + return ParsePromise.as(null); + }, + }); + + var schema = new ParseSchema('SchemaTest'); + schema.get().then(() => { + // Should never reach + expect(true).toBe(false); + done(); + }, (error) => { + expect(error.message).toBe('Schema not found.'); + done(); + }); + }); + + it('can get all schema', (done) => { + CoreManager.setSchemaController({ + send() {}, + create() {}, + update() {}, + delete() {}, + get(className, options) { + expect(className).toBe(''); + expect(options).toEqual({}); + return ParsePromise.as({ + results: ['all'] + }); + }, + }); + + ParseSchema.all().then((results) => { + expect(results[0]).toEqual('all'); + done(); + }); + }); + + it('can get all schema with options', (done) => { + CoreManager.setSchemaController({ + send() {}, + create() {}, + update() {}, + delete() {}, + get(className, options) { + expect(className).toBe(''); + expect(options).toEqual({ sessionToken: 1234 }); + return ParsePromise.as({ + results: ['all'] + }); + }, + }); + + ParseSchema.all({ sessionToken: 1234 }).then((results) => { + expect(results[0]).toEqual('all'); + done(); + }); + }); + + it('cannot get all schema when empty', (done) => { + CoreManager.setSchemaController({ + send() {}, + create() {}, + update() {}, + delete() {}, + get(className, options) { + expect(className).toBe(''); + expect(options).toEqual({}); + return ParsePromise.as({ + results: [] + }); + }, + }); + + ParseSchema.all().then(() => { + // Should never reach + expect(true).toBe(false); + done(); + }, (error) => { + expect(error.message).toBe('Schema not found.'); + done(); + }); + }); +}); + +describe('SchemaController', () => { + beforeEach(() => { + CoreManager.setSchemaController(defaultController); + var request = function(method, path, data, options) { + var name = path.substr(path.indexOf('/') + 1); + return ParsePromise.as([]); + }; + var ajax = function(method, path, data, headers) { + var name = path.substr(path.indexOf('/') + 1); + return ParsePromise.as([]); + }; + CoreManager.setRESTController({ request: request, ajax: ajax }); + }); + + it('save schema with sessionToken', (done) => { + var schema = new ParseSchema('SchemaTest'); + schema.save({ sessionToken: 1234 }).then((results) => { + expect(results).toEqual([]); + done(); + }); + }); + + it('get schema', (done) => { + var schema = new ParseSchema('SchemaTest'); + schema.get().then((results) => { + expect(results).toEqual([]); + done(); + }); + }); + + it('update schema', (done) => { + var schema = new ParseSchema('SchemaTest'); + schema.update().then((results) => { + expect(results).toEqual([]); + done(); + }); + }); + + it('delete schema', (done) => { + var schema = new ParseSchema('SchemaTest'); + schema.delete().then((results) => { + expect(results).toEqual([]); + done(); + }); + }); +});