Skip to content

Commit b7505dc

Browse files
Rafaellyjhosefmarks
authored andcommitted
feat(http-interceptor): permite exibição múltiplas mensagens do back end
Exibe em uma modal os detalhamentos de mensagens de erro e sucesso recebidas do back end. Fixes DTHFUI-898
1 parent 07559a7 commit b7505dc

12 files changed

+917
-206
lines changed

projects/ui/src/lib/interceptors/po-http-interceptor/po-http-interceptor-base.service.spec.ts

Lines changed: 241 additions & 66 deletions
Large diffs are not rendered by default.
Lines changed: 93 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
import { ComponentRef } from '@angular/core';
12
import { HttpInterceptor, HttpHandler, HttpRequest, HttpResponse, HttpEvent, HttpErrorResponse } from '@angular/common/http';
23

34
import { Observable } from 'rxjs';
45
import { tap } from 'rxjs/operators';
56

7+
import { PoComponentInjectorService } from '../../services/po-component-injector/po-component-injector.service';
8+
import { PoHttpInterceptorDetail } from './po-http-interceptor-detail/po-http-interceptor-detail.interface';
9+
import { PoHttpInterceptorDetailComponent } from './po-http-interceptor-detail/po-http-interceptor-detail.component';
10+
11+
// DEPRECATED 4.x.x
612
const NO_ERROR_HEADER_PARAM = 'X-Portinari-No-Error';
13+
const NO_MESSAGE_HEADER_PARAM = 'X-Portinari-No-Message';
714

815
/**
916
* @description
@@ -15,31 +22,40 @@ const NO_ERROR_HEADER_PARAM = 'X-Portinari-No-Error';
1522
* Ao analisar o objeto `_messages` retornado pela requisição, o serviço exibirá notificações com mensagens na tela.
1623
* Os retornos de erros com códigos 4xx e 5xx são tratados automaticamente, sem a necessidade de incluir o `_messages`.
1724
*
18-
* Também existe a possibilidade de não apresentar a notificação quando houver algum erro com códigos 4xx e 5xx,
19-
* utilizando o parâmetro `X-Portinari-No-Error` que foi definido conforme o
20-
* [**Guia de implementação das APIs TOTVS**](http://tdn.totvs.com/pages/viewpage.action?pageId=484701395) (em Cabeçalhos Customizados).
21-
* O parâmetro `X-Portinari-No-Error` deve ser informado no cabeçalho da requisição com o valor `'true'` para funcionar corretamente,
22-
* por exemplo:
25+
* É possível dispensar a notificação para o usuário utilizando-se no cabeçalho da requisição os parâmetros listados abaixo com o valor
26+
* igual a `true`:
27+
*
28+
* - `X-Portinari-No-Message`: Não exibe notificações de erro e/ou sucesso.
29+
*
30+
* - **Depreciado** `X-Portinari-No-Error`: não mostra notificações de erro com códigos `4xx` e `5xx`.
2331
*
2432
* ```
2533
* ...
26-
* const headers = { 'X-Portinari-No-Error': 'true' };
34+
* const headers = { 'X-Portinari-No-Message': 'true' };
2735
*
2836
* this.http.get(`/customers/1`, { headers: headers });
2937
* ...
3038
*
3139
* ```
32-
* > Após a validação no interceptor, o parâmetro será removido do cabeçalho da requisição.
40+
*
41+
* Mais detalhes no tópico sobre cabeçalhos customizados no
42+
* [**Guia de implementação das APIs TOTVS**](http://tdn.totvs.com/pages/viewpage.action?pageId=484701395)
43+
*
44+
* > Após a validação no interceptor, os parâmetros serão removidos do cabeçalho da requisição.
3345
*
3446
* O `Content-Type` deve ser `application/json` e a estrutura de mensagem recebida pelo serviço deve seguir o
3547
* [**Guia de implementação das APIs TOTVS**](http://tdn.totvs.com/pages/viewpage.action?pageId=484701395)
3648
* (em Mensagens de sucesso para coleções), exemplo:
37-
* - _messages: lista de mensagens de erro ou informativo resultante do serviço.
38-
* - type: success, warning, error, e information;
49+
* - _messages: lista de mensagens ou objeto de mensagem, resultante do serviço.
50+
* - type: success, warning, error, e information (será exibido a `tag` apenas se esta propriedade possuir valor);
3951
* - code: título ou código da mensagem;
4052
* - message: texto da mensagem;
4153
* - detailedMessage: detalhamento do erro ou informativo;
4254
*
55+
* Ao clicar na ação 'Detalhes' no
56+
* [`po-notification`](/documentation/po-notification) os detalhes das mensagens de sucesso e de erro são apresentados em
57+
* um [`po-modal`](/documentation/po-modal) com um [`po-accordion`](/documentation/po-accordion) que possui um item por mensagem.
58+
*
4359
* Ao importar o módulo `PoModule` na aplicação, o `po-http-interceptor` é automaticamente configurado sem a necessidade
4460
* de qualquer configuração extra.
4561
*
@@ -69,18 +85,20 @@ export abstract class PoHttpInterceptorBaseService implements HttpInterceptor {
6985

7086
notificationTypes = ['success', 'warning', 'error', 'information'];
7187

72-
constructor(private notification: any, private dialog: any) { }
88+
private httpInterceptorDetailComponent: ComponentRef<PoHttpInterceptorDetailComponent> = undefined;
89+
90+
constructor(private componentInjector: PoComponentInjectorService, private notification: any) { }
7391

7492
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
7593
const cloneRequest = request.clone();
7694

77-
request = request.headers.has(NO_ERROR_HEADER_PARAM) ? this.cloneRequestWithoutNoErrorHeaderParam(request) : request;
95+
request = request && this.hasParameters(request) ? this.cloneRequestWithoutParameters(request) : request;
7896

7997
return next.handle(request).pipe(tap((response: HttpEvent<any>) => {
8098

8199
if (response instanceof HttpResponse) {
82100

83-
this.processResponse(response);
101+
this.processResponse(response, cloneRequest);
84102

85103
}
86104
}, (error: HttpErrorResponse) => {
@@ -90,13 +108,15 @@ export abstract class PoHttpInterceptorBaseService implements HttpInterceptor {
90108
}));
91109
}
92110

93-
processResponse(response: HttpResponse<any>) {
94-
if (response.body && response.body._messages) {
111+
processResponse(response: HttpResponse<any>, request: HttpRequest<any>) {
112+
const hasNoMessageParam = this.hasNoMessageParam(request);
113+
114+
if (!hasNoMessageParam && response.body && response.body._messages) {
95115

96116
const messages = response.body._messages;
97117

98118
if (messages instanceof Array) {
99-
messages.forEach((message: any) => {
119+
messages.forEach((message: PoHttpInterceptorDetail) => {
100120
this.showNotification(message);
101121
});
102122
} else {
@@ -111,15 +131,41 @@ export abstract class PoHttpInterceptorBaseService implements HttpInterceptor {
111131
: { code: 0, message: 'Servidor não está respondendo.', detailedMessage: response.message };
112132

113133
const hasNoErrorParam = this.hasNoErrorParam(request);
134+
const hasNoMessageParam = this.hasNoMessageParam(request);
114135

115-
// not show the notification when has NoError parameter on header of request.
116-
if (errorResponse && errorResponse.message && !hasNoErrorParam) {
136+
if (errorResponse && errorResponse.message && !hasNoErrorParam && !hasNoMessageParam) {
117137
this.showNotification({ ...errorResponse, type: 'error' });
118138
}
119139
}
120140

121-
private cloneRequestWithoutNoErrorHeaderParam(request: HttpRequest<any>): HttpRequest<any> {
122-
return request && request.clone({headers: request.headers.delete(NO_ERROR_HEADER_PARAM)});
141+
private cloneRequestWithoutParameters(request: HttpRequest<any>): HttpRequest<any> {
142+
const headers = request.headers
143+
.delete(NO_ERROR_HEADER_PARAM)
144+
.delete(NO_MESSAGE_HEADER_PARAM);
145+
146+
return request.clone({ headers });
147+
}
148+
149+
private createModal(responseMessage: PoHttpInterceptorDetail) {
150+
const details = responseMessage.details ? [ responseMessage, ...responseMessage.details ] : [ responseMessage ];
151+
152+
this.httpInterceptorDetailComponent = this.componentInjector.createComponentInApplication(PoHttpInterceptorDetailComponent);
153+
this.httpInterceptorDetailComponent.instance.detail = details;
154+
this.httpInterceptorDetailComponent.instance.closed.subscribe(() => this.destroyModal());
155+
this.httpInterceptorDetailComponent.instance.open();
156+
}
157+
158+
private destroyModal() {
159+
if (this.httpInterceptorDetailComponent) {
160+
this.componentInjector.destroyComponentInApplication(this.httpInterceptorDetailComponent);
161+
this.httpInterceptorDetailComponent = undefined;
162+
}
163+
}
164+
165+
private hasMessage(responseMessage: PoHttpInterceptorDetail) {
166+
const hasMessageProperties = responseMessage.message;
167+
168+
return responseMessage && hasMessageProperties;
123169
}
124170

125171
private hasNoErrorParam(request: HttpRequest<any>): boolean {
@@ -128,7 +174,22 @@ export abstract class PoHttpInterceptorBaseService implements HttpInterceptor {
128174
return noErrorParam && noErrorParam.toString().toLocaleLowerCase() === 'true';
129175
}
130176

177+
private hasNoMessageParam(request: HttpRequest<any>): boolean {
178+
const noMessageParam = request && request.headers.get(NO_MESSAGE_HEADER_PARAM);
179+
180+
return noMessageParam && noMessageParam.toString().toLocaleLowerCase() === 'true';
181+
}
182+
183+
private hasParameters(request: HttpRequest<any>) {
184+
return request.headers.has(NO_ERROR_HEADER_PARAM) || request.headers.has(NO_MESSAGE_HEADER_PARAM);
185+
}
186+
131187
private showNotification(response: any) {
188+
189+
if (!this.hasMessage(response)) {
190+
return;
191+
}
192+
132193
const typeNotification = this.notificationTypes.includes(response.type) ? response.type : 'information';
133194

134195
const notificationAction = this.generateNotificationAction(response);
@@ -140,26 +201,26 @@ export abstract class PoHttpInterceptorBaseService implements HttpInterceptor {
140201
});
141202
}
142203

143-
private generateNotificationAction(errorResponse: any) {
204+
private generateDetailModal(responseMessage: any) {
205+
return () => {
206+
if (!this.httpInterceptorDetailComponent) {
207+
this.createModal(responseMessage);
208+
}
209+
};
210+
}
211+
212+
private generateNotificationAction(responseMessage: any) {
144213

145214
let notificationAction;
146215
let notificationLabel;
147216

148-
let notificationMessage = errorResponse.message.concat(` ${errorResponse.detailedMessage}`);
149-
150-
if (errorResponse.details && errorResponse.details instanceof Array) {
151-
errorResponse.details.forEach((detailError: any) => {
152-
notificationMessage += `\n${detailError.message}`;
153-
});
154-
}
155-
156-
if (errorResponse.helpUrl && !(errorResponse.detailedMessage || errorResponse.details)) {
217+
if (responseMessage.helpUrl && !(responseMessage.detailedMessage || responseMessage.details)) {
157218
notificationLabel = 'Ajuda';
158-
notificationAction = this.generateUrlHelpFunction(errorResponse.helpUrl);
219+
notificationAction = this.generateUrlHelpFunction(responseMessage.helpUrl);
159220

160-
} else if (errorResponse.detailedMessage || errorResponse.details) {
221+
} else if (responseMessage.detailedMessage || responseMessage.details) {
161222
notificationLabel = 'Detalhes';
162-
notificationAction = this.generateDialogDetailFunction(errorResponse, notificationMessage);
223+
notificationAction = this.generateDetailModal(responseMessage);
163224
}
164225
return { label: notificationLabel, action: notificationAction };
165226
}
@@ -168,13 +229,4 @@ export abstract class PoHttpInterceptorBaseService implements HttpInterceptor {
168229
return () => { window.open(helpUrl, '_blank'); };
169230
}
170231

171-
private generateDialogDetailFunction(errorResponse: any, notificationMessage: string) {
172-
return () => {
173-
this.dialog.alert({
174-
title: errorResponse.code,
175-
message: notificationMessage,
176-
ok: errorResponse.helpUrl ? this.generateUrlHelpFunction(errorResponse.helpUrl) : undefined
177-
});
178-
};
179-
}
180232
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
export const poHttpInterceptorDetailLiteralsDefault = {
2+
en: <any> {
3+
closeButton: 'Close',
4+
details: 'Details',
5+
detail: 'Detail',
6+
error: 'Error',
7+
warning: 'Warning',
8+
info: 'Information',
9+
success: 'Success'
10+
},
11+
es: <any> {
12+
closeButton: 'Cerrar',
13+
details: 'Detalles',
14+
detail: 'Detalle',
15+
error: 'Error',
16+
warning: 'Advertencia',
17+
info: 'Informacion',
18+
success: 'Éxito'
19+
},
20+
pt: <any> {
21+
closeButton: 'Fechar',
22+
details: 'Detalhes',
23+
detail: 'Detalhe',
24+
error: 'Erro',
25+
warning: 'Aviso',
26+
info: 'Informação',
27+
success: 'Sucesso'
28+
},
29+
ru: <any>{
30+
closeButton: 'близко',
31+
details: 'Детали',
32+
detail: 'деталь',
33+
error: 'ошибка',
34+
warning: 'предупреждение',
35+
info: 'информация',
36+
success: 'Yспех'
37+
}
38+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<po-modal
2+
p-hide-close p-size="lg"
3+
[p-primary-action]="primaryAction"
4+
[p-title]="title">
5+
6+
<div class="po-row">
7+
<po-accordion class="po-md-12 po-mt-1 po-mb-1">
8+
<po-accordion-item *ngFor="let detail of details" [p-label]="formatDetailItemTitle(detail)">
9+
10+
<div *ngIf="detail.type" class="po-row po-mb-1">
11+
<po-tag [p-color]="typeColor(detail.type)" [p-value]="typeValue(detail.type)"></po-tag>
12+
</div>
13+
14+
<div class="po-row">
15+
<p>{{detail.detailedMessage}}</p>
16+
</div>
17+
18+
</po-accordion-item>
19+
</po-accordion>
20+
</div>
21+
22+
</po-modal>

0 commit comments

Comments
 (0)