AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Retrofit

skill-snk-devcenter-addon-studio-retrofit · by snk-devcenter

Configura, revisa e debuga integração HTTP externa em Sankhya Addon Studio usando Retrofit + Moshi + OkHttp — declara dependências `moduleLib` no `build.gradle`, escreve interface client (`@GET`/`@POST`/`@Body`/`@Path`/`@Query`/`@Headers`), faz wiring Guice via `@Provides @Singleton` em `@CustomModule`, cria interceptors OkHttp (autenticação, logging, headers). Use ao integrar API REST/JSON exter…

No reviews yet
0 installs
4 views
0.0% view→install

Install

$ agentstack add skill-snk-devcenter-addon-studio-retrofit

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-snk-devcenter-addon-studio-retrofit)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
13d ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

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 →
Are you the author of Retrofit? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Integração HTTP com Retrofit — Addon Studio 2.0

Stack oficial para integração HTTP externa: Retrofit + Moshi + OkHttp. Substitui HttpClient nativo, URLConnection, Apache HTTP Client.

Esta skill cobre: declaração de deps no Gradle, interface client, wiring Guice, interceptors. Decisões arquiteturais (factory wrapper vs builder direto, retry, URL dinâmica) são do projeto — a skill mostra patterns válidos sem opinar.


1. Dependências (build.gradle do módulo)

> NÃO declarar no build.gradle raiz. Vai no build.gradle do módulo (model/build.gradle, etc.).

Use somente moduleLib — a configuração custom do plugin br.com.sankhya.addonstudio já adiciona o JAR ao classpath de compilação e empacota no EJB final. Não duplicar com implementation.

dependencies {
    // Retrofit + Moshi (JSON) — padrão para APIs REST.
    moduleLib 'com.squareup.retrofit2:retrofit:2.12.0'
    moduleLib 'com.squareup.retrofit2:converter-moshi:2.12.0'
    moduleLib 'com.squareup.moshi:moshi:1.15.2'

    // OkHttp — cliente HTTP subjacente (obrigatório).
    moduleLib 'com.squareup.okhttp3:okhttp:3.14.9'
}

Conversores opcionais

| Conversor | Quando usar | Dependência | |:----------|:------------|:------------| | converter-moshi | JSON (padrão) | com.squareup.retrofit2:converter-moshi:2.12.0 | | converter-scalars | Body String cru (SOAP/XML/texto) | com.squareup.retrofit2:converter-scalars:2.12.0 | | converter-jaxb | XML via JAXB | usar fábrica externa (JaxbConverterFactory de pacote terceiro) |

> Para testes com MockWebServer, adicionar em testImplementation: > ``groovy > testImplementation 'com.squareup.okhttp3:mockwebserver:3.14.9' > ``


2. Interface do client

Declare a API como interface anotada com Retrofit. Sempre retorno Call (síncrono — executor trata .execute()).

import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Query;

public interface ParceiroApi {

    @GET("v1/produtos/{id}")
    Call buscarProduto(@Path("id") Long id);

    @GET("v1/produtos")
    Call listarProdutos(@Query("limit") Integer limit,
                                          @Query("offset") Integer offset);

    @POST("v1/pedidos")
    @Headers("Content-Type: application/json")
    Call criarPedido(@Body NovoPedidoDto pedido);
}

Anotações principais

| Anotação | Uso | |:---------|:----| | @GET/@POST/@PUT/@DELETE/@PATCH | Verbo HTTP + path relativo (v1/recurso/{id}) | | @Path("id") | Substitui {id} no path | | @Query("k") | Adiciona ?k=v na query string | | @QueryMap | Map dinâmico de query params | | @Body | Corpo JSON serializado pelo conversor (Moshi) | | @Headers("K: V") | Header estático no método | | @Header("K") | Header dinâmico (parâmetro) | | @FormUrlEncoded + @Field | application/x-www-form-urlencoded | | @Multipart + @Part | Upload multipart/form-data |

DTOs (Moshi)

DTOs são POJOs Java 8 com Lombok. Moshi serializa via getters/setters — nomes de campo precisam bater com JSON (use @Json(name = "...") se diferente).

import com.squareup.moshi.Json;
import lombok.Data;

@Data
public class ProdutoDto {
    private Long id;
    private String nome;

    @Json(name = "preco_unitario")
    private Double precoUnitario;
}

3. Wiring Guice — duas opções

Escolha conforme o projeto. Skill não opina — ambas são válidas.

Opção A — Retrofit.Builder direto no @Provides

Indicado quando addon tem uma única integração HTTP ou cada cliente tem config muito diferente. Sem boilerplate adicional.

import br.com.sankhya.studio.stereotypes.CustomModule;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.moshi.MoshiConverterFactory;

import java.util.concurrent.TimeUnit;

@CustomModule
public class ParceiroIntegrationModule extends AbstractModule {

    private static final String BASE_URL = "https://api.parceiro.com.br/";

    @Override
    protected void configure() { }

    @Provides
    @Singleton
    public ParceiroApi provideParceiroApi(ParceiroAuthInterceptor authInterceptor) {
        OkHttpClient httpClient = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .addInterceptor(authInterceptor)
            .build();

        return new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(httpClient)
            .addConverterFactory(MoshiConverterFactory.create())
            .build()
            .create(ParceiroApi.class);
    }
}

Opção B — Factory wrapper reutilizável

Indicado quando addon tem múltiplas integrações HTTP — encapsula timeouts/converter padrão em um lugar só, deixa @Provides enxuto. Trocar a lib base no futuro vira mudança em arquivo único.

Boilerplate:

import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.converter.moshi.MoshiConverterFactory;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Factory centralizada de clientes Retrofit. Injetável via Guice
 * (classe concreta sem deps — auto-resolvida).
 */
public class RetrofitClientFactory {

    /** JSON (Moshi) — padrão REST. */
    public  T create(Class service, List interceptors, String baseUrl) {
        return create(service, interceptors, baseUrl, MoshiConverterFactory.create());
    }

    /** Conversor customizado (XML, scalars, etc.). */
    public  T create(Class service, List interceptors, String baseUrl,
                        Converter.Factory converterFactory) {
        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS);

        for (Interceptor interceptor : interceptors) {
            clientBuilder.addInterceptor(interceptor);
        }

        return new Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(clientBuilder.build())
            .addConverterFactory(converterFactory)
            .build()
            .create(service);
    }
}

Uso no módulo:

@CustomModule
public class ParceiroIntegrationModule extends AbstractModule {

    private static final String BASE_URL = "https://api.parceiro.com.br/";

    @Override
    protected void configure() { }

    @Provides
    @Singleton
    public ParceiroApi provideParceiroApi(RetrofitClientFactory factory,
                                          ParceiroAuthInterceptor authInterceptor) {
        return factory.create(
            ParceiroApi.class,
            Collections.singletonList(authInterceptor),
            BASE_URL
        );
    }
}

Regras universais (qualquer opção)

  • @CustomModule + extends AbstractModule — sem essa dupla, Guice ignora o módulo.
  • @Provides @Singleton — cliente Retrofit é stateless, reutilizar instância evita reconstruir OkHttp pool a cada chamada.
  • Interceptors recebidos por parâmetro do método @Provides — Guice resolve automático.
  • Ver skill dependency-injection para detalhes de @CustomModule, @Provides, Multibinder.

4. Interceptors OkHttp

Interceptors são pipeline que processa cada request/response. Use para autenticação, logging, headers globais, retry, métricas.

Estrutura básica

import br.com.sankhya.studio.stereotypes.Component;
import com.google.inject.Singleton;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;

@Component
@Singleton
public class ParceiroAuthInterceptor implements Interceptor {

    private final TokenProvider tokenProvider;

    @com.google.inject.Inject
    public ParceiroAuthInterceptor(TokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();
        Request authenticated = original.newBuilder()
            .header("Authorization", "Bearer " + tokenProvider.getToken())
            .build();
        return chain.proceed(authenticated);
    }
}

Regras:

  • @Component @Singleton — uma instância para todas as chamadas.
  • Implementa okhttp3.Interceptor.
  • chain.proceed(request) é obrigatório — sem ele a request não vai pra rede.
  • @Inject via construtor para resolver dependências (token provider, config, etc.).

Dependência circular (interceptor → client → interceptor)

Comum quando o interceptor precisa de um client que precisa do próprio interceptor (refresh token, por exemplo). Use Provider:

@Singleton
public class ParceiroAuthInterceptor implements Interceptor {

    private final Provider authClientProvider;

    @Inject
    public ParceiroAuthInterceptor(Provider authClientProvider) {
        this.authClientProvider = authClientProvider;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        ParceiroAuthClient client = authClientProvider.get(); // resolve lazy
        // ...
    }
}

Ver skill dependency-injection (seção Provider).

Múltiplos interceptors

Ordem importa. Passe lista na ordem desejada:

factory.create(
    ParceiroApi.class,
    Arrays.asList(loggingInterceptor, authInterceptor, headerInterceptor),
    BASE_URL
);

Pipeline OkHttp: cada interceptor envolve o próximo (request desce, response sobe).

> Atenção — segundo interceptor @Component quebra o deploy: cada @Component é bindado automaticamente às interfaces que implementa; dois @Component implements Interceptor no mesmo addon geram [Guice/BindingAlreadySet]: okhttp3.Interceptor was bound multiple times na subida do Wildfly (build e testes passam). A partir do segundo interceptor, remova o stereotype e proveja via @Provides @Singleton do tipo concreto — ver skill dependency-injection (seção 9).


5. Executando chamadas

O retorno Call é preguiçoso — só executa quando você chama .execute() (sync) ou .enqueue() (async).

Execução simples (sync)

import retrofit2.Call;
import retrofit2.Response;

@Component
public class ParceiroGateway {

    private final ParceiroApi api;

    @Inject
    public ParceiroGateway(ParceiroApi api) {
        this.api = api;
    }

    public ProdutoDto buscar(Long id) {
        try {
            Response response = api.buscarProduto(id).execute();
            if (!response.isSuccessful()) {
                throw new IntegrationApiException(
                    "Erro " + response.code() + ": " + (response.errorBody() != null ? response.errorBody().string() : "")
                );
            }
            ProdutoDto body = response.body();
            if (body == null) {
                throw new IntegrationApiException("Resposta com corpo nulo");
            }
            return body;
        } catch (IOException e) {
            throw new IntegrationNetworkException("Falha de rede", e);
        }
    }
}

> Boilerplate de tratamento (status, body nulo, IOException) repete em toda chamada. Projetos costumam extrair um RetrofitCallExecutor — ver bloco "Patterns avançados" abaixo.


6. Configuração de base URL

Três abordagens válidas. Skill não opina.

| Abordagem | Quando usar | |:----------|:------------| | Constante static final no módulo | URL fixa, mudou só com release | | Campo @Value(value = "INTEGRATION_PARCEIRO_URL", type = ValueType.ENV_VAR, defaultValue = "...") num @Component de config, injetado como parâmetro do @Provides | URL varia por ambiente (dev/homol/prod) — sintaxe completa na skill value | | Tabela de configuração no banco | URL gerenciada pelo cliente final em runtime — usar factory pattern (avançado) |


7. Patterns avançados — menção curta

Estes patterns são comuns mas opcionais. Skill não detalha — implemente conforme a necessidade do projeto.

  • URL dinâmica (config no banco): crie interface funcional ParceiroApiFactory { ParceiroApi create(String urlBase); } e binde via @Provides retornando lambda. Útil quando gateway lê URL de ConfiguracaoPlataforma.
  • Retry com backoff: envolva call.execute() em um RetryExecutor próprio — distinga exceções de rede (retry) das de API (não retry). Considere okhttp3.ConnectionPool e timeouts antes de retry.
  • Logging: okhttp3.logging.HttpLoggingInterceptor (dep adicional moduleLib 'com.squareup.okhttp3:logging-interceptor:3.14.9'). Configurar nível BODY só em dev.
  • SOAP/XML: use converter-scalars + interceptor que envelopa request em ` e extrai da response. DTOs ficam só com conteúdo funcional. Anote método Retrofit com @Headers("SOAPAction: ...")`.
  • Executor compartilhado: RetrofitCallExecutor (@Component @Singleton) que recebe Call e devolve T, centralizando tratamento de status/body/IOException. Reduz boilerplate em gateways.

8. Erros Comuns

| Erro | Causa | Correção | |:-----|:------|:---------| | NoClassDefFoundError: retrofit2/Retrofit em runtime | Declarado só como implementation, sem moduleLib | Trocar para moduleLib (ou ambos em versões antigas do plugin Gradle) | | IllegalArgumentException: Illegal URL no Retrofit.Builder | Base URL sem barra final | Adicionar / no fim: "https://api.x.com/" | | EOFException em response com 204 No Content | Tentou desserializar corpo vazio | Use Call ou cheque response.body() == null | | MalformedJsonException ao deserializar | DTO não bate com JSON | Adicionar @Json(name = "...") em campos com nome diferente | | @Inject de javax.inject no interceptor | Pacote errado | com.google.inject.Inject (ver skill addon-studio) | | Token expirou mid-request | Auth interceptor sem refresh | Implementar okhttp3.Authenticator separado para 401, ou refresh proativo | | Timeouts disparam sob carga | Defaults OkHttp são baixos | Configurar connectTimeout/readTimeout/writeTimeout no OkHttpClient.Builder | | Method not annotated with HTTP method type | Falta @GET/@POST/etc. | Adicionar verbo HTTP no método da interface |


9. Checklist

Nova integração HTTP

  1. [ ] Adicionar deps moduleLib (retrofit + converter-moshi + moshi + okhttp) no build.gradle do módulo.
  2. [ ] Criar interface XxxApi com anotações Retrofit. Retorno Call.
  3. [ ] Criar DTOs Java 8 com Lombok (@Data) — usar @Json(name=...) quando JSON não casar.
  4. [ ] Criar @CustomModule com @Provides @Singleton retornando o cliente.
  5. [ ] Se múltiplas APIs no addon, considerar RetrofitClientFactory (opção B).
  6. [ ] Criar interceptors necessários (auth, logging) como @Component @Singleton.
  7. [ ] Gateway/adapter (@Component) injeta o client e expõe métodos de domínio.
  8. [ ] Tratar response status, body nulo, IOException — extrair executor compartilhado se boilerplate repetir.

Skills relacionadas

  • dependency-injection@CustomModule, @Provides, @Singleton, Provider, Multibinder
  • value — injetar base URL e timeouts via @Value
  • addon-studio — regras universais (Java 8, @Inject de com.google.inject)
  • test — testar gateway com MockWebServer (OkHttp)
  • controller-advice — mapear IntegrationApiException/IntegrationNetworkException para HTTP errors do addon
  • build — comandos Gradle (gradle deployAddon)
  • encoding — garantir ISO-8859-1 após criar arquivos .java

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.