function GoogleImagesViewModel()

in google-cloud-server/src/main/resources/buildServerResources/images.vm.js [3:759]


function GoogleImagesViewModel($, ko, dialog, config) {
    var self = this;
    var timeout = null;

    self.loadingResources = ko.observable(false);
    self.loadingResourcesByZone = ko.observable(false);
    self.validatingKey = ko.observable(false);
    self.errorResources = ko.observable("");
    self.showAccessKey = ko.observable(false);
    self.showMetadata = ko.observable(false);
    self.showServiceAccount = ko.observable(false);
    self.isDragOver = ko.observable(false);
    self.hasFileReader = ko.observable(typeof FileReader !== "undefined");

    // Credentials
    self.credentials = ko.validatedObservable({
        type: ko.observable(),
        accessKey: ko.observable().extend({required: true}).extend({
            validation: {
                async: true,
                validator: function (accessKey, otherVal, callback) {
                    var url = getBasePath() + "resource=permissions";
                    self.validatingKey(true);

                    $.post(url, {
                        "prop:secure:accessKey": accessKey
                    }).then(function (response) {
                        var $response = $(response);
                        var errors = getErrors($response);
                        if (errors) {
                            callback({isValid: false, message: errors});
                        } else {
                            callback(true);
                        }
                    }, function (error) {
                        callback({isValid: false, message: error.message});
                    }).always(function () {
                        self.validatingKey(false);
                    });
                },
                message: 'Invalid key'
            }
        })
    });

    self.credentials().type.subscribe(function (value) {
        if (value) self.loadInfo();
    });

    self.isValidCredentials = ko.pureComputed(function () {
        return self.credentials().type() === 'environment' ||
            self.credentials().type() === 'key' && self.credentials().accessKey.isValid();
    });

    self.isValidCredentials.subscribe(function (value) {
        if (value) self.loadInfo();
    });

    var imageTypes = {
        image: 'Image',
        imageFamily: 'ImageFamily',
        template: 'Template'
    };

    self.imageTypes = ko.observableArray([
        {id: imageTypes.image, text: "Image"},
        {id: imageTypes.imageFamily, text: "Image Family"},
        {id: imageTypes.template, text: "Instance Template"}
    ]);

    self.imageType = ko.observable();

    // Image details
    var maxLength = 60;
    self.machineCustom = ko.observable(false);
    self.subnetManually = ko.observable(false);
    self.image = ko.validatedObservable({
        sourceProject: ko.observable(),
        imageType: self.imageType,
        sourceImage: ko.observable().extend({
            required: {
                onlyIf: function () {
                    return self.imageType() === imageTypes.image
                }
            }
        }),
       sourceImageFamily: ko.observable().extend({
           required: {
               onlyIf: function () {
                   return self.imageType() === imageTypes.imageFamily
               }
           }
       }),
        instanceTemplate: ko.observable().extend({
            required: {
                onlyIf: function () {
                    return self.imageType() === imageTypes.template
                }
            }
        }),
        zone: ko.observable().extend({required: true}),
        network: ko.observable().extend({
            required: {
                onlyIf: function () {
                    return [imageTypes.image, imageTypes.imageFamily].includes(self.imageType())
                }
            }
        }),
        subnetManually: self.subnetManually,
        subnetInput: ko.observable().extend({
            onlyIf: function () {
                return [imageTypes.image, imageTypes.imageFamily].includes(self.imageType()) && self.subnetManually() === true;
            }
        }),
        subnet: ko.observable().extend({
            onlyIf: function () {
                return [imageTypes.image, imageTypes.imageFamily].includes(self.imageType()) && self.subnetManually() === false;
            }
        }),
        maxInstances: ko.observable(1).extend({required: true, min: 0}),
        preemptible: ko.observable(false),
        machineCustom: self.machineCustom,
        machineType: ko.observable().extend({
            validation: {
                validator: function (value) {
                    return value;
                },
                message: "Machine type should not be empty",
                onlyIf: function () {
                    return [imageTypes.image, imageTypes.imageFamily].includes(self.imageType()) && self.machineCustom() === false;
                }
            }
        }),
        machineCores: ko.observable().extend({
            validation: {
                validator: function (value) {
                    var number = parseInt(value);
                    return number > 0 && (number === 1 || (number % 2 === 0));
                },
                message: "1 or an even number of vCPUs can be created",
                onlyIf: function () {
                    return [imageTypes.image, imageTypes.imageFamily].includes(self.imageType()) && self.machineCustom() === true;
                }
            }
        }),
        machineMemory: ko.observable().extend({
            validation: {
                validator: function (value) {
                    return value > 900 && (value % 256 === 0);
                },
                message: "Total memory must be a multiple of 256 MB",
                onlyIf: function () {
                    return [imageTypes.image, imageTypes.imageFamily].includes(self.imageType()) && self.machineCustom() === true;
                }
            }
        }),
        machineMemoryExt: ko.observable(false),
        diskType: ko.observable(),
        diskSizeGb: ko.observable('').extend({required: false, min: 0}),
        vmNamePrefix: ko.observable('').trimmed().extend({required: true, maxLength: maxLength}).extend({
            validation: {
                validator: function (value) {
                    return self.originalImage && self.originalImage['source-id'] === value ||
                        !ko.utils.arrayFirst(self.images(), function (image) {
                            return image['source-id'] === value;
                        });
                },
                message: 'Name prefix should be unique within subscription'
            }
        }).extend({
            pattern: {
                message: 'Name can contain alphanumeric characters, underscore and hyphen',
                params: /^[a-z][a-z0-9_-]*$/i
            }
        }),
        metadata: ko.observable().extend({
            validation: {
                validator: function (value) {
                    if (!value) return true;

                    var root;
                    try {
                        root = JSON.parse(value);
                    } catch (error) {
                        console.log("Unable to parse metadata: " + error);
                        return false;
                    }

                    for (var key in root) {
                        if (root.hasOwnProperty(key)) {
                            if ("object" === typeof(key)) {
                                console.log("Invalid key: " + key);
                                return false;
                            }

                            if ("object" === typeof(root[key])) {
                                console.log("Invalid value for key: " + key);
                                return false;
                            }
                        }
                    }

                    return true;
                },
                message: "Invalid metadata value"
            }
        }),
        growingId: ko.observable(false),
        externalIP: ko.observable(false),
        serviceAccount: ko.observable(),
        scopes: ko.observable(),
        agentPoolId: ko.observable().extend({required: true}),
        profileId: ko.observable()
    });

    // Data from APIs
    self.sourceImages = ko.observableArray([]);
    self.sourceImageFamilies = ko.observableArray([]);
    self.instanceTemplates = ko.observableArray([]);
    self.zones = ko.observableArray([]);
    self.networks = ko.observableArray([]);
    self.subnets = ko.observableArray([]);
    self.machineTypes = ko.observableArray([]);
    self.diskTypes = ko.observableArray([]);
    self.agentPools = ko.observableArray([]);
    self.nets = {};

    // Hidden fields for serialized values
    self.images_data = ko.observable();

    // Deserialized values
    self.images = ko.observableArray();

    // Reload info on credentials change
    self.credentials().accessKey.isValidating.subscribe(function (isValidating) {
        if (isValidating || !self.credentials().accessKey.isValid()) {
            return;
        }

        self.showAccessKey(false);
    });

    self.image().sourceProject.subscribe(function (project) {
        if (!project) return;

        clearTimeout(timeout);
        // Wait 500ms to ensure that user has finished typing
        timeout = setTimeout(function() {
            // Reload image and image family list
            loadInfoBySourceProject(project);
        }, 500);
    })

    self.image().sourceImage.subscribe(function (image) {
        if (!image) return;

        // Fill in vm name prefix
        if (self.image().vmNamePrefix()) return;
        var vmName = image.slice(-maxLength);
        self.image().vmNamePrefix(vmName);
    });

    self.image().sourceImageFamily.subscribe(function (imageFamily) {
        if (!imageFamily) return;

        // Fill in vm name prefix
        if (self.image().vmNamePrefix()) return;
        var vmName = imageFamily.slice(-maxLength);
        self.image().vmNamePrefix(vmName);
    });

    self.image().zone.subscribe(function (zone) {
        if (!zone) return;
        loadInfoByZone(zone);
    });

    self.image().network.subscribe(function (network) {
        changeSubnets(network, self.image().subnet());
    });

    self.images_data.subscribe(function (data) {
        var images = ko.utils.parseJson(data || "[]");
        images.forEach(function (image) {
            image.sourceProject = image.sourceProject || '';
            image.imageType = image.imageType || imageTypes.image;
            image.imageFamily = image.imageFamily || '';
            image.preemptible = getBoolean(image.preemptible);
            image.machineCustom = getBoolean(image.machineCustom);
            image.subnetManually = getBoolean(image.subnetManually);
            image.machineMemoryExt = getBoolean(image.machineMemoryExt);
            image.growingId = getBoolean(image.growingId);
            image.externalIP = getBoolean(image.externalIP)
        });
        self.images(images);
    });

    // Dialogs
    self.originalImage = null;

    self.showDialog = function (data) {
        self.originalImage = data;

        var model = self.image();
        var image = data || {
            sourceProject: '',
            imageType: imageTypes.image,
            maxInstances: 1,
            diskSizeGb: '',
            preemptible: false,
            machineCustom: false,
            subnetManually: false,
            growingId: false,
            externalIP: false,
        };

        var sourceImage = image.sourceImage;
        if (sourceImage && !ko.utils.arrayFirst(self.sourceImages(), function (item) {
            return item.id === sourceImage;
        })) {
            self.sourceImages({id: sourceImage, text: sourceImage});
        }

        var sourceImageFamily = image.sourceImageFamily;
        if (sourceImageFamily && !ko.utils.arrayFirst(self.sourceImageFamilies(), function (item) {
            return item.id === sourceImageFamily;
        })) {
            self.sourceImageFamilies({id: sourceImageFamily, text: sourceImageFamily});
        }

        var instanceTemplate = image.instanceTemplate;
        if (instanceTemplate && !ko.utils.arrayFirst(self.instanceTemplates(), function (item) {
            return item.id === instanceTemplate;
        })) {
            self.instanceTemplates({id: instanceTemplate, text: instanceTemplate});
        }

        var machineType = image.machineType;
        if (machineType && !ko.utils.arrayFirst(self.machineTypes(), function (item) {
            return item.id === machineType;
        })) {
            self.machineTypes({id: machineType, text: machineType});
        }

        var diskType = image.diskType;
        if (diskType && !ko.utils.arrayFirst(self.diskTypes(), function (item) {
            return item.id === diskType;
        })) {
            self.diskTypes({id: diskType, text: diskType});
        }

        var network = image.network;
        if (network && !ko.utils.arrayFirst(self.networks(), function (item) {
            return item.id === network;
        })) {
            self.networks({id: network, text: network});
        }

        model.sourceProject(image.sourceProject);
        model.imageType(image.imageType);
        model.sourceImage(image.sourceImage);
        model.sourceImageFamily(image.sourceImageFamily);
        model.instanceTemplate(image.instanceTemplate);
        model.zone(image.zone);
        model.network(network);
        var subnet = image.subnet;
        changeSubnets(network, subnet);
        model.subnet(subnet);
        model.subnetManually(image.subnetManually || false);
        model.subnetInput(image.subnetInput)
        model.machineCustom(image.machineCustom || false);
        model.machineType(machineType);
        model.machineCores(image.machineCores);
        model.machineMemory(image.machineMemory);
        model.machineMemoryExt(image.machineMemoryExt);
        model.diskType(diskType);
        model.diskSizeGb(image.diskSizeGb);
        model.maxInstances(image.maxInstances);
        model.preemptible(image.preemptible);
        model.vmNamePrefix(image['source-id']);
        model.metadata(image.metadata);
        model.growingId(image.growingId);
        model.externalIP(image.externalIP);
        model.serviceAccount(image.serviceAccount);
        model.scopes(image.scopes);
        model.agentPoolId(image.agent_pool_id);
        model.profileId(image.profileId);

        self.showMetadata(false);
        self.showServiceAccount(!!image.serviceAccount);

        self.image.errors.showAllMessages(false);
        dialog.showDialog(!self.originalImage);

        return false;
    };

    self.closeDialog = function () {
        dialog.close();
        return false;
    };

    self.saveImage = function () {
        var model = self.image();
        var image = {
            sourceProject: model.sourceProject(),
            imageType: model.imageType(),
            sourceImage: model.sourceImage(),
            sourceImageFamily: model.sourceImageFamily(),
            instanceTemplate: model.instanceTemplate(),
            zone: model.zone(),
            network: model.network(),
            subnet: model.subnet(),
            subnetManually: model.subnetManually(),
            subnetInput: model.subnetInput(),
            maxInstances: model.maxInstances(),
            preemptible: model.preemptible(),
            'source-id': model.vmNamePrefix(),
            machineCustom: model.machineCustom(),
            machineType: model.machineType(),
            machineCores: model.machineCores(),
            machineMemory: model.machineMemory(),
            machineMemoryExt: model.machineMemoryExt(),
            diskType: model.diskType(),
            diskSizeGb: model.diskSizeGb(),
            metadata: model.metadata(),
            growingId: model.growingId(),
            externalIP: model.externalIP(),
            serviceAccount: model.serviceAccount(),
            scopes: model.scopes(),
            agent_pool_id: model.agentPoolId(),
            profileId: model.profileId()
        };

        var originalImage = self.originalImage;
        if (originalImage) {
            self.images.replace(originalImage, image);
        } else {
            self.images.push(image);
        }
        saveImages();

        dialog.close();
        return false;
    };

    self.deleteImage = function (image) {
        var message = "Do you really want to delete agent image based on " + image.image + "?";
        var remove = confirm(message);
        if (!remove) {
            return false;
        }

        self.images.remove(image);
        saveImages();

        return false;
    };

    self.loadInfo = function () {
        if (!self.isValidCredentials()) {
            return
        }

        self.loadingResources(true);

        var url = getBasePath() +
            "resource=permissions" +
            "&resource=zones" +
            "&resource=networks" +
            "&resource=images" +
            "&resource=imageFamilies" +
            "&resource=templates";

        $.post(url, getCredentials()).then(function (response) {
            var $response = $(response);
            var errors = getErrors($response);
            if (errors) {
                self.errorResources(errors);
                return;
            } else {
                self.errorResources("");
            }

            self.sourceImages(getSourceImages($response));
            self.sourceImageFamilies(getSourceImageFamilies($response));
            self.instanceTemplates(getInstanceTemplates($response));
            self.zones(getZones($response));
            self.networks(getNetworks($response));
        }, function (error) {
            self.errorResources("Failed to load data: " + error.message);
            console.log(error);
        }).always(function () {
            self.loadingResources(false);
        });
    };

    function loadInfoBySourceProject(sourceProject) {
        if (!self.isValidCredentials()) {
            return
        }

        var credentialsType = self.credentials().type();
        var accessKey = self.credentials().accessKey();

        self.loadingResources(true);

        var url = getBasePath() +
            "sourceProject=" + sourceProject +
            "&resource=images" +
            "&resource=imageFamilies";

        $.post(url, {
            "prop:credentialsType": credentialsType,
            "prop:secure:accessKey": accessKey
        }).then(function (response) {
            var $response = $(response);
            var errors = getErrors($response);
            if (errors) {
                self.errorResources(errors);
                return;
            } else {
                self.errorResources("");
            }

            self.sourceImages(getSourceImages($response));
            self.sourceImageFamilies(getSourceImageFamilies($response));
        }, function (error) {
            self.errorResources("Failed to load data: " + error.message);
            console.log(error);
        }).always(function () {
            self.loadingResources(false);
        });
    }

    function changeSubnets(network, subnet) {
        if (!network) return;

        var subnets = self.nets[network];
        if (subnet && !subnets) {
            subnets = [{id: subnet, text: subnet}];
        }

        self.subnets(subnets || []);
    }

    function loadInfoByZone(zoneId) {
        if (!self.isValidCredentials()) {
            return
        }

        var credentialsType = self.credentials().type();
        var accessKey = self.credentials().accessKey();

        var zone = ko.utils.arrayFirst(self.zones(), function (item) {
            return item.id === zoneId;
        });

        self.loadingResourcesByZone(true);

        var url = getBasePath() +
            "zone=" + zone.id +
            "&region=" + zone.region +
            "&resource=subnets" +
            "&resource=machineTypes" +
            "&resource=diskTypes";

        $.post(url, {
            "prop:credentialsType": credentialsType,
            "prop:secure:accessKey": accessKey
        }).then(function (response) {
            var $response = $(response);
            var errors = getErrors($response);
            if (errors) {
                self.errorResources(errors);
                return;
            } else {
                self.errorResources("");
            }

            self.machineTypes(getMachineTypes($response));
            self.diskTypes(getDiskTypes($response));
            getSubnets($response);
            changeSubnets(self.image().network())
        }, function (error) {
            self.errorResources("Failed to load data: " + error.message);
            console.log(error);
        }).always(function () {
            self.loadingResourcesByZone(false);
        });
    }

    self.loadAccessKey = function (file) {
        if (!self.hasFileReader()) return;
        var reader = new FileReader();
        reader.onload = function (e) {
            self.credentials().accessKey(e.target.result);
            document.getElementById("secure:accessKey").value = e.target.result;
        };
        reader.readAsText(file);
    };

    self.dragEnterHandler = function () {
        self.isDragOver(true);
    };

    self.dragLeaveHandler = function () {
        self.isDragOver(false);
    };

    self.dropHandler = function (e) {
        self.isDragOver(false);
        var dt = e.dataTransfer || e.originalEvent.dataTransfer;
        if (dt.items) {
            for (var i = 0; i < dt.items.length; i++) {
                if (dt.items[i].kind === "file") {
                    var file = dt.items[i].getAsFile();
                    self.loadAccessKey(file);
                    return
                }
            }
        } else {
            for (var i = 0; i < dt.files.length; i++) {
                self.loadAccessKey(dt.files[i]);
                return
            }
        }
    };

    function saveImages() {
        var images = self.images();
        self.images_data(JSON.stringify(images));
    }

    function getBasePath() {
        return config.baseUrl + "?";
    }

    function getCredentials() {
        var credentialsType = self.credentials().type();
        var accessKey = self.credentials().accessKey();
        if (credentialsType === "key") {
            return {
                "prop:credentialsType": credentialsType,
                "prop:secure:accessKey": accessKey
            };
        } else {
            return {
                "prop:credentialsType": credentialsType
            };
        }
    }

    function getErrors($response) {
        var $errors = $response.find("errors:eq(0) error");
        if ($errors.length) {
            return $.map($errors, function (error) {
                return $(error).text();
            }).join(", ");
        }

        return "";
    }

    function getZones($response) {
        return $response.find("zones:eq(0) zone").map(function () {
            return {id: $(this).attr("id"), region: $(this).attr("region"), text: $(this).text()};
        }).get();
    }

    function getNetworks($response) {
        return $response.find("networks:eq(0) network").map(function () {
            return {id: $(this).attr("id"), text: $(this).text()};
        }).get();
    }

    function getSubnets($response) {
        self.nets = {};
        return $response.find("subnets:eq(0) subnet").map(function () {
            var subnet = {id: $(this).attr("id"), text: $(this).text(), network: $(this).attr("network")};
            var nets = self.nets[subnet.network] || [];
            nets.push(subnet);
            self.nets[subnet.network] = nets;
            return subnet;
        }).get();
    }

    function getMachineTypes($response) {
        return $response.find("machineTypes:eq(0) machineType").map(function () {
            return {id: $(this).attr("id"), text: $(this).text()};
        }).get();
    }

    function getDiskTypes($response) {
        return $response.find("diskTypes:eq(0) diskType").map(function () {
            return {id: $(this).attr("id"), text: $(this).text()};
        }).get();
    }

    function getSourceImages($response) {
        return $response.find("images:eq(0) image").map(function () {
            return {id: $(this).attr("id"), text: $(this).text()};
        }).get();
    }

    function getSourceImageFamilies($response) {
        return $response.find("imageFamilies:eq(0) imageFamily").map(function () {
            return {id: $(this).attr("id"), text: $(this).text()};
        }).get();
    }

    function getInstanceTemplates($response) {
        return $response.find("templates:eq(0) template").map(function () {
            return {id: $(this).attr("id"), text: $(this).text()};
        }).get();
    }

    function getBoolean(variable) {
        if (typeof variable === 'string' || variable instanceof String) {
            return ko.utils.parseJson(variable);
        }

        return variable
    }

    (function loadAgentPools() {
        var url = config.baseUrl + "?resource=agentPools&projectId=" + encodeURIComponent(config.projectId);
        return $.post(url).then(function (response) {
            var $response = $(response);
            var errors = getErrors($response);
            if (errors) {
                self.errorResources(errors);
                return;
            } else {
                self.errorResources("");
            }

            var agentPools = $response.find("agentPools:eq(0) agentPool").map(function () {
                return {
                    id: $(this).attr("id"),
                    text: $(this).text()
                };
            }).get();

            self.agentPools(agentPools);
            self.image().agentPoolId.valueHasMutated();
        }, function (error) {
            self.errorResources("Failed to load data: " + error.message);
            console.log(error);
        });
    })();

    self.afterRender = function () {
        if (!self.credentials().accessKey()) {
            self.showAccessKey(true);
        }
    };
}