UNPKG

kinvey-flex-sdk

Version:

SDK for creating Kinvey Flex Services

230 lines (186 loc) 8.28 kB
/** * 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 BaseStore = require('./baseStore'); const async = require('async'); const BASE_ROUTE = 'appdata'; const SAVE_BATCH_SIZE = 100; // Initialize the DataStore; initializes to current User Context, unless useMasterSecret = true function initDataStore(appMetadata, requestContext, taskMetadata) { class DataStore extends BaseStore { _buildAppdataRequest(collectionName, options) { return this._buildKinveyRequest(BASE_ROUTE, collectionName, options); } _makeAppdataRequest(requestOptions, collectionName, callback) { if (this._taskMetadata.objectName === collectionName && (this._useBl === true || this._useUserContext === true)) { const error = new Error('DataStoreError'); error.description = 'Not Allowed'; error.debug = 'Recursive data operations to the same collection cannot use user context or use BL.'; return callback ? setImmediate(() => callback(error)) : Promise.reject(error); } return this._makeRequest(requestOptions, callback); } _makeAppdataBatchRequest(requestOptions, entities, collectionName, callback) { if (this._taskMetadata.objectName === collectionName && (this._useBl === true || this._useUserContext === true)) { const error = new Error('DataStoreError'); error.description = 'Not Allowed'; error.debug = 'Recursive data operations to the same collection cannot use user context or use BL.'; return callback ? setImmediate(() => callback(error)) : Promise.reject(error); } // send multi-insert request in batches const entitiesInBatches = []; let start = 0; while (start < entities.length) { entitiesInBatches.push(entities.slice(start, start + SAVE_BATCH_SIZE)); start += SAVE_BATCH_SIZE; } const totalEntities = []; const totalErrors = []; return new Promise((resolve, reject) => { async.eachOfSeries(entitiesInBatches, (batch, i, cb) => { requestOptions.body = batch; this._makeRequest(requestOptions, (err, resBody) => { if (err) return cb(err); totalEntities.push(...resBody.entities); // change batch-specific index to absolute index resBody.errors.forEach((e) => { e.index = (SAVE_BATCH_SIZE * i) + e.index; }); totalErrors.push(...resBody.errors); cb(); }); }, (err) => { if (err) { return callback ? callback(err) : reject(err); } // form the result const resBody = { entities: totalEntities, errors: totalErrors }; return callback ? callback(null, resBody) : resolve(resBody); }); }); } collection(collectionName) { const _throwEntityRequiredError = (argName, arg, callback) => { const cb = typeof arg === 'function' ? arg : callback; const err = new Error('DataStoreError'); err.description = 'Bad Request'; err.debug = `${argName} is required`; return cb ? setImmediate(() => cb(err)) : Promise.reject(err); }; const create = (entity, callback) => { if (!entity || typeof entity === 'function') { return _throwEntityRequiredError('entity', entity, callback); } const requestOptions = this._buildAppdataRequest(collectionName); requestOptions.method = 'POST'; if (Array.isArray(entity) && entity.length > SAVE_BATCH_SIZE) { return this._makeAppdataBatchRequest(requestOptions, entity, collectionName, callback); } requestOptions.body = entity; return this._makeAppdataRequest(requestOptions, collectionName, callback); }; const update = (entity, callback) => { if (!entity || typeof entity === 'function') { return _throwEntityRequiredError('entity', entity, callback); } const requestOptions = this._buildAppdataRequest(collectionName); requestOptions.method = 'PUT'; requestOptions.url += entity._id; requestOptions.body = entity; return this._makeAppdataRequest(requestOptions, collectionName, callback); }; const save = (entity, callback) => { if (!entity || typeof entity === 'function') { return _throwEntityRequiredError('entity', entity, callback); } if (Array.isArray(entity)) { const err = new Error('DataStoreError'); err.description = 'Unable to save an array of entities. To insert multiple entities use "create" function.'; return callback ? setImmediate(() => callback(err)) : Promise.reject(err); } if (entity._id) { return update(entity, callback); } return create(entity, callback); }; const findById = (entityId, callback) => { if (!entityId || typeof entityId === 'function') { return _throwEntityRequiredError('entityId', entityId, callback); } const requestOptions = this._buildAppdataRequest(collectionName); requestOptions.method = 'GET'; requestOptions.url += entityId; return this._makeAppdataRequest(requestOptions, collectionName, callback); }; const find = (query, callback) => { const requestOptions = this._buildAppdataRequest(collectionName); const cb = !callback && typeof query === 'function' ? query : callback; requestOptions.method = 'GET'; if (query && typeof query !== 'function') { requestOptions.qs = this._generateQueryString(query); } return this._makeAppdataRequest(requestOptions, collectionName, cb); }; const removeById = (entityId, callback) => { if (!entityId || typeof entityId === 'function') { return _throwEntityRequiredError('entityId', entityId, callback); } const requestOptions = this._buildAppdataRequest(collectionName); requestOptions.method = 'DELETE'; requestOptions.url += entityId; return this._makeAppdataRequest(requestOptions, collectionName, callback); }; const remove = (query, callback) => { const requestOptions = this._buildAppdataRequest(collectionName); const cb = !callback && typeof query === 'function' ? query : callback; requestOptions.method = 'DELETE'; if (query && typeof query !== 'function') { requestOptions.qs = this._generateQueryString(query); } return this._makeAppdataRequest(requestOptions, collectionName, cb); }; const count = (query, callback) => { const requestOptions = this._buildAppdataRequest(collectionName); const cb = !callback && typeof query === 'function' ? query : callback; requestOptions.method = 'GET'; requestOptions.url += '_count/'; if (query && typeof query !== 'function') { requestOptions.qs = this._generateQueryString(query); } return this._makeAppdataRequest(requestOptions, collectionName, cb); }; return { create, save, findById, find, removeById, remove, count, collectionName, _useUserContext: this._useUserContext, _useBl: this._useBl, _appMetadata: this._appMetadata, _requestContext: this._requestContext }; } } function generateDataStore(storeOptions = {}) { return new DataStore(storeOptions, appMetadata, requestContext, taskMetadata); } return generateDataStore; } module.exports = initDataStore;