| /* |
| * 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/domains', |
| inject: ['mongo', 'services/spaces', 'services/caches', 'errors'] |
| }; |
| |
| /** |
| * @param mongo |
| * @param {SpacesService} spacesService |
| * @param {CachesService} cachesService |
| * @param errors |
| * @returns {DomainsService} |
| */ |
| module.exports.factory = (mongo, spacesService, cachesService, errors) => { |
| /** |
| * Convert remove status operation to own presentation. |
| * |
| * @param {RemoveResult} result - The results of remove operation. |
| */ |
| const convertRemoveStatus = ({result}) => ({rowsAffected: result.n}); |
| |
| const _updateCacheStore = (cacheStoreChanges) => |
| Promise.all(_.map(cacheStoreChanges, (change) => mongo.Cache.update({_id: {$eq: change.cacheId}}, change.change, {}).exec())); |
| |
| /** |
| * Update existing domain. |
| * |
| * @param {Object} domain - The domain for updating |
| * @param savedDomains List of saved domains. |
| * @returns {Promise.<mongo.ObjectId>} that resolves domain id |
| */ |
| const update = (domain, savedDomains) => { |
| const domainId = domain._id; |
| |
| return mongo.DomainModel.update({_id: domainId}, domain, {upsert: true}).exec() |
| .then(() => mongo.Cache.update({_id: {$in: domain.caches}}, {$addToSet: {domains: domainId}}, {multi: true}).exec()) |
| .then(() => mongo.Cache.update({_id: {$nin: domain.caches}}, {$pull: {domains: domainId}}, {multi: true}).exec()) |
| .then(() => { |
| savedDomains.push(domain); |
| |
| return _updateCacheStore(domain.cacheStoreChanges); |
| }) |
| .catch((err) => { |
| if (err.code === mongo.errCodes.DUPLICATE_KEY_UPDATE_ERROR || err.code === mongo.errCodes.DUPLICATE_KEY_ERROR) |
| throw new errors.DuplicateKeyException('Domain model with value type: "' + domain.valueType + '" already exist.'); |
| else |
| throw err; |
| }); |
| }; |
| |
| /** |
| * Create new domain. |
| * |
| * @param {Object} domain - The domain for creation. |
| * @param savedDomains List of saved domains. |
| * @returns {Promise.<mongo.ObjectId>} that resolves cluster id. |
| */ |
| const create = (domain, savedDomains) => { |
| return mongo.DomainModel.create(domain) |
| .then((createdDomain) => { |
| savedDomains.push(createdDomain); |
| |
| return mongo.Cache.update({_id: {$in: domain.caches}}, {$addToSet: {domains: createdDomain._id}}, {multi: true}).exec() |
| .then(() => _updateCacheStore(domain.cacheStoreChanges)); |
| }) |
| .catch((err) => { |
| if (err.code === mongo.errCodes.DUPLICATE_KEY_ERROR) |
| throw new errors.DuplicateKeyException('Domain model with value type: "' + domain.valueType + '" already exist.'); |
| else |
| throw err; |
| }); |
| }; |
| |
| const _saveDomainModel = (domain, savedDomains) => { |
| const domainId = domain._id; |
| |
| if (domainId) |
| return update(domain, savedDomains); |
| |
| return create(domain, savedDomains); |
| }; |
| |
| const _save = (domains) => { |
| if (_.isEmpty(domains)) |
| throw new errors.IllegalArgumentException('Nothing to save!'); |
| |
| const savedDomains = []; |
| const generatedCaches = []; |
| |
| const promises = _.map(domains, (domain) => { |
| if (domain.newCache) { |
| return mongo.Cache.findOne({space: domain.space, name: domain.newCache.name}).exec() |
| .then((cache) => { |
| if (cache) |
| return Promise.resolve(cache); |
| |
| // If cache not found, then create it and associate with domain model. |
| const newCache = domain.newCache; |
| newCache.space = domain.space; |
| |
| return cachesService.merge(newCache); |
| }) |
| .then((cache) => { |
| domain.caches = [cache._id]; |
| |
| generatedCaches.push(cache); |
| |
| return _saveDomainModel(domain, savedDomains); |
| }); |
| } |
| |
| return _saveDomainModel(domain, savedDomains); |
| }); |
| |
| return Promise.all(promises).then(() => ({savedDomains, generatedCaches})); |
| }; |
| |
| /** |
| * Remove all caches by space ids. |
| * |
| * @param {Array.<Number>} spaceIds - The space ids for cache deletion. |
| * @returns {Promise.<RemoveResult>} - that resolves results of remove operation. |
| */ |
| const removeAllBySpaces = (spaceIds) => { |
| return mongo.Cache.update({space: {$in: spaceIds}}, {domains: []}, {multi: true}).exec() |
| .then(() => mongo.DomainModel.remove({space: {$in: spaceIds}}).exec()); |
| }; |
| |
| class DomainsService { |
| static shortList(userId, demo, clusterId) { |
| return spacesService.spaceIds(userId, demo) |
| .then((spaceIds) => { |
| const sIds = _.map(spaceIds, (spaceId) => mongo.ObjectId(spaceId)); |
| |
| return mongo.DomainModel.aggregate([ |
| {$match: {space: {$in: sIds}, clusters: mongo.ObjectId(clusterId)}}, |
| {$project: { |
| keyType: 1, |
| valueType: 1, |
| queryMetadata: 1, |
| hasIndex: { |
| $or: [ |
| { |
| $and: [ |
| {$eq: ['$queryMetadata', 'Annotations']}, |
| { |
| $or: [ |
| {$eq: ['$generatePojo', false]}, |
| { |
| $and: [ |
| {$eq: ['$databaseSchema', '']}, |
| {$eq: ['$databaseTable', '']} |
| ] |
| } |
| ] |
| } |
| ] |
| }, |
| {$gt: [{$size: {$ifNull: ['$keyFields', []]}}, 0]} |
| ] |
| } |
| }} |
| ]).exec(); |
| }); |
| } |
| |
| static get(userId, demo, _id) { |
| return spacesService.spaceIds(userId, demo) |
| .then((spaceIds) => mongo.DomainModel.findOne({space: {$in: spaceIds}, _id}).lean().exec()); |
| } |
| |
| static upsert(model) { |
| if (_.isNil(model._id)) |
| return Promise.reject(new errors.IllegalArgumentException('Model id can not be undefined or null')); |
| |
| const query = _.pick(model, ['space', '_id']); |
| |
| return mongo.DomainModel.update(query, {$set: model}, {upsert: true}).exec() |
| .then(() => mongo.Cache.update({_id: {$in: model.caches}}, {$addToSet: {domains: model._id}}, {multi: true}).exec()) |
| .then(() => mongo.Cache.update({_id: {$nin: model.caches}}, {$pull: {domains: model._id}}, {multi: true}).exec()) |
| .then(() => _updateCacheStore(model.cacheStoreChanges)) |
| .catch((err) => { |
| if (err.code === mongo.errCodes.DUPLICATE_KEY_ERROR) |
| throw new errors.DuplicateKeyException(`Model with value type: "${model.valueType}" already exist.`); |
| |
| throw err; |
| }); |
| } |
| |
| /** |
| * Remove model. |
| * |
| * @param {mongo.ObjectId|String} ids - The model id for remove. |
| * @returns {Promise.<{rowsAffected}>} - The number of affected rows. |
| */ |
| static remove(ids) { |
| if (_.isNil(ids)) |
| return Promise.reject(new errors.IllegalArgumentException('Model id can not be undefined or null')); |
| |
| ids = _.castArray(ids); |
| |
| if (_.isEmpty(ids)) |
| return Promise.resolve({rowsAffected: 0}); |
| |
| return mongo.Cache.update({domains: {$in: ids}}, {$pull: {domains: ids}}, {multi: true}).exec() |
| .then(() => mongo.Cluster.update({models: {$in: ids}}, {$pull: {models: ids}}, {multi: true}).exec()) |
| .then(() => mongo.DomainModel.remove({_id: {$in: ids}}).exec()) |
| .then(convertRemoveStatus); |
| } |
| |
| /** |
| * Batch merging domains. |
| * |
| * @param {Array.<mongo.DomainModel>} domains |
| */ |
| static batchMerge(domains) { |
| return _save(domains); |
| } |
| |
| /** |
| * Get domain and linked objects by space. |
| * |
| * @param {mongo.ObjectId|String} spaceIds - The space id that own domain. |
| * @returns {Promise.<Array.<mongo.DomainModel>>} contains requested domains. |
| */ |
| static listBySpaces(spaceIds) { |
| return mongo.DomainModel.find({space: {$in: spaceIds}}).sort('valueType').lean().exec(); |
| } |
| |
| /** |
| * Remove all domains by user. |
| * @param {mongo.ObjectId|String} userId - The user id that own domain. |
| * @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 DomainsService; |
| }; |