Install
$ agentstack add skill-snk-devcenter-addon-studio-controller ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Controller (@Controller) — Addon Studio 2.0
@Controller marca classes = pontos entrada API interna add-on. Cada metodo publico auto-exposto como endpoint servico. Controllers orquestram fluxo requisicao — nunca contem logica negocio.
> Referencias complementares: > - addon-studio — overview, regras universais, naming convention, fluxo CRUD > - dependency-injection — Injecao de dependencia (Guice) > - mapstruct — Mapeamento de objetos (MapStruct) > - controller-advice — Tratamento global de excecoes
1. Anatomia de um Controller
import br.com.sankhya.studio.annotations.Controller;
import br.com.sankhya.studio.annotations.enums.EJBTransactionType;
import br.com.sankhya.studio.persistence.Transactional;
import com.google.inject.Inject;
import javax.validation.Valid;
@Controller(
serviceName = "PedidoControllerSP", // Obrigatorio, sufixo "SP"
transactionType = EJBTransactionType.Supports // Opcional (Supports e o padrao)
)
public class PedidoController {
private final PedidoService pedidoService; // Dependencias como final
private final PedidoRestMapper mapper;
@Inject // Injecao via construtor
public PedidoController(PedidoService pedidoService,
PedidoRestMapper mapper) {
this.pedidoService = pedidoService;
this.mapper = mapper;
}
@Transactional // Metodos que alteram dados
public CriarPedidoResponse criar(@Valid CriarPedidoRequest request) {
Pedido pedido = mapper.toPedido(request); // DTO -> entidade @JapeEntity
Pedido salvo = pedidoService.criar(pedido);
return mapper.toCriarResponse(salvo); // entidade -> DTO
}
}
O que atravessa controller -> service
O objeto que sai do mapper e entra no service e a entidade @JapeEntity — ou um valor simples (BigDecimal nuPedido), quando o service nao precisa do registro inteiro.
Nao existe terceiro modelo entre DTO e entidade. O caminho padrao tem dois modelos e um mapper:
Request DTO -> @JapeEntity -> Response DTO
Um objeto de dominio separado da entidade so se paga quando existe logica que nao mapeia 1:1 com tabela — orquestracao entre varias entidades, maquina de estados propria, calculo com invariante que a tabela nao expressa. Isso e decisao consciente do projeto, tomada com o dev: nao e o default de CRUD e nao deve ser inferido a partir dos exemplos desta skill.
2. Atributos do @Controller
serviceName (obrigatorio)
Nome servico registrado plataforma. Deve terminar com sufixo SP.
@Controller(serviceName = "PedidoControllerSP")
> serviceName define URL acesso servico. Cada metodo publico exposto como ..
transactionType (opcional)
Define comportamento transacional padrao todos metodos classe.
| EJBTransactionType | Descricao | Quando usar | |:---------------------|:----------|:------------| | Supports | Usa transacao se ja existir; senao, sem. | Padrao. Controllers mistura leitura+escrita. | | Required | Sempre executa em transacao (cria se nao existir). | Controllers 100% escrita. | | NotSupported | Executa fora transacao (suspende se existir). | Controllers 100% leitura. |
// Padrao (leitura + escrita com @Transactional granular)
@Controller(serviceName = "MeuControllerSP", transactionType = EJBTransactionType.Supports)
// Somente escrita
@Controller(serviceName = "MeuControllerSP", transactionType = EJBTransactionType.Required)
// Somente leitura
@Controller(serviceName = "ConsultaControllerSP", transactionType = EJBTransactionType.NotSupported)
3. Controle Transacional com @Transactional
@Transactional so pode ser aplicado em metodo (@Target(METHOD)) e sempre tem precedencia sobre transactionType da classe.
Valores de Transactional.TxType
Import: br.com.sankhya.studio.persistence.Transactional.
| TxType | Semantica | |:---------|:----------| | REQUIRED | Default do @Transactional bare. Usa a transacao existente; cria uma se nao houver. | | REQUIRES_NEW | Sempre cria transacao nova, suspendendo a atual se existir. | | MANDATORY | Exige transacao ativa; lanca excecao se nao houver. | | NOT_SUPPORTED | Executa fora de transacao; suspende a atual se existir. | | NEVER | Lanca excecao se houver transacao ativa. |
> Nao existe TxType.SUPPORTS. Esses cinco valores sao o enum inteiro.
EJBTransactionType (classe) vs TxType (metodo)
Sao enums distintos e nao equivalentes — nao ha par para todo valor:
| EJBTransactionType (classe) | Equivalente em TxType (metodo) | |:------------------------------|:---------------------------------| | Supports (padrao) | nenhum — SUPPORTS nao existe no TxType | | Required | REQUIRED | | NotSupported | NOT_SUPPORTED |
REQUIRES_NEW, MANDATORY e NEVER so existem por metodo — nao ha equivalente de classe.
> Metodo que deve seguir Supports: omita @Transactional — ele herda o transactionType da classe. Supports nao e expressavel por metodo.
@Controller(serviceName = "MeuControllerSP", transactionType = EJBTransactionType.NotSupported)
public class MeuController {
// Usa o padrao da classe (NotSupported) — sem transacao
public List listar() { ... }
// Sobrepoe o padrao — executa em transacao propria
@Transactional
public MeuResponse criar(@Valid MeuRequest request) { ... }
// Sobrepoe com transacao nova (isolada)
@Transactional(Transactional.TxType.REQUIRES_NEW)
public void processarBatch() { ... }
}
Quando usar @Transactional
| Operacao | @Transactional | Motivo | |:---------|:-----------------|:-------| | Create / Update / Delete | Sim | Garante atomicidade | | Leitura simples | Nao | Sem necessidade transacao | | Leitura + escrita mesmo metodo | Sim | Garante consistencia | | Operacao idempotente (sem side effects) | Nao | Desnecessario |
4. DTOs (Request / Response)
Controllers nunca expoe a entidade @JapeEntity diretamente na response. Use DTOs = contratos entrada/saida.
Organizacao
> Skill nao opina sobre pacotes. Padrao comum: DTOs (Request/Response) e mapper MapStruct ficam junto do controller (mesmo pacote ou subpacotes dto/ e mapper/ ao lado do *Controller.java). Ajuste a sua arquitetura.
Request DTO
Usa @Data (Lombok) + validacao javax.validation:
import lombok.Data;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.DecimalMin;
import java.math.BigDecimal;
@Data
public class CriarPedidoRequest {
@NotNull(message = "O codigo do parceiro e obrigatorio.")
private BigDecimal codParceiro;
@NotBlank(message = "A descricao e obrigatoria.")
private String descricao;
@DecimalMin(value = "0.01", message = "O valor deve ser maior que zero.")
private BigDecimal valor;
private String observacao; // Opcional — sem validacao
}
Response DTO
Usa @Data (Lombok), sem validacao:
import lombok.Data;
import java.math.BigDecimal;
@Data
public class CriarPedidoResponse {
private BigDecimal numeroPedido;
private BigDecimal valorTotal;
private String status;
}
Anotacoes de validacao comuns
| Anotacao | Uso | Exemplo | |:---------|:----|:--------| | @NotNull | Campo obrigatorio (qualquer tipo) | @NotNull(message = "...") | | @NotBlank | String obrigatoria e nao vazia | @NotBlank(message = "...") | | @NotEmpty | Collection/String nao vazia | @NotEmpty(message = "...") | | @DecimalMin | Valor minimo (BigDecimal) | @DecimalMin(value = "0.01", message = "...") | | @Size | Tamanho min/max de String ou Collection | @Size(min = 1, max = 200) | | @Valid | Validacao em cascata (objetos nested) | @Valid MeuRequest request |
> @Valid em parametro metodo controller ativa validacao automatica. Falha = framework retorna erro antes executar metodo.
5. Protocolo HTTP — Request e Response
URL de acesso
//service.sbr?serviceName=.&mgeSession=
contexto-addon= valorrootProject.nameemsettings.gradleserviceName= atributo@ControllernomeMetodo= nome metodo publico controller
Exemplo:
http://localhost:8080/meu-addon/service.sbr?serviceName=PedidoControllerSP.criarPedido&mgeSession=ABC123
Autenticacao
Todas requisicoes exigem auth via mgeSession. jsessionId obtido com MobileLogin:
curl --location 'http://localhost:8080/mge/service.sbr?serviceName=MobileLoginSP.login&outputType=json' \
--header 'Content-Type: application/json' \
--data '{
"requestBody": {
"NOMUSU": {"$": "USUARIO"},
"INTERNO": {"$": "SENHA"}
}
}'
> Ambientes com Gateway Sankhya: auth via MobileLogin nao necessaria — Gateway gerencia token.
Formato da Request (POST)
{
"serviceName": "PedidoControllerSP.criarPedido",
"requestBody": {
"request": {
"codParceiro": 12345,
"descricao": "Pedido de teste",
"valor": 150.50,
"observacao": "Entrega urgente"
}
}
}
Regras:
serviceName:.(mesmo valor URL).requestBody: Contem argumentos metodo como propriedades nomeadas pelo nome parametro Java.
> Metodo criarPedido(CriarPedidoRequest request) = JSON usa "request" como chave. Se fosse criarPedido(CriarPedidoRequest pedido), chave seria "pedido".
Formato da Response
Sucesso (status = "1"):
{
"serviceName": "PedidoControllerSP.criarPedido",
"status": "1",
"pendingPrinting": "false",
"transactionId": "CB0F625A72C214CF8449F0B18E1FA81A",
"responseBody": {
"numeroPedido": 987654,
"valorTotal": 551.00,
"status": "PENDENTE"
}
}
Erro (status != "1"):
{
"serviceName": "PedidoControllerSP.criarPedido",
"status": "0",
"pendingPrinting": "false",
"transactionId": "CB0F625A72C214CF8449F0B18E1FA81A",
"statusMessage": "Erro de validacao: O campo descricao e obrigatorio"
}
| status | Significado | |:---------|:------------| | "1" | Sucesso | | "0" | Erro de execucao | | "3" | Timeout | | "4" | Cancelado por concorrencia |
> Erro: responseBody nao incluido. Mensagem fica em statusMessage.
6. Tipo de Retorno dos Metodos
Tipo retorno cada metodo definido pela regra negocio projeto:
- Metodos retornam dados = retornam DTO resposta diretamente.
- Metodos sem retorno dados =
void.
// Com retorno de dados
public PedidoResponse criarPedido(@Valid CriarPedidoRequest request) {
...
return mapper.toResponse(resultado);
}
// Sem retorno de dados
@Transactional
public void cancelar(@Valid CancelarPedidoRequest request) {
pedidoService.cancelar(request.getNuPedido());
}
Framework serializa automaticamente o objeto retornado em responseBody da response.
7. Tratamento Global de Excecoes (@ControllerAdvice)
Excecoes lancadas em metodos do controller devem ser tratadas em classe @ControllerAdvice separada. Nunca capturar excecao no proprio controller — deixar propagar.
> Ver controller-advice para regras criticas (handler nao pode retornar void, multiplas excecoes por handler, rollback automatico, proibicao de Exception.class) e niveis de log sugeridos.
8. Fluxo Padrao de um Metodo
Request DTO —@Valid—> Controller —mapper—> @JapeEntity
|
|— Service.(entidade)
| |— regra de negocio + repository
|
|— mapper.toResponse(resultado)
|
|— return response
Metodo com entrada e saida (CRUD)
@Transactional
public CriarPedidoResponse criarPedido(@Valid CriarPedidoRequest request) {
Pedido pedido = mapper.toPedido(request);
Pedido resultado = pedidoService.criar(pedido);
return mapper.toCriarResponse(resultado);
}
Metodo somente com entrada (acao sem retorno)
@Transactional
public void cancelar(@Valid CancelarPedidoRequest request) {
pedidoService.cancelar(request.getNuPedido());
}
Metodo somente com saida (consulta)
public List listarProdutos() {
List produtos = produtoService.listar();
return produtos.stream()
.map(mapper::toResponse)
.collect(Collectors.toList());
}
Metodo sem entrada e sem saida (trigger)
@Transactional
public void sincronizar() {
sincronizacaoService.sincronizar();
}
9. Exemplos Completos
Exemplos completos — controller simples (CRUD) e controller completo (múltiplas operações: criar, emitir, cancelar, com ServiceContext para impressão) — em [references/examples.md](references/examples.md).
10. Convencoes
| Convencao | Padrao | Exemplo | |:----------|:-------|:--------| | Nome da classe | Controller | PedidoController | | serviceName | ControllerSP | PedidoControllerSP | | Nome Request DTO | Request ou Request | CriarPedidoRequest | | Nome Response DTO | Response ou Response | EmitirPedidoResponse | | Nome Mapper | RestMapper | PedidoRestMapper | | Nome Service | Service, com metodos nomeados por operacao | PedidoService.criar(...), .cancelar(...) |
> Service por feature, nao por endpoint. Um Service com metodos nomeados e o padrao — mantem o construtor do controller enxuto. Quebrar em classe por operacao (CriarPedidoService.execute(...)) e legitimo quando a operacao cresce a ponto de ter deps proprias, mas e decisao do projeto: nao gerar um service por endpoint por default.
11. Checklist: Novo Controller
- [ ] Criar classe Controller (organizar conforme arquitetura do projeto).
- [ ] Anotar com
@Controller(serviceName = "ControllerSP"). - [ ] Definir
transactionTypeadequado (ou usar padraoSupports). - [ ] Injetar dependencias (services, mappers) via construtor com
@Inject. - [ ] Criar Request DTOs com validacao (
@NotNull,@NotBlank, etc.). - [ ] Criar Response DTOs.
- [ ] Criar MapStruct Mapper (ver
mapstruct). - [ ] Usar
@Validem parametro dos metodos que recebem DTOs. - [ ] Usar
@Transactionalem metodos que alteram dados. - [ ] Retornar tipo adequado conforme regra negocio (DTO resposta ou
void). - [ ] NAO colocar logica negocio — delegar para o service.
- [ ] NAO capturar excecoes — deixar
@ControllerAdvicetratar. - [ ] Verificar se
@ControllerAdvicecobre excecoes lancadas pelo service.
12. Anti-Patterns (PROIBIDO)
| Anti-Pattern | Correcao | |:-------------|:---------| | Retornar entidade @JapeEntity diretamente | Usar Response DTO + MapStruct | | Logica de negocio no controller | Mover para o service | | Criar um objeto de dominio intermediario entre DTO e @JapeEntity por default | Mapear direto para a entidade — terceiro modelo so por decisao explicita do projeto | | try/catch no controller para excecoes de negocio | Deixar o @ControllerAdvice tratar | | Controller acessando Repository diretamente | Usar Service como intermediario | | Controller chamando Gateway diretamente | Usar Service como intermediario | | serviceName sem sufixo SP | Sempre SP | | Esquecer @Transactional em metodo de escrita | Adicionar @Transactional | | @Transactional(Transactional.TxType.SUPPORTS) | Nao existe — omitir @Transactional (metodo herda Supports da classe) | | @Transactional na classe | So vale em metodo (@Target(METHOD)) — use transactionType no @Controller | | Esquecer @Valid no parametro | Adicionar @Valid para ativar validacao | | Adicionar @Component no controller | @Controller ja e gerenciado — nao misturar | | Capturar excecao e retornar null | Deixar a e
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: snk-devcenter
- Source: snk-devcenter/addon-studio
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.