Download the PHP package risetechapps/fusion-report-client without Composer
On this page you can find all versions of the php package risetechapps/fusion-report-client. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download risetechapps/fusion-report-client
More information about risetechapps/fusion-report-client
Files in risetechapps/fusion-report-client
Package fusion-report-client
Short Description Laravel client for the Fusion Report Server — fluent, typed API for report generation.
License MIT
Homepage https://github.com/risetechapps/fusion-report-client
Informations about the package fusion-report-client
fusion-report-client
Laravel client package para comunicação com o fusion-report-server — API fluente e tipada para geração de relatórios JasperSoft.
Requisitos
- PHP ^8.4
- Laravel 12
Instalação
Publique o arquivo de configuração:
Configuração
.env:
api_keyé obrigatória. É ela que vai no headerX-API-KEY, a única credencial que o client usa. Sem ela, qualquer chamada — inclusivefile($id)->download()— lançaAuthenticationExceptionantes de a requisição sair, em vez de partir anônima e voltar como 401 genérico.A chave é lida do config a cada requisição, não uma vez na inicialização. Aplicações que a definem em runtime (bootstrapper de tenant, worker de fila que troca de contexto) são atendidas com o valor do contexto corrente.
Templates JRXML
Listar
Retorna todos os templates registrados. O endpoint do servidor é paginado
(per_page padrão 50, máximo 100), mas list() percorre as páginas
internamente — não é preciso paginar na aplicação.
Buscar por ID
Cadastrar
Atualizar por ID
Atualizar por nome
Baixar o .jrxml registrado
Devolve o XML cru (application/xml), não o envelope JSON do resto da API.
Útil para conferir o que está publicado antes de reenviar.
Excluir por ID
Excluir por nome
Histórico de gerações
Indisponível via
X-API-KEY— ambas as rotas são restritas ao dashboard (Sanctum) e retornam 401. Para histórico na integração por API key, use a tabela localfusion_report_generations. Ver Métodos indisponíveis via X-API-KEY.
Geração de Relatórios
Report Definitions (recomendado)
Crie uma classe que estende ReportDefinition:
Context no fluxo assíncrono. Em
onGenerated(síncrono) o$contextchega direto por parâmetro. NoonWebhookReceived(assíncrono) o callback é uma requisição nova, então ocontextinformado no disparo é persistido com a geração e o package o recarrega e injeta automaticamente no segundo parâmetro — você não precisa consultar nada. Requerlog_generationshabilitado (default).O package ainda injeta no context o
localeefetivo da geração (o de->locale()ou, na falta, odefaults.localedo config), disponível como$context['locale']nos dois hooks. Umlocaleque você já tenha colocado no context manualmente é preservado (não é sobrescrito).
webhookParams()— os parâmetros retornados são anexados como query string à URL base (webhook.url) apenas nogenerateAsync(). A URL final fica, por exemplo,https://seu-app.com/fusion/webhook?tenant_id=abc. Recebe os params já mesclados da geração, então você pode derivar valores do próprio relatório. Retorne[](default) para não anexar nada.
Registre no config:
Sincronizar templates com o servidor
O comando fusion-report:sync registra no servidor os templates definidos em
fusion-report.reports, expandindo o themes() de cada definition: cada
ThemeFusion vira um template (name + theme) independente. Ele primeiro lista o
que já existe no servidor (templates()->list(), indexado por name + theme) e
decide a ação por tema:
| Situação | sync |
sync --force |
sync --all |
|---|---|---|---|
Tema novo (name + theme) |
cadastra (upload) |
não toca¹ | cadastra (upload) |
| Tema já registrado | ignora | atualiza só se selecionado | atualiza (updateByName) |
themes() vazio |
falha (pula a definition) | — | falha (pula a definition) |
ThemeFusion sem ->from() / arquivo inexistente |
falha (pula o tema) | falha (pula o tema) | falha (pula o tema) |
¹ --force é cirúrgico: lista só os temas já registrados num seletor
(multiselect), processa apenas os marcados e ignora todo o resto — a barra de
progresso reflete só o trabalho real. Para cadastrar temas novos use o sync
normal ou --all.
--forceabre um seletor interativo dos temas já registrados; marque quais reenviar (espaço marca, Enter confirma). Sem terminal interativo (ex.: CI) ele cai para o comportamento de atualizar todos os registrados.--allatualiza todos os registrados e cadastra os novos, sem prompt — útil em pipelines / deploy.-
Se nada for selecionado no
--force, o comando informa e sai sem alterações. - O nome do tema é o que você passa em
ThemeFusion::make('...')— não há mais umdefaults.themeglobal no registro. ->withResources()é opcional: sem ele, o template é registrado sem recursos.- Cada tema é processado isoladamente: uma falha não aborta os demais; o comando retorna código de saída diferente de zero se houver qualquer falha.
Geração síncrona
Geração assíncrona
O resultado da geração assíncrona chega pelo webhook, não por polling.
FusionReport::generation($id)->get()bate emGET /api/v1/generations/{id}, que o servidor restringe ao dashboard (Sanctum). Autenticado porX-API-KEY, retorna 401 →FusionReportException. Ver Métodos indisponíveis via X-API-KEY.Configure
webhook.urle implementeonWebhookReceived()na suaReportDefinition— é o caminho suportado para saber que o relatório ficou pronto.
Formatos
O servidor exige pelo menos um formato em toda geração. Quando você não chama
->format(), vale o defaults.format do config (pdf de fábrica):
Entrada é normalizada: espaços são removidos, maiúsculas viram minúsculas
('PDF' → 'pdf') e duplicatas caem fora.
Válidos: pdf, xlsx, xls, docx, odt, csv, html, frp.
Definir
defaults.formatcomonullou''volta ao comportamento anterior: o payload omiteformate o servidor responde 422 se a geração não informar um.
Parâmetros
O servidor recebe params como lista de strings CHAVE=valor. A conversão é
feita pelo package:
| Tipo PHP | Vira |
|---|---|
string, int, float |
o próprio valor |
bool |
true / false |
null |
string vazia |
array |
JSON (["a","b"], {"x":1}) |
BackedEnum |
o value do case |
DateTimeInterface |
ISO-8601 (DATE_ATOM) |
Stringable |
__toString() |
| qualquer outro | FusionReportException |
Geração pelo builder (sem registry)
Geração por ID do template
Datasources
Nenhum datasource
JSON inline
CSV inline
URL JSON externa
Perfil pré-configurado no servidor
Proteção de documentos
Arquivos Gerados
Re-exportar uma geração inteira
Quando você tem o ID da geração e não o do arquivo, o servidor localiza o .frp
sozinho:
Formatos aceitos: pdf, xlsx, xls, docx, odt, csv, html. O frp não
entra na lista — é o próprio intermediário.
Lança ReportNotFoundException quando a geração não tem .frp disponível ou ele
já expirou.
Webhook
O package registra automaticamente a rota do callback (default POST /fusion/webhook). Quando a geração assíncrona termina, o servidor faz POST nessa rota e o package:
- Verifica a assinatura HMAC (obrigatória — ver Verificação de assinatura).
- Atualiza o registro em
fusion_report_generations(status + URLs). - Descarta reentregas: se o status gravado já é o do payload, para aqui.
- Dispara
ReportGenerationCompleted/ReportGenerationFailed. - Chama
onWebhookReceived()daReportDefinitioncujoname()bate com otemplate_namedo payload.
Entrega duplicada. O servidor reentrega o callback até 3 vezes (backoff 10s/60s/180s) sempre que não recebe resposta — e o timeout dele é de 15s, contados enquanto o seu handler ainda está rodando. Um
onWebhookReceived()lento aumenta a própria chance de ser reexecutado.O passo 3 protege contra isso comparando o status já gravado com o do payload. Ainda assim, mantenha o hook leve: prefira
dispatch()de um job a fazer o trabalho pesado inline.
Processamento em fila (recomendado)
Por padrão o callback é processado dentro da requisição: o 200 só sai
depois do update, dos eventos e do onWebhookReceived(). Como o servidor
concede 15s e reentrega até 3 vezes, um hook que manda e-mail ou sobe arquivo
passa desse limite — o trabalho é feito, mas o servidor registra
Client webhook gave up after all attempts e reentrega. O log passa a mentir
sobre o que aconteceu.
Ligando a fila, a assinatura é verificada, o payload é enfileirado e o 200 sai
em milissegundos:
O job é o ProcessFusionReportWebhook (tries = 3, backoff 10s/60s/180s).
Exige worker rodando. Sem
queue:workconsumindo a fila, os callbacks ficam parados e o fluxo assíncrono não completa. Por isso o padrão éfalse.
Contexto (tenancy, auth). O job é despachado de dentro da requisição, onde o
middleware de webhook.middleware já estabeleceu o contexto. Pacotes de tenancy
com suporte a fila propagam esse contexto automaticamente. Se o seu não faz
isso, mantenha queue => false ou trate a propagação no seu próprio
WebhookHandler.
Validação de payload malformado continua acontecendo antes do
enfileiramento, então o 422 segue valendo — depois do 200 não haveria como
avisar o servidor.
Deduplicação
A deduplicação usa dois guards, e basta um acusar entrega anterior:
| Guard | Força | Limite |
|---|---|---|
| Status já gravado na linha | Durável, sobrevive a flush de cache | Só existe com log_generations ligado |
Cache::add() da chave fusion-report:webhook:{uuid}:{status} |
Atômico — fecha a corrida de entregas simultâneas | Perde efeito se o cache for limpo, ou com driver null |
O TTL do cache sai de webhook.deduplication_ttl (minutos, padrão 1440). O
servidor reentrega no máximo por ~4 minutos, então 24h cobre com folga.
Com
log_generations = falsenão existe registro, e com ele some o$context— que chega sempre vazio emonWebhookReceived()e nos eventos — e o$owner, que vemnull. A deduplicação passa a depender só do cache. Os hooks continuam disparando; antes desta versão, o callback virava no-op silencioso.Se você usa
->context([...])ou->for($model), mantenha o log ligado.
Registro ausente com o log ligado é tratado como anomalia, não como
duplicata: o package registra Log::warning e não dispara nada. Em apps
multi-tenant isso normalmente significa que o middleware de webhook.middleware
não inicializou o tenant antes do controller — a consulta caiu no banco errado.
1. Reagindo ao callback — onWebhookReceived() (recomendado)
Basta implementar onWebhookReceived() na sua ReportDefinition (ver exemplo na seção Report Definitions). O package faz o roteamento pelo template_name automaticamente — você não precisa de rota nem handler próprios.
2. Handler customizado (opcional)
Para lógica global (independente de template), rebinde o contrato WebhookHandler num service provider seu:
Contexto da requisição (multi-tenant, auth, etc.)
O callback chega do servidor externo sem o contexto da sua aplicação. Como o package é agnóstico de contexto, a rota do webhook usa o middleware definido em webhook.middleware — é aí que a app hospedeira estabelece o que precisar antes do handler rodar.
Exemplo multi-tenant: a definition anexa o tenant_id na URL via webhookParams(), e um middleware seu lê esse valor e inicializa o tenant — assim a query da geração já cai no banco correto:
API do payload
Verificação de assinatura
A verificação HMAC é feita automaticamente pelo package.
webhook.secreté obrigatório. Sem ele a rota responde 503 e não processa nada — a alternativa seria aceitar payload não assinado, permitindo que qualquer POST alterasse status e URLs de download das suas gerações e disparasse os eventos da aplicação.Para desenvolvimento local sem segredo, e só para isso:
Com essa flag o package registra um
Log::warninga cada callback aceito sem verificação.
Respostas da rota de webhook:
| Situação | HTTP |
|---|---|
| Processado | 200 |
| Assinatura ausente ou inválida | 401 |
Payload sem id ou status |
422 (o servidor para de retentar) |
webhook.secret não configurado |
503 |
Para uso manual:
Monitoramento Jasper
Indisponível via
X-API-KEY. As cinco rotas/api/v1/jasper/*são exclusivas do dashboard (Sanctum) — expõem estado global do servidor Java, que não é escopado por cliente. Chamadas autenticadas por API key retornam 401. Os métodos seguem no package para quem autentica de outra forma.
Tratamento de Erros
Todas as exceptions do package herdam de FusionReportException, então um único
catch continua capturando tudo. As específicas existem para quem precisa
distinguir o caso.
Falha de conexão fica de fora. Timeout e rede indisponível lançam
Illuminate\Http\Client\ConnectionException, que não herda deFusionReportException— não há resposta HTTP para o package traduzir. Capture separadamente quando isso importar:
| HTTP | Situação | Exception |
|---|---|---|
| — | api_key não configurada — nenhuma requisição chega a sair |
AuthenticationException |
| 401 | Credencial ausente/inválida, ou rota só de dashboard | AuthenticationException |
| 403 | Recurso pertence a outra conta | AuthorizationException |
| 422 | Campo obrigatório ausente ou arquivo inválido | FusionReportException |
| 429 | Cota de requisições do plano esgotada | RateLimitException |
| 404 | ID não encontrado (route model binding) | ReportNotFoundException |
| 410 | Nome não encontrado, conflito ou erro interno | ReportNotFoundException / FusionReportException |
Eventos
O package dispara eventos em todo ciclo de geração. Use quando quiser reagir sem
ser dono da ReportDefinition — o hook onGenerated()/onWebhookReceived()
continua funcionando e os dois convivem.
| Evento | Quando |
|---|---|
ReportGenerationStarted |
O servidor aceitou a requisição (síncrona e assíncrona) |
ReportGenerationCompleted |
Concluída — na síncrona, pela resposta; na assíncrona, pelo webhook |
ReportGenerationFailed |
Falhou, pelos mesmos dois caminhos |
Todos expõem as mesmas propriedades públicas:
ReportGenerationFailed tem ainda $event->error.
Na geração síncrona o
Startedé seguido imediatamente porCompleted/Failed, no mesmo ciclo — a resposta já vem finalizada. Na assíncrona só oStartedsai na hora; o desfecho chega pelo webhook.
$event->errorcostuma virnullno caminho assíncrono: o servidor mantémerror_messageem$hidden. Ver Campos sempre vazios.
Métodos indisponíveis via X-API-KEY
O client autentica exclusivamente por X-API-KEY. Parte das rotas do servidor é
restrita ao dashboard e exige Bearer token do Sanctum — chamá-las com API key
retorna 401, que o package converte em FusionReportException.
Os métodos abaixo continuam existindo (podem ser usados por quem autentica de outra forma), mas não funcionam na integração por API key:
| Método | Rota | Alternativa |
|---|---|---|
generation($id)->get() |
GET /api/v1/generations/{id} |
webhook (onWebhookReceived()) |
generation($id)->status() |
GET /api/v1/generations/{id} |
webhook |
generation($id)->cancel() |
DELETE /api/v1/generations/{id} |
— |
templates()->generations($id) |
GET /api/v1/reports/{id}/generations |
tabela fusion_report_generations |
templates()->generationsByName($n) |
GET /api/v1/reports/name/{n}/generations |
tabela fusion_report_generations |
jasper()->formats() |
GET /api/v1/jasper/formats |
— |
jasper()->status() |
GET /api/v1/jasper/status |
— |
jasper()->jobs() |
GET /api/v1/jasper/jobs |
— |
jasper()->fontsStatus() |
GET /api/v1/jasper/fonts/status |
— |
jasper()->fontsSync() |
POST /api/v1/jasper/fonts/sync |
— |
Para histórico de gerações, use o log local do próprio package
(fusion_report_generations), alimentado na geração e atualizado pelo webhook —
ver Report Definitions e a trait
HasFusionReportGenerations.
Campos sempre vazios
O servidor mantém alguns campos em $hidden, então nunca chegam no JSON. Estes
accessors retornam sempre o valor padrão, independentemente da autenticação:
| Accessor | Retorna sempre |
|---|---|
GenerationResource::errorMessage() |
null |
GenerationResource::progressPercent() |
0 |
WebhookPayload::error() |
null |
TemplateResource::filePath() |
'' |
TemplateResource::resourcesPath() |
null |
Na prática: quando uma geração assíncrona falha, o webhook chega com
status: 'failed' e sem motivo. O texto do erro só aparece na geração
síncrona, na mensagem da FusionReportException.
Limitações conhecidas
Comportamentos que valem saber antes de integrar. Nenhum é acidental — todos estão registrados aqui em vez de serem descobertos em produção.
Timeout menor que o do servidor
O client espera 60s por resposta. O servidor concede 90s ao Jasper numa geração síncrona, e ainda gasta tempo depois disso baixando e armazenando os arquivos.
Numa geração síncrona pesada, o client desiste antes de o servidor terminar:
Duas consequências práticas:
- O timeout não é configurável — está fixo em
FusionReportHttp. ConnectionExceptionnão herda deFusionReportException. Quem fazcatch (FusionReportException $e)não pega falha de conexão. CaptureIlluminate\Http\Client\ConnectionExceptionseparadamente.
Para relatórios grandes, prefira generateAsync() com webhook.
Sem retry automático
Um 5xx transitório ou soluço de rede vira exceção direto no chamador. Retry não
foi adicionado de propósito: seria seguro apenas em leituras (get, download)
e perigoso em generate, async, upload e export, que criam estado e
consomem cota — o servidor cobra por requisição recebida, então três tentativas
de uma geração seriam três débitos.
Se precisar, implemente o retry na sua aplicação, envolvendo só as leituras.
URL do servidor não é configurável
FusionReportHttp::BASE_URL é uma constante. Não há config nem variável de
ambiente para apontar o client a outra instância — trocar exige editar o
package.
Replay de webhook
O servidor assina apenas o corpo, sem timestamp, então o par corpo+assinatura é válido indefinidamente. A deduplicação limita muito o estrago: um replay do mesmo status não reexecuta os efeitos. Proteção completa exigiria timestamp assinado, o que é mudança no servidor.
Com log_generations = false a deduplicação depende só do cache — expirado o
TTL, um replay volta a disparar os hooks.
Outros
- A poda de
fusion_report_generationsé fixa em 90 dias (FusionReportGeneration::prunable()), sem config. - Os dois
export()não registram a geração nova emfusion_report_generations, ao contrário degenerate()egenerateAsync(). generationsByName()não aceitatheme. Com o mesmo nome em temas diferentes, não dá para escolher qual histórico buscar.- O roteamento do webhook para a
ReportDefinitioncasa apenas porname(), ignorando o tema.
Referência completa — Endpoints cobertos
Rotas marcadas com ⚠️ exigem Sanctum e não funcionam via X-API-KEY
(ver seção acima).
| Endpoint | Método do package |
|---|---|
GET /api/v1/reports |
templates()->list() |
POST /api/v1/reports |
templates()->upload(...) |
GET /api/v1/reports/{id} |
templates()->find($id) |
POST /api/v1/reports/{id} |
templates()->update($id, ...) |
POST /api/v1/reports/name/{name} |
templates()->updateByName($name, ...) |
GET /api/v1/reports/{id}/jrxml |
templates()->downloadJrxml($id) |
DELETE /api/v1/reports/{id} |
templates()->delete($id) |
DELETE /api/v1/reports/name/{name} |
templates()->deleteByName($name, $theme) |
GET /api/v1/reports/{id}/generations |
templates()->generations($id) ⚠️ |
GET /api/v1/reports/name/{name}/generations |
templates()->generationsByName($name) ⚠️ |
POST /api/v1/generate |
report('name')->generate() |
POST /api/v1/async |
report('name')->generateAsync() |
POST /api/v1/reports/{id}/generate |
reportById($id)->generate() |
POST /api/v1/reports/{id}/async |
reportById($id)->generateAsync() |
GET /api/v1/generations/{id} |
generation($id)->get() ⚠️ |
DELETE /api/v1/generations/{id} |
generation($id)->cancel() ⚠️ |
GET /api/v1/files/{id}/download |
file($id)->download() |
POST /api/v1/files/{id}/export |
file($id)->export($format) |
POST /api/v1/generations/{id}/export |
generation($id)->export($format) |
GET /api/v1/jasper/formats |
jasper()->formats() ⚠️ |
GET /api/v1/jasper/status |
jasper()->status() ⚠️ |
GET /api/v1/jasper/jobs |
jasper()->jobs() ⚠️ |
GET /api/v1/jasper/fonts/status |
jasper()->fontsStatus() ⚠️ |
POST /api/v1/jasper/fonts/sync |
jasper()->fontsSync() ⚠️ |
All versions of fusion-report-client with dependencies
illuminate/bus Version ^12
illuminate/contracts Version ^12
illuminate/http Version ^12
illuminate/queue Version ^12
illuminate/support Version ^12