Minimal Cockpit "history plugins" for Camunda >= 7.14.0

My thought now is to send a request to /history/process-instance?startedBy={ insert Current User Id Here} to get the process-instance then start from there.

Sounds possible. Once you have the processInstanceId from task details, you should be able to re-use: camunda-cockpit-plugins/src/instance-historic-activities.tsx at master · datakurre/camunda-cockpit-plugins · GitHub

That was easier than I thought. If you checkout the latest version of my project, enter that directory and start Docker with (on MacOS or Linux):

docker run --rm -p 8082:8080 -v $(pwd):/camunda/webapps/camunda/app/cockpit/scripts/:ro -v $(pwd)/tasklist-config.js:/camunda/webapps/camunda/app/tasklist/scripts/config.js:ro -v $(pwd)/tasklist-audit-log.js:/camunda/webapps/camunda/app/tasklist/scripts/tasklist-audit-log.js:ro  camunda/camunda-bpm-platform:7.15.0

You have both the cockpit plugins and the tasklist plugin.

Links from tasklist Audit Log to processes or decisions are still broken, because AuditLog-component expect to be already inside cockpit, but those should not be too hard…

@LevinHelix You probably want to customize the audit log columns for you use case, so just clone / fork the project and customize as you wish. I might make the component more configurable to have simpler tab for the task list than for the cockpit, but not any time too soon. (This initial implementation was fast, because I had not thought before your post about how easy the reuse could be.)

2 Likes

And those links from task audit log to cockpit are now fixed. Could be prettier, but works for now. Later I need to update documentation to take task list into account and figure out, what I do with the bad naming for the project…

OMG this is lighting response speed. I will definitely try this out. Much appreciated.

1 Like

Hello @datakurre, would it be possible to fix the case when decisions map is not available?

And one more question: Did you (or someone else) test your plugins - especially the statistics - on a cockpit having running millions of processes?

Could you elaborate more. What do you mean with “decision map” and when it is not available?

Unfortunately, it is not even close what you seem to expect. I recall, it currently fetches 100 latest events for each version of the process definition in question. That’s well enough to help when developing and testing a process, but obviously not for monitoring high volume production data.

There will be interactive filter for managing the statics query, but that branch is still incomplete and currently I have other priorities.

Even then, a frontend plugin like this can never analyze statistics for millions of process executions. In theory, a database specific process engine add-on could specify periodically updated database statistic tables with custom REST API endpoints to expose them to a cockpit plugin, but in practice, it is simpler to export the data into an external out-of-the-box BI solution.

“Could you elaborate more. What do you mean with “decision map” and when it is not available?”

new Map(decisions.map(function (decision) { return [decision.activityInstanceId, decision.id]; }));

It is not available if the DMN engine is not enabled.
I get a HTTP status code 500 for /decision-instance , /decision-definition and /decision-requirements-definition

“Unfortunately, it is not even close what you seem to expect.”

I just expect that the plugins don’t break the cockpit (by making it unusable slow) if there are millions of instances.

:+1: Exactly, why it make a such limited query until there is UI for configuring the filter.

Thanks for explaining. I agree the plugin should be fixed to not break when decision engine is not enabled. I’ll make this into an issue to remember to fix this.

2 Likes

Hi @Bernd

I tried to copy all the files (as @datakurre suggested) into ./src/main/resources/META-INF/resources/webjars/camunda/app/cockpit/scripts/camunda-cockpit-plugins
and ./src/main/resources/META-INF/resources/webjars/camunda/app/cockpit/scripts/
of my Spring Boot App.

What did exactly work for you?

Hi datakurre, @datakurre ,
I have some novice question to ask here.
I am writing a plugin for tasklist in plug-in point : tasklist.list.
my code is something like this:

const host = "http://localhost:8080"

export default {
    id: 'tasklist.myprocess',
    pluginPoint: 'tasklist.list',
    priority: -1,
    render: (node, {api}) => {

        const data = { startedBy: 'demo'}

        console.log("api", api);
        fetch(api.engineApi + "/history/process-instance", {
            method: 'post',
            body: JSON.stringify(data),
            headers: {
                "Accept": "application/json",
                "Content-Type": "application/json",
                "X-XSRF-TOKEN": api.CSRFToken,
            }
        })
            .then(async  (response) => await response.json())
            .then(async (data) => {
                const myProcessContainer = document.createElement("div");
                const banner = document.createElement("h5");
                banner.innerText = "Process created by me";
                myProcessContainer.append(banner);

                //console.log("Success:", data)
                for(let i = 0; i < data.length; i++){
                    const obj = data[i];
                    console.log(obj);
                    let process_item_div = document.createElement("div");
                    let process_item_a = document.createElement("a");

                    process_item_a.setAttribute('id', obj['id']);
                    process_item_a.innerText = obj['processDefinitionKey'] + " " + obj['startTime'];
                    process_item_a.href = host + "/camunda/app/cockpit/default/#/history/process-instance/" + obj['id']

                    process_item_div.append(process_item_a)

                    myProcessContainer.append(process_item_div);
                }
                node.innerHTML = myProcessContainer.innerHTML;
            })
            .catch((error) => {
                console.error('Error', error);
            });

    },
    properties: {
        label: 'My process instance'
    }
}

I want to know if I can get the current userId somehow and put it in my request body. so that I can get process-instance of current user from history.

I am not aware of any public API for that. But you can try to do any request (possibly even to dummy endpoints like /engine or /version) and check if the response headers include X-Authorized-User.

Sadly, I can’t find a dummy endpoint that have X-Authorized-User returned yet.

Then you need to make a dummy call to an endpoint that uses authorization.

I tried to use this one Perform an Authorization Check | docs.camunda.org
but I need to provide the userId to use it. . .

Any endpoint that uses authorization to filter its responses should return X-Authorized-User. Like that /history/process-instance?maxResults=1. But it might require that authorization is enabled for the REST API in general :thinking:

Well guess it can work. but the code is kind of messy. Hope there is an easier way. I will paste my plugin-code here. I have to write a fetch inside the then of the dummy fetch like this:

dummy-fetch(…).then(
true-fetch(…)
)

code:

const host = "http://localhost:8080"

export default {
    id: 'tasklist.myprocess',
    pluginPoint: 'tasklist.list',
    priority: 9999,
    render: (node, {api}) => {

        let userId = "";
        //console.log("api", api);
        fetch(api.engineApi + "/history/process-instance?maxResults=1",{
            method: 'get',
            headers: {
                "Accept": "application/json",
                "Content-Type": "application/json",
                "X-XSRF-TOKEN": api.CSRFToken,
            }
        })
            .then(
                (response) => {
                    userId = response.headers.get('X-Authorized-User');
                    let data = {};
                    data.startedBy = userId;
                    console.log(data);

                    fetch(api.engineApi + "/history/process-instance", {
                        method: 'post',
                        body: JSON.stringify(data),
                        headers: {
                            "Accept": "application/json",
                            "Content-Type": "application/json",
                            "X-XSRF-TOKEN": api.CSRFToken,
                        }
                    })
                        .then((response) => response.json())
                        .then((data) => {
                            const myProcessContainer = document.createElement("div");
                            const banner = document.createElement("h5");
                            banner.innerText = "Process created by me";
                            myProcessContainer.append(banner);

                            //console.log("Success:", data)
                            for(let i = 0; i < data.length; i++){
                                const obj = data[i];
                                console.log(obj);
                                let process_item_div = document.createElement("div");
                                let process_item_a = document.createElement("a");

                                process_item_a.setAttribute('id', obj['id']);
                                process_item_a.innerText = obj['processDefinitionKey'] + " " + obj['startTime'];
                                process_item_a.href = host + "/camunda/app/cockpit/default/#/history/process-instance/" + obj['id']

                                process_item_div.append(process_item_a)

                                myProcessContainer.append(process_item_div);
                            }
                            node.innerHTML = myProcessContainer.innerHTML;
                        })
                        .catch((error) => {
                            console.error('Error', error);
                        });
                }
            )
            .catch((error) => {
                console.error('Error', error);
            });

    },
    properties: {
        label: 'My process instance'
    }
}

If you don’t need to support old browsers (or are able to eventually use some transpiling for your code), you may use async ... await syntax by wrapping your code as async funtion…

(async () {
  let response;
  response = await fetch ...;
  response = await fetch ...;
  ...
})();
1 Like

Hi @pme123 ,

I’m try to use this plugin with Spring Boot.

I made this steps to works:

  1. Exclude transient dependency camunda-webapp-webjar from camunda-bpm-spring-boot-starter-webapp. In gradle:

     implementation('org.camunda.bpm.springboot:camunda-bpm-spring-boot-starter-webapp:7.15.0') {
             exclude module: 'camunda-webapp-webjar'
         }
    
  2. Copy all content of folder META-INF of the camunda-webapp-webjar to your project in src/main/resources/META-INF/.

  3. Override the content of resources/META-INF/resources/webjars/camunda/app/cockpit/scripts with js files of camunda-cockpit-plugins.

I hope this help you and others with same problem

2 Likes

Hi @cjcalmeida, THANKS that works!