🏡 index : quine-global/quine-anaconda-webui.git

/*
 * Copyright (C) 2022 Red Hat, Inc.
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */

import cockpit from "cockpit";

import { getInstallationStatusAction, getPendingErrorAction } from "../actions/boss-actions.js";

import { debug, error } from "../helpers/log.js";
import { _callClient, _getProperty } from "./helpers.js";

import { moduleClients } from "./index.js";
import { installationState } from "./installation_state.js";
import { LocalizationClient } from "./localization.js";
import { NetworkClient } from "./network.js";
import { RuntimeClient } from "./runtime.js";

const OBJECT_PATH = "/org/fedoraproject/Anaconda/Boss";
const INTERFACE_NAME = "org.fedoraproject.Anaconda.Boss";
export const INSTALLATION_STATUS = { FAILED: "failed", NOT_STARTED: "not_started", RUNNING: "running", SUCCEEDED: "succeeded" };

const callClient = (...args) => {
    return _callClient(BossClient, OBJECT_PATH, INTERFACE_NAME, ...args);
};

/**
 * @param {string} address      Anaconda bus address
 * @param {Function} dispatch   Redux dispatch function
 *
 * @returns {Object}            A DBus client for the Boss bus
 */
export class BossClient {
    constructor (address, dispatch) {
        if (BossClient.instance && (!address || BossClient.instance.address === address)) {
            return BossClient.instance;
        }

        BossClient.instance?.client.close();

        BossClient.instance = this;

        this.client = cockpit.dbus(
            INTERFACE_NAME,
            { address, bus: "none", superuser: "try" }
        );
        this.address = address;
        this.dispatch = dispatch;
    }

    async init (args = {}) {
        this.client.addEventListener("close", () => error("Boss client closed"));

        const status = await getInstallationStatus();
        const installationStarted = status !== INSTALLATION_STATUS.NOT_STARTED;
        installationState.active = installationStarted;
        const clients = installationStarted ? [RuntimeClient, NetworkClient, LocalizationClient] : moduleClients;
        return Promise.all([
            this.dispatch(getInstallationStatusAction()),
            this.dispatch(getPendingErrorAction()),
            ...clients.map(Client => new Client(this.address, this.dispatch).init(args))
        ]).then(() => {
            this.startEventMonitor();
        });
    }

    startEventMonitor () {
        this._subscription = this.client.subscribe(
            { },
            (path, iface, signal, args) => {
                if (signal === "PropertiesChanged" &&
                    args[0] === INTERFACE_NAME) {
                    if (Object.hasOwn(args[1], "InstallationStatus")) {
                        debug("Boss: InstallationStatus property changed");
                        this.dispatch(getInstallationStatusAction());
                    }
                    if (Object.hasOwn(args[1], "ActiveInstallationTask") &&
                        args[1].ActiveInstallationTask.v) {
                        installationState.active = true;
                        for (const Client of moduleClients) {
                            Client.instance?.stopEventMonitor();
                        }
                    }
                }
            }
        );
    }

    stopEventMonitor () {
        this._subscription?.remove();
    }
}

/**
 * @param {string} task         DBus path to a task
 *
 * @returns {Promise}           Resolves the total number of tasks
 */
export const getSteps = ({ task }) => {
    return new BossClient().client.call(
        task,
        "org.freedesktop.DBus.Properties",
        "Get",
        ["org.fedoraproject.Anaconda.Task", "Steps"]
    )
            .then(ret => ret[0]);
};

/**
 * @returns {Promise}           Resolves the object path of the active installation task, or ""
 */
export const getActiveInstallationTask = () => {
    return _getProperty(BossClient, OBJECT_PATH, INTERFACE_NAME, "ActiveInstallationTask");
};

/**
 * @returns {Promise}           Resolves a list of tasks
 */
export const installWithTasks = () => {
    return callClient("InstallWithTasks", []);
};

/**
 * @param {string} locale       Locale id
 */
export const setLocale = ({ locale }) => {
    return callClient("SetLocale", [locale]);
};

/**
 * @returns {Promise}           Resolves the installation status enum value
 */
export const getInstallationStatus = () => {
    return _getProperty(BossClient, OBJECT_PATH, INTERFACE_NAME, "InstallationStatus");
};

/**
 * @returns {Promise}           Resolves the pending error message, or ""
 */
export const getPendingErrorMessage = () => {
    return _getProperty(BossClient, OBJECT_PATH, INTERFACE_NAME, "PendingErrorMessage");
};

/**
 * @returns {Promise}           Resolves the pending error type
 */
export const getPendingErrorType = () => {
    return _getProperty(BossClient, OBJECT_PATH, INTERFACE_NAME, "PendingErrorType");
};