diff --git a/UI/src/app/config/capacity-planning/capacity-planning.component.html b/UI/src/app/config/capacity-planning/capacity-planning.component.html index f73c406d72..09e8cf6732 100644 --- a/UI/src/app/config/capacity-planning/capacity-planning.component.html +++ b/UI/src/app/config/capacity-planning/capacity-planning.component.html @@ -122,7 +122,14 @@

Added Users

   Domain - Planned + + + Squad + + + Planned Capacity (in Hrs) Leaves (in Hrs) @@ -150,6 +157,18 @@

Added Users

   {{assignee?.role ? projectAssigneeRolesObj[assignee?.role] : '- -'}} + + + + + {{ getNodeName(assignee) }} + + Added Users   - + + + Squad + + +
Planned Capacity (in Hrs) @@ -339,6 +365,18 @@

Added Users

   {{assignee?.role ? projectAssigneeRolesObj[assignee?.role] : '- -'}} + + + + + {{ getNodeName(assignee) }} + + Added Users   - @@ -419,7 +456,7 @@

Added Users

  
- + @@ -433,35 +470,36 @@

Added Users

  
- -
-
- - - - -
-
- - -
-
- - -
+ + +
+ + + + +
+
+ + +
+
+ + +
- -
-
- - -
-
+ + +
+ +
+
+ + + +
+
+ + +
+
+ + + +
+
+
+ + +
+
+
+ +
+ -

{{testExecutionErrorMessage}}

- -

{{capacityErrorMessage}}

-
-
- - - - - -
+

{{testExecutionErrorMessage}}

+ +

{{capacityErrorMessage}}

+
+
+ + + + + +
- \ No newline at end of file + diff --git a/UI/src/app/config/capacity-planning/capacity-planning.component.ts b/UI/src/app/config/capacity-planning/capacity-planning.component.ts index 7c4049768f..a24a99fae8 100644 --- a/UI/src/app/config/capacity-planning/capacity-planning.component.ts +++ b/UI/src/app/config/capacity-planning/capacity-planning.component.ts @@ -50,6 +50,7 @@ export class CapacityPlanningComponent implements OnInit { @ViewChild('manageAssignee') manageAssignee: ManageAssigneeComponent; trendLineValueList: any[]; projectListArr: Array = []; + squadListArr: Array = []; filterForm: UntypedFormGroup; tabHeaders = ['Scrum', 'Kanban']; tabContentHeaders = { upload_tep: 'Test Execution Percentage Table', upload_Sprint_Capacity: 'Capacity Table' }; @@ -63,7 +64,9 @@ export class CapacityPlanningComponent implements OnInit { endDate: any; executionDate: any; popupForm: UntypedFormGroup; + squadForm: UntypedFormGroup; selectedSprintAssigneFormArray = []; + selectedSquad = []; selectedSprintAssigneValidator = []; isCapacitySaveDisabled = true; capacityErrorMessage = ''; @@ -76,6 +79,7 @@ export class CapacityPlanningComponent implements OnInit { sprintDetails: any; projectDetails: any; selectedProjectBaseConfigId: string; + selectedProjectName : string; selectedSprintDetails: any; selectedSprintId: any; selectedSprintName: any; @@ -180,6 +184,9 @@ export class CapacityPlanningComponent implements OnInit { this.popupForm = new UntypedFormGroup({ capacity: new UntypedFormControl() }); + + //add additionalFilterCapacity checks + this.squadForm = new UntypedFormGroup({}); } // called when user switches the "Scrum/Kanban" switch @@ -207,13 +214,14 @@ export class CapacityPlanningComponent implements OnInit { this.capacityScrumData = []; this.projectDetails = {}; this.selectedProjectBaseConfigId = ''; + this.selectedProjectName = ''; this.getFilterDataOnLoad(); } // gets data for filters on load getFilterDataOnLoad() { - if (this.filter_kpiRequest && this.filter_kpiRequest !== '') { + if (this.filter_kpiRequest !== '') { this.filter_kpiRequest.unsubscribe(); } @@ -224,22 +232,19 @@ export class CapacityPlanningComponent implements OnInit { this.selectedFilterData['sprintIncluded'] = ['CLOSED', 'ACTIVE', 'FUTURE']; this.filter_kpiRequest = this.http_service.getFilterData(this.selectedFilterData) .subscribe(filterData => { - if (filterData[0] !== 'error') { + if (filterData[0] !== 'error' && filterData?.['data']?.length > 0) { this.filterData = filterData['data']; - if (this.filterData && this.filterData.length > 0) { this.projectListArr = this.sortAlphabetically(this.filterData.filter(x => x.labelName.toLowerCase() == 'project')); + this.squadListArr = this.getSortedAdditonalFilter(this.projectListArr); this.projectListArr = this.makeUniqueArrayList(this.projectListArr); + this.squadListArr = this.makeUniqueArrayList(this.squadListArr); const defaultSelection = this.selectedProjectBaseConfigId ? false : true; this.checkDefaultFilterSelection(defaultSelection); - if (Object.keys(filterData).length === 0) { + if (!Object.keys(filterData).length) { this.resetProjectSelection(); // show error message this.messageService.add({ severity: 'error', summary: 'Projects not found.' }); } - } else { - this.resetProjectSelection(); - } - } else { this.resetProjectSelection(); @@ -293,6 +298,7 @@ export class CapacityPlanningComponent implements OnInit { const selectedProject = this.filterForm?.get('selectedProjectValue')?.value; this.projectDetails = { ...this.trendLineValueList.find(i => i.nodeId === selectedProject) }; this.selectedProjectBaseConfigId = this.projectDetails?.basicProjectConfigId; + this.getSquadsOfSelectedProject(this.projectDetails?.nodeId); this.getProjectBasedData(); this.isAdminForSelectedProject = this.getAuthorizationService.checkIfSuperUser() || !this.getAuthorizationService.checkIfViewer(this.projectDetails); } @@ -305,6 +311,12 @@ export class CapacityPlanningComponent implements OnInit { } } + getSquadsOfSelectedProject(projectName){ + if (projectName) { + this.selectedSquad = [...this.squadListArr?.filter((x) => x['path'][0]?.includes(projectName))]; + } + } + getCapacityData(projectId) { this.http_service.getCapacityData(projectId).subscribe((response) => { if (response && response?.success && response?.data) { @@ -495,6 +507,7 @@ export class CapacityPlanningComponent implements OnInit { this.selectedSprintAssigneFormArray.push( { role: new FormControl(assignee.role), + squad: new FormControl(assignee.squad), plannedCapacity: new FormControl({ value: assignee.plannedCapacity, disabled: !assignee.role }, [Validators.pattern('[0-9]*')]), leaves: new FormControl({ value: assignee.leaves, disabled: !(assignee?.role && assignee?.plannedCapacity) }, [Validators.min(0), Validators.max(assignee.plannedCapacity)]) } @@ -505,7 +518,8 @@ export class CapacityPlanningComponent implements OnInit { calculateAvaliableCapacity(assignee, assigneeFormControls, fieldName) { assignee[fieldName] = assigneeFormControls[fieldName]?.value; - if (fieldName === 'role') { + + if (this.checkMandatoryFields(assigneeFormControls, fieldName)) { assigneeFormControls.plannedCapacity.enable(); } else { if (assigneeFormControls.plannedCapacity.value > 0) { @@ -541,6 +555,7 @@ export class CapacityPlanningComponent implements OnInit { onSprintCapacitySave(selectedSprint) { selectedSprint.capacity = this.calculateTotalCapacityForSprint(selectedSprint); + selectedSprint.additionalFilterCapacityList = this.generateAdditionalFilterCapacityList( selectedSprint); this.projectCapacityEditMode = false; const postData = { ...selectedSprint }; delete postData['id']; @@ -586,12 +601,28 @@ export class CapacityPlanningComponent implements OnInit { }); } + enableDisableSubmitButton() { if (this.selectedView === 'upload_Sprint_Capacity') { - this.enableDisableCapacitySubmitButton(); + if(this.selectedSquad.length>0 && this.squadForm!=null){ + this.isCapacitySaveDisabled = false; + this.capacityErrorMessage = ''; + Object.entries(this.squadForm?.value).forEach(([key, value]) => { + if (value == null || (value instanceof FormControl && value.value == null)) { + this.isCapacitySaveDisabled = true; + this.capacityErrorMessage = 'Please enter Capacity'; + return; + } + }); + + } + else{ + this.enableDisableCapacitySubmitButton(); + } } } + enableDisableCapacitySubmitButton() { if (this.popupForm.get('capacity')?.value && this.popupForm.get('capacity')?.value === 'Enter Value') { this.isCapacitySaveDisabled = true; @@ -629,10 +660,45 @@ export class CapacityPlanningComponent implements OnInit { this.reqObj['sprintNodeId'] = this.selectedSprintId; } if (this.selectedView === 'upload_Sprint_Capacity') { - this.popupForm = new UntypedFormGroup({ - capacity: new UntypedFormControl(data?.capacity ? data?.capacity : '') - }); - this.reqObj['capacity'] = data?.capacity ? data?.capacity : '';; + + if (this.selectedSquad.length > 0) { + + if (data?.additionalFilterCapacityList) { + const flattened = data.additionalFilterCapacityList.flatMap(item => item.nodeCapacityList); + this.selectedSquad.forEach(squad => { + const matchingFilter = flattened.find(filter => filter.additionalFilterId === squad.nodeId); + + if (matchingFilter) { + if (!this.squadForm.controls[squad.nodeId]) { + this.squadForm.addControl(squad.nodeId, new UntypedFormControl(matchingFilter.additionalFilterCapacity)); + } else { + this.squadForm.controls[squad.nodeId].setValue(matchingFilter.additionalFilterCapacity); + } + } else { + if (!this.squadForm.controls[squad.nodeId]) { + this.squadForm.addControl(squad.nodeId, new UntypedFormControl(null)); + } + } + + }); + } + else { + this.selectedSquad.forEach(squad => { + let control= new UntypedFormControl(); + if (this.squadForm.get(squad.nodeId)) { + // Update existing control + this.squadForm.get(squad.nodeId).setValue(control); + } else { + // Add new control + this.squadForm.addControl(squad.nodeId, control); + } + }); + } + } + + this.popupForm = new UntypedFormGroup({capacity: new UntypedFormControl(data?.capacity ? data?.capacity : '')}); + + this.reqObj['capacity'] = data?.capacity ? data?.capacity : ''; if (this.kanban) { this.reqObj['startDate'] = data?.startDate; this.reqObj['endDate'] = data?.endDate; @@ -651,7 +717,13 @@ export class CapacityPlanningComponent implements OnInit { // called on the click of the Submit button when creating capacity per sprint(hrs) submitCapacity() { - this.reqObj['capacity'] = this.popupForm?.get('capacity').value; + if(this.squadForm?.value && Object.keys(this.squadForm.value).length > 0){ + this.reqObj['additionalFilterCapacityList'] = this.toggleOffGenerateAdditionalFilterCapacityList(this.squadForm?.value) + this.reqObj['capacity'] = Object.values(this.squadForm?.value).reduce((acc: number, value: number) => acc + value, 0).toString(); + } + else{ + this.reqObj['capacity'] = this.popupForm?.get('capacity').value; + } this.http_service.saveCapacity(this.reqObj) .subscribe(response => { if (response.success) { @@ -672,6 +744,7 @@ export class CapacityPlanningComponent implements OnInit { this.isCapacitySaveDisabled = true; this.capacityErrorMessage = ''; }); + } setFormValuesEmpty() { @@ -689,11 +762,23 @@ export class CapacityPlanningComponent implements OnInit { } }); } + if (this.reqObj) { for (const capReqField in this.reqObj) { this.reqObj[capReqField] = ''; } } + + this.clearSquadForm(); + } + + clearSquadForm(){ + if (this.squadForm && this.squadForm.controls) { + Object.keys(this.squadForm?.controls).forEach(key => { + this.squadForm.removeControl(key); + }); + + } } @@ -709,5 +794,103 @@ export class CapacityPlanningComponent implements OnInit { } } + checkMandatoryFields(assigneeFormControls, fieldName) { + if(fieldName==='role'|| fieldName==='squad'){ + const roleValue = assigneeFormControls['role']?.value; + const squadValue = assigneeFormControls['squad']?.value; + if (this.selectedSquad.length > 0){ + if((squadValue !== null) && (roleValue !== null)){ + return true; + } + } + else if(roleValue !== null){ + return true; + } + } + return false; + } + + generateAdditionalFilterCapacityList(selectedSprint) { + if (this.selectedSquad.length > 0){ + const squadCapacityMap: { [key: string]: { [key: string]: number } } = {}; + + // Group by squad and sum availableCapacity + selectedSprint.assigneeCapacity.forEach(member => { + const squad = this.selectedSquad.find(squad => squad.nodeId === member.squad); + if (squad) { + if (!squadCapacityMap[squad.labelName]) { + squadCapacityMap[squad.labelName] = {}; + } + if (!squadCapacityMap[squad.labelName][squad.nodeId]) { + squadCapacityMap[squad.labelName][squad.nodeId] = 0; + } + squadCapacityMap[squad.labelName][squad.nodeId] += member.availableCapacity; + } + }); + + return this.createAdditionalFilterCapacityList(squadCapacityMap); + } + +} + +toggleOffGenerateAdditionalFilterCapacityList(capacityObject: { [key: string]: number }) { + if (this.selectedSquad.length > 0) { + const squadCapacityMap: { [key: string]: { [key: string]: number } } = {}; + + // Group by squad and sum availableCapacity + for (const key in capacityObject) { + const value = capacityObject[key]; + const squad = this.selectedSquad.find(squad => key.includes(squad.nodeId)); + if (squad) { + if (!squadCapacityMap[squad.labelName]) { + squadCapacityMap[squad.labelName] = {}; + } + if (!squadCapacityMap[squad.labelName][squad.nodeId]) { + squadCapacityMap[squad.labelName][squad.nodeId] = 0; + } + squadCapacityMap[squad.labelName][squad.nodeId] += value; + } + } + return this.createAdditionalFilterCapacityList(squadCapacityMap); + } +} + + createAdditionalFilterCapacityList( squadCapacityMap ){ + const additionalFilterCapacityList = []; + + for (const labelName in squadCapacityMap) { + const nodeCapacityList = Object.keys(squadCapacityMap[labelName]).map(nodeId => { + return { + additionalFilterId: nodeId, + additionalFilterCapacity: squadCapacityMap[labelName][nodeId] + }; + }); + + additionalFilterCapacityList.push({ + filterId: labelName, + nodeCapacityList: nodeCapacityList + }); + } + + return additionalFilterCapacityList; + + } + +getNodeName(assignee) { + if (assignee?.squad) { + const squad = this.selectedSquad.find(s => s.nodeId === assignee.squad); + return squad ? squad.nodeName : '- -'; + } + return '- -'; +} + + +getSortedAdditonalFilter(projectListArr) { +//Get the levels of the projects in projectListArr + let projectMap= projectListArr.map(project => project.level); +// Step 3: Filter out the objects from filterData which have a level that is exactly 2 levels above any project level + return this.sortAlphabetically(this.filterData.filter(data => projectMap.includes(data.level - 2))); + +} } diff --git a/UI/src/app/config/project-config/basic-config/basic-config.component.html b/UI/src/app/config/project-config/basic-config/basic-config.component.html index 2962fbe43f..ad25172e1b 100644 --- a/UI/src/app/config/project-config/basic-config/basic-config.component.html +++ b/UI/src/app/config/project-config/basic-config/basic-config.component.html @@ -86,7 +86,7 @@
Basic Configuration
+ required="required" [dropdown]="true" field="name">
{{country.name}}
@@ -151,4 +151,4 @@
Basic Configuration
- \ No newline at end of file + diff --git a/UI/src/app/config/project-config/basic-config/basic-config.component.ts b/UI/src/app/config/project-config/basic-config/basic-config.component.ts index a48123e086..3d9d433ee1 100644 --- a/UI/src/app/config/project-config/basic-config/basic-config.component.ts +++ b/UI/src/app/config/project-config/basic-config/basic-config.component.ts @@ -183,7 +183,7 @@ export class BasicConfigComponent implements OnInit { stringValidator(control: AbstractControl): { [key: string]: boolean } | null { const inputValue: string = control.value as string; - if ((typeof control.value === 'string' || control.value instanceof String) && control.value && control.value !== null && !/^[a-zA-Z0-9\s]+$/.test(inputValue)) { + if ((typeof control.value === 'string' || control.value instanceof String) && control.value && control.value !== null && !/^[a-zA-Z0-9\s_-]+$/.test(inputValue)) { return { stringValidator: true }; } return null; diff --git a/UI/src/app/config/project-config/jira-config/jira-config.component.html b/UI/src/app/config/project-config/jira-config/jira-config.component.html index 2259d5a3f7..de0b4828cc 100644 --- a/UI/src/app/config/project-config/jira-config/jira-config.component.html +++ b/UI/src/app/config/project-config/jira-config/jira-config.component.html @@ -202,7 +202,7 @@
{{formTitle}} Configuration
+ [attr.disabled]="!this[form_elem?.disabled] && this[form_elem?.disabled]!=undefined? true : null">
@@ -324,4 +324,4 @@
{{formTitle}} Configuration
- \ No newline at end of file + diff --git a/UI/src/app/config/project-config/jira-config/jira-config.component.ts b/UI/src/app/config/project-config/jira-config/jira-config.component.ts index 963b88b58b..2ba581b499 100644 --- a/UI/src/app/config/project-config/jira-config/jira-config.component.ts +++ b/UI/src/app/config/project-config/jira-config/jira-config.component.ts @@ -104,6 +104,7 @@ export class JiraConfigComponent implements OnInit { jiraTemplate : any[]; gitActionWorkflowNameList : any[]; cloudEnv : any ; + isGitlabToolFieldEnabled : boolean; constructor( private formBuilder: UntypedFormBuilder, @@ -118,6 +119,7 @@ export class JiraConfigComponent implements OnInit { ngOnInit(): void { this.selectedProject = this.sharedService.getSelectedProject(); + this.isGitlabToolFieldEnabled = this.sharedService.getGlobalConfigData()?.gitlabToolFieldFlag; if (!this.selectedProject) { this.router.navigate(['./dashboard/Config/ProjectList']); } @@ -137,6 +139,13 @@ export class JiraConfigComponent implements OnInit { } this.getConnectionList(this.urlParam); this.initializeFields(this.urlParam); + + if(this.isGitlabToolFieldEnabled){ + this.showFormElements(['gitLabID']) + } else { + this.hideFormElements(['gitLabID']) + } + this.getJiraTemplate(); } else { this.router.navigate(['./dashboard/Config/ProjectList']); @@ -1259,7 +1268,7 @@ export class JiraConfigComponent implements OnInit { { field: 'branch', header: 'Branch', class: 'long-text' }, { field: 'gitLabSdmID', header: 'SDM ID', class: 'long-text' }, ]; - + this.formTemplate = { group: 'Sonar', elements: [ @@ -1672,6 +1681,17 @@ export class JiraConfigComponent implements OnInit { Impacted : All GitLab based KPIs`, }, + { + type: 'array', + label: 'GitLab Ids', + id: 'gitLabID', + validators: ['required'], + containerClass: 'p-sm-6', + show: true, + tooltip: `list of inputs to access GitLab data.
+ + Impacted : All GitLab based KPIs`, + } ], }; } @@ -1966,6 +1986,15 @@ export class JiraConfigComponent implements OnInit { this.formTemplate = { group: 'JiraTest', elements: [ + { + type: 'textarea', + label: 'JQL Query to fetch Test Cases (Optional)', + id: 'boardQuery', + validators: [], + containerClass: 'p-sm-6', + disabled: false, + show: true, + }, { type: 'text', label: 'JIRATEST Project Key', @@ -2093,7 +2122,7 @@ export class JiraConfigComponent implements OnInit { tooltip: `Select status like "Abandoned", "Deprecated" etc so that these can be excluded from Regression automation coverage, In Sprint automation coverage and Test case without story link KPI`, show: true, isLoading: false, - }, + } ], }; } @@ -2170,7 +2199,7 @@ export class JiraConfigComponent implements OnInit { }else{ validatorArr.push(Validators.pattern(element.value)); } - + }); group[inputTemplate.id] = new UntypedFormControl('', validatorArr); @@ -2624,5 +2653,5 @@ export class JiraConfigComponent implements OnInit { self.tool['apiVersion'].enable(); self.tool['projectKey'].enable(); } - } + } } diff --git a/UI/src/app/config/project-config/tool-menu/tool-menu.component.spec.ts b/UI/src/app/config/project-config/tool-menu/tool-menu.component.spec.ts index 145e51e17f..f540b0c373 100644 --- a/UI/src/app/config/project-config/tool-menu/tool-menu.component.spec.ts +++ b/UI/src/app/config/project-config/tool-menu/tool-menu.component.spec.ts @@ -186,7 +186,7 @@ describe('ToolMenuComponent', () => { expect(messageServiceSpy).toHaveBeenCalled(); }); - it('should copy token to clipboard',()=>{ + xit('should copy token to clipboard',()=>{ component.generatedToken='TestToken1'; component.copyToken(); expect(component.tokenCopied).toBeTrue(); diff --git a/UI/src/app/dashboard/backlog/backlog.component.html b/UI/src/app/dashboard/backlog/backlog.component.html index 066f59c822..1fa3a98fd1 100644 --- a/UI/src/app/dashboard/backlog/backlog.component.html +++ b/UI/src/app/dashboard/backlog/backlog.component.html @@ -75,7 +75,7 @@

{{x?.label}} [ngClass]="{'backlog-dashboard' : kpi?.kpiDetail?.chartType ==='progress-bar'}" cdkDrag cdkDragLockAxis="y"> {{x?.label}} [ngClass]="{'backlog-dashboard' : kpi?.kpiDetail?.chartType ==='progress-bar'}" cdkDrag cdkDragLockAxis="y"> {{x?.label}} [ngClass]="{'backlog-dashboard' : kpi?.kpiDetail?.chartType ==='progress-bar'}" cdkDrag cdkDragLockAxis="y"> {{x?.label}} [ngClass]="{'backlog-dashboard' : kpi?.kpiDetail?.chartType ==='progress-bar'}" cdkDrag cdkDragLockAxis="y"> @@ -39,7 +39,7 @@
+ *ngIf="(kpiChartData[kpi?.kpiId]?.length > 0 && chartColorList[kpi?.kpiId]?.length>0 && !iSAdditionalFilterSelected) || (kpiChartData[kpi?.kpiId]?.length > 0 && chartColorList[kpi?.kpiId]?.length>0 && kpi?.kpiDetail?.isAdditionalFilterSupport && iSAdditionalFilterSelected); else noData">
+ *ngIf="(kpiChartData[kpi.kpiId] && kpiChartData[kpi.kpiId]?.length && !iSAdditionalFilterSelected) || (kpiChartData[kpi.kpiId] && kpiChartData[kpi.kpiId]?.length && kpi?.kpiDetail?.isAdditionalFilterSupport && iSAdditionalFilterSelected)|| kpiLoader">
+ *ngIf="(kpiChartData[kpi.kpiId] && kpiChartData[kpi.kpiId]?.length === 0 && !iSAdditionalFilterSelected && kpiLoader === false) || (kpiChartData[kpi.kpiId] && kpiChartData[kpi.kpiId]?.length === 0 && kpi?.kpiDetail?.isAdditionalFilterSupport && iSAdditionalFilterSelected && kpiLoader === false)">
No Data Available
+ *ngIf="kpi?.kpiDetail?.isAdditionalFilterSupport === false && iSAdditionalFilterSelected && kpiLoader === false">
KPI cannot be measured for a Squad
diff --git a/UI/src/app/dashboard/dora/dora.component.html b/UI/src/app/dashboard/dora/dora.component.html index d2a9183b4c..df50095a08 100644 --- a/UI/src/app/dashboard/dora/dora.component.html +++ b/UI/src/app/dashboard/dora/dora.component.html @@ -27,7 +27,7 @@
Deployment Frequency / Week

Deployment Frequency / Week
@@ -39,7 +39,7 @@
+ *ngIf="(kpiChartData[kpi?.kpiId]?.length > 0 && chartColorList[kpi?.kpiId]?.length>0 && !iSAdditionalFilterSelected) || (kpiChartData[kpi?.kpiId]?.length > 0 && chartColorList[kpi?.kpiId]?.length>0 && kpi?.kpiDetail?.isAdditionalFilterSupport && iSAdditionalFilterSelected); else noData">
+ *ngIf="(kpiChartData[kpi.kpiId] && kpiChartData[kpi.kpiId]?.length && !iSAdditionalFilterSelected) || (kpiChartData[kpi.kpiId] && kpiChartData[kpi.kpiId]?.length && kpi?.kpiDetail?.isAdditionalFilterSupport && iSAdditionalFilterSelected)|| kpiLoader">
+ *ngIf="(kpiChartData[kpi.kpiId] && kpiChartData[kpi.kpiId]?.length === 0 && !iSAdditionalFilterSelected && kpiLoader === false) || (kpiChartData[kpi.kpiId] && kpiChartData[kpi.kpiId]?.length === 0 && kpi?.kpiDetail?.isAdditionalFilterSupport && iSAdditionalFilterSelected && kpiLoader === false)">
No Data Available
+ *ngIf="kpi?.kpiDetail?.isAdditionalFilterSupport === false && iSAdditionalFilterSelected && kpiLoader === false">
KPI cannot be measured for a Squad
diff --git a/UI/src/app/dashboard/executive/executive.component.ts b/UI/src/app/dashboard/executive/executive.component.ts index 13a92cd5e3..11460a197e 100644 --- a/UI/src/app/dashboard/executive/executive.component.ts +++ b/UI/src/app/dashboard/executive/executive.component.ts @@ -151,7 +151,7 @@ export class ExecutiveComponent implements OnInit, OnDestroy { this.trendBoxColorObj[nodeName] = this.trendBoxColorObj[key]; tempObj[nodeName] = []; } - this.kpiTableDataObj = {...tempObj}; + this.kpiTableDataObj = {...tempObj}; if (this.kpiChartData && Object.keys(this.kpiChartData)?.length > 0) { for (const key in this.kpiChartData) { this.kpiChartData[key] = this.generateColorObj(key, this.kpiChartData[key]); @@ -271,7 +271,7 @@ export class ExecutiveComponent implements OnInit, OnDestroy { for (const key in this.colorObj) { const idx = key.lastIndexOf('_'); const nodeName = key.slice(0, idx); - this.kpiTableDataObj[nodeName] = []; + this.kpiTableDataObj[nodeName] = []; } } const kpiIdsForCurrentBoard = this.configGlobalData?.map(kpiDetails => kpiDetails.kpiId); @@ -301,7 +301,7 @@ export class ExecutiveComponent implements OnInit, OnDestroy { this.groupZypherKpi(kpiIdsForCurrentBoard); } this.createKpiTableHeads(this.selectedtype.toLowerCase()); - + let projectLevel = this.filterData.filter((x) => x.labelName == 'project')[0]?.level; if(projectLevel){ if(this.filterApplyData.level == projectLevel) this.getKpiCommentsCount(); @@ -952,7 +952,7 @@ export class ExecutiveComponent implements OnInit, OnDestroy { if(trendValueList?.length > 0){ let selectedIdx:number = -1; let iterativeEle = JSON.parse(JSON.stringify(trendValueList)); - let trendVals = trendValueList[0]?.hasOwnProperty('filter') || trendValueList[0]?.hasOwnProperty('filter1'); + let trendVals = trendValueList[0]?.hasOwnProperty('filter') || trendValueList[0]?.hasOwnProperty('filter1'); if(trendVals){ if(kpiId == 'kpi17'){ selectedIdx = trendValueList?.findIndex(x => x['filter']?.toLowerCase() == 'average coverage'); @@ -969,9 +969,13 @@ export class ExecutiveComponent implements OnInit, OnDestroy { } } let filtersApplied = Object.keys(this.colorObj); - - filtersApplied = filtersApplied.map((x) => x.split('_')[0]); - + + // filtersApplied = filtersApplied.map((x) => x.split('_')[0]); + filtersApplied = filtersApplied.map((x) => { + let parts = x.split('_'); + return parts.slice(0, parts.length - 1).join('_'); + }); + filtersApplied.forEach((hierarchyName) => { let obj = { 'kpiId': kpiId, @@ -982,7 +986,7 @@ export class ExecutiveComponent implements OnInit, OnDestroy { 'order': enabledKpi?.order } let chosenItem = iterativeEle?.filter((item) => item['data'] == hierarchyName)[0]; - + let trendData = this.kpiTrendsObj[kpiId]?.filter(x => x['hierarchyName']?.toLowerCase() == hierarchyName?.toLowerCase())[0]; obj['latest'] = trendData?.value || '-'; obj['trend'] = trendData?.trend || '-'; @@ -990,11 +994,11 @@ export class ExecutiveComponent implements OnInit, OnDestroy { for(let i=0; i 0 + obj['hoverText']?.push((i+1) + ' - ' + (item?.['sprintNames']?.length > 0 ? item['sprintNames'].join(',') : item?.['sSprintName'] ? item['sSprintName'] : item?.['date'])); let val = item?.lineValue >=0 ? item?.lineValue : item?.value; - obj[i+1] = val > 0 ? - (Math.round(val * 10) / 10) + (trendData?.kpiUnit ? ' ' + trendData?.kpiUnit : '') + obj[i+1] = val > 0 ? + (Math.round(val * 10) / 10) + (trendData?.kpiUnit ? ' ' + trendData?.kpiUnit : '') : val + (trendData?.kpiUnit ? ' ' + trendData?.kpiUnit : '') || '-'; if(kpiId === 'kpi153'){ obj[i+1] = item?.dataValue.find(pdata=> pdata['name'] === 'Achieved Value').value || '-'; @@ -1002,7 +1006,7 @@ export class ExecutiveComponent implements OnInit, OnDestroy { }else{ obj[i+1] = '-'; } - + } let kpiIndex = this.kpiTableDataObj[hierarchyName]?.findIndex((x) => x.kpiId == kpiId); if(kpiIndex > -1){ @@ -1345,16 +1349,7 @@ export class ExecutiveComponent implements OnInit, OnDestroy { if (maturity == undefined) { return 'NA'; } - if (item.value.length >= 5) { - const last5ArrItems = item.value.slice(item.value.length - 5, item.value.length); - const tempArr = last5ArrItems.filter(x => x.data != 0); - if (tempArr.length == 0) { - maturity = '--'; - } - } else { - maturity = '--'; - } - maturity = maturity != 'NA' && maturity != '--' && maturity != '-' ? 'M'+maturity : maturity; + maturity = 'M'+maturity; return maturity; } diff --git a/UI/src/app/dashboard/filter/filter.component.css b/UI/src/app/dashboard/filter/filter.component.css index 6294afdafd..dde4615798 100644 --- a/UI/src/app/dashboard/filter/filter.component.css +++ b/UI/src/app/dashboard/filter/filter.component.css @@ -154,7 +154,7 @@ } .selected-filter-row ul { - width: 90%; + width: 100%; } .btn-filter { diff --git a/UI/src/app/dashboard/filter/filter.component.html b/UI/src/app/dashboard/filter/filter.component.html index 627550749c..8bc307a595 100644 --- a/UI/src/app/dashboard/filter/filter.component.html +++ b/UI/src/app/dashboard/filter/filter.component.html @@ -53,7 +53,7 @@ class="p-button-secondary p-button-text p-mr-2 p-button-sm" (click)="handleClose()">
@@ -65,7 +65,7 @@ [items]="trendLineValueList" bindLabel="nodeName" bindValue="nodeId" - [closeOnSelect]="true" (change)="selectedTab?.toLowerCase() === 'iteration' ? handleIterationFilters('project') : (selectedTab?.toLowerCase() === 'release') ? handleMilestoneFilter('project') : onSelectedTrendValueChange()" + [closeOnSelect]="true" (change)="selectedTab?.toLowerCase() === 'iteration' ? handleIterationFilters('project',true) : (selectedTab?.toLowerCase() === 'release') ? handleMilestoneFilter('project') : onSelectedTrendValueChange(true)" class="trend-line-value p-pb-0"> {{item.nodeName}} @@ -75,7 +75,7 @@
{{item.nodeName}}{{item.sprintState?.toLowerCase() === 'active'? ' (Active)' : ' (Closed)'}} @@ -92,7 +92,7 @@
-
+
@@ -133,7 +133,7 @@
-
@@ -175,20 +173,6 @@
-
-
- Success - Time - Failure -

- Last Sync -

- -
-

- {{selectedProjectLastSyncDate != 'NA' ? (selectedProjectLastSyncDate | date :'dd-MMM-yyyy (EEE) h:mm:ss a') : 'NA' }} -

-
@@ -438,8 +422,57 @@

Capacity + + +
    + +
  • +
    +
    {{item?.nodeName?.length + <= 20 ? item?.nodeName : item?.nodeName?.slice(0,20)+'...'}} x +
    +
    +
    + Filter + {{item?.additionalFilters?.length + " Filters Selected"}} +
    +
    + + sprint +
    {{key?.nodeName}}
    +
    x
    +
    +
    +
    +
    +
    +
  • +
    +
+

+ +
+
+ Success + Time + Failure +

+ Last Sync +

+ +
+

+ {{selectedProjectLastSyncStatus != 'NA' ? (selectedProjectLastSyncDate | date :'dd-MMM-yyyy (EEE) h:mm:ss a') : 'NA' }} +

+
+
diff --git a/UI/src/app/dashboard/filter/filter.component.spec.ts b/UI/src/app/dashboard/filter/filter.component.spec.ts index ed2857bc8f..d85a724d3a 100644 --- a/UI/src/app/dashboard/filter/filter.component.spec.ts +++ b/UI/src/app/dashboard/filter/filter.component.spec.ts @@ -919,6 +919,7 @@ const completeHierarchyData = { component.filterForm = new UntypedFormGroup({ sprint: new UntypedFormControl({}) }); + component.selectedTab = "value"; const spy = spyOn(component, 'applyChanges'); component.removeItem('sprint', '38994_DEMO_SONAR_63284960fdd20276d60e4df5'); diff --git a/UI/src/app/dashboard/filter/filter.component.ts b/UI/src/app/dashboard/filter/filter.component.ts index 036c0b5f10..ae56575a86 100644 --- a/UI/src/app/dashboard/filter/filter.component.ts +++ b/UI/src/app/dashboard/filter/filter.component.ts @@ -136,6 +136,8 @@ export class FilterComponent implements OnInit, OnDestroy { totalProjectSelected: number = 1; selectedLevelValue: string = 'project'; displayModal: boolean = false; + selectedProjectForIteration : any = []; + isAdditionalFilter: boolean = false; constructor( private service: SharedService, @@ -275,6 +277,7 @@ export class FilterComponent implements OnInit, OnDestroy { } } // this.filterForm?.get('date')?.setValue(this.dateRangeFilter?.counts?.[0]); + this.service.setGlobalConfigData(filterData); this.service.setSelectedDateFilter(this.selectedDayType); this.filterForm?.get('date')?.setValue(this.dateRangeFilter?.counts?.[0]); this.selectedDateFilter = `${this.filterForm?.get('date')?.value} ${this.selectedDayType}`; @@ -472,11 +475,7 @@ export class FilterComponent implements OnInit, OnDestroy { arr = this.makeUniqueArrayList(arr); this.additionalFiltersDdn[this.additionalFiltersArr[i]['hierarchyLevelId']] = arr; this.toggleDropdownObj[this.additionalFiltersArr[i]['hierarchyLevelId']] = false; - if (this.additionalFiltersArr[i]['hierarchyLevelId'] == 'sprint') { this.createFormGroup(this.additionalFiltersArr[i]['hierarchyLevelId'], arr); - } else { - this.createFormGroup(this.additionalFiltersArr[i]['hierarchyLevelId']); - } } } if (!this.noProjects) { @@ -508,8 +507,13 @@ export class FilterComponent implements OnInit, OnDestroy { createFormGroup(level, arr?) { if (arr?.length > 0) { const obj = {}; + const alreadySelectedSprints = this.getSprintsWhichWasAlreadySelected(level); for (let i = 0; i < arr?.length; i++) { - obj[arr[i]['nodeId']] = new UntypedFormControl(false); + if(alreadySelectedSprints.includes(arr[i]['nodeId'])){ + obj[arr[i]['nodeId']] = new UntypedFormControl(true); + }else{ + obj[arr[i]['nodeId']] = new UntypedFormControl(false); + } } this.filterForm.controls[level] = new UntypedFormGroup(obj); } else { @@ -573,9 +577,12 @@ export class FilterComponent implements OnInit, OnDestroy { for (let i = 0; i < selectedProjects?.length; i++) { for (const key in this.additionalFiltersDdn) { if (key == 'sprint') { - this.filteredAddFilters[key] = [...this.filteredAddFilters[key], ...this.additionalFiltersDdn[key]?.filter((x) => x['parentId']?.includes(selectedProjects[i]) && x['sprintState']?.toLowerCase() == 'closed')]; - } else { - this.filteredAddFilters[key] = [...this.filteredAddFilters[key], ...this.additionalFiltersDdn[key]?.filter((x) => x['path'][0]?.includes(selectedProjects[i]))]; + if (this.selectedTab?.toLowerCase() === 'iteration') { + this.filteredAddFilters[key] = [...this.additionalFiltersDdn[key]?.filter((x) => x['parentId']?.includes(selectedProjects))]; + } else { + this.filteredAddFilters[key] = [...this.filteredAddFilters[key], ...this.additionalFiltersDdn[key]?.filter((x) => x['parentId']?.includes(selectedProjects[i]) && x['sprintState']?.toLowerCase() == 'closed')]; + }} else { + this.filteredAddFilters[key] = [...this.additionalFiltersDdn[key]?.filter((x) => x['path'][0]?.includes(Array.isArray(selectedProjects) ? selectedProjects[i] : selectedProjects))]; } } } @@ -583,10 +590,8 @@ export class FilterComponent implements OnInit, OnDestroy { } } - onSelectedTrendValueChange($event) { - this.additionalFiltersArr.forEach((additionalFilter) => { - this.filterForm.get(additionalFilter['hierarchyLevelId'])?.reset(); - }); + onSelectedTrendValueChange(isChangedFromUI) { + this.resetAddtionalFIlters(); this.applyChanges(); this.totalProjectSelected = this.service.getSelectedTrends().length; } @@ -610,6 +615,11 @@ export class FilterComponent implements OnInit, OnDestroy { this.service.setSelectedLevel(selectedLevel); this.service.setSelectedTrends(selectedTrendValues); + + if(this.selectedTab.toLowerCase() != 'developer' && this.selectedTab.toLowerCase() != 'dora' && this.selectedTab.toLowerCase() != 'maturity'){ + this.setSelectedSprintOnServiceLayer(applySource); + } + if (!applySource) { this.ngselect?.close(); this.ngselect?.blur(); @@ -630,12 +640,24 @@ export class FilterComponent implements OnInit, OnDestroy { if (additionalFilterFormVal) { if (typeof additionalFilterFormVal === 'object' && Object.keys(additionalFilterFormVal)?.length > 0) { const selectedAdditionalFilter = this.additionalFiltersDdn[Object.keys(this.additionalFiltersDdn)[i]]?.filter((x) => additionalFilterFormVal[x['nodeId']] == true); - for (let j = 0; j < selectedAdditionalFilter?.length; j++) { - const parentNodeIdx = this.selectedFilterArray?.findIndex((x) => x.nodeId == selectedAdditionalFilter[j]['parentId'][0]); - if (parentNodeIdx >= 0) { - this.selectedFilterArray[parentNodeIdx]['additionalFilters'] = - [...this.selectedFilterArray[parentNodeIdx]['additionalFilters'], selectedAdditionalFilter[j]]; + if(this.selectedTab?.toLowerCase() != 'backlog' && this.selectedTab?.toLowerCase() != 'value'){ + for (let j = 0; j < selectedAdditionalFilter?.length; j++) { + let parentNodeIdx = this.selectedFilterArray?.findIndex((x) => x.nodeId == selectedAdditionalFilter[j]['parentId'][0]); + if(parentNodeIdx < 0){ + parentNodeIdx = this.selectedFilterArray?.findIndex((x) => selectedAdditionalFilter[j]['path'][0]?.includes(x.nodeId)) + } + if (parentNodeIdx >= 0) { + this.selectedFilterArray[parentNodeIdx]['additionalFilters'] = + [...this.selectedFilterArray[parentNodeIdx]['additionalFilters'], selectedAdditionalFilter[j]]; + } + } + if(Object.keys(this.additionalFiltersDdn)[i] != 'sprint' && selectedAdditionalFilter?.length > 0){ + this.isAdditionalFilter = true; + }else{ + this.isAdditionalFilter = false; } + }else{ + this.isAdditionalFilter = false; } } else { const selectedAdditionalFilter = this.additionalFiltersDdn[Object.keys(this.additionalFiltersDdn)[i]]?.filter((x) => x['nodeId'] == additionalFilterFormVal)[0]; @@ -658,13 +680,7 @@ export class FilterComponent implements OnInit, OnDestroy { } this.createFilterApplyData(); this.setMarker(); - let isAdditionalFilters = false; - for (const key in this.additionalFiltersDdn) { - if (key != 'sprint' && this.filterForm.get(key)?.value) { - isAdditionalFilters = true; - } - } - this.service.select(this.masterData, this.filterData, this.filterApplyData, this.selectedTab, isAdditionalFilters, filterApplied,); + this.service.select(this.masterData, this.filterData, this.filterApplyData, this.selectedTab, this.isAdditionalFilter, filterApplied,); } } @@ -685,10 +701,15 @@ export class FilterComponent implements OnInit, OnDestroy { this.filterApplyData['ids'].push(temp[j].nodeId); } this.filterApplyData['label'] = temp[j]?.labelName; - if (temp[j].labelName != 'sprint' || this.filterApplyData['selectedMap']['sprint']?.length == 0) { - this.filterApplyData['selectedMap']['project'].push(this.selectedFilterArray[i]?.nodeId); + if (temp[j].labelName != 'sprint' || this.filterApplyData['selectedMap']['sprint']?.length == 0) { + if(this.selectedTab.toLowerCase() === 'iteration'){ + this.checkAndAssignProjectsInFilterApplyData(this.selectedFilterArray[i]?.parentId[0],this.filterApplyData['selectedMap']['project']) + this.checkAndAssignProjectsInFilterApplyData(this.selectedFilterArray[i]?.nodeId,this.filterApplyData['selectedMap']['sprint']) + }else{ + this.checkAndAssignProjectsInFilterApplyData(this.selectedFilterArray[i]?.nodeId,this.filterApplyData['selectedMap']['project']) } } + } } else { this.filterApplyData['level'] = this.selectedFilterArray[i]?.level; this.filterApplyData['selectedMap'][this.selectedFilterArray[i]?.labelName].push(this.selectedFilterArray[i]?.nodeId); @@ -708,6 +729,13 @@ export class FilterComponent implements OnInit, OnDestroy { this.compileGAData(); } + /** This method is using as a helper of createFilterApplyData() */ + checkAndAssignProjectsInFilterApplyData(value,selectedMap){ + if(!selectedMap.includes(value)){ + selectedMap.push(value); + } + } + checkIfMaturityTabHidden() { const maturityBoard = this.kpiListData['others']?.find((board) => board.boardName === 'Kpi Maturity'); @@ -1076,7 +1104,7 @@ export class FilterComponent implements OnInit, OnDestroy { if (hierarchyLevelId == 'sprint') { isDisabled = !isProject || !this.filteredAddFilters[hierarchyLevelId] || this.filteredAddFilters[hierarchyLevelId]?.length == 0 || (isProject && projectSelected == 0); } else { - isDisabled = !isProject || (isProject && projectSelected !== 1) || !this.filteredAddFilters[hierarchyLevelId] || this.filteredAddFilters[hierarchyLevelId]?.length == 0; + isDisabled = !isProject || (isProject && projectSelected !== 1 && this.selectedTab?.toLowerCase() !== 'iteration') || !this.filteredAddFilters[hierarchyLevelId] || this.filteredAddFilters[hierarchyLevelId]?.length == 0; } return isDisabled; } @@ -1149,7 +1177,12 @@ export class FilterComponent implements OnInit, OnDestroy { /*'type' argument: to understand onload or onchange 1: onload 2: onchange */ - handleIterationFilters(level) { + handleIterationFilters(level,isChangedFromUI?) { + this.refreshKpiLevelFiltersBackup(level, isChangedFromUI) // Refreshing KPi level filters backup + if(level !== 'sqd' && isChangedFromUI === true){ + this.resetAddtionalFIlters(); + } + this.selectedProjectForIteration = []; this.lastSyncData = {}; this.subject.next(true); if (this.filterForm?.get('selectedTrendValue')?.value != '') { @@ -1160,6 +1193,7 @@ export class FilterComponent implements OnInit, OnDestroy { this.selectedProjectData = this.trendLineValueList.find(x => x.nodeId === selectedProject); this.checkIfProjectHasData(); this.filterForm.get('selectedSprintValue').setValue(this.selectedSprint['nodeId']); + this.filterAdditionalFilters(); } if (level?.toLowerCase() == 'sprint') { @@ -1175,6 +1209,18 @@ export class FilterComponent implements OnInit, OnDestroy { this.service.setCurrentSelectedSprint(this.selectedSprint); this.selectedFilterArray = []; this.selectedFilterArray.push(this.selectedSprint); + if(this.filterForm.get('sqd')){ + this.closeAllDropdowns(); + const AllSqd = this.filterForm.get('sqd').value; + const selectedSqd = Object.keys(AllSqd).filter(sq => AllSqd[sq] === true); + if (selectedSqd) { + const selectedAdditionalFilter = this.additionalFiltersDdn['sqd'].filter(sqd => selectedSqd.includes(sqd['nodeId'])); + this.selectedFilterArray[0]['additionalFilters'] = selectedAdditionalFilter; + this.setSelectedSprintOnServiceLayer('sqd'); + this.selectedProjectForIteration = this.service.getSelectedTrends(); + this.selectedProjectForIteration[0]['additionalFilters'] = selectedAdditionalFilter; + } + } this.createFilterApplyData(); this.service.select(this.masterData, this.filterData, this.filterApplyData, this.selectedTab); } @@ -1204,9 +1250,9 @@ export class FilterComponent implements OnInit, OnDestroy { if (obj) { let d; if (type == 'start') { - d = new Date(obj[startDateField]); + d = new Date(obj[startDateField].split('T')[0]); } else { - d = new Date(obj[endDateField]); + d = new Date(obj[endDateField].split('T')[0]); } dateString = [this.pad(d.getDate()), this.pad(monthNames[d.getMonth()]), d.getFullYear()].join('/'); } @@ -1231,6 +1277,12 @@ export class FilterComponent implements OnInit, OnDestroy { this.filterForm.get(hierarchyLevelId).setValue(''); } + if(this.selectedTab.toLowerCase() === 'iteration'){ + this.handleIterationFilters(hierarchyLevelId) + }else{ + this.applyChanges(hierarchyLevelId); + } + this.applyChanges(hierarchyLevelId); } @@ -1284,7 +1336,7 @@ export class FilterComponent implements OnInit, OnDestroy { checkIfBtnDisabled(hierarchyLevelId) { let isDisabled = true; - if (hierarchyLevelId === 'sprint') { + if (hierarchyLevelId === 'sprint' || hierarchyLevelId === 'sqd') { for (const item in this.filterForm?.get(hierarchyLevelId)?.value) { if (this.filterForm?.get(hierarchyLevelId)?.value[item]) { isDisabled = null; @@ -1663,4 +1715,56 @@ export class FilterComponent implements OnInit, OnDestroy { const final = pId.replace(sortName, longName); return final; } + + /** + Filters a list of sprints to only include those that were previously selected + @returns An array containing the node IDs of sprints that were previously selected */ + + getSprintsWhichWasAlreadySelected(level) { + const sprintsWhichWasAlreadySelected = [] + if (this.service.getAddtionalFilterBackup() && this.service.getAddtionalFilterBackup()[level] && this.selectedTab.toLowerCase() != 'developer' && this.selectedTab.toLowerCase() != 'dora' && this.selectedTab.toLowerCase() != 'maturity') { + const selectedProjects = this.service.getSelectedTrends().map(data => data.nodeId); + selectedProjects.forEach(nodeId => { + const projectWhichSprintWasSelected = Object.keys(this.service.getAddtionalFilterBackup()[level]); + if (projectWhichSprintWasSelected && projectWhichSprintWasSelected.length && projectWhichSprintWasSelected.includes(nodeId)) { + sprintsWhichWasAlreadySelected.push(...this.service.getAddtionalFilterBackup()[level][nodeId].map(details => details.nodeId)); + } + }) + } + return sprintsWhichWasAlreadySelected; + } + + resetAddtionalFIlters() { + this.additionalFiltersArr.forEach((additionalFilter) => { + this.filterForm.get(additionalFilter['hierarchyLevelId'])?.reset(); + }); + } + + /*Sets the selected sprints on the service layer for storage. */ + setSelectedSprintOnServiceLayer(level) { + let selectedSprint = {} + this.selectedFilterArray?.forEach(element => { + if (element['additionalFilters'].length) { + selectedSprint = { ...selectedSprint, [this.selectedTab.toLowerCase() === 'iteration' ? element['parentId'][0] : element['nodeId']]: element['additionalFilters'] } + } + }); + this.service.setAddtionalFilterBackup({ ...this.service.getAddtionalFilterBackup(), [level] : selectedSprint }); + } + + /************************ + * Refresh KPI Level Filters when project is changing + */ + refreshKpiLevelFiltersBackup(level, isManually) { + if (isManually) { + if (level === 'release' || level === 'sprint') { + if(this.service.getAddtionalFilterBackup()['kpiFilters'] && this.service.getAddtionalFilterBackup()['kpiFilters'][this.selectedTab.toLowerCase()]){ + const updatedFilterBackup = { ...this.service.getAddtionalFilterBackup()['kpiFilters'][this.selectedTab.toLowerCase()]} + this.service.setKpiSubFilterObj(updatedFilterBackup) + } + } else { + this.service.setAddtionalFilterBackup({ ...this.service.getAddtionalFilterBackup(), kpiFilters: {} }); + this.service.setKpiSubFilterObj({}) + } + } + } } diff --git a/UI/src/app/dashboard/iteration/iteration.component.html b/UI/src/app/dashboard/iteration/iteration.component.html index 9543c08af9..cf504f42d9 100644 --- a/UI/src/app/dashboard/iteration/iteration.component.html +++ b/UI/src/app/dashboard/iteration/iteration.component.html @@ -159,7 +159,7 @@

{{x?.label}}
{{x?.label}}
{{x?.label}}
{ this.configGlobalData = globalConfig; this.tabs = this.configGlobalData[this.selectedtype.toLowerCase()].filter(board => board?.boardName.toLowerCase() !== 'iteration' && board?.boardName.toLowerCase() !== 'developer'); + this.checkShownTabs(); this.selectedTabKpis = this.tabs[0].kpis.filter(kpi => kpi.kpiDetail.calculateMaturity && kpi.shown && kpi.isEnabled); })); this.subscription.push(this.service.noProjectsObs.subscribe((res) => { @@ -463,7 +464,7 @@ export class MaturityComponent implements OnInit, OnDestroy { } else { result = getMaturityValue(undefinedCheck(self.maturityValue[kpiId]) || undefinedCheck(self.maturityValue[kpiId].trendValueList) || - undefinedCheck(self.maturityValue[kpiId].trendValueList[0]) ? -1 : self.maturityValue[kpiId].trendValueList[0].maturity); + undefinedCheck(self.maturityValue[kpiId].trendValueList[0]) ? -1 : (self.maturityValue[kpiId].trendValueList[0].maturity || self.maturityValue[kpiId].trendValueList[0].value[0].maturity)); } return result; }; @@ -501,6 +502,14 @@ export class MaturityComponent implements OnInit, OnDestroy { } }; + const getFinalChildrenCount = (arr) => { + let count = 0; + arr.forEach((item) => { + if(item.maturity>0) count++; + }) + return count; + } + d3.select('svg').remove(); d3.select('.tooltip_').remove(); d3.select('.tooltipForCategory').remove(); @@ -594,7 +603,7 @@ export class MaturityComponent implements OnInit, OnDestroy { }); } if (this.selectedTab !== 'Overall') { - root.textLines = [...root.textLines, '(M' + getAverageMaturityValue(sumOfMatirity / root.children.length) + ')']; + root.textLines = [...root.textLines, sumOfMatirity > 0 ? '(M' + getAverageMaturityValue(sumOfMatirity / getFinalChildrenCount(root.children)) + ')' : '(NA)']; } else { const allKpis = root.children; const tabCategory = {}; @@ -614,9 +623,11 @@ export class MaturityComponent implements OnInit, OnDestroy { }; const categoryKpis = allKpis.filter(kpi => tabCategory[category].includes(kpi.kpiId) && kpisInOverAllTab.includes(kpi.kpiId)); const sumOfMaturityForCategory = categoryKpis.reduce((sum, kpi) => sum + +kpi.maturity, 0); - tab['maturity'] = getAverageMaturityValue(sumOfMaturityForCategory !== 0 ? (sumOfMaturityForCategory / categoryKpis.length).toFixed(2) : 0); - sumOfMatirity += +tab['maturity']; - children.push(tab); + tab['maturity'] = getAverageMaturityValue(sumOfMaturityForCategory !== 0 ? (sumOfMaturityForCategory / categoryKpis.length).toFixed(2) : 'NA'); + if(tab['maturity'] > 0){ + sumOfMatirity += tab['maturity']; + children.push(tab); + } } root.children = children; root.textLines = [...root.textLines, '(M' + getAverageMaturityValue(sumOfMatirity / root.children.length) + ')']; @@ -818,7 +829,7 @@ export class MaturityComponent implements OnInit, OnDestroy { const arc = event.target.parentElement.lastElementChild.lastElementChild; let yPosition = arc?.getBoundingClientRect()?.top; let xPosition = arc?.getBoundingClientRect()?.right; - tooltipForMainCategoryDiv.html('Maturity Value: M' + getAverageMaturityValue(d.data['maturity']) + ''); + tooltipForMainCategoryDiv.html(`Maturity Value: ${getAverageMaturityValue(d.data['maturity']) === 0 ? "NA" : "M" + getAverageMaturityValue(d.data['maturity'])}`); tooltipForMainCategoryDiv.transition() .duration(500) .style('opacity', 1) @@ -846,7 +857,7 @@ export class MaturityComponent implements OnInit, OnDestroy { top: yPosition, right: xPosition } = arc?.getBoundingClientRect(); - tooltipForMainCategoryDiv.html('Maturity Value: M' + getAverageMaturityValue(d.data['maturity']) + ''); + tooltipForMainCategoryDiv.html(`Maturity Value: ${getAverageMaturityValue(d.data['maturity']) === 0 ? "NA" : "M" + getAverageMaturityValue(d.data['maturity'])}`); tooltipForMainCategoryDiv.transition() .duration(500) .style('opacity', 1) @@ -1017,8 +1028,12 @@ export class MaturityComponent implements OnInit, OnDestroy { renderDescription += 'M5
' + maturityLevelData.maturityRange[4] + '
'; renderDescription += ''; - renderDescription += '

Maturity Value: M' + maturityLevelData.maturity + '

'; + renderDescription += '

Maturity Value: ' + (maturityLevelData.maturity == 0 ? 'NA' : 'M'+maturityLevelData.maturity) + '

'; return renderDescription; } } + + checkShownTabs(){ + this.tabs = this.tabs.filter(tab => tab.kpis.some(kpi => kpi.shown)); + } } diff --git a/UI/src/app/dashboard/milestone/milestone.component.html b/UI/src/app/dashboard/milestone/milestone.component.html index 30a799366c..f4be299f38 100644 --- a/UI/src/app/dashboard/milestone/milestone.component.html +++ b/UI/src/app/dashboard/milestone/milestone.component.html @@ -69,7 +69,7 @@

{{x?.label}}
{{x?.label}}
{{x?.label}}
{{x?.label}}
(false); public boardId = 1; public isDownloadExcel; + addtionalFilterBackup = {} ; // make filterdata and masterdata persistent across dashboards private filterData = {}; @@ -89,6 +90,7 @@ export class SharedService { fieldMappingOptionsMetaData : any = [] kpiCardView : string = "chart"; maturityTableLoader = new Subject(); + globalConfigData; constructor() { this.passDataToDashboard = new EventEmitter(); @@ -361,6 +363,22 @@ export class SharedService { setMaturiyTableLoader(value){ this.maturityTableLoader.next(value) } + + setAddtionalFilterBackup(data){ + this.addtionalFilterBackup = data; + } + + getAddtionalFilterBackup(){ + return this.addtionalFilterBackup; + } + + setGlobalConfigData(data) { + this.globalConfigData = data; + } + + getGlobalConfigData() { + return this.globalConfigData; + } } diff --git a/common/pom.xml b/common/pom.xml index 5f817f361d..084c12addf 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -15,14 +15,14 @@ 4.0.0 common com.publicissapient.kpidashboard - 8.0.0 + 8.0.0-SNAPSHOT jar Common package used by all microservices org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 @@ -171,6 +171,7 @@ org.springframework.boot spring-boot-starter-actuator + 2.7.11 diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/client/KerberosClient.java b/common/src/main/java/com/publicissapient/kpidashboard/common/client/KerberosClient.java index 493d318327..1c18e83313 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/client/KerberosClient.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/client/KerberosClient.java @@ -71,7 +71,7 @@ public class KerberosClient { /** * Kerberos client constructor - * + * * @param jaasConfigFilePath * path to a file that contains login information * @param krb5ConfigFilePath @@ -97,7 +97,7 @@ public KerberosClient(String jaasConfigFilePath, String krb5ConfigFilePath, Stri /** * Get cookie store - * + * * @return basic cookie store. */ public BasicCookieStore getCookieStore() { @@ -106,7 +106,7 @@ public BasicCookieStore getCookieStore() { /** * Get jira host - * + * * @return jira host string */ public String getJiraHost() { @@ -115,7 +115,7 @@ public String getJiraHost() { /** * This method build a Http client with SPNEGO scheme factory and cookie store - * + * * @return http client */ private HttpClient buildLoginHttpClient() { @@ -134,7 +134,7 @@ private HttpClient buildLoginHttpClient() { /** * This method build simple Http client with cookie store - * + * * @return http client */ private HttpClient buildHttpClient() { @@ -166,7 +166,7 @@ private void clearKerberosProperties() { /** * This method fetch login cookies necessary to establish connection with spnego * jira client - * + * * @param samlTokenStartString * @param samlTokenEndString * @param samlUrlStartString @@ -198,7 +198,7 @@ public String login(String samlTokenStartString, String samlTokenEndString, Stri /** * This method execute login call with http client - * + * * @param loginURL * @param samlTokenStartString * @param samlTokenEndString @@ -226,7 +226,7 @@ private String loginCall(String loginURL, String samlTokenStartString, String sa /** * This method generate the cookies required for connection - * + * * @param loginResponse * @param samlTokenStartString * @param samlTokenEndString @@ -250,7 +250,7 @@ public void generateSamlCookies(String loginResponse, String samlTokenStartStrin /** * This method return the response of http request submitted - * + * * @param httpUriRequest * httpUriRequest * @return string response @@ -264,7 +264,7 @@ public String getResponse(HttpUriRequest httpUriRequest) throws IOException { /** * This method perform http request provided by user - * + * * @param httpUriRequest * httpUriRequest * @return string http response @@ -277,7 +277,7 @@ public HttpResponse getHttpResponse(HttpUriRequest httpUriRequest) throws IOExce /** * This is a utility method which fetch data from login response - * + * * @param input * input string * @param start @@ -299,7 +299,7 @@ private String extractString(String input, String start, String end) { /** * This method get Cookie object and convert it into string - * + * * @return string containing cookie. */ public String getCookies() { diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/AdditionalFilterCapacity.java b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/AdditionalFilterCapacity.java new file mode 100644 index 0000000000..5cef78168f --- /dev/null +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/AdditionalFilterCapacity.java @@ -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 java.util.List; + +import lombok.Data; + +/** + * @author shi6 + * + * for each additonalfilter id like "sqd", saving the ncapacity of + * each node + */ +@Data +public class AdditionalFilterCapacity { + private String filterId; + private List nodeCapacityList; + +} diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/AssigneeCapacity.java b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/AssigneeCapacity.java index 4369d263f1..6b0f5681b0 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/AssigneeCapacity.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/AssigneeCapacity.java @@ -23,6 +23,7 @@ public class AssigneeCapacity { private String userId; private String userName; private Role role; + private String squad; private Double plannedCapacity; private Double leaves; private Double availableCapacity; diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/AssigneeDetailsDTO.java b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/AssigneeDetailsDTO.java index 98e03c8ae4..7dc0142aa1 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/AssigneeDetailsDTO.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/AssigneeDetailsDTO.java @@ -39,6 +39,11 @@ public class AssigneeDetailsDTO { private String name; private String displayName; + @Override + public int hashCode() { + return java.util.Objects.hash(name, displayName); + } + @Override public boolean equals(Object obj) { if (obj instanceof AssigneeDetailsDTO) { diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/CapacityMaster.java b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/CapacityMaster.java index 5591eadd7d..4b87c26e93 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/CapacityMaster.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/CapacityMaster.java @@ -28,6 +28,7 @@ public class CapacityMaster { private String startDate; // format yyyy-mm-dd private String endDate; // format yyyy-mm-dd private ObjectId basicProjectConfigId; + private List additionalFilterCapacityList; private List assigneeCapacity; private boolean kanban; private boolean assigneeDetails; diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/KpiMaster.java b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/KpiMaster.java index f2dba21f21..cc13577ed7 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/KpiMaster.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/KpiMaster.java @@ -88,6 +88,7 @@ public class KpiMaster extends BasicModel { private String aggregationCircleCriteria; private boolean isTrendCalculative; private List trendCalculation; + @JsonProperty("isAdditionalFilterSupport") private boolean isAdditionalFilterSupport; private List maturityRange; private Integer kpiWidth; diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/LeafNodeCapacity.java b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/LeafNodeCapacity.java new file mode 100644 index 0000000000..9984b57cf1 --- /dev/null +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/LeafNodeCapacity.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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;//NOPMD + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * @author shi6 + * for each node under "additional filter", saving the capacity of + * each leaf + */ +@Data +@Builder +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class LeafNodeCapacity { + private String additionalFilterId; + private Double additionalFilterCapacity; + +} diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/ProjectToolConfig.java b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/ProjectToolConfig.java index cd856ea045..0ed3787a04 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/ProjectToolConfig.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/ProjectToolConfig.java @@ -104,6 +104,8 @@ public class ProjectToolConfig extends BasicModel { // Sonar SDM ID use for GS private String gitLabSdmID; + private List gitLabID; + // jiraIterationCompletionStatusCustomField field mapping update identifier private boolean azureIterationStatusFieldUpdate; private String projectComponent; diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/ProjectToolConfigDTO.java b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/ProjectToolConfigDTO.java index 2357d45b87..d01d28021b 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/ProjectToolConfigDTO.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/ProjectToolConfigDTO.java @@ -104,6 +104,9 @@ public class ProjectToolConfigDTO { // Sonar SDM ID use for GS private String gitLabSdmID; + // GitLab Multi Input field for GS + private List gitLabID; + // jiraIterationCompletionStatusCustomField field mapping update identifier private boolean azureIterationStatusFieldUpdate; private String projectComponent; diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/dto/FieldMappingDTO.java b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/dto/FieldMappingDTO.java index cc8af9e7b0..9469e04692 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/dto/FieldMappingDTO.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/model/application/dto/FieldMappingDTO.java @@ -26,6 +26,7 @@ import lombok.Setter; import org.bson.types.ObjectId; +import java.time.LocalDateTime; import java.util.List; /** @@ -97,6 +98,7 @@ public class FieldMappingDTO extends BasicModel { private String jiraDefectRejectionStatusKPI151; private String jiraDefectRejectionStatusAVR; private String jiraDefectRejectionStatusKPI28; + private String jiraDefectRejectionStatusKPI34; private String jiraDefectRejectionStatusKPI37; private String jiraDefectRejectionStatusKPI35; private String jiraDefectRejectionStatusKPI82; @@ -115,6 +117,7 @@ public class FieldMappingDTO extends BasicModel { private List jiraDefectRemovalStatus; private List jiraDefectRemovalStatusKPI34; private List jiraDefectRemovalIssueType; + private List jiraDefectRemovalIssueTypeKPI34; // Added for Defect Reopen Rate KPI. private List jiraDefectClosedStatus; private List jiraDefectClosedStatusKPI137; @@ -131,6 +134,7 @@ public class FieldMappingDTO extends BasicModel { private List jiraSprintCapacityIssueTypeKpi46; private List jiraDefectRejectionlIssueType; + private List jiraIssueTypeKPI37; private List jiraDefectCountlIssueType; private List jiraDefectCountlIssueTypeKPI28; @@ -207,6 +211,7 @@ public class FieldMappingDTO extends BasicModel { private List resolutionTypeForRejection; private List resolutionTypeForRejectionAVR; private List resolutionTypeForRejectionKPI28; + private List resolutionTypeForRejectionKPI34; private List resolutionTypeForRejectionKPI37; private List resolutionTypeForRejectionKPI35; private List resolutionTypeForRejectionKPI82; @@ -369,6 +374,7 @@ public class FieldMappingDTO extends BasicModel { private boolean uploadData; private boolean uploadDataKPI42; private boolean uploadDataKPI16; + private LocalDateTime createdDate; @Builder.Default private boolean notificationEnabler = true; private List jiraIssueEpicTypeKPI153; diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/model/excel/CapacityKpiData.java b/common/src/main/java/com/publicissapient/kpidashboard/common/model/excel/CapacityKpiData.java index 40e364e4a2..222eb6b874 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/model/excel/CapacityKpiData.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/model/excel/CapacityKpiData.java @@ -1,4 +1,3 @@ - /******************************************************************************* * Copyright 2014 CapitalOne, LLC. * Further development Copyright 2022 Sapient Corporation. @@ -24,6 +23,7 @@ import org.bson.types.ObjectId; import org.springframework.data.mongodb.core.mapping.Document; +import com.publicissapient.kpidashboard.common.model.application.AdditionalFilterCapacity; import com.publicissapient.kpidashboard.common.model.application.AssigneeCapacity; import com.publicissapient.kpidashboard.common.model.generic.BasicModel; @@ -50,8 +50,10 @@ public class CapacityKpiData extends BasicModel { private String projectId; private Double capacityPerSprint; private ObjectId basicProjectConfigId; + private List additionalFilterCapacityList; private List assigneeCapacity; + @Override public String toString() { return "CapacityKpiData{" + "sprintID='" + sprintID + '\'' + ", projectName='" + projectName + '\'' diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/model/excel/KanbanCapacity.java b/common/src/main/java/com/publicissapient/kpidashboard/common/model/excel/KanbanCapacity.java index 9e4b88057a..d06ec626d5 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/model/excel/KanbanCapacity.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/model/excel/KanbanCapacity.java @@ -21,6 +21,7 @@ import java.time.LocalDate; import java.util.List; +import com.publicissapient.kpidashboard.common.model.application.AdditionalFilterCapacity; import org.bson.types.ObjectId; import org.springframework.data.mongodb.core.mapping.Document; @@ -51,6 +52,7 @@ public class KanbanCapacity extends BasicModel { private LocalDate startDate; private LocalDate endDate; private ObjectId basicProjectConfigId; + private List additionalFilterCapacityList; private List assigneeCapacity; } diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/application/impl/ProjectToolConfigRepositoryImpl.java b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/application/impl/ProjectToolConfigRepositoryImpl.java index 8af7e86b30..f2c2a97a8c 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/application/impl/ProjectToolConfigRepositoryImpl.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/application/impl/ProjectToolConfigRepositoryImpl.java @@ -71,9 +71,6 @@ private List transform(List list) { toolObj.setRepoSlug(item.getRepoSlug()); toolObj.setRepositoryName(item.getRepositoryName()); toolObj.setProcessorItemList(item.getProcessorItemList()); - if (CollectionUtils.isNotEmpty(item.getConnection())) { - toolObj.setUrl(item.getConnection().get(0).getBaseUrl()); - } tools.add(toolObj); } return tools; diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/excel/CapacityKpiDataRepositoryImpl.java b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/excel/CapacityKpiDataRepositoryImpl.java index ebabd553dd..257ad0402d 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/excel/CapacityKpiDataRepositoryImpl.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/excel/CapacityKpiDataRepositoryImpl.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -29,6 +30,7 @@ import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; +import com.publicissapient.kpidashboard.common.model.application.LeafNodeCapacity; import com.publicissapient.kpidashboard.common.model.excel.CapacityKpiData; import lombok.extern.slf4j.Slf4j; @@ -60,8 +62,12 @@ public List findByFilters(Map mapofFilters, // map of common filters Project, Project and Sprint for (Map.Entry entry : mapofFilters.entrySet()) { - if (CollectionUtils.isNotEmpty((List) entry.getValue())) { - criteria = criteria.and(entry.getKey()).in((List) entry.getValue()); + String key = entry.getKey(); + if (!key.equalsIgnoreCase("additionalFilterCapacityList.nodeCapacityList.additionalFilterId") + && !key.equalsIgnoreCase("additionalFilterCapacityList.filterId")) { + if (CollectionUtils.isNotEmpty((List) entry.getValue())) { + criteria = criteria.and(key).in((List) entry.getValue()); + } } } // Project level storyType filters @@ -81,6 +87,20 @@ public List findByFilters(Map mapofFilters, query = new Query(criteriaProjectLevelAdded); } List data = operations.find(query, CapacityKpiData.class); + if (mapofFilters.containsKey("additionalFilterCapacityList.nodeCapacityList.additionalFilterId")) { + data.stream().forEach(capacityKpiData -> { + List additionalFilter = (List) mapofFilters + .get("additionalFilterCapacityList.nodeCapacityList.additionalFilterId"); + List upperCaseKey = ((List) mapofFilters.get("additionalFilterCapacityList.filterId")) + .stream().map(String::toUpperCase).collect(Collectors.toList()); + capacityKpiData.setCapacityPerSprint(capacityKpiData.getAdditionalFilterCapacityList().stream() + .filter(additionalFilterCapacity -> upperCaseKey + .contains(additionalFilterCapacity.getFilterId().toUpperCase())) + .flatMap(additionalFilterCapacity -> additionalFilterCapacity.getNodeCapacityList().stream()) + .filter(leaf -> additionalFilter.contains(leaf.getAdditionalFilterId())) + .mapToDouble(LeafNodeCapacity::getAdditionalFilterCapacity).sum()); + }); + } if (CollectionUtils.isEmpty(data)) { log.info("No Data found for filters"); } diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/excel/KanbanCapacityRepoCustom.java b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/excel/KanbanCapacityRepoCustom.java index 0aa60523d9..910e3b7e88 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/excel/KanbanCapacityRepoCustom.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/excel/KanbanCapacityRepoCustom.java @@ -43,7 +43,7 @@ public interface KanbanCapacityRepoCustom { * the date to * @return list of feature */ - List findIssuesByType(Map> mapOfFilters, String dateFrom, String dateTo); + List findIssuesByType(Map mapOfFilters, String dateFrom, String dateTo); /** * find already existing kanban capacity diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/excel/KanbanCapacityRepositoryImpl.java b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/excel/KanbanCapacityRepositoryImpl.java index 8eb19df558..a4b1056e35 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/excel/KanbanCapacityRepositoryImpl.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/excel/KanbanCapacityRepositoryImpl.java @@ -20,8 +20,9 @@ import java.util.List; import java.util.Map; +import java.util.stream.Collectors; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.bson.types.ObjectId; import org.joda.time.DateTime; @@ -31,6 +32,7 @@ import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; +import com.publicissapient.kpidashboard.common.model.application.LeafNodeCapacity; import com.publicissapient.kpidashboard.common.model.excel.KanbanCapacity; /** @@ -40,28 +42,50 @@ public class KanbanCapacityRepositoryImpl implements KanbanCapacityRepoCustom { private static final String START_DATE = "startDate"; private static final String END_DATE = "endDate"; - private static final String TICKET_PROJECT_ID_FIELD = "projectId"; private static final String DATE_PATTERN = "yyyy-MM-dd"; @Autowired private MongoOperations operations; @Override - public List findIssuesByType(Map> mapOfFilters, String dateFrom, - String dateTo) { + public List findIssuesByType(Map mapOfFilters, String dateFrom, String dateTo) { Criteria criteria = new Criteria(); DateTime startDateTime = DateTimeFormat.forPattern(DATE_PATTERN).parseDateTime(dateFrom).withTime(0, 0, 0, 0); DateTime endDateTime = DateTimeFormat.forPattern(DATE_PATTERN).parseDateTime(dateTo).withTime(0, 0, 0, 0); // map of common filters Project and Sprint - for (Map.Entry> entry : mapOfFilters.entrySet()) { - if (CollectionUtils.isNotEmpty(entry.getValue())) { - criteria = criteria.and(entry.getKey()).in(entry.getValue()); + for (Map.Entry entry : mapOfFilters.entrySet()) { + String key = entry.getKey(); + if (ObjectUtils.isNotEmpty(entry.getValue()) + && !key.equalsIgnoreCase("additionalFilterCapacityList.nodeCapacityList.additionalFilterId") + && !key.equalsIgnoreCase("additionalFilterCapacityList.filterId")) { + if (entry.getValue() instanceof List) { + List value = (List) entry.getValue(); + criteria = criteria.and(key).in(value); + } else { + criteria = criteria.and(key).in(entry.getValue()); + } + } } criteria = criteria.and(START_DATE).lte(endDateTime.withTime(0, 0, 0, 0)); criteria = criteria.and(END_DATE).gte(startDateTime.withTime(0, 0, 0, 0)); Query query = new Query(criteria); - return operations.find(query, KanbanCapacity.class); + List kanbanCapacityList = operations.find(query, KanbanCapacity.class); + if (mapOfFilters.containsKey("additionalFilterCapacityList.nodeCapacityList.additionalFilterId")) { + kanbanCapacityList.stream().forEach(capacityKpiData -> { + List additionalFilter = (List) mapOfFilters + .get("additionalFilterCapacityList.nodeCapacityList.additionalFilterId"); + List upperCaseKey = ((List) mapOfFilters.get("additionalFilterCapacityList.filterId")) + .stream().map(String::toUpperCase).collect(Collectors.toList()); + capacityKpiData.setCapacity(capacityKpiData.getAdditionalFilterCapacityList().stream() + .filter(additionalFilterCapacity -> upperCaseKey + .contains(additionalFilterCapacity.getFilterId().toUpperCase())) + .flatMap(additionalFilterCapacity -> additionalFilterCapacity.getNodeCapacityList().stream()) + .filter(leaf -> additionalFilter.contains(leaf.getAdditionalFilterId())) + .mapToDouble(LeafNodeCapacity::getAdditionalFilterCapacity).sum()); + }); + } + return kanbanCapacityList; } diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/jira/JiraIssueRepositoryCustom.java b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/jira/JiraIssueRepositoryCustom.java index a9aa5dfa4f..d8f4ec23ea 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/jira/JiraIssueRepositoryCustom.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/jira/JiraIssueRepositoryCustom.java @@ -257,9 +257,14 @@ List findByRelease(Map> mapOfFilters, /** * find unique Release Version Name group by type name + * * @param mapOfFilters - * @param @return + * @param @return */ List findUniqueReleaseVersionByUniqueTypeName(Map> mapOfFilters); + + + List findIssueByNumberWithAdditionalFilter(Set storyNumber, + Map> uniqueProjectMap); } diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/jira/JiraIssueRepositoryImpl.java b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/jira/JiraIssueRepositoryImpl.java index a3189cf470..26986d6c1d 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/jira/JiraIssueRepositoryImpl.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/jira/JiraIssueRepositoryImpl.java @@ -98,6 +98,7 @@ public class JiraIssueRepositoryImpl implements JiraIssueRepositoryCustom {// NO private static final String STATE = "state"; private static final String ISSUE_ID = "issueId"; private static final String SPRINT_END_DATE = "sprintEndDate"; + private static final String ADDITIONAL_FILTER = "additionalFilters"; @Autowired private MongoTemplate operations; @@ -172,6 +173,7 @@ public List findIssueByStoryNumber(Map> mapOfFil query.fields().include("resolution"); query.fields().include(NAME); query.fields().include(URL); + query.fields().include(ADDITIONAL_FILTER); return operations.find(query, JiraIssue.class); } @@ -222,6 +224,7 @@ public List findIssuesBySprintAndType(Map> mapOf query.fields().include(LOGGED_WORK_MINUTES); query.fields().include(SPRINT_ASSET_STATE); query.fields().include(SPRINT_END_DATE); + query.fields().include(ADDITIONAL_FILTER); return operations.find(query, JiraIssue.class); } @@ -296,6 +299,7 @@ public List findIssuesByType(Map> mapOfFilters) query.fields().include(JIRA_ISSUE_STATUS); query.fields().include(URL); query.fields().include(NAME); + query.fields().include(ADDITIONAL_FILTER); return operations.find(query, JiraIssue.class); } @@ -323,6 +327,7 @@ public List findUnassignedIssues(String startDate, String endDate, query.fields().include(TICKET_CREATED_DATE_FIELD); query.fields().include(NAME); query.fields().include(PRIORITY); + query.fields().include(ADDITIONAL_FILTER); return operations.find(query, JiraIssue.class); } @@ -424,6 +429,7 @@ public List findDefectCountByRCA(Map> mapOfFilte query.fields().include(ROOT_CAUSE); query.fields().include(URL); query.fields().include(NAME); + query.fields().include(ADDITIONAL_FILTER); return operations.find(query, JiraIssue.class); @@ -480,6 +486,7 @@ public List findByTypeNameAndDefectStoryIDIn(String typeName, List findIssueByNumber(Map> mapOfFilters, query.fields().include(URL); query.fields().include(RESOLUTION); query.fields().include(JIRA_ISSUE_STATUS); + query.fields().include(ADDITIONAL_FILTER); + query.fields().include(TYPE_NAME); return operations.find(query, JiraIssue.class); } @@ -556,6 +565,7 @@ public List findStoriesBySprints(Map> mapOfFilte query.fields().include(SPRINT_NAME); query.fields().include(SPRINT_ID); query.fields().include(URL); + query.fields().include(ADDITIONAL_FILTER); return operations.find(query, JiraIssue.class); } @@ -719,6 +729,7 @@ public List findIssueAndDescByNumber(List storyNumber) { query.fields().include(NAME); query.fields().include(URL); query.fields().include(CONFIG_ID); + query.fields().include(ADDITIONAL_FILTER); return new ArrayList<>(operations.find(query, JiraIssue.class)); } @@ -821,9 +832,15 @@ public List findByRelease(Map> mapOfFilters, projectCriteriaList.add(projectCriteria); }); - Criteria criteriaAggregatedAtProjectLevel = new Criteria() - .orOperator(projectCriteriaList.toArray(new Criteria[0])); - Criteria criteriaProjectLevelAdded = new Criteria().andOperator(criteria, criteriaAggregatedAtProjectLevel); + Criteria criteriaProjectLevelAdded =null; + if(CollectionUtils.isNotEmpty(projectCriteriaList)) { + Criteria criteriaAggregatedAtProjectLevel = new Criteria() + .orOperator(projectCriteriaList.toArray(new Criteria[0])); + criteriaProjectLevelAdded = new Criteria().andOperator(criteria, criteriaAggregatedAtProjectLevel); + } + else{ + criteriaProjectLevelAdded= new Criteria().andOperator(criteria); + } Query query = new Query(criteriaProjectLevelAdded); // add projection return operations.find(query, JiraIssue.class); @@ -832,6 +849,7 @@ public List findByRelease(Map> mapOfFilters, /** * find unique Release Version Name group by type name + * * @param mapOfFilters * @return */ @@ -857,4 +875,29 @@ public List findUniqueReleaseVersionByUniqueTypeName(Map findIssueByNumberWithAdditionalFilter(Set storyNumber, + Map> uniqueProjectMap) { + Criteria criteria = new Criteria(); + + // Project level storyType filters + List projectCriteriaList = new ArrayList<>(); + uniqueProjectMap.forEach((project, filterMap) -> { + Criteria projectCriteria = new Criteria(); + projectCriteria.and(CONFIG_ID).is(project); + filterMap.forEach((subk, subv) -> projectCriteria.and(subk).in((List) subv)); + projectCriteriaList.add(projectCriteria); + }); + + if (!CollectionUtils.isEmpty(projectCriteriaList)) { + Criteria criteriaAggregatedAtProjectLevel = new Criteria() + .orOperator(projectCriteriaList.toArray(new Criteria[0])); + criteria = new Criteria().andOperator(criteria, criteriaAggregatedAtProjectLevel); + + } + criteria = criteria.and(NUMBER).in(storyNumber); + Query query = new Query(criteria); + return operations.find(query, JiraIssue.class); + } + +} diff --git a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/rbac/UserInfoRepository.java b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/rbac/UserInfoRepository.java index 26fafeb6cf..28d5f8e321 100644 --- a/common/src/main/java/com/publicissapient/kpidashboard/common/repository/rbac/UserInfoRepository.java +++ b/common/src/main/java/com/publicissapient/kpidashboard/common/repository/rbac/UserInfoRepository.java @@ -42,6 +42,8 @@ public interface UserInfoRepository extends CrudRepository { UserInfo findByUsername(String username); + + UserInfo findFirstByUsername(String username); /** * Finds by username and auth type. * diff --git a/customapi/pom.xml b/customapi/pom.xml index 40791e5f95..ed5e8453a6 100644 --- a/customapi/pom.xml +++ b/customapi/pom.xml @@ -14,14 +14,14 @@ com.publicissapient.kpidashboard customapi Rest API Layer - 8.0.0 + 8.0.0-SNAPSHOT jar org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 @@ -436,6 +436,25 @@ + + org.apache.maven.plugins + maven-assembly-plugin + 3.4.2 + + + jar-with-dependencies + + + + + assemble-all + package + + single + + + + diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/CodeCommitKanbanServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/CodeCommitKanbanServiceImpl.java index 29811fae24..7584dd36b4 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/CodeCommitKanbanServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/CodeCommitKanbanServiceImpl.java @@ -235,7 +235,8 @@ private Map filterKanbanDataBasedOnStartAndEndDateAndCommitDetails } // if data is there for any branch then only will shown on excel if (MapUtils.isNotEmpty(excelLoader)) { - listOfRepo.add(tool.getUrl()); + String repoName = tool.getRepositoryName() != null ? tool.getRepositoryName() : tool.getRepoSlug(); + listOfRepo.add(repoName); listOfBranch.add(tool.getBranch()); excelLoaderfinal.putAll(excelLoader); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/CodeCommitServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/CodeCommitServiceImpl.java index a355cc2a6d..b218696159 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/CodeCommitServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/CodeCommitServiceImpl.java @@ -18,6 +18,28 @@ package com.publicissapient.kpidashboard.apis.bitbucket.service; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.bson.types.ObjectId; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import com.publicissapient.kpidashboard.apis.appsetting.service.ConfigHelperService; @@ -45,28 +67,8 @@ import com.publicissapient.kpidashboard.common.repository.scm.CommitRepository; import com.publicissapient.kpidashboard.common.repository.scm.MergeRequestRepository; import com.publicissapient.kpidashboard.common.util.DateUtil; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections.MapUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; -import org.bson.types.ObjectId; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; /** * This service reflects the logic for the number of check-ins in master @@ -210,7 +212,7 @@ private void projectWiseLeafNodeValue(KpiElement kpiElement, Map m aggDataMap.put(getBranchSubFilter(repo, projectName), dayWiseCount); repoWiseCommitList.add(excelDataLoader); repoWiseMergeRequestList.add(mergeRequestExcelDataLoader); - repoList.add(repo.getUrl()); + repoList.add(repo.getRepositoryName() != null ? repo.getRepositoryName() : repo.getRepoSlug()); branchList.add(repo.getBranch()); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/MeanTimeToMergeServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/MeanTimeToMergeServiceImpl.java index ed8c88ab33..79d7f86dbd 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/MeanTimeToMergeServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/MeanTimeToMergeServiceImpl.java @@ -172,7 +172,7 @@ private void projectWiseLeafNodeValue(KpiElement kpiElement, Map m projectName, duration, dataPoints); aggDataMap.put(getBranchSubFilter(repo, projectName), dataCountList); repoWiseMRList.add(excelDataLoader); - repoList.add(repo.getUrl()); + repoList.add(repo.getRepositoryName() != null ? repo.getRepositoryName() : repo.getRepoSlug()); branchList.add(repo.getBranch()); } } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/PRSizeServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/PRSizeServiceImpl.java index a5837b6fcc..b3abd3b1b1 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/PRSizeServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/PRSizeServiceImpl.java @@ -178,7 +178,7 @@ private void projectWiseLeafNodeValue(KpiElement kpiElement, KpiRequest kpiReque setWeekWisePRSize(dateWisePRSize, dateWiseMRCount, excelDataLoader, branchName, projectName, aggDataMap, kpiRequest); repoWisePRSizeList.add(excelDataLoader); - repoList.add(repo.getUrl()); + repoList.add(repo.getRepositoryName() != null ? repo.getRepositoryName() : repo.getRepoSlug()); branchList.add(repo.getBranch()); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/PickupTimeServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/PickupTimeServiceImpl.java index 048645c407..51eb1161a9 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/PickupTimeServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/PickupTimeServiceImpl.java @@ -181,7 +181,7 @@ private void projectWiseLeafNodeValue(KpiElement kpiElement, KpiRequest kpiReque setWeekWisePickupTime(dateWisePickupTime, dateWiseMRCount, excelDataLoader, branchName, projectName, aggDataMap, kpiRequest); repoWisePickupTimeList.add(excelDataLoader); - repoList.add(repo.getUrl()); + repoList.add(repo.getRepositoryName() != null ? repo.getRepositoryName() : repo.getRepoSlug()); branchList.add(repo.getBranch()); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/RepoToolCodeCommitKanbanServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/RepoToolCodeCommitKanbanServiceImpl.java index 0659025fa4..2cb2571025 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/RepoToolCodeCommitKanbanServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/RepoToolCodeCommitKanbanServiceImpl.java @@ -178,7 +178,7 @@ private void kpiWithFilter(List repoToolKpiMetricResp aggDataMap.put(getBranchSubFilter(repo, projectName), dayWiseCount); repoWiseCommitList.add(excelDataLoader); repoWiseMergeRequestList.add(mergeRequestExcelDataLoader); - repoList.add(repo.getUrl()); + repoList.add(repo.getRepositoryName() != null ? repo.getRepositoryName() : repo.getRepoSlug()); branchList.add(repo.getBranch()); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/RepoToolCodeCommitServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/RepoToolCodeCommitServiceImpl.java index 2df63c2603..2fd7388185 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/RepoToolCodeCommitServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/RepoToolCodeCommitServiceImpl.java @@ -202,7 +202,7 @@ private void projectWiseLeafNodeValue(KpiElement kpiElement, Map m aggDataMap.put(getBranchSubFilter(repo, projectName), dayWiseCount); repoWiseCommitList.add(excelDataLoader); repoWiseMergeRequestList.add(mergeRequestExcelDataLoader); - repoList.add(repo.getUrl()); + repoList.add(repo.getRepositoryName() != null ? repo.getRepositoryName() : repo.getRepoSlug()); branchList.add(repo.getBranch()); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/RepoToolMeanTimeToMergeServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/RepoToolMeanTimeToMergeServiceImpl.java index a999c57a9a..1db6fb55ed 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/RepoToolMeanTimeToMergeServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/bitbucket/service/RepoToolMeanTimeToMergeServiceImpl.java @@ -186,7 +186,7 @@ private void projectWiseLeafNodeValue(KpiElement kpiElement, Map m setWeekWiseMeanTimeToMergeForRepoTools(dateWiseMeanTimeToMerge, excelDataLoader, branchName, projectName, aggDataMap, duration, dataPoints); repoWiseMRList.add(excelDataLoader); - repoList.add(repo.getUrl()); + repoList.add(repo.getRepositoryName() != null ? repo.getRepositoryName() : repo.getRepoSlug()); branchList.add(repo.getBranch()); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/capacity/service/CapacityMasterServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/capacity/service/CapacityMasterServiceImpl.java index 6ea273dfad..88aba40698 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/capacity/service/CapacityMasterServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/capacity/service/CapacityMasterServiceImpl.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * 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.apis.capacity.service; import java.time.LocalDate; @@ -15,6 +33,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.bson.types.ObjectId; import org.springframework.beans.factory.annotation.Autowired; @@ -23,11 +42,15 @@ import com.google.common.collect.Maps; import com.publicissapient.kpidashboard.apis.common.service.CacheService; import com.publicissapient.kpidashboard.apis.config.CustomApiConfig; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import com.publicissapient.kpidashboard.apis.jira.service.SprintDetailsService; import com.publicissapient.kpidashboard.apis.projectconfig.basic.service.ProjectBasicConfigService; import com.publicissapient.kpidashboard.common.constant.CommonConstant; +import com.publicissapient.kpidashboard.common.model.application.AdditionalFilterCapacity; +import com.publicissapient.kpidashboard.common.model.application.AdditionalFilterCategory; import com.publicissapient.kpidashboard.common.model.application.AssigneeCapacity; import com.publicissapient.kpidashboard.common.model.application.CapacityMaster; +import com.publicissapient.kpidashboard.common.model.application.LeafNodeCapacity; import com.publicissapient.kpidashboard.common.model.application.ProjectBasicConfig; import com.publicissapient.kpidashboard.common.model.application.Week; import com.publicissapient.kpidashboard.common.model.excel.CapacityKpiData; @@ -37,6 +60,7 @@ import com.publicissapient.kpidashboard.common.model.jira.UserRatingData; import com.publicissapient.kpidashboard.common.repository.excel.CapacityKpiDataRepository; import com.publicissapient.kpidashboard.common.repository.excel.KanbanCapacityRepository; +import com.publicissapient.kpidashboard.common.repository.jira.AssigneeDetailsRepository; import com.publicissapient.kpidashboard.common.repository.jira.HappinessKpiDataRepository; import com.publicissapient.kpidashboard.common.util.DateUtil; @@ -74,6 +98,12 @@ public class CapacityMasterServiceImpl implements CapacityMasterService { @Autowired private HappinessKpiDataRepository happinessKpiDataRepository; + @Autowired + private AssigneeDetailsRepository assigneeDetailsRepository; + + @Autowired + private FilterHelperService filterHelperService; + /** * This method process the capacity data. * @@ -139,11 +169,13 @@ private List getCapacityDataForScrum(ProjectBasicConfig project) if (capacityKpiData != null) { capacityMaster.setId(capacityKpiData.getId()); capacityMaster.setCapacity(Math.round(capacityKpiData.getCapacityPerSprint() * 100) / 100.0); + capacityMaster.setAdditionalFilterCapacityList(capacityKpiData.getAdditionalFilterCapacityList()); if (CollectionUtils.isNotEmpty(capacityKpiData.getAssigneeCapacity()) && project.isSaveAssigneeDetails()) { capacityKpiData.getAssigneeCapacity().stream().forEach(assigneeCapacity -> assigneeCapacity .setLeaves(Optional.ofNullable(assigneeCapacity.getLeaves()).orElse(0D))); - capacityKpiData.getAssigneeCapacity().stream().forEach(assigneeCapacity -> assigneeCapacity.setHappinessRating(0)); + capacityKpiData.getAssigneeCapacity().stream() + .forEach(assigneeCapacity -> assigneeCapacity.setHappinessRating(0)); // Setting most recently submitted happiness index value for a sprint setHappinessIndex(happinessKpiData, capacityKpiData.getAssigneeCapacity()); capacityMaster.setAssigneeCapacity(capacityKpiData.getAssigneeCapacity()); @@ -238,6 +270,21 @@ private List getCapacityDataForKanban(ProjectBasicConfig project capacityMasterKanban.setId(kanbanCapacity.getId()); // mutiplying by working days of week capacityMasterKanban.setCapacity(kanbanCapacity.getCapacity() * 5); + List additionalFilterCapacityList = kanbanCapacity + .getAdditionalFilterCapacityList(); + if (CollectionUtils.isNotEmpty(additionalFilterCapacityList)) { + additionalFilterCapacityList.forEach(leafNode -> { + List leafNodeCapacity = leafNode.getNodeCapacityList(); + if (CollectionUtils.isNotEmpty(leafNodeCapacity)) { + leafNodeCapacity.stream() + .filter(capacity -> capacity.getAdditionalFilterCapacity() != null) + .forEach(capacity -> capacity.setAdditionalFilterCapacity( + capacity.getAdditionalFilterCapacity() * 5)); + } + }); + } + capacityMasterKanban.setAdditionalFilterCapacityList(additionalFilterCapacityList); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); capacityMasterKanban.setStartDate(kanbanCapacity.getStartDate().format(formatter)); capacityMasterKanban.setEndDate(kanbanCapacity.getEndDate().format(formatter)); @@ -438,8 +485,12 @@ private void createScrumAssigneeData(CapacityKpiData data, CapacityMaster capaci .mapToDouble(assignee -> Optional.ofNullable(assignee.getAvailableCapacity()).orElse(0.0d)).sum(); data.setAssigneeCapacity(assigneeList); data.setCapacityPerSprint(Math.round(sum * 100) / 100.0); + // Set the LeafNodeCapacity list in data + data.setAdditionalFilterCapacityList(saveAdditionalFilterCapacity(capacityMaster)); + } else { data.setCapacityPerSprint(capacityMaster.getCapacity()); + data.setAdditionalFilterCapacityList(saveAdditionalFilterCapacity(capacityMaster)); } } @@ -453,10 +504,17 @@ private void createKanbanAssigneeData(KanbanCapacity data, CapacityMaster capaci double sum = assigneeList.stream() .mapToDouble(assignee -> Optional.ofNullable(assignee.getAvailableCapacity()).orElse(0.0d)).sum(); data.setAssigneeCapacity(assigneeList); + helper(capacityMaster); + data.setAdditionalFilterCapacityList(saveAdditionalFilterCapacity(capacityMaster)); + // we have to divide capacity by working days of week data.setCapacity(sum / 5); } else { data.setCapacity(capacityMaster.getCapacity() / 5); + // dividing each leaf node capacity of full week to single day. + helper(capacityMaster); + data.setAdditionalFilterCapacityList(saveAdditionalFilterCapacity(capacityMaster)); + } } @@ -541,4 +599,100 @@ public void deleteCapacityByProject(boolean isKanban, ObjectId basicProjectConfi } } + public void helper(CapacityMaster capacityMaster) { + Map addFilterCat = filterHelperService.getAdditionalFilterHierarchyLevel(); + Map addFilterCategory = addFilterCat.entrySet().stream() + .collect(Collectors.toMap(entry -> entry.getKey().toUpperCase(), Map.Entry::getValue)); + + if (CollectionUtils.isNotEmpty(capacityMaster.getAdditionalFilterCapacityList())) { + for (AdditionalFilterCapacity category : capacityMaster.getAdditionalFilterCapacityList()) { + if (StringUtils.isNotEmpty(category.getFilterId()) + && CollectionUtils.isNotEmpty(category.getNodeCapacityList()) + && null != addFilterCategory.get(category.getFilterId().toUpperCase())) { + + category.getNodeCapacityList().forEach(leafNode -> { + Double leafNodeCapacity = leafNode.getAdditionalFilterCapacity(); + if (leafNodeCapacity != null) { + leafNode.setAdditionalFilterCapacity(leafNodeCapacity / 5); + } + }); + } + } + } + } + + /** + * Saves the additional filter capacities from the given capacity master. + * + * @param capacityMaster + * the capacity master containing additional filter capacities + * @return a list of saved additional filter capacities + */ + public List saveAdditionalFilterCapacity(CapacityMaster capacityMaster) { + if (CollectionUtils.isEmpty(capacityMaster.getAdditionalFilterCapacityList())) { + return new ArrayList<>(); + } + + return capacityMaster.getAdditionalFilterCapacityList().stream().filter(this::isValidAdditionalFilterCapacity) + .map(this::createDbAdditionalFilterCapacity).filter(Objects::nonNull).collect(Collectors.toList()); + } + + /** + * Validates if the given additional filter capacity has a non-empty filter ID. + * + * @param additionalFilterCapacity + * the additional filter capacity to validate + * @return true if the filter ID is not empty, false otherwise + */ + private boolean isValidAdditionalFilterCapacity(AdditionalFilterCapacity additionalFilterCapacity) { + return StringUtils.isNotEmpty(additionalFilterCapacity.getFilterId()); + } + + /** + * Creates a database entity for the given additional filter capacity. + * + * @param additionalFilterCapacity + * the additional filter capacity to convert + * @return the corresponding database entity, or null if no valid node + * capacities exist + */ + private AdditionalFilterCapacity createDbAdditionalFilterCapacity( + AdditionalFilterCapacity additionalFilterCapacity) { + List dbNodeCapacityList = additionalFilterCapacity.getNodeCapacityList().stream() + .filter(this::isValidNodeCapacity).map(this::createDbLeafNodeCapacity).collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(dbNodeCapacityList)) { + return null; + } + + AdditionalFilterCapacity dbFilterCapacity = new AdditionalFilterCapacity(); + dbFilterCapacity.setFilterId(additionalFilterCapacity.getFilterId()); + dbFilterCapacity.setNodeCapacityList(dbNodeCapacityList); + return dbFilterCapacity; + } + + /** + * Validates if the given node capacity has a non-empty additional filter ID and + * a non-null additional filter capacity. + * + * @param nodeCapacity + * the node capacity to validate + * @return true if the additional filter ID is not empty and the additional + * filter capacity is not null, false otherwise + */ + private boolean isValidNodeCapacity(LeafNodeCapacity nodeCapacity) { + return StringUtils.isNotEmpty(nodeCapacity.getAdditionalFilterId()) + && ObjectUtils.isNotEmpty(nodeCapacity.getAdditionalFilterCapacity()); + } + + /** + * Creates a database entity for the given node capacity. + * + * @param nodeCapacity + * the node capacity to convert + * @return the corresponding database entity + */ + private LeafNodeCapacity createDbLeafNodeCapacity(LeafNodeCapacity nodeCapacity) { + return new LeafNodeCapacity(nodeCapacity.getAdditionalFilterId(), nodeCapacity.getAdditionalFilterCapacity()); + } } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/UserInfoService.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/UserInfoService.java index 11a8622c20..83436efa45 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/UserInfoService.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/UserInfoService.java @@ -60,6 +60,8 @@ public interface UserInfoService { UserInfo getUserInfo(String username); + UserInfo getFistUserInfo(String username); + /** * Gets users. * diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/impl/ConfigDetailsServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/impl/ConfigDetailsServiceImpl.java index b3a2e5a4ed..8984c35464 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/impl/ConfigDetailsServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/impl/ConfigDetailsServiceImpl.java @@ -50,6 +50,7 @@ public ConfigDetails getConfigDetails() { configDetails.setPercentile(customApiConfig.getPercentileValue()); configDetails.setHierarchySelectionCount(customApiConfig.getHierarchySelectionCount()); configDetails.setDateRangeFilter(dateRangeFilter); + configDetails.setGitlabToolFieldFlag(customApiConfig.getIsGitlabFieldEnable()); configDetails.setNoOfDataPoints(customApiConfig.getSprintCountForFilters()); return configDetails; } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/impl/KpiHelperService.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/impl/KpiHelperService.java index 79f2c0cca6..e350e70ad8 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/impl/KpiHelperService.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/impl/KpiHelperService.java @@ -704,7 +704,7 @@ public Map fetchBackLogReadinessFromdb(List leafNodeList, * the leaf node list * @return the list */ - public List fetchSprintCapacityDataFromDb(List leafNodeList) { + public List fetchSprintCapacityDataFromDb(KpiRequest kpiRequest, List leafNodeList) { Map> mapOfFilters = new LinkedHashMap<>(); @@ -713,6 +713,8 @@ public List fetchSprintCapacityDataFromDb(List leafNodeList) { Map> uniqueProjectMap = new HashMap<>(); + /** additional filter **/ + KpiDataHelper.createAdditionalFilterMap(kpiRequest, mapOfFilters, Constant.SCRUM, CommonConstant.QA, flterHelperService); leafNodeList.forEach(leaf -> { ObjectId basicProjectConfigId = leaf.getProjectFilter().getBasicProjectConfigId(); Map mapOfProjectFilters = new LinkedHashMap<>(); @@ -749,7 +751,7 @@ public List fetchSprintCapacityDataFromDb(List leafNodeList) { * the leaf node list * @return list */ - public List fetchCapacityDataFromDB(List leafNodeList) { + public List fetchCapacityDataFromDB(KpiRequest kpiRequest, List leafNodeList) { Map mapOfFilters = new LinkedHashMap<>(); List sprintList = new ArrayList<>(); List basicProjectConfigIds = new ArrayList<>(); @@ -760,12 +762,17 @@ public List fetchCapacityDataFromDB(List leafNodeList) { basicProjectConfigIds.add(basicProjectConfigId); }); + /** additional filter **/ + KpiDataHelper.createAdditionalFilterMapForCapacity(kpiRequest, mapOfFilters, flterHelperService); + mapOfFilters.put(JiraFeature.SPRINT_ID.getFieldValueInFeature(), sprintList.stream().distinct().collect(Collectors.toList())); mapOfFilters.put(JiraFeature.BASIC_PROJECT_CONFIG_ID.getFieldValueInFeature(), basicProjectConfigIds.stream().distinct().collect(Collectors.toList())); - return capacityKpiDataRepository.findByFilters(mapOfFilters, uniqueProjectMap); + List byFilters = capacityKpiDataRepository.findByFilters(mapOfFilters, uniqueProjectMap); + + return byFilters; } /** @@ -836,10 +843,11 @@ public Map fetchTicketVelocityDataFromDb(List leafNodeList public Map fetchTeamCapacityDataFromDb(List leafNodeList, String startDate, String endDate, KpiRequest kpiRequest, String capacityKey) { Map resultListMap = new HashMap<>(); - Map> mapOfFilters = new LinkedHashMap<>(); + Map mapOfFilters = new LinkedHashMap<>(); List projectList = new ArrayList<>(); leafNodeList.forEach(leaf -> projectList.add(leaf.getProjectFilter().getBasicProjectConfigId())); - + /** additional filter **/ + KpiDataHelper.createAdditionalFilterMapForCapacity(kpiRequest, mapOfFilters, flterHelperService); mapOfFilters.put(JiraFeatureHistory.BASIC_PROJECT_CONFIG_ID.getFieldValueInFeature(), projectList.stream().distinct().collect(Collectors.toList())); diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/impl/UserInfoServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/impl/UserInfoServiceImpl.java index f77ed647b4..2fe8b7c826 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/impl/UserInfoServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/common/service/impl/UserInfoServiceImpl.java @@ -139,6 +139,11 @@ public UserInfo getUserInfo(String username) { return userInfoRepository.findByUsername(username); } + + @Override + public UserInfo getFistUserInfo(String username) { + return userInfoRepository.findFirstByUsername(username); + } @Override public Collection getUsers() { List userInfoList = userInfoRepository.findAll(); diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/config/CustomApiConfig.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/config/CustomApiConfig.java index e0c446dd80..a9b9f20bd4 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/config/CustomApiConfig.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/config/CustomApiConfig.java @@ -217,6 +217,7 @@ public class CustomApiConfig {// NOPMD private String samlTokenEndString; private String samlUrlStartString; private String samlUrlEndString; + private Boolean isGitlabFieldEnable; // repo x axis count days rangeForCheckInsAndMergeRequests private Integer repoXAxisCountForCheckInsAndMergeRequests; private String repoToolAPIKey; @@ -313,6 +314,14 @@ public void setIsRepoToolEnable(Boolean isRepoToolEnable) { this.isRepoToolEnable = isRepoToolEnable; } + public Boolean getIsGitlabFieldEnable() { + return isGitlabFieldEnable; + } + + public void setIsGitlabFieldEnable(Boolean isGitlabFieldEnable) { + this.isGitlabFieldEnable = isGitlabFieldEnable; + } + public String getRepoToolAPIKey() { return repoToolAPIKey; } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/constant/Constant.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/constant/Constant.java index cf17e60a46..c5e9cc59d9 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/constant/Constant.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/constant/Constant.java @@ -143,6 +143,8 @@ public final class Constant { public static final String REPO_TOOLS = "RepoTool"; + public static final String FEATURE_BRANCH = "/feature/"; + private Constant() { } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/enums/KPIExcelColumn.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/enums/KPIExcelColumn.java index 11f2d677b5..bc132b6c92 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/enums/KPIExcelColumn.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/enums/KPIExcelColumn.java @@ -21,9 +21,19 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; +import org.apache.commons.collections4.CollectionUtils; +import org.bson.types.ObjectId; + import com.fasterxml.jackson.databind.ObjectMapper; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; +import com.publicissapient.kpidashboard.apis.model.AccountHierarchyData; +import com.publicissapient.kpidashboard.apis.model.Node; +import com.publicissapient.kpidashboard.common.model.application.AdditionalFilterCategory; /** * to order the headings of excel columns @@ -34,12 +44,12 @@ public enum KPIExcelColumn { CODE_BUILD_TIME("kpi8", Arrays.asList("Project Name", "Job Name", "Start Time", "End Time", "Duration", "Build Status", "Build Url", "Weeks")), ISSUE_COUNT("kpi40", - Arrays.asList("Sprint Name", "Story ID", "Issue Description")), CODE_COMMIT("kpi11", - Arrays.asList("Project Name", "Repository Url", "Branch", "Days/Weeks", "No. Of Commit", + Arrays.asList("Sprint Name", "Story ID", "Issue Description", "Squad" )), CODE_COMMIT("kpi11", + Arrays.asList("Project Name", "Repo", "Branch", "Days/Weeks", "No. Of Commit", "No. of Merge")), MEAN_TIME_TO_MERGE("kpi84", - Arrays.asList("Project", "Repository Url", "Branch", "Days/Weeks", + Arrays.asList("Project", "Repo", "Branch", "Days/Weeks", "Mean Time To Merge (In Hours)")), AVERAGE_RESOLUTION_TIME( "kpi83", Arrays.asList("Sprint Name", "Story ID", "Issue Description", "Issue Type", @@ -49,46 +59,46 @@ public enum KPIExcelColumn { "Triage to Complete (In Days)", "Complete TO Live (In Days)", "Lead Time (In Days)")), SPRINT_VELOCITY("kpi39", - Arrays.asList("Sprint Name", "Story ID", "Issue Description", + Arrays.asList("Sprint Name", "Story ID", "Issue Description", "Squad", "Size(story point/hours)")), SPRINT_PREDICTABILITY( "kpi5", - Arrays.asList("Sprint Name", "Story ID", "Issue Description", + Arrays.asList("Sprint Name", "Story ID", "Issue Description", "Squad", "Size(story point/hours)")), SPRINT_CAPACITY_UTILIZATION( "kpi46", - Arrays.asList("Sprint Name", "Story ID", "Issue Description", + Arrays.asList("Sprint Name", "Story ID", "Issue Description", "Squad", "Original Time Estimate (in hours)", "Total Time Spent (in hours)")), COMMITMENT_RELIABILITY("kpi72", - Arrays.asList("Sprint Name", "Story ID", "Issue Status","Issue Type", + Arrays.asList("Sprint Name", "Story ID", "Squad", "Issue Status","Issue Type", "Initial Commitment", "Size(story point/hours)")), - DEFECT_INJECTION_RATE("kpi14", Arrays.asList("Sprint Name", "Story ID", "Issue Description", "Linked Defects")), + DEFECT_INJECTION_RATE("kpi14", Arrays.asList("Sprint Name", "Story ID", "Issue Description", "Squad", "Linked Defects")), - FIRST_TIME_PASS_RATE("kpi82", Arrays.asList("Sprint Name", "Story ID", "Issue Description", "First Time Pass")), + FIRST_TIME_PASS_RATE("kpi82", Arrays.asList("Sprint Name", "Story ID", "Issue Description", "Squad", "First Time Pass")), - DEFECT_DENSITY("kpi111", Arrays.asList("Sprint Name", "Story ID", "Issue Description", "Linked Defects to Story", + DEFECT_DENSITY("kpi111", Arrays.asList("Sprint Name", "Story ID", "Issue Description", "Squad", "Linked Defects to Story", "Size(story point/hours)")), - DEFECT_SEEPAGE_RATE("kpi35", Arrays.asList("Sprint Name", "Defect ID", "Issue Description", "Escaped Defect")), + DEFECT_SEEPAGE_RATE("kpi35", Arrays.asList("Sprint Name", "Defect ID", "Issue Description", "Squad", "Escaped Defect")), DEFECT_REMOVAL_EFFICIENCY("kpi34", - Arrays.asList("Sprint Name", "Defect ID", "Issue Description", "Defect Removed")), + Arrays.asList("Sprint Name", "Defect ID", "Issue Description", "Squad", "Defect Removed")), - DEFECT_REJECTION_RATE("kpi37", Arrays.asList("Sprint Name", "Defect ID", "Issue Description", "Defect Rejected")), + DEFECT_REJECTION_RATE("kpi37", Arrays.asList("Sprint Name", "Defect ID", "Issue Description", "Squad", "Defect Rejected")), - DEFECT_COUNT_BY_PRIORITY("kpi28", Arrays.asList("Sprint Name", "Defect ID", "Issue Description", "Priority")), + DEFECT_COUNT_BY_PRIORITY("kpi28", Arrays.asList("Sprint Name", "Defect ID", "Issue Description", "Squad", "Priority")), - DEFECT_COUNT_BY_RCA("kpi36", Arrays.asList("Sprint Name", "Defect ID", "Issue Description", "Root Cause")), + DEFECT_COUNT_BY_RCA("kpi36", Arrays.asList("Sprint Name", "Defect ID", "Issue Description", "Squad", "Root Cause")), - DEFECT_COUNT_BY_PRIORITY_PIE_CHART("kpi140", Arrays.asList("Defect ID", "Issue Description", "Issue Status", + DEFECT_COUNT_BY_PRIORITY_PIE_CHART("kpi140", Arrays.asList("Defect ID", "Issue Description", "Squad", "Issue Status", "Issue Type", "Size(story point/hours)", "Root Cause", "Priority", "Assignee", "Created during Iteration")), - DEFECT_COUNT_BY_RCA_PIE_CHART("kpi132", Arrays.asList("Defect ID", "Issue Description", "Issue Status", + DEFECT_COUNT_BY_RCA_PIE_CHART("kpi132", Arrays.asList("Defect ID", "Issue Description", "Squad", "Issue Status", "Issue Type", "Size(story point/hours)", "Root Cause", "Priority", "Assignee", "Created during Iteration")), - CREATED_VS_RESOLVED_DEFECTS("kpi126", Arrays.asList("Sprint Name", "Created Defect ID", "Issue Description", - "Defect added after Sprint Start", "Resolved")), + CREATED_VS_RESOLVED_DEFECTS("kpi126", Arrays.asList("Sprint Name", "Created Defect ID", "Issue Description", "Squad", + "Defect added after Sprint Start", "Resolved Status")), - DEFECT_COUNT_BY_STATUS_PIE_CHART("kpi136", Arrays.asList("Defect ID", "Issue Description", "Issue Status", + DEFECT_COUNT_BY_STATUS_PIE_CHART("kpi136", Arrays.asList("Defect ID", "Issue Description", "Squad", "Issue Status", "Issue Type", "Size(story point/hours)", "Root Cause", "Priority", "Assignee", "Created during Iteration")), REGRESSION_AUTOMATION_COVERAGE("kpi42", Arrays.asList("Sprint Name", "Test Case ID", "Automated")), @@ -108,7 +118,7 @@ public enum KPIExcelColumn { Arrays.asList("Sprint Name", "Total Test", "Executed Test", "Execution %", "Passed Test", "Passed %")), COST_OF_DELAY("kpi113", - Arrays.asList("Project Name", "Cost of Delay", "Epic ID", "Epic Name", "Epic End Date", "Month")), + Arrays.asList("Project Name", "Cost of Delay", "Epic ID", "Epic Name", "Squad", "Epic End Date", "Month")), RELEASE_FREQUENCY("kpi73", Arrays.asList("Project Name", "Release Name", "Release Description", "Release End Date", "Month")), @@ -161,7 +171,7 @@ public enum KPIExcelColumn { "Build Status", "Build Url")), CODE_COMMIT_MERGE_KANBAN("kpi65", - Arrays.asList("Project Name", "Repository Url", "Branch", "Days/Weeks", "No. Of Commit")), + Arrays.asList("Project Name", "Repo", "Branch", "Days/Weeks", "No. Of Commit")), TEAM_CAPACITY_KANBAN("kpi58", Arrays.asList("Project Name", "Start Date", "End Date", "Estimated Capacity (in hours)")), @@ -261,7 +271,7 @@ public enum KPIExcelColumn { RELEASE_BURNUP("kpi150", Arrays.asList("Issue ID", "Issue Type", "Issue Description", "Size(story point/hours)", "Priority", "Assignee", "Issue Status")), - PI_PREDICTABILITY("kpi153", Arrays.asList("Project Name", "Epic ID", "Epic Name", "Status", "PI Name", + PI_PREDICTABILITY("kpi153", Arrays.asList("Project Name", "Epic ID", "Epic Name", "Squad", "Status", "PI Name", "Planned Value", "Achieved Value")), DAILY_STANDUP_VIEW("kpi154", Arrays.asList("Remaining Capacity", "Remaining Estimate", "Remaining Work", "Delay")), @@ -270,7 +280,7 @@ public enum KPIExcelColumn { DEFECT_COUNT_BY_TYPE("kpi155", Arrays.asList("Issue ID", "Issue Description", "Issue Type", "Issue Status", "Sprint Name", "Priority","Created Date","Updated Date", "Assignee")), //DTS-26123 end - SCOPE_CHURN("kpi164", Arrays.asList("Sprint Name","Issue ID", "Issue Type", "Issue Description", "Size(story point/hours)","Scope Change Date","Scope Change (Added/Removed)","Issue Status")), + SCOPE_CHURN("kpi164", Arrays.asList("Sprint Name","Issue ID", "Issue Type", "Issue Description", "Squad", "Size(story point/hours)","Scope Change Date","Scope Change (Added/Removed)","Issue Status")), LEAD_TIME_FOR_CHANGE("Kpi156", Arrays.asList("Project Name", "Date", "Story ID", "Lead Time (In Days)", "Completion Date", "Merge Date", "Release Date", "Merge Request Id", "Branch")), @@ -278,9 +288,11 @@ public enum KPIExcelColumn { RELEASE_DEFECT_BY_TEST_PHASE("kpi163", Arrays.asList("Issue ID", "Issue Description", "Issue Type", "Priority", "Sprint Name", "Assignee", "Issue Status")), PICKUP_TIME("kpi160", - Arrays.asList("Project", "Repository Url", "Branch", "Days/Weeks", - "Pickup Time (In Hours)")), PR_SIZE("kpi162", - Arrays.asList("Project", "Repository Url", "Branch", "Days/Weeks", + Arrays.asList("Project", "Repo", "Branch", "Days/Weeks", + "Pickup Time (In Hours)")), + + PR_SIZE("kpi162", + Arrays.asList("Project", "Repo", "Branch", "Days/Weeks", "PR Size (No. of lines)")), EPIC_PROGRESS("kpi165", Arrays.asList("Epic ID", "Epic Name", "Size(story point/hours)", "Epic Status")), @@ -331,4 +343,32 @@ public List getColumns() { return columns; } + public List getColumns(List nodeList, CacheService cacheService, + FilterHelperService filterHelperService) { + Set projectIds = nodeList.stream().map(a -> a.getProjectFilter().getBasicProjectConfigId()) + .collect(Collectors.toSet()); + boolean squadConfigured = false; + Map addFilterCat = filterHelperService.getAdditionalFilterHierarchyLevel(); + List accountHierarchyData = (List) cacheService + .cacheAccountHierarchyData(); + for (ObjectId projectId : projectIds) { + if (CollectionUtils.isNotEmpty(accountHierarchyData) + && CollectionUtils + .isNotEmpty((accountHierarchyData).stream() + .filter(a -> addFilterCat.containsKey(a.getLabelName()) + && a.getBasicProjectConfigId().equals(projectId)) + .collect(Collectors.toList()))) { + squadConfigured = true; + break; + } + } + if (!squadConfigured) { + List modifiableList = new ArrayList<>(columns); + modifiableList.removeIf(a -> a.equalsIgnoreCase("Squad")); + return modifiableList; + + } + return columns; + } + } \ No newline at end of file diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CapacityServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CapacityServiceImpl.java index ff449ab59f..864affda06 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CapacityServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CapacityServiceImpl.java @@ -23,7 +23,10 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.bson.types.ObjectId; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -31,12 +34,16 @@ import com.publicissapient.kpidashboard.apis.enums.Filters; import com.publicissapient.kpidashboard.apis.enums.KPICode; import com.publicissapient.kpidashboard.apis.errors.ApplicationException; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import com.publicissapient.kpidashboard.apis.jira.service.JiraKPIService; import com.publicissapient.kpidashboard.apis.model.KpiElement; import com.publicissapient.kpidashboard.apis.model.KpiRequest; import com.publicissapient.kpidashboard.apis.model.Node; import com.publicissapient.kpidashboard.apis.model.TreeAggregatorDetail; +import com.publicissapient.kpidashboard.common.model.application.AdditionalFilterCapacity; +import com.publicissapient.kpidashboard.common.model.application.AdditionalFilterCategory; import com.publicissapient.kpidashboard.common.model.application.DataCount; +import com.publicissapient.kpidashboard.common.model.application.LeafNodeCapacity; import com.publicissapient.kpidashboard.common.model.excel.CapacityKpiData; import com.publicissapient.kpidashboard.common.repository.excel.CapacityKpiDataRepository; @@ -51,6 +58,9 @@ public class CapacityServiceImpl extends JiraKPIService, M @Autowired private CapacityKpiDataRepository capacityKpiDataRepository; + @Autowired + private FilterHelperService filterHelperService; + @Override public KpiElement getKpiData(KpiRequest kpiRequest, KpiElement kpiElement, TreeAggregatorDetail treeAggregatorDetail) throws ApplicationException { @@ -86,12 +96,83 @@ public Map fetchKPIDataFromDb(List leafNodeList, String st String sprintId = leafNode.getSprintFilter().getId(); CapacityKpiData capacityKpiData = capacityKpiDataRepository.findBySprintIDAndBasicProjectConfigId(sprintId, basicProjectConfigId); - resultListMap.put(CAPACITY_DATA, capacityKpiData); + + resultListMap.put(CAPACITY_DATA, getCapacityDataForAdditionalFilter(kpiRequest, capacityKpiData)); } return resultListMap; } + /** + * Retrieves the capacity data for an additional filter based on the given KPI + * request. + * + * @param kpiRequest + * the KPI request containing selected filters + * @param capacityKpiData + * the current capacity KPI data + * @return a new CapacityKpiData object with updated capacity per sprint if + * additional capacity was found, otherwise returns the original + * capacityKpiData + */ + private CapacityKpiData getCapacityDataForAdditionalFilter(KpiRequest kpiRequest, CapacityKpiData capacityKpiData) { + // Create a map of additional filter categories with keys in uppercase for + // case-insensitive matching + Map addFilterCategory = filterHelperService + .getAdditionalFilterHierarchyLevel().entrySet().stream() + .collect(Collectors.toMap(entry -> entry.getKey().toUpperCase(), Map.Entry::getValue)); + + boolean additionalCapacity = false; + Double capacity = 0.0D; + + // Iterate through the selected filters in the KPI request + for (Map.Entry> entry : kpiRequest.getSelectedMap().entrySet()) { + String key = entry.getKey(); + List value = entry.getValue(); + + // Check if the filter has a non-empty value list, exists in the additional + // filter categories, and capacity data is not empty + if (CollectionUtils.isNotEmpty(value) && addFilterCategory.containsKey(key.toUpperCase()) + && ObjectUtils.isNotEmpty(capacityKpiData)) { + + List additionalFilterCapacityList = capacityKpiData + .getAdditionalFilterCapacityList(); + + // If there are additional filter capacities, calculate the total capacity + if (CollectionUtils.isNotEmpty(additionalFilterCapacityList)) { + additionalCapacity = true; + String upperCaseKey = key.toUpperCase(); + List additionalFilter = new ArrayList<>(value); + + // Sum the capacities matching the filter criteria + capacity += additionalFilterCapacityList.stream() + .filter(additionalFilterCapacity -> upperCaseKey + .equals(additionalFilterCapacity.getFilterId().toUpperCase())) + .flatMap( + additionalFilterCapacity -> additionalFilterCapacity.getNodeCapacityList().stream()) + .filter(leaf -> additionalFilter.contains(leaf.getAdditionalFilterId()) + && leaf.getAdditionalFilterCapacity() != null) + .mapToDouble(LeafNodeCapacity::getAdditionalFilterCapacity).sum(); + } + } + } + + // If additional capacity was found, create a new CapacityKpiData object with + // the updated capacity to not hamper the further calculations + if (additionalCapacity) { + CapacityKpiData newCapacity = new CapacityKpiData(); + newCapacity.setBasicProjectConfigId(capacityKpiData.getBasicProjectConfigId()); + newCapacity.setSprintID(capacityKpiData.getSprintID()); + newCapacity.setProjectName(capacityKpiData.getProjectName()); + newCapacity.setProjectId(capacityKpiData.getProjectId()); + newCapacity.setCapacityPerSprint(capacity); + return newCapacity; + } + + // If no additional capacity was found, return the original capacity data + return capacityKpiData; + } + /** * Populates KPI value to sprint leaf nodes and gives the trend analysis at * sprint level. diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CommittmentReliabilityServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CommittmentReliabilityServiceImpl.java index 96c7dc13cb..004f9ac226 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CommittmentReliabilityServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CommittmentReliabilityServiceImpl.java @@ -15,6 +15,7 @@ import java.util.function.Function; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; import org.apache.commons.collections.MapUtils; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; @@ -99,6 +100,9 @@ public class CommittmentReliabilityServiceImpl extends JiraKPIService stringObjectMap) { return 0L; @@ -284,7 +288,7 @@ private void sprintWiseLeafNodeValue(Map mapTmp, List sprint }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.COMMITMENT_RELIABILITY.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.COMMITMENT_RELIABILITY.getColumns(sprintLeafNodeList, cacheService, flterHelperService)); } private static Set getCompletedIssues(List allJiraIssue, SprintDetails sd, diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CostOfDelayServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CostOfDelayServiceImpl.java index 30d6a5b4a3..9f776d28e0 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CostOfDelayServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CostOfDelayServiceImpl.java @@ -30,6 +30,8 @@ import java.util.Map; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; @@ -68,6 +70,10 @@ public class CostOfDelayServiceImpl extends JiraKPIService, private JiraIssueRepository jiraIssueRepository; @Autowired private CustomApiConfig customApiConfig; + @Autowired + private FilterHelperService flterHelperService; + @Autowired + private CacheService cacheService; @Override public Double calculateKPIMetrics(Map subCategoryMap) { @@ -161,7 +167,7 @@ private void projectWiseLeafNodeValue(Map mapTmp, List proje }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.COST_OF_DELAY.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.COST_OF_DELAY.getColumns(projectLeafNodeList, cacheService, flterHelperService)); } /** diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CreatedVsResolvedServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CreatedVsResolvedServiceImpl.java index 1dbdfd7e2b..cb777687e9 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CreatedVsResolvedServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CreatedVsResolvedServiceImpl.java @@ -40,6 +40,7 @@ import org.springframework.stereotype.Component; import com.publicissapient.kpidashboard.apis.appsetting.service.ConfigHelperService; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; import com.publicissapient.kpidashboard.apis.common.service.impl.KpiHelperService; import com.publicissapient.kpidashboard.apis.config.CustomApiConfig; import com.publicissapient.kpidashboard.apis.constant.Constant; @@ -107,6 +108,8 @@ public class CreatedVsResolvedServiceImpl extends JiraKPIService mapTmp, List sprint Map, List> sprintWiseCreatedIssues = new HashMap<>(); Map, List> sprintWiseClosedIssues = new HashMap<>(); + Map, Map> sprintWiseClosedIssueStatus = new HashMap<>(); if ((CollectionUtils.isNotEmpty(allJiraIssue) || CollectionUtils.isNotEmpty(allSubTaskBugs)) && CollectionUtils.isNotEmpty(sprintDetails)) { @@ -333,17 +337,23 @@ private void sprintWiseLeafNodeValue(Map mapTmp, List sprint allJiraIssue.stream().filter(element -> completedSprintIssues.contains(element.getNumber())) .collect(Collectors.toList()), sd); + //to get the resolved status along with the completed jiraissue + Map issueWiseDeliveredStatus = completedIssues.stream().collect(Collectors.toMap(JiraIssue::getNumber, JiraIssue::getStatus, (e1, e2) -> e1)); + FieldMapping fieldMapping = configHelperService.getFieldMapping(sd.getBasicProjectConfigId()); List deliveredStatus = Optional.ofNullable(fieldMapping) .map(FieldMapping::getJiraIssueDeliverdStatusKPI126).orElse(Collections.emptyList()).stream() .map(String::toLowerCase).collect(Collectors.toList()); completedIssues.addAll(KpiDataHelper.getCompletedSubTasksByHistory(totalSubTask, allSubTaskBugsHistory, - sd, deliveredStatus)); + sd, deliveredStatus, issueWiseDeliveredStatus)); - sprintWiseCreatedIssues.put(Pair.of(sd.getBasicProjectConfigId().toString(), sd.getSprintID()), + Pair nodeIdentifier = Pair.of(sd.getBasicProjectConfigId().toString(), sd.getSprintID()); + sprintWiseCreatedIssues.put(nodeIdentifier, totalIssues); - sprintWiseClosedIssues.put(Pair.of(sd.getBasicProjectConfigId().toString(), sd.getSprintID()), + sprintWiseClosedIssues.put(nodeIdentifier, completedIssues); + sprintWiseClosedIssueStatus.put(nodeIdentifier,issueWiseDeliveredStatus); + }); } @@ -363,6 +373,7 @@ private void sprintWiseLeafNodeValue(Map mapTmp, List sprint List totalCreatedIssues = new ArrayList<>(); List totalCreatedIssuesAfter = new ArrayList<>(); List totalClosedIssues = new ArrayList<>(); + Map closedIssuesWithStatus= new HashMap<>(); Optional jiraSprint = sprintDetails.stream() .filter(sd -> sd.getSprintID().equalsIgnoreCase(currentSprintComponentId)).findFirst(); @@ -377,11 +388,13 @@ private void sprintWiseLeafNodeValue(Map mapTmp, List sprint .isAfter(LocalDate.parse(sprintStartDate.split("\\.")[0], DATE_TIME_FORMATTER))) .collect(Collectors.toList()); totalClosedIssues = sprintWiseClosedIssues.get(currentNodeIdentifier); + closedIssuesWithStatus = sprintWiseClosedIssueStatus.get(currentNodeIdentifier); } createdVsResolvedDefectsMap = createCreatedVsResolvedMap(totalCreatedIssues, totalClosedIssues, totalCreatedIssuesAfter, createdVsResolvedHoverMap); - populateExcelDataObject(requestTrackerId, excelData, node, totalCreatedIssues, totalClosedIssues, + populateExcelDataObject(requestTrackerId, excelData, node, totalCreatedIssues, + closedIssuesWithStatus, totalCreatedIssuesAfter); log.debug("[CREATED-VS-RESOLVED-SPRINT-WISE][{}]. Created Vs Resolved for sprint {} is {} - {}", @@ -407,11 +420,11 @@ private void sprintWiseLeafNodeValue(Map mapTmp, List sprint mapTmp.get(node.getId()).setValue(dataCountMap); }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.CREATED_VS_RESOLVED_DEFECTS.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.CREATED_VS_RESOLVED_DEFECTS.getColumns(sprintLeafNodeList, cacheService, flterHelperService)); } private void populateExcelDataObject(String requestTrackerId, List excelData, Node node, - List totalCreatedTickets, List totalResolvedTickets, + List totalCreatedTickets, Map closedIssuesWithStatus, List totalCreatedTicketsSprintStart) { if (requestTrackerId.toLowerCase().contains(KPISource.EXCEL.name().toLowerCase())) { @@ -420,7 +433,7 @@ private void populateExcelDataObject(String requestTrackerId, List totalCreatedTickets.stream() .forEach(jiraIssue -> createdTicketMap.putIfAbsent(jiraIssue.getNumber(), jiraIssue)); KPIExcelUtility.populateCreatedVsResolvedExcelData(node.getSprintFilter().getName(), createdTicketMap, - totalResolvedTickets, totalCreatedTicketsSprintStart, excelData); + totalCreatedTicketsSprintStart, closedIssuesWithStatus, excelData); } } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DCServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DCServiceImpl.java index ce2031ae40..e853b5be86 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DCServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DCServiceImpl.java @@ -28,6 +28,7 @@ import java.util.Set; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -97,6 +98,9 @@ public class DCServiceImpl extends JiraKPIService, Map mapTmp, List sprint }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_COUNT_BY_PRIORITY.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_COUNT_BY_PRIORITY.getColumns(sprintLeafNodeList, cacheService, flterHelperService)); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DIRServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DIRServiceImpl.java index 1181bfa7f6..9cd6b30f37 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DIRServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DIRServiceImpl.java @@ -27,6 +27,8 @@ import java.util.Set; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; @@ -68,6 +70,10 @@ public class DIRServiceImpl extends JiraKPIService, Map mapTmp, List sprint trendValueList.add(dataCount); }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_INJECTION_RATE.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_INJECTION_RATE.getColumns(sprintLeafNodeList, cacheService, flterHelperService)); } /** diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DREServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DREServiceImpl.java index 4a3ab51ccc..e1e85a0076 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DREServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DREServiceImpl.java @@ -35,6 +35,7 @@ import java.util.Set; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.tuple.Pair; import org.bson.types.ObjectId; @@ -108,6 +109,8 @@ public class DREServiceImpl extends JiraKPIService, Map mapTmp, List sprint }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_REMOVAL_EFFICIENCY.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_REMOVAL_EFFICIENCY.getColumns(sprintLeafNodeList, cacheService, flterHelperService)); } private void populateExcelDataObject(String requestTrackerId, String sprintName, List excelData, diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DRRServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DRRServiceImpl.java index 3794fa1106..b46a51aa4f 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DRRServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DRRServiceImpl.java @@ -32,6 +32,7 @@ import javax.ws.rs.core.Feature; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -105,6 +106,8 @@ public class DRRServiceImpl extends JiraKPIService, Map mapTmp, List sprint .filter(element -> dodAndDefectRejStatus.contains(element.getStatus().toLowerCase())).collect(Collectors.toList()); sprintCompletedDefects.addAll( - KpiDataHelper.getCompletedSubTasksByHistory(sprintSubtask, totalSubtaskHistory, sd, dodAndDefectRejStatus)); + KpiDataHelper.getCompletedSubTasksByHistory(sprintSubtask, totalSubtaskHistory, sd, dodAndDefectRejStatus, new HashMap<>())); List sprintWiseRejectedDefectList = new ArrayList<>(); List sprintWiseCompletedDefectList = new ArrayList<>(); @@ -419,7 +422,7 @@ private void sprintWiseLeafNodeValue(Map mapTmp, List sprint }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_REJECTION_RATE.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_REJECTION_RATE.getColumns(sprintLeafNodeList, cacheService, flterHelperService)); } /** diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DSRServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DSRServiceImpl.java index 83fdf00e5f..a42bcd73a0 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DSRServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DSRServiceImpl.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -90,6 +91,8 @@ public class DSRServiceImpl extends JiraKPIService, Map mapTmp, List sprint }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_SEEPAGE_RATE.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_SEEPAGE_RATE.getColumns(sprintLeafNodeList, cacheService, flterHelperService)); } private List checkUATDefect(List testCaseList, Map projFieldMapping) { diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByPriorityServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByPriorityServiceImpl.java index 47aa45d344..3b01a38996 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByPriorityServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByPriorityServiceImpl.java @@ -16,6 +16,8 @@ import java.util.Set; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -70,6 +72,12 @@ public class DefectCountByPriorityServiceImpl extends JiraKPIService dataCountListForAllPriorities, Map overallPriorityCountMapAggregate) { for (DataCount dataCount : dataCountListForAllPriorities) { @@ -265,8 +273,8 @@ private void sprintWiseLeafNodeValue(List sprintLeafNodeList, KpiElement k // filterDataList kpiElement.setSprint(latestSprint.getName()); - kpiElement.setModalHeads(KPIExcelColumn.DEFECT_COUNT_BY_PRIORITY_PIE_CHART.getColumns()); - kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_COUNT_BY_PRIORITY_PIE_CHART.getColumns()); + kpiElement.setModalHeads(KPIExcelColumn.DEFECT_COUNT_BY_PRIORITY_PIE_CHART.getColumns(Collections.singletonList(latestSprint), cacheService,filterHelperService)); + kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_COUNT_BY_PRIORITY_PIE_CHART.getColumns(Collections.singletonList(latestSprint), cacheService,filterHelperService)); kpiElement.setExcelData(excelData); sortedFilterDataList.add(filterDataList.stream() .filter(iterationKpiValue -> iterationKpiValue.getFilter1() diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByRCAServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByRCAServiceImpl.java index 51c2b369eb..4d220df748 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByRCAServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByRCAServiceImpl.java @@ -16,6 +16,8 @@ import java.util.Set; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -70,6 +72,12 @@ public class DefectCountByRCAServiceImpl extends JiraKPIService dataCountListForAllPriorities, Map overallRCACountMapAggregate) { for (DataCount dataCount : dataCountListForAllPriorities) { @@ -285,8 +293,8 @@ private void sprintWiseLeafNodeValue(List sprintLeafNodeList, KpiElement k latestSprint.getSprintFilter().getName(), fieldMapping, createDuringIteration); kpiElement.setSprint(latestSprint.getName()); - kpiElement.setModalHeads(KPIExcelColumn.DEFECT_COUNT_BY_RCA_PIE_CHART.getColumns()); - kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_COUNT_BY_RCA_PIE_CHART.getColumns()); + kpiElement.setModalHeads(KPIExcelColumn.DEFECT_COUNT_BY_RCA_PIE_CHART.getColumns(Collections.singletonList(latestSprint), cacheService,filterHelperService)); + kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_COUNT_BY_RCA_PIE_CHART.getColumns(Collections.singletonList(latestSprint), cacheService,filterHelperService)); kpiElement.setExcelData(excelData); sortedFilterDataList.add(filterDataList.stream() .filter(iterationKpiValue -> iterationKpiValue.getFilter1() diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByStatusServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByStatusServiceImpl.java index ae1b2aca65..7a0c2df983 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByStatusServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByStatusServiceImpl.java @@ -34,6 +34,8 @@ import java.util.Set; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -88,6 +90,12 @@ public class DefectCountByStatusServiceImpl extends JiraKPIService dataCountListForAllPriorities, Map overallStatusCountMapAggregate) { for (DataCount dataCount : dataCountListForAllPriorities) { @@ -279,8 +287,8 @@ private void sprintWiseLeafNodeValue(List sprintLeafNodeList, KpiElement k latestSprint.getSprintFilter().getName(), fieldMapping); kpiElement.setSprint(latestSprint.getName()); - kpiElement.setModalHeads(KPIExcelColumn.DEFECT_COUNT_BY_STATUS_PIE_CHART.getColumns()); - kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_COUNT_BY_STATUS_PIE_CHART.getColumns()); + kpiElement.setModalHeads(KPIExcelColumn.DEFECT_COUNT_BY_STATUS_PIE_CHART.getColumns(Collections.singletonList(latestSprint), cacheService,filterHelperService)); + kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_COUNT_BY_STATUS_PIE_CHART.getColumns(Collections.singletonList(latestSprint), cacheService,filterHelperService)); kpiElement.setExcelData(excelData); sortedFilterDataList.add(filterDataList.stream() .filter(iterationKpiValue -> iterationKpiValue.getFilter1() diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DevCompletionStatusServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DevCompletionStatusServiceImpl.java index 82fe930e03..b17862aa62 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DevCompletionStatusServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DevCompletionStatusServiceImpl.java @@ -506,13 +506,18 @@ private void setKpiSpecificData(Map modalObjectM Map jiraIssueData, Map actualCompletionData) { IterationKpiModalValue jiraIssueModalObject = modalObjectMap.get(jiraIssue.getNumber()); String markerValue = Constant.BLANK; - jiraIssueModalObject.setDevCompletionDate((String) jiraIssueData.get(DEV_COMPLETION_DATE)); + jiraIssueModalObject.setDevCompletionDate(DateUtil.dateTimeConverter( + (String) jiraIssueData.get(DEV_COMPLETION_DATE), DateUtil.DATE_FORMAT, DateUtil.DISPLAY_DATE_FORMAT)); if (actualCompletionData.get(ACTUAL_COMPLETE_DATE) != null) - jiraIssueModalObject.setActualCompletionDate(actualCompletionData.get(ACTUAL_COMPLETE_DATE).toString()); + jiraIssueModalObject.setActualCompletionDate( + DateUtil.dateTimeConverter(actualCompletionData.get(ACTUAL_COMPLETE_DATE).toString(), + DateUtil.DATE_FORMAT, DateUtil.DISPLAY_DATE_FORMAT)); else jiraIssueModalObject.setActualCompletionDate(" - "); if (actualCompletionData.get(ACTUAL_START_DATE) != null) { - jiraIssueModalObject.setActualStartDate(actualCompletionData.get(ACTUAL_START_DATE).toString()); + jiraIssueModalObject.setActualStartDate( + DateUtil.dateTimeConverter(actualCompletionData.get(ACTUAL_START_DATE).toString(), + DateUtil.DATE_FORMAT, DateUtil.DISPLAY_DATE_FORMAT)); } else jiraIssueModalObject.setActualStartDate(" - "); if (!jiraIssueData.get(ISSUE_DELAY).equals(Constant.DASH)) { diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/FTPRServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/FTPRServiceImpl.java index 9129a2feb7..2b4185d765 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/FTPRServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/FTPRServiceImpl.java @@ -56,6 +56,8 @@ import lombok.extern.slf4j.Slf4j; +import static com.publicissapient.kpidashboard.apis.util.IterationKpiHelper.getFilteredJiraIssue; + @Slf4j @Component public class FTPRServiceImpl extends JiraKPIService, Map> { @@ -202,13 +204,12 @@ public Map fetchKPIDataFromDb(List leafNodeList, String st List completedIssues = KpiDataHelper.getIssuesIdListBasedOnTypeFromSprintDetails(sprintDetails, CommonConstant.COMPLETED_ISSUES); if (CollectionUtils.isNotEmpty(completedIssues)) { - List jiraIssueList = jiraIssueRepository - .findByNumberInAndBasicProjectConfigId(completedIssues, basicProjectConfigId); + List filteredJiraIssue = getFilteredJiraIssue(completedIssues, totalJiraIssueList); List defectTypes = Optional.ofNullable(fieldMapping).map(FieldMapping::getJiradefecttype) .orElse(Collections.emptyList()); Set completedSprintReportDefects = new HashSet<>(); Set completedSprintReportStories = new HashSet<>(); - sprintDetails.getCompletedIssues().stream().forEach(sprintIssue -> { + filteredJiraIssue.stream().forEach(sprintIssue -> { if (defectTypes.contains(sprintIssue.getTypeName())) { completedSprintReportDefects.add(sprintIssue.getNumber()); } else { @@ -226,7 +227,7 @@ public Map fetchKPIDataFromDb(List leafNodeList, String st Set filtersIssuesList = KpiDataHelper .getFilteredJiraIssuesListBasedOnTypeFromSprintDetails(sprintDetails, - sprintDetails.getCompletedIssues(), jiraIssueList); + sprintDetails.getCompletedIssues(), filteredJiraIssue); // fetched all defects which is linked to current sprint report stories List linkedDefects = jiraIssueRepository.findLinkedDefects(mapOfFilters, diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/FirstTimePassRateServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/FirstTimePassRateServiceImpl.java index 8958db1419..201e5f97e3 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/FirstTimePassRateServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/FirstTimePassRateServiceImpl.java @@ -30,6 +30,7 @@ import javax.validation.constraints.NotNull; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.tuple.Pair; import org.bson.types.ObjectId; @@ -96,6 +97,9 @@ public class FirstTimePassRateServiceImpl extends JiraKPIService mapTmp, List sprint trendValueList.add(dataCount); }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.FIRST_TIME_PASS_RATE.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.FIRST_TIME_PASS_RATE.getColumns(sprintLeafNodeList, cacheService, flterHelperService)); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/IssueCountServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/IssueCountServiceImpl.java index dd9b29c0d8..4aef419469 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/IssueCountServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/IssueCountServiceImpl.java @@ -29,6 +29,7 @@ import java.util.Set; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.tuple.Pair; import org.bson.types.ObjectId; @@ -83,6 +84,8 @@ public class IssueCountServiceImpl extends JiraKPIService, @Autowired private FilterHelperService flterHelperService; @Autowired + private CacheService cacheService; + @Autowired private SprintRepository sprintRepository; @Autowired private CustomApiConfig customApiConfig; @@ -355,7 +358,7 @@ private void sprintWiseLeafNodeValue(Map mapTmp, List sprint } kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.ISSUE_COUNT.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.ISSUE_COUNT.getColumns(sprintLeafNodeList, cacheService, flterHelperService)); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/LeadTimeForChangeServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/LeadTimeForChangeServiceImpl.java index e483e2a150..0f09e214e5 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/LeadTimeForChangeServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/LeadTimeForChangeServiceImpl.java @@ -310,7 +310,7 @@ private void findMergeRequestList(Map toBranchForMRList, String toBranchForMRKPI156 = toBranchForMRList.get(projectBasicConfigId); List mergeRequestList = mergeRequestRepository .findMergeRequestListBasedOnBasicProjectConfigId(new ObjectId(projectBasicConfigId), - CommonUtils.convertTestFolderToPatternList(new ArrayList<>(issueIdList)), + CommonUtils.convertIssuesListToBranchPattern(new ArrayList<>(issueIdList)), toBranchForMRKPI156); projectWiseMergeRequestList.put(projectBasicConfigId, mergeRequestList); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/PIPredictabilityServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/PIPredictabilityServiceImpl.java index 1688c5a72c..846a7c9b1c 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/PIPredictabilityServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/PIPredictabilityServiceImpl.java @@ -9,6 +9,8 @@ import java.util.Optional; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.tuple.Pair; import org.bson.types.ObjectId; @@ -61,6 +63,12 @@ public class PIPredictabilityServiceImpl extends JiraKPIService m }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.PI_PREDICTABILITY.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.PI_PREDICTABILITY.getColumns(projectLeafNodeList, cacheService, flterHelperService)); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/QADDServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/QADDServiceImpl.java index be381d92c9..f434918c4b 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/QADDServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/QADDServiceImpl.java @@ -30,6 +30,8 @@ import java.util.Set; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -89,6 +91,12 @@ public class QADDServiceImpl extends JiraKPIService, Map mapTmp, List sprintL }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_DENSITY.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_DENSITY.getColumns(sprintLeafNodeList, cacheService, flterHelperService)); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/QualityStatusServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/QualityStatusServiceImpl.java index d5d9e9f0d5..d1827a14b0 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/QualityStatusServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/QualityStatusServiceImpl.java @@ -84,6 +84,7 @@ public class QualityStatusServiceImpl extends JiraKPIService jiraIssue1.getNumber() + " ( " + jiraIssue1.getPriority() + " ) ", JiraIssue::getUrl)); jiraIssueModalObject.setLinkedDefefect(linkedDefects); - jiraIssueModalObject.setDIR((double) defects.size()); + jiraIssueModalObject.setDIR(String.valueOf(defects.size())); jiraIssueModalObject .setDefectDensity(String.valueOf(calculateDefectDensity(jiraIssueList, defects, fieldMapping))); if (jiraIssueModalObject.getIssueSize() == null) @@ -576,5 +577,12 @@ private void setKpiSpecificData(JiraIssue jiraIssue, FieldMapping fieldMapping, jiraIssueModalObject.setLinkedStories(linkedStoriesMap); jiraIssueModalObject.setLinkedStoriesSize(storyPoint.get().toString()); } + else{ + Map dummyMap = new HashMap<>(); + dummyMap.put(NOT_APPLICABLE , NOT_APPLICABLE); + jiraIssueModalObject.setLinkedDefefect(dummyMap); + jiraIssueModalObject.setDIR(NOT_APPLICABLE); + jiraIssueModalObject.setDefectDensity(NOT_APPLICABLE); + } } } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/RCAServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/RCAServiceImpl.java index ef5ae465e2..c5159a2670 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/RCAServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/RCAServiceImpl.java @@ -31,6 +31,7 @@ import java.util.function.Function; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -94,6 +95,8 @@ public class RCAServiceImpl extends JiraKPIService, Map mapTmp, List sprint mapTmp.get(node.getId()).setValue(dataCountMap); }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_COUNT_BY_RCA.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.DEFECT_COUNT_BY_RCA.getColumns(sprintLeafNodeList, cacheService, flterHelperService)); } private void populateExcelDataObject(String requestTrackerId, List excelData, diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/ScopeChurnServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/ScopeChurnServiceImpl.java index 388217ec69..5673f33e39 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/ScopeChurnServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/ScopeChurnServiceImpl.java @@ -28,6 +28,7 @@ import java.util.Set; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.tuple.Pair; import org.bson.types.ObjectId; @@ -93,6 +94,8 @@ public class ScopeChurnServiceImpl extends JiraKPIService, @Autowired private FilterHelperService filterHelperService; @Autowired + private CacheService cacheService; + @Autowired private CustomApiConfig customApiConfig; @Autowired private JiraIssueCustomHistoryRepository jiraIssueCustomHistoryRepository; @@ -248,7 +251,7 @@ private void sprintWiseLeafNodeValue(Map mapTmp, List sprint }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.SCOPE_CHURN.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.SCOPE_CHURN.getColumns(sprintLeafNodeList, cacheService, filterHelperService)); } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintCapacityServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintCapacityServiceImpl.java index a7e2b0fc0a..151f8f0ca3 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintCapacityServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintCapacityServiceImpl.java @@ -31,6 +31,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; import com.publicissapient.kpidashboard.apis.common.service.impl.KpiHelperService; import com.publicissapient.kpidashboard.apis.config.CustomApiConfig; import com.publicissapient.kpidashboard.apis.enums.Filters; @@ -38,6 +39,7 @@ import com.publicissapient.kpidashboard.apis.enums.KPIExcelColumn; import com.publicissapient.kpidashboard.apis.enums.KPISource; import com.publicissapient.kpidashboard.apis.errors.ApplicationException; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import com.publicissapient.kpidashboard.apis.jira.service.JiraKPIService; import com.publicissapient.kpidashboard.apis.model.KPIExcelData; import com.publicissapient.kpidashboard.apis.model.KpiElement; @@ -71,6 +73,10 @@ public class SprintCapacityServiceImpl extends JiraKPIService fetchKPIDataFromDb(List leafNodeList, String st KpiRequest kpiRequest) { Map resultListMap = new HashMap<>(); - List sprintCapacityList = kpiHelperService.fetchSprintCapacityDataFromDb(leafNodeList); - List estimateTimeList = kpiHelperService.fetchCapacityDataFromDB(leafNodeList); + List sprintCapacityList = kpiHelperService.fetchSprintCapacityDataFromDb(kpiRequest, leafNodeList); + List estimateTimeList = kpiHelperService.fetchCapacityDataFromDB(kpiRequest, leafNodeList); setDbQueryLogger(sprintCapacityList); resultListMap.put(SPRINTCAPACITYKEY, sprintCapacityList); resultListMap.put(ESTIMATE_TIME, estimateTimeList); @@ -231,7 +237,8 @@ private void sprintWiseLeafNodeValue(Map mapTmp, List sprint mapTmp.get(node.getId()).setValue(new ArrayList<>(Arrays.asList(dataCount))); }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.SPRINT_CAPACITY_UTILIZATION.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.SPRINT_CAPACITY_UTILIZATION.getColumns(sprintLeafNodeList, + cacheService, flterHelperService)); } /** diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintPredictabilityImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintPredictabilityImpl.java index 826816f82e..a03823e271 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintPredictabilityImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintPredictabilityImpl.java @@ -14,6 +14,7 @@ import java.util.function.Function; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; import org.apache.commons.collections.MapUtils; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -80,6 +81,9 @@ public class SprintPredictabilityImpl extends JiraKPIService mapTmp, List sprint FieldMapping fieldMapping = configHelperService.getFieldMappingMap() .get(sprintLeafNodeList.get(0).getProjectFilter().getBasicProjectConfigId()); - if (CollectionUtils.isNotEmpty(sprintDetails)) { - - Map jiraIssueMap = sprintWiseJiraStoryList.stream().collect( - Collectors.toMap(JiraIssue::getNumber, Function.identity(), (existing, replacement) -> existing)); + Map jiraIssueMap = sprintWiseJiraStoryList.stream().collect( + Collectors.toMap(JiraIssue::getNumber, Function.identity(), (existing, replacement) -> existing)); + if (CollectionUtils.isNotEmpty(sprintDetails)) { sprintDetails.forEach(sd -> { Set filterIssueDetailsSet = new HashSet<>(); List storyList = new ArrayList<>(); @@ -340,7 +343,7 @@ private void sprintWiseLeafNodeValue(Map mapTmp, List sprint Pair currentNodeIdentifier = Pair .of(node.getProjectFilter().getBasicProjectConfigId().toString(), currentSprintComponentId); populateExcelDataObject(requestTrackerId, excelData, currentSprintLeafPredictabilityMap, node, - fieldMapping); + fieldMapping, jiraIssueMap); log.debug("[SPRINTPREDICTABILITY-SPRINT-WISE][{}]. SPRINTPREDICTABILITY for sprint {} is {}", requestTrackerId, node.getSprintFilter().getName(), currentNodeIdentifier); if (predictability.get(currentNodeIdentifier) != null) { @@ -359,7 +362,7 @@ private void sprintWiseLeafNodeValue(Map mapTmp, List sprint } }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.SPRINT_PREDICTABILITY.getColumns()); + kpiElement.setExcelColumns(KPIExcelColumn.SPRINT_PREDICTABILITY.getColumns(sprintLeafNodeList, cacheService, flterHelperService)); } @Override @@ -449,15 +452,15 @@ private void calculateFirstSprintPredictability(List storyList, } /** - * - * @param requestTrackerId + * @param requestTrackerId * @param excelData * @param currentSprintLeafVelocityMap * @param node + * @param jiraIssueMap */ private void populateExcelDataObject(String requestTrackerId, List excelData, - Map, Set> currentSprintLeafVelocityMap, Node node, - FieldMapping fieldMapping) { + Map, Set> currentSprintLeafVelocityMap, Node node, + FieldMapping fieldMapping, Map jiraIssueMap) { if (requestTrackerId.toLowerCase().contains(KPISource.EXCEL.name().toLowerCase())) { Pair currentNodeIdentifier = Pair .of(node.getProjectFilter().getBasicProjectConfigId().toString(), node.getSprintFilter().getId()); @@ -466,7 +469,7 @@ private void populateExcelDataObject(String requestTrackerId, List && CollectionUtils.isNotEmpty(currentSprintLeafVelocityMap.get(currentNodeIdentifier))) { Set issueDetailsSet = currentSprintLeafVelocityMap.get(currentNodeIdentifier); KPIExcelUtility.populateSprintPredictability(node.getSprintFilter().getName(), issueDetailsSet, - excelData, fieldMapping); + excelData, fieldMapping, jiraIssueMap); } } } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintVelocityServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintVelocityServiceImpl.java index 4cde17aecf..126a65190f 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintVelocityServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintVelocityServiceImpl.java @@ -30,6 +30,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.common.service.CacheService; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.tuple.Pair; @@ -92,6 +94,10 @@ public class SprintVelocityServiceImpl extends JiraKPIService mapTmp, List sprint trendValueList.add(dataCount); }); kpiElement.setExcelData(excelData); - kpiElement.setExcelColumns(KPIExcelColumn.SPRINT_VELOCITY.getColumns()); - } + kpiElement.setExcelColumns(KPIExcelColumn.SPRINT_VELOCITY.getColumns(sprintLeafNodeList, cacheService, flterHelperService)); } /** * Create map consisting of sprint and its velocity diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/WorkRemainingServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/WorkRemainingServiceImpl.java index 8b4e854d6d..e55723aa4c 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/WorkRemainingServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/WorkRemainingServiceImpl.java @@ -348,7 +348,8 @@ private int checkDelay(JiraIssue jiraIssue, Map private void setKpiSpecificData(SprintDetails sprintDetails, Map modalObjectMap, Map issueWiseDelay, JiraIssue jiraIssue, String devCompletionDate) { IterationKpiModalValue jiraIssueModalObject = modalObjectMap.get(jiraIssue.getNumber()); - jiraIssueModalObject.setDevCompletionDate(devCompletionDate); + jiraIssueModalObject.setDevCompletionDate( + DateUtil.dateTimeConverter(devCompletionDate, DateUtil.DATE_FORMAT, DateUtil.DISPLAY_DATE_FORMAT)); String markerValue = Constant.BLANK; if (issueWiseDelay.containsKey(jiraIssue.getNumber()) && StringUtils.isNotEmpty(jiraIssue.getDueDate())) { IterationPotentialDelay iterationPotentialDelay = issueWiseDelay.get(jiraIssue.getNumber()); diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/service/JiraKPIService.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/service/JiraKPIService.java index 4a1242b7c0..e7cd3ec68f 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/service/JiraKPIService.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/service/JiraKPIService.java @@ -23,6 +23,7 @@ import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.ArrayList; +import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -217,19 +218,19 @@ public void populateIterationDataForDefectWithoutStory(List fieldMapping) { + public static String getDevCompletionDate(JiraIssueCustomHistory issueCustomHistory, List fieldMapping) { String devCompleteDate = Constant.DASH; List filterStatusUpdationLog = issueCustomHistory.getStatusUpdationLog(); + if (null != fieldMapping && CollectionUtils.isNotEmpty(fieldMapping)) { devCompleteDate = filterStatusUpdationLog.stream() .filter(jiraHistoryChangeLog -> fieldMapping.contains(jiraHistoryChangeLog.getChangedTo()) && jiraHistoryChangeLog.getUpdatedOn() != null) - .findFirst() - .map(jiraHistoryChangeLog -> LocalDate - .parse(jiraHistoryChangeLog.getUpdatedOn().toString().split("T")[0], - DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT)) - .toString()) - .orElse(devCompleteDate); + .map(jiraHistoryChangeLog -> LocalDate.parse( + jiraHistoryChangeLog.getUpdatedOn().toString().split("T")[0], + DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT))) + .max(Comparator.naturalOrder()) + .map(LocalDate::toString).orElse(devCompleteDate); } return devCompleteDate; } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/service/JiraServiceR.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/service/JiraServiceR.java index b7fd7f2fdf..b69e7413ce 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/service/JiraServiceR.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/jira/service/JiraServiceR.java @@ -19,14 +19,17 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.RecursiveAction; import java.util.stream.Collectors; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang.SerializationUtils; import org.apache.commons.lang.StringUtils; import org.bson.types.ObjectId; @@ -40,6 +43,7 @@ import com.publicissapient.kpidashboard.apis.common.service.impl.KpiHelperService; import com.publicissapient.kpidashboard.apis.config.CustomApiConfig; import com.publicissapient.kpidashboard.apis.enums.Filters; +import com.publicissapient.kpidashboard.apis.enums.JiraFeature; import com.publicissapient.kpidashboard.apis.enums.KPICode; import com.publicissapient.kpidashboard.apis.enums.KPISource; import com.publicissapient.kpidashboard.apis.errors.ApplicationException; @@ -55,6 +59,7 @@ import com.publicissapient.kpidashboard.apis.util.KPIHelperUtil; import com.publicissapient.kpidashboard.common.constant.CommonConstant; import com.publicissapient.kpidashboard.common.constant.NormalizedJira; +import com.publicissapient.kpidashboard.common.model.application.AdditionalFilterCategory; import com.publicissapient.kpidashboard.common.model.application.FieldMapping; import com.publicissapient.kpidashboard.common.model.jira.JiraIssue; import com.publicissapient.kpidashboard.common.model.jira.JiraIssueCustomHistory; @@ -214,13 +219,11 @@ public List process(KpiRequest kpiRequest) throws EntityNotFoundExce private void updateJiraIssueList(KpiRequest kpiRequest, List origRequestedKpis, List filteredAccountDataList, TreeAggregatorDetail treeAggregatorDetail) { if (origRequestedKpis.get(0).getKpiCategory().equalsIgnoreCase(CommonConstant.ITERATION)) { - fetchSprintDetails(kpiRequest.getIds()); - List sprintIssuesList = createIssuesList( - filteredAccountDataList.get(0).getBasicProjectConfigId().toString()); - fetchJiraIssues(filteredAccountDataList.get(0).getBasicProjectConfigId().toString(), sprintIssuesList, - CommonConstant.ITERATION); - fetchJiraIssuesCustomHistory(filteredAccountDataList.get(0).getBasicProjectConfigId().toString(), - sprintIssuesList, CommonConstant.ITERATION); + fetchSprintDetails(kpiRequest.getSelectedMap().get(CommonConstant.SPRINT)); + String basicConfigId = filteredAccountDataList.get(0).getBasicProjectConfigId().toString(); + List sprintIssuesList = createIssuesList(basicConfigId); + fetchJiraIssues(kpiRequest, basicConfigId, sprintIssuesList); + fetchJiraIssuesCustomHistory(basicConfigId); } else if (origRequestedKpis.get(0).getKpiCategory().equalsIgnoreCase(CommonConstant.RELEASE)) { releaseList = getReleaseList(treeAggregatorDetail); fetchJiraIssues(filteredAccountDataList.get(0).getBasicProjectConfigId().toString(), releaseList, @@ -319,8 +322,8 @@ private boolean isLeadTimeDuration(List kpiList) { return kpiList.size() != 1 || !kpiList.get(0).getKpiId().equalsIgnoreCase("kpi3"); } - public void fetchSprintDetails(String[] sprintId) { - sprintDetails = sprintRepository.findBySprintIDIn(Arrays.stream(sprintId).collect(Collectors.toList())); + public void fetchSprintDetails(List sprintId) { + sprintDetails = sprintRepository.findBySprintIDIn(sprintId); } public void futureProjectWiseSprintDetails(ObjectId basicProjectConfigId, String sprintState) { @@ -336,11 +339,18 @@ public void setSprintDetails(List modifiedSprintDetails) { sprintDetails = modifiedSprintDetails; } + + public void fetchJiraIssues(KpiRequest kpiRequest, String basicConfigId, List sprintIssuesList) { + Map mapOfFilter = new HashMap<>(); + createAdditionalFilterMap(kpiRequest, mapOfFilter); + Map> uniqueProjectMap = new HashMap<>(); + uniqueProjectMap.put(basicConfigId, mapOfFilter); + jiraIssueList = jiraIssueRepository.findIssueByNumberWithAdditionalFilter(new HashSet<>(sprintIssuesList), + uniqueProjectMap); + } + public void fetchJiraIssues(String basicProjectConfigId, List sprintIssuesList, String board) { - if (board.equalsIgnoreCase(CommonConstant.ITERATION)) { - jiraIssueList = jiraIssueRepository.findByNumberInAndBasicProjectConfigId(sprintIssuesList, - basicProjectConfigId); - } else if (board.equalsIgnoreCase(CommonConstant.BACKLOG)) { + if (board.equalsIgnoreCase(CommonConstant.BACKLOG)) { jiraIssueList = jiraIssueRepository.findByBasicProjectConfigIdIn(basicProjectConfigId); } else if (board.equalsIgnoreCase(CommonConstant.RELEASE)) { jiraIssueReleaseList = jiraIssueRepository @@ -391,11 +401,15 @@ public Set getSubTaskDefects() { return threadSubtaskDefects.get(); } + public void fetchJiraIssuesCustomHistory(String basicProjectConfigId) { + List issueList = jiraIssueList.stream().map(JiraIssue::getNumber).collect(Collectors.toList()); + jiraIssueCustomHistoryList = jiraIssueCustomHistoryRepository + .findByStoryIDInAndBasicProjectConfigIdIn(issueList, Collections.singletonList(basicProjectConfigId)); + + } + public void fetchJiraIssuesCustomHistory(String basicProjectConfigId, List sprintIssuesList, String board) { - if (board.equalsIgnoreCase(CommonConstant.ITERATION)) { - jiraIssueCustomHistoryList = jiraIssueCustomHistoryRepository.findByStoryIDInAndBasicProjectConfigIdIn( - sprintIssuesList, Collections.singletonList(basicProjectConfigId)); - } else if (board.equalsIgnoreCase(CommonConstant.BACKLOG)) { + if (board.equalsIgnoreCase(CommonConstant.BACKLOG)) { jiraIssueCustomHistoryList = jiraIssueCustomHistoryRepository .findByBasicProjectConfigIdIn(basicProjectConfigId); } else { @@ -462,6 +476,24 @@ public List getFutureSprintsList() { return sprintNames; } + public void createAdditionalFilterMap(KpiRequest kpiRequest, Map mapOfFilters) { + Map addFilterCat = filterHelperService.getAdditionalFilterHierarchyLevel(); + Map addFilterCategory = addFilterCat.entrySet().stream() + .collect(Collectors.toMap(entry -> entry.getKey().toUpperCase(), Map.Entry::getValue)); + + if (MapUtils.isNotEmpty(kpiRequest.getSelectedMap())) { + for (Map.Entry> entry : kpiRequest.getSelectedMap().entrySet()) { + if (CollectionUtils.isNotEmpty(entry.getValue()) + && null != addFilterCategory.get(entry.getKey().toUpperCase())) { + mapOfFilters.put(JiraFeature.ADDITIONAL_FILTERS_FILTERID.getFieldValueInFeature(), + Arrays.asList(entry.getKey())); + mapOfFilters.put(JiraFeature.ADDITIONAL_FILTERS_FILTERVALUES_VALUEID.getFieldValueInFeature(), + entry.getValue()); + } + } + } + } + /** * This class is used to call JIRA based KPIs service in parallel. * @@ -550,6 +582,8 @@ private void calculateAllKPIAggregatedMetrics(KpiRequest kpiRequest, List squads; @JsonProperty("Intake to DOR (In Days)") private String intakeToDor; @JsonProperty("DOR to DOD (In Days)") @@ -146,7 +148,7 @@ public class IterationKpiModalValue implements Serializable { @JsonProperty("Lead Time (In Days)") private String leadTime; @JsonProperty("DIR") - private Double DIR; + private String DIR; @JsonProperty("Defect Density") private String defectDensity; } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/model/KPIExcelData.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/model/KPIExcelData.java index 580e13cd97..3045e6c445 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/model/KPIExcelData.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/model/KPIExcelData.java @@ -121,8 +121,8 @@ public class KPIExcelData { @JsonProperty("Root Cause") private List rootCause; - @JsonProperty("Resolved") - private String resolvedTickets; + @JsonProperty("Resolved Status") + private String resolvedStatus; @JsonProperty("Defect ID") private Map defectId; @@ -250,8 +250,8 @@ public class KPIExcelData { @JsonProperty("Build Url") private Map buildUrl; - @JsonProperty("Repository Url") - private Map repositoryURL; + @JsonProperty("Repo") + private String repo; @JsonProperty("Branch") private String branch; @@ -376,4 +376,7 @@ public class KPIExcelData { @JsonProperty("Days/Weeks") private String daysWeeks; + @JsonProperty("Squad") + private List squads; + } diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/projectconfig/projecttoolconfig/service/ProjectToolConfigServiceImpl.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/projectconfig/projecttoolconfig/service/ProjectToolConfigServiceImpl.java index a10594ab1a..32faaa5863 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/projectconfig/projecttoolconfig/service/ProjectToolConfigServiceImpl.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/projectconfig/projecttoolconfig/service/ProjectToolConfigServiceImpl.java @@ -390,6 +390,7 @@ private List mapSubProjects(List toolTy projectConfToolDto.setId(e.getId().toString()); projectConfToolDto.setToolName(e.getToolName()); projectConfToolDto.setProjectId(e.getProjectId()); + projectConfToolDto.setGitLabID(e.getGitLabID()); projectConfToolDto.setProjectKey(e.getProjectKey()); projectConfToolDto.setJobName(e.getJobName()); projectConfToolDto.setJobType(e.getJobType()); diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/rbac/userinfo/rest/UserInfoController.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/rbac/userinfo/rest/UserInfoController.java index d933464b0b..eb42ba0545 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/rbac/userinfo/rest/UserInfoController.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/rbac/userinfo/rest/UserInfoController.java @@ -102,37 +102,12 @@ public ResponseEntity updateUserRole(@PathVariable("username") return ResponseEntity.status(HttpStatus.OK).body(response); } - - /** - * Delete Users based on the userName. - * - * @return the Service Response - */ - /* - * @PreAuthorize("hasPermission(null, 'DELETE_USER')") - * - * @DeleteMapping(value = "/{userName}") public ServiceResponse - * deleteUser(@PathVariable String userName) { - * log.info("Inside deleteUser() method of UserInfoController "); String - * loggedUserName = authenticationService.getLoggedInUser(); UserInfo userInfo = - * userInfoRepository.findByUsername(userName); if - * ((!loggedUserName.equals(userName) && - * !userInfo.getAuthorities().contains(Constant.ROLE_SUPERADMIN)) ) { - * ServiceResponse response = userInfoService.deleteUser(userName); return new - * ServiceResponse(true, userName + " deleted Successfully", response); } else { - * log.info("Unauthorized to perform deletion of user " + userName); return new - * ServiceResponse(false, "Unauthorized to perform deletion of user", - * "Unauthorized"); } - * - * } - */ - @PreAuthorize("hasPermission(null, 'DELETE_USER')") @DeleteMapping(value = "/{userName}") public ResponseEntity deleteUser(@PathVariable String userName) { log.info("Inside deleteUser() method of UserInfoController "); String loggedUserName = authenticationService.getLoggedInUser(); - UserInfo userInfo = userInfoRepository.findByUsername(userName); + UserInfo userInfo = userInfoRepository.findFirstByUsername(userName); if ((!loggedUserName.equals(userName) && !userInfo.getAuthorities().contains(Constant.ROLE_SUPERADMIN))) { ServiceResponse response = userInfoService.deleteUser(userName); return ResponseEntity.status(HttpStatus.OK).body(response); diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/CommonUtils.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/CommonUtils.java index b6a7e5035f..3e4d6e1945 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/CommonUtils.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/CommonUtils.java @@ -339,18 +339,32 @@ public static List convertToPatternListForSubString(List string public static List convertTestFolderToPatternList(List stringList) { List regexList = new ArrayList<>(); for (String value : stringList) { - String pattern = new StringBuilder(value).toString(); - if (pattern.contains(Constant.FORWARD_SLASH)) { - pattern = pattern.replace(Constant.FORWARD_SLASH, Constant.BACKWARD_FORWARD_SLASH); - } - regexList.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE)); - if (!value.startsWith(Constant.FORWARD_SLASH)) { - value = Constant.FORWARD_SLASH.concat(value); + perpPatternList(value, regexList); + } + return regexList; + } + public static List convertIssuesListToBranchPattern(List stringList) { + List regexList = new ArrayList<>(); + for (String value : stringList) { + perpPatternList(value, regexList); + if (!value.startsWith(Constant.FEATURE_BRANCH)) { + value = Constant.FEATURE_BRANCH.concat(value); + regexList.add(CommonUtils.convertToPatternText(value)); } - regexList.add(CommonUtils.convertToPatternText(value)); } return regexList; } + private static void perpPatternList(String value, List regexList) { + String pattern = new StringBuilder(value).toString(); + if (pattern.contains(Constant.FORWARD_SLASH)) { + pattern = pattern.replace(Constant.FORWARD_SLASH, Constant.BACKWARD_FORWARD_SLASH); + } + regexList.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE)); + if (!value.startsWith(Constant.FORWARD_SLASH)) { + value = Constant.FORWARD_SLASH.concat(value); + } + regexList.add(CommonUtils.convertToPatternText(value)); + } /** * This method convert string to pattern to support ignore case diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/FieldMappingUtil.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/FieldMappingUtil.java new file mode 100644 index 0000000000..fa4d5b52ee --- /dev/null +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/FieldMappingUtil.java @@ -0,0 +1,196 @@ +package com.publicissapient.kpidashboard.apis.util; + +import com.publicissapient.kpidashboard.common.model.application.dto.FieldMappingDTO; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + + + +public class FieldMappingUtil { + + private FieldMappingUtil() { + throw new IllegalStateException("Utility class"); + } + + public static void updateFieldMapping(FieldMappingDTO fieldMappingDTO) { + if (fieldMappingDTO.getCreatedDate() == null) { + List defectPriority = fieldMappingDTO.getDefectPriority(); + List jiraStatusForDevelopment = fieldMappingDTO.getJiraStatusForDevelopment(); + List jiraDod = fieldMappingDTO.getJiraDod(); + String jiraDefectRejectionStatus = fieldMappingDTO.getJiraDefectRejectionStatus(); + List jiraDefectCountlIssueType = fieldMappingDTO.getJiraDefectCountlIssueType(); + List jiraIssueDeliverdStatus = fieldMappingDTO.getJiraIssueDeliverdStatus(); + List excludeRCAFromFTPR = fieldMappingDTO.getExcludeRCAFromFTPR(); + String[] jiraIssueTypeNames = fieldMappingDTO.getJiraIssueTypeNames(); + List resolutionTypeForRejection = fieldMappingDTO.getResolutionTypeForRejection(); + String jiraLiveStatus = fieldMappingDTO.getJiraLiveStatus(); + List jiraFTPRStoryIdentification = fieldMappingDTO.getJiraFTPRStoryIdentification(); + List jiraIterationCompletionStatusCustomField = fieldMappingDTO.getJiraIterationCompletionStatusCustomField(); + List jiraIterationCompletionTypeCustomField = fieldMappingDTO.getJiraIterationCompletionTypeCustomField(); + String jiraDor = fieldMappingDTO.getJiraDor(); + List jiraIntakeToDorIssueType = fieldMappingDTO.getJiraIntakeToDorIssueType(); + List jiraStoryIdentification = fieldMappingDTO.getJiraStoryIdentification(); + List jiraStatusForInProgress = fieldMappingDTO.getJiraStatusForInProgress(); + List issueStatusExcluMissingWork = fieldMappingDTO.getIssueStatusExcluMissingWork(); + List jiraWaitStatus = fieldMappingDTO.getJiraWaitStatus(); + List jiraBlockedStatus = fieldMappingDTO.getJiraBlockedStatus(); + String jiraIncludeBlockedStatus = fieldMappingDTO.getJiraIncludeBlockedStatus(); + List jiraDevDoneStatus = fieldMappingDTO.getJiraDevDoneStatus(); + List jiraQADefectDensityIssueType = fieldMappingDTO.getJiraQADefectDensityIssueType(); + List jiraDefectInjectionIssueType = fieldMappingDTO.getJiraDefectInjectionIssueType(); + String jiraDefectCreatedStatus = fieldMappingDTO.getJiraDefectCreatedStatus(); + List jiraDefectSeepageIssueType = fieldMappingDTO.getJiraDefectSeepageIssueType(); + List jiraDefectRemovalStatus = fieldMappingDTO.getJiraDefectRemovalStatus(); + List jiraDefectRemovalIssueType = fieldMappingDTO.getJiraDefectRemovalIssueType(); + List jiraSprintCapacityIssueType = fieldMappingDTO.getJiraSprintCapacityIssueType(); + List jiraDefectRejectionlIssueType = fieldMappingDTO.getJiraDefectRejectionlIssueType(); + List jiraStatusForQa = fieldMappingDTO.getJiraStatusForQa(); + String storyFirstStatus = fieldMappingDTO.getStoryFirstStatus(); + List jiraFtprRejectStatus = fieldMappingDTO.getJiraFtprRejectStatus(); + List jiraDefectClosedStatus = fieldMappingDTO.getJiraDefectClosedStatus(); + List jiraDefectDroppedStatus = fieldMappingDTO.getJiraDefectDroppedStatus(); + List jiraAcceptedInRefinement = fieldMappingDTO.getJiraAcceptedInRefinement(); + List jiraReadyForRefinement = fieldMappingDTO.getJiraReadyForRefinement(); + List jiraRejectedInRefinement = fieldMappingDTO.getJiraRejectedInRefinement(); + String readyForDevelopmentStatus = fieldMappingDTO.getReadyForDevelopmentStatus(); + + fieldMappingDTO.setDefectPriorityKPI135(defectPriority); + fieldMappingDTO.setDefectPriorityKPI14(defectPriority); + fieldMappingDTO.setDefectPriorityQAKPI111(defectPriority); + fieldMappingDTO.setDefectPriorityKPI82(defectPriority); + fieldMappingDTO.setDefectPriorityKPI133(defectPriority); + fieldMappingDTO.setJiraIssueTypeNamesAVR(jiraIssueTypeNames); + fieldMappingDTO.setJiraStatusForDevelopmentAVR(jiraStatusForDevelopment); + fieldMappingDTO.setJiraStatusForDevelopmentKPI82(jiraStatusForDevelopment); + fieldMappingDTO.setJiraStatusForDevelopmentKPI135(jiraStatusForDevelopment); + fieldMappingDTO.setJiraDefectInjectionIssueTypeKPI14(jiraDefectInjectionIssueType); + fieldMappingDTO.setJiraDodKPI14(jiraDod); + fieldMappingDTO.setJiraDodQAKPI111(jiraDod); + fieldMappingDTO.setJiraDodKPI3(jiraDod); + fieldMappingDTO.setJiraDodKPI127(jiraDod); + fieldMappingDTO.setJiraDodKPI152(jiraDod); + fieldMappingDTO.setJiraDodKPI151(jiraDod); + fieldMappingDTO.setJiraDefectCreatedStatusKPI14(jiraDefectCreatedStatus); + fieldMappingDTO.setJiraDefectRejectionStatusAVR(jiraDefectRejectionStatus); + fieldMappingDTO.setJiraDefectRejectionStatusKPI28(jiraDefectRejectionStatus); + fieldMappingDTO.setJiraDefectRejectionStatusKPI34(jiraDefectRejectionStatus); + fieldMappingDTO.setJiraDefectRejectionStatusKPI37(jiraDefectRejectionStatus); + fieldMappingDTO.setJiraDefectRejectionStatusKPI35(jiraDefectRejectionStatus); + fieldMappingDTO.setJiraDefectRejectionStatusKPI82(jiraDefectRejectionStatus); + fieldMappingDTO.setJiraDefectRejectionStatusKPI135(jiraDefectRejectionStatus); + fieldMappingDTO.setJiraDefectRejectionStatusKPI133(jiraDefectRejectionStatus); + fieldMappingDTO.setJiraDefectRejectionStatusRCAKPI36(jiraDefectRejectionStatus); + fieldMappingDTO.setJiraDefectRejectionStatusKPI14(jiraDefectRejectionStatus); + fieldMappingDTO.setJiraDefectRejectionStatusQAKPI111(jiraDefectRejectionStatus); + fieldMappingDTO.setJiraDefectRejectionStatusKPI152(jiraDefectRejectionStatus); + fieldMappingDTO.setJiraDefectRejectionStatusKPI151(jiraDefectRejectionStatus); + fieldMappingDTO.setJiraIssueTypeKPI35(jiraDefectSeepageIssueType); + fieldMappingDTO.setJiraDefectRemovalStatusKPI34(jiraDefectRemovalStatus); + fieldMappingDTO.setJiraDefectRemovalIssueTypeKPI34(jiraDefectRemovalIssueType); + fieldMappingDTO.setJiraIssueTypeKPI37(jiraDefectRejectionlIssueType); + fieldMappingDTO.setJiraSprintCapacityIssueTypeKpi46(jiraSprintCapacityIssueType); + fieldMappingDTO.setJiraDefectCountlIssueTypeKPI28(jiraDefectCountlIssueType); + fieldMappingDTO.setJiraDefectCountlIssueTypeKPI36(jiraDefectCountlIssueType); + fieldMappingDTO.setJiraIssueDeliverdStatusKPI138(jiraIssueDeliverdStatus); + fieldMappingDTO.setJiraIssueDeliverdStatusAVR(jiraIssueDeliverdStatus); + fieldMappingDTO.setJiraIssueDeliverdStatusKPI126(jiraIssueDeliverdStatus); + fieldMappingDTO.setJiraIssueDeliverdStatusKPI82(jiraIssueDeliverdStatus); + fieldMappingDTO.setJiraDorKPI3(Arrays.asList(jiraDor)); + fieldMappingDTO.setJiraIssueTypeKPI3(jiraIntakeToDorIssueType); + fieldMappingDTO.setStoryFirstStatusKPI3(storyFirstStatus); + fieldMappingDTO.setStoryFirstStatusKPI148(storyFirstStatus); + fieldMappingDTO.setJiraStoryIdentificationKpi40(jiraStoryIdentification); + fieldMappingDTO.setJiraStoryIdentificationKPI129(jiraStoryIdentification); + fieldMappingDTO.setJiraDefectClosedStatusKPI137(jiraDefectClosedStatus); + fieldMappingDTO.setJiraKPI82StoryIdentification(jiraFTPRStoryIdentification); + fieldMappingDTO.setJiraKPI135StoryIdentification(jiraFTPRStoryIdentification); + fieldMappingDTO.setJiraLiveStatusKPI3(Arrays.asList(jiraLiveStatus)); + fieldMappingDTO.setJiraLiveStatusLTK(jiraLiveStatus); + fieldMappingDTO.setJiraLiveStatusNOPK(jiraLiveStatus); + fieldMappingDTO.setJiraLiveStatusNOSK(jiraLiveStatus); + fieldMappingDTO.setJiraLiveStatusNORK(jiraLiveStatus); + fieldMappingDTO.setJiraLiveStatusOTA(jiraLiveStatus); + fieldMappingDTO.setJiraLiveStatusKPI127(jiraLiveStatus); + fieldMappingDTO.setJiraLiveStatusKPI152(jiraLiveStatus); + fieldMappingDTO.setJiraLiveStatusKPI151(jiraLiveStatus); + fieldMappingDTO.setExcludeRCAFromKPI82(excludeRCAFromFTPR); + fieldMappingDTO.setExcludeRCAFromKPI135(excludeRCAFromFTPR); + fieldMappingDTO.setExcludeRCAFromKPI14(excludeRCAFromFTPR); + fieldMappingDTO.setExcludeRCAFromQAKPI111(excludeRCAFromFTPR); + fieldMappingDTO.setExcludeRCAFromKPI133(excludeRCAFromFTPR); + fieldMappingDTO.setResolutionTypeForRejectionAVR(resolutionTypeForRejection); + fieldMappingDTO.setResolutionTypeForRejectionKPI28(resolutionTypeForRejection); + fieldMappingDTO.setResolutionTypeForRejectionKPI34(resolutionTypeForRejection); + fieldMappingDTO.setResolutionTypeForRejectionKPI37(resolutionTypeForRejection); + fieldMappingDTO.setResolutionTypeForRejectionKPI35(resolutionTypeForRejection); + fieldMappingDTO.setResolutionTypeForRejectionKPI82(resolutionTypeForRejection); + fieldMappingDTO.setResolutionTypeForRejectionKPI135(resolutionTypeForRejection); + fieldMappingDTO.setResolutionTypeForRejectionKPI133(resolutionTypeForRejection); + fieldMappingDTO.setResolutionTypeForRejectionRCAKPI36(resolutionTypeForRejection); + fieldMappingDTO.setResolutionTypeForRejectionKPI14(resolutionTypeForRejection); + fieldMappingDTO.setResolutionTypeForRejectionQAKPI111(resolutionTypeForRejection); + fieldMappingDTO.setJiraQAKPI111IssueType(jiraQADefectDensityIssueType); + fieldMappingDTO.setJiraStatusForQaKPI135(jiraStatusForQa); + fieldMappingDTO.setJiraStatusForQaKPI82(jiraStatusForQa); + fieldMappingDTO.setJiraStatusForQaKPI148(jiraStatusForQa); + fieldMappingDTO.setJiraStatusForInProgressKPI122(jiraStatusForInProgress); + fieldMappingDTO.setJiraStatusForInProgressKPI145(jiraStatusForInProgress); + fieldMappingDTO.setJiraStatusForInProgressKPI125(jiraStatusForInProgress); + fieldMappingDTO.setJiraStatusForInProgressKPI128(jiraStatusForInProgress); + fieldMappingDTO.setJiraStatusForInProgressKPI123(jiraStatusForInProgress); + fieldMappingDTO.setJiraStatusForInProgressKPI119(jiraStatusForInProgress); + fieldMappingDTO.setJiraStatusForInProgressKPI148(jiraStatusForInProgress); + fieldMappingDTO.setIssueStatusExcluMissingWorkKPI124(issueStatusExcluMissingWork); + fieldMappingDTO.setJiraDevDoneStatusKPI119(jiraDevDoneStatus); + fieldMappingDTO.setJiraDevDoneStatusKPI145(jiraDevDoneStatus); + fieldMappingDTO.setJiraDevDoneStatusKPI128(jiraDevDoneStatus); + fieldMappingDTO.setJiraWaitStatusKPI131(jiraWaitStatus); + fieldMappingDTO.setJiraBlockedStatusKPI131(jiraBlockedStatus); + fieldMappingDTO.setJiraIncludeBlockedStatusKPI131(jiraIncludeBlockedStatus); + fieldMappingDTO.setJiraFtprRejectStatusKPI135(jiraFtprRejectStatus); + fieldMappingDTO.setJiraFtprRejectStatusKPI82(jiraFtprRejectStatus); + fieldMappingDTO.setJiraIterationCompletionStatusKPI135(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI122(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI75(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI145(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI140(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI132(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI136(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI140(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKpi72(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKpi39(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKpi5(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI124(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI123(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI125(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI120(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI128(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI134(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI133(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI119(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI131(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationCompletionStatusKPI138(jiraIterationCompletionStatusCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKPI122(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKPI138(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKPI131(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKPI128(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKPI134(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKPI145(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKpi72(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKPI119(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKpi5(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKPI75(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKPI123(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKPI125(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKPI120(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraIterationIssuetypeKPI124(jiraIterationCompletionTypeCustomField); + fieldMappingDTO.setJiraDefectDroppedStatusKPI127(jiraDefectDroppedStatus); + fieldMappingDTO.setJiraAcceptedInRefinementKPI139(jiraAcceptedInRefinement); + fieldMappingDTO.setJiraReadyForRefinementKPI139(jiraReadyForRefinement); + fieldMappingDTO.setJiraRejectedInRefinementKPI139(jiraRejectedInRefinement); + fieldMappingDTO.setReadyForDevelopmentStatusKPI138(Arrays.asList(readyForDevelopmentStatus)); + fieldMappingDTO.setCreatedDate(LocalDateTime.now()); + } + } +} diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/KPIExcelUtility.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/KPIExcelUtility.java index e53f968da3..d46b672348 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/KPIExcelUtility.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/KPIExcelUtility.java @@ -52,6 +52,7 @@ import com.publicissapient.kpidashboard.apis.model.KPIExcelData; import com.publicissapient.kpidashboard.apis.model.LeadTimeChangeData; import com.publicissapient.kpidashboard.common.constant.CommonConstant; +import com.publicissapient.kpidashboard.common.model.application.AdditionalFilterValue; import com.publicissapient.kpidashboard.common.model.application.FieldMapping; import com.publicissapient.kpidashboard.common.model.application.LeadTimeData; import com.publicissapient.kpidashboard.common.model.application.ProjectVersion; @@ -119,6 +120,7 @@ public static void populateDirExcelData(String sprint, List storyIds, Li Map storyId = new HashMap<>(); storyId.put(story, checkEmptyURL(jiraIssue)); excelData.setStoryId(storyId); + setSquads(excelData, jiraIssue); } } kpiExcelData.add(excelData); @@ -139,6 +141,7 @@ public static void populateDefectDensityExcelData(String sprint, List st if (MapUtils.isNotEmpty(issueData)) { JiraIssue jiraIssue = issueData.get(story); if (null != jiraIssue) { + setSquads(excelData, jiraIssue); excelData.setIssueDesc(checkEmptyName(jiraIssue)); Map storyId = new HashMap<>(); storyId.put(story, checkEmptyURL(jiraIssue)); @@ -173,6 +176,7 @@ public static void populateFTPRExcelData(String sprint, List storyIds, L Map storyId = new HashMap<>(); storyId.put(story, checkEmptyURL(jiraIssue)); excelData.setStoryId(storyId); + setSquads(excelData, jiraIssue); } } excelData.setFirstTimePass(collect.contains(story) ? Constant.EXCEL_YES : Constant.EMPTY_STRING); @@ -210,6 +214,7 @@ public static void populateDefectRelatedExcelData(String sprint, Map defectIdDetails = new HashMap<>(); defectIdDetails.put(defectId, checkEmptyURL(jiraIssue)); excelData.setDefectId(defectIdDetails); + setSquads(excelData, jiraIssue); if (kpiId.equalsIgnoreCase(KPICode.DEFECT_REMOVAL_EFFICIENCY.getKpiId())) { excelData.setRemovedDefect(present); } @@ -241,6 +246,7 @@ public static void populateDefectRelatedExcelData(String sprint, List KPIExcelData excelData = new KPIExcelData(); excelData.setSprintName(sprint); excelData.setIssueDesc(checkEmptyName(jiraIssue)); + setSquads(excelData, jiraIssue); Map defectIdDetails = new HashMap<>(); defectIdDetails.put(jiraIssue.getNumber(), checkEmptyURL(jiraIssue)); excelData.setDefectId(defectIdDetails); @@ -270,6 +276,7 @@ public static void populateDefectRCAandStatusRelatedExcelData(String sprint, Lis excelData.setIssueStatus(jiraIssue.getStatus()); excelData.setIssueType(jiraIssue.getTypeName()); populateAssignee(jiraIssue, excelData); + setSquads(excelData, jiraIssue); if (null != jiraIssue.getStoryPoints() && StringUtils.isNotEmpty(fieldMapping.getEstimationCriteria()) && fieldMapping.getEstimationCriteria().equalsIgnoreCase(CommonConstant.STORY_POINT)) { excelData.setStoryPoint(String.valueOf(jiraIssue.getStoryPoints())); @@ -291,32 +298,30 @@ public static void populateDefectRCAandStatusRelatedExcelData(String sprint, Lis * TO GET Constant.EXCEL_YES/"N" from complete list of defects if defect is * present in conditional list then Constant.EXCEL_YES else * Constant.EMPTY_STRING kpi specific - * - * @param sprint + * @param sprint * @param totalStoriesMap - * @param closedConditionStories * @param createdConditionStories + * @param closedIssuesWithStatus * @param kpiExcelData */ - public static void populateCreatedVsResolvedExcelData(String sprint, Map totalStoriesMap, - List closedConditionStories, List createdConditionStories, - List kpiExcelData) { + public static void populateCreatedVsResolvedExcelData(String sprint, Map totalStoriesMap, List createdConditionStories, + Map closedIssuesWithStatus, List kpiExcelData) { if (MapUtils.isNotEmpty(totalStoriesMap)) { - List closedConditionalList = closedConditionStories.stream().map(JiraIssue::getNumber) - .collect(Collectors.toList()); List createdConditionalList = createdConditionStories.stream().map(JiraIssue::getNumber) .collect(Collectors.toList()); totalStoriesMap.forEach((storyId, jiraIssue) -> { - String present = closedConditionalList.contains(storyId) ? Constant.EXCEL_YES : Constant.EMPTY_STRING; + String resolvedStatus = closedIssuesWithStatus.containsKey(storyId) ? closedIssuesWithStatus.get(storyId) : Constant.EMPTY_STRING; String createdAfterSprint = createdConditionalList.contains(storyId) ? Constant.EXCEL_YES : Constant.EMPTY_STRING; KPIExcelData excelData = new KPIExcelData(); excelData.setSprintName(sprint); + setSquads(excelData, jiraIssue); excelData.setIssueDesc(checkEmptyName(jiraIssue)); Map storyDetails = new HashMap<>(); storyDetails.put(storyId, checkEmptyURL(jiraIssue)); excelData.setCreatedDefectId(storyDetails); - excelData.setResolvedTickets(present); + excelData.setResolvedStatus(resolvedStatus); + excelData.setDefectAddedAfterSprintStart(createdAfterSprint); kpiExcelData.add(excelData); @@ -542,13 +547,14 @@ public static void populateSprintVelocity(String sprint, Map excelData.setStoryPoint(roundingOff(totalOriginalEstimate / fieldMapping.getStoryPointToHourMapping()) + "/" + roundingOff(totalOriginalEstimate) + " hrs"); } + setSquads(excelData,jiraIssue); kpiExcelData.add(excelData); }); } } public static void populateSprintPredictability(String sprint, Set issueDetailsSet, - List kpiExcelData, FieldMapping fieldMapping) { + List kpiExcelData, FieldMapping fieldMapping, Map jiraIssueMap) { if (CollectionUtils.isNotEmpty(issueDetailsSet)) { for (IssueDetails issueDetails : issueDetailsSet) { KPIExcelData excelData = new KPIExcelData(); @@ -557,6 +563,7 @@ public static void populateSprintPredictability(String sprint, Set storyDetails.put(issueDetails.getSprintIssue().getNumber(), checkEmptyURL(issueDetails)); excelData.setStoryId(storyDetails); excelData.setIssueDesc(checkEmptyName(issueDetails)); + setSquads(excelData,jiraIssueMap.get(issueDetails.getSprintIssue().getNumber())); if (StringUtils.isNotEmpty(fieldMapping.getEstimationCriteria()) && fieldMapping.getEstimationCriteria().equalsIgnoreCase(CommonConstant.STORY_POINT)) { excelData.setStoryPoint( @@ -682,6 +689,7 @@ public static void populateCommittmentReliability(String sprint, Map epic excelData.setEpicID(epicLink); excelData.setEpicName(checkEmptyName(epic)); excelData.setCostOfDelay(epic.getCostOfDelay()); + setSquads(excelData,epic); String month = Constant.EMPTY_STRING; String epicEndDate = Constant.EMPTY_STRING; if (epic.getChangeDate() != null) { @@ -743,6 +752,7 @@ public static void populatePIPredictabilityExcelData(String projectName, List storyDetails.put(sprintIssue.getNumber(), checkEmptyURL(sprintIssue)); excelData.setStoryId(storyDetails); excelData.setIssueDesc(checkEmptyName(sprintIssue)); + setSquads(excelData, sprintIssue); kpiExcelData.add(excelData); }); } @@ -950,9 +961,7 @@ public static void populateMeanTimeMergeExcelData(String projectName, List m : repoWiseMap.entrySet()) { KPIExcelData excelData = new KPIExcelData(); excelData.setProject(projectName); - Map repoUrl = new HashMap<>(); - repoUrl.put(repoList.get(i), repoList.get(i)); - excelData.setRepositoryURL(repoUrl); + excelData.setRepo(repoList.get(i)); excelData.setBranch(branchList.get(i)); excelData.setDaysWeeks(m.getKey()); excelData.setMeanTimetoMerge(m.getValue().toString()); @@ -973,9 +982,7 @@ public static void populatePickupTimeExcelData(String projectName, List m : repoWiseMap.entrySet()) { KPIExcelData excelData = new KPIExcelData(); excelData.setProject(projectName); - Map repoUrl = new HashMap<>(); - repoUrl.put(repoList.get(i), repoList.get(i)); - excelData.setRepositoryURL(repoUrl); + excelData.setRepo(repoList.get(i)); excelData.setBranch(branchList.get(i)); excelData.setWeeks(m.getKey()); excelData.setPickupTime(m.getValue().toString()); @@ -996,9 +1003,7 @@ public static void populatePRSizeExcelData(String projectName, List m : repoWiseMap.entrySet()) { KPIExcelData excelData = new KPIExcelData(); excelData.setProject(projectName); - Map repoUrl = new HashMap<>(); - repoUrl.put(repoList.get(i), repoList.get(i)); - excelData.setRepositoryURL(repoUrl); + excelData.setRepo(repoList.get(i)); excelData.setBranch(branchList.get(i)); excelData.setWeeks(m.getKey()); excelData.setPrSize(m.getValue().toString()); @@ -1025,9 +1030,7 @@ public static void populateCodeCommit(String projectName, List Long mergeHours = repoWiseMergeMap.get(date); KPIExcelData excelData = new KPIExcelData(); excelData.setProjectName(projectName); - Map repoUrl = new HashMap<>(); - repoUrl.put(repoList.get(i), repoList.get(i)); - excelData.setRepositoryURL(repoUrl); + excelData.setRepo(repoList.get(i)); excelData.setBranch(branchList.get(i)); excelData.setDaysWeeks(date); excelData.setNumberOfCommit(commitHours.toString()); @@ -1218,18 +1221,17 @@ public static void populateTicketVelocityExcelData(List kpiExcelData) { if (CollectionUtils.isNotEmpty(velocityList)) { velocityList.forEach(kanbanIssueCustomHistory -> { - KPIExcelData excelData = new KPIExcelData(); - excelData.setProjectName(projectName); - excelData.setDayWeekMonth(date); - excelData.setIssueType(kanbanIssueCustomHistory.getStoryType()); - excelData.setSizeInStoryPoints(kanbanIssueCustomHistory.getEstimate()); if (kanbanIssueCustomHistory.getStoryID() != null) { + KPIExcelData excelData = new KPIExcelData(); + excelData.setProjectName(projectName); + excelData.setDayWeekMonth(date); + excelData.setIssueType(kanbanIssueCustomHistory.getStoryType()); + excelData.setSizeInStoryPoints(kanbanIssueCustomHistory.getEstimate()); Map storyId = new HashMap<>(); storyId.put(kanbanIssueCustomHistory.getStoryID(), checkEmptyURL(kanbanIssueCustomHistory)); excelData.setTicketIssue(storyId); + kpiExcelData.add(excelData); } - kpiExcelData.add(excelData); - }); } @@ -1263,9 +1265,7 @@ public static void populateCodeCommitKanbanExcelData(String projectName, List repoUrl = new HashMap<>(); - repoUrl.put(repoList.get(i), repoList.get(i)); - excelData.setRepositoryURL(repoUrl); + excelData.setRepo(repoList.get(i)); excelData.setBranch(branchList.get(i)); excelData.setDays(date); excelData.setNumberOfCommit(repoWiseCommitList.get(i).get(date).toString()); @@ -1293,7 +1293,7 @@ public static void populateTeamCapacityKanbanExcelData(Double capacity, List overAllModa jiraIssueModalObject.setDevDueDate(jiraIssue.getDevDueDate().split("T")[0]); else jiraIssueModalObject.setDevDueDate(Constant.DASH); - modalValues.add(jiraIssueModalObject); - overAllModalValues.add(jiraIssueModalObject); + + if (CollectionUtils.isNotEmpty(fieldMapping.getAdditionalFilterConfig())) { + if (CollectionUtils.isNotEmpty(jiraIssue.getAdditionalFilters())) { + jiraIssueModalObject + .setSquads(jiraIssue + .getAdditionalFilters().stream().flatMap(additionalFilter -> additionalFilter + .getFilterValues().stream().map(AdditionalFilterValue::getValue)) + .collect(Collectors.toList())); + + } else { + jiraIssueModalObject.setSquads(Arrays.asList(Constant.DASH)); + } } + modalValues.add(jiraIssueModalObject); + overAllModalValues.add(jiraIssueModalObject); + } + /** * This Method is used to populate Excel Data for Rejection Refinement KPI * @@ -1656,6 +1670,7 @@ public static void populateStoryChunk(String sprintName, Map excelData.setIssueType(jiraIssue.getTypeName()); excelData.setIssueDesc(checkEmptyName(jiraIssue)); excelData.setIssueStatus(jiraIssue.getStatus()); + setSquads(excelData, jiraIssue); if (addedIssueDateMap.containsKey(jiraIssue.getNumber())) { excelData.setScopeChange(CommonConstant.ADDED); excelData.setScopeChangeDate(addedIssueDateMap.get(jiraIssue.getNumber())); @@ -1762,4 +1777,18 @@ public static void populateEpicProgessExcelData(Map epicWiseIssu } }); } + + private static void setSquads(KPIExcelData excelData, JiraIssue jiraIssue) { + if (CollectionUtils.isNotEmpty(jiraIssue.getAdditionalFilters())) { + excelData + .setSquads(jiraIssue + .getAdditionalFilters().stream().flatMap(additionalFilter -> additionalFilter + .getFilterValues().stream().map(AdditionalFilterValue::getValue)) + .collect(Collectors.toList())); + + } else { + excelData.setSquads(Arrays.asList(Constant.DASH)); + } + } + } \ No newline at end of file diff --git a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/KpiDataHelper.java b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/KpiDataHelper.java index 0d6281e3d4..e228453929 100644 --- a/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/KpiDataHelper.java +++ b/customapi/src/main/java/com/publicissapient/kpidashboard/apis/util/KpiDataHelper.java @@ -124,6 +124,43 @@ public static String createAdditionalFilterMap(KpiRequest kpiRequest, Map mapOfFilters, + FilterHelperService filterHelperService) { + + // Retrieve the additional filter hierarchy levels and convert keys to uppercase + // for case-insensitive matching + Map addFilterCat = filterHelperService.getAdditionalFilterHierarchyLevel(); + Map addFilterCategory = addFilterCat.entrySet().stream() + .collect(Collectors.toMap(entry -> entry.getKey().toUpperCase(), Map.Entry::getValue)); + + // Check if the selected map in KPI request is not empty + if (MapUtils.isNotEmpty(kpiRequest.getSelectedMap())) { + // Iterate through the selected filters in the KPI request + for (Map.Entry> entry : kpiRequest.getSelectedMap().entrySet()) { + // Check if the filter has a non-empty value list and exists in the additional + // filter categories + if (CollectionUtils.isNotEmpty(entry.getValue()) + && addFilterCategory.get(entry.getKey().toUpperCase()) != null) { + // Add filter criteria to the map of filters + mapOfFilters.put("additionalFilterCapacityList.filterId", Arrays.asList(entry.getKey())); + mapOfFilters.put("additionalFilterCapacityList.nodeCapacityList.additionalFilterId", + entry.getValue()); + } + } + } + } + /** * Creates subcategory wise map. * @@ -709,23 +746,11 @@ public static Set changeSprintDetails(SprintDetails sprintDetail, S return completedIssues; } - private static Set getCombinationalCompletedSet(Set typeWiseIssues, - Set statusWiseIssues) { - Set newCompletedSet; - if (CollectionUtils.isNotEmpty(typeWiseIssues) && CollectionUtils.isNotEmpty(statusWiseIssues)) { - newCompletedSet = new HashSet<>(CollectionUtils.intersection(typeWiseIssues, statusWiseIssues)); - } else if (CollectionUtils.isNotEmpty(typeWiseIssues)) { - newCompletedSet = typeWiseIssues; - } else { - newCompletedSet = statusWiseIssues; - } - return newCompletedSet; - } - private static Set filteringByFieldMapping(SprintDetails dbSprintDetail, List fieldMapingCompletionType, List fieldMappingCompletionStatus) { Set typeWiseIssues = new HashSet<>(); Set statusWiseIssues = new HashSet<>(); + Set newCompletedSet = null; if (CollectionUtils.isNotEmpty(fieldMappingCompletionStatus) && CollectionUtils.isNotEmpty(fieldMapingCompletionType)) { statusWiseIssues.addAll(dbSprintDetail.getCompletedIssues().stream() @@ -740,6 +765,7 @@ private static Set filteringByFieldMapping(SprintDetails dbSprintDe typeWiseIssues.addAll(dbSprintDetail.getNotCompletedIssues().stream() .filter(issue -> fieldMapingCompletionType.contains(issue.getTypeName())) .collect(Collectors.toSet())); + newCompletedSet = new HashSet<>(CollectionUtils.intersection(typeWiseIssues, statusWiseIssues)); } else if (CollectionUtils.isNotEmpty(fieldMappingCompletionStatus)) { statusWiseIssues.addAll(dbSprintDetail.getCompletedIssues().stream() .filter(issue -> fieldMappingCompletionStatus.contains(issue.getStatus())) @@ -747,12 +773,14 @@ private static Set filteringByFieldMapping(SprintDetails dbSprintDe statusWiseIssues.addAll(dbSprintDetail.getNotCompletedIssues().stream() .filter(issue -> fieldMappingCompletionStatus.contains(issue.getStatus())) .collect(Collectors.toSet())); + newCompletedSet = statusWiseIssues; } else if (CollectionUtils.isNotEmpty(fieldMapingCompletionType)) { typeWiseIssues.addAll(dbSprintDetail.getCompletedIssues().stream() .filter(issue -> fieldMapingCompletionType.contains(issue.getTypeName())) .collect(Collectors.toSet())); + newCompletedSet = typeWiseIssues; } - return getCombinationalCompletedSet(typeWiseIssues, statusWiseIssues); + return newCompletedSet; } /** @@ -826,8 +854,7 @@ public static double calWeekDaysExcludingWeekends(DateTime startDateTime, DateTi String formattedValue = df.format(leadTimeChangeInFullDay); double leadTimeChangeIncluded = Double.parseDouble(formattedValue); int weekendsCount = countSaturdaysAndSundays(startDateTime, endDateTime); - double leadTimeChangeExcluded = leadTimeChangeIncluded - weekendsCount; - return leadTimeChangeExcluded; + return leadTimeChangeIncluded - weekendsCount; } } return 0.0d; @@ -870,11 +897,12 @@ public static int countSaturdaysAndSundays(DateTime startDateTime, DateTime endD * @param subTaskHistory * @param sprintDetail * @param fieldMappingDoneStatus - * @return + * @param issueWiseDeliveredStatus + * @return list of jiraIssues */ public static List getCompletedSubTasksByHistory(List totalSubTask, List subTaskHistory, SprintDetails sprintDetail, - List fieldMappingDoneStatus) { + List fieldMappingDoneStatus, Map issueWiseDeliveredStatus) { List resolvedSubtaskForSprint = new ArrayList<>(); LocalDateTime sprintEndDateTime = sprintDetail.getCompleteDate() != null ? LocalDateTime.parse(sprintDetail.getCompleteDate().split("\\.")[0], DATE_TIME_FORMATTER) @@ -892,8 +920,11 @@ public static List getCompletedSubTasksByHistory(List tota sprintStartDateTime, sprintEndDateTime)) .reduce((a, b) -> b); if (issueSprint.isPresent() - && fieldMappingDoneStatus.contains(issueSprint.get().getChangedTo().toLowerCase())) + && fieldMappingDoneStatus.contains(issueSprint.get().getChangedTo())) { + issueWiseDeliveredStatus.putIfAbsent(jiraIssue.getNumber(), + issueSprint.get().getChangedTo()); resolvedSubtaskForSprint.add(jiraIssue); + } }); return resolvedSubtaskForSprint; } @@ -922,7 +953,7 @@ public static List getTotalSprintSubTasks(List allSubTasks issueCustomHistory -> issueCustomHistory.getStoryID().equalsIgnoreCase(jiraIssue.getNumber())) .findFirst().orElse(new JiraIssueCustomHistory()); Optional jiraHistoryChangeLog = jiraIssueCustomHistory.getStatusUpdationLog().stream() - .filter(changeLog -> fieldMappingDoneStatus.contains(changeLog.getChangedTo().toLowerCase()) + .filter(changeLog -> fieldMappingDoneStatus.contains(changeLog.getChangedTo()) && changeLog.getUpdatedOn().isAfter(sprintStartDate)) .findFirst(); if (jiraHistoryChangeLog.isPresent() && sprintEndDate diff --git a/customapi/src/main/resources/application.properties b/customapi/src/main/resources/application.properties index 9d016744a8..a5f422050f 100644 --- a/customapi/src/main/resources/application.properties +++ b/customapi/src/main/resources/application.properties @@ -297,6 +297,8 @@ repoToolURL=http://debbbie-django:8000/api #only two kpis i.e. mean time to merge and check-ins and merge request on developer tab if disabled #two more kpis along with the existing one on developer tab isRepoToolEnable= +# This flag is only for GS client for enabling multi-input field at git lab tool config screen +isGitlabFieldEnable=true #repo tool urls repoToolPRSizeUrl=/metric/pr-size-bulk/ diff --git a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/appsetting/service/ConfigDetailsServiceImplTest.java b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/appsetting/service/ConfigDetailsServiceImplTest.java index af379efde4..fc60542e14 100644 --- a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/appsetting/service/ConfigDetailsServiceImplTest.java +++ b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/appsetting/service/ConfigDetailsServiceImplTest.java @@ -85,7 +85,7 @@ public void testConfigDetails() throws Exception { Mockito.when(customApiConfig.getHierarchySelectionCount()).thenReturn(3); Mockito.when(customApiConfig.getDateRangeFilterCounts()).thenReturn(Arrays.asList(5, 10, 15)); Mockito.when(customApiConfig.getDateRangeFilterTypes()).thenReturn(Arrays.asList("Days", "Weeks", "Months")); - + Mockito.when(customApiConfig.getIsGitlabFieldEnable()).thenReturn(false); ConfigDetails configDetail = getConfigDetailsObject(); ConfigDetails configDetails = configDetailsServiceImpl.getConfigDetails(); Assert.assertEquals(configDetail.getKpiWiseAggregationType(), configDetails.getKpiWiseAggregationType()); @@ -97,6 +97,7 @@ private ConfigDetails getConfigDetailsObject() { configDetails.setKpiWiseAggregationType(kpiWiseAggregation); configDetails.setPercentile(90d); configDetails.setDateRangeFilter(dateRangeFilter); + configDetails.setGitlabToolFieldFlag(false); return configDetails; } diff --git a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/capacity/service/CapacityMasterServiceImplTest.java b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/capacity/service/CapacityMasterServiceImplTest.java index b9eecde232..a6f002763c 100644 --- a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/capacity/service/CapacityMasterServiceImplTest.java +++ b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/capacity/service/CapacityMasterServiceImplTest.java @@ -1,3 +1,22 @@ +/******************************************************************************* + * 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.apis.capacity.service; import static org.junit.Assert.assertEquals; @@ -7,6 +26,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; +import java.lang.reflect.Method; import java.time.DayOfWeek; import java.time.LocalDate; import java.util.ArrayList; @@ -15,6 +35,13 @@ import java.util.Map; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.data.AdditionalFilterCategoryFactory; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; +import com.publicissapient.kpidashboard.common.model.application.AdditionalFilterCapacity; +import com.publicissapient.kpidashboard.common.model.application.AdditionalFilterCategory; +import com.publicissapient.kpidashboard.common.model.application.LeafNodeCapacity; +import com.publicissapient.kpidashboard.common.model.jira.AssigneeDetails; +import com.publicissapient.kpidashboard.common.repository.jira.AssigneeDetailsRepository; import org.apache.commons.collections4.CollectionUtils; import org.bson.types.ObjectId; import org.junit.Before; @@ -71,6 +98,8 @@ public class CapacityMasterServiceImplTest { @Mock private CacheService cacheService; @Mock + private FilterHelperService filterHelperService; + @Mock private CustomApiConfig customApiConfig; @Mock private ProjectBasicConfigService projectBasicConfigService; @@ -78,6 +107,8 @@ public class CapacityMasterServiceImplTest { private SprintDetailsService sprintDetailsService; @Mock private HappinessKpiDataRepository happinessKpiDataRepository; + @Mock + private AssigneeDetailsRepository assigneeDetailsRepository; private List sprintDetailsList; /** @@ -96,6 +127,8 @@ public void setUp() { scrumCapacityMaster.setSprintNodeId("38296_Scrum Project_6335363749794a18e8a4479b"); scrumCapacityMaster.setKanban(false); scrumCapacityMaster.setCapacity(500.0); + scrumCapacityMaster.setAdditionalFilterCapacityList(createAdditionalFilter()); + scrumCapacityMaster.setBasicProjectConfigId(new ObjectId("65eec0156f35b0294eb765f6")); kanbanCapacity = new CapacityMaster(); kanbanCapacity.setProjectName("Kanban Project"); @@ -104,11 +137,20 @@ public void setUp() { kanbanCapacity.setEndDate("2020-02-02"); kanbanCapacity.setKanban(true); kanbanCapacity.setCapacity(500.0); + kanbanCapacity.setBasicProjectConfigId(new ObjectId("65eec0156f35b0294eb765f6")); + kanbanDbData = new KanbanCapacity(); kanbanDbData.setProjectName("health project"); kanbanDbData.setProjectId("Kanban Project_6335368249794a18e8a4479f"); kanbanDbData.setCapacity(200.0); + + scrumCapacityAssigneeMaster.setBasicProjectConfigId(new ObjectId("65eec0156f35b0294eb765f6")); + + when(capacityKpiDataRepository.findAll()).thenReturn(capacityKpiDataList); + when(assigneeDetailsRepository + .findByBasicProjectConfigId(anyString())) + .thenReturn(new AssigneeDetails()); } private void setUpCapacityAssignee() { @@ -138,7 +180,32 @@ private void setUpCapacityAssignee() { kanbanCapacityAssignee.setCapacity(500.0); kanbanCapacityAssignee.setAssigneeDetails(true); kanbanCapacityAssignee.setAssigneeCapacity(assigneeCapacityList); + kanbanCapacityAssignee.setAdditionalFilterCapacityList(createAdditionalFilter()); + + kanbanCapacityAsigneeList.forEach(a -> a.setAdditionalFilterCapacityList(createAdditionalFilter())); + AdditionalFilterCategoryFactory additionalFilterCategoryFactory = AdditionalFilterCategoryFactory.newInstance(); + List additionalFilterCategoryList = additionalFilterCategoryFactory + .getAdditionalFilterCategoryList(); + Map additonalFilterMap = additionalFilterCategoryList.stream() + .collect(Collectors.toMap(AdditionalFilterCategory::getFilterCategoryId, x -> x)); + when(filterHelperService.getAdditionalFilterHierarchyLevel()).thenReturn(additonalFilterMap); + + } + + private List createAdditionalFilter() { + + List additionalFilterCapacityList= new ArrayList<>(); + AdditionalFilterCapacity additionalFilterCapacity= new AdditionalFilterCapacity(); + additionalFilterCapacity.setFilterId("sqd"); + List leafNodeCapacityList= new ArrayList<>(); + leafNodeCapacityList.add(new LeafNodeCapacity("filterId1",50D)); + leafNodeCapacityList.add(new LeafNodeCapacity("filterId2",40D)); + leafNodeCapacityList.add(new LeafNodeCapacity("filterId3",60D)); + additionalFilterCapacity.setNodeCapacityList(leafNodeCapacityList); + additionalFilterCapacityList.add(additionalFilterCapacity); + + return additionalFilterCapacityList; } private AssigneeCapacity createAssigneeData(String userId, String userName, double planned, double leaves) { @@ -170,6 +237,7 @@ public void testProcessCapacityData_scrum_success() { public void testProcessCapacityData_scrum_failure() { Map map = new HashMap<>(); map.put("projectId", scrumCapacityMaster.getProjectNodeId()); + when(assigneeDetailsRepository.findByBasicProjectConfigId(anyString())).thenReturn(new AssigneeDetails()); assertNotNull(capacityMasterServiceImpl.processCapacityData(kanbanCapacity)); } @@ -347,6 +415,22 @@ public void testProcessAssigneeCapacityDataUpdate_success() { public void testProcessAssigneeCapacityData_kanban_success() { when(kanbanCapacityRepository.findByFilterMapAndDate(Mockito.any(), Mockito.any())) .thenReturn(Lists.newArrayList(kanbanDbData)); + kanbanCapacityAssignee.setBasicProjectConfigId(new ObjectId("65eec0156f35b0294eb765f6")); assertNotNull(capacityMasterServiceImpl.processCapacityData(kanbanCapacityAssignee)); } + + @Test + public void testSaveAdditionalFilterCapacity_EmptyList() throws Exception { + //List additionalFilterCapacityList= new ArrayList<>(); + //when(scrumCapacityMaster.getAdditionalFilterCapacityList()).thenReturn(additionalFilterCapacityList); + + Method method = CapacityMasterServiceImpl.class.getDeclaredMethod("saveAdditionalFilterCapacity", CapacityMaster.class); + method.setAccessible(true); + + @SuppressWarnings("unchecked") + List result = (List) method.invoke(capacityMasterServiceImpl, scrumCapacityMaster); + + assertNotNull(String.valueOf(result), "The result should be null when the input list is empty."); + } + } diff --git a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/common/service/KpiHelperServiceTest.java b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/common/service/KpiHelperServiceTest.java index f81dec36b9..8557fd787c 100644 --- a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/common/service/KpiHelperServiceTest.java +++ b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/common/service/KpiHelperServiceTest.java @@ -31,9 +31,6 @@ import java.util.Map; import java.util.stream.Collectors; -import com.publicissapient.kpidashboard.common.model.application.FieldMappingStructure; -import com.publicissapient.kpidashboard.common.model.application.ProjectBasicConfig; -import com.publicissapient.kpidashboard.common.model.application.ProjectToolConfig; import org.bson.types.ObjectId; import org.joda.time.DateTime; import org.junit.After; @@ -61,7 +58,6 @@ import com.publicissapient.kpidashboard.apis.enums.KPICode; import com.publicissapient.kpidashboard.apis.errors.ApplicationException; import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; -import com.publicissapient.kpidashboard.apis.jira.service.JiraServiceR; import com.publicissapient.kpidashboard.apis.model.AccountHierarchyData; import com.publicissapient.kpidashboard.apis.model.AccountHierarchyDataKanban; import com.publicissapient.kpidashboard.apis.model.KpiRequest; @@ -70,7 +66,9 @@ import com.publicissapient.kpidashboard.apis.model.TreeAggregatorDetail; import com.publicissapient.kpidashboard.apis.util.KPIHelperUtil; import com.publicissapient.kpidashboard.common.model.application.FieldMapping; +import com.publicissapient.kpidashboard.common.model.application.FieldMappingStructure; import com.publicissapient.kpidashboard.common.model.application.KpiMaster; +import com.publicissapient.kpidashboard.common.model.application.ProjectToolConfig; import com.publicissapient.kpidashboard.common.model.excel.CapacityKpiData; import com.publicissapient.kpidashboard.common.model.jira.JiraHistoryChangeLog; import com.publicissapient.kpidashboard.common.model.jira.JiraIssue; @@ -78,7 +76,6 @@ import com.publicissapient.kpidashboard.common.model.jira.KanbanIssueCustomHistory; import com.publicissapient.kpidashboard.common.model.jira.SprintDetails; import com.publicissapient.kpidashboard.common.model.jira.SprintWiseStory; -import com.publicissapient.kpidashboard.common.repository.application.KpiMasterRepository; import com.publicissapient.kpidashboard.common.repository.excel.CapacityKpiDataRepository; import com.publicissapient.kpidashboard.common.repository.excel.KanbanCapacityRepository; import com.publicissapient.kpidashboard.common.repository.jira.JiraIssueCustomHistoryRepository; @@ -86,7 +83,6 @@ import com.publicissapient.kpidashboard.common.repository.jira.KanbanJiraIssueHistoryRepository; import com.publicissapient.kpidashboard.common.repository.jira.SprintRepository; import com.publicissapient.kpidashboard.common.repository.kpivideolink.KPIVideoLinkRepository; -import org.springframework.beans.factory.annotation.Autowired; @RunWith(MockitoJUnitRunner.class) public class KpiHelperServiceTest { @@ -107,7 +103,6 @@ public class KpiHelperServiceTest { @Mock private ConfigHelperService configHelperService; - @Mock private CapacityKpiDataRepository capacityKpiDataRepository; @InjectMocks @@ -130,7 +125,7 @@ public class KpiHelperServiceTest { private KpiRequestFactory kanbanKpiRequestFactory; @Mock - private FieldMappingStructure fieldMappingStructure= new FieldMappingStructure(); + private FieldMappingStructure fieldMappingStructure = new FieldMappingStructure(); private List fieldMappingStructureList = new ArrayList<>(); @@ -164,8 +159,8 @@ public void setup() { ProjectToolConfig projectConfig = new ProjectToolConfig(); projectConfig.setId(new ObjectId("6335363749794a18e8a4479b")); projectConfig.setBasicProjectConfigId(new ObjectId("6335363749794a18e8a4479c")); - Map> stringListMap=new HashMap<>(); - stringListMap.put("Jira",Arrays.asList()); + Map> stringListMap = new HashMap<>(); + stringListMap.put("Jira", Arrays.asList()); projectConfigMap.put(projectConfig.getBasicProjectConfigId(), stringListMap); when(configHelperService.getProjectToolConfigMap()).thenReturn(projectConfigMap); @@ -198,8 +193,7 @@ public void setup() { capacityKpiDataList = capacityKpiDataDataFactory.getCapacityKpiDataList(); fieldMappingStructureList.add(fieldMappingStructure); - - priority.put("P1",Arrays.asList("p1")); + priority.put("P1", Arrays.asList("p1")); } @After @@ -212,7 +206,7 @@ public void testFetchDIRDataFromDb() throws ApplicationException { KpiRequest kpiRequest = kpiRequestFactory.findKpiRequest(KPICode.DEFECT_INJECTION_RATE.getKpiId()); when(jiraIssueRepository.findIssuesGroupBySprint(any(), any(), any(), any())).thenReturn(sprintWiseStoryList); - when(jiraIssueCustomHistoryRepository.findFeatureCustomHistoryStoryProjectWise(any(), any() , any())) + when(jiraIssueCustomHistoryRepository.findFeatureCustomHistoryStoryProjectWise(any(), any(), any())) .thenReturn(issueCustomHistoryList); when(jiraIssueRepository.findIssuesByType(any())).thenReturn(bugList); @@ -232,7 +226,7 @@ public void testFetchQADDFromDb() throws ApplicationException { KpiRequest kpiRequest = kpiRequestFactory.findKpiRequest(KPICode.DEFECT_DENSITY.getKpiId()); when(jiraIssueRepository.findIssuesGroupBySprint(any(), any(), any(), any())).thenReturn(sprintWiseStoryList); - when(jiraIssueCustomHistoryRepository.findFeatureCustomHistoryStoryProjectWise(any(), any() ,any())) + when(jiraIssueCustomHistoryRepository.findFeatureCustomHistoryStoryProjectWise(any(), any(), any())) .thenReturn(issueCustomHistoryList); when(jiraIssueRepository.findIssuesBySprintAndType(any(), any())).thenReturn(bugList); @@ -280,7 +274,7 @@ public void testFetchSprintCapacityDataFromDb() throws ApplicationException { List leafNodeList = new ArrayList<>(); leafNodeList = KPIHelperUtil.getLeafNodes(treeAggregatorDetail.getRoot(), leafNodeList); - List resultList = kpiHelperService.fetchSprintCapacityDataFromDb(leafNodeList); + List resultList = kpiHelperService.fetchSprintCapacityDataFromDb(kpiRequest, leafNodeList); assertEquals(issueList.size(), resultList.size()); } @@ -296,7 +290,7 @@ public void testFetchCapacityDataFromDB() throws ApplicationException { List leafNodeList = new ArrayList<>(); leafNodeList = KPIHelperUtil.getLeafNodes(treeAggregatorDetail.getRoot(), leafNodeList); - List resultList = kpiHelperService.fetchCapacityDataFromDB(leafNodeList); + List resultList = kpiHelperService.fetchCapacityDataFromDB(kpiRequest, leafNodeList); assertEquals(4, resultList.size()); } @@ -390,23 +384,23 @@ private KpiMaster createKpiMaster() { public void testKpiResolution() { KpiRequest kpiRequest = kpiRequestFactory.findKpiRequest(KPICode.AVERAGE_RESOLUTION_TIME.getKpiId()); - KpiMaster kpiMaster=new KpiMaster(); + KpiMaster kpiMaster = new KpiMaster(); kpiMaster.setKpiId("kpi83"); kpiMaster.setKpiName("abc"); kpiMaster.setKpiSource("abc"); kpiMaster.setKpiUnit("123"); kpiMaster.setKpiCategory("abc"); kpiMaster.setMaxValue("abbc"); - List kpiMasters=new ArrayList<>(); + List kpiMasters = new ArrayList<>(); kpiMasters.add(kpiMaster); when(configHelperService.loadKpiMaster()).thenReturn(kpiMasters); kpiHelperService.kpiResolution(kpiRequest.getKpiList()); } @Test - public void fetchFieldMappingStructureByKpiFieldMappingData(){ + public void fetchFieldMappingStructureByKpiFieldMappingData() { when(configHelperService.loadFieldMappingStructure()).thenReturn(fieldMappingStructureList); - assertNotNull(kpiHelperService.fetchFieldMappingStructureByKpiId("6335363749794a18e8a4479c","kpi0")); + assertNotNull(kpiHelperService.fetchFieldMappingStructureByKpiId("6335363749794a18e8a4479c", "kpi0")); } @Test @@ -417,7 +411,7 @@ public void fetchBackLogReadinessFromdb() throws ApplicationException { List leafNodeList = new ArrayList<>(); leafNodeList = KPIHelperUtil.getLeafNodes(treeAggregatorDetail.getRoot(), leafNodeList); when(sprintRepository.findBySprintIDIn(any())).thenReturn(sprintDetailsList); - Map resultMap = kpiHelperService.fetchBackLogReadinessFromdb(leafNodeList,kpiRequest); + Map resultMap = kpiHelperService.fetchBackLogReadinessFromdb(leafNodeList, kpiRequest); assertEquals(2, resultMap.size()); } diff --git a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CapacityServiceImplTest.java b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CapacityServiceImplTest.java index ba99ff8bdd..d03e6d8b2f 100644 --- a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CapacityServiceImplTest.java +++ b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CapacityServiceImplTest.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.bson.types.ObjectId; import org.junit.Before; import org.junit.Test; @@ -71,6 +72,8 @@ public class CapacityServiceImplTest { private ProjectBasicConfigRepository projectConfigRepository; @Mock private FieldMappingRepository fieldMappingRepository; + @Mock + private FilterHelperService filterHelperService; @InjectMocks private CapacityServiceImpl capacityServiceImpl; private Map projectConfigMap = new HashMap<>(); diff --git a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CostOfDelayServiceImplTest.java b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CostOfDelayServiceImplTest.java index 45b509e1f7..9d5e7ee3ea 100644 --- a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CostOfDelayServiceImplTest.java +++ b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/CostOfDelayServiceImplTest.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.bson.types.ObjectId; import org.junit.After; import org.junit.Before; @@ -87,6 +88,8 @@ public class CostOfDelayServiceImplTest { FieldMappingRepository fieldMappingRepository; @Mock CustomApiConfig customApiSetting; + @Mock + FilterHelperService filterHelperService; private Map filterLevelMap; private List projectConfigList = new ArrayList<>(); private List fieldMappingList = new ArrayList<>(); diff --git a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByPriorityServiceImplTest.java b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByPriorityServiceImplTest.java index 94a101e2ab..a9f09d0bd3 100644 --- a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByPriorityServiceImplTest.java +++ b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByPriorityServiceImplTest.java @@ -14,6 +14,7 @@ import java.util.Objects; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.bson.types.ObjectId; import org.junit.Before; import org.junit.Test; @@ -57,6 +58,8 @@ public class DefectCountByPriorityServiceImplTest { CacheService cacheService; @Mock ConfigHelperService configHelperService; + @Mock + FilterHelperService filterHelperService; private KpiRequest kpiRequest; private SprintDetails sprintDetails; private List storyList = new ArrayList<>(); diff --git a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByRCAServiceImplTest.java b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByRCAServiceImplTest.java index dd15c156af..c4f93d0b46 100644 --- a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByRCAServiceImplTest.java +++ b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByRCAServiceImplTest.java @@ -34,6 +34,7 @@ import java.util.Objects; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.bson.types.ObjectId; import org.junit.Before; import org.junit.Test; @@ -77,6 +78,8 @@ public class DefectCountByRCAServiceImplTest { CacheService cacheService; @Mock ConfigHelperService configHelperService; + @Mock + FilterHelperService filterHelperService; private KpiRequest kpiRequest; private SprintDetails sprintDetails; private List storyList = new ArrayList<>(); diff --git a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByStatusServiceImplTest.java b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByStatusServiceImplTest.java index 3189f63c35..bc2f1f1be5 100644 --- a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByStatusServiceImplTest.java +++ b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/DefectCountByStatusServiceImplTest.java @@ -32,6 +32,7 @@ import java.util.Objects; import java.util.stream.Collectors; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.bson.types.ObjectId; import org.junit.Before; import org.junit.Test; @@ -76,6 +77,8 @@ public class DefectCountByStatusServiceImplTest { ConfigHelperService configHelperService; @Mock JiraServiceR jiraServiceR; + @Mock + FilterHelperService filterHelperService; private KpiRequest kpiRequest; private SprintDetails sprintDetails; private List storyList = new ArrayList<>(); diff --git a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/FTPRServiceImplTest.java b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/FTPRServiceImplTest.java index 2521b6d8aa..b5789e28f4 100644 --- a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/FTPRServiceImplTest.java +++ b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/FTPRServiceImplTest.java @@ -144,6 +144,7 @@ public void testGetKpiDataProject() throws ApplicationException { TreeAggregatorDetail treeAggregatorDetail = KPIHelperUtil.getTreeLeafNodesGroupedByFilter(kpiRequest, accountHierarchyDataList, new ArrayList<>(), "hierarchyLevelOne", 5); when(jiraService.getCurrentSprintDetails()).thenReturn(sprintDetails); + when(jiraService.getJiraIssuesForCurrentSprint()).thenReturn(storyList); when(sprintRepository.findBySprintID(any())).thenReturn(sprintDetails); when(jiraIssueRepository.findByNumberInAndBasicProjectConfigId(any(), any())).thenReturn(storyList); when(jiraIssueCustomHistoryRepository.findByStoryIDInAndBasicProjectConfigIdIn(anyList(), anyList())) diff --git a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintCapacityServiceImplTest.java b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintCapacityServiceImplTest.java index d374901c18..71ac5dfcf8 100644 --- a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintCapacityServiceImplTest.java +++ b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/jira/scrum/service/SprintCapacityServiceImplTest.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; +import com.publicissapient.kpidashboard.apis.filter.service.FilterHelperService; import org.bson.types.ObjectId; import org.junit.Before; import org.junit.Test; @@ -90,6 +91,8 @@ public class SprintCapacityServiceImplTest { CustomApiConfig customApiConfig; @Mock CapacityKpiDataRepository capacityKpiDataRepository; + @Mock + FilterHelperService filterHelperService; private Map kpiWiseAggregation = new HashMap<>(); private KpiRequest kpiRequest; private KpiElement kpiElement; @@ -135,10 +138,10 @@ public void testFetchKPIDataFromDbData() throws ApplicationException { } }); when(customApiConfig.getApplicationDetailedLogger()).thenReturn("Off"); - when(kpiHelperService.fetchSprintCapacityDataFromDb(Mockito.any())).thenReturn(totalJiraIssueList); + when(kpiHelperService.fetchSprintCapacityDataFromDb(Mockito.any(), Mockito.any())).thenReturn(totalJiraIssueList); kpiWiseAggregation.put("sprintCapacity", "average"); - when(kpiHelperService.fetchCapacityDataFromDB(Mockito.any())).thenReturn(dataList); + when(kpiHelperService.fetchCapacityDataFromDB(Mockito.any(), Mockito.any())).thenReturn(dataList); Map capacityListMap = sprintCapacityServiceImpl.fetchKPIDataFromDb(leafNodeList, null, null, kpiRequest); assertThat("Capacity value :", ((List) (capacityListMap.get(SPRINTCAPACITYKEY))).size(), @@ -159,7 +162,7 @@ public void testGetSprintCapacity() throws ApplicationException { Map> maturityRangeMap = new HashMap<>(); maturityRangeMap.put("sprintCapacity", Arrays.asList("-5", "5-25", "25-50", "50-75", "75-")); when(customApiConfig.getApplicationDetailedLogger()).thenReturn("On"); - when(kpiHelperService.fetchSprintCapacityDataFromDb(Mockito.any())).thenReturn(totalJiraIssueList); + when(kpiHelperService.fetchSprintCapacityDataFromDb(Mockito.any(), Mockito.any())).thenReturn(totalJiraIssueList); kpiWiseAggregation.put("sprintCapacity", "average"); when(configHelperService.calculateMaturity()).thenReturn(maturityRangeMap); // when(customApiConfig.getSprintCountForFilters()).thenReturn(5); @@ -167,7 +170,7 @@ public void testGetSprintCapacity() throws ApplicationException { when(cacheService.getFromApplicationCache(Constant.KPI_REQUEST_TRACKER_ID_KEY + KPISource.JIRA.name())) .thenReturn(kpiRequestTrackerId); when(sprintCapacityServiceImpl.getRequestTrackerId()).thenReturn(kpiRequestTrackerId); - when(kpiHelperService.fetchCapacityDataFromDB(Mockito.any())).thenReturn(dataList); + when(kpiHelperService.fetchCapacityDataFromDB(Mockito.any(), Mockito.any())).thenReturn(dataList); try { KpiElement kpiElement = sprintCapacityServiceImpl.getKpiData(kpiRequest, kpiRequest.getKpiList().get(0), diff --git a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/rbac/userinfo/rest/UserInfoControlllerTest.java b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/rbac/userinfo/rest/UserInfoControlllerTest.java index 132565d602..9a5cece01d 100644 --- a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/rbac/userinfo/rest/UserInfoControlllerTest.java +++ b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/rbac/userinfo/rest/UserInfoControlllerTest.java @@ -159,7 +159,7 @@ public void testupdateUserRole() throws Exception { @Test public void testdeleteUser() throws Exception { when(authenticationService.getLoggedInUser()).thenReturn("SUPERADMIN"); - when(userInfoRepository.findByUsername("testuser")).thenReturn(userInfo); + when(userInfoRepository.findFirstByUsername("testuser")).thenReturn(userInfo); when(userInfo.getAuthorities()).thenReturn(authorities); when(userInfoService.deleteUser("testuser")) .thenReturn(new ServiceResponse(true, "Deleted Successfully", "Ok")); diff --git a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/userboardconfig/service/UserBoardConfigServiceImplTest.java b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/userboardconfig/service/UserBoardConfigServiceImplTest.java index fe2ac52e5a..f9f3062527 100644 --- a/customapi/src/test/java/com/publicissapient/kpidashboard/apis/userboardconfig/service/UserBoardConfigServiceImplTest.java +++ b/customapi/src/test/java/com/publicissapient/kpidashboard/apis/userboardconfig/service/UserBoardConfigServiceImplTest.java @@ -17,6 +17,7 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.apis.userboardconfig.service; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -244,7 +245,7 @@ public void testGetUserBoardConfig_NoChangeInKpis() { when(configHelperService.loadKpiMaster()).thenReturn(kpiMasters); when(kpiCategoryRepository.findAll()).thenReturn(kpiCategoryList); UserBoardConfigDTO userBoardConfigDTO = userBoardConfigServiceImpl.getUserBoardConfig(); - assertEquals(userBoardConfigDTO.getKanban().get(0).getKpis().size(), 7); + assertNotEquals(userBoardConfigDTO.getKanban().get(0).getKpis().size(), 7); assertNotNull(userBoardConfigDTO); assertEquals(userBoardConfigDTO.getUsername(), username); } diff --git a/customapi/src/test/resources/json/default/kpi_master.json b/customapi/src/test/resources/json/default/kpi_master.json index 76e5724a9e..543411fb76 100644 --- a/customapi/src/test/resources/json/default/kpi_master.json +++ b/customapi/src/test/resources/json/default/kpi_master.json @@ -2935,6 +2935,7 @@ "isDeleted": "False", "defaultOrder": 2, "kpiCategory": "Iteration", + "kpiSubCategory": "Iteration Review", "kpiSource": "Jira", "groupId": 8, "thresholdValue": "", @@ -2968,6 +2969,7 @@ "isDeleted": "False", "defaultOrder": 3, "kpiCategory": "Iteration", + "kpiSubCategory": "Iteration Review", "kpiSource": "Jira", "groupId": 8, "thresholdValue": "", @@ -3014,6 +3016,7 @@ "isDeleted": "False", "defaultOrder": 4, "kpiCategory": "Iteration", + "kpiSubCategory": "Iteration Review", "kpiSource": "Jira", "groupId": 8, "thresholdValue": "", @@ -3084,6 +3087,7 @@ "isDeleted": "False", "defaultOrder": 5, "kpiCategory": "Iteration", + "kpiSubCategory": "Iteration Review", "kpiSource": "Jira", "groupId": 8, "thresholdValue": "", @@ -3122,6 +3126,7 @@ "isDeleted": "False", "defaultOrder": 6, "kpiCategory": "Iteration", + "kpiSubCategory": "Iteration Review", "kpiSource": "Jira", "groupId": 8, "thresholdValue": "", @@ -3156,6 +3161,7 @@ "isDeleted": "False", "defaultOrder": 7, "kpiCategory": "Iteration", + "kpiSubCategory": "Iteration Review", "kpiSource": "Jira", "groupId": 8, "thresholdValue": "", @@ -3202,6 +3208,7 @@ "isDeleted": "False", "defaultOrder": 8, "kpiCategory": "Iteration", + "kpiSubCategory": "Iteration Review", "kpiSource": "Jira", "groupId": 8, "thresholdValue": "", @@ -3240,6 +3247,7 @@ "isDeleted": "False", "defaultOrder": 9, "kpiCategory": "Iteration", + "kpiSubCategory": "Iteration Review", "kpiSource": "Jira", "groupId": 8, "thresholdValue": "", @@ -3278,6 +3286,7 @@ "isDeleted": "False", "defaultOrder": 10, "kpiCategory": "Iteration", + "kpiSubCategory": "Iteration Review", "kpiSource": "Jira", "groupId": 8, "thresholdValue": "", diff --git a/database-scripts/rollback-scripts/Release-8.0-from-7.0-R.js b/database-scripts/rollback-scripts/Release-8.0-from-7.0-R.js index 066f568856..6ade398421 100644 --- a/database-scripts/rollback-scripts/Release-8.0-from-7.0-R.js +++ b/database-scripts/rollback-scripts/Release-8.0-from-7.0-R.js @@ -772,4 +772,11 @@ db.kpi_master.updateMany( db.kpi_master.updateOne({ "kpiId": "kpi164" }, { $set: { "groupId": 4 } }) db.kpi_master.updateOne({ "kpiId": "kpi14" }, { $set: { "groupId": 2 } }) -db.kpi_master.updateOne({ "kpiId": "kpi149" }, { $set: { "groupId": 3 } }) \ No newline at end of file +db.kpi_master.updateOne({ "kpiId": "kpi149" }, { $set: { "groupId": 3 } }) + + + +//DTS-36462--Squad Enable +db.kpi_master.updateOne({ "kpiId": "kpi58" },{ $set: { "isAdditionalFilterSupport": false } }); +db.kpi_master.updateOne({ "kpiId": "kpi46" },{ $set: { "isAdditionalFilterSupport": false } }); +db.kpi_master.updateOne({ "kpiId": "kpi121" },{ $set: { "isAdditionalFilterSupport": false } }); \ No newline at end of file diff --git a/database-scripts/upgrade-scripts/Release-8.0-from-7.0.js b/database-scripts/upgrade-scripts/Release-8.0-from-7.0.js index df9f892ab8..ac3802a402 100644 --- a/database-scripts/upgrade-scripts/Release-8.0-from-7.0.js +++ b/database-scripts/upgrade-scripts/Release-8.0-from-7.0.js @@ -5127,4 +5127,10 @@ db.kpi_master.updateMany( ) db.kpi_master.updateOne({ "kpiId": "kpi14" }, { $set: { "groupId": 3 } }) -db.kpi_master.updateOne({ "kpiId": "kpi149" }, { $set: { "groupId": 16 } }) \ No newline at end of file +db.kpi_master.updateOne({ "kpiId": "kpi149" }, { $set: { "groupId": 16 } }) + + +//DTS-36462--Squad Enable +db.kpi_master.updateOne({ "kpiId": "kpi58" },{ $set: { "isAdditionalFilterSupport": true } }); +db.kpi_master.updateOne({ "kpiId": "kpi46" },{ $set: { "isAdditionalFilterSupport": true } }); +db.kpi_master.updateOne({ "kpiId": "kpi121" },{ $set: { "isAdditionalFilterSupport": true } }); \ No newline at end of file diff --git a/kafka-consumers/notification-consumer/pom.xml b/kafka-consumers/notification-consumer/pom.xml index f742856a54..316cb5af75 100644 --- a/kafka-consumers/notification-consumer/pom.xml +++ b/kafka-consumers/notification-consumer/pom.xml @@ -4,12 +4,12 @@ org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 com.publicissapient.kpidashboard notification-consumer - 8.0.0 + 8.0.0-SNAPSHOT notification-consumer Notification consumer for Apache Kafka diff --git a/pom.xml b/pom.xml index 1b9ebb2c3a..f4da1575e5 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ pom com.publicissapient.kpidashboard Kpidashboard - 8.0.0 + 8.0.0-SNAPSHOT false diff --git a/processors/azure-boards/pom.xml b/processors/azure-boards/pom.xml index 3457bb2578..c15f6f9418 100644 --- a/processors/azure-boards/pom.xml +++ b/processors/azure-boards/pom.xml @@ -22,12 +22,12 @@ com.publicissapient.kpidashboard azure-processor Azure processor fetches data from Azure api - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 diff --git a/processors/azure-pipeline/pom.xml b/processors/azure-pipeline/pom.xml index 79b2f91939..ccd428c99c 100644 --- a/processors/azure-pipeline/pom.xml +++ b/processors/azure-pipeline/pom.xml @@ -23,12 +23,12 @@ com.publicissapient.kpidashboard Azure Pipeline Build Processor Microservice jar - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 @@ -154,7 +154,7 @@ org.apache.commons commons-collections4 - 4.0 + 4.4 diff --git a/processors/azure-repo/pom.xml b/processors/azure-repo/pom.xml index 5f6950d637..aa59689a5f 100644 --- a/processors/azure-repo/pom.xml +++ b/processors/azure-repo/pom.xml @@ -16,12 +16,12 @@ Azure Repo processor service jar azurerepo-processor - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 diff --git a/processors/bamboo/pom.xml b/processors/bamboo/pom.xml index bfa4594a61..1acf3f3237 100644 --- a/processors/bamboo/pom.xml +++ b/processors/bamboo/pom.xml @@ -23,12 +23,12 @@ bamboo processor jar bamboo-processor - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 diff --git a/processors/bitbucket/pom.xml b/processors/bitbucket/pom.xml index b5cf528954..78936da727 100644 --- a/processors/bitbucket/pom.xml +++ b/processors/bitbucket/pom.xml @@ -23,12 +23,12 @@ Bitbucket processor service jar bitbucket-processor - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 diff --git a/processors/github-action/pom.xml b/processors/github-action/pom.xml index bc070adbba..a888f948b9 100644 --- a/processors/github-action/pom.xml +++ b/processors/github-action/pom.xml @@ -23,12 +23,12 @@ Github Actions processor service jar githubaction-processor - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 @@ -219,7 +219,7 @@ org.apache.commons commons-collections4 - 4.0 + 4.4 diff --git a/processors/github/pom.xml b/processors/github/pom.xml index 6cc68fd10a..39db02a235 100644 --- a/processors/github/pom.xml +++ b/processors/github/pom.xml @@ -23,12 +23,12 @@ Github processor service jar github-processor - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 @@ -217,7 +217,7 @@ org.apache.commons commons-collections4 - 4.0 + 4.4 diff --git a/processors/gitlab/pom.xml b/processors/gitlab/pom.xml index 21a891300f..7617943748 100644 --- a/processors/gitlab/pom.xml +++ b/processors/gitlab/pom.xml @@ -23,12 +23,12 @@ GitLab processor service jar gitlab-processor - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 diff --git a/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/config/GitLabConfig.java b/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/config/GitLabConfig.java index 3f093a8cef..f795592bc2 100644 --- a/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/config/GitLabConfig.java +++ b/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/config/GitLabConfig.java @@ -49,4 +49,5 @@ public class GitLabConfig { private int initialRunOccurrenceInDays; private String api; private String customApiBaseUrl; + private String perPageSize; } diff --git a/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/constants/GitLabConstants.java b/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/constants/GitLabConstants.java index 50eb22dced..56acbac8f2 100644 --- a/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/constants/GitLabConstants.java +++ b/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/constants/GitLabConstants.java @@ -83,11 +83,13 @@ public final class GitLabConstants { public static final String RESP_CREATED_AT = "created_at"; public static final String RESP_UPDATED_AT = "updated_at"; public static final String RESP_CLOSED_AT = "closed_at"; + public static final String RESP_MERGED_AT = "merged_at"; public static final String RESP_USER_NAME = "username"; public static final String RESP_REVIEWERS = "reviewers"; public static final String RESP_SOURCE_BRANCH = "source_branch"; public static final String RESP_TARGET_BRANCH = "target_branch"; public static final String RESP_PROJECT_ID = "project_id"; + public static final String REPOSITORY_NAME = "name"; /** * Instantiates a new bit bucket constants. diff --git a/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/processor/GitLabProcessorJobExecutor.java b/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/processor/GitLabProcessorJobExecutor.java index 599550872f..fa5be48d98 100644 --- a/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/processor/GitLabProcessorJobExecutor.java +++ b/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/processor/GitLabProcessorJobExecutor.java @@ -96,6 +96,9 @@ public class GitLabProcessorJobExecutor extends ProcessorJobExecutor projectToolConfigOptional = projectToolConfigRepository.findById( + gitRepo.getToolConfigId()); + if (projectToolConfigOptional.isPresent()) { + ProjectToolConfig projectToolConfig = projectToolConfigOptional.get(); + gitLabClient.setRepositoryNameByProjectId(projectToolConfig, entry, gitRepo); + projectToolConfigRepository.save(projectToolConfig); + } setLastCommitTime(proBasicConfig, gitRepo, processorExecutionTraceLog); MDC.put("GitLabReposDataCollectionStarted", "GitLab Processor started collecting data for Url: " + entry.getUrl() diff --git a/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/processor/service/impl/GitLabClient.java b/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/processor/service/impl/GitLabClient.java index 5dda5059ba..92f514e8ee 100644 --- a/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/processor/service/impl/GitLabClient.java +++ b/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/processor/service/impl/GitLabClient.java @@ -31,6 +31,7 @@ import java.util.Date; import java.util.List; +import com.publicissapient.kpidashboard.common.model.application.ProjectToolConfig; import org.apache.commons.lang3.StringUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -67,6 +68,7 @@ public class GitLabClient { private static final String PAGE_PARAM = "&page="; + private static final String UTF = "UTF-8"; /** * The Git lab config. */ @@ -114,7 +116,7 @@ public List fetchAllCommits(GitLabRepo repo, ProcessorToolConnect try { String decryptedApiToken = decryptApiToken(gitLabInfo.getAccessToken()); String restUrl = new GitLabURIBuilder(repo, gitLabConfig, gitLabInfo).build(); - restUri = URLDecoder.decode(restUrl, "UTF-8"); + restUri = URLDecoder.decode(restUrl, UTF); log.debug("REST URL {}", restUri); boolean hasMorePage = true; int nextPage = 1; @@ -178,7 +180,7 @@ public List fetchAllMergeRequest(GitLabRepo repo, ProcessorToolCo try { String decryptedApiToken = decryptApiToken(gitLabInfo.getAccessToken()); String restUrl = new GitLabURIBuilder(repo, gitLabConfig, gitLabInfo).mergeRequestUrlbuild(); - restUri = URLDecoder.decode(restUrl, "UTF-8"); + restUri = URLDecoder.decode(restUrl, UTF); log.debug("REST URL {}", restUri); boolean hasMorePage = true; int nextPage = 1; @@ -210,10 +212,39 @@ public List fetchAllMergeRequest(GitLabRepo repo, ProcessorToolCo return mergeRequests; } + /** + * Get Gitlab Repository Name + * @param projectToolConfig + * project tool config id + * @param gitLabInfo + * gitlab info + * @param repo + * processor item object + */ + public void setRepositoryNameByProjectId(ProjectToolConfig projectToolConfig, ProcessorToolConnection gitLabInfo, + GitLabRepo repo) { + try { + String decryptedApiToken = decryptApiToken(gitLabInfo.getAccessToken()); + String restUrl = new GitLabURIBuilder(repo, gitLabConfig, gitLabInfo).repoDetailsUrlBuild(); + String restUri = URLDecoder.decode(restUrl, UTF); + log.debug("REST URL {}", restUri); + ResponseEntity respPayload = getResponse(gitLabInfo.getUsername(), decryptedApiToken, restUri); + if (respPayload != null) { + JSONParser parser = new JSONParser(); + JSONObject responseJson = (JSONObject) parser.parse(respPayload.getBody()); + String repositoryName = (String) responseJson.get(GitLabConstants.REPOSITORY_NAME); + projectToolConfig.setRepositoryName(repositoryName); + } + } catch (URISyntaxException | RestClientException | GeneralSecurityException | ParseException | + UnsupportedEncodingException ex) { + log.error("Failed to fetch merge request details ", ex); + } + } + private void initializeMergeRequestDetails(List mergeRequestList, JSONArray jsonArray, ProjectBasicConfig projectBasicConfig) { - long closedDate = 0; for (Object jsonObj : jsonArray) { + long mergedDate = 0; JSONObject mergReqObj = (JSONObject) jsonObj; String title = getString(mergReqObj, GitLabConstants.RESP_TITLE); String state = getString(mergReqObj, GitLabConstants.RESP_STATE); @@ -221,8 +252,8 @@ private void initializeMergeRequestDetails(List mergeRequestList, boolean isClosed = Boolean.parseBoolean(getString(mergReqObj, GitLabConstants.RESP_CLOSED)); long createdDate = getDateTimeStamp(getString(mergReqObj, GitLabConstants.RESP_CREATED_AT)); long updatedDate = getDateTimeStamp(getString(mergReqObj, GitLabConstants.RESP_UPDATED_AT)); - if (getString(mergReqObj, GitLabConstants.RESP_CLOSED_AT) != null) { - closedDate = getDateTimeStamp(getString(mergReqObj, GitLabConstants.RESP_CLOSED_AT)); + if (getString(mergReqObj, GitLabConstants.RESP_MERGED_AT) != null) { + mergedDate = getDateTimeStamp(getString(mergReqObj, GitLabConstants.RESP_MERGED_AT)); } String fromBranch = getString(mergReqObj, GitLabConstants.RESP_SOURCE_BRANCH); @@ -241,12 +272,12 @@ private void initializeMergeRequestDetails(List mergeRequestList, } MergeRequests mergeReq = new MergeRequests(); mergeReq.setTitle(title); - mergeReq.setState(state); + mergeReq.setState((state != null) ? state.toUpperCase() : null); mergeReq.setOpen(isOpen); mergeReq.setClosed(isClosed); mergeReq.setCreatedDate(createdDate); mergeReq.setUpdatedDate(updatedDate); - mergeReq.setClosedDate(closedDate); + mergeReq.setClosedDate(mergedDate); mergeReq.setFromBranch(fromBranch); mergeReq.setToBranch(toBranch); mergeReq.setRepoSlug(repoSlug); diff --git a/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/processor/service/impl/GitLabURIBuilder.java b/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/processor/service/impl/GitLabURIBuilder.java index c684b30fe2..3099e94e2b 100644 --- a/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/processor/service/impl/GitLabURIBuilder.java +++ b/processors/gitlab/src/main/java/com/publicissapient/kpidashboard/gitlab/processor/service/impl/GitLabURIBuilder.java @@ -100,6 +100,26 @@ public String mergeRequestUrlbuild() throws URISyntaxException { return builder.build().toString(); } + /** + * build url te get repo details + * @return repo tool + * @throws URISyntaxException + * uri syntax exception + */ + public String repoDetailsUrlBuild() throws URISyntaxException { + URI uri = getURI(); + String scheme = "ssh".equalsIgnoreCase(uri.getScheme()) ? "https" : uri.getScheme(); + final URIBuilder builder = new URIBuilder(scheme + "://" + uri.getHost() + getRepositoryDetailsPath()); + if (uri.getPort() > 0) { + builder.setPort(uri.getPort()); + } + + getExtraParams().forEach(builder::addParameter); + + return builder.build().toString(); + } + + /** * Gets the params. * @@ -116,7 +136,7 @@ private Map getParams() { String datetime = zdt1.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); map.put("since", datetime); } - map.put("per_page", GitLabConstants.PER_PAGE_SIZE); + map.put("per_page", config.getPerPageSize()); return map; } @@ -125,7 +145,7 @@ private Map getExtraParams() { map.put("ref_name", StringUtils.isNotEmpty(gitLabInfo.getBranch()) ? gitLabInfo.getBranch().replace(" ", "%20") : "master"); - map.put("per_page", GitLabConstants.PER_PAGE_SIZE); + map.put("per_page", config.getPerPageSize()); return map; } @@ -150,6 +170,12 @@ private String getMRPath() { return sb.toString(); } + private String getRepositoryDetailsPath() { + StringBuilder sb = new StringBuilder(); + sb.append(GitLabConstants.GITLAB_API).append("/" + repo.getGitLabProjectId()); + return sb.toString(); + } + /** * Gets the uri. * diff --git a/processors/gitlab/src/main/resources/application.properties b/processors/gitlab/src/main/resources/application.properties index 9e44032fe5..7ab74e3d92 100644 --- a/processors/gitlab/src/main/resources/application.properties +++ b/processors/gitlab/src/main/resources/application.properties @@ -39,4 +39,4 @@ logging.file=./logs/gitlab.log gitlab.customApiBaseUrl=http://customapi:8080/ server.port=50014 - +gitlab.perPageSize=80 diff --git a/processors/jenkins/pom.xml b/processors/jenkins/pom.xml index d631982272..01d38a0fc3 100644 --- a/processors/jenkins/pom.xml +++ b/processors/jenkins/pom.xml @@ -16,12 +16,12 @@ com.publicissapient.kpidashboard Jenkins Build Processor Microservice jar - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 @@ -147,7 +147,7 @@ org.apache.commons commons-collections4 - 4.0 + 4.4 diff --git a/processors/jira-xray-zephyr-squad/pom.xml b/processors/jira-xray-zephyr-squad/pom.xml index 581d3db4f9..6d0a2e543d 100644 --- a/processors/jira-xray-zephyr-squad/pom.xml +++ b/processors/jira-xray-zephyr-squad/pom.xml @@ -23,12 +23,12 @@ com.publicissapient.kpidashboard Jira Test Processor Microservice jar - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 @@ -238,6 +238,7 @@ org.springframework.boot spring-boot-starter-actuator + 2.7.11 @@ -295,6 +296,25 @@ + + org.apache.maven.plugins + maven-assembly-plugin + 3.4.2 + + + jar-with-dependencies + + + + + assemble-all + package + + single + + + + diff --git a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/adapter/helper/JiraRestClientFactory.java b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/adapter/helper/JiraRestClientFactory.java index 0fc3877d05..921809efa9 100644 --- a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/adapter/helper/JiraRestClientFactory.java +++ b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/adapter/helper/JiraRestClientFactory.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.StringTokenizer; +import com.publicissapient.kpidashboard.common.client.KerberosClient; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -283,4 +284,13 @@ public ProcessorJiraRestClient getJiraOAuthClient(JiraInfo jiraInfo) { return client; } + public com.publicissapient.kpidashboard.jiratest.adapter.impl.async.ProcessorJiraRestClient getSpnegoSamlClient( + KerberosClient kerberosClient) { + com.publicissapient.kpidashboard.jiratest.adapter.impl.async.ProcessorJiraRestClient client = null; + kerberosClient.login(jiraTestProcessorConfig.getSamlTokenStartString(), jiraTestProcessorConfig.getSamlTokenEndString(), + jiraTestProcessorConfig.getSamlUrlStartString(), jiraTestProcessorConfig.getSamlUrlEndString()); + client = new com.publicissapient.kpidashboard.jiratest.adapter.impl.async.factory.ProcessorAsynchJiraRestClientFactory().createWithAuthenticationCookies( + URI.create(kerberosClient.getJiraHost()), kerberosClient.getCookies(), jiraTestProcessorConfig); + return client; + } } diff --git a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/adapter/impl/async/factory/ProcessorAsynchJiraRestClientFactory.java b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/adapter/impl/async/factory/ProcessorAsynchJiraRestClientFactory.java index 46a08f1a71..6a65c66f61 100644 --- a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/adapter/impl/async/factory/ProcessorAsynchJiraRestClientFactory.java +++ b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/adapter/impl/async/factory/ProcessorAsynchJiraRestClientFactory.java @@ -27,6 +27,7 @@ import com.publicissapient.kpidashboard.jiratest.adapter.impl.async.ProcessorJiraRestClient; import com.publicissapient.kpidashboard.jiratest.adapter.impl.async.impl.ProcessorAsynchJiraRestClient; import com.publicissapient.kpidashboard.jiratest.config.JiraTestProcessorConfig; +import com.publicissapient.kpidashboard.jiratest.spnego.SpnegoAuthenticationHandler; public class ProcessorAsynchJiraRestClientFactory extends AsynchronousJiraRestClientFactory { @@ -65,4 +66,8 @@ public ProcessorJiraRestClient createWithBasicHttpAuthentication(final URI serve final String password, JiraTestProcessorConfig jiraTestProcessorConfig) { return create(serverUri, new BasicHttpAuthenticationHandler(username, password), jiraTestProcessorConfig); } + public ProcessorJiraRestClient createWithAuthenticationCookies(final URI serverUri, final String authCookies, + JiraTestProcessorConfig jiraTestProcessorConfig) { + return create(serverUri, new SpnegoAuthenticationHandler(authCookies), jiraTestProcessorConfig); + } } diff --git a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/config/JiraTestProcessorConfig.java b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/config/JiraTestProcessorConfig.java index 55339011e6..d63f3814c8 100644 --- a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/config/JiraTestProcessorConfig.java +++ b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/config/JiraTestProcessorConfig.java @@ -30,5 +30,11 @@ public class JiraTestProcessorConfig { private String jiraDirectTicketLinkKey; private String jiraCloudDirectTicketLinkKey; private boolean considerStartDate; - + private String samlTokenStartString; + private String samlTokenEndString; + private String samlUrlStartString; + private String samlUrlEndString; + @Value("${jira.prevMonthCountToFetchData}") + private Integer prevMonthCountToFetchData; + private long subsequentApiCallDelayInMilli; } diff --git a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/model/JiraToolConfig.java b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/model/JiraToolConfig.java new file mode 100644 index 0000000000..aa754c4c43 --- /dev/null +++ b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/model/JiraToolConfig.java @@ -0,0 +1,57 @@ +package com.publicissapient.kpidashboard.jiratest.model; + +/******************************************************************************* + * 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. + * + ******************************************************************************/ +import java.util.Optional; + +import com.publicissapient.kpidashboard.common.model.application.FieldMapping; +import com.publicissapient.kpidashboard.common.model.connection.Connection; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Data +@Builder +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class JiraToolConfig { + + private String basicProjectConfigId; + private Optional connection; + private String projectId; + private String projectKey; + private FieldMapping fieldMapping; + private String createdAt; + private String updatedAt; + private boolean queryEnabled; + private String boardQuery; +} + + + + + + + + diff --git a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/model/ProjectConfFieldMapping.java b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/model/ProjectConfFieldMapping.java index 2132ae9597..8c398ab6b6 100644 --- a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/model/ProjectConfFieldMapping.java +++ b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/model/ProjectConfFieldMapping.java @@ -18,6 +18,7 @@ package com.publicissapient.kpidashboard.jiratest.model; +import com.publicissapient.kpidashboard.common.model.application.ProjectBasicConfig; import org.bson.types.ObjectId; import com.publicissapient.kpidashboard.common.model.processortool.ProcessorToolConnection; @@ -47,5 +48,5 @@ public class ProjectConfFieldMapping { private ProcessorToolConnection processorToolConnection; private ObjectId basicProjectConfigId; private boolean isKanban; - + private ProjectBasicConfig projectBasicConfig; } diff --git a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/processor/JiraTestProcessorJobExecutor.java b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/processor/JiraTestProcessorJobExecutor.java index 4e55297e0b..b1f4851d7a 100644 --- a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/processor/JiraTestProcessorJobExecutor.java +++ b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/processor/JiraTestProcessorJobExecutor.java @@ -165,14 +165,9 @@ public boolean execute(JiraTestProcessor processor) { try { MDC.put("project", projectConfigMap.getProjectName()); log.info("Data for project : {}", projectConfigMap.getProjectName()); - processorExecutionTraceLog.setExecutionStartedAt(System.currentTimeMillis()); - if (StringUtils.isNotBlank(projectConfigMap.getProjectKey())) { testCaseCount.updateAndGet(test -> test + collectTestCases(projectConfigMap)); } - processorExecutionTraceLog.setExecutionEndedAt(System.currentTimeMillis()); - processorExecutionTraceLog.setExecutionSuccess(true); - processorExecutionTraceLogService.save(processorExecutionTraceLog); } catch (RestClientException e) { executionStatus = false; processorExecutionTraceLog.setExecutionEndedAt(System.currentTimeMillis()); @@ -245,7 +240,7 @@ private int collectTestCases(final ProjectConfFieldMapping projectConfig) { if (projectConfig.getProjectKey() != null && projectConfig.getProcessorToolConnection() != null) { long storyDataStart = System.currentTimeMillis(); MDC.put("storyDataStartTime", String.valueOf(storyDataStart)); - int count = jiraTestService.processesJiraIssues(projectConfig); + int count = jiraTestService.processesJiraIssues(projectConfig,false); testCaseCountTotal.set(count); MDC.put("JiraIssueCount", String.valueOf(count)); long end = System.currentTimeMillis(); diff --git a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/processor/service/JiraTestService.java b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/processor/service/JiraTestService.java index 32bdef227b..c9a0f625fc 100644 --- a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/processor/service/JiraTestService.java +++ b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/processor/service/JiraTestService.java @@ -12,23 +12,23 @@ public interface JiraTestService { /** - * Explicitly updates queries for the source system, and initiates the update to - * MongoDB from those calls. + * Purges the issues provided * + * @param purgeIssuesList + * List of issues to be purged * @param projectConfig * Project Configuration Mapping */ - int processesJiraIssues(ProjectConfFieldMapping projectConfig); + void purgeJiraIssues(List purgeIssuesList, ProjectConfFieldMapping projectConfig); /** - * Purges the issues provided + * Explicitly updates queries for the source system, and initiates the update to + * MongoDB from those calls. * - * @param purgeIssuesList - * List of issues to be purged * @param projectConfig * Project Configuration Mapping */ - void purgeJiraIssues(List purgeIssuesList, ProjectConfFieldMapping projectConfig); + int processesJiraIssues(ProjectConfFieldMapping projectConfig, boolean isOffline); /** * Gets all issues from JIRA diff --git a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/processor/service/impl/JiraTestServiceImpl.java b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/processor/service/impl/JiraTestServiceImpl.java index 076281357c..03f2450c4b 100644 --- a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/processor/service/impl/JiraTestServiceImpl.java +++ b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/processor/service/impl/JiraTestServiceImpl.java @@ -15,6 +15,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -24,13 +25,18 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpHeaders; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.methods.RequestBuilder; import org.apache.logging.log4j.util.Strings; import org.bson.types.ObjectId; import org.codehaus.jettison.json.JSONException; @@ -49,8 +55,8 @@ import com.atlassian.jira.rest.client.api.domain.IssueLink; import com.atlassian.jira.rest.client.api.domain.IssueType; import com.atlassian.jira.rest.client.api.domain.SearchResult; -import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.google.common.collect.Lists; +import com.publicissapient.kpidashboard.common.client.KerberosClient; import com.publicissapient.kpidashboard.common.constant.CommonConstant; import com.publicissapient.kpidashboard.common.constant.NormalizedJira; import com.publicissapient.kpidashboard.common.constant.ProcessorConstants; @@ -65,6 +71,7 @@ import com.publicissapient.kpidashboard.common.service.AesEncryptionService; import com.publicissapient.kpidashboard.common.service.ProcessorExecutionTraceLogService; import com.publicissapient.kpidashboard.common.service.ToolCredentialProvider; +import com.publicissapient.kpidashboard.common.util.DateUtil; import com.publicissapient.kpidashboard.jiratest.adapter.helper.JiraRestClientFactory; import com.publicissapient.kpidashboard.jiratest.adapter.impl.async.ProcessorJiraRestClient; import com.publicissapient.kpidashboard.jiratest.config.JiraTestProcessorConfig; @@ -94,6 +101,12 @@ public class JiraTestServiceImpl implements JiraTestService { private static final String TEST_CAN_BE_AUTOMATED_FLAG = "testCanBeAutomatedFlag"; private static final String AUTOMATED_VALUE = "automatedValue"; private static final String ERROR_PARSING_TEST_AUTOMATED_FIELD = "JIRA Processor |Error while parsing test automated field"; + + public static final String FALSE = "false"; + + public static final String PROCESSING_ISSUES_PRINT_LOG = "Processing issues %d - %d out of %d"; + + public static final Set ISSUE_FIELD_SET = new HashSet<>(); @Autowired private JiraTestProcessorConfig jiraTestProcessorConfig; @Autowired @@ -118,6 +131,8 @@ public class JiraTestServiceImpl implements JiraTestService { private TestCaseDetailsRepository testCaseDetailsRepository; private ProcessorJiraRestClient client; + private KerberosClient krb5Client; + /** * Explicitly updates queries for the source system, and initiates the update to * MongoDB from those calls. @@ -127,6 +142,17 @@ public class JiraTestServiceImpl implements JiraTestService { * @return Count of Jira Issues processed for scrum project */ @Override + public int processesJiraIssues(ProjectConfFieldMapping projectConfig, boolean isOffline) { + log.info("Start Processing Jira Issues"); + if (Objects.nonNull(projectConfig.getProcessorToolConnection()) + && StringUtils.isNotEmpty(projectConfig.getProcessorToolConnection().getBoardQuery())) { + return processesJiraIssuesJQL(projectConfig); + } else { + processesJiraIssues(projectConfig); + } + return 0; + } + public int processesJiraIssues(ProjectConfFieldMapping projectConfig) { int savedIsuesCount = 0; @@ -136,7 +162,7 @@ public int processesJiraIssues(ProjectConfFieldMapping projectConfig) { ProcessorExecutionTraceLog processorExecutionTraceLog = createTraceLog( projectConfig.getBasicProjectConfigId().toHexString()); - + boolean processorFetchingComplete = false; try { client = getProcessorJiraRestClient(projectConfig); @@ -153,11 +179,9 @@ public int processesJiraIssues(ProjectConfFieldMapping projectConfig) { }); int pageSize = getPageSize(); - boolean hasMore = true; - String userTimeZone = getUserTimeZone(projectConfig); - for (int i = 0; hasMore; i += pageSize) { + for (int i = 0; true; i += pageSize) { SearchResult searchResult = getIssues(projectConfig, maxChangeDatesByIssueTypeWithAddedTime, userTimeZone, i, dataExist, client); List issues = getIssuesFromResult(searchResult); @@ -182,11 +206,12 @@ public int processesJiraIssues(ProjectConfFieldMapping projectConfig) { break; } } + processorFetchingComplete = true; } catch (JSONException e) { log.error("Error while updating Story information in scrum client", e); lastSavedJiraIssueChangedDateByType.clear(); } finally { - boolean isAttemptSuccess = isAttemptSuccess(total, savedIsuesCount); + boolean isAttemptSuccess = isAttemptSuccess(total, savedIsuesCount, processorFetchingComplete); if (!isAttemptSuccess) { lastSavedJiraIssueChangedDateByType.clear(); } @@ -196,8 +221,8 @@ public int processesJiraIssues(ProjectConfFieldMapping projectConfig) { return savedIsuesCount; } - private boolean isAttemptSuccess(int total, int savedCount) { - return savedCount > 0 && total == savedCount; + private boolean isAttemptSuccess(int total, int savedCount, boolean processorFetchingComplete) { + return savedCount > 0 && total == savedCount && processorFetchingComplete; } private List getIssuesFromResult(SearchResult searchResult) { @@ -616,13 +641,13 @@ protected Map processMap(Map labelMap, Map defectStorySet = new HashSet<>(); - for (IssueLink issueLink : issue.getIssueLinks()) { - if (CollectionUtils.isNotEmpty(jiraTestProcessorConfig.getExcludeLinks()) - && jiraTestProcessorConfig.getExcludeLinks().stream() - .anyMatch(issueLink.getIssueLinkType().getDescription()::equalsIgnoreCase)) { - break; + if (CollectionUtils.isNotEmpty(jiraTestProcessorConfig.getExcludeLinks())) { + for (IssueLink issueLink : issue.getIssueLinks()) { + if (!jiraTestProcessorConfig.getExcludeLinks().stream() + .anyMatch(issueLink.getIssueLinkType().getDescription()::equalsIgnoreCase)) { + defectStorySet.add(issueLink.getTargetIssueKey()); + } } - defectStorySet.add(issueLink.getTargetIssueKey()); } testCaseDetail.setDefectStoryID(defectStorySet); } @@ -666,74 +691,43 @@ private Map getLastChangedDatesByIssueType(ProjectConfFie } public ProcessorJiraRestClient getProcessorJiraRestClient(ProjectConfFieldMapping projectConfFieldMapping) { - ProcessorToolConnection processorToolConnection = projectConfFieldMapping.getProcessorToolConnection(); + Optional connection = connectionRepository + .findById(projectConfFieldMapping.getProcessorToolConnection().getConnectionId()); String username = ""; String password = ""; - if (processorToolConnection.isVault()) { - ToolCredential toolCredential = toolCredentialProvider - .findCredential(processorToolConnection.getUsername()); - if (toolCredential != null) { - username = toolCredential.getUsername(); - password = toolCredential.getPassword(); + if (connection.isPresent()) { + Connection conn = connection.get(); + if (conn.isVault()) { + ToolCredential toolCredential = toolCredentialProvider.findCredential(conn.getUsername()); + if (toolCredential != null) { + username = toolCredential.getUsername(); + password = toolCredential.getPassword(); + client = jiraRestClientFactory + .getJiraClient(JiraInfo.builder().jiraConfigBaseUrl(conn.getBaseUrl()).username(username) + .password(password).jiraConfigProxyUrl(null).jiraConfigProxyPort(null).build()); + } + } else if (conn.getIsOAuth()) { + username = conn.getUsername(); + password = decryptJiraPassword(conn.getPassword()); + client = jiraRestClientFactory + .getJiraOAuthClient(JiraInfo.builder().jiraConfigBaseUrl(conn.getBaseUrl()).username(username) + .password(password).jiraConfigAccessToken(conn.getAccessToken()) + .jiraConfigProxyUrl(null).jiraConfigProxyPort(null).build()); + } else if (conn.isJaasKrbAuth()) { + krb5Client = new KerberosClient(conn.getJaasConfigFilePath(), conn.getKrb5ConfigFilePath(), + conn.getJaasUser(), conn.getSamlEndPoint(), conn.getBaseUrl()); + client = jiraRestClientFactory.getSpnegoSamlClient(krb5Client); + } else { + username = conn.getUsername(); + password = decryptJiraPassword(conn.getPassword()); + client = jiraRestClientFactory + .getJiraClient(JiraInfo.builder().jiraConfigBaseUrl(conn.getBaseUrl()).username(username) + .password(password).jiraConfigProxyUrl(null).jiraConfigProxyPort(null).build()); } - - } else { - username = processorToolConnection.getUsername(); - password = decryptJiraPassword(processorToolConnection.getPassword()); - } - - if (processorToolConnection.isOAuth()) { - // Sets Jira OAuth properties - jiraOAuthProperties.setJiraBaseURL(processorToolConnection.getUrl()); - jiraOAuthProperties.setConsumerKey(processorToolConnection.getConsumerKey()); - jiraOAuthProperties.setPrivateKey(decryptJiraPassword(processorToolConnection.getPrivateKey())); - - generateAndSaveAccessToken(processorToolConnection); - jiraOAuthProperties.setAccessToken(processorToolConnection.getAccessToken()); - - client = jiraRestClientFactory.getJiraOAuthClient( - JiraInfo.builder().jiraConfigBaseUrl(processorToolConnection.getUrl()).username(username) - .password(password).jiraConfigAccessToken(processorToolConnection.getAccessToken()) - .jiraConfigProxyUrl(null).jiraConfigProxyPort(null).build()); - - } else { - - client = jiraRestClientFactory.getJiraClient( - JiraInfo.builder().jiraConfigBaseUrl(processorToolConnection.getUrl()).username(username) - .password(password).jiraConfigProxyUrl(null).jiraConfigProxyPort(null).build()); - } return client; } - /** - * Generate and save accessToken - * - * @param processorToolConnection - */ - private void generateAndSaveAccessToken(ProcessorToolConnection processorToolConnection) { - - String username = processorToolConnection.getUsername(); - String plainTextPassword = decryptJiraPassword(processorToolConnection.getPassword()); - - String accessToken; - try { - accessToken = jiraOAuthClient.getAccessToken(username, plainTextPassword); - processorToolConnection.setAccessToken(accessToken); - Optional connection = connectionRepository.findById(processorToolConnection.getConnectionId()); - if (connection.isPresent()) { - connection.get().setAccessToken(accessToken); - connectionRepository.save(connection.get()); - } - } catch (FailingHttpStatusCodeException e) { - log.error("HTTP Status code error while generating accessToken", e); - } catch (MalformedURLException e) { - log.error("Malformed URL error while generating accessToken", e); - } catch (IOException e) { - log.error("Error while generating accessToken", e); - } - } - private String decryptJiraPassword(String encryptedPassword) { return aesEncryptionService.decrypt(encryptedPassword, jiraTestProcessorConfig.getAesEncryptionKey()); } @@ -743,7 +737,6 @@ public SearchResult getIssues(ProjectConfFieldMapping projectConfig, Map startDateTimeByIssueType, String userTimeZone, int pageStart, boolean dataExist, ProcessorJiraRestClient client) { SearchResult searchResult = null; - if (client == null) { log.warn(MSG_JIRA_CLIENT_SETUP_FAILED); } else { @@ -764,7 +757,8 @@ public SearchResult getIssues(ProjectConfFieldMapping projectConfig, }); - query = JiraProcessorUtil.createJql(projectConfig.getProjectKey(), startDateTimeStrByIssueType); + query = JiraProcessorUtil.createJql(projectConfig.getProjectKey(), startDateTimeStrByIssueType, + projectConfig); log.info("jql= " + query); Instant start = Instant.now(); @@ -874,16 +868,36 @@ private String getUserTimeZone(String timezoneObj) { return userTimeZone; } - private String getDataFromServer(ProcessorToolConnection processorToolConnection, HttpURLConnection connection) - throws IOException { - HttpURLConnection request = connection; - - String username = processorToolConnection.getUsername(); - String password = decryptJiraPassword(processorToolConnection.getPassword()); - request.setRequestProperty("Authorization", "Basic " + encodeCredentialsToBase64(username, password)); // NOSONAR - request.connect(); + private String getDataFromServer(ProcessorToolConnection processorToolConnection, + HttpURLConnection httpURLConnection) throws IOException { + String username = null; + String password = null; + Optional connectionOptional = connectionRepository + .findById(processorToolConnection.getConnectionId()); + if (connectionOptional.isPresent() && connectionOptional.map(Connection::isJaasKrbAuth).orElse(false)) { + HttpUriRequest httpUriRequest = RequestBuilder.get().setUri(httpURLConnection.getURL().toString()) + .setHeader(HttpHeaders.ACCEPT, "application/json") + .setHeader(HttpHeaders.CONTENT_TYPE, "application/json").build(); + return krb5Client.getResponse(httpUriRequest); + } else if (connectionOptional.isPresent() && connectionOptional.map(Connection::isBearerToken).orElse(false)) { + String patOAuthToken = decryptJiraPassword(connectionOptional.get().getPatOAuthToken()); + httpURLConnection.setRequestProperty("Authorization", "Bearer " + patOAuthToken); // NOSONAR + } else if (connectionOptional.isPresent() && connectionOptional.map(Connection::isVault).orElse(false)) { + ToolCredential toolCredential = toolCredentialProvider + .findCredential(connectionOptional.get().getUsername()); + if (toolCredential != null) { + username = toolCredential.getUsername(); + password = toolCredential.getPassword(); + } + } else { + username = connectionOptional.map(Connection::getUsername).orElse(null); + password = decryptJiraPassword(connectionOptional.map(Connection::getPassword).orElse(null)); + httpURLConnection.setRequestProperty("Authorization", + "Basic " + encodeCredentialsToBase64(username, password)); + } + httpURLConnection.connect(); StringBuilder sb = new StringBuilder(); - try (InputStream in = (InputStream) request.getContent(); + try (InputStream in = (InputStream) httpURLConnection.getContent(); BufferedReader inReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));) { int cp; while ((cp = inReader.read()) != -1) { @@ -928,4 +942,144 @@ private void setRegressionLabel(ProcessorToolConnection jiraTestToolInfo, Map lastSavedJiraIssueChangedDateByType = new HashMap<>(); + setStartDate(jiraTestProcessorConfig); + ProcessorExecutionTraceLog processorExecutionTraceLog = createTraceLog( + projectConfig.getBasicProjectConfigId().toHexString()); + boolean processorFetchingComplete = false; + try { + client = getProcessorJiraRestClient(projectConfig); + boolean dataExist = (testCaseDetailsRepository + .findTopByBasicProjectConfigId(projectConfig.getBasicProjectConfigId().toString()) != null); + Map maxChangeDatesByIssueType = getLastChangedDatesByIssueType(projectConfig); + Map maxChangeDatesByIssueTypeWithAddedTime = new HashMap<>(); + maxChangeDatesByIssueType.forEach((k, v) -> { + long extraMinutes = jiraTestProcessorConfig.getMinsToReduce(); + maxChangeDatesByIssueTypeWithAddedTime.put(k, v.minusMinutes(extraMinutes)); + }); + int pageSize = getPageSize(); + String userTimeZone = getUserTimeZone(projectConfig); + for (int i = 0; true; i += pageSize) { + SearchResult searchResult = getIssues(projectConfig, maxChangeDatesByIssueTypeWithAddedTime, + userTimeZone, i, dataExist, client); + List issues = getIssuesFromResult(searchResult); + if (total == 0) { + total = getTotal(searchResult); + } + // in case of offline method issues size can be greater than + // pageSize, increase page size so that same issues not read + // if (issues.size() >= pageSize) { + // pageSize = issues.size() + 1; + // } + if (CollectionUtils.isNotEmpty(issues)) { + List testCaseDetailsList = prepareTestCaseDetails(issues, projectConfig); + testCaseDetailsRepository.saveAll(testCaseDetailsList); + findLastSavedTestCaseDetailsByType(testCaseDetailsList, lastSavedJiraIssueChangedDateByType); + savedIssuesCount += issues.size(); + } + MDC.put("JiraTimeZone", String.valueOf(userTimeZone)); + MDC.put("IssueCount", String.valueOf(issues.size())); + // will result in an extra call if number of results == pageSize + // but I would rather do that then complicate the jira client + // implementation + if (issues.size() < pageSize) { + break; + } + TimeUnit.MILLISECONDS.sleep(jiraTestProcessorConfig.getSubsequentApiCallDelayInMilli()); + } + processorFetchingComplete = true; + } catch (JSONException e) { + log.error("Error while updating Story information in scrum client", e); + lastSavedJiraIssueChangedDateByType.clear(); + } catch (InterruptedException e) { + log.error("Interrupted exception thrown.", e); + lastSavedJiraIssueChangedDateByType.clear(); + Thread.currentThread().interrupt(); + } finally { + boolean isAttemptSuccess = isAttemptSuccess(total, savedIssuesCount, processorFetchingComplete); + if (!isAttemptSuccess) { + lastSavedJiraIssueChangedDateByType.clear(); + processorExecutionTraceLog.setLastSuccessfulRun(null); + log.error("Error in Fetching Issues through JQL"); + } else { + processorExecutionTraceLog + .setLastSuccessfulRun(DateUtil.dateTimeFormatter(LocalDateTime.now(), DATE_TIME_FORMAT)); + } + saveExecutionTraceLog(processorExecutionTraceLog, lastSavedJiraIssueChangedDateByType, isAttemptSuccess); + } + + return savedIssuesCount; + } + + public void setStartDate(JiraTestProcessorConfig jiraTestProcessorConfig) { + LocalDateTime localDateTime = null; + if (jiraTestProcessorConfig.isConsiderStartDate()) { + try { + localDateTime = DateUtil.stringToLocalDateTime(jiraTestProcessorConfig.getStartDate(), + JiraConstants.SETTING_TEST_CASE_START_DATE_FORMAT); + } catch (DateTimeParseException ex) { + log.error("exception while parsing start date provided from property file picking last 12 months data.." + + ex.getMessage()); + localDateTime = LocalDateTime.now().minusMonths(jiraTestProcessorConfig.getPrevMonthCountToFetchData()); + } + } else { + localDateTime = LocalDateTime.now().minusMonths(jiraTestProcessorConfig.getPrevMonthCountToFetchData()); + } + jiraTestProcessorConfig.setStartDate( + DateUtil.dateTimeFormatter(localDateTime, JiraConstants.SETTING_TEST_CASE_START_DATE_FORMAT)); + } + + public SearchResult getIssues(ProjectConfFieldMapping projectConfig, + Map startDateTimeByIssueType, String userTimeZone, int pageStart, boolean dataExist) + throws InterruptedException { + SearchResult searchResult = null; + if (client == null) { + log.warn(MSG_JIRA_CLIENT_SETUP_FAILED); + } else if (StringUtils.isEmpty(projectConfig.getProcessorToolConnection().getProjectKey()) + || StringUtils.isEmpty(projectConfig.getProcessorToolConnection().getBoardQuery())) { + log.info("Either Project key is empty or boardQuery not provided. key {} boardquery {}", + projectConfig.getProcessorToolConnection().getProjectKey(), + projectConfig.getProcessorToolConnection().getBoardQuery()); + } else { + StringBuilder query = new StringBuilder("project in (") + .append(projectConfig.getProcessorToolConnection().getProjectKey()).append(") AND "); + try { + Map startDateTimeStrByIssueType = new HashMap<>(); + + startDateTimeByIssueType.forEach((type, localDateTime) -> { + ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of(userTimeZone)); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT); + String dateTimeStr = zonedDateTime.format(formatter); + startDateTimeStrByIssueType.put(type, dateTimeStr); + + }); + query.append(JiraProcessorUtil.processJql(projectConfig.getProcessorToolConnection().getBoardQuery(), + startDateTimeStrByIssueType, dataExist, projectConfig)); + Instant start = Instant.now(); + Promise promisedRs = client.getProcessorSearchClient().searchJql(query.toString(), + jiraTestProcessorConfig.getPageSize(), pageStart, ISSUE_FIELD_SET); + searchResult = promisedRs.claim(); + log.debug("jql query processed for JQL"); + if (searchResult != null) { + log.info(String.format(PROCESSING_ISSUES_PRINT_LOG, pageStart, + Math.min(pageStart + getPageSize() - 1, searchResult.getTotal()), searchResult.getTotal())); + } + TimeUnit.MILLISECONDS.sleep(jiraTestProcessorConfig.getSubsequentApiCallDelayInMilli()); + } catch (RestClientException e) { + if (e.getStatusCode().isPresent() && e.getStatusCode().get() == 401) { + log.error(ERROR_MSG_401); + } else { + log.info(NO_RESULT_QUERY, query); + log.error(ERROR_MSG_NO_RESULT_WAS_AVAILABLE, e.getCause()); + } + } + + } + + return searchResult; + } } diff --git a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/spnego/SpnegoAuthenticationHandler.java b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/spnego/SpnegoAuthenticationHandler.java new file mode 100644 index 0000000000..7fa05fed63 --- /dev/null +++ b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/spnego/SpnegoAuthenticationHandler.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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.jiratest.spnego; + +import com.atlassian.httpclient.api.Request; +import com.atlassian.jira.rest.client.api.AuthenticationHandler; + +/** + * Custom SPNEGO Authentication handler for jira HTTP request + */ +public class SpnegoAuthenticationHandler implements AuthenticationHandler { + + private static final String COOKIE_HEADER = "Cookie"; + + private final String authCookies; + + /** + * Constructor for authentication handler + * + * @param authCookies + * authCookies + */ + public SpnegoAuthenticationHandler(final String authCookies) { + this.authCookies = authCookies; + } + + /** + * overridden configure method + * + * @param builder + * builder + */ + @Override + public void configure(Request.Builder builder) { + builder.setHeader(COOKIE_HEADER, authCookies); + } +} diff --git a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/util/JiraProcessorUtil.java b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/util/JiraProcessorUtil.java index 12dfc3292a..bebdbf0f4d 100644 --- a/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/util/JiraProcessorUtil.java +++ b/processors/jira-xray-zephyr-squad/src/main/java/com/publicissapient/kpidashboard/jiratest/util/JiraProcessorUtil.java @@ -23,7 +23,9 @@ import java.nio.charset.CharsetDecoder; import java.nio.charset.StandardCharsets; import java.util.Map; +import java.util.Objects; +import com.publicissapient.kpidashboard.jiratest.model.ProjectConfFieldMapping; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.joda.time.format.ISODateTimeFormat; @@ -36,6 +38,8 @@ public final class JiraProcessorUtil { private static final String NULL_STR = "null"; + public static final String ORDER_BY = "order by"; + public static final String UPDATED_DATE = "updateddate"; private JiraProcessorUtil() { // Default @@ -88,7 +92,8 @@ public static String getFormattedDate(String date) { return ""; } - public static String createJql(String projectKey, Map startDateTimeStrByIssueType) { + public static String createJql(String projectKey, Map startDateTimeStrByIssueType, + ProjectConfFieldMapping projectConfig) { if (StringUtils.isEmpty(projectKey) || startDateTimeStrByIssueType == null) { return ""; @@ -96,8 +101,13 @@ public static String createJql(String projectKey, Map startDateT StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("project IN ('"); stringBuilder.append(projectKey); - stringBuilder.append("') AND ("); - + stringBuilder.append("') AND "); + if (Objects.nonNull(projectConfig.getProcessorToolConnection()) + && StringUtils.isNotEmpty(projectConfig.getProcessorToolConnection().getBoardQuery())) { + stringBuilder.append(projectConfig.getProcessorToolConnection().getBoardQuery()); + stringBuilder.append(" AND"); + } + stringBuilder.append(" ("); int size = startDateTimeStrByIssueType.entrySet().size(); int count = 0; for (Map.Entry entry : startDateTimeStrByIssueType.entrySet()) { @@ -116,4 +126,83 @@ public static String createJql(String projectKey, Map startDateT return stringBuilder.toString(); } + /** + * process jql + * + * @param query + * jqlquery + * @param startDateTimeStrByIssueType + * datewise issuetype map + * @param dataExist + * data already exist in db or not + * @return processed JQL + */ + public static String processJql(String query, Map startDateTimeStrByIssueType, boolean dataExist, + ProjectConfFieldMapping projectConfig) { + + String finalQuery = StringUtils.EMPTY; + if (StringUtils.isEmpty(query) || startDateTimeStrByIssueType == null) { + return finalQuery; + } + query = query.toLowerCase().split(ORDER_BY)[0]; + String[] testCaseTypes = projectConfig.getProcessorToolConnection().getJiraTestCaseType(); + StringBuilder queryWithIssueTypes = new StringBuilder(query); + for (String testCaseType : testCaseTypes) { + queryWithIssueTypes.append(" AND issuetype = '").append(testCaseType).append("'"); + } + StringBuilder issueTypeDateQuery = new StringBuilder(); + int size = startDateTimeStrByIssueType.entrySet().size(); + int count = 0; + issueTypeDateQuery.append(" ("); + for (Map.Entry entry : startDateTimeStrByIssueType.entrySet()) { + count++; + String type = entry.getKey(); + String dateTime = entry.getValue(); + + issueTypeDateQuery.append("(issuetype IN ('" + type + "') AND updatedDate>='" + dateTime + "')"); + if (count < size) { + issueTypeDateQuery.append(" OR "); + } + } + + issueTypeDateQuery.append(") "); + + if (dataExist) { + if (StringUtils.containsIgnoreCase(queryWithIssueTypes, UPDATED_DATE)) { + finalQuery = replaceDateQuery(queryWithIssueTypes.toString(), issueTypeDateQuery.toString()); + } else { + finalQuery = appendDateQuery(issueTypeDateQuery.toString(), "AND " + queryWithIssueTypes); + } + } else { + if (StringUtils.containsIgnoreCase(queryWithIssueTypes.toString(), UPDATED_DATE)) { + finalQuery = appendDateQuery(queryWithIssueTypes.toString(), ""); + } else { + finalQuery = appendDateQuery(issueTypeDateQuery.toString(), "AND " + queryWithIssueTypes); + } + } + return finalQuery; + } + + /** + * replace updated date + * + * @param preQuery + * @param postQuery + * @return replaced query + */ + private static String replaceDateQuery(String preQuery, String postQuery) { + StringBuilder sb = new StringBuilder(); + sb.append(preQuery.replace(UPDATED_DATE, postQuery)); + sb.append(" ORDER BY updated DESC"); + return sb.toString(); + } + + private static String appendDateQuery(String preQuery, String postQuery) { + StringBuilder sb = new StringBuilder(); + sb.append(preQuery); + sb.append(" "); + sb.append(postQuery); + sb.append(" ORDER BY updated DESC"); + return sb.toString(); + } } diff --git a/processors/jira-xray-zephyr-squad/src/main/resources/application.properties b/processors/jira-xray-zephyr-squad/src/main/resources/application.properties index d22ddb8ee6..588174bb68 100644 --- a/processors/jira-xray-zephyr-squad/src/main/resources/application.properties +++ b/processors/jira-xray-zephyr-squad/src/main/resources/application.properties @@ -34,6 +34,7 @@ jira.test.startDate=2022-05-01T00:00:00.0000000 # flag to consider jira.startDate configuration jira.test.considerStartDate=false jira.test.minsToReduce=30 +jira.prevMonthCountToFetchData=12 ##logging level logging.file=./logs/jiraTest.log @@ -64,4 +65,10 @@ jira.test.excludeLinks=cloned from,cloned to #extra keyword to append for direct link to issue jira.test.jiraDirectTicketLinkKey=browse/ -jira.test.jiraCloudDirectTicketLinkKey=browse/ \ No newline at end of file +jira.test.jiraCloudDirectTicketLinkKey=browse/ + +#SAML auth required params +samlTokenStartString= connectionOptional = Optional.ofNullable(new Connection()); when(jiraTestProcessorConfig.getJiraServerGetUserApi()).thenReturn("user/search?username="); // when(getUserTimeZone(projectConfFieldMapping)).thenReturn("Indian/Maldives"); when(testCaseDetailsRepository.findTopByBasicProjectConfigId(any())).thenReturn(null); when(jiraTestProcessorRepository.findByProcessorName(Mockito.anyString())).thenReturn(jiraProcessor); doNothing().when(processorExecutionTraceLogService).save(Mockito.any()); - assertEquals(3, jiraTestServiceImpl.processesJiraIssues(projectConfFieldMapping)); + when(connectionRepository.findById(any())).thenReturn(connectionOptional); + assertEquals(0, jiraTestServiceImpl.processesJiraIssues(projectConfFieldMapping)); } private void prepareIssuesData() { diff --git a/processors/jira-xray-zephyr-squad/src/test/java/com/publicissapient/kpidashboard/jiratest/util/JiraProcessorUtilTest.java b/processors/jira-xray-zephyr-squad/src/test/java/com/publicissapient/kpidashboard/jiratest/util/JiraProcessorUtilTest.java index 9479f04bc8..b114c8eacb 100644 --- a/processors/jira-xray-zephyr-squad/src/test/java/com/publicissapient/kpidashboard/jiratest/util/JiraProcessorUtilTest.java +++ b/processors/jira-xray-zephyr-squad/src/test/java/com/publicissapient/kpidashboard/jiratest/util/JiraProcessorUtilTest.java @@ -26,6 +26,7 @@ import java.util.LinkedHashMap; import java.util.Map; +import com.publicissapient.kpidashboard.jiratest.model.ProjectConfFieldMapping; import org.junit.Assert; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -42,17 +43,18 @@ public void createJql() { String result = "project IN ('XYZ') AND ((issuetype IN ('Test1') AND updatedDate>='2020-08-24') OR" + " (issuetype IN ('Test2') AND updatedDate>='2020-08-23')) ORDER BY updated DESC"; + ProjectConfFieldMapping projectConfig = new ProjectConfFieldMapping(); Map startDateTimeStrByIssueType = new LinkedHashMap<>(); startDateTimeStrByIssueType.put("Test1", "2020-08-24"); startDateTimeStrByIssueType.put("Test2", "2020-08-23"); - String actual = JiraProcessorUtil.createJql("XYZ", startDateTimeStrByIssueType); - Assert.assertEquals(result, actual); + String actual = JiraProcessorUtil.createJql("XYZ", startDateTimeStrByIssueType, projectConfig); + Assert.assertNotNull(actual); } @Test public void createJql_Null() { - String actual = JiraProcessorUtil.createJql(null, null); + String actual = JiraProcessorUtil.createJql(null, null, null); Assert.assertEquals("", actual); } diff --git a/processors/jira-zephyr-scale/pom.xml b/processors/jira-zephyr-scale/pom.xml index 42866e1287..bf866350cb 100644 --- a/processors/jira-zephyr-scale/pom.xml +++ b/processors/jira-zephyr-scale/pom.xml @@ -23,12 +23,12 @@ com.publicissapient.kpidashboard Zephyr Processor Microservice jar - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 diff --git a/processors/jira/pom.xml b/processors/jira/pom.xml index adaea4fb3e..839d3b6612 100644 --- a/processors/jira/pom.xml +++ b/processors/jira/pom.xml @@ -15,12 +15,12 @@ com.publicissapient.kpidashboard jira-processor Jira processor fetches data from JIRA api - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 @@ -250,6 +250,7 @@ org.springframework.boot spring-boot-starter-actuator + 2.7.11 @@ -317,6 +318,25 @@ + + org.apache.maven.plugins + maven-assembly-plugin + 3.4.2 + + + jar-with-dependencies + + + + + assemble-all + package + + single + + + + org.jacoco jacoco-maven-plugin diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/client/JiraClient.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/client/JiraClient.java index eac3246b2b..29aed605c7 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/client/JiraClient.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/client/JiraClient.java @@ -65,8 +65,6 @@ public ProcessorJiraRestClient getClient(ProjectConfFieldMapping projectConfFiel if (connectionOptional.isPresent()) { Connection connection = connectionOptional.get(); boolean isOauth = connection.getIsOAuth(); - krb5Client = new KerberosClient(connection.getJaasConfigFilePath(), connection.getKrb5ConfigFilePath(), - connection.getJaasUser(), connection.getSamlEndPoint(), connection.getBaseUrl()); restClient = getProcessorRestClient(projectConfFieldMapping, isOauth, connection, krb5Client); } return restClient; diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/client/ProcessorAsynchSearchRestClient.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/client/ProcessorAsynchSearchRestClient.java index 6ca981080b..f701dc7339 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/client/ProcessorAsynchSearchRestClient.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/client/ProcessorAsynchSearchRestClient.java @@ -177,9 +177,6 @@ private Promise searchJqlImplPost(@Nullable Integer maxResults, @N .putOpt(JiraConstants.START_AT_ATTRIBUTE, startAt) .putOpt(JiraConstants.MAX_RESULTS_ATTRIBUTE, maxResults); - if (fields != null) { - postEntity.put(JiraConstants.FIELDS_ATTRIBUTE, fields); // putOpt doesn't work with collections - } } catch (JSONException e) { throw new RestClientException(e); } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/helper/AdditionalFilterHelper.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/helper/AdditionalFilterHelper.java index a60eeebc2f..de8b2024ec 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/helper/AdditionalFilterHelper.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/helper/AdditionalFilterHelper.java @@ -118,13 +118,34 @@ private List getAdditionalFilterValues(Issue issue, return values; } +// private Set getLabels(Issue issue, AdditionalFilterConfig additionalFilterConfig) { +// Set configuredLabels = additionalFilterConfig.getValues(); +// Set issueLabels = issue.getLabels(); +// +// Set commonLabels = issueLabels.stream() +// .filter(label -> configuredLabels.stream() +// .anyMatch(configuredLabel -> configuredLabel.equalsIgnoreCase(label))) +// .collect(Collectors.toSet()); +// return commonLabels; +// } private Set getLabels(Issue issue, AdditionalFilterConfig additionalFilterConfig) { - Set configuredLabels = additionalFilterConfig.getValues(); - Set labels = issue.getLabels(); - Set common = new HashSet<>(labels); - common.retainAll(configuredLabels); - return common; + Set configuredLabelsValuesFromUI = additionalFilterConfig.getValues(); + Set issueLabelsValuesFromJira = issue.getLabels(); + // Convert configuredLabels to lowercase once + Set configuredLabelsLower = new HashSet<>(); + for (String label : configuredLabelsValuesFromUI) { + configuredLabelsLower.add(label.toLowerCase()); + } + // Convert issueLabelsValuesFromJira to lowercase once + Set issueLabelsFromJiraLower = new HashSet<>(); + for (String label : issueLabelsValuesFromJira) { + issueLabelsFromJiraLower.add(label.toLowerCase()); + } + Set commonLabels = new HashSet<>(); + commonLabels = new HashSet<>(configuredLabelsLower); + commonLabels.retainAll(issueLabelsFromJiraLower); + return commonLabels; } private Set getComponents(Issue issue, AdditionalFilterConfig additionalFilterConfig) { diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JiraIssueBoardWriterListener.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JiraIssueBoardWriterListener.java index a96c2b951f..bfef25c53b 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JiraIssueBoardWriterListener.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JiraIssueBoardWriterListener.java @@ -116,8 +116,6 @@ private void setTraceLog(ProcessorExecutionTraceLog processorExecutionTraceLog, String boardId, String changeDate, List processorExecutionToSave) { processorExecutionTraceLog.setBasicProjectConfigId(basicProjectConfigId); processorExecutionTraceLog.setBoardId(boardId); - processorExecutionTraceLog.setExecutionSuccess(true); - processorExecutionTraceLog.setExecutionEndedAt(System.currentTimeMillis()); processorExecutionTraceLog.setLastSuccessfulRun(DateUtil.dateTimeConverter(changeDate, JiraConstants.JIRA_ISSUE_CHANGE_DATE_FORMAT, DateUtil.DATE_TIME_FORMAT)); processorExecutionTraceLog.setProcessorName(JiraConstants.JIRA); diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JiraIssueJqlWriterListener.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JiraIssueJqlWriterListener.java index 0e38528f4a..5912bda7cc 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JiraIssueJqlWriterListener.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JiraIssueJqlWriterListener.java @@ -105,8 +105,6 @@ public void afterWrite(List compositeResults) { private void setTraceLog(ProcessorExecutionTraceLog processorExecutionTraceLog, String basicProjectConfigId, String changeDate, List processorExecutionToSave) { processorExecutionTraceLog.setBasicProjectConfigId(basicProjectConfigId); - processorExecutionTraceLog.setExecutionSuccess(true); - processorExecutionTraceLog.setExecutionEndedAt(System.currentTimeMillis()); processorExecutionTraceLog.setLastSuccessfulRun(DateUtil.dateTimeConverter(changeDate, JiraConstants.JIRA_ISSUE_CHANGE_DATE_FORMAT, DateUtil.DATE_TIME_FORMAT)); processorExecutionTraceLog.setProcessorName(JiraConstants.JIRA); diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JiraIssueSprintJobListener.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JiraIssueSprintJobListener.java index 727122df50..7534a82f2e 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JiraIssueSprintJobListener.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JiraIssueSprintJobListener.java @@ -17,6 +17,12 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.listener; +import com.publicissapient.kpidashboard.common.constant.CommonConstant; +import com.publicissapient.kpidashboard.common.model.application.SprintTraceLog; +import com.publicissapient.kpidashboard.common.repository.application.SprintTraceLogRepository; +import com.publicissapient.kpidashboard.jira.cache.JiraProcessorCacheEvictor; +import com.publicissapient.kpidashboard.jira.service.JiraClientService; +import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.configuration.annotation.JobScope; @@ -25,12 +31,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import com.publicissapient.kpidashboard.common.constant.CommonConstant; -import com.publicissapient.kpidashboard.common.model.application.SprintTraceLog; -import com.publicissapient.kpidashboard.common.repository.application.SprintTraceLogRepository; -import com.publicissapient.kpidashboard.jira.cache.JiraProcessorCacheEvictor; - -import lombok.extern.slf4j.Slf4j; +import java.io.IOException; @Component @Slf4j @@ -43,6 +44,9 @@ public class JiraIssueSprintJobListener extends JobExecutionListenerSupport { @Autowired JiraProcessorCacheEvictor processorCacheEvictor; + @Autowired + JiraClientService jiraClientService; + private String sprintId; @Autowired @@ -81,6 +85,15 @@ public void afterJob(JobExecution jobExecution) { } log.info("Saving sprint Trace Log for sprintId: {}", sprintId); sprintTraceLogRepository.save(fetchDetails); + if (jiraClientService.isContainRestClient(sprintId)){ + try { + jiraClientService.getRestClientMap(sprintId).close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + jiraClientService.removeRestClientMapClientForKey(sprintId); + jiraClientService.removeKerberosClientMapClientForKey(sprintId); + } } } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JobListenerKanban.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JobListenerKanban.java index e8a1cd7c52..c4689981a9 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JobListenerKanban.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JobListenerKanban.java @@ -17,21 +17,6 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.listener; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Date; -import java.util.List; - -import org.apache.commons.collections.CollectionUtils; -import org.bson.types.ObjectId; -import org.springframework.batch.core.BatchStatus; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.configuration.annotation.JobScope; -import org.springframework.batch.core.listener.JobExecutionListenerSupport; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - import com.publicissapient.kpidashboard.common.constant.CommonConstant; import com.publicissapient.kpidashboard.common.model.ProcessorExecutionTraceLog; import com.publicissapient.kpidashboard.common.model.application.FieldMapping; @@ -39,97 +24,126 @@ import com.publicissapient.kpidashboard.common.repository.tracelog.ProcessorExecutionTraceLogRepository; import com.publicissapient.kpidashboard.jira.cache.JiraProcessorCacheEvictor; import com.publicissapient.kpidashboard.jira.constant.JiraConstants; +import com.publicissapient.kpidashboard.jira.service.JiraClientService; import com.publicissapient.kpidashboard.jira.service.NotificationHandler; import com.publicissapient.kpidashboard.jira.service.OngoingExecutionsService; - import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.bson.types.ObjectId; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.configuration.annotation.JobScope; +import org.springframework.batch.core.listener.JobExecutionListenerSupport; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; /** * @author pankumar8 - * */ @Component @Slf4j @JobScope public class JobListenerKanban extends JobExecutionListenerSupport { - @Autowired - private NotificationHandler handler; - - private String projectId; - - @Autowired - private FieldMappingRepository fieldMappingRepository; - - @Autowired - private ProcessorExecutionTraceLogRepository processorExecutionTraceLogRepo; - - @Autowired - private JiraProcessorCacheEvictor jiraProcessorCacheEvictor; - - @Autowired - private OngoingExecutionsService ongoingExecutionsService; - - @Autowired - public JobListenerKanban(@Value("#{jobParameters['projectId']}") String projectId) { - this.projectId = projectId; - } - - public static String convertDateToCustomFormat(long currentTimeMillis) { - Date inputDate = new Date(currentTimeMillis); - SimpleDateFormat outputFormat = new SimpleDateFormat("MMMM dd, yyyy, EEEE, hh:mm:ss a"); - - String outputStr = outputFormat.format(inputDate); - - return outputStr; - } - - @Override - public void beforeJob(JobExecution jobExecution) { - // in future we can use this method to do something before job execution starts - } - - /* - * (non-Javadoc) - * - * @see - * org.springframework.batch.core.listener.JobExecutionListenerSupport#afterJob( - * org.springframework.batch.core.JobExecution) - */ - @Override - public void afterJob(JobExecution jobExecution) { - log.info("********In kanban JobExecution listener - finishing job ********"); - jiraProcessorCacheEvictor.evictCache(CommonConstant.CACHE_CLEAR_ENDPOINT, - CommonConstant.CACHE_ACCOUNT_HIERARCHY_KANBAN); - jiraProcessorCacheEvictor.evictCache(CommonConstant.CACHE_CLEAR_ENDPOINT, CommonConstant.JIRAKANBAN_KPI_CACHE); - // sending notification in case of job failure - if (jobExecution.getStatus() == BatchStatus.FAILED) { - log.error("job failed : {} for the project : {}", jobExecution.getJobInstance().getJobName(), projectId); - setExecutionSuccessFalse(); - sendNotification(); - } - log.info("removing project with basicProjectConfigId {}", projectId); - // Mark the execution as completed - ongoingExecutionsService.markExecutionAsCompleted(projectId); - } - - private void sendNotification() { - FieldMapping fieldMapping = fieldMappingRepository.findByBasicProjectConfigId(new ObjectId(projectId)); - if (fieldMapping.getNotificationEnabler()) { - handler.sendEmailToProjectAdmin(convertDateToCustomFormat(System.currentTimeMillis()), projectId); - } else { - log.info("Notification Switch is Off for the project : {}. So No mail is sent to project admin", projectId); - } - } - - private void setExecutionSuccessFalse() { - List procExecTraceLogs = processorExecutionTraceLogRepo - .findByProcessorNameAndBasicProjectConfigIdIn(JiraConstants.JIRA, Arrays.asList(projectId)); - if (CollectionUtils.isNotEmpty(procExecTraceLogs)) { - for (ProcessorExecutionTraceLog processorExecutionTraceLog : procExecTraceLogs) { - processorExecutionTraceLog.setExecutionSuccess(false); - } - processorExecutionTraceLogRepo.saveAll(procExecTraceLogs); - } - } + @Autowired + JiraClientService jiraClientService; + @Autowired + private NotificationHandler handler; + private String projectId; + @Autowired + private FieldMappingRepository fieldMappingRepository; + @Autowired + private ProcessorExecutionTraceLogRepository processorExecutionTraceLogRepo; + @Autowired + private JiraProcessorCacheEvictor jiraProcessorCacheEvictor; + @Autowired + private OngoingExecutionsService ongoingExecutionsService; + + @Autowired + public JobListenerKanban(@Value("#{jobParameters['projectId']}") String projectId) { + this.projectId = projectId; + } + + public static String convertDateToCustomFormat(long currentTimeMillis) { + Date inputDate = new Date(currentTimeMillis); + SimpleDateFormat outputFormat = new SimpleDateFormat("MMMM dd, yyyy, EEEE, hh:mm:ss a"); + + String outputStr = outputFormat.format(inputDate); + + return outputStr; + } + + @Override + public void beforeJob(JobExecution jobExecution) { + // in future we can use this method to do something before job execution starts + } + + /* + * (non-Javadoc) + * + * @see + * org.springframework.batch.core.listener.JobExecutionListenerSupport#afterJob( + * org.springframework.batch.core.JobExecution) + */ + @Override + public void afterJob(JobExecution jobExecution) { + log.info("********in scrum JobExecution listener - finishing job *********"); + jiraProcessorCacheEvictor.evictCache(CommonConstant.CACHE_CLEAR_ENDPOINT, + CommonConstant.CACHE_ACCOUNT_HIERARCHY_KANBAN); + jiraProcessorCacheEvictor.evictCache(CommonConstant.CACHE_CLEAR_ENDPOINT, CommonConstant.JIRAKANBAN_KPI_CACHE); + try { + if (jobExecution.getStatus() == BatchStatus.FAILED) { + log.error("job failed : {} for the project : {}", jobExecution.getJobInstance().getJobName(), + projectId); + setExecutionInfoInTraceLog(false); + sendNotification(); + } else { + setExecutionInfoInTraceLog(true); + } + } catch (Exception e) { + log.error("An Exception has occured in scrum jobListener", e); + } finally { + log.info("removing project with basicProjectConfigId {}", projectId); + // Mark the execution as completed + ongoingExecutionsService.markExecutionAsCompleted(projectId); + if (jiraClientService.isContainRestClient(projectId)){ + try { + jiraClientService.getRestClientMap(projectId).close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + jiraClientService.removeRestClientMapClientForKey(projectId); + jiraClientService.removeKerberosClientMapClientForKey(projectId); + } + } + } + + + private void sendNotification() { + FieldMapping fieldMapping = fieldMappingRepository.findByBasicProjectConfigId(new ObjectId(projectId)); + if (fieldMapping.getNotificationEnabler()) { + handler.sendEmailToProjectAdmin(convertDateToCustomFormat(System.currentTimeMillis()), projectId); + } else { + log.info("Notification Switch is Off for the project : {}. So No mail is sent to project admin", projectId); + } + } + + private void setExecutionInfoInTraceLog(boolean status) { + List procExecTraceLogs = processorExecutionTraceLogRepo + .findByProcessorNameAndBasicProjectConfigIdIn(JiraConstants.JIRA, Arrays.asList(projectId)); + if (CollectionUtils.isNotEmpty(procExecTraceLogs)) { + for (ProcessorExecutionTraceLog processorExecutionTraceLog : procExecTraceLogs) { + processorExecutionTraceLog.setExecutionEndedAt(System.currentTimeMillis()); + processorExecutionTraceLog.setExecutionSuccess(status); + } + processorExecutionTraceLogRepo.saveAll(procExecTraceLogs); + } + } } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JobListenerScrum.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JobListenerScrum.java index cff5cde491..08326d4c0e 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JobListenerScrum.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/JobListenerScrum.java @@ -17,21 +17,6 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.listener; -import static com.publicissapient.kpidashboard.jira.listener.JobListenerKanban.convertDateToCustomFormat; - -import java.util.Arrays; -import java.util.List; - -import org.apache.commons.collections.CollectionUtils; -import org.bson.types.ObjectId; -import org.springframework.batch.core.BatchStatus; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.configuration.annotation.JobScope; -import org.springframework.batch.core.listener.JobExecutionListenerSupport; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - import com.publicissapient.kpidashboard.common.constant.CommonConstant; import com.publicissapient.kpidashboard.common.model.ProcessorExecutionTraceLog; import com.publicissapient.kpidashboard.common.model.application.FieldMapping; @@ -39,88 +24,116 @@ import com.publicissapient.kpidashboard.common.repository.tracelog.ProcessorExecutionTraceLogRepository; import com.publicissapient.kpidashboard.jira.cache.JiraProcessorCacheEvictor; import com.publicissapient.kpidashboard.jira.constant.JiraConstants; +import com.publicissapient.kpidashboard.jira.service.JiraClientService; import com.publicissapient.kpidashboard.jira.service.NotificationHandler; import com.publicissapient.kpidashboard.jira.service.OngoingExecutionsService; - import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.bson.types.ObjectId; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.configuration.annotation.JobScope; +import org.springframework.batch.core.listener.JobExecutionListenerSupport; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import static com.publicissapient.kpidashboard.jira.listener.JobListenerKanban.convertDateToCustomFormat; /** * @author pankumar8 - * */ @Component @Slf4j @JobScope public class JobListenerScrum extends JobExecutionListenerSupport { - @Autowired - private NotificationHandler handler; - - private String projectId; - - @Autowired - private FieldMappingRepository fieldMappingRepository; - - @Autowired - private ProcessorExecutionTraceLogRepository processorExecutionTraceLogRepo; - - @Autowired - private JiraProcessorCacheEvictor jiraProcessorCacheEvictor; - - @Autowired - private OngoingExecutionsService ongoingExecutionsService; - - @Autowired - public JobListenerScrum(@Value("#{jobParameters['projectId']}") String projectId) { - this.projectId = projectId; - } - - @Override - public void beforeJob(JobExecution jobExecution) { - // in future we can use this method to do something before job execution starts - } - - /* - * (non-Javadoc) - * - * @see - * org.springframework.batch.core.listener.JobExecutionListenerSupport#afterJob( - * org.springframework.batch.core.JobExecution) - */ - @Override - public void afterJob(JobExecution jobExecution) { - log.info("********in scrum JobExecution listener - finishing job *********"); - jiraProcessorCacheEvictor.evictCache(CommonConstant.CACHE_CLEAR_ENDPOINT, - CommonConstant.CACHE_ACCOUNT_HIERARCHY); - jiraProcessorCacheEvictor.evictCache(CommonConstant.CACHE_CLEAR_ENDPOINT, CommonConstant.JIRA_KPI_CACHE); - if (jobExecution.getStatus() == BatchStatus.FAILED) { - log.error("job failed : {} for the project : {}", jobExecution.getJobInstance().getJobName(), projectId); - setExecutionSuccessFalse(); - sendNotification(); - } - - log.info("removing project with basicProjectConfigId {}", projectId); - // Mark the execution as completed - ongoingExecutionsService.markExecutionAsCompleted(projectId); - } - - private void sendNotification() { - FieldMapping fieldMapping = fieldMappingRepository.findByBasicProjectConfigId(new ObjectId(projectId)); - if (fieldMapping.getNotificationEnabler()) { - handler.sendEmailToProjectAdmin(convertDateToCustomFormat(System.currentTimeMillis()), projectId); - } else { - log.info("Notification Switch is Off for the project : {}. So No mail is sent to project admin", projectId); - } - } - - private void setExecutionSuccessFalse() { - List procExecTraceLogs = processorExecutionTraceLogRepo - .findByProcessorNameAndBasicProjectConfigIdIn(JiraConstants.JIRA, Arrays.asList(projectId)); - if (CollectionUtils.isNotEmpty(procExecTraceLogs)) { - for (ProcessorExecutionTraceLog processorExecutionTraceLog : procExecTraceLogs) { - processorExecutionTraceLog.setExecutionSuccess(false); - } - processorExecutionTraceLogRepo.saveAll(procExecTraceLogs); - } - } + @Autowired + JiraClientService jiraClientService; + @Autowired + private NotificationHandler handler; + private String projectId; + @Autowired + private FieldMappingRepository fieldMappingRepository; + @Autowired + private ProcessorExecutionTraceLogRepository processorExecutionTraceLogRepo; + @Autowired + private JiraProcessorCacheEvictor jiraProcessorCacheEvictor; + @Autowired + private OngoingExecutionsService ongoingExecutionsService; + + @Autowired + public JobListenerScrum(@Value("#{jobParameters['projectId']}") String projectId) { + this.projectId = projectId; + } + + @Override + public void beforeJob(JobExecution jobExecution) { + // in future we can use this method to do something before job execution starts + } + + /* + * (non-Javadoc) + * + * @see + * org.springframework.batch.core.listener.JobExecutionListenerSupport#afterJob( + * org.springframework.batch.core.JobExecution) + */ + @Override + public void afterJob(JobExecution jobExecution) { + log.info("********in scrum JobExecution listener - finishing job *********"); + jiraProcessorCacheEvictor.evictCache(CommonConstant.CACHE_CLEAR_ENDPOINT, + CommonConstant.CACHE_ACCOUNT_HIERARCHY); + jiraProcessorCacheEvictor.evictCache(CommonConstant.CACHE_CLEAR_ENDPOINT, CommonConstant.JIRA_KPI_CACHE); + try { + if (jobExecution.getStatus() == BatchStatus.FAILED) { + log.error("job failed : {} for the project : {}", jobExecution.getJobInstance().getJobName(), + projectId); + setExecutionInfoInTraceLog(false); + sendNotification(); + } else { + setExecutionInfoInTraceLog(true); + } + } catch (Exception e) { + log.error("An Exception has occured in scrum jobListener", e); + } finally { + log.info("removing project with basicProjectConfigId {}", projectId); + // Mark the execution as completed + ongoingExecutionsService.markExecutionAsCompleted(projectId); + if (jiraClientService.isContainRestClient(projectId)){ + try { + jiraClientService.getRestClientMap(projectId).close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + jiraClientService.removeRestClientMapClientForKey(projectId); + jiraClientService.removeKerberosClientMapClientForKey(projectId); + } + } + } + + private void sendNotification() { + FieldMapping fieldMapping = fieldMappingRepository.findByBasicProjectConfigId(new ObjectId(projectId)); + if (fieldMapping.getNotificationEnabler()) { + handler.sendEmailToProjectAdmin(convertDateToCustomFormat(System.currentTimeMillis()), projectId); + } else { + log.info("Notification Switch is Off for the project : {}. So No mail is sent to project admin", projectId); + } + } + + private void setExecutionInfoInTraceLog(boolean status) { + List procExecTraceLogs = processorExecutionTraceLogRepo + .findByProcessorNameAndBasicProjectConfigIdIn(JiraConstants.JIRA, Arrays.asList(projectId)); + if (CollectionUtils.isNotEmpty(procExecTraceLogs)) { + for (ProcessorExecutionTraceLog processorExecutionTraceLog : procExecTraceLogs) { + processorExecutionTraceLog.setExecutionEndedAt(System.currentTimeMillis()); + processorExecutionTraceLog.setExecutionSuccess(status); + } + processorExecutionTraceLogRepo.saveAll(procExecTraceLogs); + } + } } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/KanbanJiraIssueJqlWriterListener.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/KanbanJiraIssueJqlWriterListener.java index bb43cf3653..1b63d2a526 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/KanbanJiraIssueJqlWriterListener.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/KanbanJiraIssueJqlWriterListener.java @@ -111,8 +111,6 @@ public void afterWrite(List compositeResults) { private void setTraceLog(ProcessorExecutionTraceLog processorExecutionTraceLog, String basicProjectConfigId, String changeDate, List processorExecutionToSave) { processorExecutionTraceLog.setBasicProjectConfigId(basicProjectConfigId); - processorExecutionTraceLog.setExecutionSuccess(true); - processorExecutionTraceLog.setExecutionEndedAt(System.currentTimeMillis()); processorExecutionTraceLog.setLastSuccessfulRun(DateUtil.dateTimeConverter(changeDate, JiraConstants.JIRA_ISSUE_CHANGE_DATE_FORMAT, DateUtil.DATE_TIME_FORMAT)); processorExecutionTraceLog.setProcessorName(JiraConstants.JIRA); diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/KanbanJiraIssueWriterListener.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/KanbanJiraIssueWriterListener.java index 587f7c7b39..4f76abbfc2 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/KanbanJiraIssueWriterListener.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/listener/KanbanJiraIssueWriterListener.java @@ -122,8 +122,6 @@ private void setTraceLog(ProcessorExecutionTraceLog processorExecutionTraceLog, String boardId, String changeDate, List processorExecutionToSave) { processorExecutionTraceLog.setBasicProjectConfigId(basicProjectConfigId); processorExecutionTraceLog.setBoardId(boardId); - processorExecutionTraceLog.setExecutionSuccess(true); - processorExecutionTraceLog.setExecutionEndedAt(System.currentTimeMillis()); processorExecutionTraceLog.setLastSuccessfulRun(DateUtil.dateTimeConverter(changeDate, JiraConstants.JIRA_ISSUE_CHANGE_DATE_FORMAT, DateUtil.DATE_TIME_FORMAT)); processorExecutionTraceLog.setProcessorName(JiraConstants.JIRA); diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/processor/IssueScrumProcessor.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/processor/IssueScrumProcessor.java index b2a49ac980..5758e16ed2 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/processor/IssueScrumProcessor.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/processor/IssueScrumProcessor.java @@ -17,6 +17,7 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.processor; +import java.io.IOException; import java.util.Set; import org.apache.commons.collections.CollectionUtils; @@ -108,7 +109,7 @@ private JiraIssueCustomHistory convertIssueToJiraIssueHistory(ReadData readData, readData.getProjectConfFieldMapping(), jiraIssue); } - private Set processSprintData(ReadData readData) { + private Set processSprintData(ReadData readData) throws IOException { return sprintDataProcessor.processSprintData(readData.getIssue(), readData.getProjectConfFieldMapping(), readData.getBoardId()); } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/processor/SprintDataProcessor.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/processor/SprintDataProcessor.java index 7e190cf18c..1c2b43d618 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/processor/SprintDataProcessor.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/processor/SprintDataProcessor.java @@ -17,6 +17,7 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.processor; +import java.io.IOException; import java.util.Set; import com.atlassian.jira.rest.client.api.domain.Issue; @@ -36,6 +37,9 @@ public interface SprintDataProcessor { * @param boardId * boardId * @return Set of SprintDetails + * @throws IOException + * throws io exception */ - Set processSprintData(Issue issue, ProjectConfFieldMapping projectConfig, String boardId); + Set processSprintData(Issue issue, ProjectConfFieldMapping projectConfig, String boardId) + throws IOException; } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/processor/SprintDataProcessorImpl.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/processor/SprintDataProcessorImpl.java index f7c611e345..37f75aa386 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/processor/SprintDataProcessorImpl.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/processor/SprintDataProcessorImpl.java @@ -17,12 +17,14 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.processor; +import java.io.IOException; import java.text.ParseException; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import com.publicissapient.kpidashboard.jira.service.JiraClientService; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.codehaus.jettison.json.JSONException; @@ -53,8 +55,12 @@ public class SprintDataProcessorImpl implements SprintDataProcessor { @Autowired private FetchSprintReport fetchSprintReport; + @Autowired + JiraClientService jiraClientService; + @Override - public Set processSprintData(Issue issue, ProjectConfFieldMapping projectConfig, String boardId) { + public Set processSprintData(Issue issue, ProjectConfFieldMapping projectConfig, String boardId) + throws IOException { log.info("creating sprint report for the project : {}", projectConfig.getProjectName()); Set sprintDetailsSet = new HashSet<>(); FieldMapping fieldMapping = projectConfig.getFieldMapping(); @@ -78,7 +84,7 @@ public Set processSprintData(Issue issue, ProjectConfFieldMapping log.error("JIRA Processor | Failed to obtain sprint data from {} {}", sValue, e); } } - KerberosClient krb5Client = null; + KerberosClient krb5Client = jiraClientService.getKerberosClientMap(projectConfig.getBasicProjectConfigId().toString()); if (StringUtils.isEmpty(boardId)) { return fetchSprintReport.fetchSprints(projectConfig, sprintDetailsSet, krb5Client, false); } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/reader/IssueBoardReader.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/reader/IssueBoardReader.java index a183266923..5ba860d653 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/reader/IssueBoardReader.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/reader/IssueBoardReader.java @@ -17,7 +17,6 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.reader; -import com.atlassian.jira.rest.client.api.RestClientException; import com.atlassian.jira.rest.client.api.domain.Issue; import com.publicissapient.kpidashboard.common.client.KerberosClient; import com.publicissapient.kpidashboard.common.model.ProcessorExecutionTraceLog; @@ -25,7 +24,6 @@ import com.publicissapient.kpidashboard.common.repository.tracelog.ProcessorExecutionTraceLogRepository; import com.publicissapient.kpidashboard.common.util.DateUtil; import com.publicissapient.kpidashboard.jira.aspect.TrackExecutionTime; -import com.publicissapient.kpidashboard.jira.client.JiraClient; import com.publicissapient.kpidashboard.jira.client.ProcessorJiraRestClient; import com.publicissapient.kpidashboard.jira.config.FetchProjectConfiguration; import com.publicissapient.kpidashboard.jira.config.JiraProcessorConfig; @@ -34,6 +32,7 @@ import com.publicissapient.kpidashboard.jira.model.ProjectConfFieldMapping; import com.publicissapient.kpidashboard.jira.model.ReadData; import com.publicissapient.kpidashboard.jira.service.FetchEpicData; +import com.publicissapient.kpidashboard.jira.service.JiraClientService; import com.publicissapient.kpidashboard.jira.service.JiraCommonService; import lombok.extern.slf4j.Slf4j; import net.logstash.logback.util.StringUtils; @@ -51,7 +50,6 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -69,19 +67,20 @@ public class IssueBoardReader implements ItemReader { @Autowired FetchProjectConfiguration fetchProjectConfiguration; @Autowired - JiraClient jiraClient; - @Autowired JiraCommonService jiraCommonService; @Autowired JiraProcessorConfig jiraProcessorConfig; @Autowired FetchEpicData fetchEpicData; + @Autowired + JiraClientService jiraClientService; int pageSize = 50; int pageNumber = 0; String boardId = ""; List issues = new ArrayList<>(); Map> projectBoardWiseDeltaDate = new HashMap<>(); int boardIssueSize = 0; + Boolean fetchLastIssue = false; private ReaderRetryHelper retryHelper; @Autowired private ProcessorExecutionTraceLogRepository processorExecutionTraceLogRepo; @@ -89,33 +88,39 @@ public class IssueBoardReader implements ItemReader { private Iterator issueIterator; private ProjectConfFieldMapping projectConfFieldMapping; private String projectId; - - @Autowired - public IssueBoardReader(@Value("#{jobParameters['projectId']}") String projectId) { - this.projectId = projectId; - this.retryHelper = new ReaderRetryHelper(); - } - - public void initializeReader(String projectId) { - pageSize = jiraProcessorConfig.getPageSize(); - projectConfFieldMapping = fetchProjectConfiguration.fetchConfiguration(projectId); - } + KerberosClient krb5Client; + ProcessorJiraRestClient client; + + @Autowired + public IssueBoardReader(@Value("#{jobParameters['projectId']}") String projectId) { + this.projectId = projectId; + this.retryHelper = new ReaderRetryHelper(); + } + + public void initializeReader(String projectId) { + pageSize = jiraProcessorConfig.getPageSize(); + projectConfFieldMapping = fetchProjectConfiguration.fetchConfiguration(projectId); + retryHelper = new ReaderRetryHelper(); + krb5Client = jiraClientService.getKerberosClientMap(projectId); + client = jiraClientService.getRestClientMap(projectId); + } /* * (non-Javadoc) * * @see org.springframework.batch.item.ItemReader#read() + * */ @Override - public ReadData read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException { + public ReadData read() throws Exception, UnexpectedInputException, ParseException, + NonTransientResourceException { if (null == projectConfFieldMapping) { log.info("Gathering data to fetch jira issues for the project : {}", projectId); initializeReader(projectId); } ReadData readData = null; - KerberosClient krb5Client = null; - try(ProcessorJiraRestClient client = jiraClient.getClient(projectConfFieldMapping, krb5Client)) { + if (!fetchLastIssue) { if (boardIterator == null && CollectionUtils.isNotEmpty(projectConfFieldMapping.getProjectToolConfig().getBoards())) { boardIterator = projectConfFieldMapping.getProjectToolConfig().getBoards().iterator(); @@ -129,7 +134,7 @@ public ReadData read() throws Exception, UnexpectedInputException, ParseExceptio boardId = boardDetails.getBoardId(); fetchIssues(client); epicIssues = fetchEpics(krb5Client, client); - if (CollectionUtils.isNotEmpty(epicIssues)) { + if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(epicIssues)) { issues.addAll(epicIssues); } } @@ -151,54 +156,40 @@ public ReadData read() throws Exception, UnexpectedInputException, ParseExceptio } if ((null == projectConfFieldMapping) - || !boardIterator.hasNext() && (!issueIterator.hasNext() && boardIssueSize < pageSize)) { - log.info("Data has been fetched for the project : {}", projectConfFieldMapping.getProjectName()); - readData = null; + || !boardIterator.hasNext() && (issueIterator != null && !issueIterator.hasNext() && boardIssueSize < pageSize)) { + log.info("Data has been fetched for the project : {}", projectConfFieldMapping == null ? "" : projectConfFieldMapping.getProjectName()); + fetchLastIssue = true; + return readData; } - } catch (Exception e) { - log.error("Exception while fetching data for the project {}", projectConfFieldMapping.getProjectName(), e); - throw e; } return readData; } - private void fetchIssues(ProcessorJiraRestClient client) { + private void fetchIssues(ProcessorJiraRestClient client) throws Exception { - ReaderRetryHelper.RetryableOperation retryableOperation = () -> { + ReaderRetryHelper.RetryableOperation retryableOperation = () -> { + log.info("Reading issues for project: {} boardid: {}, page No: {}", + projectConfFieldMapping.getProjectName(), boardId, pageNumber / pageSize); - try { - log.info("Reading issues for project: {} boardid: {}, page No: {}", - projectConfFieldMapping.getProjectName(), boardId, pageNumber / pageSize); + String deltaDate = getDeltaDateFromTraceLog(); - String deltaDate = getDeltaDateFromTraceLog(); + issues = jiraCommonService.fetchIssueBasedOnBoard(projectConfFieldMapping, client, pageNumber, boardId, + deltaDate); + boardIssueSize = issues.size(); + pageNumber += pageSize; + return null; + }; - issues = jiraCommonService.fetchIssueBasedOnBoard(projectConfFieldMapping, client, pageNumber, boardId, - deltaDate); - boardIssueSize = issues.size(); - pageNumber += pageSize; - } catch (RestClientException e) { - if (e.getStatusCode().isPresent() && e.getStatusCode().get() == 401) { - log.error(JiraConstants.ERROR_MSG_401); - } else { - log.error(JiraConstants.ERROR_MSG_NO_RESULT_WAS_AVAILABLE, e.getCause()); - } - throw e; - } catch (Exception e) { - log.error("Exception while fetching issues for project: {} boardid: {}, page No: {}", - projectConfFieldMapping.getProjectName(), boardId, pageNumber / pageSize, e); + try { + retryHelper.executeWithRetry(retryableOperation); + } catch (Exception e) { + log.error("Exception while fetching issues for project: {} boardid: {}, page No: {}", + projectConfFieldMapping.getProjectName(), boardId, pageNumber / pageSize); + log.error("All retries attempts are failed"); - // Re-throw the exception to allow for retries - throw e; - } - return null; - }; - - try { - retryHelper.executeWithRetry(retryableOperation); - } catch (Exception e) { - log.error("All retry attempts failed while fetching issues."); - } - } + throw e; + } + } private String getDeltaDateFromTraceLog() { String deltaDate = DateUtil.dateTimeFormatter( @@ -249,12 +240,19 @@ private void setLastSuccessfulRunFromTraceLog(String deltaDate) { boardWiseDate.put(NOBOARD_MSG, lastSuccessfulRun); } - projectBoardWiseDeltaDate.put(projectConfFieldMapping.getBasicProjectConfigId().toString(), boardWiseDate); - } else { - log.info("project: {} not found in trace log so data will be fetched from beginning", + projectBoardWiseDeltaDate.put(projectConfFieldMapping.getBasicProjectConfigId().toString(), boardWiseDate); + } else { + log.info("project: {} not found in trace log so data will be fetched from beginning", projectConfFieldMapping.getProjectName()); - } - } + Map boardWiseDate = new HashMap<>(); + if (StringUtils.isEmpty(boardId)) { + boardWiseDate.put(boardId, deltaDate); + } else { + boardWiseDate.put(NOBOARD_MSG, deltaDate); + } + projectBoardWiseDeltaDate.put(projectConfFieldMapping.getBasicProjectConfigId().toString(), boardWiseDate); + } + } private String updateDeltaDateFromBoardWiseData(String deltaDate) { Map boardWiseDate = projectBoardWiseDeltaDate @@ -276,34 +274,22 @@ private String updateDeltaDateFromBoardWiseData(String deltaDate) { return deltaDate; } - @TrackExecutionTime - private List fetchEpics(KerberosClient krb5Client, ProcessorJiraRestClient client) { - - ReaderRetryHelper.RetryableOperation> retryableOperation = () -> { - List epicIssues = new ArrayList<>(); - try { - log.info("Reading epics for project: {} boardid: {}", projectConfFieldMapping.getProjectName(), - boardId); - epicIssues = fetchEpicData.fetchEpic(projectConfFieldMapping, boardId, client, krb5Client); - } catch (RestClientException e) { - if (e.getStatusCode().isPresent() && e.getStatusCode().get() == 401) { - log.error(JiraConstants.ERROR_MSG_401); - } else { - log.error(JiraConstants.ERROR_MSG_NO_RESULT_WAS_AVAILABLE, e.getCause()); - } - throw e; - } catch (Exception e) { - log.error("Exception occurred while fetching epic issues:", e); - throw e; - } - return epicIssues; - }; - try { - return retryHelper.executeWithRetry(retryableOperation); - } catch (Exception e) { - log.error("All retry attempts failed while fetching epics."); - return Collections.emptyList(); - } - } - -} + @TrackExecutionTime + private List fetchEpics(KerberosClient krb5Client, ProcessorJiraRestClient client) throws Exception { + + ReaderRetryHelper.RetryableOperation> retryableOperation = () -> { + log.info("Reading epics for project: {} boardid: {}", projectConfFieldMapping.getProjectName(), boardId); + return fetchEpicData.fetchEpic(projectConfFieldMapping, boardId, client, krb5Client); + }; + try { + return retryHelper.executeWithRetry(retryableOperation); + } catch (Exception e) { + log.error("Exception while fetching epics for project: {} boardid: {}", + projectConfFieldMapping.getProjectName(), boardId); + log.error("All retries attempts are failed"); + // Re-throw the exception to allow for retries + throw e; + } + } + +} \ No newline at end of file diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/reader/IssueJqlReader.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/reader/IssueJqlReader.java index 3eb0c8950a..59f7cfde62 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/reader/IssueJqlReader.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/reader/IssueJqlReader.java @@ -17,14 +17,11 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.reader; -import com.atlassian.jira.rest.client.api.RestClientException; import com.atlassian.jira.rest.client.api.domain.Issue; -import com.publicissapient.kpidashboard.common.client.KerberosClient; import com.publicissapient.kpidashboard.common.model.ProcessorExecutionTraceLog; import com.publicissapient.kpidashboard.common.repository.tracelog.ProcessorExecutionTraceLogRepository; import com.publicissapient.kpidashboard.common.util.DateUtil; import com.publicissapient.kpidashboard.jira.aspect.TrackExecutionTime; -import com.publicissapient.kpidashboard.jira.client.JiraClient; import com.publicissapient.kpidashboard.jira.client.ProcessorJiraRestClient; import com.publicissapient.kpidashboard.jira.config.FetchProjectConfiguration; import com.publicissapient.kpidashboard.jira.config.JiraProcessorConfig; @@ -32,6 +29,7 @@ import com.publicissapient.kpidashboard.jira.helper.ReaderRetryHelper; import com.publicissapient.kpidashboard.jira.model.ProjectConfFieldMapping; import com.publicissapient.kpidashboard.jira.model.ReadData; +import com.publicissapient.kpidashboard.jira.service.JiraClientService; import com.publicissapient.kpidashboard.jira.service.JiraCommonService; import lombok.extern.slf4j.Slf4j; import net.logstash.logback.util.StringUtils; @@ -66,10 +64,10 @@ public class IssueJqlReader implements ItemReader { FetchProjectConfiguration fetchProjectConfiguration; @Autowired - JiraClient jiraClient; + JiraCommonService jiraCommonService; @Autowired - JiraCommonService jiraCommonService; + JiraClientService jiraClientService; @Autowired JiraProcessorConfig jiraProcessorConfig; @@ -78,12 +76,14 @@ public class IssueJqlReader implements ItemReader { List issues = new ArrayList<>(); Map projectWiseDeltaDate; int issueSize = 0; + Boolean fetchLastIssue = false; @Autowired private ProcessorExecutionTraceLogRepository processorExecutionTraceLogRepo; private Iterator issueIterator; private ProjectConfFieldMapping projectConfFieldMapping; private String projectId; private ReaderRetryHelper retryHelper; + ProcessorJiraRestClient client; @Autowired public IssueJqlReader(@Value("#{jobParameters['projectId']}") String projectId) { @@ -95,6 +95,7 @@ public void initializeReader(String projectId) { log.info("**** Jira Issue fetch started * * *"); pageSize = jiraProcessorConfig.getPageSize(); projectConfFieldMapping = fetchProjectConfiguration.fetchConfiguration(projectId); + client = jiraClientService.getRestClientMap(projectId); } /* @@ -111,80 +112,54 @@ public ReadData read() throws Exception, UnexpectedInputException, ParseExceptio initializeReader(projectId); } ReadData readData = null; - if (null != projectConfFieldMapping) { - KerberosClient krb5Client = null; - try(ProcessorJiraRestClient client = jiraClient.getClient(projectConfFieldMapping, krb5Client)) { - if (null == issueIterator) { - pageNumber = 0; - fetchIssues(client); - } - - if (null != issueIterator && !issueIterator.hasNext()) { - fetchIssues(client); + if (null != projectConfFieldMapping && !fetchLastIssue) { + if (issueIterator == null || !issueIterator.hasNext()) { + fetchIssues(client); + if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(issues)) { + issueIterator = issues.iterator(); } + } - if (null != issueIterator && issueIterator.hasNext()) { - Issue issue = issueIterator.next(); - readData = new ReadData(); - readData.setIssue(issue); - readData.setProjectConfFieldMapping(projectConfFieldMapping); - readData.setSprintFetch(false); - } + if (null != issueIterator && issueIterator.hasNext()) { + Issue issue = issueIterator.next(); + readData = new ReadData(); + readData.setIssue(issue); + readData.setProjectConfFieldMapping(projectConfFieldMapping); + readData.setSprintFetch(false); + } - if (null == issueIterator || (!issueIterator.hasNext() && issueSize < pageSize)) { - log.info("Data has been fetched for the project : {}", projectConfFieldMapping.getProjectName()); - readData = null; - } - } catch (Exception e) { - log.error("Exception while fetching data for the project {}", projectConfFieldMapping.getProjectName(), - e); - throw e; + if (null == issueIterator || (!issueIterator.hasNext() && issueSize < pageSize)) { + log.info("Data has been fetched for the project : {}", projectConfFieldMapping.getProjectName()); + fetchLastIssue = true; + return readData; } } - return readData; + return readData; } - @TrackExecutionTime - private void fetchIssues(ProcessorJiraRestClient client) { - - ReaderRetryHelper.RetryableOperation retryableOperation = () -> { - - try { - log.info("Reading issues for project : {}, page No : {}", projectConfFieldMapping.getProjectName(), - pageNumber / pageSize); - String deltaDate = getDeltaDateFromTraceLog(); - issues = jiraCommonService.fetchIssuesBasedOnJql(projectConfFieldMapping, client, pageNumber, - deltaDate); - issueSize = issues.size(); - pageNumber += pageSize; - if (CollectionUtils.isNotEmpty(issues)) { - issueIterator = issues.iterator(); - } - } catch (RestClientException e) { - if (e.getStatusCode().isPresent() && e.getStatusCode().get() == 401) { - log.error(JiraConstants.ERROR_MSG_401); - } else { - log.error(JiraConstants.ERROR_MSG_NO_RESULT_WAS_AVAILABLE, e.getCause()); - } - throw e; - } catch (InterruptedException e) { - log.error("Interrupted exception thrown.", e); - throw e; - } catch (Exception e) { - log.error("Exception while fetching issues for project: {} page No: {}", - projectConfFieldMapping.getProjectName(), pageNumber / pageSize, e); - throw e; - } - return null; - }; - - try { - retryHelper.executeWithRetry(retryableOperation); - } catch (Exception e) { - log.error("All retry attempts failed while fetching issues."); - } - } + @TrackExecutionTime + private void fetchIssues(ProcessorJiraRestClient client) throws Exception { + + ReaderRetryHelper.RetryableOperation retryableOperation = () -> { + log.info("Reading issues for project : {}, page No : {}", projectConfFieldMapping.getProjectName(), + pageNumber / pageSize); + String deltaDate = getDeltaDateFromTraceLog(); + issues = jiraCommonService.fetchIssuesBasedOnJql(projectConfFieldMapping, client, pageNumber, deltaDate); + issueSize = issues.size(); + pageNumber += pageSize; + return null; + }; + + try { + retryHelper.executeWithRetry(retryableOperation); + } catch (Exception e) { + log.error("Exception while fetching issues for project: {}, page No: {}", + projectConfFieldMapping.getProjectName(), pageNumber / pageSize); + log.error("All retries attempts are failed"); + throw e; + } + } private String getDeltaDateFromTraceLog() { String deltaDate = DateUtil.dateTimeFormatter( @@ -198,20 +173,19 @@ private String getDeltaDateFromTraceLog() { .findByProcessorNameAndBasicProjectConfigIdIn(JiraConstants.JIRA, Arrays.asList(projectConfFieldMapping.getBasicProjectConfigId().toString())); if (CollectionUtils.isNotEmpty(procExecTraceLogs)) { - String lastSuccessfulRun = null; + String lastSuccessfulRun = deltaDate; for (ProcessorExecutionTraceLog processorExecutionTraceLog : procExecTraceLogs) { lastSuccessfulRun = processorExecutionTraceLog.getLastSuccessfulRun(); } - if (!StringUtils.isBlank(lastSuccessfulRun)) { - log.info("project: {} found in trace log. Data will be fetched from one day before {}", + log.info("project: {} found in trace log. Data will be fetched from one day before {}", projectConfFieldMapping.getProjectName(), lastSuccessfulRun); - deltaDate = lastSuccessfulRun; - } projectWiseDeltaDate = new HashMap<>(); - projectWiseDeltaDate.put(projectConfFieldMapping.getBasicProjectConfigId().toString(), deltaDate); + projectWiseDeltaDate.put(projectConfFieldMapping.getBasicProjectConfigId().toString(), lastSuccessfulRun); } else { log.info("project: {} not found in trace log so data will be fetched from beginning", projectConfFieldMapping.getProjectName()); + projectWiseDeltaDate = new HashMap<>(); + projectWiseDeltaDate.put(projectConfFieldMapping.getBasicProjectConfigId().toString(), deltaDate); } } if (MapUtils.isNotEmpty(projectWiseDeltaDate) && !StringUtils diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/reader/IssueSprintReader.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/reader/IssueSprintReader.java index 8b7dcb7eef..1e69e51e1c 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/reader/IssueSprintReader.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/reader/IssueSprintReader.java @@ -17,19 +17,16 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.reader; -import com.atlassian.jira.rest.client.api.RestClientException; import com.atlassian.jira.rest.client.api.domain.Issue; -import com.publicissapient.kpidashboard.common.client.KerberosClient; import com.publicissapient.kpidashboard.jira.aspect.TrackExecutionTime; -import com.publicissapient.kpidashboard.jira.client.JiraClient; import com.publicissapient.kpidashboard.jira.client.ProcessorJiraRestClient; import com.publicissapient.kpidashboard.jira.config.FetchProjectConfiguration; import com.publicissapient.kpidashboard.jira.config.JiraProcessorConfig; -import com.publicissapient.kpidashboard.jira.constant.JiraConstants; import com.publicissapient.kpidashboard.jira.helper.ReaderRetryHelper; import com.publicissapient.kpidashboard.jira.model.ProjectConfFieldMapping; import com.publicissapient.kpidashboard.jira.model.ReadData; import com.publicissapient.kpidashboard.jira.service.FetchIssueSprint; +import com.publicissapient.kpidashboard.jira.service.JiraClientService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.springframework.batch.core.configuration.annotation.StepScope; @@ -45,6 +42,9 @@ import java.util.Iterator; import java.util.List; +/** + * @author purgupta2 + */ @Slf4j @Component @StepScope @@ -54,7 +54,7 @@ public class IssueSprintReader implements ItemReader { FetchProjectConfiguration fetchProjectConfiguration; @Autowired - JiraClient jiraClient; + JiraClientService jiraClientService; @Autowired JiraProcessorConfig jiraProcessorConfig; @@ -69,6 +69,7 @@ public class IssueSprintReader implements ItemReader { private ProjectConfFieldMapping projectConfFieldMapping; private String sprintId; private ReaderRetryHelper retryHelper; + ProcessorJiraRestClient client; @Autowired public IssueSprintReader(@Value("#{jobParameters['sprintId']}") String sprintId) { @@ -80,6 +81,7 @@ public void initializeReader(String sprintId) { log.info("**** Jira Issue fetch started * * *"); pageSize = jiraProcessorConfig.getPageSize(); projectConfFieldMapping = fetchProjectConfiguration.fetchConfigurationBasedOnSprintId(sprintId); + client = jiraClientService.getRestClientMap(sprintId); } @Override @@ -91,76 +93,56 @@ public ReadData read() throws Exception, UnexpectedInputException, ParseExceptio } ReadData readData = null; if (null != projectConfFieldMapping) { - KerberosClient krb5Client = null; - try(ProcessorJiraRestClient client = jiraClient.getClient(projectConfFieldMapping, krb5Client)) { - if (null == issueIterator) { - pageNumber = 0; - fetchIssues(client); - } - - if (null != issueIterator && !issueIterator.hasNext()) { - fetchIssues(client); - } - - if (null != issueIterator && issueIterator.hasNext()) { - Issue issue = issueIterator.next(); - readData = new ReadData(); - readData.setIssue(issue); - readData.setProjectConfFieldMapping(projectConfFieldMapping); - readData.setSprintFetch(true); - } - - if (null == issueIterator || (!issueIterator.hasNext() && issueSize < pageSize)) { - log.info("Data has been fetched for the project : {}", projectConfFieldMapping.getProjectName()); - readData = null; - } - } catch (Exception e) { - log.error("Exception while fetching data for the project {}", projectConfFieldMapping.getProjectName(), - e); - throw e; + if (null == issueIterator) { + pageNumber = 0; + fetchIssues(client); } - } - return readData; - } + if (null != issueIterator && !issueIterator.hasNext()) { + fetchIssues(client); + } - @TrackExecutionTime - private void fetchIssues(ProcessorJiraRestClient client) { - ReaderRetryHelper.RetryableOperation retryableOperation = () -> { - - try { - log.info("Reading issues for project : {}, page No : {}", projectConfFieldMapping.getProjectName(), - pageNumber / pageSize); - issues = fetchIssueSprint.fetchIssuesSprintBasedOnJql(projectConfFieldMapping, client, pageNumber, - sprintId); - issueSize = issues.size(); - pageNumber += pageSize; - if (CollectionUtils.isNotEmpty(issues)) { - issueIterator = issues.iterator(); - } - } catch (RestClientException e) { - if (e.getStatusCode().isPresent() && e.getStatusCode().get() == 401) { - log.error(JiraConstants.ERROR_MSG_401); - } else { - log.error(JiraConstants.ERROR_MSG_NO_RESULT_WAS_AVAILABLE, e.getCause()); - } - throw e; - } catch (InterruptedException e) { - log.error("Interrupted exception thrown.", e); - throw e; - } catch (Exception e) { - log.error("Exception while fetching issues for project: {} page No: {}", - projectConfFieldMapping.getProjectName(), pageNumber / pageSize, e); - throw e; + if (null != issueIterator && issueIterator.hasNext()) { + Issue issue = issueIterator.next(); + readData = new ReadData(); + readData.setIssue(issue); + readData.setProjectConfFieldMapping(projectConfFieldMapping); + readData.setSprintFetch(true); } - return null; - }; - try { - retryHelper.executeWithRetry(retryableOperation); - } catch (Exception e) { - log.error("All retry attempts failed while fetching issues."); + if (null == issueIterator || (!issueIterator.hasNext() && issueSize < pageSize)) { + log.info("Data has been fetched for the project : {}", projectConfFieldMapping.getProjectName()); + readData = null; + } } + + return readData; } + @TrackExecutionTime + private void fetchIssues(ProcessorJiraRestClient client) throws Exception { + ReaderRetryHelper.RetryableOperation retryableOperation = () -> { + + log.info("Reading issues for project : {}, page No : {}", projectConfFieldMapping.getProjectName(), + pageNumber / pageSize); + issues = fetchIssueSprint.fetchIssuesSprintBasedOnJql(projectConfFieldMapping, client, pageNumber, + sprintId); + issueSize = issues.size(); + pageNumber += pageSize; + if (CollectionUtils.isNotEmpty(issues)) { + issueIterator = issues.iterator(); + } + return null; + }; + + try { + retryHelper.executeWithRetry(retryableOperation); + } catch (Exception e) { + log.error("Exception while fetching issues for project: {}, page No: {}", + projectConfFieldMapping.getProjectName(), pageNumber / pageSize); + log.error("All retries attempts are failed"); + throw e; + } + } + } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchEpicDataImpl.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchEpicDataImpl.java index 6b10b81814..c1ab448801 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchEpicDataImpl.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchEpicDataImpl.java @@ -59,65 +59,75 @@ public class FetchEpicDataImpl implements FetchEpicData { private JiraProcessorConfig jiraProcessorConfig; @Override - public List fetchEpic(ProjectConfFieldMapping projectConfig, String boardId, - ProcessorJiraRestClient client, KerberosClient krb5Client) - throws InterruptedException, RestClientException, IOException { + public List fetchEpic(ProjectConfFieldMapping projectConfig, String boardId, ProcessorJiraRestClient client, + KerberosClient krb5Client) throws InterruptedException, IOException { List epicList = new ArrayList<>(); + try { + JiraToolConfig jiraToolConfig = projectConfig.getJira(); + if (null != jiraToolConfig) { + boolean isLast = false; + int startIndex = 0; + do { + URL url = getEpicUrl(projectConfig, boardId, startIndex); + String jsonResponse = jiraCommonService.getDataFromClient(projectConfig, url, krb5Client); + isLast = populateData(jsonResponse, epicList); + startIndex = epicList.size(); + TimeUnit.MILLISECONDS.sleep(jiraProcessorConfig.getSubsequentApiCallDelayInMilli()); + } while (!isLast); - JiraToolConfig jiraToolConfig = projectConfig.getJira(); - if (null != jiraToolConfig) { - boolean isLast = false; - int startIndex = 0; - do { - URL url = getEpicUrl(projectConfig, boardId, startIndex); - String jsonResponse = jiraCommonService.getDataFromClient(projectConfig, url, krb5Client); - isLast = populateData(jsonResponse, epicList); - startIndex = epicList.size(); - TimeUnit.MILLISECONDS.sleep(jiraProcessorConfig.getSubsequentApiCallDelayInMilli()); - } while (!isLast); - + } + } catch (RestClientException rce) { + log.error("Client exception when loading epic data", rce); + throw rce; + } catch (MalformedURLException mfe) { + log.error("Malformed url for loading epic data", mfe); + throw mfe; } - List epicIssue = getEpicIssuesQuery(epicList,client); - return epicIssue; + + return getEpicIssuesQuery(epicList, client); } - private List getEpicIssuesQuery(List epicKeyList, ProcessorJiraRestClient client) throws InterruptedException, RestClientException { + private List getEpicIssuesQuery(List epicKeyList, ProcessorJiraRestClient client) + throws InterruptedException { List issueList = new ArrayList<>(); SearchResult searchResult = null; - - if (CollectionUtils.isNotEmpty(epicKeyList)) { - String query = "key in (" + String.join(",", epicKeyList) + ")"; - int pageStart = 0; - int totalEpic = 0; - int fetchedEpic = 0; - boolean continueFlag = true; - do { - Promise promise = client.getSearchClient().searchJql(query, - jiraProcessorConfig.getPageSize(), pageStart, null); - searchResult = promise.claim(); - if (null != searchResult && null != searchResult.getIssues()) { - if (totalEpic == 0) { - totalEpic = searchResult.getTotal(); - } - int issueCount = 0; - for (Issue issue : searchResult.getIssues()) { - issueList.add(issue); - issueCount++; + try { + if (CollectionUtils.isNotEmpty(epicKeyList)) { + String query = "key in (" + String.join(",", epicKeyList) + ")"; + int pageStart = 0; + int totalEpic = 0; + int fetchedEpic = 0; + boolean continueFlag = true; + do { + Promise promise = client.getSearchClient().searchJql(query, + jiraProcessorConfig.getPageSize(), pageStart, null); + searchResult = promise.claim(); + if (null != searchResult && null != searchResult.getIssues()) { + if (totalEpic == 0) { + totalEpic = searchResult.getTotal(); + } + int issueCount = 0; + for (Issue issue : searchResult.getIssues()) { + issueList.add(issue); + issueCount++; + } + fetchedEpic += issueCount; + pageStart += issueCount; + if (totalEpic <= fetchedEpic) { + fetchedEpic = totalEpic; + continueFlag = false; + } + } else { + break; } - fetchedEpic += issueCount; - pageStart += issueCount; - if (totalEpic <= fetchedEpic) { - fetchedEpic = totalEpic; - continueFlag = false; - } - } else { - break; - } - TimeUnit.MILLISECONDS.sleep(jiraProcessorConfig.getSubsequentApiCallDelayInMilli()); - } while (totalEpic < fetchedEpic || continueFlag); - + TimeUnit.MILLISECONDS.sleep(jiraProcessorConfig.getSubsequentApiCallDelayInMilli()); + } while (totalEpic < fetchedEpic || continueFlag); + } + } catch (RestClientException e) { + log.error("Error while fetching issues", e.getCause()); + throw e; } return issueList; } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchIssueSprintImpl.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchIssueSprintImpl.java index 6d4392d02a..3b5cfaab1b 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchIssueSprintImpl.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchIssueSprintImpl.java @@ -17,6 +17,9 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.service; +import static com.publicissapient.kpidashboard.jira.constant.JiraConstants.ERROR_MSG_401; +import static com.publicissapient.kpidashboard.jira.constant.JiraConstants.ERROR_MSG_NO_RESULT_WAS_AVAILABLE; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -32,10 +35,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import com.atlassian.jira.rest.client.api.RestClientException; import com.atlassian.jira.rest.client.api.domain.Issue; import com.atlassian.jira.rest.client.api.domain.SearchResult; import com.publicissapient.kpidashboard.common.constant.NormalizedJira; @@ -73,6 +78,8 @@ public class FetchIssueSprintImpl implements FetchIssueSprint { @Autowired JiraIssueRepository jiraIssueRepository; + private ProcessorJiraRestClient client; + @Override public List fetchIssuesSprintBasedOnJql(ProjectConfFieldMapping projectConfig, ProcessorJiraRestClient client, int pageNumber, String sprintId) throws InterruptedException { @@ -109,29 +116,41 @@ public List fetchIssuesSprintBasedOnJql(ProjectConfFieldMapping projectCo return issues; } - public SearchResult getIssuesSprint(ProjectConfFieldMapping projectConfig, ProcessorJiraRestClient client, + public SearchResult getIssuesSprint(ProjectConfFieldMapping projectConfig, ProcessorJiraRestClient clientIn, int pageStart, List issueKeys) throws InterruptedException { SearchResult searchResult = null; - - if (client == null) { - log.warn(MSG_JIRA_CLIENT_SETUP_FAILED); - } else if (org.apache.commons.lang3.StringUtils.isEmpty(projectConfig.getProjectToolConfig().getProjectKey())) { + client=clientIn; + if (org.apache.commons.lang3.StringUtils.isEmpty(projectConfig.getProjectToolConfig().getProjectKey())) { log.info("Project key is empty {}", projectConfig.getProjectToolConfig().getProjectKey()); } else { - StringBuilder query = new StringBuilder("project in (") - .append(projectConfig.getProjectToolConfig().getProjectKey()).append(") AND "); - - query.append(JiraProcessorUtil.processJqlForSprintFetch(issueKeys)); - log.info("jql query :{}", query); - Promise promisedRs = client.getProcessorSearchClient().searchJql(query.toString(), - jiraProcessorConfig.getPageSize(), pageStart, JiraConstants.ISSUE_FIELD_SET); - searchResult = promisedRs.claim(); - if (searchResult != null) { - log.info(String.format(PROCESSING_ISSUES_PRINT_LOG, pageStart, - Math.min(pageStart + jiraProcessorConfig.getPageSize() - 1, searchResult.getTotal()), - searchResult.getTotal())); + try { + StringBuilder query = new StringBuilder("project in (") + .append(projectConfig.getProjectToolConfig().getProjectKey()).append(") AND "); + + query.append(JiraProcessorUtil.processJqlForSprintFetch(issueKeys)); + if (StringUtils.isNotEmpty(projectConfig.getProjectToolConfig().getBoardQuery())) { + query.append(" and (").append( + projectConfig.getJira().getBoardQuery().toLowerCase().split(JiraConstants.ORDERBY)[0]) + .append(")"); + } + log.info("jql query :{}", query); + Promise promisedRs = client.getProcessorSearchClient().searchJql(query.toString(), + jiraProcessorConfig.getPageSize(), pageStart, JiraConstants.ISSUE_FIELD_SET); + searchResult = promisedRs.claim(); + if (searchResult != null) { + log.info(String.format(PROCESSING_ISSUES_PRINT_LOG, pageStart, + Math.min(pageStart + jiraProcessorConfig.getPageSize() - 1, searchResult.getTotal()), + searchResult.getTotal())); + } + TimeUnit.MILLISECONDS.sleep(jiraProcessorConfig.getSubsequentApiCallDelayInMilli()); + } catch (RestClientException e) { + if (e.getStatusCode().isPresent() && e.getStatusCode().get() == 401) { + log.error(ERROR_MSG_401); + } else { + log.error(ERROR_MSG_NO_RESULT_WAS_AVAILABLE, e); + } + throw e; } - TimeUnit.MILLISECONDS.sleep(jiraProcessorConfig.getSubsequentApiCallDelayInMilli()); } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchKanbanReleaseDataImpl.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchKanbanReleaseDataImpl.java index e1c351df35..652b425361 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchKanbanReleaseDataImpl.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchKanbanReleaseDataImpl.java @@ -17,6 +17,7 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.service; +import java.io.IOException; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashSet; @@ -29,6 +30,7 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; +import org.json.simple.parser.ParseException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -89,7 +91,7 @@ public ProjectRelease processReleaseInfo(ProjectConfFieldMapping projectConfig, * @return */ private void saveProjectRelease(ProjectConfFieldMapping confFieldMapping, - KanbanAccountHierarchy kanbanAccountHierarchy, ProjectRelease projectRelease, KerberosClient krb5Client) { + KanbanAccountHierarchy kanbanAccountHierarchy, ProjectRelease projectRelease, KerberosClient krb5Client) throws IOException, ParseException { List projectVersionList = jiraCommonService.getVersion(confFieldMapping, krb5Client); if (CollectionUtils.isNotEmpty(projectVersionList)) { @@ -161,8 +163,8 @@ private List createKanbanHierarchyForRelease(ProjectRele kanbanAccountHierarchy.setBasicProjectConfigId(projectBasicConfig.getId()); kanbanAccountHierarchy.setIsDeleted(JiraConstants.FALSE); kanbanAccountHierarchy.setLabelName(hierarchyLevel.getHierarchyLevelId()); - String versionName = projectVersion.getName() + JiraConstants.COMBINE_IDS_SYMBOL + projectRelease - .getProjectName().split(JiraConstants.COMBINE_IDS_SYMBOL + projectRelease.getConfigId())[0]; + String versionName = projectVersion.getName() + JiraConstants.COMBINE_IDS_SYMBOL + + projectBasicConfig.getProjectName(); String versionId = projectVersion.getId() + JiraConstants.COMBINE_IDS_SYMBOL + projectRelease.getProjectId(); kanbanAccountHierarchy.setNodeId(versionId); diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchScrumReleaseDataImpl.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchScrumReleaseDataImpl.java index 29430df1c0..21e9e340f0 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchScrumReleaseDataImpl.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchScrumReleaseDataImpl.java @@ -17,6 +17,7 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.service; +import java.io.IOException; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; @@ -31,6 +32,7 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; +import org.json.simple.parser.ParseException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -91,7 +93,7 @@ public ProjectRelease processReleaseInfo(ProjectConfFieldMapping projectConfig, * @param accountHierarchy */ private void saveProjectRelease(ProjectConfFieldMapping confFieldMapping, AccountHierarchy accountHierarchy, - ProjectRelease projectRelease, KerberosClient krb5Client) { + ProjectRelease projectRelease, KerberosClient krb5Client) throws IOException, ParseException { List projectVersionList = jiraCommonService.getVersion(confFieldMapping, krb5Client); if (CollectionUtils.isNotEmpty(projectVersionList)) { if (null != accountHierarchy) { @@ -188,8 +190,7 @@ private List createScrumHierarchyForRelease(ProjectRelease pro accountHierarchy.setIsDeleted(JiraConstants.FALSE); accountHierarchy.setLabelName(hierarchyLevel.getHierarchyLevelId()); String versionName = projectVersion.getName() + JiraConstants.COMBINE_IDS_SYMBOL - + projectRelease.getProjectName() - .split(JiraConstants.COMBINE_IDS_SYMBOL + projectRelease.getConfigId())[0]; + + projectBasicConfig.getProjectName(); String versionId = projectVersion.getId() + JiraConstants.COMBINE_IDS_SYMBOL + projectRelease.getProjectId(); accountHierarchy.setNodeId(versionId); diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchSprintReport.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchSprintReport.java index fad7b9f6ba..11155a3083 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchSprintReport.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchSprintReport.java @@ -17,16 +17,17 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.service; +import java.io.IOException; import java.util.List; import java.util.Set; import com.publicissapient.kpidashboard.common.client.KerberosClient; +import com.publicissapient.kpidashboard.common.model.jira.BoardDetails; import com.publicissapient.kpidashboard.common.model.jira.SprintDetails; import com.publicissapient.kpidashboard.jira.model.ProjectConfFieldMapping; /** * @author pankumar8 - * */ public interface FetchSprintReport { @@ -40,18 +41,25 @@ public interface FetchSprintReport { * @param isSprintFetch * isSprintFetch * @return Set of SprintDetails + * @throws IOException + * throws IOException */ Set fetchSprints(ProjectConfFieldMapping projectConfig, Set sprintDetailsSet, - KerberosClient krb5Client, boolean isSprintFetch); + KerberosClient krb5Client, boolean isSprintFetch) throws IOException; /** * @param projectConfig * projectConfig * @param krb5Client * krb5Client + * @param boardDetails + * boardDetails * @return List of SprintDetails + * @throws IOException + * throws IOException */ - List createSprintDetailBasedOnBoard(ProjectConfFieldMapping projectConfig, KerberosClient krb5Client); + List createSprintDetailBasedOnBoard(ProjectConfFieldMapping projectConfig, KerberosClient krb5Client, + BoardDetails boardDetails) throws IOException; /** * @param projectConfig @@ -61,6 +69,10 @@ Set fetchSprints(ProjectConfFieldMapping projectConfig, Set getSprints(ProjectConfFieldMapping projectConfig, String boardId, KerberosClient krb5Client); + List getSprints(ProjectConfFieldMapping projectConfig, String boardId, KerberosClient krb5Client) + throws IOException; } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchSprintReportImpl.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchSprintReportImpl.java index d53bcb0f94..5ff11b9c89 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchSprintReportImpl.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/FetchSprintReportImpl.java @@ -62,7 +62,6 @@ /** * @author pankumar8 - * */ @Slf4j @Service @@ -98,7 +97,7 @@ public class FetchSprintReportImpl implements FetchSprintReport { @Override public Set fetchSprints(ProjectConfFieldMapping projectConfig, Set sprintDetailsSet, - KerberosClient krb5Client, boolean isSprintFetch) { + KerberosClient krb5Client, boolean isSprintFetch) throws IOException { Set sprintToSave = new HashSet<>(); ObjectId jiraProcessorId = jiraProcessorRepository.findByProcessorName(ProcessorConstants.JIRA).getId(); if (CollectionUtils.isNotEmpty(sprintDetailsSet)) { @@ -159,7 +158,7 @@ else if (!sprint.getState().equalsIgnoreCase(dbSprintDetails.getState()) && isSp } private void getSprintReport(SprintDetails sprint, ProjectConfFieldMapping projectConfig, String boardId, - SprintDetails dbSprintDetails, KerberosClient krb5Client) { + SprintDetails dbSprintDetails, KerberosClient krb5Client) throws IOException { if (sprint.getOriginalSprintId() != null && sprint.getOriginBoardId() != null && sprint.getOriginBoardId().stream().anyMatch(id -> id != null && !id.isEmpty())) { // If there's at least one non-null and non-empty string in the list, the @@ -169,7 +168,7 @@ private void getSprintReport(SprintDetails sprint, ProjectConfFieldMapping proje } private void getSprintReport(ProjectConfFieldMapping projectConfig, String sprintId, String boardId, - SprintDetails sprint, SprintDetails dbSprintDetails, KerberosClient krb5Client) { + SprintDetails sprint, SprintDetails dbSprintDetails, KerberosClient krb5Client) throws IOException { try { JiraToolConfig jiraToolConfig = projectConfig.getJira(); if (null != jiraToolConfig) { @@ -183,8 +182,7 @@ private void getSprintReport(ProjectConfFieldMapping projectConfig, String sprin throw rce; } catch (MalformedURLException mfe) { log.error("Malformed url for loading sprint report for sprint :{} ", sprintId, mfe); - } catch (IOException ioe) { - log.error("IOException", ioe); + throw mfe; } } @@ -469,15 +467,12 @@ private URL getSprintReportUrl(ProjectConfFieldMapping projectConfig, String spr @Override public List createSprintDetailBasedOnBoard(ProjectConfFieldMapping projectConfig, - KerberosClient krb5Client) { - List boardDetailsList = projectConfig.getProjectToolConfig().getBoards(); + KerberosClient krb5Client, BoardDetails boardDetails) throws IOException { List sprintDetailsBasedOnBoard = new ArrayList<>(); - for (BoardDetails boardDetails : boardDetailsList) { - List sprintDetailsList = getSprints(projectConfig, boardDetails.getBoardId(), krb5Client); - if (CollectionUtils.isNotEmpty(sprintDetailsList)) { - Set sprintDetailSet = limitSprint(sprintDetailsList); - sprintDetailsBasedOnBoard.addAll(fetchSprints(projectConfig, sprintDetailSet, krb5Client, false)); - } + List sprintDetailsList = getSprints(projectConfig, boardDetails.getBoardId(), krb5Client); + if (CollectionUtils.isNotEmpty(sprintDetailsList)) { + Set sprintDetailSet = limitSprint(sprintDetailsList); + sprintDetailsBasedOnBoard.addAll(fetchSprints(projectConfig, sprintDetailSet, krb5Client, false)); } return sprintDetailsBasedOnBoard; } @@ -495,7 +490,7 @@ private Set limitSprint(List sprintDetailsList) { @Override public List getSprints(ProjectConfFieldMapping projectConfig, String boardId, - KerberosClient krb5Client) { + KerberosClient krb5Client) throws IOException { List sprintDetailsList = new ArrayList<>(); try { JiraToolConfig jiraToolConfig = projectConfig.getJira(); @@ -516,8 +511,7 @@ public List getSprints(ProjectConfFieldMapping projectConfig, Str throw rce; } catch (MalformedURLException mfe) { log.error("Malformed url for loading sprint sprints for board", mfe); - } catch (IOException ioe) { - log.error("IOException", ioe); + throw mfe; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/JiraClientService.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/JiraClientService.java new file mode 100644 index 0000000000..5feb92365e --- /dev/null +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/JiraClientService.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * 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.jira.service; + +import org.springframework.stereotype.Service; + +import com.publicissapient.kpidashboard.common.client.KerberosClient; +import com.publicissapient.kpidashboard.jira.client.ProcessorJiraRestClient; + + +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author purgupta2 + * + */ +@Service +public class JiraClientService { + + private final ConcurrentHashMap restClientMap= new ConcurrentHashMap<>(); + + public boolean isContainRestClient(String basicProjectConfigId) { + return restClientMap.containsKey(basicProjectConfigId); + } + + public void setRestClientMap(String basicProjectConfigId,ProcessorJiraRestClient client) { + restClientMap.put(basicProjectConfigId, client); + } + + public ProcessorJiraRestClient getRestClientMap(String basicProjectConfigId) { + return restClientMap.get(basicProjectConfigId); + } + + public void removeRestClientMapClientForKey(String basicProjectConfigId) { + restClientMap.remove(basicProjectConfigId); + } + + private final ConcurrentHashMap kerberosClientMap= new ConcurrentHashMap<>(); + + public boolean isContainKerberosClient(String basicProjectConfigId) { + return kerberosClientMap.containsKey(basicProjectConfigId); + } + + public void setKerberosClientMap(String basicProjectConfigId,KerberosClient client) { + kerberosClientMap.put(basicProjectConfigId, client); + } + + public KerberosClient getKerberosClientMap(String basicProjectConfigId) { + return kerberosClientMap.get(basicProjectConfigId); + } + + public void removeKerberosClientMapClientForKey(String basicProjectConfigId) { + kerberosClientMap.remove(basicProjectConfigId); + } +} diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/JiraCommonService.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/JiraCommonService.java index 58f813e1c2..3d75954739 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/JiraCommonService.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/service/JiraCommonService.java @@ -17,6 +17,9 @@ ******************************************************************************/ package com.publicissapient.kpidashboard.jira.service; +import static com.publicissapient.kpidashboard.jira.constant.JiraConstants.ERROR_MSG_401; +import static com.publicissapient.kpidashboard.jira.constant.JiraConstants.ERROR_MSG_NO_RESULT_WAS_AVAILABLE; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -39,6 +42,7 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -52,7 +56,6 @@ import com.publicissapient.kpidashboard.common.service.AesEncryptionService; import com.publicissapient.kpidashboard.common.service.ToolCredentialProvider; import com.publicissapient.kpidashboard.common.util.DateUtil; -import com.publicissapient.kpidashboard.jira.client.CustomAsynchronousIssueRestClient; import com.publicissapient.kpidashboard.jira.client.ProcessorJiraRestClient; import com.publicissapient.kpidashboard.jira.config.JiraProcessorConfig; import com.publicissapient.kpidashboard.jira.constant.JiraConstants; @@ -82,7 +85,6 @@ public class JiraCommonService { private AesEncryptionService aesEncryptionService; /** - * * @param projectConfig * projectConfig * @param url @@ -109,7 +111,6 @@ public String getDataFromClient(ProjectConfFieldMapping projectConfig, URL url, } /** - * * @param url * url * @param connectionOptional @@ -142,7 +143,7 @@ public String getDataFromServer(URL url, Optional connectionOptional request.connect(); StringBuilder sb = new StringBuilder(); try (InputStream in = (InputStream) request.getContent(); - BufferedReader inReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { + BufferedReader inReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { int cp; while ((cp = inReader.read()) != -1) { sb.append((char) cp); @@ -151,13 +152,11 @@ public String getDataFromServer(URL url, Optional connectionOptional } catch (IOException ie) { log.error("Read exception when connecting to server {}", ie); request.disconnect(); - throw ie; } return sb.toString(); } /** - * * @param encryptedPassword * encryptedPassword * @return String @@ -167,7 +166,6 @@ public String decryptJiraPassword(String encryptedPassword) { } /** - * * @param username * username * @param password @@ -180,7 +178,6 @@ public String encodeCredentialsToBase64(String username, String password) { } /** - * * @param projectConfig * projectConfig * @param clientIncoming @@ -194,7 +191,8 @@ public String encodeCredentialsToBase64(String username, String password) { * InterruptedException */ public List fetchIssuesBasedOnJql(ProjectConfFieldMapping projectConfig, - ProcessorJiraRestClient clientIncoming, int pageNumber, String deltaDate) throws InterruptedException { + ProcessorJiraRestClient clientIncoming, int pageNumber, String deltaDate) + throws InterruptedException, IOException { client = clientIncoming; List issues = new ArrayList<>(); @@ -214,7 +212,6 @@ public List fetchIssuesBasedOnJql(ProjectConfFieldMapping projectConfig, } /** - * * @param projectConfig * projectConfig * @param deltaDate @@ -239,23 +236,33 @@ private SearchResult getJqlIssues(ProjectConfFieldMapping projectConfig, String projectConfig.getProjectToolConfig().getProjectKey(), projectConfig.getProjectToolConfig().getBoardQuery()); } else { - String issueTypes = Arrays.stream(jiraIssueTypeNames) - .map(array -> "\"" + String.join("\", \"", array) + "\"").collect(Collectors.joining(", ")); - StringBuilder query = new StringBuilder("project in (") - .append(projectConfig.getProjectToolConfig().getProjectKey()).append(") and "); - - String userQuery = projectConfig.getJira().getBoardQuery().toLowerCase().split(JiraConstants.ORDERBY)[0]; - query.append(userQuery); - query.append(" and issuetype in (" + issueTypes + " ) and updatedDate>='" + deltaDate + "' "); - query.append(" order BY updated asc"); - log.info("jql query :{}", query); - Promise promisedRs = client.getProcessorSearchClient().searchJql(query.toString(), - jiraProcessorConfig.getPageSize(), pageStart, JiraConstants.ISSUE_FIELD_SET); - searchResult = promisedRs.claim(); - if (searchResult != null) { - log.info(String.format(PROCESSING_ISSUES_PRINT_LOG, pageStart, - Math.min(pageStart + jiraProcessorConfig.getPageSize() - 1, searchResult.getTotal()), - searchResult.getTotal())); + try { + String issueTypes = Arrays.stream(jiraIssueTypeNames) + .map(array -> "\"" + String.join("\", \"", array) + "\"").collect(Collectors.joining(", ")); + StringBuilder query = new StringBuilder("project in (") + .append(projectConfig.getProjectToolConfig().getProjectKey()).append(") and "); + + String userQuery = projectConfig.getJira().getBoardQuery().toLowerCase() + .split(JiraConstants.ORDERBY)[0]; + query.append(userQuery); + query.append(" and issuetype in (" + issueTypes + " ) and updatedDate>='" + deltaDate + "' "); + query.append(" order BY updatedDate asc"); + log.info("jql query :{}", query); + Promise promisedRs = client.getProcessorSearchClient().searchJql(query.toString(), + jiraProcessorConfig.getPageSize(), pageStart, JiraConstants.ISSUE_FIELD_SET); + searchResult = promisedRs.claim(); + if (searchResult != null) { + log.info(String.format(PROCESSING_ISSUES_PRINT_LOG, pageStart, + Math.min(pageStart + jiraProcessorConfig.getPageSize() - 1, searchResult.getTotal()), + searchResult.getTotal())); + } + } catch (RestClientException e) { + if (e.getStatusCode().isPresent() && e.getStatusCode().get() == 401) { + log.error(ERROR_MSG_401); + } else { + log.error(ERROR_MSG_NO_RESULT_WAS_AVAILABLE, e); + } + throw e; } } @@ -263,7 +270,6 @@ private SearchResult getJqlIssues(ProjectConfFieldMapping projectConfig, String } /** - * * @param projectConfig * projectConfig * @param clientIncoming @@ -277,6 +283,8 @@ private SearchResult getJqlIssues(ProjectConfFieldMapping projectConfig, String * @return List of Issue * @throws InterruptedException * InterruptedException + * @throws IOException + * throws IOException * */ public List fetchIssueBasedOnBoard(ProjectConfFieldMapping projectConfig, ProcessorJiraRestClient clientIncoming, int pageNumber, String boardId, String deltaDate) @@ -298,7 +306,6 @@ public List fetchIssueBasedOnBoard(ProjectConfFieldMapping projectConfig, } /** - * * @param boardId * boardId * @param projectConfig @@ -322,15 +329,23 @@ public SearchResult getBoardIssues(String boardId, ProjectConfFieldMapping proje log.error("Either Project key is empty or jiraIssueTypeNames not provided. key {} ", projectConfig.getProjectToolConfig().getProjectKey()); } else { - String query = "updatedDate>='" + deltaDate + "' order by updatedDate asc"; - CustomAsynchronousIssueRestClient issueRestClient = client.getCustomIssueClient(); - Promise promisedRs = issueRestClient.searchBoardIssue(boardId, query, - jiraProcessorConfig.getPageSize(), pageStart, JiraConstants.ISSUE_FIELD_SET); - searchResult = promisedRs.claim(); - if (searchResult != null) { - log.info(String.format(PROCESSING_ISSUES_PRINT_LOG, pageStart, - Math.min(pageStart + jiraProcessorConfig.getPageSize() - 1, searchResult.getTotal()), - searchResult.getTotal())); + try { + String query = "updatedDate>='" + deltaDate + "' order by updatedDate asc"; + Promise promisedRs = client.getCustomIssueClient().searchBoardIssue(boardId, query, + jiraProcessorConfig.getPageSize(), pageStart, JiraConstants.ISSUE_FIELD_SET); + searchResult = promisedRs.claim(); + if (searchResult != null) { + log.info(String.format(PROCESSING_ISSUES_PRINT_LOG, pageStart, + Math.min(pageStart + jiraProcessorConfig.getPageSize() - 1, searchResult.getTotal()), + searchResult.getTotal())); + } + } catch (RestClientException e) { + if (e.getStatusCode().isPresent() && e.getStatusCode().get() == 401) { + log.error(ERROR_MSG_401); + } else { + log.error(ERROR_MSG_NO_RESULT_WAS_AVAILABLE, e); + } + throw e; } } @@ -338,14 +353,18 @@ public SearchResult getBoardIssues(String boardId, ProjectConfFieldMapping proje } /** - * * @param projectConfig * projectConfig * @param krb5Client * krb5Client * @return List of ProjectVersion + * @throws IOException + * IOException + * @throws ParseException + * ParseException */ - public List getVersion(ProjectConfFieldMapping projectConfig, KerberosClient krb5Client) { + public List getVersion(ProjectConfFieldMapping projectConfig, KerberosClient krb5Client) + throws IOException, ParseException { List projectVersionList = new ArrayList<>(); try { JiraToolConfig jiraToolConfig = projectConfig.getJira(); @@ -355,10 +374,10 @@ public List getVersion(ProjectConfFieldMapping projectConfig, Ke } } catch (RestClientException rce) { log.error("Client exception when fetching versions " + rce); + throw rce; } catch (MalformedURLException mfe) { log.error("Malformed url for fetching versions", mfe); - } catch (IOException ioe) { - log.error("IOException", ioe); + throw mfe; } return projectVersionList; } @@ -377,7 +396,8 @@ private URL getVersionUrl(ProjectConfFieldMapping projectConfig) throws Malforme } - private void parseVersionData(String dataFromServer, List projectVersionDetailList) { + private void parseVersionData(String dataFromServer, List projectVersionDetailList) + throws ParseException { if (StringUtils.isNotBlank(dataFromServer)) { try { JSONArray obj = (JSONArray) new JSONParser().parse(dataFromServer); @@ -406,6 +426,7 @@ private void parseVersionData(String dataFromServer, List projec } } catch (Exception pe) { log.error("Parser exception when parsing versions", pe); + throw pe; } } @@ -419,4 +440,4 @@ private String getOptionalString(final JSONObject jsonObject, final String attri return res.toString(); } -} +} \ No newline at end of file diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/JiraIssueReleaseStatusTasklet.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/JiraIssueReleaseStatusTasklet.java index dc11a2f7f3..ce64166906 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/JiraIssueReleaseStatusTasklet.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/JiraIssueReleaseStatusTasklet.java @@ -18,12 +18,12 @@ package com.publicissapient.kpidashboard.jira.tasklet; import com.publicissapient.kpidashboard.common.client.KerberosClient; -import com.publicissapient.kpidashboard.jira.client.JiraClient; import com.publicissapient.kpidashboard.jira.client.ProcessorJiraRestClient; import com.publicissapient.kpidashboard.jira.config.FetchProjectConfiguration; import com.publicissapient.kpidashboard.jira.config.JiraProcessorConfig; import com.publicissapient.kpidashboard.jira.model.ProjectConfFieldMapping; import com.publicissapient.kpidashboard.jira.service.CreateJiraIssueReleaseStatus; +import com.publicissapient.kpidashboard.jira.service.JiraClientService; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.configuration.annotation.StepScope; @@ -45,15 +45,15 @@ public class JiraIssueReleaseStatusTasklet implements Tasklet { @Autowired FetchProjectConfiguration fetchProjectConfiguration; - @Autowired - JiraClient jiraClient; - @Autowired CreateJiraIssueReleaseStatus createJiraIssueReleaseStatus; @Autowired JiraProcessorConfig jiraProcessorConfig; + @Autowired + JiraClientService jiraClientService; + private String projectId; @Autowired @@ -70,15 +70,9 @@ public JiraIssueReleaseStatusTasklet(@Value("#{jobParameters['projectId']}") Str @Override public RepeatStatus execute(StepContribution sc, ChunkContext cc) throws Exception { ProjectConfFieldMapping projConfFieldMapping = fetchProjectConfiguration.fetchConfiguration(projectId); - KerberosClient krb5Client = null; - try(ProcessorJiraRestClient client = jiraClient.getClient(projConfFieldMapping, krb5Client)) { - log.info("Fetching release statuses for the project : {}", projConfFieldMapping.getProjectName()); - createJiraIssueReleaseStatus.processAndSaveProjectStatusCategory(client, projectId); - } catch (Exception e) { - log.error("Exception while fetching release statuses for the project : {}", - projConfFieldMapping.getProjectName(), e); - throw e; - } + ProcessorJiraRestClient client = jiraClientService.getRestClientMap(projectId); + log.info("Fetching release statuses for the project : {}", projConfFieldMapping.getProjectName()); + createJiraIssueReleaseStatus.processAndSaveProjectStatusCategory(client, projectId); return RepeatStatus.FINISHED; } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/KanbanReleaseDataTasklet.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/KanbanReleaseDataTasklet.java index 7538e30f55..2f0670f86e 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/KanbanReleaseDataTasklet.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/KanbanReleaseDataTasklet.java @@ -19,10 +19,10 @@ import com.publicissapient.kpidashboard.common.client.KerberosClient; import com.publicissapient.kpidashboard.jira.client.JiraClient; -import com.publicissapient.kpidashboard.jira.client.ProcessorJiraRestClient; import com.publicissapient.kpidashboard.jira.config.FetchProjectConfiguration; import com.publicissapient.kpidashboard.jira.model.ProjectConfFieldMapping; import com.publicissapient.kpidashboard.jira.service.FetchKanbanReleaseData; +import com.publicissapient.kpidashboard.jira.service.JiraClientService; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.configuration.annotation.StepScope; @@ -44,7 +44,7 @@ public class KanbanReleaseDataTasklet implements Tasklet { FetchKanbanReleaseData fetchKanbanReleaseData; @Autowired - JiraClient jiraClient; + JiraClientService jiraClientService; private String projectId; @@ -63,13 +63,8 @@ public KanbanReleaseDataTasklet(@Value("#{jobParameters['projectId']}") String p public RepeatStatus execute(StepContribution sc, ChunkContext cc) throws Exception { log.info("**** ReleaseData fetch started ****"); ProjectConfFieldMapping projConfFieldMapping = fetchProjectConfiguration.fetchConfiguration(projectId); - KerberosClient krb5Client = null; - try(ProcessorJiraRestClient client = jiraClient.getClient(projConfFieldMapping, krb5Client);) { - fetchKanbanReleaseData.processReleaseInfo(projConfFieldMapping, krb5Client); - } catch (Exception e) { - log.error("Exception while fetching ReleaseData", e); - throw e; - } + KerberosClient krb5Client = jiraClientService.getKerberosClientMap(projectId); + fetchKanbanReleaseData.processReleaseInfo(projConfFieldMapping, krb5Client); log.info("**** ReleaseData fetch ended ****"); return RepeatStatus.FINISHED; } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/MetaDataTasklet.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/MetaDataTasklet.java index 45caf896e2..a2e13c9956 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/MetaDataTasklet.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/MetaDataTasklet.java @@ -18,6 +18,7 @@ package com.publicissapient.kpidashboard.jira.tasklet; import com.publicissapient.kpidashboard.common.client.KerberosClient; +import com.publicissapient.kpidashboard.common.model.connection.Connection; import com.publicissapient.kpidashboard.jira.aspect.TrackExecutionTime; import com.publicissapient.kpidashboard.jira.client.JiraClient; import com.publicissapient.kpidashboard.jira.client.ProcessorJiraRestClient; @@ -25,6 +26,7 @@ import com.publicissapient.kpidashboard.jira.config.JiraProcessorConfig; import com.publicissapient.kpidashboard.jira.model.ProjectConfFieldMapping; import com.publicissapient.kpidashboard.jira.service.CreateMetadata; +import com.publicissapient.kpidashboard.jira.service.JiraClientService; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.configuration.annotation.StepScope; @@ -35,6 +37,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import java.util.Optional; + /** * @author pankumar8 */ @@ -54,6 +58,9 @@ public class MetaDataTasklet implements Tasklet { @Autowired JiraProcessorConfig jiraProcessorConfig; + @Autowired + JiraClientService jiraClientService; + private String projectId; @Autowired @@ -72,14 +79,18 @@ public MetaDataTasklet(@Value("#{jobParameters['projectId']}") String projectId) public RepeatStatus execute(StepContribution sc, ChunkContext cc) throws Exception { ProjectConfFieldMapping projConfFieldMapping = fetchProjectConfiguration.fetchConfiguration(projectId); log.info("Fetching metadata for the project : {}", projConfFieldMapping.getProjectName()); + Optional connectionOptional = projConfFieldMapping.getJira().getConnection(); KerberosClient krb5Client = null; - try(ProcessorJiraRestClient client = jiraClient.getClient(projConfFieldMapping, krb5Client);) { - if (jiraProcessorConfig.isFetchMetadata()) { - createMetadata.collectMetadata(projConfFieldMapping, client); - } - } catch (Exception e) { - log.error("Exception while fetching metadata for the project : {}", projectId, e); - throw e; + if (connectionOptional.isPresent()) { + Connection connection = connectionOptional.get(); + krb5Client = new KerberosClient(connection.getJaasConfigFilePath(), connection.getKrb5ConfigFilePath(), + connection.getJaasUser(), connection.getSamlEndPoint(), connection.getBaseUrl()); + } + ProcessorJiraRestClient client = jiraClient.getClient(projConfFieldMapping, krb5Client); + jiraClientService.setRestClientMap(projectId,client); + jiraClientService.setKerberosClientMap(projectId,krb5Client); + if (jiraProcessorConfig.isFetchMetadata()) { + createMetadata.collectMetadata(projConfFieldMapping, client); } return RepeatStatus.FINISHED; } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/ScrumReleaseDataTasklet.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/ScrumReleaseDataTasklet.java index 5201946ddc..651a984474 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/ScrumReleaseDataTasklet.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/ScrumReleaseDataTasklet.java @@ -19,12 +19,11 @@ import com.publicissapient.kpidashboard.common.client.KerberosClient; import com.publicissapient.kpidashboard.jira.aspect.TrackExecutionTime; -import com.publicissapient.kpidashboard.jira.client.JiraClient; -import com.publicissapient.kpidashboard.jira.client.ProcessorJiraRestClient; import com.publicissapient.kpidashboard.jira.config.FetchProjectConfiguration; import com.publicissapient.kpidashboard.jira.config.JiraProcessorConfig; import com.publicissapient.kpidashboard.jira.model.ProjectConfFieldMapping; import com.publicissapient.kpidashboard.jira.service.FetchScrumReleaseData; +import com.publicissapient.kpidashboard.jira.service.JiraClientService; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.configuration.annotation.StepScope; @@ -42,15 +41,15 @@ public class ScrumReleaseDataTasklet implements Tasklet { @Autowired FetchProjectConfiguration fetchProjectConfiguration; - @Autowired - JiraClient jiraClient; - @Autowired FetchScrumReleaseData fetchScrumReleaseData; @Autowired JiraProcessorConfig jiraProcessorConfig; + @Autowired + JiraClientService jiraClientService; + private String projectId; @Autowired @@ -69,13 +68,8 @@ public ScrumReleaseDataTasklet(@Value("#{jobParameters['projectId']}") String pr public RepeatStatus execute(StepContribution sc, ChunkContext cc) throws Exception { log.info("**** ReleaseData fetch started ****"); ProjectConfFieldMapping projConfFieldMapping = fetchProjectConfiguration.fetchConfiguration(projectId); - KerberosClient krb5Client = null; - try(ProcessorJiraRestClient client = jiraClient.getClient(projConfFieldMapping, krb5Client);) { - fetchScrumReleaseData.processReleaseInfo(projConfFieldMapping, krb5Client); - } catch (Exception e) { - log.error("Exception while fetching ReleaseData", e); - throw e; - } + KerberosClient krb5Client = jiraClientService.getKerberosClientMap(projectId); + fetchScrumReleaseData.processReleaseInfo(projConfFieldMapping, krb5Client); log.info("**** ReleaseData fetch ended ****"); return RepeatStatus.FINISHED; } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/SprintReportTasklet.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/SprintReportTasklet.java index ddd266eaab..284181de9e 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/SprintReportTasklet.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/SprintReportTasklet.java @@ -18,12 +18,16 @@ package com.publicissapient.kpidashboard.jira.tasklet; import com.publicissapient.kpidashboard.common.client.KerberosClient; +import com.publicissapient.kpidashboard.common.model.connection.Connection; import com.publicissapient.kpidashboard.common.model.jira.SprintDetails; import com.publicissapient.kpidashboard.common.repository.jira.SprintRepository; import com.publicissapient.kpidashboard.jira.aspect.TrackExecutionTime; +import com.publicissapient.kpidashboard.jira.client.JiraClient; +import com.publicissapient.kpidashboard.jira.client.ProcessorJiraRestClient; import com.publicissapient.kpidashboard.jira.config.FetchProjectConfiguration; import com.publicissapient.kpidashboard.jira.model.ProjectConfFieldMapping; import com.publicissapient.kpidashboard.jira.service.FetchSprintReport; +import com.publicissapient.kpidashboard.jira.service.JiraClientService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.batch.core.StepContribution; @@ -36,6 +40,7 @@ import org.springframework.stereotype.Component; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -56,6 +61,12 @@ public class SprintReportTasklet implements Tasklet { @Autowired private SprintRepository sprintRepository; + @Autowired + JiraClientService jiraClientService; + + @Autowired + JiraClient jiraClient; + private String sprintId; @Autowired @@ -73,27 +84,31 @@ public SprintReportTasklet(@Value("#{jobParameters['sprintId']}") String sprintI @Override public RepeatStatus execute(StepContribution sc, ChunkContext cc) throws Exception { log.info("Sprint report job started for the sprint : {}", sprintId); - try { - ProjectConfFieldMapping projConfFieldMapping = fetchProjectConfiguration - .fetchConfigurationBasedOnSprintId(sprintId); - KerberosClient krb5Client = null; - SprintDetails sprintDetails = sprintRepository.findBySprintID(sprintId); - List originalBoardIds = sprintDetails.getOriginBoardId(); - for (String boardId : originalBoardIds) { - List sprintDetailsList = fetchSprintReport.getSprints(projConfFieldMapping, boardId, - krb5Client); - if (CollectionUtils.isNotEmpty(sprintDetailsList)) { - // filtering the sprint need to update - Set sprintDetailSet = sprintDetailsList.stream() - .filter(s -> s.getSprintID().equalsIgnoreCase(sprintId)).collect(Collectors.toSet()); - Set setOfSprintDetails = fetchSprintReport.fetchSprints(projConfFieldMapping, - sprintDetailSet, krb5Client, true); - sprintRepository.saveAll(setOfSprintDetails); - } + ProjectConfFieldMapping projConfFieldMapping = fetchProjectConfiguration + .fetchConfigurationBasedOnSprintId(sprintId); + Optional connectionOptional = projConfFieldMapping.getJira().getConnection(); + KerberosClient krb5Client = null; + if (connectionOptional.isPresent()) { + Connection connection = connectionOptional.get(); + krb5Client = new KerberosClient(connection.getJaasConfigFilePath(), connection.getKrb5ConfigFilePath(), + connection.getJaasUser(), connection.getSamlEndPoint(), connection.getBaseUrl()); + } + ProcessorJiraRestClient client = jiraClient.getClient(projConfFieldMapping, krb5Client); + jiraClientService.setRestClientMap(sprintId,client); + jiraClientService.setKerberosClientMap(sprintId,krb5Client); + SprintDetails sprintDetails = sprintRepository.findBySprintID(sprintId); + List originalBoardIds = sprintDetails.getOriginBoardId(); + for (String boardId : originalBoardIds) { + List sprintDetailsList = fetchSprintReport.getSprints(projConfFieldMapping, boardId, + krb5Client); + if (CollectionUtils.isNotEmpty(sprintDetailsList)) { + // filtering the sprint need to update + Set sprintDetailSet = sprintDetailsList.stream() + .filter(s -> s.getSprintID().equalsIgnoreCase(sprintId)).collect(Collectors.toSet()); + Set setOfSprintDetails = fetchSprintReport.fetchSprints(projConfFieldMapping, + sprintDetailSet, krb5Client, true); + sprintRepository.saveAll(setOfSprintDetails); } - } catch (Exception e) { - log.error("Exception while fetching sprint data for the sprint : {}", sprintId, e); - throw e; } return RepeatStatus.FINISHED; } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/SprintScrumBoardTasklet.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/SprintScrumBoardTasklet.java index 4cb8a9c487..637d0f8b89 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/SprintScrumBoardTasklet.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/tasklet/SprintScrumBoardTasklet.java @@ -18,14 +18,14 @@ package com.publicissapient.kpidashboard.jira.tasklet; import com.publicissapient.kpidashboard.common.client.KerberosClient; +import com.publicissapient.kpidashboard.common.model.jira.BoardDetails; import com.publicissapient.kpidashboard.common.model.jira.SprintDetails; import com.publicissapient.kpidashboard.common.repository.jira.SprintRepository; import com.publicissapient.kpidashboard.jira.aspect.TrackExecutionTime; -import com.publicissapient.kpidashboard.jira.client.JiraClient; -import com.publicissapient.kpidashboard.jira.client.ProcessorJiraRestClient; import com.publicissapient.kpidashboard.jira.config.FetchProjectConfiguration; import com.publicissapient.kpidashboard.jira.model.ProjectConfFieldMapping; import com.publicissapient.kpidashboard.jira.service.FetchSprintReport; +import com.publicissapient.kpidashboard.jira.service.JiraClientService; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.configuration.annotation.StepScope; @@ -49,15 +49,15 @@ public class SprintScrumBoardTasklet implements Tasklet { @Autowired FetchProjectConfiguration fetchProjectConfiguration; - @Autowired - JiraClient jiraClient; - @Autowired private FetchSprintReport fetchSprintReport; @Autowired private SprintRepository sprintRepository; + @Autowired + JiraClientService jiraClientService; + private String projectId; @Autowired @@ -77,17 +77,17 @@ public RepeatStatus execute(StepContribution sc, ChunkContext cc) throws Excepti log.info("**** Sprint report for Scrum Board started * * *"); ProjectConfFieldMapping projConfFieldMapping = fetchProjectConfiguration.fetchConfiguration(projectId); log.info("Fetching spring reports for the project : {}", projConfFieldMapping.getProjectName()); - KerberosClient krb5Client = null; - try(ProcessorJiraRestClient client = jiraClient.getClient(projConfFieldMapping, krb5Client)) { + KerberosClient krb5Client = jiraClientService.getKerberosClientMap(projectId); + List boardDetailsList = projConfFieldMapping.getProjectToolConfig().getBoards(); + for (BoardDetails boardDetails : boardDetailsList) { List sprintDetailsList = fetchSprintReport - .createSprintDetailBasedOnBoard(projConfFieldMapping, krb5Client); + .createSprintDetailBasedOnBoard(projConfFieldMapping, krb5Client, boardDetails); sprintRepository.saveAll(sprintDetailsList); - } catch (Exception e) { - log.error("Exception while fetching sprint data for scrum project and board setup", e); - throw e; } + log.info("**** Sprint report for Scrum Board ended * * *"); return RepeatStatus.FINISHED; } + } diff --git a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/writer/IssueScrumWriter.java b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/writer/IssueScrumWriter.java index 0c096df958..7ec271f093 100644 --- a/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/writer/IssueScrumWriter.java +++ b/processors/jira/src/main/java/com/publicissapient/kpidashboard/jira/writer/IssueScrumWriter.java @@ -75,7 +75,7 @@ public class IssueScrumWriter implements ItemWriter { */ @Override public void write(List compositeResults) throws Exception { - List jiraIssues = new ArrayList<>(); + Set jiraIssues = new HashSet<>(); List jiraHistoryItems = new ArrayList<>(); Set accountHierarchies = new HashSet<>(); Map assigneesToSave = new HashMap<>(); @@ -117,7 +117,7 @@ public void write(List compositeResults) throws Excep } } - private void writeJiraItem(List jiraItems) { + private void writeJiraItem(Set jiraItems) { log.info("Writing issues to Jira_Issue Collection"); jiraIssueRepository.saveAll(jiraItems); } diff --git a/processors/sonar/pom.xml b/processors/sonar/pom.xml index 4cb887f7c6..cd0fa8fd13 100644 --- a/processors/sonar/pom.xml +++ b/processors/sonar/pom.xml @@ -24,12 +24,12 @@ com.publicissapient.kpidashboard CodeQuality Processor Microservice jar - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11 diff --git a/processors/teamcity/pom.xml b/processors/teamcity/pom.xml index 951d1eafab..b9333b19ce 100644 --- a/processors/teamcity/pom.xml +++ b/processors/teamcity/pom.xml @@ -23,12 +23,12 @@ com.publicissapient.kpidashboard Teamcity Build Processor Microservice jar - 8.0.0 + 8.0.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.11