Skip to content

Commit e6b3c95

Browse files
authored
Merge pull request #124 from delphi-hub/feature/addLabels
Feature/add labels
2 parents 5fc0560 + 7592e4b commit e6b3c95

11 files changed

+250
-31
lines changed

app/controllers/ApiRouter.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,6 @@ class ApiRouter @Inject()(irController: InstanceRegistryController, sysControlle
4444
case POST(p"/resumeInstance" ? q"instanceID=$instanceID") => irController.handleRequest(action="/resume", instanceID)
4545
case POST(p"/deleteInstance" ? q"instanceID=$instanceID") => irController.handleRequest(action="/delete", instanceID)
4646
case POST(p"/reconnectInstance" ? q"from=$from"& q"to=$to") => irController.reconnect(from.toInt, to.toInt)
47+
case POST(p"/labelInstance" ? q"instanceID=$instanceID"& q"label=$label") => irController.labelInstance(instanceID, label)
4748
}
4849
}

app/controllers/InstanceRegistryController.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,22 @@ class InstanceRegistryController @Inject()(implicit system: ActorSystem, mat: Ma
186186
}
187187
}(myExecutionContext)
188188
}
189+
190+
def labelInstance(instanceID: String, label: String): Action[AnyContent] = Action.async
191+
{
192+
request =>
193+
ws.url(instanceRegistryUri + "/instances/" + instanceID + "/label")
194+
.withHttpHeaders(("Authorization", s"Bearer ${AuthProvider.generateJwt()}"))
195+
.post(Json.obj("Label" -> label))
196+
.map { response =>
197+
response.status match {
198+
// scalastyle:off magic.number
199+
case 202 =>
200+
// scalastyle:on magic.number
201+
Ok(response.body)
202+
case x: Any =>
203+
new Status(x)
204+
}
205+
}(myExecutionContext)
206+
}
189207
}

client/src/app/api/api/api.service.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
PAUSE_INSTANCE,
3636
RESUME_INSTANCE,
3737
DELETE_INSTANCE,
38+
NEW_LABEL_INSTANCE,
3839
INSTANCE_NETWORK,
3940
RECONNECT
4041
} from '../variables';
@@ -167,6 +168,17 @@ export class ApiService {
167168
return this.postAction(DELETE_INSTANCE, instanceId);
168169
}
169170

171+
/**
172+
* Create an Instance
173+
* @param instanceId
174+
* @param label
175+
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
176+
* @param reportProgress flag to report request and response progress.
177+
*/
178+
public labelInstance( instanceId: string, labelName: string, observe: any = 'body', reportProgress: boolean = false) {
179+
return this.postLabel(NEW_LABEL_INSTANCE, instanceId, labelName);
180+
}
181+
170182
private get<T>(endpoint: string, componentType?: string) {
171183

172184
let queryParameters = new HttpParams({ encoder: new CustomHttpUrlEncodingCodec() });
@@ -227,6 +239,30 @@ export class ApiService {
227239
);
228240
}
229241

242+
private postLabel(endpoint: string, idInstance: string, labelName: string, observe: any = 'body', reportProgress: boolean = false): any {
243+
if (idInstance === null || idInstance === undefined && labelName === null || labelName === undefined) {
244+
throw new Error('Required parameter instanceId and Label Name was null or undefined when calling postlabel.');
245+
}
246+
247+
let queryParameters = new HttpParams({ encoder: new CustomHttpUrlEncodingCodec() });
248+
if (idInstance !== undefined && labelName !== undefined) {
249+
queryParameters = queryParameters.set('instanceID', <any>idInstance);
250+
queryParameters = queryParameters.set('label', <any>labelName);
251+
}
252+
253+
let headers = this.defaultHeaders;
254+
255+
// to determine the Accept header
256+
const httpHeaderAccepts: string[] = [
257+
'application/json'
258+
];
259+
const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts);
260+
if (httpHeaderAcceptSelected !== undefined) {
261+
headers = headers.set('Accept', httpHeaderAcceptSelected);
262+
}
263+
264+
return this.commonConf(endpoint, queryParameters, observe, reportProgress);
265+
}
230266

231267
private postAction(endpoint: string, idInstance: string, observe: any = 'body', reportProgress: boolean = false) {
232268
let queryParam = new HttpParams({ encoder: new CustomHttpUrlEncodingCodec() });

client/src/app/api/variables.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export const PAUSE_INSTANCE = 'api/pauseInstance';
3030
export const RESUME_INSTANCE = 'api/resumeInstance';
3131
export const DELETE_INSTANCE = 'api/deleteInstance';
3232
export const RECONNECT = 'api/reconnectInstance';
33+
export const NEW_LABEL_INSTANCE = 'api/labelInstance';
3334
export const COLLECTION_FORMATS = {
3435
'csv': ',',
3536
'tsv': ' ',

client/src/app/dashboard/dashboard.module.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { GraphViewModule } from './graph-view/graph-view.module';
4242
import { InforCenterComponent } from './infor-center/infor-center.component';
4343
import { InstanceDetailsComponent } from './instance-details/instance-details.component';
4444
import { TableOverviewComponent } from './table-overview/table-overview.component';
45+
import { LabelDialogComponent } from './label-dialog/label-dialog.component';
4546

4647

4748
@NgModule({
@@ -74,10 +75,11 @@ import { TableOverviewComponent } from './table-overview/table-overview.componen
7475
AddDialogComponent,
7576
InforCenterComponent,
7677
InstanceDetailsComponent,
77-
TableOverviewComponent
78+
TableOverviewComponent,
79+
LabelDialogComponent
7880
],
7981
entryComponents: [
80-
DeleteDialogComponent, AddDialogComponent
82+
DeleteDialogComponent, AddDialogComponent, LabelDialogComponent
8183
],
8284
providers: [],
8385
})

client/src/app/dashboard/label-dialog/label-dialog.component.css

Whitespace-only changes.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
<h2 mat-dialog-title>Add Label</h2>
3+
<div mat-dialog-content>
4+
<form class="mat-dialog-content" #formControl="ngForm">
5+
<div class="form">
6+
<mat-form-field color="primary">
7+
<input matInput #input class="form-control" placeholder="Label name" [(ngModel)]="labelName" name="labelName" required>
8+
<mat-error *ngIf="formControl.invalid">{{getErrorMessage()}}</mat-error>
9+
</mat-form-field>
10+
</div>
11+
</form>
12+
</div>
13+
<mat-dialog-actions>
14+
<button mat-raised-button color="primary" [disabled]="!formControl.valid" [mat-dialog-close]="1" id="confirmButton" (click)="onConfirmAddLabel()">Add</button>
15+
<button mat-raised-button (click)="onCloseCancelLabel()">Cancel</button>
16+
</mat-dialog-actions>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
2+
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
3+
import { HttpClientModule } from '@angular/common/http';
4+
import { BrowserModule } from '@angular/platform-browser';
5+
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
6+
import { MatIconModule } from '@angular/material/icon';
7+
import { MatInputModule } from '@angular/material';
8+
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
9+
import { FormsModule } from '@angular/forms';
10+
import { MatFormFieldModule } from '@angular/material/form-field';
11+
import { LabelDialogComponent } from './label-dialog.component';
12+
import { ApiService } from '../../api/api/api.service';
13+
14+
describe('LabelDialogComponent', () => {
15+
let component: LabelDialogComponent;
16+
let fixture: ComponentFixture<LabelDialogComponent>;
17+
const mocklabelDialogRef = {
18+
close: jasmine.createSpy('close')
19+
};
20+
21+
beforeEach(async(() => {
22+
TestBed.configureTestingModule({
23+
declarations: [LabelDialogComponent],
24+
imports: [ HttpClientTestingModule, HttpClientModule, BrowserModule, BrowserAnimationsModule, MatIconModule,
25+
MatInputModule, MatDialogModule, FormsModule, MatFormFieldModule ],
26+
providers: [{
27+
provide: MatDialogRef,
28+
useValue: mocklabelDialogRef
29+
}, {
30+
provide: MAT_DIALOG_DATA,
31+
useValue: {}
32+
}]
33+
34+
}).compileComponents();
35+
}));
36+
37+
beforeEach(() => {
38+
fixture = TestBed.createComponent(LabelDialogComponent);
39+
component = fixture.componentInstance;
40+
fixture.detectChanges();
41+
});
42+
43+
it('should check for confirm button inside the Label dialog', () => {
44+
component.onConfirmAddLabel();
45+
expect(mocklabelDialogRef.close).toHaveBeenCalled();
46+
});
47+
48+
it('should check for button inside the Label dialog', () => {
49+
component.onCloseCancelLabel();
50+
expect(mocklabelDialogRef.close).toHaveBeenCalled();
51+
});
52+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Component, OnInit } from '@angular/core';
2+
import { MatDialogRef } from '@angular/material';
3+
import { FormControl, Validators } from '@angular/forms';
4+
5+
@Component({
6+
selector: 'app-label-dialog',
7+
templateUrl: './label-dialog.component.html',
8+
styleUrls: ['./label-dialog.component.css']
9+
})
10+
export class LabelDialogComponent implements OnInit {
11+
12+
constructor(public thislabelDialogRef: MatDialogRef<LabelDialogComponent>) { }
13+
14+
labelName: String;
15+
16+
formControl = new FormControl('', [
17+
Validators.required
18+
]);
19+
20+
ngOnInit() {
21+
}
22+
23+
getErrorMessage() {
24+
return this.formControl.hasError('required') ? 'Required field' :
25+
this.formControl.hasError('labelName') ? 'Not a valid name' :
26+
'';
27+
}
28+
29+
onConfirmAddLabel(): void {
30+
31+
this.thislabelDialogRef.close({
32+
status: 'Add',
33+
labelName: this.labelName
34+
});
35+
}
36+
37+
onCloseCancelLabel() {
38+
this.thislabelDialogRef.close('CancelLabel');
39+
40+
}
41+
}

client/src/app/dashboard/table-all/table-all.component.html

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,6 @@
4949
<mat-cell *matCellDef="let element"> {{element.instanceState}} </mat-cell>
5050
</ng-container>
5151

52-
<ng-container matColumnDef="Details">
53-
<mat-header-cell *matHeaderCellDef> </mat-header-cell>
54-
<mat-cell *matCellDef="let element; let row" class="example-element-row"
55-
[class.example-expanded-row]="expandedElement === element"
56-
(click)="expandedElement = expandedElement === element ? null : element; data = onRowClicked(row)">
57-
<button mat-button [ngClass]="'customOutline'">
58-
View Details
59-
</button>
60-
</mat-cell>
61-
</ng-container>
62-
6352
<ng-container matColumnDef="action">
6453
<mat-header-cell [ngClass]="'customWidthClass'" *matHeaderCellDef> Action </mat-header-cell>
6554
<mat-cell [ngClass]="'customWidthClass customWidthClassNew'"
@@ -93,18 +82,34 @@
9382

9483
</ng-container>
9584

85+
<ng-container matColumnDef="Details">
86+
<mat-header-cell *matHeaderCellDef> </mat-header-cell>
87+
<mat-cell *matCellDef="let element; let row" class="example-element-row"
88+
[class.example-expanded-row]="expandedID === element.id"
89+
(click)="onRowClicked(row)">
90+
<button mat-button [ngClass]="'customOutline'">
91+
View Details
92+
</button>
93+
</mat-cell>
94+
</ng-container>
95+
9696
<ng-container matColumnDef="expandedDetail">
97-
<td mat-cell *matCellDef="let element" [attr.colspan]="displayedColumns.length">
97+
<td mat-cell *matCellDef="let element; let row" [attr.colspan]="displayedColumns.length">
9898
<div class="example-element-detail"
99-
[@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
99+
[@detailExpand]="element.id == expandedID ? 'expanded' : 'collapsed'">
100100
<table mat-table [dataSource]="data" class="mat-elevation-z8">
101101
<ng-container matColumnDef="dockerId">
102102
<th mat-header-cell *matHeaderCellDef> DockerID </th>
103103
<td mat-cell *matCellDef="let element"> {{element.dockerId}} </td>
104104
</ng-container>
105105
<ng-container matColumnDef="labels">
106106
<th mat-header-cell *matHeaderCellDef> Labels </th>
107-
<td mat-cell *matCellDef="let element"> {{element.labels}} </td>
107+
<td mat-cell *matCellDef="let element"> {{element.labels}}
108+
<button mat-button [disabled]="(element.labels.length === 3)" mat-icon-button (click)="openlabelDialog(row.id)"
109+
matTooltip="Add labels">
110+
<mat-icon aria-label="add_circle_outline">add_circle_outline</mat-icon>
111+
</button>
112+
</td>
108113
</ng-container>
109114
<tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
110115
<tr mat-row *matRowDef="let row; columns: columnsToDisplay;"></tr>

0 commit comments

Comments
 (0)