modules/backend/services/caches.js (110 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ 'use strict'; const _ = require('lodash'); // Fire me up! module.exports = { implements: 'services/caches', inject: ['mongo', 'services/spaces', 'errors'] }; /** * @param mongo * @param {SpacesService} spacesService * @param errors * @returns {CachesService} */ module.exports.factory = (mongo, spacesService, errors) => { /** * Convert remove status operation to own presentation. * * @param {RemoveResult} result - The results of remove operation. */ const convertRemoveStatus = (result) => ({rowsAffected: result.n}); /** * Update existing cache. * * @param {Object} cache - The cache for updating. * @returns {Promise.<mongo.ObjectId>} that resolves cache id. */ const update = (cache) => { const cacheId = cache._id; return mongo.Cache.updateOne({_id: cacheId}, cache, {upsert: true}).exec() .then(() => mongo.Cluster.updateMany({_id: {$in: cache.clusters}}, {$addToSet: {caches: cacheId}}).exec()) .then(() => mongo.Cluster.updateMany({_id: {$nin: cache.clusters}}, {$pull: {caches: cacheId}}).exec()) .then(() => mongo.DomainModel.updateMany({_id: {$in: cache.domains}}, {$addToSet: {caches: cacheId}}).exec()) .then(() => mongo.DomainModel.updateMany({_id: {$nin: cache.domains}}, {$pull: {caches: cacheId}}).exec()) .then(() => cache) .catch((err) => { if (err.code === mongo.errCodes.DUPLICATE_KEY_UPDATE_ERROR || err.code === mongo.errCodes.DUPLICATE_KEY_ERROR) throw new errors.DuplicateKeyException('Cache with name: "' + cache.name + '" already exist.'); else throw err; }); }; /** * Create new cache. * * @param {Object} cache - The cache for creation. * @returns {Promise.<mongo.ObjectId>} that resolves cache id. */ const create = (cache) => { return mongo.Cache.create(cache) .then((savedCache) => mongo.Cluster.updateMany({_id: {$in: savedCache.clusters}}, {$addToSet: {caches: savedCache._id}}).exec() .then(() => mongo.DomainModel.updateMany({_id: {$in: savedCache.domains}}, {$addToSet: {caches: savedCache._id}}).exec()) .then(() => savedCache) ) .catch((err) => { if (err.code === mongo.errCodes.DUPLICATE_KEY_ERROR) throw new errors.DuplicateKeyException('Cache with name: "' + cache.name + '" already exist.'); else throw err; }); }; /** * Remove all caches by space ids. * * @param {Number[]} spaceIds - The space ids for cache deletion. * @returns {Promise.<RemoveResult>} - that resolves results of remove operation. */ const removeAllBySpaces = (spaceIds) => { return mongo.Cluster.updateMany({space: {$in: spaceIds}}, {caches: []}).exec() .then(() => mongo.Cluster.updateMany({space: {$in: spaceIds}}, {$pull: {checkpointSpi: {kind: 'Cache'}}}).exec()) .then(() => mongo.DomainModel.updateMany({space: {$in: spaceIds}}, {caches: []}).exec()) .then(() => mongo.Cache.deleteMany({space: {$in: spaceIds}}).exec()); }; /** * Service for manipulate Cache entities. */ class CachesService { static shortList(userId, demo, clusterId) { return spacesService.spaceIds(userId, demo) .then((spaceIds) => mongo.Cache.find({space: {$in: spaceIds}, clusters: clusterId }).select('name cacheMode atomicityMode backups').lean().exec()); } static get(userId, demo, _id) { return spacesService.spaceIds(userId, demo) .then((spaceIds) => mongo.Cache.findOne({space: {$in: spaceIds}, _id}).lean().exec()); } static upsertBasic(cache) { if (_.isNil(cache._id)) return Promise.reject(new errors.IllegalArgumentException('Cache id can not be undefined or null')); const query = _.pick(cache, ['space', '_id']); const newDoc = _.pick(cache, ['space', '_id', 'name', 'cacheMode', 'atomicityMode', 'backups', 'clusters']); return mongo.Cache.updateOne(query, {$set: newDoc}, {upsert: true}).exec() .catch((err) => { if (err.code === mongo.errCodes.DUPLICATE_KEY_ERROR) throw new errors.DuplicateKeyException(`Cache with name: "${cache.name}" already exist.`); throw err; }) .then((updated) => { if (updated.nModified === 0) return mongo.Cache.updateOne(query, {$set: cache}, {upsert: true}).exec(); return updated; }); } static upsert(cache) { if (_.isNil(cache._id)) return Promise.reject(new errors.IllegalArgumentException('Cache id can not be undefined or null')); const query = _.pick(cache, ['space', '_id']); return mongo.Cache.updateOne(query, {$set: cache}, {upsert: true}).exec() .then(() => mongo.DomainModel.updateMany({_id: {$in: cache.domains}}, {$addToSet: {caches: cache._id}}).exec()) .then(() => mongo.DomainModel.updateMany({_id: {$nin: cache.domains}}, {$pull: {caches: cache._id}}).exec()) .catch((err) => { if (err.code === mongo.errCodes.DUPLICATE_KEY_ERROR) throw new errors.DuplicateKeyException(`Cache with name: "${cache.name}" already exist.`); throw err; }); } /** * Create or update cache. * * @param {Object} cache - The cache. * @returns {Promise.<mongo.ObjectId>} that resolves cache id of merge operation. */ static merge(cache) { if (cache._id) return update(cache); return create(cache); } /** * Get caches by spaces. * * @param {mongo.ObjectId|String} spaceIds - The spaces ids that own caches. * @returns {Promise.<mongo.Cache[]>} - contains requested caches. */ static listBySpaces(spaceIds) { return mongo.Cache.find({space: {$in: spaceIds}}).sort('name').lean().exec(); } /** * Remove caches. * * @param {Array.<String>|String} ids - The cache ids for remove. * @returns {Promise.<{rowsAffected}>} - The number of affected rows. */ static remove(ids) { if (_.isNil(ids)) return Promise.reject(new errors.IllegalArgumentException('Cache id can not be undefined or null')); ids = _.castArray(ids); if (_.isEmpty(ids)) return Promise.resolve({rowsAffected: 0}); return mongo.Cluster.updateMany({caches: {$in: ids}}, {$pull: {caches: {$in: ids}}}).exec() .then(() => mongo.Cluster.updateMany({}, {$pull: {checkpointSpi: {kind: 'Cache', Cache: {cache: {$in: ids}}}}}).exec()) // TODO WC-201 fix cleanup of cache on deletion for cluster service configuration. // .then(() => mongo.Cluster.updateMany({'serviceConfigurations.cache': cacheId}, {$unset: {'serviceConfigurations.$.cache': ''}}).exec()) .then(() => mongo.DomainModel.updateMany({caches: {$in: ids}}, {$pull: {caches: {$in: ids}}}).exec()) .then(() => mongo.Cache.deleteMany({_id: {$in: ids}}).exec()) .then(convertRemoveStatus); } /** * Remove all caches by user. * * @param {mongo.ObjectId|String} userId - The user id that own caches. * @param {Boolean} demo - The flag indicates that need lookup in demo space. * @returns {Promise.<{rowsAffected}>} - The number of affected rows. */ static removeAll(userId, demo) { return spacesService.spaceIds(userId, demo) .then(removeAllBySpaces) .then(convertRemoveStatus); } } return CachesService; };