From 2e3ed338b56b2c15a346118fd8788063c4cee7c0 Mon Sep 17 00:00:00 2001 From: Gemini Date: Fri, 3 Jul 2026 09:18:25 -0300 Subject: [PATCH] =?UTF-8?q?release:=202.0.0=20=E2=80=94=20motor=20de=20tem?= =?UTF-8?q?plates=20de=20payload=20(produto=20ambiente-agn=C3=B3stico)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - bump 2.0.0 (final) + CHANGELOG datado - docs/TEMPLATES.md (guia do usuário) + seção no README - docs/API.md: seção do motor de templates (componentes, precedência, descoberta) Retrocompatível: sem template ativo, comportamento idêntico ao 1.x. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 2 +- README.md | 1 + docs/API.md | 31 ++++++++++++++ docs/TEMPLATES.md | 106 ++++++++++++++++++++++++++++++++++++++++++++++ setup.php | 2 +- 5 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 docs/TEMPLATES.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 0948f45..4165beb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Todas as mudanças relevantes deste plugin são documentadas aqui. O formato segue [Keep a Changelog](https://keepachangelog.com/pt-BR/1.0.0/) e o versionamento segue [SemVer](https://semver.org/lang/pt-BR/). -## [2.0.0-dev] — em desenvolvimento (branch main; estável = stable/1.x) +## [2.0.0] - 2026-07-03 ### Adicionado - **Motor de templates de payload** (Fase 1, invisível na UI — ver `docs/DESIGN-2.0.md`): diff --git a/README.md b/README.md index e32b6a6..804afda 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ TicketTask "Feito" ──────────────────── - ✅ **Observadores padrão** configuráveis nas issues. - ✅ Chave de API **criptografada** em repouso (GLPIKey). - ✅ Interface 100% nativa do GLPI 11 (componentes Twig). +- ✅ **Templates de Payload (2.0)** — personalize o JSON enviado ao Redmine em cada operação, com **variáveis do GLPI** e **descoberta automática dos campos do seu Redmine** (inclusive campos personalizados obrigatórios). Editor visual + testador embutido. Veja [`docs/TEMPLATES.md`](docs/TEMPLATES.md). ## Requisitos diff --git a/docs/API.md b/docs/API.md index f0d7097..bde8ddd 100644 --- a/docs/API.md +++ b/docs/API.md @@ -280,3 +280,34 @@ Content-Type: application/json 3. (1ª vez) `POST /issues.json` cria a issue — autor = técnico atribuído (impersonation + auto-membership). 4. `POST /time_entries.json` lança as horas — autor = quem lançou a task. 5. Ao mudar o status do chamado, `PUT /issues/{id}.json` reflete no Redmine. + +--- + +## Motor de Templates de Payload (2.0) + +A partir da 2.0, o corpo de cada operação pode vir de um **template** editável em +vez de ser montado no código. Guia do usuário: [`TEMPLATES.md`](TEMPLATES.md). + +### Componentes internos +- **`PluginRedmineTemplateengine`** (`inc/templateengine.class.php`): armazenamento + (`glpi_plugin_redmine_templates`), `defaultTemplate()`/`mergeDiscoveredCustomFields()` + (seeds), `variableCatalog()` e `fieldDictionary()` (paleta "DE" e dicionário "PARA"), + `buildContext()`/`buildFormContext()`/`sampleContext()` (contextos), e o renderizador + `render()` (tags mustache-like, tipo nativo p/ tag inteira, filtros, `prune` de nulos). +- **`PluginRedmineRedmineapi::createIssueFromPayload()` / `logTimeFromPayload()`**: + enviam um payload já renderizado (com fallback de impersonation → admin). +- **`PluginRedmineRedmineapi::rawRequest()`**: requisição crua (código+corpo) usada + pelo testador — o erro **é** a informação. +- **`ajax/template_test.php`**: `action=preview` (render com `sampleContext`) e + `action=dryrun` (cria+apaga objeto de teste, traduz 422 em dicas). + +### Precedência (motor 2.0 × 1.x) +Em cada operação do hook/controllers: +1. Existe template **ativo** para a operação? Renderiza e envia esse payload. +2. Falha no **render** (JSON/tag inválidos)? Loga e **cai no comportamento 1.x**. +3. Sem template ativo? Comportamento **1.x** (idêntico às versões anteriores). + +### Descoberta de campos +`GET /custom_fields.json` (admin) é cacheado por `syncMetadata()` — os campos +personalizados de `project`, `issue` e `time_entry` alimentam tanto o seed do +"Gerar template" (só os **obrigatórios**) quanto o dicionário "PARA" (todos). diff --git a/docs/TEMPLATES.md b/docs/TEMPLATES.md new file mode 100644 index 0000000..fd0756d --- /dev/null +++ b/docs/TEMPLATES.md @@ -0,0 +1,106 @@ +# Templates de Payload (2.0) + +A partir da versão 2.0, o plugin deixa de ter o corpo das requisições ao Redmine +"chumbado" no código: cada operação passa a ter um **template de payload** editável, +para que o plugin se adapte às **peculiaridades de cada Redmine** (campos +personalizados obrigatórios, campos extras, textos fixos) sem precisar de alteração +de código. + +> **Retrocompatível:** sem template **ativo**, o plugin se comporta **exatamente** +> como nas versões 1.x. Você adota os templates quando (e se) quiser. + +## Onde fica + +**Configurar → Plugins → Redmine Integration → "Templates de Payload (avançado)"**. + +Há um card por operação: + +| Operação | Quando dispara | Endpoint Redmine | +|---|---|---| +| **Criar tarefa (issue)** | 1º lançamento de tempo de um chamado | `POST /issues.json` | +| **Lançar tempo** | tarefa (TicketTask) marcada como *Feito* | `POST /time_entries.json` | +| **Atualizar status** | mudança de status do chamado | `PUT /issues/{id}.json` | +| **Criar projeto** | vínculo "criar novo" na aba Redmine | `POST /projects.json` | + +## O conceito: "DE → PARA" + +- **DE (variáveis do GLPI):** o que o plugin tem para oferecer — `chamado.titulo`, + `tarefa.horas`, `vinculo.projeto_redmine`, etc. Aparecem como **autocomplete** no + campo de valor de cada linha (e há uma lista de referência colapsável). +- **PARA (campos do Redmine):** o que **o seu Redmine** espera em cada operação — + descoberto automaticamente (campos padrão + **campos personalizados** via + `GET /custom_fields.json`), com marcação de **obrigatório** e valores possíveis. + Clicar num campo do "PARA" adiciona a linha no editor com o caminho já pronto. + +## Fluxo recomendado + +1. **Sincronizar Metadados** (aba Conexão) — é o que descobre trackers, status, + atividades e **campos personalizados** do seu Redmine. +2. No card da operação, clique em **Gerar template** — cria o ponto de partida + equivalente ao comportamento padrão **+ os campos obrigatórios do seu ambiente**. +3. Ajuste as linhas (o autocomplete ajuda a achar as variáveis do GLPI). +4. Clique em **Testar**: + - **Preview** — mostra o JSON final renderizado com dados de exemplo (seguro). + - **Teste real** (opcional) — cria e apaga um objeto de teste no Redmine e, + se recusado, **traduz o erro** ("Atividade não pode ficar vazio → mapeie esse campo"). +5. Marque **Ativo** e clique em **Salvar** (o botão único no fim da página grava tudo). + +## Sintaxe das tags + +``` +{{ variavel }} → valor da variável +{{ variavel | filtro }} → aplica um filtro +{{ variavel | filtro:arg }} → filtro com argumento +{{ mapa[chave.dinamica] }} → lookup indexado (ex.: map.status[chamado.status]) +``` + +**Regra de tipos:** quando a tag ocupa **todo** o valor, o resultado sai com o +**tipo nativo** da variável (número, array, booleano — sem aspas no JSON). Quando a +tag está no meio de um texto, vira interpolação de string. +Campos que resolverem para vazio/nulo são **omitidos** do payload. + +### Filtros disponíveis + +| Filtro | Efeito | +|---|---| +| `truncar:N` | corta a string em N caracteres (ex.: `subject` no limite de 255) | +| `sem_html` | remove HTML/entidades | +| `minusculo` | tudo minúsculo | +| `identificador` | normaliza para identificador Redmine válido (minúsculas, `-`) | +| `padrao:'texto'` | valor fixo quando a variável estiver vazia | + +## Variáveis do GLPI (o "DE") + +| Grupo | Variáveis | +|---|---| +| `chamado.*` | `id`, `titulo`, `descricao`, `status`, `entidade`, `tecnico` | +| `tarefa.*` | `id`, `horas`, `segundos`, `descricao`, `comentario`, `data`, `autor` | +| `vinculo.*` | `projeto_redmine`, `issue_redmine`, `categoria_padrao` | +| `config.*` | `tracker_padrao`, `prioridade_padrao`, `atividade_padrao`, `observadores` | +| `map.status` | mapa de status GLPI→Redmine (use como `map.status[chamado.status]`) | +| `form.*` (criar projeto) | `nome`, `identificador`, `descricao`, `publico`, `subprojeto_de`, `trackers`, `modulos`, `cf_` | + +> **Autoria:** `chamado.tecnico` e `tarefa.autor` são **texto** (para usar em +> comentários, por exemplo). A **autoria real** da issue/tempo continua automática, +> via *impersonation* — não é um campo do template. + +## Segurança e robustez + +- As tags são um mini-motor de substituição (mustache-like), **não** executam código + (não é Twig completo) — seguro para edição por administradores. +- Template com JSON inválido **não é salvo** (aviso na tela). +- Se um template ativo falhar no **render** em produção, o plugin **cai no + comportamento 1.x** e registra no log — nunca perde um lançamento. + +## Exemplo (o que o "Gerar template" produz para `log_time`) + +```json +{ + "time_entry": { + "issue_id": "{{ vinculo.issue_redmine }}", + "hours": "{{ tarefa.horas }}", + "comments": "{{ tarefa.comentario }}", + "activity_id": "{{ config.atividade_padrao }}" + } +} +``` diff --git a/setup.php b/setup.php index b887a85..072a151 100644 --- a/setup.php +++ b/setup.php @@ -1,6 +1,6 @@