export function CatalogItemModalController()

in ui-modules/blueprint-composer/app/components/catalog-saver/catalog-saver.directive.js [188:414]


export function CatalogItemModalController($scope, $filter, blueprintService, paletteApi, brUtilsGeneral) {
    $scope.REASONS = REASONS;
    $scope.VIEWS = VIEWS;
    $scope.TYPES = TYPES;
    $scope.state = {
        pattern: VALID_FIELD_PATTERN,
        view: VIEWS.form,
        saving: false,
        force: false
    };
    /* Derived properties & calculators, will be updated whenever $scope.state.view changes */
    $scope.getTitle = () => {
        // expect we should always have current or default name, possibly don't need symbolicName or `blueprint` defaults
        const name = $scope.config.current.name || $scope.config.local.default.name || $scope.config.current.symbolicName || $scope.config.local.default.symbolicName;
        switch ($scope.state.view) {
            case VIEWS.form:
                return $scope.isUpdate() ? `Update ${name || 'blueprint'}` : 'Add to catalog';
            case VIEWS.saved:
                return `${name || 'Blueprint'} ${$scope.isUpdate() ? 'updated' : 'saved'}`;
        }
    };

    $scope.getCatalogURL = () => {
        const urlPartVersion = _.get($scope, 'config.current.version') || _.get($scope, 'config.version');
        if (!urlPartVersion) return "";

        switch ($scope.state.view) {
            case VIEWS.form:
                return '';
            case VIEWS.saved:
                // TODO where do these come from
                return `/brooklyn-ui-catalog/#!/bundles/${$scope.config.catalogBundleId}/${urlPartVersion}/types/${$scope.config.catalogBundleSymbolicName}/${urlPartVersion}`;
        }
    };

    $scope.title = $scope.getTitle();
    $scope.catalogURL = $scope.getCatalogURL();
    $scope.catalogBomPrefix = 'catalog-bom-';

    $scope.$watch('state.view', (newValue, oldValue) => {
        if (newValue !== oldValue) {
            $scope.title = $scope.getTitle();
            $scope.catalogURL = $scope.getCatalogURL();
        }
    });
    /* END Derived properties */

    const allTypes = [];
    const allBundles = [];

    // Prepare resources for analysis if this is not an Update request.
    if (!$scope.isUpdate()) {

        // Get all types and bundles for analysis.
        const promiseTypes = paletteApi.getTypes({params: {versions: 'all'}}).then(data => {
            allTypes.push(...data);
        }).catch(error => {
            $scope.state.error = error;
        });

        const promiseBundles = paletteApi.getBundles({params: {versions: 'all', detail: true}}).then(data => {
            allBundles.push(...data);
        }).catch(error => {
            $scope.state.error = error;
        });

        function checkIfBundleExists() {
            const bundleName = getBundleId();
            if (allBundles.find(item => item.symbolicName === bundleName)) {
                $scope.showAdvanced = true;
                $scope.state.warning = `Bundle with name "${bundleName}" exists already.`;
            } else {
                $scope.state.warning = undefined;
            }
        }

        Promise.all([promiseTypes, promiseBundles]).then(() => {
            console.info(`Loaded ${allBundles.length} bundles and ${allTypes.length} types for analysis.`)

            // Trigger an initial bundle name check.
            checkIfBundleExists();
        });

        // Watch for bundle name and display warning if bundle exists already.
        $scope.$watchGroup(['config.current.bundle', 'config.local.default.bundle'], () => {
            checkIfBundleExists();
        });
    }

    $scope.save = () => {
        $scope.state.saving = true;
        $scope.state.error = undefined;

        // Analyse existing catalog bundles if this is not an Update request.
        if (!$scope.isUpdate()) {

            const thisBundle = getBundleId();
            const bundles = [];
            const uniqueBundlesIds = new Set();

            // Check if type exists in other bundles.
            bundles.push(...allTypes.filter(item => item.symbolicName === getSymbolicName()).map(item => item.containingBundle));
            bundles.forEach(item => uniqueBundlesIds.add(item.split(':')[0]));

            if (uniqueBundlesIds.size > 0 && !uniqueBundlesIds.has(thisBundle)) {
                $scope.state.error = `This type cannot be saved in bundle "${thisBundle}" from the composer because ` +
                    `it would conflict with a type with the same ID "${getSymbolicName()}" in ${bundles.map(item => `"${item}"`).join(', ')}.`;
                $scope.showAdvanced = true;
                $scope.state.saving = false;
                return; // DO NOT SAVE!
            }

            // Check if any of existing bundles include other types.
            if (uniqueBundlesIds.size) {

                const bundlesWithMultipleTypes = bundles.filter(bundle => {
                    const [bundleName, bundleVersion] = bundle.split(':');
                    if (bundleName !== thisBundle) {
                        return false;
                    }
                    const existingBundle = allBundles.find(item => item.symbolicName === bundleName && item.version === bundleVersion);
                    const otherTypes = existingBundle.types.filter(item => item.symbolicName !== getSymbolicName())
                    return otherTypes.length > 0;
                });

                if (!$scope.state.error && bundlesWithMultipleTypes.length) {
                    $scope.state.error = `This type cannot be saved in bundle "${thisBundle}" from the composer because ` +
                        `${bundlesWithMultipleTypes.map(item => `"${item}"`).join(', ')} include${bundlesWithMultipleTypes.length > 1 ? '' : 's'} other types.`;
                    $scope.showAdvanced = true;
                    $scope.state.saving = false;
                    return; // DO NOT SAVE!
                }
            }
        }

        // Now, try to save.
        let bom = createBom();
        $scope.config.initial = $scope.config.current;
        paletteApi.create(bom, {forceUpdate: $scope.state.force})
            .then((savedItem) => {
                if (!angular.isArray($scope.config.versions)) {
                    $scope.config.versions = [];
                }
                $scope.config.versions.push($scope.config.current.version);
                $scope.state.view = VIEWS.saved;
            })
            .catch(error => {
                $scope.state.error = error.error.message;
            })
            .finally(() => {
                $scope.state.saving = false;
            });
    };

    function getBundleBase() {
        return $scope.config.current.bundle || $scope.config.local.default.bundle;
    }

    function getBundleId() {
        return getBundleBase() && $scope.catalogBomPrefix + getBundleBase();
    }

    function getSymbolicName() {
        return $scope.config.current.symbolicName || $scope.config.local.default.symbolicName;
    }

    function createBom() {
        let blueprint = blueprintService.getAsJson();

        const bundleBase = getBundleBase();
        const bundleSymbolicName = getSymbolicName();
        if (!bundleBase || !bundleSymbolicName) {
            throw "Either the display name must be set, or the bundle and symbolic name must be explicitly set";
        }

        let bomItem = {
            id: bundleSymbolicName,
            itemType: $scope.config.current.itemType,
            item: blueprint
        };
        // tags can now be added to a blueprint created in the YAML Editor
        let tags = [];
        if (blueprint.tags) {
            tags = tags.concat(blueprint.tags);
            delete blueprint['tags'];
        }
        if (blueprint['brooklyn.tags']) {
            tags = [].concat(blueprint['brooklyn.tags']).concat(tags);
        }
        blueprint['brooklyn.tags'] = tags;
        const bundleId = getBundleId();
        let bomCatalogYaml = {
            bundle: bundleId,
            version: $scope.config.current.version,
            items: [ bomItem ]
        };
        if(tags) {
            bomCatalogYaml.tags = tags
        }

        let bundleName = $scope.config.current.name || $scope.config.local.default.name;
        if (brUtilsGeneral.isNonEmpty(bundleName)) {
            bomItem.name = bundleName;
        }
        if (brUtilsGeneral.isNonEmpty($scope.config.current.description)) {
            bomItem.description = $scope.config.current.description;
        }
        if (brUtilsGeneral.isNonEmpty($scope.config.current.iconUrl)) {
            bomItem.iconUrl = $scope.config.current.iconUrl;
        }
        $scope.config.catalogBundleId = bundleId;
        $scope.config.catalogBundleSymbolicName = bundleSymbolicName;
        return jsYaml.dump({ 'brooklyn.catalog': bomCatalogYaml });
    }

    let bundlize = $filter('bundlize');
    $scope.updateDefaults = (newName) => {
        if (!newName) newName = $scope.config.local.default.name;
        $scope.config.local.default.symbolicName = $scope.config.default.symbolicName || ($scope.config.current.itemType==='template' && $scope.config.original.symbolicName) || bundlize(newName) || null;
        $scope.config.local.default.bundle = $scope.config.default.bundle || ($scope.config.current.itemType==='template' && $scope.config.original.bundle) || bundlize(newName) || null;
    };
    $scope.$watchGroup(['config.current.name', 'config.current.itemType', 'config.current.bundle', 'config.current.symbolicName'], (newVals) => {
        $scope.updateDefaults(newVals[0]);
        $scope.form.name.$validate();
        $scope.buttonText = $scope.buttonTextFn();
    });
}