diff --git a/nginx.conf b/nginx.conf index ff4d1c3f..52b518c7 100644 --- a/nginx.conf +++ b/nginx.conf @@ -5,13 +5,13 @@ events { } http { - client_max_body_size 2048M; + client_max_body_size 5120M; server { listen 80; server_name localhost; - client_max_body_size 2048M; + client_max_body_size 5120M; root /usr/share/nginx/html; index index.html index.htm; include /etc/nginx/mime.types; diff --git a/package-lock.json b/package-lock.json index 8a667c3e..5198af6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13557,9 +13557,9 @@ } }, "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", diff --git a/src/app/administration/api-token/api-keys.component.html b/src/app/administration/api-token/api-keys.component.html index c97e22e4..ccfac534 100644 --- a/src/app/administration/api-token/api-keys.component.html +++ b/src/app/administration/api-token/api-keys.component.html @@ -21,11 +21,8 @@
API Keys - - - No api tokens... - - + Nothing here yet! Add your first api token. + diff --git a/src/app/administration/projects/administration.component.html b/src/app/administration/projects/administration.component.html index a8fd9f05..a3cc5538 100644 --- a/src/app/administration/projects/administration.component.html +++ b/src/app/administration/projects/administration.component.html @@ -22,11 +22,7 @@
Projects - - - No data... - - + Nothing here yet! Add your first project. diff --git a/src/app/dashboard/dashboard.component.html b/src/app/dashboard/dashboard.component.html index 56061c94..64bdc1ca 100644 --- a/src/app/dashboard/dashboard.component.html +++ b/src/app/dashboard/dashboard.component.html @@ -81,7 +81,7 @@
Recent Test Runs
- No Data... + Nothing here yet! Upload some test reports. Show me how! diff --git a/src/app/graphs/scenarios.ts b/src/app/graphs/scenarios.ts index 8ecdaf7d..f2728d96 100644 --- a/src/app/graphs/scenarios.ts +++ b/src/app/graphs/scenarios.ts @@ -11,7 +11,7 @@ export const scenarioHistory = (inputData) => { }; } const dt = inputData.map(_ => { - return { percentil: _.percentil, date: _.startDate }; + return { percentile90: _.percentile90, date: _.startDate }; }); return { type: "bar", @@ -20,7 +20,7 @@ export const scenarioHistory = (inputData) => { labels: dt.map(_ => _.date), datasets: [ { - data: dt.map(_ => _.percentil), + data: dt.map(_ => _.percentile90), backgroundColor: "rgb(17,122,139, 0.8)", fill: true, borderWidth: 1, diff --git a/src/app/item-detail/item-detail.component.html b/src/app/item-detail/item-detail.component.html index db669599..739f1edb 100644 --- a/src/app/item-detail/item-detail.component.html +++ b/src/app/item-detail/item-detail.component.html @@ -125,20 +125,52 @@

{{itemData.overview.throughput > 1000 ? -
+
-

{{ - Math.round((itemData.overview.percentil / 1000) * 100) / 100}} s +

{{ + Math.round(((itemData.overview.percentil || itemData.overview.percentile90) / 1000) * 100) / 100}} s

-

{{ - itemData.overview.percentil}} ms +

{{ + itemData.overview.percentil || itemData.overview.percentile90}} ms

+ +
+
+
+

{{ + Math.round((itemData.overview.percentile95 / 1000) * 100) / 100}} s +

+

{{ + itemData.overview.percentile95}} ms +

+

N/A ms

+ +
+ +
+
+ +
+
+
+

{{ + Math.round((itemData.overview.percentile99 / 1000) * 100) / 100}} s +

+

{{ + itemData.overview.percentile99}} ms +

+

N/A ms

+
+ +
+
+
diff --git a/src/app/item-detail/request-stats/request-stats-compare.component.spec.ts b/src/app/item-detail/request-stats/request-stats-compare.component.spec.ts index 1abf124b..00cad592 100644 --- a/src/app/item-detail/request-stats/request-stats-compare.component.spec.ts +++ b/src/app/item-detail/request-stats/request-stats-compare.component.spec.ts @@ -62,7 +62,10 @@ describe("RequestStatsCompareComponent", () => { endDate: "", startDate: "", errorCount: 0, - percentil: 10, + percentil: null, + percentile90: 10, + percentile95: 10, + percentile99: 10, errorRate: 0, throughput: 100 }, diff --git a/src/app/items.service.model.ts b/src/app/items.service.model.ts index 460776e5..ba0e90ef 100644 --- a/src/app/items.service.model.ts +++ b/src/app/items.service.model.ts @@ -75,7 +75,10 @@ interface TopMetricsSettings { avgResponseTime: boolean; avgConnectionTime: boolean; avgLatency: boolean; - percentile: boolean; + percentile: boolean; // legacy setting + percentile90: boolean; + percentile95: boolean; + percentile99: boolean } interface ItemOverview { @@ -86,20 +89,10 @@ interface ItemOverview { endDate: string; errorRate: number; maxVu: number; - percentil: number; - startDate: string; - throughput: number; - errorCount?: number; -} - -interface ItemOverview { - avgLatency: number; - avgResponseTime: number; - duration: number; - endDate: string; - errorRate: number; - maxVu: number; - percentil: number; + percentil?: number; // legacy, it needs to be kept for backwards compatibility + percentile90: number; + percentile95: number; + percentile99: number; startDate: string; throughput: number; errorCount?: number; diff --git a/src/app/notification/notification-messages.ts b/src/app/notification/notification-messages.ts index 2bc794a4..059eb638 100644 --- a/src/app/notification/notification-messages.ts +++ b/src/app/notification/notification-messages.ts @@ -14,7 +14,7 @@ export class NotificationMessage { return this.statusCodeMessage(response, "New project has been saved"); } - newScenarionNotificationMessage(response) { + newScenarioNotificationMessage(response) { return this.statusCodeMessage(response, "New scenario has been created"); } @@ -100,7 +100,7 @@ export class NotificationMessage { } private statusCodeMessage(response, successMessage) { - let message = { success: false, message: (typeof response === "string" && response?.includes("Unexpected error")) ? response : "Unexpected error occurred" }; + let message = { success: false, message: (typeof response === "string" && !response?.includes("Unexpected error")) ? response : "Unexpected error occurred" }; if (response.status >= 200 && response.status < 300) { message = { success: true, message: successMessage }; } else if (response.status === 400) { diff --git a/src/app/project/graph/scenarios-graph.component.ts b/src/app/project/graph/scenarios-graph.component.ts index 227a10a8..75d32290 100644 --- a/src/app/project/graph/scenarios-graph.component.ts +++ b/src/app/project/graph/scenarios-graph.component.ts @@ -11,6 +11,7 @@ import { } from "@angular/core"; import { Chart } from "chart.js"; import { scenarioHistory } from "src/app/graphs/scenarios"; +import { normalizeOverviewData } from "../../utils/normalizeOverviewData"; @Component({ selector: "app-scenarios-graph", @@ -46,7 +47,7 @@ export class ScenariosGraphComponent implements AfterViewInit, OnDestroy { data.datasets[0].data[i] = 0; } }, - // after the update .. + // after the update afterUpdate: function(chart) { if (length === -1) { return; } }, @@ -68,7 +69,7 @@ export class ScenariosGraphComponent implements AfterViewInit, OnDestroy { } } }); - this.chart = new Chart(this.chartCanvas.nativeElement, scenarioHistory(this.graphData)); + this.chart = new Chart(this.chartCanvas.nativeElement, scenarioHistory(this.graphData.map(normalizeOverviewData))); } ngOnDestroy(): void { diff --git a/src/app/project/new-scenario/add-new.scenario.component.ts b/src/app/project/new-scenario/add-new.scenario.component.ts index a7d5696b..f5be3e6a 100644 --- a/src/app/project/new-scenario/add-new.scenario.component.ts +++ b/src/app/project/new-scenario/add-new.scenario.component.ts @@ -55,7 +55,7 @@ export class AddNewScenarioComponent implements OnInit { this.scenarioApiService.createNewScenario(this.projectName, { scenarioName }) .pipe(catchError(r => of(r))) .subscribe(_ => { - const message = this.notification.newProjectNotificationMessage(_); + const message = this.notification.newScenarioNotificationMessage(_); this.projectService.fetchScenarios(this.projectName); return this.scenarioApiService.setData(message); }); diff --git a/src/app/project/project.component.html b/src/app/project/project.component.html index f8d184ef..e96f490a 100644 --- a/src/app/project/project.component.html +++ b/src/app/project/project.component.html @@ -77,7 +77,10 @@
Scenarios
- + + Nothing here yet! Add your first scenario. + + {{_.name}} {{_.data[0].startDate | date:'d. L. yyyy H:mm'}} diff --git a/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.html b/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.html index 6a537c49..6784c615 100644 --- a/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.html +++ b/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.html @@ -16,15 +16,25 @@

- - + + + + +
- +
- +
- - + +
+
+ + +
+
+ + +
+ + +
diff --git a/src/app/shared/shared-project/project-settings/project-settings.component.ts b/src/app/shared/shared-project/project-settings/project-settings.component.ts index c36aeae1..7a376738 100644 --- a/src/app/shared/shared-project/project-settings/project-settings.component.ts +++ b/src/app/shared/shared-project/project-settings/project-settings.component.ts @@ -10,21 +10,23 @@ import { UserService } from "../../../_services/user.service"; import { UserRole, Users } from "../../../_services/users.model"; @Component({ - selector: "app-project-settings", - templateUrl: "./project-settings.component.html", - styleUrls: ["./project-settings.component.css"] + selector: 'app-project-settings', + templateUrl: './project-settings.component.html', + styleUrls: ['./project-settings.component.css'] }) export class ProjectSettingsComponent implements OnInit { projectSettingsForm: FormGroup; projectMembersForm: FormGroup; projectMembers: FormArray; - projectMembersData + projectMembersData; metricsEditable; formControls = { virtualUsers: null, throughput: null, - percentile: null, + percentile90: null, + percentile95: null, + percentile99: null, avgResponseTime: null, avgConnectionTime: null, avgLatency: null, @@ -49,7 +51,8 @@ export class ProjectSettingsComponent implements OnInit { } // eslint-disable-next-line @typescript-eslint/no-empty-function - ngOnInit() {} + ngOnInit() { + } get usersFormArray() { return this.projectMembersForm.controls.projectMembers as FormArray; @@ -57,23 +60,25 @@ export class ProjectSettingsComponent implements OnInit { createFormControls(settings) { this.formControls.virtualUsers = new FormControl(settings.topMetricsSettings.virtualUsers, []); - this.formControls.percentile = new FormControl(settings.topMetricsSettings.percentile, []); + this.formControls.percentile90 = new FormControl((settings.topMetricsSettings.percentile || settings.topMetricsSettings.percentile90), []); + this.formControls.percentile95 = new FormControl(settings.topMetricsSettings.percentile95 || false, []); + this.formControls.percentile99 = new FormControl(settings.topMetricsSettings.percentile99 || false, []); this.formControls.throughput = new FormControl(settings.topMetricsSettings.throughput, []); this.formControls.errorRate = new FormControl(settings.topMetricsSettings.errorRate, []); this.formControls.errorCount = new FormControl(settings.topMetricsSettings.errorCount || false, []), - this.formControls.network = new FormControl(settings.topMetricsSettings.network, []); + this.formControls.network = new FormControl(settings.topMetricsSettings.network, []); this.formControls.networkSent = new FormControl(settings.topMetricsSettings.networkSent || false, []); this.formControls.networkReceived = new FormControl(settings.topMetricsSettings.networkReceived || false, []); this.formControls.avgLatency = new FormControl(settings.topMetricsSettings.avgLatency, []); this.formControls.avgConnectionTime = new FormControl(settings.topMetricsSettings.avgConnectionTime, []); this.formControls.avgResponseTime = new FormControl(settings.topMetricsSettings.avgResponseTime, []); - this.formControls.scenarioUpsert = new FormControl(settings.upsertScenario, []) + this.formControls.scenarioUpsert = new FormControl(settings.upsertScenario, []); this.formControls.projectName = new FormControl(settings.projectName, [ Validators.required, Validators.maxLength(50), Validators.minLength(3), ]); - this.formControls.projectMembers = new FormArray([]) + this.formControls.projectMembers = new FormArray([]); } createForm() { @@ -81,7 +86,9 @@ export class ProjectSettingsComponent implements OnInit { virtualUsers: this.formControls.virtualUsers, errorRate: this.formControls.errorRate, errorCount: this.formControls.errorCount, - percentile: this.formControls.percentile, + percentile90: this.formControls.percentile90, + percentile95: this.formControls.percentile95, + percentile99: this.formControls.percentile99, throughput: this.formControls.throughput, network: this.formControls.network, networkSent: this.formControls.networkSent, @@ -95,7 +102,7 @@ export class ProjectSettingsComponent implements OnInit { }); this.projectMembersForm = new FormGroup({ projectMembers: this.formControls.projectMembers, - }) + }); } open(content) { @@ -104,9 +111,9 @@ export class ProjectSettingsComponent implements OnInit { if (role === "admin") { this.userService.fetchUsers().subscribe((users) => { - this.mapProjectMembersToUsersData(r.body.projectMembers, users) - this.addCheckboxes() - }) + this.mapProjectMembersToUsersData(r.body.projectMembers, users); + this.addCheckboxes(); + }); } this.createFormControls(r.body); @@ -123,7 +130,7 @@ export class ProjectSettingsComponent implements OnInit { const projectMembers = this.projectMembersData .filter(member => member.role !== UserRole.Admin) .filter(member => member.isMember) - .map(member => member.id) + .map(member => member.id); const payload = { projectName: this.formControls.projectName.value, @@ -131,7 +138,9 @@ export class ProjectSettingsComponent implements OnInit { topMetricsSettings: { virtualUsers: this.formControls.virtualUsers.value, errorRate: this.formControls.errorRate.value, - percentile: this.formControls.percentile.value, + percentile90: this.formControls.percentile90.value, + percentile95: this.formControls.percentile95.value, + percentile99: this.formControls.percentile99.value, throughput: this.formControls.throughput.value, network: this.formControls.network.value, avgLatency: this.formControls.avgLatency.value, @@ -151,15 +160,16 @@ export class ProjectSettingsComponent implements OnInit { this.projectService.loadProjects(); return this.projectApiService.setData(message); }); - this.projectSettingsForm.reset() + this.projectSettingsForm.reset(); this.modalService.dismissAll(); } } isEditable() { - const enabledMetrics = Object.values([this.formControls.virtualUsers, this.formControls.errorRate, this.formControls.percentile, - this.formControls.throughput, this.formControls.network, this.formControls.avgLatency, this.formControls.avgResponseTime, - this.formControls.avgConnectionTime, this.formControls.errorCount, this.formControls.networkSent, this.formControls.networkReceived]) + const enabledMetrics = Object.values([this.formControls.virtualUsers, this.formControls.errorRate, this.formControls.percentile90, + this.formControls.percentile95, this.formControls.percentile99, this.formControls.throughput, this.formControls.network, + this.formControls.avgLatency, this.formControls.avgResponseTime, + this.formControls.avgConnectionTime, this.formControls.errorCount, this.formControls.networkSent, this.formControls.networkReceived]) .map(control => control.value).filter(value => value === true); this.metricsEditable = enabledMetrics.length > 5; } @@ -171,17 +181,17 @@ export class ProjectSettingsComponent implements OnInit { private mapProjectMembersToUsersData = (projectMembers: string[], users: Users[]) => { this.projectMembersData = users .map(user => { - const isMember = projectMembers.find(member => member === user.id) - return { - id: user.id, - username: user.username, - isMember: user.role === UserRole.Admin ? true : !!isMember, - role: user.role, - isDisabled: user.role === UserRole.Admin - } - }) + const isMember = projectMembers.find(member => member === user.id); + return { + id: user.id, + username: user.username, + isMember: user.role === UserRole.Admin ? true : !!isMember, + role: user.role, + isDisabled: user.role === UserRole.Admin + }; + }); - } + }; private addCheckboxes() { this.projectMembersData.forEach((projectMember) => this.usersFormArray.push(new FormControl(projectMember.isMember))); diff --git a/src/app/utils/normalizeOverviewData.ts b/src/app/utils/normalizeOverviewData.ts new file mode 100644 index 00000000..4815bc57 --- /dev/null +++ b/src/app/utils/normalizeOverviewData.ts @@ -0,0 +1,7 @@ +export const normalizeOverviewData = (overviewData: any) => { + // legacy property `percentil` mapped to a new property + if (overviewData["percentil"]) { + overviewData["percentile90"] = overviewData["percentil"] + } + return overviewData +}