Skip to content

Commit

Permalink
Feature: scenario trends sharing (#367)
Browse files Browse the repository at this point in the history
  • Loading branch information
ludeknovy authored Nov 10, 2023
1 parent fbafb5a commit b83948a
Show file tree
Hide file tree
Showing 25 changed files with 530 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Component, Input, OnInit } from "@angular/core";
import { FormControl, Validators, FormGroup } from "@angular/forms";
import { NgbModal, NgbModalRef } from "@ng-bootstrap/ng-bootstrap";
import { of } from "rxjs";
import { catchError } from "rxjs/operators";
import { ItemsApiService } from "src/app/items-api.service";
import { ItemsService } from "src/app/items.service";
import { NotificationMessage } from "src/app/notification/notification-messages";
Expand Down
9 changes: 9 additions & 0 deletions src/app/notification/notification-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,15 @@ export class NotificationMessage {
return this.statusCodeMessage(response, "Settings updated");
}

createScenarioShareToken(response) {
return this.statusCodeMessage(response, "Scenario share token was created");
}

deleteScenarioShareTokenNotification(response) {
return this.statusCodeMessage(response, "Scenario share token was deleted");

}

appInitialization(response) {
return this.statusCodeMessage(response, "")
}
Expand Down
5 changes: 5 additions & 0 deletions src/app/scenario-api.service.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface ScenarioShareToken {
id: string
toke: string
note: string
}
17 changes: 15 additions & 2 deletions src/app/scenario-api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { HttpClient } from "@angular/common/http";
import { BehaviorSubject, Observable } from "rxjs";
import { IScenarios, ScenarioNotifications } from "./items.service.model";
import { Scenario } from "./scenario.service.model";
import { ScenarioShareToken } from "./scenario-api.service.model";

@Injectable({
providedIn: "root"
Expand Down Expand Up @@ -36,9 +37,9 @@ export class ScenarioApiService {
`projects/${projectName}/scenarios/${scenarioName}/trends`, { params });
}

fetchScenarioEnvironments(projectName, scenarioName): Observable<any> {
fetchScenarioEnvironments(projectName, scenarioName, params): Observable<any> {
return this.http.get<any>(
`projects/${projectName}/scenarios/${scenarioName}/environment`);
`projects/${projectName}/scenarios/${scenarioName}/environment`, { params });
}

createNewScenario(projectName, body): Observable<Record<string, any>> {
Expand All @@ -61,6 +62,18 @@ export class ScenarioApiService {
return this.http.post(`projects/${projectName}/scenarios/${scenarioName}/trends/settings`, body, { observe: "response" });
}

fetchScenarioShareTokens(projectName, scenarioName): Observable<ScenarioShareToken[]> {
return this.http.get<ScenarioShareToken[]>(`projects/${projectName}/scenarios/${scenarioName}/share-token`);
}

createScenarioShareToken(projectName, scenarioName, body) {
return this.http.post(`projects/${projectName}/scenarios/${scenarioName}/share-token`, body, { observe: "response" });
}

deleteScenarioShareToken(projectName, scenarioName, token) {
return this.http.delete(`projects/${projectName}/scenarios/${scenarioName}/share-token/${token}`, { observe: "response" })
}

setData(data) {
this.response.next(data);
}
Expand Down
18 changes: 14 additions & 4 deletions src/app/scenario.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BehaviorSubject, Observable } from "rxjs";
import { ScenarioNotifications } from "./items.service.model";
import { ScenarioApiService } from "./scenario-api.service";
import { EnvironmentService } from "./_services/environment.service";
import { ScenarioShareToken } from "./scenario-api.service.model";

@Injectable({
providedIn: "root"
Expand All @@ -18,6 +19,10 @@ export class ScenarioService {

private notifications = new BehaviorSubject<ScenarioNotifications[]>([]);
public notifications$ = this.notifications.asObservable();

private scenarioShareTokens = new BehaviorSubject<ScenarioShareToken[]>([])
public scenarioShareTokens$ = this.scenarioShareTokens.asObservable()

private environment: string;

constructor(
Expand All @@ -30,8 +35,8 @@ export class ScenarioService {
}


fetchScenarioTrends(projectName, scenarioName) {
const queryParams = { environment: this.environment };
fetchScenarioTrends(projectName, scenarioName, params = {}) {
const queryParams = { environment: this.environment, ...params };
this.scenarioApiService.fetchScenarioTrend(projectName, scenarioName, queryParams)
.subscribe(_ => this.trends.next(_));
}
Expand All @@ -41,8 +46,13 @@ export class ScenarioService {
.subscribe(_ => this.notifications.next(_));
}

fetchEnvironments(projectName, scenarioName) {
this.scenarioApiService.fetchScenarioEnvironments(projectName, scenarioName)
fetchScenarioShareTokens(projectName: string, scenarioName: string) {
this.scenarioApiService.fetchScenarioShareTokens(projectName, scenarioName)
.subscribe(tokens => this.scenarioShareTokens.next(tokens))
}

fetchEnvironments(projectName, scenarioName, queryParams = {}) {
this.scenarioApiService.fetchScenarioEnvironments(projectName, scenarioName, queryParams)
.subscribe(_ => this.environments.next(_));
}

Expand Down
4 changes: 4 additions & 0 deletions src/app/scenario/environments/environments.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ describe("EnvironmentsComponent", () => {
fixture = TestBed.createComponent(EnvironmentsComponent);
component = fixture.componentInstance;
component.params = { projectName: "test-project", scenarioName: "test-scenario" };
component.anonymous = {
token: "",
isAnonymous: false,
}
fixture.detectChanges();
});

Expand Down
12 changes: 8 additions & 4 deletions src/app/scenario/environments/environments.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class EnvironmentsComponent implements OnInit {
defaultEnvironment = "All Environments";

@Input() params
@Input() anonymous: { token?: string, isAnonymous: boolean}
constructor(
private scenarioService: ScenarioService,
private itemsService: ItemsService,
Expand All @@ -24,7 +25,7 @@ export class EnvironmentsComponent implements OnInit {
}

ngOnInit(): void {
this.scenarioService.fetchEnvironments(this.params.projectName, this.params.scenarioName);
this.scenarioService.fetchEnvironments(this.params.projectName, this.params.scenarioName, { token: this.anonymous.token });
this.reloadData("")

}
Expand All @@ -38,9 +39,12 @@ export class EnvironmentsComponent implements OnInit {

private reloadData(environment) {
this.environmentService.setEnvironment(environment)
this.itemsService.fetchItems(this.params.projectName, this.params.scenarioName, { limit: 15, offset: 0 });
this.scenarioService.fetchScenarioTrends(this.params.projectName, this.params.scenarioName)
this.itemsService.setProcessingItemsIntervalSubscription(this.params.projectName, this.params.scenarioName);

if (!this.anonymous.isAnonymous) {
this.itemsService.fetchItems(this.params.projectName, this.params.scenarioName, { limit: 15, offset: 0 });
this.itemsService.setProcessingItemsIntervalSubscription(this.params.projectName, this.params.scenarioName);
}
this.scenarioService.fetchScenarioTrends(this.params.projectName, this.params.scenarioName, { token: this.anonymous.token })
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,6 @@ <h6>Generate extra chart aggregations <i class="fas fa-flask"></i></h6>

</ng-template>
</li>


</ul>
<div [ngbNavOutlet]="nav" class="ms-2 ml-10 w-100" ></div>

Expand Down
13 changes: 7 additions & 6 deletions src/app/scenario/scenario.component.html
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
<app-control-panel>
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<app-breadcrumb></app-breadcrumb>
<app-breadcrumb [isAnonymous]="isAnonymous"></app-breadcrumb>

</li>
</ul>
<div>

<div class="btn-group">
<app-environments [params]="params"></app-environments>
<app-add-new-item-modal></app-add-new-item-modal>
<div display="dynamic" [placement]="['bottom-right', 'bottom-left']" class="btn-group" ngbDropdown role="group"
<app-environments [params]="params" [anonymous]="{ token, isAnonymous }"></app-environments>
<app-add-new-item-modal *ngIf="!isAnonymous"></app-add-new-item-modal>
<div *ngIf="!isAnonymous" display="dynamic" [placement]="['bottom-right', 'bottom-left']" class="btn-group" ngbDropdown role="group"
aria-label="Button group with nested dropdown">
<button class="btn btn-sm jtl-no-glow jtl-control-menu hamburger-menu" ngbDropdownToggle><i
class="fas fa-bars"></i></button>
<div class="dropdown-menu jtl-dropdown-control-menu" ngbDropdownMenu>
<app-scenario-settings (scenarioNameChangeEvent)="updateScenarioName($event)"></app-scenario-settings>
<app-external-notification></app-external-notification>
<app-share-token [params]="params"></app-share-token>
<app-delete-scenario [scenarioData]="params"></app-delete-scenario>
</div>
</div>
Expand All @@ -26,14 +27,14 @@
</app-control-panel>
<ngx-spinner bdColor="rgba(51, 51, 51, 0.8)" size="default" color="#fff" type="pacman"></ngx-spinner>

<div class="row">
<div class="row" >
<div class="col">
<div class="items-overview content-container">
<div class="container-fluid">

<app-scenario-trends [params]="params"></app-scenario-trends>

<div class="row rr">
<div class="row rr" *ngIf="!isAnonymous">
<div class="col" *ngIf="items$ | async; let items">
<div class="card recent-runs card-shadow">
<h6 class="card-header bg-transparent">Test Runs
Expand Down
73 changes: 48 additions & 25 deletions src/app/scenario/scenario.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, OnInit, OnDestroy } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { of, Observable, Subscription } from "rxjs";
import { switchMap, catchError } from "rxjs/operators";
import { switchMap, catchError, withLatestFrom } from "rxjs/operators";
import { Items } from "../items.service.model";
import { ItemsService } from "../items.service";
import { SharedMainBarService } from "../shared-main-bar.service";
Expand All @@ -28,6 +28,8 @@ export class ScenarioComponent implements OnInit, OnDestroy {
processingItems;
subscription: Subscription;
zeroErrorToleranceEnabled: boolean;
token: string;
isAnonymous = false;

constructor(
private route: ActivatedRoute,
Expand All @@ -38,8 +40,6 @@ export class ScenarioComponent implements OnInit, OnDestroy {
private sharedMainBarService: SharedMainBarService,
private environmentService: EnvironmentService,
) {
this.items$ = itemsService.items$;
this.environment$ = this.environmentService.environment$.subscribe(value => this.page = 1);
}

ngOnDestroy() {
Expand All @@ -49,32 +49,55 @@ export class ScenarioComponent implements OnInit, OnDestroy {
}

ngOnInit() {
this.route.params.pipe<any>(
switchMap(routeParams => {
this.params = routeParams;
this.sharedMainBarService.setProjectName(this.params.projectName);
this.scenarioApiService.getScenario(this.params.projectName, this.params.scenarioName).subscribe(_ => {
this.zeroErrorToleranceEnabled = _.zeroErrorToleranceEnabled;
});
return new Observable().pipe(catchError(err => of([])));
})
).subscribe(_ => _);
this.subscription = this.itemsService.processingItems$.subscribe((_) => {
this.processingItems = _;
const { inprogress } = _ as any;
if (Array.isArray(inprogress)) {
const processingItems = inprogress.map((item) => item.id);
const reloadItems = !this.currentProcessingItems.every((id) => processingItems.includes(id));
if (reloadItems) {
this.itemsService.fetchItems(this.params.projectName, this.params.scenarioName, { limit: LIMIT, offset: 0 });
this.scenarioService.fetchScenarioTrends(this.params.projectName, this.params.scenarioName);
this.scenarioService.fetchEnvironments(this.params.projectName, this.params.scenarioName);
}
return this.currentProcessingItems = inprogress.map((item) => item.id);
this.route.queryParams.subscribe(_ => {
this.token = _.token;
if (this.token) {
this.isAnonymous = true;
}
});

if (!this.isAnonymous) {
this.items$ = this.itemsService.items$;
this.environment$ = this.environmentService.environment$.subscribe(value => this.page = 1);

this.route.params.pipe<any>(
switchMap(routeParams => {
this.params = routeParams;
this.sharedMainBarService.setProjectName(this.params.projectName);
this.scenarioApiService.getScenario(this.params.projectName, this.params.scenarioName).subscribe(_ => {
this.zeroErrorToleranceEnabled = _.zeroErrorToleranceEnabled;
});
return new Observable().pipe(catchError(err => of([])));
})
).subscribe(_ => _);
this.subscription = this.itemsService.processingItems$.subscribe((_) => {
this.processingItems = _;
const { inprogress } = _ as any;
if (Array.isArray(inprogress)) {
const processingItems = inprogress.map((item) => item.id);
const reloadItems = !this.currentProcessingItems.every((id) => processingItems.includes(id));
if (reloadItems) {
this.itemsService.fetchItems(this.params.projectName, this.params.scenarioName, { limit: LIMIT, offset: 0 });
this.scenarioService.fetchScenarioTrends(this.params.projectName, this.params.scenarioName);
this.scenarioService.fetchEnvironments(this.params.projectName, this.params.scenarioName);
}
return this.currentProcessingItems = inprogress.map((item) => item.id);
}
});
} else {
this.route.params.pipe<any>(
switchMap(routeParams => {
this.params = routeParams;
return new Observable().pipe(catchError(err => of([])));
})
).subscribe(_ => _);
}



}


loadMore() {
const offset = (this.page - 1) * OFFSET;
this.itemsService.fetchItems(this.params.projectName, this.params.scenarioName, { limit: LIMIT, offset });
Expand Down
8 changes: 6 additions & 2 deletions src/app/scenario/scenario.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import { RoleModule } from "../_directives/role.module";
import { AddNewItemModule } from "./add-new-item/add-new-item.module";
import { ScenarioTrendsSettingsComponent } from "./scenario-trends/scenario-trends-settings/scenario-trends-settings.component";
import { EnvironmentsComponent } from "./environments/environments.component";
import { NewScenarioShareTokenComponent } from "./share-token/new-share-token/new-scenario-share-token.component";
import { DeleteScenarioShareTokenComponent } from "./share-token/delete-share-token/delete-scenario-share-token.component";
import { ScenarioShareTokenComponent } from "./share-token/scenario-share-token.component";

const routes: Routes = [
{
Expand All @@ -37,7 +40,7 @@ const routes: Routes = [
@NgModule({
declarations: [ScenarioComponent, ScenarioTrendsComponent,
SettingsScenarioComponent, DeleteScenarioComponent, ExternalNotificationComponent,
ItemControlsComponent, AddNewExternalNotificationComponent, DeleteExternalNotificationComponent, ScenarioTrendsSettingsComponent, EnvironmentsComponent
ItemControlsComponent, AddNewExternalNotificationComponent, DeleteExternalNotificationComponent, ScenarioTrendsSettingsComponent, EnvironmentsComponent, NewScenarioShareTokenComponent, DeleteScenarioShareTokenComponent, ScenarioShareTokenComponent, ScenarioShareTokenComponent
],
imports: [
CommonModule, RouterModule.forRoot(routes), NgxSpinnerModule, NgbModule, SharedModule, HighchartsChartModule,
Expand All @@ -48,4 +51,5 @@ const routes: Routes = [
SettingsScenarioComponent, DeleteScenarioComponent, ExternalNotificationComponent,
ItemControlsComponent]
})
export class ScenarioModule { }
export class ScenarioModule {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.revoke {
margin-left: 10px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<ng-template #content let-modal>
<div class="modal-header">
<h5 class="modal-title" id="modal-basic-title">Delete Share Link</h5>
<button type="button" style="outline: none;" class="close" aria-label="Close" (click)="modal.dismiss('Cross click')">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form [formGroup]="deleteScenarioShareTokenForm" (ngSubmit)="onSubmit()">
<div class="modal-body">
<div class="alert alert-danger" role="alert">
<i class="fas fa-info-circle"> </i>
By deleting this token you will immediately revoke its access.
</div>
<div class="form-group mb-3">
<label for="deleteCheck">Please enter 5 random characters to confirm this action:</label>
<input type="deleteCheck" id="deleteCheck" class="form-control" formControlName="deleteCheck" aria-label="Default"
aria-describedby="inputGroup-sizing-default">
<div class="form-control-feedback" *ngIf="deleteCheck.errors && (deleteCheck.dirty || deleteCheck.touched)">
<p class="form-validation-error" *ngIf="deleteCheck.errors.minlength">Please enter at least 5 characters...</p>
</div>
</div>

</div>
<div class="modal-footer">
<button type="submit" class="btn btn-danger">Delete</button>
</div>
</form>

</ng-template>

<button class="btn btn-sm jtl-btn-light jtl-no-glow revoke" (click)="open(content)">
Delete
</button>
Loading

0 comments on commit b83948a

Please sign in to comment.