Source: widget/help/catalogues/SEN.js

(function() {
    /**
     * @typedef {Object} Help4.widget.help.catalogues.SEN.CatalogueUrlConfig
     * @property {string} serverBaseUrl - base URL of the content server
     * @property {string} pubUrl - URL extension to public end-user content
     * @property {string} headUrl - URL extension to non-public author only content
     */

    /**
     * @typedef {Object} Help4.widget.help.catalogues.SEN.CatalogueProject
     * @property {string} [alias] - alternative project name; internally added
     * @property {string} appUrl - screen ID
     * @property {string} [clone_src] - clone source of project
     * @property {string} [cloneSrc] - reduced clone source of project; internally added
     * @property {string|Array<*>} conditions - possible conditions; stringified JSON or array
     * @property {Help4.widget.help.ContextTypes} contextType - context type
     * @property {boolean} hidden - whether project is hidden for end-users
     * @property {string} id - project ID
     * @property {string} locale - project language
     * @property {string} loio - project LOIO (LOgical Info Object)
     * @property {number|string} [maxversion] - highest version of project
     * @property {string} modification_time - time of last modification
     * @property {string} product - context information
     * @property {Help4.widget.help.PUBLISHED_STATUS} [published] - project published information
     * @property {string} system - context information
     * @property {string} title - title of the project
     * @property {string} version - context information
     * @property {number} [wt] - write token information
     * @property {string} [wt_location] - write token information
     * @property {string} [wt_owner] - write token information
     * @property {Help4.widget.help.CatalogueTypes} [_catalogueType] - SEN, SEN2, UACP, ...; internally added
     * @property {Help4.widget.help.DataTypes} [_dataType] - SEN, UACP, ...; internally added
     */

    /**
     * @typedef {Object} Help4.widget.help.catalogues.SEN.CatalogueProjects
     * @property {Help4.widget.help.catalogues.SEN.CatalogueProject[]|null} pub - public catalogue projects for end-users
     * @property {Help4.widget.help.catalogues.SEN.CatalogueProject[]|null} [head] - non-public catalogue projects for authors
     */

    /**
     * this backend connector is able to handle SEN catalogue content for a RO model configuration
     * - in case of SEN config: will download published and head data
     * - in case of EXT config: will only download published data
     */
    Help4.widget.help.catalogues.SEN = class {
        /** @returns {Help4.widget.help.DataTypes} */
        static getDataType() {
            return 'SEN';
        }

        /**
         * @param {Help4.typedef.SystemConfiguration} config - the system configuration
         * @returns {Promise<Help4.widget.help.catalogues.SEN.CatalogueProjects|null>}
         */
        static async load(config) {
            const {wpb, sen, ext} = Help4.SERVICE_LAYER;
            const {roModel, serviceUrl} = config.help;
            if (!serviceUrl || roModel !== wpb && roModel !== sen) return null;

            const {help: {serviceLayer}, core: {editor}} = config;
            const {serverBaseUrl, pubUrl, headUrl} = /** @type {Help4.widget.help.catalogues.SEN.CatalogueUrlConfig} */ this._getUrls(config, serviceUrl);

            // important: in case of EXT - always load and use published data for RO source!
            const {SEN} = Help4.widget.companionCore;
            if (editor && serviceLayer !== ext) {
                // editor: load PUB and HEAD
                /** @type {Array<{url: string}>} */ const request = [pubUrl, headUrl].map(url => ({url}));
                const [
                    /** @type {Array<?Help4.widget.companionCore.SEN.CatalogueResponse>} */ pub,
                    /** @type {Array<?Help4.widget.companionCore.SEN.CatalogueResponse>} */ head
                ] = await SEN.doMultifileRequest(config, {serverBaseUrl, request}) || [];

                /** @type {Help4.widget.help.catalogues.SEN.CatalogueProject[]|null} */ const pubProjects = this._parse(pub);
                /** @type {Help4.widget.help.catalogues.SEN.CatalogueProject[]|null} */ const headProjects = this._parse(head);
                return {pub: pubProjects, head: headProjects};
            } else {
                // non-editor or EXT: load PUB only
                /** @type {?Help4.widget.companionCore.SEN.CatalogueResponse} */
                const pub = await SEN.doGetRequest(serverBaseUrl + pubUrl);

                /** @type {Help4.widget.help.catalogues.SEN.CatalogueProject[]|null} */ const pubProjects = this._parse(pub);
                return {pub: pubProjects};
            }
        }

        /**
         * @param {?Help4.widget.companionCore.SEN.CatalogueResponse} serverResponse - the server response
         * @param {Help4.widget.help.CatalogueTypes} [catalogueType = 'SEN']
         * @returns {Help4.widget.help.catalogues.SEN.CatalogueProject[]|null}
         * @protected
         */
        static _parse(serverResponse, catalogueType = 'SEN') {
            /**
             * @param {Help4.widget.help.catalogues.SEN.CatalogueProject} project - catalogue project data
             * @returns {Help4.widget.help.catalogues.SEN.CatalogueProject}
             */
            const normalize = project => {
                const {clone_src, contextType, id, maxversion, published} = project;

                project._catalogueType = catalogueType;
                project._dataType = 'SEN';

                project.loio ||= id;
                project.alias = project.loio;  // XRAY-3406

                if (contextType === 'TOUR' && clone_src) project.cloneSrc = clone_src.split('!')[1];

                const {PUBLISHED_STATUS} = Help4.widget.help;
                const pub = Number(published);
                const max = Number(maxversion);
                project.published = isNaN(max)
                    ? PUBLISHED_STATUS.invalid
                    : isNaN(pub) ? PUBLISHED_STATUS.new : (pub === max ? PUBLISHED_STATUS.published : PUBLISHED_STATUS.updated);

                return setDefaults(project);
            }

            /**
             * @param {Help4.widget.help.catalogues.SEN.CatalogueProject} project - project data
             * @returns {Help4.widget.help.catalogues.SEN.CatalogueProject}
             */
            const setDefaults = project => {
                for (const [key, {d, f, t, wpb}] of Object.entries(Help4.PROJECT_DEFAULTS)) {
                    const value = project[wpb || key];

                    if (value === undefined) {
                        project[key] = f;
                        continue;
                    }

                    if (t === 'json') {
                        try {
                            project[key] = Help4.JSON.parse(value);
                        } catch (e) {
                            project[key] = f;
                        }
                    } else {
                        project[key] = value;
                    }
                }

                return project;
            }

            const {status, response} = serverResponse || {};
            return !status && Help4.isArray(response) && response.length
                ? response.map(catalogueProject => normalize(catalogueProject))
                : null;
        }

        /**
         * @param {Help4.typedef.SystemConfiguration} config - the system configuration
         * @param {string} serviceUrl - the server base URL
         * @returns {Help4.widget.help.catalogues.SEN.CatalogueUrlConfig}
         * @protected
         */
        static _getUrls(config, serviceUrl) {
            const {SEN} = Help4.widget.companionCore;
            const {WHATSNEW_SCREEN_ID} = Help4.widget.help.CatalogueBackend;

            const query = {};
            const {playbackTag, screenId} = config.core;
            query.locale = SEN.getLanguages(config);
            query.appUrl = [screenId, screenId + WHATSNEW_SCREEN_ID];
            ['product', 'version', 'system', 'solution'].forEach(key => query[key] = config.core[key]);
            const url = '/.catalogue?' + encodeURIComponent(Help4.JSON.stringify(query));

            const serverBaseUrl = SEN.getServerBaseUrl(serviceUrl);
            const urlSuffix = serviceUrl.substring(serverBaseUrl.length);

            const pubUrl = SEN.createPubUrl({serverUrl: urlSuffix, tag: playbackTag}) + url;
            const headUrl = urlSuffix + url;
            return {serverBaseUrl, pubUrl, headUrl};
        }
    }
})();