Skip to content

Commit 47bac62

Browse files
ivan-risuenoIván Risueño
andauthored
Feature/display new request deletion dialog (#22)
* Update component catalog openapi specification * Add simple dialog when requesting deletion of automatic deletion components * Keep both toast messages when a deletion is requested * Test: Ensure correct dialogs are called when requesting a deletion * Lint: Swap String references with string * Fix toast message --------- Co-authored-by: Iván Risueño <ivan.risueno_martin.ext@boehringer-ingelheim.com>
1 parent 98cfbd9 commit 47bac62

10 files changed

Lines changed: 437 additions & 26 deletions

openapi-specs/component-catalog-v1.0.0.yaml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,50 @@ paths:
7575
application/json:
7676
schema:
7777
$ref: '#/components/schemas/RestErrorMessage'
78+
/project/{projectKey}/component/{componentId}:
79+
get:
80+
tags:
81+
- Project-components
82+
summary: Returns the extended information of a project component given both its project key and component ID in the Bitbucket repository.
83+
operationId: getProjectComponentById
84+
parameters:
85+
- name: projectKey
86+
in: path
87+
description: project key.
88+
required: true
89+
schema:
90+
type: string
91+
- name: componentId
92+
in: path
93+
description: component ID.
94+
required: true
95+
schema:
96+
type: string
97+
responses:
98+
"200":
99+
description: The extended information of a project component.
100+
content:
101+
application/json:
102+
schema:
103+
$ref: '#/components/schemas/ProjectComponentExtendedInfo'
104+
"401":
105+
description: Invalid client token on the request.
106+
content:
107+
application/json:
108+
schema:
109+
$ref: '#/components/schemas/RestErrorMessage'
110+
"403":
111+
description: Insufficient permissions for the client to access the resource.
112+
content:
113+
application/json:
114+
schema:
115+
$ref: '#/components/schemas/RestErrorMessage'
116+
"500":
117+
description: Server error.
118+
content:
119+
application/json:
120+
schema:
121+
$ref: '#/components/schemas/RestErrorMessage'
78122
/catalog-descriptors:
79123
get:
80124
tags:
@@ -847,12 +891,51 @@ components:
847891
canBeDeleted:
848892
type: boolean
849893
example: true
894+
hasAutomatedDeletionWorkflow:
895+
type: boolean
896+
example: true
850897
logoUrl:
851898
type: string
852899
example: https://somepic.jpg
853900
componentUrl:
854901
type: string
855902
example: 'https://bitbucket.com/projects/CATALOGS/repos/project-components/browse/projects'
903+
ProjectComponentParameter:
904+
properties:
905+
name:
906+
type: string
907+
example: 'environment'
908+
values:
909+
type: array
910+
items:
911+
type: string
912+
example:
913+
- 'dev'
914+
- 'test'
915+
ProjectComponentExtendedInfo:
916+
properties:
917+
componentId:
918+
type: string
919+
example: 'nextjs-basic-app'
920+
catalogItemId:
921+
type: string
922+
example: 'some-encoded-info'
923+
catalogItemRef:
924+
type: string
925+
example: 'more-encoded-info'
926+
status:
927+
type: string
928+
example: 'CREATING'
929+
componentUrl:
930+
type: string
931+
example: 'https://bitbucket.com/projects/CATALOGS/repos/project-components/browse/projects'
932+
workflowJobId:
933+
type: string
934+
example: '123456'
935+
parameters:
936+
type: array
937+
items:
938+
$ref: '#/components/schemas/ProjectComponentParameter'
856939
CatalogDescriptor:
857940
properties:
858941
id:
@@ -1206,6 +1289,12 @@ components:
12061289
example: "https://bitbucket.com/projects/DEVSTACK/repos/devstack-component-catalog"
12071290
nullable: true
12081291

1292+
workflowJobId:
1293+
type: string
1294+
description: the workflow job id from AWX to correlate provisioning status with AWX job status updates
1295+
example: "123456"
1296+
nullable: true
1297+
12091298
parameters:
12101299
type: array
12111300
description: List of name/value string parameters.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<div class="dialog-header">
2+
<h2 mat-dialog-title>{{ data.componentName }} - Request Deletion</h2>
3+
<appshell-icon icon="close" (click)="onCancel()"></appshell-icon>
4+
</div>
5+
<mat-dialog-content>
6+
<div class="help-message-ctn">
7+
<appshell-icon class="info-icon" icon="circle_i"></appshell-icon>
8+
<span>To request deletion for deployed components, a change requested is needed.</span>
9+
</div>
10+
</mat-dialog-content>
11+
<mat-dialog-actions align="end">
12+
<button mat-button (click)="onCancel()">Cancel</button>
13+
<button mat-flat-button color="accent" (click)="onAccept()">Request</button>
14+
</mat-dialog-actions>
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
app-request-deletion-dialog {
2+
width: 38.25rem;
3+
display: block !important;
4+
padding: 1.5rem;
5+
box-sizing: border-box;
6+
7+
> * {
8+
padding: 0 !important;
9+
color: var(--appshell-color-dark-green-main) !important;
10+
}
11+
12+
.dialog-header {
13+
margin: 0.5rem 0 0 !important;
14+
display: inline-flex;
15+
flex-direction: row;
16+
justify-content: space-between;
17+
align-items: center;
18+
width: 100%;
19+
20+
h2 {
21+
margin: 0 !important;
22+
padding: 0;
23+
font-family: "AppShellHeadingFont";
24+
font-size: 20px;
25+
font-weight: 500;
26+
line-height: 24px;
27+
28+
&::before,
29+
&::after {
30+
height: auto !important;
31+
}
32+
}
33+
34+
appshell-icon {
35+
cursor: pointer;
36+
}
37+
}
38+
39+
mat-dialog-content {
40+
display: flex;
41+
flex-direction: column;
42+
gap: 16px;
43+
min-height: 100px;
44+
45+
.help-message-ctn {
46+
display: flex;
47+
flex-direction: row;
48+
align-items: center;
49+
gap: 0.75rem;
50+
margin: 1.5rem 0 !important;
51+
padding: 1rem;
52+
53+
background-color: #d2f2f7;
54+
color: #076d7e;
55+
56+
font-size: 14px;
57+
font-weight: 400;
58+
line-height: 20px;
59+
}
60+
}
61+
62+
mat-dialog-actions {
63+
gap: 1rem;
64+
65+
button:first-of-type {
66+
padding: 0.75rem 1rem;
67+
68+
font-size: 0.875rem;
69+
line-height: 1.25rem;
70+
71+
--mdc-text-button-container-height: unset !important;
72+
--mdc-text-button-container-padding-horizontal: 0;
73+
74+
&:hover {
75+
background-color: transparent;
76+
}
77+
}
78+
79+
button:last-of-type {
80+
padding: 0.75rem 1.5rem;
81+
height: auto;
82+
83+
font-size: 0.875rem;
84+
line-height: 1.25rem;
85+
86+
--mdc-filled-button-container-height: unset !important;
87+
88+
display: inline-flex;
89+
align-items: center;
90+
91+
border: 2px solid var(--mdc-filled-button-container-color);
92+
border-radius: 0.125rem;
93+
94+
&:disabled {
95+
border: none;
96+
}
97+
}
98+
}
99+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { CommonModule } from "@angular/common";
2+
import { Component, Inject, ViewEncapsulation } from "@angular/core";
3+
import { FormsModule } from "@angular/forms";
4+
import { MatButtonModule } from "@angular/material/button";
5+
import { MAT_DIALOG_DATA, MatDialogRef, MatDialogContent, MatDialogActions } from "@angular/material/dialog";
6+
import { MatFormFieldModule } from "@angular/material/form-field";
7+
import { MatInputModule } from "@angular/material/input";
8+
import { MatRadioModule } from "@angular/material/radio";
9+
import { RequestDeletionDialogData } from "../../models/request-deletion-dialog-data";
10+
import { AppShellIconComponent } from "@opendevstack/ngx-appshell";
11+
12+
@Component({
13+
selector: 'app-request-deletion-dialog',
14+
imports: [
15+
CommonModule,
16+
MatButtonModule,
17+
MatFormFieldModule,
18+
MatInputModule,
19+
MatRadioModule,
20+
FormsModule,
21+
AppShellIconComponent,
22+
MatDialogContent,
23+
MatDialogActions
24+
],
25+
templateUrl: './request-deletion-simple-dialog.component.html',
26+
styleUrl: './request-deletion-simple-dialog.component.scss',
27+
encapsulation: ViewEncapsulation.None
28+
})
29+
export class RequestDeletionSimpleDialogComponent {
30+
31+
constructor(
32+
public dialogRef: MatDialogRef<RequestDeletionSimpleDialogComponent>,
33+
@Inject(MAT_DIALOG_DATA) public data: RequestDeletionDialogData
34+
) {}
35+
36+
onAccept(): void {
37+
this.dialogRef.close(this.data);
38+
}
39+
40+
onCancel(): void {
41+
this.dialogRef.close();
42+
}
43+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
3+
import { By } from '@angular/platform-browser';
4+
5+
import { RequestDeletionSimpleDialogComponent } from './request-deletion-simple-dialog.component';
6+
import { RequestDeletionDialogData } from '../../models/request-deletion-dialog-data';
7+
8+
describe('RequestDeletionSimpleDialogComponent', () => {
9+
let component: RequestDeletionSimpleDialogComponent;
10+
let fixture: ComponentFixture<RequestDeletionSimpleDialogComponent>;
11+
let dialogRefSpy: jasmine.SpyObj<MatDialogRef<RequestDeletionSimpleDialogComponent>>;
12+
13+
const dialogData: RequestDeletionDialogData = {
14+
componentName: 'test-component',
15+
projectKey: 'test-project',
16+
location: 'test-location'
17+
};
18+
19+
beforeEach(async () => {
20+
dialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['close']);
21+
22+
await TestBed.configureTestingModule({
23+
imports: [RequestDeletionSimpleDialogComponent],
24+
providers: [
25+
{ provide: MatDialogRef, useValue: dialogRefSpy },
26+
{ provide: MAT_DIALOG_DATA, useValue: dialogData }
27+
]
28+
}).compileComponents();
29+
30+
fixture = TestBed.createComponent(RequestDeletionSimpleDialogComponent);
31+
component = fixture.componentInstance;
32+
fixture.detectChanges();
33+
});
34+
35+
it('should create', () => {
36+
expect(component).toBeTruthy();
37+
});
38+
39+
it('should close dialog with data on accept', () => {
40+
component.onAccept();
41+
42+
expect(dialogRefSpy.close).toHaveBeenCalledWith(dialogData);
43+
});
44+
45+
it('should close dialog without data on cancel', () => {
46+
component.onCancel();
47+
48+
expect(dialogRefSpy.close).toHaveBeenCalledWith();
49+
});
50+
51+
it('should call onCancel when close icon is clicked', () => {
52+
spyOn(component, 'onCancel');
53+
54+
const closeIcon = fixture.debugElement.query(
55+
By.css('appshell-icon[icon="close"]')
56+
);
57+
closeIcon.triggerEventHandler('click', null);
58+
59+
expect(component.onCancel).toHaveBeenCalled();
60+
});
61+
62+
it('should call onCancel when Cancel button is clicked', () => {
63+
spyOn(component, 'onCancel');
64+
65+
const cancelButton = fixture.debugElement
66+
.queryAll(By.css('button'))
67+
.find(btn => btn.nativeElement.textContent.trim() === 'Cancel');
68+
69+
cancelButton!.nativeElement.click();
70+
71+
expect(component.onCancel).toHaveBeenCalled();
72+
});
73+
74+
it('should call onAccept when Request button is clicked', () => {
75+
spyOn(component, 'onAccept');
76+
77+
const requestButton = fixture.debugElement
78+
.queryAll(By.css('button'))
79+
.find(btn => btn.nativeElement.textContent.trim() === 'Request');
80+
81+
requestButton!.nativeElement.click();
82+
83+
expect(component.onAccept).toHaveBeenCalled();
84+
});
85+
});

src/app/models/project-component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export interface ProjectComponent {
66
logo: string | null;
77
url: string;
88
canDelete: boolean;
9+
hasAutomatedDeletionWorkflow: boolean;
910
}

0 commit comments

Comments
 (0)