ui-modules/utils/persistence-importer/persistence-importer.js (132 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. */ import angular from 'angular'; import template from './persistence-importer.html'; import serverApi from '../../utils/api/brooklyn/server.js'; const MODULE_NAME = 'brooklyn.components.persistence-importer'; /** * @ngdoc module * @name brooklyn.components.persistence-importer * @requires serverApi * * @description * TODO */ angular.module(MODULE_NAME, [serverApi]) .service('brooklynPersistenceImporter', ['$q', 'serverApi', persistenceImporterService]) .directive('customOnChange', customOnChangeDirective) .directive('brooklynPersistenceImporter', ['$compile', 'brooklynPersistenceImporter', persistenceImporterDirective]); export default MODULE_NAME; /** * @ngdoc directive * @name brooklynPersistenceImporter * @module brooklyn.components.persistence-importer * @restrict A * * @description * TODO * * @param {string} brooklynPersistenceImporter The value can be empty. Otherwise, the directive will listen for any event broadcasted * with this name and will trigger the overlay upon reception. */ export function persistenceImporterDirective($compile, brooklynPersistenceImporter) { return { restrict: 'A', link: link }; function link(scope, element, attrs) { let div = document.createElement('div'); if ((('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && 'FormData' in window && 'FileReader' in window) { element.addClass('br-has-drag-upload'); } element.append($compile(template)(scope)); let counter = 0; element.bind('drag dragstart dragend dragover dragenter dragleave drop', (event)=> { event.preventDefault(); event.stopPropagation(); }).bind('drag dragstart dragover dragenter', (event)=> { event.dataTransfer.dropEffect = 'copy'; element.addClass('br-drag-active'); }).bind('dragenter', ()=> { counter++; }).bind('dragleave', (event)=> { counter--; if (counter === 0) { element.removeClass('br-drag-active'); } }).bind('drop', (event)=> { scope.upload(event.dataTransfer.files); }); let field = attrs.brooklynPersistenceImporter; if (angular.isDefined(field)) { scope.$on(field, ()=> { counter++; element.addClass('br-drag-active'); }); } scope.selectedFiles = []; scope.close = ()=> { counter--; element.removeClass('br-drag-active'); }; scope.filesChanged = (event)=> { scope.upload(event.target.files); }; scope.upload = (files)=> { for (let i = 0; i < files.length; i++) { let file = files[i]; brooklynPersistenceImporter.upload(file).then((data)=> { file.result = data; }).catch((error)=> { file.error = error; }).finally(()=> { scope.$applyAsync(); }); scope.selectedFiles.unshift(file); scope.$apply(); } }; scope.getPersistenceItemUrl = (item)=> { let itemTraits = item.tags? item.tags.find(item => item.hasOwnProperty("traits")) : {"traits":[]}; return (item.supertypes ? item.supertypes : itemTraits.traits) .includes('org.apache.brooklyn.api.location.Location') ? `/brooklyn-ui-location-manager/#!/location?symbolicName=${item.symbolicName}&version=${item.version}` : `/brooklyn-ui-persistence/#!/bundles/${item.containingBundle.split(':')[0]}/${item.containingBundle.split(':')[1]}/types/${item.symbolicName}/${item.version}`; }; } } /** * @ngdoc service * @name brooklynPersistenceImporter * @module brooklyn.components.persistence-importer * * @description * Encapsulate the logic to validate files to upload to the persistence. */ export function persistenceImporterService($q, serverApi) { let extensions = { 'zip' : { headers: { 'Content-Type': 'multipart/form-data', 'Accept': 'application/json', 'Brooklyn-Allow-Non-Master-Access': 'true'}, transformRequest: angular.identity } }; return { /** * @ngdoc method * @name upload * @methodOf brooklynPersistenceImporter * * @description * Upload a file to the persistence. This will validate the extensions and reject the promises if the current one is * not supported. * * @param {object} file A file object, representing a file to upload to the persistence. * * @return A promise that gets resolve if the upload *and* process of the file server side is successful; the * resolve data contains the persistence items that have been added a map of `{id: item}`. Otherwise, the promise * is rejected with the error message as parameter. */ upload: upload }; function upload(file) { let defer = $q.defer(); if (new RegExp('^.*\.(' + Object.keys(extensions).join('|') + ')$').test(file.name)) { Object.keys(extensions).forEach((extension)=> { if (!new RegExp('^.*\.(' + extension + ')$').test(file.name)) { return; } let options = extensions[extension]; let reader = new FileReader(); reader.addEventListener('load', ()=> { try { let rawData = new Uint8Array(reader.result); serverApi.importPersistenceData(rawData, options).then((response)=> { defer.resolve(response); }).catch((response)=> { file.error = response; defer.reject('Cannot upload item to the persistence: ' + response.error.message); }); } catch (error) { file.error = error; defer.reject('Cannot read file: ' + error.message); } }, false); reader.readAsArrayBuffer(file); }); } else { defer.reject('Unsupported file type. Please upload only files with the following extensions: ' + Object.keys(extensions).map((extension)=>('*.' + extension)).join(', ')); } return defer.promise; } } export function customOnChangeDirective() { return { restrict: 'A', link: function (scope, element, attrs) { element.on('change', x => { var onChangeHandler = scope.$eval(attrs.customOnChange); onChangeHandler(x); }); element.on('$destroy', function() { element.off(); }); } }; }