kinvey-flex-sdk
Version:
SDK for creating Kinvey Flex Services
1,342 lines (1,194 loc) • 75.1 kB
JavaScript
/**
* Copyright (c) 2018 Kinvey Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
const nock = require('nock');
const should = require('should');
const dataStore = require('../../../../lib/service/modules/dataStore');
const Query = require('../../../../lib/service/modules/query');
const uuid = require('uuid');
const environmentId = 'kid1234';
const blFlags = {};
const appsecret = '123456';
const mastersecret = '789012';
const authenticatedUsername = 'test@test.com';
const baasUrl = 'https://baas.kinvey.com';
const authenticatedUserId = '1234abcd';
const apiVersion = 5;
const authorization = 'Kinvey adfjkldsafjdsalkfjds90fd8sfd=';
const clientAppVersion = {};
const customRequestProperties = {};
function _generateAppMetadata() {
return {
_id: environmentId,
blFlags,
appsecret,
mastersecret,
authenticatedUsername,
baasUrl
};
}
function _generateRequestContext() {
return {
authenticatedUsername,
authenticatedUserId,
apiVersion,
authorization,
clientAppVersion,
customRequestProperties
};
}
function _generateTaskMetadata() {
return {
taskType: 'data',
objectName: 'someObject',
hookType: undefined,
target: undefined,
taskId: uuid.v4(),
containerId: uuid.v4()
};
}
describe('dataStore', () => {
beforeEach(() => {
this.appMetadata = _generateAppMetadata();
this.requestContext = _generateRequestContext();
this.taskMetadata = _generateTaskMetadata();
});
it('should initialize datastore', () => {
const store = dataStore(this.appMetadata, this.requestContext, this.taskMetadata);
store.should.be.a.Function();
store.name.should.eql('generateDataStore');
});
describe('datastore object', () => {
beforeEach(() => {
this.store = dataStore(this.appMetadata, this.requestContext, this.taskMetadata);
});
afterEach(() => {
this.store = null;
});
it('should create a datastore object', () => {
const myStore = this.store();
should.exist(myStore.collection);
myStore.collection.should.be.a.Function();
myStore.collection.name.should.eql('collection');
myStore._useUserContext.should.be.false();
myStore._useBl.should.be.false();
myStore._appMetadata.should.containDeep(this.appMetadata);
myStore._requestContext.should.containDeep(this.requestContext);
myStore._apiVersion.should.eql(this.requestContext.apiVersion);
});
it('should create a datastore object that uses userContext', () => {
const myStore = this.store({ useUserContext: true });
should.exist(myStore.collection);
myStore.collection.should.be.a.Function();
myStore.collection.name.should.eql('collection');
myStore._useUserContext.should.be.true();
myStore._appMetadata.should.containDeep(this.appMetadata);
myStore._requestContext.should.containDeep(this.requestContext);
});
it('should create a datastore object that uses BL', () => {
const myStore = this.store({ useBl: true });
should.exist(myStore.collection);
myStore.collection.should.be.a.Function();
myStore.collection.name.should.eql('collection');
myStore._useBl.should.be.true();
myStore._appMetadata.should.containDeep(this.appMetadata);
myStore._requestContext.should.containDeep(this.requestContext);
});
it('should create a datastore object that overrides the apiVersion', () => {
const API_VERSION = 4;
const myStore = this.store({ apiVersion: API_VERSION });
should.exist(myStore.collection);
myStore.collection.should.be.a.Function();
myStore.collection.name.should.eql('collection');
myStore._appMetadata.should.containDeep(this.appMetadata);
myStore._requestContext.should.containDeep(this.requestContext);
myStore._apiVersion.should.not.eql(this.requestContext.apiVersion);
myStore._apiVersion.should.eql(API_VERSION);
});
it('should be able to create two datastore objects with different settings', () => {
const myStore = this.store();
const myStore2 = this.store({ useUserContext: true });
should.exist(myStore.collection);
myStore.collection.should.be.a.Function();
myStore.collection.name.should.eql('collection');
myStore._useUserContext.should.be.false();
myStore._appMetadata.should.containDeep(this.appMetadata);
myStore._requestContext.should.containDeep(this.requestContext);
should.exist(myStore2.collection);
myStore2.collection.should.be.a.Function();
myStore2.collection.name.should.eql('collection');
myStore2._useUserContext.should.be.true();
myStore2._appMetadata.should.containDeep(this.appMetadata);
myStore2._requestContext.should.containDeep(this.requestContext);
});
it('should create multiple data stores for two different apps', () => {
const secondAppMetadata = _generateAppMetadata();
secondAppMetadata._id = 'abcd';
const myStore = this.store();
const myStore2 = dataStore(secondAppMetadata, this.requestContext)();
myStore._appMetadata._id.should.eql(this.appMetadata._id);
myStore2._appMetadata._id.should.eql(secondAppMetadata._id);
});
it('should create multiple data stores for two different requests', () => {
const secondRequestContext = _generateRequestContext();
secondRequestContext.authenticatedUserId = 'foo';
const myStore = this.store();
const myStore2 = dataStore(this.appMetadata, secondRequestContext)();
myStore._requestContext.authenticatedUserId.should.eql(this.requestContext.authenticatedUserId);
myStore2._requestContext.authenticatedUserId.should.eql(secondRequestContext.authenticatedUserId);
});
});
describe('collection object', () => {
beforeEach(() => {
this.store = dataStore(this.appMetadata, this.requestContext);
});
afterEach(() => {
this.store = null;
});
it('should create a collection object', () => {
const myCollection = this.store().collection('myCollection');
myCollection.collectionName.should.eql('myCollection');
should.exist(myCollection.save);
should.exist(myCollection.findById);
should.exist(myCollection.find);
should.exist(myCollection.removeById);
should.exist(myCollection.remove);
myCollection.save.should.be.a.Function();
myCollection.findById.should.be.a.Function();
myCollection.find.should.be.a.Function();
myCollection.removeById.should.be.a.Function();
myCollection.remove.should.be.a.Function();
myCollection.save.name.should.eql('save');
myCollection.findById.name.should.eql('findById');
myCollection.find.name.should.eql('find');
myCollection.removeById.name.should.eql('removeById');
myCollection.remove.name.should.eql('remove');
myCollection._useUserContext.should.be.false();
myCollection._appMetadata.should.containDeep(this.appMetadata);
myCollection._requestContext.should.containDeep(this.requestContext);
myCollection._useBl.should.be.false();
});
it('should allow creation of multiple collection objects', () => {
const collection = this.store().collection('myCollection');
const collection2 = this.store().collection('mySecondCollection');
collection.collectionName.should.eql('myCollection');
collection2.collectionName.should.eql('mySecondCollection');
});
});
describe('find', () => {
beforeEach(() => {
this.store = dataStore(this.appMetadata, this.requestContext, this.taskMetadata);
});
afterEach(() => {
nock.cleanAll();
});
it('should return a promise', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.get(`/appdata/${environmentId}/myCollection/`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, [{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
const collection = this.store().collection('myCollection');
(collection.find()).should.be.a.Promise(); // eslint-disable-line new-cap
done();
});
it('should find all records', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.get(`/appdata/${environmentId}/myCollection/`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, [{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
const collection = this.store().collection('myCollection');
collection.find((err, result) => {
should.not.exist(err);
result.should.containDeep([{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
return done();
});
});
it('should resolve with all records if callback isn\'t passed', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.get(`/appdata/${environmentId}/myCollection/`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, [{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
const collection = this.store().collection('myCollection');
collection.find()
.then((result) => {
result.should.containDeep([{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
done();
});
});
it('should find all records using userContext', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.matchHeader('authorization', authorization)
.get(`/appdata/${environmentId}/myCollection/`)
.reply(200, [{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
const collection = this.store({ useUserContext: true }).collection('myCollection');
collection.find((err, result) => {
should.not.exist(err);
result.should.containDeep([{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
return done();
});
});
it('should find all records and use bl', (done) => {
nock('https://baas.kinvey.com', { badheaders: ['x-kinvey-skip-business-logic'] })
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.get(`/appdata/${environmentId}/myCollection/`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, [{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
const collection = this.store({ useBl: true }).collection('myCollection');
collection.find((err, result) => {
should.not.exist(err);
result.should.containDeep([{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
return done();
});
});
it('should find all records with overriden API version', (done) => {
const API_VERSION = 4;
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', `${API_VERSION}`)
.get(`/appdata/${environmentId}/myCollection/`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, [{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
const collection = this.store({ apiVersion: API_VERSION }).collection('myCollection');
collection.find((err, result) => {
should.not.exist(err);
result.should.containDeep([{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
return done();
});
});
it('should prevent recursive requests to the same object that use bl', (done) => {
const collection = this.store({ useBl: true }).collection(this.taskMetadata.objectName);
collection.find((err, result) => {
should.not.exist(result);
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
return done();
});
});
it('should prevent recursive requests to the same object that use user context', (done) => {
const collection = this.store({ useUserContext: true }).collection(this.taskMetadata.objectName);
collection.find((err, result) => {
should.not.exist(result);
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
return done();
});
});
it('should prevent recursive requests to the same object that use user context and bl', (done) => {
const collection = this.store({ useUserContext: true, useBl: true }).collection(this.taskMetadata.objectName);
collection.find((err, result) => {
should.not.exist(result);
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
return done();
});
});
it('should allow recursive requests to the same object that use mastersecret and skip bl', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.get(`/appdata/${environmentId}/${this.taskMetadata.objectName}/`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, [{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
const collection = this.store().collection(this.taskMetadata.objectName);
collection.find((err, result) => {
should.not.exist(err);
result.should.containDeep([{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
return done();
});
});
it('should invoke rejection handler if there is an error and callback isn\'t passed', (done) => {
const collection = this.store({ useBl: true }).collection(this.taskMetadata.objectName);
collection.find()
.catch((err) => {
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
done();
});
});
it('should find records with a query object', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.get(`/appdata/${environmentId}/myCollection/`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.query({ query: '{"foo":"bar"}' })
.reply(200, [{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
const query = new Query();
query.equalTo('foo', 'bar');
const collection = this.store().collection('myCollection');
collection.find(query, (err, result) => {
should.not.exist(err);
result.should.containDeep([{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
done();
});
});
it('should resolve and return records with a query object', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.get(`/appdata/${environmentId}/myCollection/`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.query({ query: '{"foo":"bar"}' })
.reply(200, [{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
const query = new Query();
query.equalTo('foo', 'bar');
const collection = this.store().collection('myCollection');
collection.find(query)
.then((result) => {
result.should.containDeep([{ _id: 123, someData: 'abc' }, { _id: 456, someData: 'xyz' }]);
done();
});
});
});
describe('findById', () => {
beforeEach(() => {
this.store = dataStore(this.appMetadata, this.requestContext, this.taskMetadata);
});
afterEach(() => {
nock.cleanAll();
});
it('should return a promise', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.get(`/appdata/${environmentId}/myCollection/1234`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store().collection('myCollection');
(collection.findById(1234)).should.be.a.Promise(); // eslint-disable-line new-cap
done();
});
it('should find a single entity', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.get(`/appdata/${environmentId}/myCollection/1234`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store().collection('myCollection');
collection.findById(1234, (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should resolve with a single entity if callback isn\'t passed', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.get(`/appdata/${environmentId}/myCollection/1234`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store().collection('myCollection');
collection.findById(1234)
.then((result) => {
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should find a single entity record using user context', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.matchHeader('authorization', authorization)
.get(`/appdata/${environmentId}/myCollection/1234`)
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store({ useUserContext: true }).collection('myCollection');
collection.findById(1234, (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should find a single entity and use bl', (done) => {
nock('https://baas.kinvey.com', { badheaders: ['x-kinvey-skip-business-logic'] })
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.get(`/appdata/${environmentId}/myCollection/1234`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store({ useBl: true }).collection('myCollection');
collection.findById('1234', (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should find a single entity with overriden API version', (done) => {
const API_VERSION = 4;
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', `${API_VERSION}`)
.get(`/appdata/${environmentId}/myCollection/1234`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store({ apiVersion: API_VERSION }).collection('myCollection');
collection.findById('1234', (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should prevent recursive requests to the same object that use bl', (done) => {
const collection = this.store({ useBl: true }).collection(this.taskMetadata.objectName);
collection.findById(1234, (err, result) => {
should.not.exist(result);
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
done();
});
});
it('should prevent recursive requests to the same object that use user context', (done) => {
const collection = this.store({ useUserContext: true }).collection(this.taskMetadata.objectName);
collection.findById(1234, (err, result) => {
should.not.exist(result);
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
done();
});
});
it('should prevent recursive requests to the same object that use user context and bl', (done) => {
const collection = this.store({ useUserContext: true, useBl: true }).collection(this.taskMetadata.objectName);
collection.findById(1234, (err, result) => {
should.not.exist(result);
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
done();
});
});
it('should allow recursive requests to the same object that use mastersecret and skip bl', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.get(`/appdata/${environmentId}/${this.taskMetadata.objectName}/1234`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store().collection(this.taskMetadata.objectName);
collection.findById(1234, (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should return an error if missing entityId', (done) => {
const collection = this.store().collection('myCollection');
collection.findById((err, result) => {
should.not.exist(result);
err.message.should.eql('DataStoreError');
err.description.should.eql('Bad Request');
err.debug.should.eql('entityId is required');
done();
});
});
it('should return an error if null entityId', (done) => {
const collection = this.store().collection('myCollection');
collection.findById(null, (err, result) => {
should.not.exist(result);
err.message.should.eql('DataStoreError');
err.description.should.eql('Bad Request');
err.debug.should.eql('entityId is required');
done();
});
});
it('should return an error if blank entityId', (done) => {
const collection = this.store().collection('myCollection');
collection.findById('', (err, result) => {
should.not.exist(result);
err.message.should.eql('DataStoreError');
err.description.should.eql('Bad Request');
err.debug.should.eql('entityId is required');
done();
});
});
it('should reject if missing entityId and no callback is passed', (done) => {
const collection = this.store().collection('myCollection');
collection.findById()
.catch((err) => {
err.message.should.eql('DataStoreError');
err.description.should.eql('Bad Request');
err.debug.should.eql('entityId is required');
done();
});
});
it('should invoke rejection handler if an error occurs and callback isn\'t passed', (done) => {
const collection = this.store({ useBl: true }).collection(this.taskMetadata.objectName);
collection.findById(1234)
.catch((err) => {
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
done();
});
});
});
describe('save', () => {
beforeEach(() => {
this.store = dataStore(this.appMetadata, this.requestContext, this.taskMetadata);
});
afterEach(() => {
nock.cleanAll();
});
it('should return a promise', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.post(`/appdata/${environmentId}/myCollection/`, {
someData: 'abc'
})
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store().collection('myCollection');
(collection.save({ someData: 'abc' })).should.be.a.Promise(); // eslint-disable-line new-cap
done();
});
it('should save a new entity', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.post(`/appdata/${environmentId}/myCollection/`, {
someData: 'abc'
})
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store().collection('myCollection');
collection.save({ someData: 'abc' }, (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should return an error for multiple entities', (done) => {
const entities = [{
someData: 'abc'
}, {
someData: 'xyz'
}];
const collection = this.store().collection('myCollection');
collection.save(entities, (err, result) => {
err.should.have.property('message', 'DataStoreError');
err.should.have.property('description', 'Unable to save an array of entities. To insert multiple entities use "create" function.');
should.not.exist(result);
done();
});
});
it('should resolve if a callback isn\'t passed', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.post(`/appdata/${environmentId}/myCollection/`, {
someData: 'abc'
})
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store().collection('myCollection');
collection.save({ someData: 'abc' })
.then((result) => {
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should save a new entity using user context', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.matchHeader('authorization', authorization)
.post(`/appdata/${environmentId}/myCollection/`, {
someData: 'abc'
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store({ useUserContext: true }).collection('myCollection');
collection.save({ someData: 'abc' }, (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should save a new entity and use bl', (done) => {
nock('https://baas.kinvey.com', { badheaders: ['x-kinvey-skip-business-logic'] })
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.post(`/appdata/${environmentId}/myCollection/`, {
someData: 'abc'
})
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store({ useBl: true }).collection('myCollection');
collection.save({ someData: 'abc' }, (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should save a new entity with overriden API version', (done) => {
const API_VERSION = 4;
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', `${API_VERSION}`)
.post(`/appdata/${environmentId}/myCollection/`, {
someData: 'abc'
})
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store({ apiVersion: API_VERSION }).collection('myCollection');
collection.save({ someData: 'abc' }, (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should prevent recursive requests to the same object that use bl', (done) => {
const collection = this.store({ useBl: true }).collection(this.taskMetadata.objectName);
collection.save({ someData: 'abc' }, (err, result) => {
should.not.exist(result);
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
done();
});
});
it('should prevent recursive requests to the same object that use user context', (done) => {
const collection = this.store({ useUserContext: true }).collection(this.taskMetadata.objectName);
collection.save({ someData: 'abc' }, (err, result) => {
should.not.exist(result);
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
done();
});
});
it('should prevent recursive requests to the same object that use user context and bl', (done) => {
const collection = this.store({ useUserContext: true, useBl: true }).collection(this.taskMetadata.objectName);
collection.save({ someData: 'abc' }, (err, result) => {
should.not.exist(result);
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
done();
});
});
it('should allow recursive requests to the same object that use mastersecret and skip bl', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.post(`/appdata/${environmentId}/${this.taskMetadata.objectName}/`, {
someData: 'abc'
})
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store().collection(this.taskMetadata.objectName);
collection.save({ someData: 'abc' }, (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should return an error if missing entity', (done) => {
const collection = this.store().collection('myCollection');
collection.save((err, result) => {
should.not.exist(result);
err.message.should.eql('DataStoreError');
err.description.should.eql('Bad Request');
err.debug.should.eql('entity is required');
done();
});
});
it('should return an error if null entity', (done) => {
const collection = this.store().collection('myCollection');
collection.save(null, (err, result) => {
should.not.exist(result);
err.message.should.eql('DataStoreError');
err.description.should.eql('Bad Request');
err.debug.should.eql('entity is required');
done();
});
});
it('should return an error if blank entityId', (done) => {
const collection = this.store().collection('myCollection');
collection.save('', (err, result) => {
should.not.exist(result);
err.message.should.eql('DataStoreError');
err.description.should.eql('Bad Request');
err.debug.should.eql('entity is required');
done();
});
});
it('should reject if missing entityId and no callback is passed', (done) => {
const collection = this.store().collection('myCollection');
collection.save()
.catch((err) => {
err.message.should.eql('DataStoreError');
err.description.should.eql('Bad Request');
err.debug.should.eql('entity is required');
done();
});
});
it('should invoke the rejection handler if an error occurs and no callback is passed', (done) => {
const collection = this.store({ useBl: true }).collection(this.taskMetadata.objectName);
collection.save({ someData: 'abc' })
.catch((err) => {
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
done();
});
});
it('should save an existing entity', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.put(`/appdata/${environmentId}/myCollection/1234`, {
_id: 1234,
someData: 'abc'
})
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store().collection('myCollection');
collection.save({ _id: 1234, someData: 'abc' }, (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should save an existing entity using user context', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.matchHeader('authorization', authorization)
.put(`/appdata/${environmentId}/myCollection/1234`, {
_id: 1234,
someData: 'abc'
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store({ useUserContext: true }).collection('myCollection');
collection.save({ _id: 1234, someData: 'abc' }, (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should save an existing entity and use bl', (done) => {
nock('https://baas.kinvey.com', { badheaders: ['x-kinvey-skip-business-logic'] })
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.put(`/appdata/${environmentId}/myCollection/1234`, {
_id: 1234,
someData: 'abc'
})
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store({ useBl: true }).collection('myCollection');
collection.save({ _id: 1234, someData: 'abc' }, (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should save an existing entity with overriden API version', (done) => {
const API_VERSION = 4;
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', `${API_VERSION}`)
.put(`/appdata/${environmentId}/myCollection/1234`, {
_id: 1234,
someData: 'abc'
})
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store({ apiVersion: API_VERSION }).collection('myCollection');
collection.save({ _id: 1234, someData: 'abc' }, (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
it('should prevent recursive requests to the same object that use bl', (done) => {
const collection = this.store({ useBl: true }).collection(this.taskMetadata.objectName);
collection.save({ _id: 1234, someData: 'abc' }, (err, result) => {
should.not.exist(result);
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
done();
});
});
it('should prevent recursive requests to the same object that use user context', (done) => {
const collection = this.store({ useUserContext: true }).collection(this.taskMetadata.objectName);
collection.save({ _id: 1234, someData: 'abc' }, (err, result) => {
should.not.exist(result);
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
done();
});
});
it('should prevent recursive requests to the same object that use user context and bl', (done) => {
const collection = this.store({ useUserContext: true, useBl: true }).collection(this.taskMetadata.objectName);
collection.save({ _id: 1234, someData: 'abc' }, (err, result) => {
should.not.exist(result);
should.exist(err);
err.message.should.eql('DataStoreError');
err.description.should.eql('Not Allowed');
err.debug.should.eql('Recursive data operations to the same collection cannot use user context or use BL.');
done();
});
});
it('should allow recursive requests to the same object that use mastersecret and skip bl', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.put(`/appdata/${environmentId}/${this.taskMetadata.objectName}/1234`, {
_id: 1234,
someData: 'abc'
})
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { _id: 1234, someData: 'abc' });
const collection = this.store().collection(this.taskMetadata.objectName);
collection.save({ _id: 1234, someData: 'abc' }, (err, result) => {
should.not.exist(err);
result.should.containDeep({ _id: 1234, someData: 'abc' });
done();
});
});
});
describe('create', () => {
const entities = [];
before('entities', () => {
// create 250 entities
for (let i = 0; i < 250; i += 1) {
entities.push({ index: i, name: uuid.v4() });
}
});
beforeEach(() => {
this.store = dataStore(this.appMetadata, this.requestContext, this.taskMetadata);
});
afterEach(() => {
nock.cleanAll();
});
it('should insert entities in batches of 100', (done) => {
const firstBatch = entities.slice(0, 100);
const secondBatch = entities.slice(100, 200);
const thirdBatch = entities.slice(200, 250);
const firstBatchResponse = {
entities: firstBatch,
errors: []
};
const secondBatchResponse = {
entities: secondBatch,
errors: []
};
const thirdBatchResponse = {
entities: thirdBatch,
errors: []
};
const expectedFinalResponse = {
entities,
errors: []
};
const scope = nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.post(`/appdata/${environmentId}/myCollection/`, firstBatch)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(207, firstBatchResponse)
.post(`/appdata/${environmentId}/myCollection/`, secondBatch)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(207, secondBatchResponse)
.post(`/appdata/${environmentId}/myCollection/`, thirdBatch)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(207, thirdBatchResponse);
const collection = this.store().collection('myCollection');
collection.create(entities, (err, result) => {
should.not.exist(err);
result.should.have.keys('entities', 'errors');
result.should.deepEqual(expectedFinalResponse);
scope.isDone().should.be.true;
done();
});
});
it('should resolve if a callback is not passed', (done) => {
const firstBatch = entities.slice(0, 100);
const secondBatch = entities.slice(100, 200);
const thirdBatch = entities.slice(200, 250);
const firstBatchResponse = {
entities: firstBatch,
errors: []
};
const secondBatchResponse = {
entities: secondBatch,
errors: []
};
const thirdBatchResponse = {
entities: thirdBatch,
errors: []
};
const expectedFinalResponse = {
entities,
errors: []
};
const scope = nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.post(`/appdata/${environmentId}/myCollection/`, firstBatch)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(207, firstBatchResponse)
.post(`/appdata/${environmentId}/myCollection/`, secondBatch)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(207, secondBatchResponse)
.post(`/appdata/${environmentId}/myCollection/`, thirdBatch)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(207, thirdBatchResponse);
const collection = this.store().collection('myCollection');
collection.create(entities)
.then((result) => {
result.should.deepEqual(expectedFinalResponse);
scope.isDone().should.be.true;
done();
});
});
it('should set error index correctly when saving in batches', (done) => {
const firstBatch = entities.slice(0, 100);
const secondBatch = entities.slice(100, 200);
const thirdBatch = entities.slice(200, 250);
// first batch has one failure: entity with index = 5
const firstBatchResponse = {
entities: firstBatch,
errors: [{ index: 5 }]
};
// second batch has one failure too
const secondBatchResponse = {
entities: secondBatch,
errors: [{ index: 6 }]
};
// third batch has 2 failures
const thirdBatchResponse = {
entities: thirdBatch,
errors: [{ index: 25 }, { index: 40 }]
};
// final response should have index matching original request
const expectedFinalResponse = {
entities,
errors: [{ index: 5 }, { index: 106 }, { index: 225 }, { index: 240 }]
};
const scope = nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.post(`/appdata/${environmentId}/myCollection/`, firstBatch)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(207, firstBatchResponse)
.post(`/appdata/${environmentId}/myCollection/`, secondBatch)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(207, secondBatchResponse)
.post(`/appdata/${environmentId}/myCollection/`, thirdBatch)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(207, thirdBatchResponse);
const collection = this.store().collection('myCollection');
collection.create(entities, (err, result) => {
should.not.exist(err);
result.should.have.keys('entities', 'errors');
result.entities.should.eql(entities);
result.errors.should.eql(expectedFinalResponse.errors);
scope.isDone().should.be.true;
done();
});
});
});
describe('remove', () => {
beforeEach(() => {
this.store = dataStore(this.appMetadata, this.requestContext, this.taskMetadata);
});
afterEach(() => {
nock.cleanAll();
});
it('should return a Promise', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.delete(`/appdata/${environmentId}/myCollection/`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { count: 30 });
const collection = this.store().collection('myCollection');
(collection.remove()).should.be.a.Promise(); // eslint-disable-line new-cap
done();
});
it('should remove all records', (done) => {
nock('https://baas.kinvey.com')
.matchHeader('content-type', 'application/json')
.matchHeader('x-kinvey-api-version', '5')
.matchHeader('x-kinvey-skip-business-logic', 'true')
.delete(`/appdata/${environmentId}/myCollection/`)
.basicAuth({
user: environmentId,
pass: mastersecret
})
.reply(200, { count: 30 });
const collection = this.store().collection('myCollection');
collection.remove((err, result) => {
should.not.exist(err);
result.should.containDeep({ count: 30 });
done();
});
});