Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DTS-38481:Read exception should fail processing | <Release 10.1.0> #1180

Merged
merged 15 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 86 additions & 49 deletions UI/src/app/config/advanced-settings/advanced-settings.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,37 +39,36 @@
<div class="content select-scenario" *ngIf="selectedView === 'processor_state'">

<p-table [value]="processorData.data" [paginator]="true" [rows]="15" [loading]="dataLoading"
styleClass="p-datatable-striped" loadingIcon="loading-img" [autoLayout]="true">
styleClass="p-datatable-striped" loadingIcon="loading-img" [autoLayout]="true">

<ng-template pTemplate="caption">
<div class="table-header">
Processors
<div class="d-flex align-items-center">
<span style="font-size: 0.8em;">
Select Project
</span>
<span class="p-mr-3 p-ml-3">
<p-dropdown [options]="userProjects" [(ngModel)]="selectedProject" optionLabel="name"
[style]="{'min-width':'300px', 'max-width':'300px'}" [panelStyle]="{'min-width':'inherit', 'max-width':'inherit'}"
[filter]="true" filterBy="name" [showClear]="false" placeholder="Select a Project"
(onChange)="updateProjectSelection($event)"
>
<ng-template pTemplate="selectedItem">
<div class="userProject-item userProject-item-value" *ngIf="selectedProject">
<div>{{selectedProject.name}}</div>
</div>
</ng-template>
<ng-template let-userProject pTemplate="item">
<div class="userProject-item">
<div>{{userProject.name}}</div>
</div>
</ng-template>
</p-dropdown>
</span>
<span style="font-size: 0.8em;">
Select Project
</span>
<span class="p-mr-3 p-ml-3">
<p-dropdown [options]="userProjects" [(ngModel)]="selectedProject" optionLabel="name"
[style]="{'min-width':'300px', 'max-width':'300px'}"
[panelStyle]="{'min-width':'inherit', 'max-width':'inherit'}" [filter]="true" filterBy="name"
[showClear]="false" placeholder="Select a Project" (onChange)="updateProjectSelection($event)">
<ng-template pTemplate="selectedItem">
<div class="userProject-item userProject-item-value" *ngIf="selectedProject">
<div>{{selectedProject.name}}</div>
</div>
</ng-template>
<ng-template let-userProject pTemplate="item">
<div class="userProject-item">
<div>{{userProject.name}}</div>
</div>
</ng-template>
</p-dropdown>
</span>

</div>
</div>
</div>
</ng-template>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th id="processorName">Processor Name</th>
Expand All @@ -86,33 +85,44 @@
</td>

<td class='custom-table-cell'>
<span *ngIf="(!findTraceLogForTool(processor.processorName)?.executionOngoing && processor.processorName === 'Jira') || processor.processorName !== 'Jira'">{{ showExecutionDate(processor.processorName) }}</span>
<span
*ngIf="(!findTraceLogForTool(processor.processorName)?.executionOngoing && processor.processorName === 'Jira') || processor.processorName !== 'Jira'">{{
showExecutionDate(processor.processorName) }}</span>
</td>
<td class="custom-table-cell">
<p-progressBar mode="indeterminate" *ngIf="processor.processorName === 'Jira' && findTraceLogForTool(processor.processorName)?.executionOngoing === true " [style]="{ height: '6px' }"></p-progressBar>
<p-progressBar mode="indeterminate"
*ngIf="processor.processorName === 'Jira' && findTraceLogForTool(processor.processorName)?.executionOngoing === true "
[style]="{ height: '6px' }"></p-progressBar>
</td>
<td class='custom-table-cell'>
<div class="p-d-flex p-align-center">

<div class="p-d-flex p-align-center" *ngIf="(!findTraceLogForTool(processor.processorName)?.executionOngoing && processor.processorName === 'Jira') || processor.processorName !== 'Jira'">

<div class="p-d-flex p-align-center"
*ngIf="(!findTraceLogForTool(processor.processorName)?.executionOngoing && processor.processorName === 'Jira') || processor.processorName !== 'Jira'">
<div *ngIf="showProcessorLastState(processor.processorName) !== 'NA'" class="status-container"
[ngClass]="showProcessorLastState(processor.processorName) === 'Success' ? 'active' : 'inactive'">
{{ showProcessorLastState(processor.processorName)}}
</div>
<span *ngIf="showProcessorLastState(processor.processorName) === 'NA'">NA</span>
</div>
<div class="p-ml-2" *ngIf="processor.processorName === 'Jira'">
<i class="pi pi-info-circle" [ngClass]="{'disabled': !(findTraceLogForTool('Jira')?.progressStatusList?.length || findTraceLogForTool('Jira')?.errorMessage)}"
(click)="(findTraceLogForTool('Jira')?.errorMessage || findTraceLogForTool('Jira')?.progressStatusList?.length) ? op.toggle($event) : ''" style="font-size: 1.2rem"></i>
<div class="p-ml-2" *ngIf="processor.processorName === 'Jira'">
<i class="pi pi-info-circle"
[ngClass]="{'disabled': !(findTraceLogForTool('Jira')?.progressStatusList?.length || findTraceLogForTool('Jira')?.errorMessage)}"
(click)="(findTraceLogForTool('Jira')?.errorMessage || findTraceLogForTool('Jira')?.progressStatusList?.length) ? op.toggle($event) : ''"
style="font-size: 1.2rem"></i>
</div>
</div>
</div>
</td>
<td class='custom-table-cell p-d-flex p-align-center column-width'>
<button pButton pRipple label="Run Now"
icon="{{findTraceLogForTool(processor.processorName)?.executionOngoing === true ? 'pi pi-spin pi-spinner' : 'pi pi-forward'}}"
[disabled]="(shouldDisableRunProcessor() || findTraceLogForTool(processor.processorName)?.executionOngoing === true) && processor.processorName !== 'Jira' "
class="p-button-sm p-button-success p-button-raised" (click)="runProcessor(processor.processorName)"></button>
<div class="action-btns p-ml-3" [ngClass]="{'disabled':!(getToolDetailsForProcessor(processor?.processorName)?.length > 0 && findTraceLogForTool(processor?.processorName)) || findTraceLogForTool(processor?.processorName)?.isDeleteDisable}" *ngIf="!shouldDisableRunProcessor()"><i class="far fa-trash-alt" (click)="deleteProcessorData(processor)"></i></div>
class="p-button-sm p-button-success p-button-raised"
(click)="runProcessor(processor.processorName)"></button>
<div class="action-btns p-ml-3"
[ngClass]="{'disabled':!(getToolDetailsForProcessor(processor?.processorName)?.length > 0 && findTraceLogForTool(processor?.processorName)) || findTraceLogForTool(processor?.processorName)?.isDeleteDisable}"
*ngIf="!shouldDisableRunProcessor()"><i class="far fa-trash-alt"
(click)="deleteProcessorData(processor)"></i></div>
</td>
</tr>
</ng-template>
Expand All @@ -131,34 +141,61 @@

<p-confirmDialog #cd [transitionOptions]="'0ms'">
<p-footer>
<button type="button" (click)="cd.accept()" pButton label="Yes"></button>
<button type="button" (click)="cd.reject()" pButton label="No"></button>
<button type="button" (click)="cd.accept()" pButton label="Yes"></button>
<button type="button" (click)="cd.reject()" pButton label="No"></button>
</p-footer>
</p-confirmDialog>

<p-overlayPanel #op [showCloseIcon]="false">
<div class="progress-dialog-body p-mb-4">
<section *ngIf="findTraceLogForTool('Jira')?.progressStatusList?.length && !(findTraceLogForTool('Jira')?.errorMessage); else logError">
<h3>Progress Stats:</h3>
<p-table [value]="findTraceLogForTool('Jira')?.progressStatusList" [tableStyle]="{ 'min-width': '40rem' }"
[scrollable]="true" scrollHeight="200px" *ngIf="findTraceLogForTool('Jira')?.progressStatusList?.length && !(findTraceLogForTool('Jira')?.errorMessage) ; else logError">
[scrollable]="true" scrollHeight="200px">
<ng-template pTemplate="header">
<tr>
<th>Step Name</th>
<th>End Time</th>
<th>Status</th>
</tr>
<tr>
<th>Step Name</th>
<th>End Time</th>
<th>Status</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-item>
<tr>
<td>{{item.stepName}}</td>
<td>{{endTimeConversion(item?.endTime)}}</td>
<td>{{item.status}}</td>
</tr>

<tr>
<td>{{item.stepName}}</td>
<td>{{endTimeConversion(item?.endTime)}}</td>
<td>{{item.status}}</td>
</tr>
</ng-template>
</p-table>
</p-table>
</section>

<ng-template #logError>
<h3 class="p-text-center" class="error-text"><img src="../../../assets/img/icon-failure.svg" alt="Failure" class="p-mr-1" />{{ findTraceLogForTool('Jira')?.errorMessage}}</h3>
</ng-template>

<section *ngIf="findTraceLogForTool('Jira')?.errorDetailList?.length > 0">
<h3>Errors:</h3>
<p-table [value]="findTraceLogForTool('Jira')?.errorDetailList" [tableStyle]="{ 'min-width': '40rem' }"
[scrollable]="true" scrollHeight="200px">
<ng-template pTemplate="header">
<tr>
<th>URL</th>
<th>Impact</th>
<th>Status</th>
<th>Error</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-item>
<tr>
<td style="word-break: break-all">{{item.url}}</td>
<td>{{item.impact}}</td>
<td>{{item.status}}</td>
<td>{{item.error}}</td>
</tr>
</ng-template>
</p-table>
</section>

<ul class="p-pr-1 p-pl-0 list-style-none" *ngIf="dataMismatchObj['Jira']">
<li class="tooltip-item p-d-flex p-pt-2">
<div class="icon-container">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.mapping.Document;

import com.publicissapient.kpidashboard.common.model.application.ErrorDetail;
import com.publicissapient.kpidashboard.common.model.application.ProgressStatus;

import lombok.Data;
Expand Down Expand Up @@ -58,4 +59,6 @@ public class ProcessorExecutionTraceLog {
private String errorMessage;
private String failureLog;
private List<ProgressStatus> progressStatusList;
//save any resource not found error
private List<ErrorDetail> errorDetailList;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2014 CapitalOne, LLC.
* Further development Copyright 2022 Sapient Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.publicissapient.kpidashboard.common.model.application;

import lombok.Data;

@Data
public class ErrorDetail {
private int status;
private String url;
private String impact;
private String error;

public ErrorDetail(int status, String url, String error, String impact) {
this.status = status;
this.url = url;
this.impact = impact;
this.error = error;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.bson.types.ObjectId;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
Expand All @@ -55,10 +56,13 @@
import com.atlassian.jira.rest.client.api.domain.SearchResult;
import com.publicissapient.kpidashboard.common.client.KerberosClient;
import com.publicissapient.kpidashboard.common.exceptions.ClientErrorMessageEnum;
import com.publicissapient.kpidashboard.common.model.ProcessorExecutionTraceLog;
import com.publicissapient.kpidashboard.common.model.ToolCredential;
import com.publicissapient.kpidashboard.common.model.application.ErrorDetail;
import com.publicissapient.kpidashboard.common.model.application.ProjectVersion;
import com.publicissapient.kpidashboard.common.model.connection.Connection;
import com.publicissapient.kpidashboard.common.processortool.service.ProcessorToolConnectionService;
import com.publicissapient.kpidashboard.common.repository.tracelog.ProcessorExecutionTraceLogRepository;
import com.publicissapient.kpidashboard.common.service.AesEncryptionService;
import com.publicissapient.kpidashboard.common.service.ToolCredentialProvider;
import com.publicissapient.kpidashboard.common.util.DateUtil;
Expand Down Expand Up @@ -91,6 +95,8 @@ public class JiraCommonService {
private AesEncryptionService aesEncryptionService;
@Autowired
private ProcessorToolConnectionService processorToolConnectionService;
@Autowired
private ProcessorExecutionTraceLogRepository processorExecutionTraceLogRepository;

/**
* @param projectConfig
Expand All @@ -106,6 +112,7 @@ public class JiraCommonService {
public String getDataFromClient(ProjectConfFieldMapping projectConfig, URL url, KerberosClient krb5Client)
throws IOException {
Optional<Connection> connectionOptional = projectConfig.getJira().getConnection();
ObjectId projectConfigId = projectConfig.getBasicProjectConfigId();
boolean spenagoClient = connectionOptional.map(Connection::isJaasKrbAuth).orElse(false);
if (spenagoClient) {
HttpUriRequest request = RequestBuilder.get().setUri(url.toString())
Expand All @@ -114,7 +121,7 @@ public String getDataFromClient(ProjectConfFieldMapping projectConfig, URL url,
String responce = krb5Client.getResponse(request);
return responce;
} else {
return getDataFromServer(url, connectionOptional);
return getDataFromServer(url, connectionOptional, projectConfigId);
}
}

Expand All @@ -127,7 +134,7 @@ public String getDataFromClient(ProjectConfFieldMapping projectConfig, URL url,
* @throws IOException
* IOException
*/
public String getDataFromServer(URL url, Optional<Connection> connectionOptional) throws IOException {
public String getDataFromServer(URL url, Optional<Connection> connectionOptional, ObjectId projectConfigId) throws IOException {
HttpURLConnection request = (HttpURLConnection) url.openConnection();

String username = null;
Expand All @@ -154,6 +161,8 @@ public String getDataFromServer(URL url, Optional<Connection> connectionOptional
request.setRequestProperty("Authorization", "Basic " + encodeCredentialsToBase64(username, password)); // NOSONAR
}
request.connect();
// process the client error
processClientError(connectionOptional, request, projectConfigId);
StringBuilder sb = new StringBuilder();
try (InputStream in = (InputStream) request.getContent();
BufferedReader inReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
Expand All @@ -174,6 +183,71 @@ public String getDataFromServer(URL url, Optional<Connection> connectionOptional
return sb.toString();
}

/**
* Method to process client error and update the connection broken flag
*
* @param connectionOptional
* connectionOptional
* @param request
* request
* @throws IOException
* throw IO Error
*/
private void processClientError(Optional<Connection> connectionOptional, HttpURLConnection request,
ObjectId basicProjectConfigId) throws IOException {
int responseCode = request.getResponseCode();
if (responseCode >= 400 && responseCode < 500) {
// Read error message from the server
String errorMessage = readErrorStream(request.getErrorStream());
if (responseCode == 404) {
ErrorDetail errorDetail = new ErrorDetail(responseCode, request.getURL().toString(), errorMessage,
determineImpactBasedOnUrl(request.getURL().toString()));
Optional<ProcessorExecutionTraceLog> existingTraceLog = processorExecutionTraceLogRepository
.findByProcessorNameAndBasicProjectConfigIdAndProgressStatsTrue(JiraConstants.JIRA,
basicProjectConfigId.toString());
existingTraceLog.ifPresent(traceLog -> {
List<ErrorDetail> errorDetailList = Optional.ofNullable(traceLog.getErrorDetailList())
.orElseGet(ArrayList::new);
errorDetailList.add(errorDetail);
traceLog.setErrorDetailList(errorDetailList);
processorExecutionTraceLogRepository.save(traceLog);
});
}
// flagging the connection flag w.r.t error code.
connectionOptional.ifPresent(connection -> {
String errMsg = ClientErrorMessageEnum.fromValue(responseCode).getReasonPhrase();
processorToolConnectionService.updateBreakingConnection(connection.getId(), errMsg);
});
log.error("Exception when reading from server {} - {}", responseCode, errorMessage);
// Throw exception for non-404 errors, as 404 indicates the resource mightn't exist
if (responseCode != 404) {
request.disconnect();
throw new IOException(String.format("Error: %d - %s", responseCode, errorMessage));
}
}
}

private String readErrorStream(InputStream errorStream) throws IOException {
StringBuilder response = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
}
return response.toString();
}

private String determineImpactBasedOnUrl(String url) {
if (url.contains("sprint")) {
return "Sprint KPI's";
} else if (url.contains("versions")) {
return "Release KPI's";
} else if (url.contains("epic")) {
return "Epic KPI's";
}
return ""; // Default or unknown impact
}
/**
*
* @param connectionOptional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public void setExecutionOngoingForProcessor(String processorName, String basicPr
processorExecutionTraceLog.setProgressStatusList(new ArrayList<>()); // clear the prev record
processorExecutionTraceLog.setErrorMessage(null); // Clear the error message
processorExecutionTraceLog.setFailureLog(null); // Clear the failure log message
processorExecutionTraceLog.setErrorDetailList(new ArrayList<>());
}
log.info("ProjectId {} for processor {} executionOngoing to {} ", basicProjectConfigId, processorName ,
executionOngoing);
Expand Down
Loading
Loading