Navigation in the cockpit is disappearing after any application is added to it

Product/components used and version/fix level:

Cockpit version 1018.0.240

Detailed explanation of the problem:

Navigation in the cockpit is disappearing after any extension is added to it using Administration->Ecosystem->Extensions

Error messages / full error message screenshot / log file:

No error message are produced by the application

Question related to a free trial, or to a production (customer) instance?

1 Like

Hi @Piotr_Chabros,

to further look into this, we would need to know if this was the stock cockpit application how it is shipped by Cumulocity, or if your cockpit application contains any e.g. A1 specific customizations?
Please also let us know which extensions you have installed.

Regards,
Tristan

Hi @Tristan_Bastian ,

thank you for your reply.

I am using a modified version of the cockpit with custom NavigatorNodeFactory implementation. This modification changes the structure of the navigation. It does not produce any errors. The cockpit not have any problems with displaying the navigation on another tenant. This problem occurs just on one tenant.

HI @Piotr_Chabros,

can you provide the sources of your custom NavigatorNodeFactory?
Have you checked if the issue exists also without your custom NavigatorNodeFactory?

Hi @Tristan_Bastian,

I have checked and the issue still exists without my custom NavigatorNodeFactory.

Hi @Piotr_Chabros,

we are not able to support you, if you are not providing the information we are requesting.
We would need to know which extensions you tried to install in order to check what might be causing the issue.

Iā€™m still guessing that your custom NavigatorNodeFactory might be causing the issue, so it would help if you could provide the sources of that.

@Tristan_Bastian,

the issue still exists without the custom NavigatorNodeFactory. I have removed it from the cockpit and redeployed it, and the navigation is still not displaying.

I am not sure why you need the sources if they have no impact on this problem:

import {Injectable} from '@angular/core';
import {IManagedObject, InventoryService} from '@c8y/client';
import {NavigatorNode, NavigatorNodeFactory} from '@c8y/ngx-components';

@Injectable({ providedIn: 'root' })
export class CustomNavigatorNodesFactory implements NavigatorNodeFactory {
    private nodes: NavigatorNode[];

    constructor(private inventory: InventoryService) {}

    async get(): Promise<NavigatorNode | NavigatorNode[]> {
        if (this.nodes) {
            return this.nodes;
        }

        this.nodes = [];

        const groups = await this.getGroups();
        const dynamicGroups = await this.getDynamicGroups();

        for (let dynamicGroup of dynamicGroups) {
            if (dynamicGroup.c8y_AutoSmartGroupDashboard) {
                dynamicGroups.splice(dynamicGroups.indexOf(dynamicGroup, 1));
                this.nodes.push(new NavigatorNode({
                    label: 'Analytics',
                    path: 'group/' + dynamicGroup.id,
                    icon: 'area-chart',
                    routerLinkExact: false
                }));
            }
        }

        groups.push(...dynamicGroups);

        let groupNodes: NavigatorNode[] = [];

        for (let group of groups) {

            const groupNode = new NavigatorNode({
                label: group.name,
                path: 'group/' + group.id,
                icon: 'group',
                routerLinkExact: false
            });

            await this.addChildren(group, groupNode);

            groupNodes.push(groupNode);
        }


        const allChildrenNodes = this.flatMap(groupNodes.map(node => node.children));
        const allChildren: NavigatorNode[] = [];
        allChildrenNodes.forEach((child: NavigatorNode) => {
            this.getChildrenNodes(child, allChildren);
        })
        const allChildrenNodesIds = allChildren.map(node => node.path.split('/')[1]);

        let indexesToRemove = [];

        for (let node of groupNodes) {
            if (allChildrenNodesIds.includes(node.path.split('/')[1])) {
                indexesToRemove.push(groupNodes.indexOf(node, 1));
            }
        }

        for (let index of indexesToRemove) {
            groupNodes.splice(index, 1);
        }

        indexesToRemove = [];

        for (let node of groupNodes) {
            if (this.findNodeInChildren(node.path.split('/')[1], allChildrenNodes)) {
                indexesToRemove.push(groupNodes.indexOf(node));
            }
        }

        for (let index of indexesToRemove) {
            groupNodes.splice(index, 1);
        }

        const groupsNode = new NavigatorNode({
            label: 'Groups ',
            path: 'group',
            icon: 'group',
            routerLinkExact: false
        })

        this.nodes.push(groupsNode);

        groupNodes.forEach(groupNode => {
            groupsNode.add(groupNode);
        });

        return this.nodes;
    }

    async getGroups(): Promise<IManagedObject[]> {
        const { data: groups } = await this.inventory.list({
          pageSize: 2000,
          withTotalPages: true,
          query: 'has(c8y_IsDeviceGroup)'
        });
        return groups;
    }

    async getDynamicGroups(): Promise<IManagedObject[]> {
        const { data: groups } = await this.inventory.list({
            pageSize: 2000,
            withTotalPages: true,
            query: 'has(c8y_IsDynamicGroup)'
        });
        return groups;
    }

    async addChildren(group: IManagedObject, parentNode: NavigatorNode) {
        for (let reference of group.childAssets.references) {
            const child = await this.inventory.detail(reference.managedObject.id);

            if (child.data.c8y_IsDeviceGroup) {
                const node = new NavigatorNode({
                    label: child.data.name,
                    path: 'group/' + child.data.id,
                    icon: 'group',
                    routerLinkExact: false
                });

                this.addChildren(child.data, node);

                parentNode.add(node);
            } else {
                parentNode.add(new NavigatorNode({
                    label: child.data.name,
                    path: 'device/' + child.data.id,
                    routerLinkExact: false
                }));
            }
        }
    }

    getChildrenNodes(parentNode: NavigatorNode, children: NavigatorNode[]) {
        if (parentNode.children && parentNode.children.length) {
            parentNode.children.forEach(child => {
                this.getChildrenNodes(child, children);
            });
        }
        if (!children.includes(parentNode)) {
            children.push(parentNode);
        }
    }

    findNodeInChildren(idToFind: string, nodes: NavigatorNode[]) {
        if (nodes.length === 0) {
            return false;
        }

        if (nodes.find(node => node.path.split('/')[1] === (idToFind))) {
            return true;
        }

        return this.findNodeInChildren(idToFind, this.flatMap(nodes.map(node => node.children)));
    }

    flatMap(array: NavigatorNode[][]) {
        const result: NavigatorNode[] = [];
        for (let arrayElement of array) {
            for (let arrayElement1 of arrayElement) {
                result.push(arrayElement1);
            }
        }
        return result;
    }

}