Os pacotes @igrp/framework-process-studio-client, -types e -bpmn-editor — criar o cliente, métodos por recurso, erros e o editor BPMN em React.
O frontend do Process Studio fala com o Process Studio API através de três pacotes, que também podes usar numa app tua (por exemplo, para listar ou publicar processos a partir de outro sistema):
| Pacote | Versão | Para quê |
|---|---|---|
@igrp/framework-process-studio-client | 0.0.1-beta.16 | Cliente HTTP: projetos, definições, diagrama, publicação |
@igrp/framework-process-studio-types | 0.0.1-beta.16 | Tipos partilhados (Project, ProcessDefinition, filtros, paginação) |
@igrp/framework-process-studio-bpmn-editor | 0.1.0-beta.13 | Editor BPMN (bpmn-js + painel de propriedades) como componente React |
Os pacotes estão no registry @igrp (Sonatype) e são ESM, para Node ≥ 20.
pnpm add @igrp/framework-process-studio-client @igrp/framework-process-studio-typesCriar o cliente
import { createProcessStudioClient } from "@igrp/framework-process-studio-client";
const client = createProcessStudioClient({
baseUrl: process.env.API_GATEWAY ?? "", // rota do gateway, SEM /api/v1
timeout: 30000, // ms (default 30000)
headers: { Authorization: `Bearer ${accessToken}` },
});| Opção | Notas |
|---|---|
baseUrl | Rota do gateway do process-studio-api (http://<slug>:2575/gateway-api/<serviço>-process-studio-api). O /api/v1 já faz parte dos caminhos. Vazio → usa NEXT_PUBLIC_API_GATEWAY. |
timeout | Cancela o pedido (AbortController). |
headers | Junta-se a Content-Type/Accept: application/json. A autenticação vai aqui — não há opção token. |
No frontend do Process Studio o cliente é criado no servidor, por pedido, com o token da sessão NextAuth, e as server actions são consumidas no browser via TanStack Query:
// lib/server-client.ts
import { createProcessStudioClient } from "@igrp/framework-process-studio-client";
import { serverSession } from "@/lib/auth";
export async function createServerClient() {
const session = await serverSession();
if (!session?.accessToken) throw new Error("Authentication required.");
return createProcessStudioClient({
baseUrl: process.env.API_GATEWAY ?? "",
headers: { Authorization: `Bearer ${session.accessToken}` },
});
}O cliente regista cada pedido na consola
Nesta versão, cada pedido faz console.log do URL e das opções — incluindo o cabeçalho Authorization. Usa-o no servidor e não em produção com logs partilhados até isto ser removido.
Métodos
client.projects
| Método | Pedido | Devolve |
|---|---|---|
getAll(filter?) | GET /api/v1/projects?appCode&pageNumber&pageSize | PaginatedResponse<Project> |
getById(id) | GET /api/v1/projects/{id} | Project |
create(project) | POST /api/v1/projects | Project |
update(project) | PUT /api/v1/projects/{projectId} | Project |
createOrUpdate(project) | PUT se tiver projectId, senão POST | Project |
enable(id) · disable(id) | PATCH …/{id}/enable · /disable | — |
delete(id) | igual a disable (apagamento lógico) | — |
getDeployedProcesses(id, filter?) | GET /api/v1/projects/{id}/deployed-process | Project com os processos publicados |
getProcessHistory(id, filter?) | GET /api/v1/projects/{id}/history-process | Project |
O corpo de create/update é { code, name, description, appCode }.
client.processDefinitions
| Método | Pedido | Devolve |
|---|---|---|
list(filter?) | GET /api/v1/projects/process-definitions | PaginatedResponse<ProcessDefinition> |
getAll() | lista os 100 primeiros projetos e junta os processos | ProcessDefinition[] |
getById(id) | GET …/process-definitions/{id} | ProcessDefinition (com bpmFileContent) |
create(projectId, pd) | POST /api/v1/projects/{projectId}/process-definitions | ProcessDefinition |
update(processId, pd) | PUT …/process-definitions/{processId} | ProcessDefinition |
createOrUpdate(pd) | conforme processDefinitionId | ProcessDefinition |
saveDiagram(processKey, { content }) | PUT …/process-definitions/{processKey}/diagram | ProcessDefinition |
deploy(processKey, { content }) | POST …/process-definitions/{processKey}/deploy | ProcessDefinition |
getVariables(id) · createOrUpdateVariable(id, vars) | GET · POST …/process-definitions/{id}/variables | VariableDefinition[] |
delete(id) · restore(id) | PATCH …/{id}/delete · /restore | — |
getStates() | GET /parameterization/process-definition-state | EnumItem[] |
O corpo de create/update é { title, processKey, description, projectId, status }. Os artefactos (User Tasks e Form Keys) chegam só em leitura, em ProcessDefinition.processArtifacts.
Namespace do XML
O editor trabalha em camunda; o motor guarda em activiti. O frontend do Process Studio converte o conteúdo (convertCamundaToActiviti) antes de saveDiagram e deploy. Se chamares o cliente diretamente, envia o XML já com activiti:formKey.
Exemplo — publicar um processo
"use server";
import { createServerClient } from "@/lib/server-client";
export async function publish(processKey: string, xml: string) {
const client = await createServerClient();
try {
await client.processDefinitions.saveDiagram(processKey, { content: xml });
return await client.processDefinitions.deploy(processKey, { content: xml });
} catch (e) {
const err = e as { status?: number; details?: { message?: string } };
throw new Error(err.details?.message ?? `Falha ao publicar (HTTP ${err.status})`);
}
}Erros
Qualquer falha lança um ApiClientError com:
| Campo | Conteúdo |
|---|---|
message | HTTP Error: <status> <statusText> |
status | Código HTTP; 0 em falha de rede ou timeout |
details | Corpo da resposta (JSON ou texto) |
A classe não é exportada: lê status e details do erro, como no exemplo acima.
Ajudantes
| Função | O que faz |
|---|---|
formatDeploymentDate(valor) | Data de publicação (ISO, array LocalDateTime do Java ou texto) → dd/MM/yyyy HH:mm |
formatAuthorSuffix(user) | " por <nome>" ou vazio |
decorateProcessDefinitionRow(pd) | Linha pronta para tabela (versão N/D, datas e autores formatados) |
filterProcessDefinitionsBySubstring(rows, filtros) | Filtro local por processKey, processName (título) e projectName |
convertToMapOptions(items, nome?, valor?) | Lista → { label, value }[] para selects |
Tipos
@igrp/framework-process-studio-types exporta, entre outros:
| Tipo | Campos principais |
|---|---|
Project | projectId, code, name, description, appCode, active, processDefinitions[] |
ProcessDefinition | processDefinitionId, processKey, title, status, statusDesc, version, deploymentDate, bpmFileContent, processArtifacts[] |
ProcessArtifact | taskKey, name, formKey, artifactVariables[] |
ProcessDefinitionState | DRAFT · VALIDATED · PUBLISHED · DEPRECATED · ARCHIVED · DISABLED · ERROR · DELETED |
ProcessDefinitionFilter | appCode, processKey, processName, projectCode, projectName, state, pageNumber, pageSize |
PaginatedResponse<T> | content, pageNumber, pageSize, totalElements, totalPages, first, last |
Editor BPMN
pnpm add @igrp/framework-process-studio-bpmn-editorExporta IGRPBpmnModeler, IGRPBpmnControls e IGRPZoomControls. O modeler já inclui a barra de controlos e o zoom.
"use client";
import { IGRPBpmnModeler } from "@igrp/framework-process-studio-bpmn-editor";
export function Editor({ pd, onSave }: { pd: ProcessDefinition; onSave: (xml: string) => void }) {
return (
<IGRPBpmnModeler
processKey={pd.processKey}
processName={pd.title}
xml={pd.bpmFileContent ?? ""}
onChange={onSave} // chamado a cada alteração no canvas
/>
);
}| Prop | Notas |
|---|---|
processKey, processName | Obrigatórios; com xml vazio, criam o diagrama inicial |
xml | Diagrama a carregar; o editor ignora o eco do seu próprio onChange |
onChange(xml) | A cada alteração (commandStack.changed) e ao criar o diagrama inicial |
onLoad(modeler) | Acesso à instância bpmn-js |
className | Altura por defeito calc(100dvh - 13rem), mínimo 480 px |
Requisitos:
-
Peers:
next15,next-auth4,react/react-dom19; usa botões e toasts do Design System (precisa do provider de toasts na app). -
Estilos: o CSS do bpmn-js vem com o componente; as classes Tailwind não. Acrescenta ao CSS global da app:
@source "../../node_modules/@igrp/framework-process-studio-bpmn-editor/dist/**/*.{js,mjs}"; -
Uma instância por página — o painel de propriedades usa um id fixo (
#js-properties-panel). -
Segue o tema escuro pela classe
.darkno<html>.
Comportamentos desta versão
- Carregar um ficheiro pelo botão de upload não chama
onChange: grava o diagrama depois de uma alteração no canvas. - O diagrama inicial (
xmlvazio) começa por?xmlem vez de<?xml; o servidor rejeita-o ao guardar. Passa sempre um XML válido ou desenha antes de guardar.
Última atualização:
