Cliente Java do Notification Service — dependência Maven, configuração M2M num serviço Spring Boot, envio com template, erros.
O módulo client é o cliente Java do Notification Service. Usa só java.net.http.HttpClient e Jackson: não depende do Spring. A configuração num serviço Spring Boot (propriedades, beans) é feita por ti — ver Configurar num serviço Spring Boot.
É o cliente que o Access Management usa para enviar os emails de convite.
Dependência Maven
<dependency>
<groupId>cv.igrp.platform.notification</groupId>
<artifactId>client</artifactId>
<version>0.2.0-beta.5</version>
</dependency>O artefacto está no repositório Maven do iGRP (não no registry npm):
<repository>
<id>igrp-framework-releases</id>
<url>https://sonatype.nosi.cv/repository/igrp-framework-releases/</url>
</repository>ApiClient e classes de API
ApiClient guarda a base URL e as credenciais. Cada domínio da API tem uma classe que recebe o ApiClient no construtor:
| Classe | Endpoint | Métodos principais |
|---|---|---|
NotificationsApi | /api/v1/notifications | send, list, get, resend |
BulkApi | /api/v1/notifications/bulk | bulk(request), bulkCsv(request, csv, filename) |
AttachmentsApi | /api/v1/attachments | upload, download |
TemplatesApi | /api/v1/templates | list, create, get, update, publish, archive, preview |
InboxApi | /api/v1/inbox | list, unreadCount, markRead, archive |
BatchesApi, RecipientsApi, GroupsApi, RecurrencesApi, DeliveriesApi, StatsApi, SettingsApi, AdminApi, WebhooksApi | Restantes recursos da API REST |
Autenticação M2M
ApiClient client = new ApiClient();
client.setBaseUrl("https://<gateway>/igrp-notification-service"); // sem /api/v1
client.setClientCredentials(
"https://<gateway>/igrp-access-management", // authServerBaseUrl
clientId,
clientSecret,
null); // scope: só se o cliente OAuth2 o exigirO cliente pede o token em POST {authServerBaseUrl}/oauth2/token (client_credentials), guarda-o em cache e renova-o antes de expirar.
| Sobrecarga | Token pedido a |
|---|---|
setClientCredentials(authServerBaseUrl, clientId, clientSecret, scope) | authServerBaseUrl — a recomendada |
setClientCredentials(clientId, clientSecret[, scope]) | baseUrl do Notification Service — só funciona se ambos estiverem atrás do mesmo host |
authServerBaseUrl é o Access Management
O endpoint é {authServerBaseUrl}/oauth2/token: usa a base do Access Management, não o URL do realm do Keycloak.
setAuthToken(token) serve quando já tens um token (por exemplo, o do utilizador para ler a inbox).
applicationCode
O serviço lê a aplicação do claim application_code do token; se o token não o tiver, usa request.setApplicationCode(...) (escritas) ou o argumento applicationCode das sobrecargas de leitura (get(id, applicationCode), list(..., applicationCode)). Sem nenhum dos dois a resposta é 400 APPLICATION_CODE_REQUIRED.
Enviar com template
EMAIL precisa de SMTP configurado
Para enviar por EMAIL, o provider SMTP tem de estar configurado no Notification Service (NOTIFICATION_PROVIDERS_EMAILSMTP_*: servidor, porta, credenciais e remetente). Sem ele, o envio é recusado com 422 CHANNEL_NOT_ENABLED e nenhum email sai — ver Configurar os canais › EMAIL (SMTP). O canal também tem de estar ativo nas Definições da aplicação.
Os modelos são JavaBeans (construtor vazio + setters):
TemplateRefDto template = new TemplateRefDto();
template.setCode("pedido-confirmacao");
template.setLocale("pt-CV");
RecipientDto recipient = new RecipientDto();
recipient.setEmail(email);
SendNotificationRequest request = new SendNotificationRequest();
request.setChannelStrategy(ChannelStrategy.SPECIFIC);
request.setChannels(List.of(Channel.EMAIL));
request.setRecipients(List.of(recipient));
request.setTemplate(template);
request.setVariables(Map.of("nome", nome, "numeroPedido", referencia));
request.setBusinessKey(referencia);
request.setIdempotencyKey("pedido-confirmacao:" + referencia);
NotificationResponse response = new NotificationsApi(client).send(request);
UUID id = response.getId(); // o serviço responde 202: a entrega é assíncrona| Modelo | Campos |
|---|---|
SendNotificationRequest | channelStrategy (obrigatório), channels, recipients, groups, template ou inline, variables, priority, locale, attachments, category, businessKey, correlationId, idempotencyKey, callbackUrl, scheduledAt, expiresAt, applicationCode |
RecipientDto | userId, email, phone, pushToken |
GroupRefDto | um de groupId, departmentCode, roleCode, permissionName |
TemplateRefDto | code, locale |
InlineContentDto | subject, title, body |
AttachmentRefDto | filename, contentRef |
Enumerados (cv.igrp.platform.notification.client.constants): ChannelStrategy = SPECIFIC, ALL, PREFERRED; Channel = EMAIL, SMS, WHATSAPP, PUSH, IN_APP, OFFLINE; Priority = LOW, NORMAL, HIGH, URGENT.
O locale do template tem de existir
O serviço procura o template publicado com o locale exato e não faz fallback. Sem ele responde 422 TEMPLATE_ERROR com a mensagem No PUBLISHED template…. O Access Management repete o envio uma vez com o locale por omissão do módulo antes de desistir.
Envio em massa
new BulkApi(client).bulk(request) envia o mesmo pedido a muitos destinatários e devolve BulkResponse (batchId, notificationId, totalDeliveries, deliveries). bulkCsv(request, csvBytes, "destinatarios.csv") lê os destinatários de um CSV. O progresso consulta-se em BatchesApi.
Anexos
AttachmentsApi.upload não envia multipart
upload(byte[], filename) manda os bytes como corpo com Content-Type: multipart/form-data, sem partes. O endpoint espera um multipart com a parte file. Faz o upload com um cliente HTTP multipart (por exemplo RestClient com MultipartBodyBuilder), com o mesmo token, e usa o contentRef devolvido em AttachmentRefDto.
Erros
As chamadas lançam ApiException:
| Método | Devolve |
|---|---|
getCode() | Estado HTTP |
getErrorCode() | Código do serviço (String), por exemplo TEMPLATE_ERROR |
getErrorCodeEnum() | O mesmo como IgrpErrorCode (null se desconhecido) |
getErrorMessage() | Mensagem do serviço |
Códigos mais comuns (IgrpErrorCode):
| Código | HTTP |
|---|---|
VALIDATION_ERROR, INVALID_REQUEST, APPLICATION_CODE_REQUIRED | 400 |
APPLICATION_ACCESS_DENIED, ATTACHMENT_ACCESS_DENIED, MISSING_USER_ID | 403 |
TEMPLATE_NOT_FOUND, NOTIFICATION_NOT_FOUND, ATTACHMENT_NOT_FOUND, … | 404 |
ATTACHMENT_TOO_LARGE | 413 |
TEMPLATE_ERROR, CHANNEL_NOT_ENABLED, CHANNEL_NOT_ALLOWED, CALLBACK_DOMAIN_NOT_ALLOWED, ACCESS_MANAGER_UNAVAILABLE | 422 |
QUOTA_EXCEEDED | 429 |
Configurar num serviço Spring Boot
O cliente não traz propriedades nem auto-configuração. O padrão usado no Access Management:
@ConfigurationProperties(prefix = "app.notification")
public record NotificationProperties(
boolean enabled, String baseUrl, String authUrl,
String clientId, String clientSecret, String scope) {}@Configuration
@EnableConfigurationProperties(NotificationProperties.class)
public class NotificationClientConfig {
@Bean
@ConditionalOnProperty(name = "app.notification.enabled", havingValue = "true")
ApiClient notificationApiClient(NotificationProperties props) {
ApiClient client = new ApiClient();
client.setBaseUrl(props.baseUrl());
client.setClientCredentials(props.authUrl(), props.clientId(), props.clientSecret(), props.scope());
return client;
}
@Bean
@ConditionalOnProperty(name = "app.notification.enabled", havingValue = "true")
NotificationsApi notificationsApi(ApiClient notificationApiClient) {
return new NotificationsApi(notificationApiClient);
}
}app.notification.enabled=${NOTIFICATION_ENABLED:false}
app.notification.base-url=${NOTIFICATION_SERVICE_BASE_URL:}
app.notification.auth-url=${IGRP_ACCESS_MANAGEMENT_API:}
app.notification.client-id=${IGRP_M2M_CLIENT_ID:}
app.notification.client-secret=${IGRP_M2M_CLIENT_SECRET:}
app.notification.scope=Com enabled=false os beans não são criados: injeta Optional<NotificationsApi> no serviço que envia e trata a ausência (sem envio, ou um envio alternativo). O prefixo app.notification é um exemplo; usa o da tua aplicação.
Última atualização:
