From 40ef8383659218e067464f1abc5fadfe8b3b1b56 Mon Sep 17 00:00:00 2001 From: Finn Martens Date: Thu, 21 Mar 2019 15:51:31 +0100 Subject: [PATCH 01/16] [IT-2666] Sort Applications on the landing page by descending CSI value. --- frontend/src/app/modules/landing/landing.component.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/modules/landing/landing.component.ts b/frontend/src/app/modules/landing/landing.component.ts index 5405842ea1..1585171cec 100644 --- a/frontend/src/app/modules/landing/landing.component.ts +++ b/frontend/src/app/modules/landing/landing.component.ts @@ -26,7 +26,13 @@ export class LandingComponent { ); this.applications$ = combineLatest(this.applicationService.applications$, this.applicationService.applicationCsiById$).pipe( filter(([applications]) => !applications.isLoading && !!applications.data), - map(([applications, csiById]) => applications.data.map(app => new ApplicationWithCsi(app, csiById[app.id], csiById.isLoading))) + map(([applications, csiById]) => applications.data.map(app => new ApplicationWithCsi(app, csiById[app.id], csiById.isLoading)).sort((a, b) => { + if (!b.recentCsi.csiDocComplete) { + return -1; + } else { + return b.recentCsi.csiDocComplete - a.recentCsi.csiDocComplete; + } + })) ); this.applicationService.loadApplications(); this.applicationService.loadRecentCsiForApplications(); From 89e1e75fa0cda13edf0b85378f56e2003989bc4d Mon Sep 17 00:00:00 2001 From: Finn Martens Date: Thu, 21 Mar 2019 16:10:26 +0100 Subject: [PATCH 02/16] [IT-2666] Changed order in test because of sorting. --- .../app/modules/landing/landing.component.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/modules/landing/landing.component.spec.ts b/frontend/src/app/modules/landing/landing.component.spec.ts index e22897df72..0201403422 100644 --- a/frontend/src/app/modules/landing/landing.component.spec.ts +++ b/frontend/src/app/modules/landing/landing.component.spec.ts @@ -105,14 +105,14 @@ describe('LandingComponent', () => { expect(fixture.debugElement.query(By.css(".clickable-list"))).toBeTruthy(); const links = fixture.debugElement.queryAll(By.css(".clickable-list a")); expect(links.length).toBe(2); - expect(links[0].nativeElement.href).toMatch(".*/applicationDashboard/1"); - expect(links[0].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestOne"); + expect(links[0].nativeElement.href).toMatch(".*/applicationDashboard/2"); + expect(links[0].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestTwo"); expect(links[0].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeFalsy(); - expect(links[0].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(60); - expect(links[1].nativeElement.href).toMatch(".*/applicationDashboard/2"); - expect(links[1].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestTwo"); + expect(links[0].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(70); + expect(links[1].nativeElement.href).toMatch(".*/applicationDashboard/1"); + expect(links[1].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestOne"); expect(links[1].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeFalsy(); - expect(links[1].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(70); + expect(links[1].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(60); }); it('should show a button to view all jobs', () => { From 17fa501b0cca82c5eec692fb8e5d4fa112cc8fa8 Mon Sep 17 00:00:00 2001 From: Finn Martens Date: Thu, 21 Mar 2019 16:31:55 +0100 Subject: [PATCH 03/16] [IT-2666] Check if both values are undefined for sorting. --- frontend/src/app/modules/landing/landing.component.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/app/modules/landing/landing.component.ts b/frontend/src/app/modules/landing/landing.component.ts index 1585171cec..7d2815bc57 100644 --- a/frontend/src/app/modules/landing/landing.component.ts +++ b/frontend/src/app/modules/landing/landing.component.ts @@ -29,6 +29,8 @@ export class LandingComponent { map(([applications, csiById]) => applications.data.map(app => new ApplicationWithCsi(app, csiById[app.id], csiById.isLoading)).sort((a, b) => { if (!b.recentCsi.csiDocComplete) { return -1; + } else if (!a.recentCsi.csiDocComplete && !b.recentCsi.csiDocComplete) { + return 0; } else { return b.recentCsi.csiDocComplete - a.recentCsi.csiDocComplete; } From eb4e94ec29f4e2f4b6d23e9dcf9a87eca98bb87c Mon Sep 17 00:00:00 2001 From: Finn Martens Date: Fri, 22 Mar 2019 11:42:11 +0100 Subject: [PATCH 04/16] [IT-2666] Changed test to consider sorting. --- .../modules/landing/landing.component.spec.ts | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/modules/landing/landing.component.spec.ts b/frontend/src/app/modules/landing/landing.component.spec.ts index 0201403422..d42e62cb39 100644 --- a/frontend/src/app/modules/landing/landing.component.spec.ts +++ b/frontend/src/app/modules/landing/landing.component.spec.ts @@ -8,7 +8,6 @@ import {By} from "@angular/platform-browser"; import {Application} from "../../models/application.model"; import {CsiValueSmallComponent} from "../shared/components/csi-value/csi-value-small/csi-value-small.component"; import {ApplicationCsi} from "../../models/application-csi.model"; -import { of } from 'rxjs'; describe('LandingComponent', () => { let component: LandingComponent; @@ -79,40 +78,55 @@ describe('LandingComponent', () => { expect(fixture.debugElement.query(By.css(".clickable-list"))).toBeTruthy(); const links = fixture.debugElement.queryAll(By.css(".clickable-list a")); expect(links.length).toBe(2); - expect(links[0].nativeElement.href).toMatch(".*/applicationDashboard/1"); - expect(links[0].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestOne"); + + const linkHrefs = [".*/applicationDashboard/1", ".*/applicationDashboard/2"]; + const linkTitles = ["TestOne", "TestTwo"]; + expect(links.some(element => element.nativeElement.href.match(linkHrefs[0]))); + expect(links.some(element => element.query(By.css(".title")).nativeElement.textContent.trim().match(linkTitles[0]))); + expect(links.some(element => element.nativeElement.href.match(linkHrefs[1]))); + expect(links.some(element => element.query(By.css(".title")).nativeElement.textContent.trim().match(linkTitles[1]))); expect(links[0].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeTruthy(); - expect(links[1].nativeElement.href).toMatch(".*/applicationDashboard/2"); - expect(links[1].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestTwo"); expect(links[1].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeTruthy(); }); - it('should show a list of applications if existing, with CSI values', () => { + it('should show a list of applications if existing, with CSI values (in descending order)', () => { applicationService.applications$.next({ isLoading: false, data: [ new Application({id: 1, name: "TestOne"}), - new Application({id: 2, name: "TestTwo"}) + new Application({id: 2, name: "TestTwo"}), + new Application({id: 3, name: "TestThree"}), + new Application({id: 4, name: "TestFour"}) ] }); applicationService.applicationCsiById$.next({ isLoading: false, 1: new ApplicationCsi({csiValues: [{csiDocComplete: 50}, {csiDocComplete: 60}]}), - 2: new ApplicationCsi({csiValues: [{csiDocComplete: 60}, {csiDocComplete: 70}]}) + 2: new ApplicationCsi({csiValues: [{csiDocComplete: 60}, {csiDocComplete: 70}]}), + 3: new ApplicationCsi({csiValues: [{csiDocComplete: 70}, {csiDocComplete: 80}]}), + 4: new ApplicationCsi({csiValues: []}) }); fixture.detectChanges(); expect(fixture.debugElement.query(By.css("main")).classes.center).toBeFalsy(); expect(fixture.debugElement.query(By.css(".clickable-list"))).toBeTruthy(); const links = fixture.debugElement.queryAll(By.css(".clickable-list a")); - expect(links.length).toBe(2); - expect(links[0].nativeElement.href).toMatch(".*/applicationDashboard/2"); - expect(links[0].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestTwo"); + expect(links.length).toBe(4); + expect(links[0].nativeElement.href).toMatch(".*/applicationDashboard/3"); + expect(links[0].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestThree"); expect(links[0].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeFalsy(); - expect(links[0].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(70); - expect(links[1].nativeElement.href).toMatch(".*/applicationDashboard/1"); - expect(links[1].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestOne"); + expect(links[0].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(80); + expect(links[1].nativeElement.href).toMatch(".*/applicationDashboard/2"); + expect(links[1].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestTwo"); expect(links[1].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeFalsy(); - expect(links[1].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(60); + expect(links[1].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(70); + expect(links[2].nativeElement.href).toMatch(".*/applicationDashboard/1"); + expect(links[2].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestOne"); + expect(links[2].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeFalsy(); + expect(links[2].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(60); + expect(links[3].nativeElement.href).toMatch(".*/applicationDashboard/4"); + expect(links[3].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestFour"); + expect(links[3].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeFalsy(); + expect(links[3].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(undefined); }); it('should show a button to view all jobs', () => { From b2d0bf738da0cac21c209badf140946eff3933e9 Mon Sep 17 00:00:00 2001 From: Nils Kuhn Date: Mon, 25 Mar 2019 09:55:37 +0100 Subject: [PATCH 05/16] Removed separate hql query for job tags --- .../measurement/schedule/JobController.groovy | 1 - .../measurement/schedule/JobService.groovy | 16 ---------- grails-app/views/job/_jobTable.gsp | 29 ++++++++++++------- grails-app/views/job/index.gsp | 2 +- 4 files changed, 19 insertions(+), 29 deletions(-) diff --git a/grails-app/controllers/de/iteratec/osm/measurement/schedule/JobController.groovy b/grails-app/controllers/de/iteratec/osm/measurement/schedule/JobController.groovy index a7036b67bc..75e039e4ce 100644 --- a/grails-app/controllers/de/iteratec/osm/measurement/schedule/JobController.groovy +++ b/grails-app/controllers/de/iteratec/osm/measurement/schedule/JobController.groovy @@ -89,7 +89,6 @@ class JobController { } def model = [ jobs : jobs, - jobsWithTags : jobService.listJobsWithTags(), filters : params.filters, measurementsEnabled : inMemoryConfigService.areMeasurementsGenerallyEnabled(), lastNMinutesToShowSuccessfulResultsInJoblist : LAST_N_MINUTES_TO_SHOW_SUCCESSFUL_RESULTS_IN_JOBLIST, diff --git a/grails-app/services/de/iteratec/osm/measurement/schedule/JobService.groovy b/grails-app/services/de/iteratec/osm/measurement/schedule/JobService.groovy index 9b1b811674..72ab8b756b 100644 --- a/grails-app/services/de/iteratec/osm/measurement/schedule/JobService.groovy +++ b/grails-app/services/de/iteratec/osm/measurement/schedule/JobService.groovy @@ -66,22 +66,6 @@ class JobService { return csiJobs } - /** - * Returns a list of all Jobs which are tagged along with their tags. - * This was implemented using HQL since iterating over all jobs returned by Job.list() - * and querying job.tags on each of time usually takes at least 50 ms per job - * resulting in unacceptably long load times. - * - * @return A list of Maps. Each Map contains two items: "jobId" (long) and - * "tag" (String). So if one Job has multiple tags, multiple Maps - * with the same jobId but different tags will be returned. - */ - List listJobsWithTags() { - return Job.executeQuery("""SELECT new map(tagLink.tagRef as jobId, tagLink.tag.name as tag) - FROM TagLink tagLink - WHERE tagLink.type = 'job'""") - } - @Transactional void updateActivity(Job job, boolean activityToSet) { job.active = activityToSet diff --git a/grails-app/views/job/_jobTable.gsp b/grails-app/views/job/_jobTable.gsp index f343ff605c..7c6373a2a2 100644 --- a/grails-app/views/job/_jobTable.gsp +++ b/grails-app/views/job/_jobTable.gsp @@ -2,13 +2,14 @@ - - + data-tags="${tags.join(',')}"> - + + class="jobCheckbox"/> +
@@ -18,15 +19,15 @@ class="fas fa-chart-bar">
+ - + ${job.script.label} (${job.script.measuredEventsCount}) @@ -41,9 +42,15 @@ ${flash.massExecutionResults ? (flash.massExecutionResults[job.id]?.message ? "
" + flash.massExecutionResults[job.id].message : '') : ''}
- ${job.jobGroup.name} - ${job.location.removeBrowser(job.location.uniqueIdentifierForServer ?: job.location.location)} - ${job.location.browser.name != de.iteratec.osm.measurement.environment.Browser.UNDEFINED ? job.location.browser.name : ''} + + ${job.jobGroup.name} + + + ${job.location.removeBrowser(job.location.uniqueIdentifierForServer ?: job.location.location)} + + + ${job.location.browser.name != de.iteratec.osm.measurement.environment.Browser.UNDEFINED ? job.location.browser.name : ''} + - + From c19223e042231817aca0a1c684d97b4b5d1145ff Mon Sep 17 00:00:00 2001 From: DanielSteger Date: Thu, 18 Apr 2019 17:16:19 +0200 Subject: [PATCH 06/16] [IT-2679/80] Change UI of configuration dialog for metrics --- frontend/package-lock.json | 6 +-- .../performance-aspect-inspect.component.html | 40 ++++--------------- .../performance-aspect-inspect.component.scss | 22 +++++++--- .../performance-aspect-inspect.component.ts | 36 ++++------------- ...rformance-aspect-management.component.html | 22 ++++++---- ...rformance-aspect-management.component.scss | 27 ++++++++++--- ...performance-aspect-management.component.ts | 27 ++++++++++--- .../measurand-select.component.html | 4 +- grails-app/i18n/messages.properties | 14 +++---- grails-app/i18n/messages_de.properties | 12 +++--- 10 files changed, 106 insertions(+), 104 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index dea3ef93be..8eed141a38 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1650,7 +1650,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -3619,7 +3619,7 @@ }, "engine.io-client": { "version": "3.2.1", - "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", "dev": true, "requires": { @@ -9360,7 +9360,7 @@ }, "socket.io-parser": { "version": "3.2.0", - "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", "dev": true, "requires": { diff --git a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-inspect/performance-aspect-inspect.component.html b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-inspect/performance-aspect-inspect.component.html index aba250940a..df35eb6c2b 100644 --- a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-inspect/performance-aspect-inspect.component.html +++ b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-inspect/performance-aspect-inspect.component.html @@ -1,47 +1,21 @@
-
+ {{"frontend.de.iteratec.osm.performance-aspect." + performanceAspectWrapped?.data?.performanceAspectType + ".user-centric-question" | translate}} -
+ +
-
- -
{{"frontend.de.iteratec.isr.measurand." + (selectedMetric$ | async)?.name | translate}}
-
-
-
-
-
- -
-
+ +
- - - - - - - - - -
- {{"frontend.de.iteratec.osm.performance-aspect-metric" | translate}} {{"frontend.de.iteratec.osm.performance-aspect." + performanceAspectWrapped?.data?.performanceAspectType | translate}} + class="text-info">{{"frontend.de.iteratec.osm.performance-aspect." + performanceAspectWrapped?.data?.performanceAspectType + ".description" | translate}}
diff --git a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-inspect/performance-aspect-inspect.component.scss b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-inspect/performance-aspect-inspect.component.scss index fb088a93b4..7551aa20b8 100644 --- a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-inspect/performance-aspect-inspect.component.scss +++ b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-inspect/performance-aspect-inspect.component.scss @@ -1,26 +1,32 @@ .edit-container { display: grid; - grid-template-columns: repeat(5, 1fr); + grid-template-columns: 1fr 3fr; grid-auto-columns: max-content; grid-template-rows: 1fr; grid-gap: 0.8em; } -.aspect-label{ - font-size: 14px; +@media (max-width:768px) { + .edit-container { + grid-template-columns: 2fr 2fr; + } +} + +.aspect-label { + font-size: 12px; color: #555555; - margin: 0 0 2px 2px; + font-weight: lighter; } .aspect-type { grid-column-start: 1; - //grid-column-end: span 2; + grid-column-end: span 1; } .measurand { grid-column-start: 2; grid-column-end: span 2; - align-self: end; + align-self: start; } .buttons { @@ -33,3 +39,7 @@ margin: 0 0.5em; width: 8em; } + +.performance-aspect-type { + font-size: 15px; +} diff --git a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-inspect/performance-aspect-inspect.component.ts b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-inspect/performance-aspect-inspect.component.ts index b14ed4d4a9..db9f9681d9 100644 --- a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-inspect/performance-aspect-inspect.component.ts +++ b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-inspect/performance-aspect-inspect.component.ts @@ -1,10 +1,8 @@ -import {Component, Input, OnChanges, OnInit} from '@angular/core'; +import {Component, EventEmitter, Input, OnChanges, OnInit, Output} from '@angular/core'; import {PerformanceAspect} from "../../../../../models/perfomance-aspect.model"; -import {TranslateService} from "@ngx-translate/core"; import {ReplaySubject} from "rxjs"; import {ResponseWithLoadingState} from "../../../../../models/response-with-loading-state.model"; import {SelectableMeasurand} from "../../../../../models/measurand.model"; -import {ApplicationService} from "../../../../../services/application.service"; @Component({ selector: 'osm-performance-aspect-inspect', @@ -13,51 +11,31 @@ import {ApplicationService} from "../../../../../services/application.service"; }) export class PerformanceAspectInspectComponent implements OnInit, OnChanges { @Input() performanceAspectWrapped: ResponseWithLoadingState; - selectedMetric$: ReplaySubject = new ReplaySubject(); - editMode: boolean = false; + @Output() onSelect: EventEmitter = new EventEmitter(); + metric$: ReplaySubject = new ReplaySubject(); performanceAspectInEditing: PerformanceAspect; - constructor(private translateService: TranslateService, private applicationService: ApplicationService) { + constructor() { } ngOnInit() { if(this.performanceAspectWrapped){ - this.setAspectInEditing(); this.updateSelectedMetric(); } } ngOnChanges(){ if(this.performanceAspectWrapped){ - this.setAspectInEditing(); this.updateSelectedMetric(); } } private updateSelectedMetric() { - this.selectedMetric$.next(this.performanceAspectWrapped.data.measurand); - } - - private setAspectInEditing() { - this.performanceAspectInEditing = Object.assign({}, this.performanceAspectWrapped.data); - } - - edit() { - this.setAspectInEditing(); - this.editMode = true; - } - - cancel() { - this.setAspectInEditing(); - this.editMode = false; - } - - save() { - this.applicationService.createOrUpdatePerformanceAspect(this.performanceAspectInEditing); - this.editMode = false; + this.metric$.next(this.performanceAspectWrapped.data.measurand); } selectMeasurandForAspect(measurand: SelectableMeasurand) { - this.performanceAspectInEditing.measurand = measurand; + this.performanceAspectWrapped.data.measurand = measurand; + this.onSelect.emit(this.performanceAspectWrapped.data); } } diff --git a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.html b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.html index da81b94912..daa3d97781 100644 --- a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.html +++ b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.html @@ -1,15 +1,23 @@ -
+
- -

{{"frontend.de.iteratec.osm.performance-aspect.mgmt" | translate}} {{pageName}} + +

+ {{"frontend.de.iteratec.osm.performance-aspect.title" | translate}} {{ performanceAspectMgmtModal.getData() }}


-
- +
+
+ +
+
+
+ diff --git a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.scss b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.scss index 9b182098ed..38bbb38c85 100644 --- a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.scss +++ b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.scss @@ -1,17 +1,34 @@ .aspect-management-modal{ - max-width: 1200px; + max-width: 910px; } .aspect-management-box{ display: grid; grid-template-rows: 1fr 1fr 1fr; - grid-template-columns: 1fr 1fr 1fr; - //grid-gap: 8px; - grid-gap: 0.3em; + grid-template-columns: 1fr 1fr; + grid-gap: 1em; } .aspect-inspect{ grid-column-start: 1; - grid-column-end: span 3; + grid-column-end: span 2; } +.aspect-management-modal > .nsm-content > .nsm-body { + margin: 20px 20px 0 20px; +} + +.buttons { + height: 2.5em; + a { + float: right; + width: 100px; + margin: 0 5px 0 10px; + } +} + +.btn-init-dialog { + cursor: pointer; + font-size: 14px; + color: rgb(189, 188, 188); +} diff --git a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.ts b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.ts index 1d23514110..6074861d64 100644 --- a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.ts +++ b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.ts @@ -16,22 +16,37 @@ export class PerformanceAspectManagementComponent implements OnInit { @Input() pageId: number; @Input() pageName: string; performanceAspects$: Subject[]>; + changedMetrics: Map; constructor(private ngxSmartModalService: NgxSmartModalService, private measurandsService: ResultSelectionService, private applicationService: ApplicationService) { - this.performanceAspects$ = this.applicationService.performanceAspectForPage$ + this.performanceAspects$ = this.applicationService.performanceAspectForPage$; } ngOnInit() { + this.changedMetrics = new Map(); } - initDialog(){ - this.measurandsService.updatePages([{id: this.pageId, name: "does-not-matter"}]); - this.applicationService.updatePage({id: this.pageId, name: "does-not-matter"}); - this.ngxSmartModalService.open('preformanceAspectMgmtModal'); + initDialog() { + this.ngxSmartModalService.setModalData(this.pageName, "performanceAspectMgmtModal"); + this.measurandsService.updatePages([{id: this.pageId, name: this.pageName}]); + this.applicationService.updatePage({id: this.pageId, name: this.pageName}); + this.ngxSmartModalService.open('performanceAspectMgmtModal'); } - cancel(){ + cancel() { + this.changedMetrics.clear(); + this.ngxSmartModalService.resetModalData('performanceAspectMgmtModal'); + } + + updatePerformanceAspect(performanceAspect: PerformanceAspect) { + let key: string = performanceAspect.jobGroupId + "." + performanceAspect.pageId + "." + performanceAspect.performanceAspectType; + this.changedMetrics.set(key, performanceAspect); + } + save() { + this.changedMetrics.forEach((performanceAspect: PerformanceAspect) => { + this.applicationService.createOrUpdatePerformanceAspect(performanceAspect); + }); } } diff --git a/frontend/src/app/modules/result-selection/components/measurand-select/measurand-select.component.html b/frontend/src/app/modules/result-selection/components/measurand-select/measurand-select.component.html index 8df8664369..6125d31dd0 100644 --- a/frontend/src/app/modules/result-selection/components/measurand-select/measurand-select.component.html +++ b/frontend/src/app/modules/result-selection/components/measurand-select/measurand-select.component.html @@ -2,8 +2,8 @@ [compareWith]="compareMeasurands"> -
- +

{{"frontend.de.iteratec.osm.performance-aspect.title" | translate}} {{ performanceAspectMgmtModal.getData() }}

@@ -16,7 +17,7 @@


diff --git a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.ts b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.ts index dc9bf53d92..24b98dcce3 100644 --- a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.ts +++ b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.ts @@ -33,7 +33,7 @@ export class PerformanceAspectManagementComponent implements OnInit { this.ngxSmartModalService.open('performanceAspectMgmtModal'); } - cancel() { + resetModalData() { this.changedMetrics.clear(); this.ngxSmartModalService.resetModalData('performanceAspectMgmtModal'); } @@ -43,11 +43,11 @@ export class PerformanceAspectManagementComponent implements OnInit { this.changedMetrics.set(key, performanceAspect); } - save() { + saveAndClose() { this.changedMetrics.forEach((performanceAspect: PerformanceAspect) => { this.applicationService.createOrUpdatePerformanceAspect(performanceAspect); }); - this.ngxSmartModalService.resetModalData('performanceAspectMgmtModal'); + this.ngxSmartModalService.close('performanceAspectMgmtModal'); } } From 12744ef9007333768d15107caa41f37a17465396 Mon Sep 17 00:00:00 2001 From: Nils Kuhn Date: Mon, 1 Apr 2019 15:16:46 +0200 Subject: [PATCH 10/16] Enabled EventResultQueryBuilder to filter for PerformanceAspects --- .../result/ApplicationDashboardService.groovy | 7 ++- .../result/dao/EventResultQueryBuilder.groovy | 36 ++++++++++++--- .../osm/result/dao/query/AspectUtil.groovy | 46 +++++++++++++++++++ .../dao/query/EventResultQueryExecutor.groovy | 1 + 4 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 src/main/groovy/de/iteratec/osm/result/dao/query/AspectUtil.groovy diff --git a/grails-app/services/de/iteratec/osm/result/ApplicationDashboardService.groovy b/grails-app/services/de/iteratec/osm/result/ApplicationDashboardService.groovy index 628584f7d3..a9e60cef35 100644 --- a/grails-app/services/de/iteratec/osm/result/ApplicationDashboardService.groovy +++ b/grails-app/services/de/iteratec/osm/result/ApplicationDashboardService.groovy @@ -121,7 +121,12 @@ class ApplicationDashboardService { } PerformanceAspectType.values().each { PerformanceAspectType performanceAspectType -> if(!performanceAspects.collect{it.performanceAspectType}.contains(performanceAspectType)){ - performanceAspects.add([id: null, pageId: page.id, jobGroupId: jobGroup.id, performanceAspectType: performanceAspectType, metricIdentifier: performanceAspectType.defaultMetric.toString()]) + performanceAspects.add([ + id : null, + pageId : page.id, + jobGroupId : jobGroup.id, + performanceAspectType: performanceAspectType, + metricIdentifier : performanceAspectType.defaultMetric.toString()]) } } performanceAspects.each { diff --git a/src/main/groovy/de/iteratec/osm/result/dao/EventResultQueryBuilder.groovy b/src/main/groovy/de/iteratec/osm/result/dao/EventResultQueryBuilder.groovy index f32b2f7f86..4e52cf48a6 100644 --- a/src/main/groovy/de/iteratec/osm/result/dao/EventResultQueryBuilder.groovy +++ b/src/main/groovy/de/iteratec/osm/result/dao/EventResultQueryBuilder.groovy @@ -4,11 +4,9 @@ import de.iteratec.osm.csi.Page import de.iteratec.osm.measurement.schedule.JobGroup import de.iteratec.osm.result.CachedView import de.iteratec.osm.result.MeasurandGroup +import de.iteratec.osm.result.PerformanceAspectType import de.iteratec.osm.result.SelectedMeasurand -import de.iteratec.osm.result.dao.query.EventResultQueryExecutor -import de.iteratec.osm.result.dao.query.MeasurandTrim -import de.iteratec.osm.result.dao.query.ProjectionProperty -import de.iteratec.osm.result.dao.query.TrimQualifier +import de.iteratec.osm.result.dao.query.* import de.iteratec.osm.result.dao.query.projector.MeasurandAverageDataProjector import de.iteratec.osm.result.dao.query.projector.MeasurandRawDataProjector import de.iteratec.osm.result.dao.query.projector.UserTimingAverageDataProjector @@ -28,6 +26,7 @@ class EventResultQueryBuilder { private Set baseProjections private List trims = [] private PerformanceLoggingService performanceLoggingService + private AspectUtil aspectUtil private EventResultQueryExecutor measurandQueryExecutor = new EventResultQueryExecutor() private EventResultQueryExecutor userTimingQueryExecutor = new EventResultQueryExecutor() @@ -36,6 +35,7 @@ class EventResultQueryBuilder { performanceLoggingService = new PerformanceLoggingService() filters.add(initBaseClosure()) baseProjections = [] + aspectUtil = new AspectUtil() } private Closure initBaseClosure() { @@ -78,10 +78,12 @@ class EventResultQueryBuilder { } EventResultQueryBuilder withJobGroupIdsIn(List jobGroupIds, boolean project = true) { + this.aspectUtil.setJobGroupIds(jobGroupIds) return withAssociatedDomainIdsIn(jobGroupIds, 'jobGroup', project) } EventResultQueryBuilder withPageIdsIn(List pageIds, boolean project = true) { + this.aspectUtil.setPageIds(pageIds) return withAssociatedDomainIdsIn(pageIds, 'page', project) } @@ -98,17 +100,25 @@ class EventResultQueryBuilder { } EventResultQueryBuilder withJobGroupIn(List jobGroups, boolean project = true) { - return withAssociatedDomainIdsIn(jobGroups.collect { it.ident() }, 'jobGroup', project) + List jobGroupIds = jobGroups.collect { it.ident() } + this.aspectUtil.setJobGroupIds(jobGroupIds) + return withAssociatedDomainIdsIn(jobGroupIds, 'jobGroup', project) } EventResultQueryBuilder withPageIn(List pages, boolean project = true) { - return withAssociatedDomainIdsIn(pages.collect { it.ident() }, 'page', project) + List pageIds = pages.collect { it.ident() } + this.aspectUtil.setPageIds(pageIds) + return withAssociatedDomainIdsIn(pageIds, 'page', project) } EventResultQueryBuilder withoutPagesIn(List pages) { return withAssociatedDomainIdsNotIn(pages.collect { it.ident() }, 'page') } + EventResultQueryBuilder withPerformanceAspects(List aspectTypes) { + this.aspectUtil.setAspectTypes(aspectTypes) + } + EventResultQueryBuilder withCachedView(CachedView cachedView) { filters.add({ 'eq' 'cachedView', cachedView @@ -228,7 +238,21 @@ class EventResultQueryBuilder { return getResults() } + private getResultsWithAspects() { + + } + private getResults() { + + performanceLoggingService.logExecutionTime(PerformanceLoggingService.LogLevel.DEBUG, 'getting event-results - add missing aspect metrics', 3) { + aspectUtil.addMissingAspectMeasurands(userTimingQueryExecutor.selectedMeasurands) { SelectedMeasurand measurand -> + measurand.selectedType.isUserTiming() + } + aspectUtil.addMissingAspectMeasurands(measurandQueryExecutor.selectedMeasurands) { SelectedMeasurand measurand -> + !measurand.selectedType.isUserTiming() + } + } + List userTimingsResult = userTimingQueryExecutor.getResultFor(filters, trims, baseProjections, performanceLoggingService) List measurandResult = measurandQueryExecutor.getResultFor(filters, trims, baseProjections, performanceLoggingService) diff --git a/src/main/groovy/de/iteratec/osm/result/dao/query/AspectUtil.groovy b/src/main/groovy/de/iteratec/osm/result/dao/query/AspectUtil.groovy new file mode 100644 index 0000000000..bb2f02385d --- /dev/null +++ b/src/main/groovy/de/iteratec/osm/result/dao/query/AspectUtil.groovy @@ -0,0 +1,46 @@ +package de.iteratec.osm.result.dao.query + + +import de.iteratec.osm.result.PerformanceAspect +import de.iteratec.osm.result.PerformanceAspectType +import de.iteratec.osm.result.SelectedMeasurand +import de.iteratec.osm.result.dao.EventResultProjection +import groovy.transform.EqualsAndHashCode +/** + * @author nkuhn + */ +class AspectUtil { + + List jobGroupIds = [] + List pageIds = [] + List aspectTypes = [] + + void addMissingAspectMeasurands(List measurands, Closure validFn) { + getAspects().each {PerformanceAspect aspect -> + SelectedMeasurand aspectMetric = aspect.getMetric() + if (!measurands.contains(aspectMetric) && validFn(aspectMetric)) { + measurands.add(aspectMetric) + } + } + } + + List getAspects() { + if (jobGroupIds.size() == 0 || pageIds.size() == 0 || this.aspectTypes.size() == 0) return [] + return PerformanceAspect.createCriteria().list { + 'in'('jobGroup', this.jobGroupIds) + 'in'('page', this.pageIds) + 'in'('performanceAspectType', this.aspectTypes) + } + } + + void appendAspectMetrics(List metricsFromBuilder){ + + } + +} + +@EqualsAndHashCode +class AspectMetric { + PerformanceAspectType aspectType + SelectedMeasurand measurand +} diff --git a/src/main/groovy/de/iteratec/osm/result/dao/query/EventResultQueryExecutor.groovy b/src/main/groovy/de/iteratec/osm/result/dao/query/EventResultQueryExecutor.groovy index baa23b1e8b..0c71751ed9 100644 --- a/src/main/groovy/de/iteratec/osm/result/dao/query/EventResultQueryExecutor.groovy +++ b/src/main/groovy/de/iteratec/osm/result/dao/query/EventResultQueryExecutor.groovy @@ -14,6 +14,7 @@ class EventResultQueryExecutor { private EventResultTrimmer trimmer List selectedMeasurands + void setUserTimings(List selectedMeasurands) { this.selectedMeasurands = selectedMeasurands.findAll { it.selectedType.isUserTiming() } } From a76fbff09c999032139f0aa01b2c6702c32a0132 Mon Sep 17 00:00:00 2001 From: Nils Kuhn Date: Fri, 26 Apr 2019 17:23:39 +0200 Subject: [PATCH 11/16] Added browser reference to PerformanceAspect --- .../osm/result/PerformanceAspect.groovy | 4 ++- .../osm/result/PerformanceAspectSpec.groovy | 28 ++++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/grails-app/domain/de/iteratec/osm/result/PerformanceAspect.groovy b/grails-app/domain/de/iteratec/osm/result/PerformanceAspect.groovy index 095e8c211f..ea148a9ae2 100644 --- a/grails-app/domain/de/iteratec/osm/result/PerformanceAspect.groovy +++ b/grails-app/domain/de/iteratec/osm/result/PerformanceAspect.groovy @@ -1,11 +1,13 @@ package de.iteratec.osm.result import de.iteratec.osm.csi.Page +import de.iteratec.osm.measurement.environment.Browser import de.iteratec.osm.measurement.schedule.JobGroup class PerformanceAspect { JobGroup jobGroup Page page + Browser browser PerformanceAspectType performanceAspectType String metricIdentifier @@ -14,7 +16,7 @@ class PerformanceAspect { static transients = ['metric'] static constraints = { - performanceAspectType(unique: ['jobGroup', 'page']) + performanceAspectType(unique: ['jobGroup', 'page', 'browser']) } diff --git a/src/test/groovy/de/iteratec/osm/result/PerformanceAspectSpec.groovy b/src/test/groovy/de/iteratec/osm/result/PerformanceAspectSpec.groovy index a2c4bc0ea5..96185890a1 100644 --- a/src/test/groovy/de/iteratec/osm/result/PerformanceAspectSpec.groovy +++ b/src/test/groovy/de/iteratec/osm/result/PerformanceAspectSpec.groovy @@ -1,15 +1,17 @@ package de.iteratec.osm.result import de.iteratec.osm.csi.Page +import de.iteratec.osm.measurement.environment.Browser import de.iteratec.osm.measurement.schedule.JobGroup import grails.buildtestdata.BuildDataTest import grails.buildtestdata.mixin.Build import spock.lang.Specification -@Build([JobGroup, Page, PerformanceAspect]) +@Build([JobGroup, Page, PerformanceAspect, Browser]) class PerformanceAspectSpec extends Specification implements BuildDataTest { Page page1, page2 JobGroup jobGroup1, jobGroup2 + Browser browser1, browser2 SelectedMeasurand metric def setup() { @@ -17,6 +19,8 @@ class PerformanceAspectSpec extends Specification implements BuildDataTest { jobGroup1 = JobGroup.build() page2 = Page.build() jobGroup2 = JobGroup.build() + browser1 = Browser.build() + browser2 = Browser.build() metric = new SelectedMeasurand(Measurand.DOC_COMPLETE_TIME.name(), CachedView.CACHED) } @@ -29,21 +33,30 @@ class PerformanceAspectSpec extends Specification implements BuildDataTest { PerformanceAspect.build( page: page1, jobGroup: jobGroup1, + browser: browser1, performanceAspectType: PerformanceAspectType.PAGE_CONSTRUCTION_STARTED, metric: metric).save(flush: true) when: "a potential duplicate to the first aspect is created" - PerformanceAspect duplicateAspect = new PerformanceAspect(page: Page.findById(inputPageId), jobGroup: JobGroup.findById(inputJobGroupId), performanceAspectType: performanceAspectType, metric: metric) + PerformanceAspect duplicateAspect = new PerformanceAspect( + page: Page.findById(inputPageId), + jobGroup: JobGroup.findById(inputJobGroupId), + browser: Browser.findById(inputBrowserId), + performanceAspectType: performanceAspectType, + metric: metric) then: "its validation only fails if it is really a duplicate" duplicateAspect.validate() == valid where: "there are real duplicates and no real duplicates" - inputPageId | inputJobGroupId | performanceAspectType | valid - 1 | 1 | PerformanceAspectType.PAGE_CONSTRUCTION_STARTED | false - 2 | 1 | PerformanceAspectType.PAGE_CONSTRUCTION_STARTED | true - 1 | 2 | PerformanceAspectType.PAGE_CONSTRUCTION_STARTED | true - 1 | 1 | PerformanceAspectType.PAGE_SHOWS_USEFUL_CONTENT | true + inputPageId | inputJobGroupId | inputBrowserId | performanceAspectType || valid + 1 | 1 | 1 | PerformanceAspectType.PAGE_CONSTRUCTION_STARTED || false + 2 | 1 | 1 | PerformanceAspectType.PAGE_CONSTRUCTION_STARTED || true + 1 | 2 | 1 | PerformanceAspectType.PAGE_CONSTRUCTION_STARTED || true + 1 | 1 | 2 | PerformanceAspectType.PAGE_CONSTRUCTION_STARTED || true + 2 | 1 | 2 | PerformanceAspectType.PAGE_CONSTRUCTION_STARTED || true + 1 | 2 | 2 | PerformanceAspectType.PAGE_CONSTRUCTION_STARTED || true + 1 | 1 | 1 | PerformanceAspectType.PAGE_SHOWS_USEFUL_CONTENT || true } void "ensure metric survives database"() { @@ -51,6 +64,7 @@ class PerformanceAspectSpec extends Specification implements BuildDataTest { PerformanceAspect.build( page: page1, jobGroup: jobGroup1, + browser: browser1, performanceAspectType: PerformanceAspectType.PAGE_CONSTRUCTION_STARTED, metric: metric).save(flush: true) From bd38d54343871ebd3e1d9e5f789cba5b42d32e92 Mon Sep 17 00:00:00 2001 From: Philipp Albrecht Date: Mon, 29 Apr 2019 18:01:41 +0200 Subject: [PATCH 12/16] temp fix for travis npm build --- .../performance-aspect-management.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.ts b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.ts index 24b98dcce3..aaa14b6c4c 100644 --- a/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.ts +++ b/frontend/src/app/modules/application-dashboard/components/performance-aspect-management/performance-aspect-management.component.ts @@ -18,7 +18,7 @@ export class PerformanceAspectManagementComponent implements OnInit { performanceAspects$: Subject[]>; changedMetrics: Map; - constructor(private ngxSmartModalService: NgxSmartModalService, private measurandsService: ResultSelectionService, private applicationService: ApplicationService) { + constructor(public ngxSmartModalService: NgxSmartModalService, private measurandsService: ResultSelectionService, private applicationService: ApplicationService) { this.performanceAspects$ = this.applicationService.performanceAspectForPage$; } From 1a705f51d6a86ed80c058acd347b53eb97dd49a1 Mon Sep 17 00:00:00 2001 From: Nils Kuhn Date: Tue, 30 Apr 2019 17:14:05 +0200 Subject: [PATCH 13/16] Added changesets to add browser to PerformanceAspect --- .../2019-04-30-v512-browser-in-perf-aspect.groovy | 12 ++++++++++++ grails-app/migrations/changelog.groovy | 1 + 2 files changed, 13 insertions(+) create mode 100644 grails-app/migrations/2019-04-30-v512-browser-in-perf-aspect.groovy diff --git a/grails-app/migrations/2019-04-30-v512-browser-in-perf-aspect.groovy b/grails-app/migrations/2019-04-30-v512-browser-in-perf-aspect.groovy new file mode 100644 index 0000000000..1f44eab82e --- /dev/null +++ b/grails-app/migrations/2019-04-30-v512-browser-in-perf-aspect.groovy @@ -0,0 +1,12 @@ +databaseChangeLog = { + changeSet(author: "nkuhn (generated)", id: "1556619486219-1") { + sql("delete from performance_aspect" ) + } + changeSet(author: "nkuhn (generated)", id: "1556619486219-2") { + addColumn(tableName: "performance_aspect") { + column(name: "browser_id", type: "bigint") { + constraints(nullable: "false") + } + } + } +} \ No newline at end of file diff --git a/grails-app/migrations/changelog.groovy b/grails-app/migrations/changelog.groovy index 12cb2096e0..7692749e60 100644 --- a/grails-app/migrations/changelog.groovy +++ b/grails-app/migrations/changelog.groovy @@ -75,4 +75,5 @@ databaseChangeLog = { include file: '2018-11-19-v500-job-group-graphite-servers.groovy' include file: '2018-12-12-v500-nullable-job-result-label.groovy' include file: '2019-03-13-v511-add-performance-aspect.groovy' + include file: '2019-04-30-v512-browser-in-perf-aspect.groovy' } From 40b765c7be8b0ce28aab24537ff39af63561e2a7 Mon Sep 17 00:00:00 2001 From: Nils Kuhn Date: Tue, 30 Apr 2019 17:16:14 +0200 Subject: [PATCH 14/16] Get Aspects for relevant Browsers and persist them with browser --- frontend/package-lock.json | 6 +- .../src/app/models/perfomance-aspect.model.ts | 1 + .../models/location.model.ts | 9 ++ .../src/app/services/application.service.ts | 94 ++++++++++++---- .../ApplicationDashboardController.groovy | 16 ++- .../result/ApplicationDashboardService.groovy | 102 +++++++++++------- 6 files changed, 162 insertions(+), 66 deletions(-) create mode 100644 frontend/src/app/modules/application-dashboard/models/location.model.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8eed141a38..dea3ef93be 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1650,7 +1650,7 @@ }, "util": { "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -3619,7 +3619,7 @@ }, "engine.io-client": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", "dev": true, "requires": { @@ -9360,7 +9360,7 @@ }, "socket.io-parser": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", "dev": true, "requires": { diff --git a/frontend/src/app/models/perfomance-aspect.model.ts b/frontend/src/app/models/perfomance-aspect.model.ts index 35feb3984d..0fed7d1b2f 100644 --- a/frontend/src/app/models/perfomance-aspect.model.ts +++ b/frontend/src/app/models/perfomance-aspect.model.ts @@ -5,6 +5,7 @@ export interface PerformanceAspect { id: number pageId: number jobGroupId: number + browserId: number measurand: SelectableMeasurand performanceAspectType: string } diff --git a/frontend/src/app/modules/application-dashboard/models/location.model.ts b/frontend/src/app/modules/application-dashboard/models/location.model.ts new file mode 100644 index 0000000000..5f44f3d161 --- /dev/null +++ b/frontend/src/app/modules/application-dashboard/models/location.model.ts @@ -0,0 +1,9 @@ +export interface BrowserDto { + id: number + name: string +} +export interface LocationDto { + id: Number + name: string + parent: BrowserDto +} diff --git a/frontend/src/app/services/application.service.ts b/frontend/src/app/services/application.service.ts index d5f57c3354..fdb88b9834 100644 --- a/frontend/src/app/services/application.service.ts +++ b/frontend/src/app/services/application.service.ts @@ -10,7 +10,16 @@ import { ApplicationCsiDTOById } from "../models/application-csi.model"; import {Application, ApplicationDTO} from "../models/application.model"; -import {catchError, distinctUntilKeyChanged, filter, map, startWith, switchMap, withLatestFrom} from "rxjs/operators"; +import { + catchError, + distinctUntilKeyChanged, + filter, + map, + mergeMap, + startWith, + switchMap, + withLatestFrom +} from "rxjs/operators"; import {ResponseWithLoadingState} from "../models/response-with-loading-state.model"; import {Csi, CsiDTO} from "../models/csi.model"; import {FailingJobStatistic} from "../modules/application-dashboard/models/failing-job-statistic.model"; @@ -18,6 +27,7 @@ import {GraphiteServer, GraphiteServerDTO} from "../modules/application-dashboar import {FailingJob, FailingJobDTO} from '../modules/landing/models/failing-jobs.model'; import {PerformanceAspect} from "../models/perfomance-aspect.model"; import {Page} from "../models/page.model"; +import {LocationDto} from "../modules/application-dashboard/models/location.model"; @Injectable() export class ApplicationService { @@ -35,9 +45,11 @@ export class ApplicationService { selectedApplication$ = new ReplaySubject(1); constructor(private http: HttpClient) { - this.combinedParams().pipe( - switchMap(params => this.getPerformanceAspects(params)) - ).subscribe(this.performanceAspectForPage$); + this.getPerfAspectParams() + .pipe( + switchMap(perfAspectParams => this.getPerformanceAspects(perfAspectParams)), + startWith([]) + ).subscribe(nextAspects => this.performanceAspectForPage$.next(nextAspects)); this.selectedApplication$.pipe( switchMap((application: Application) => this.updateMetricsForPages(application)) @@ -126,17 +138,44 @@ export class ApplicationService { this.selectedPage$.next(page); } - private combinedParams(): Observable { - return combineLatest( - this.selectedApplication$, - this.selectedPage$, - (application: Application, page: Page) => this.generateParams(application, page)); + getLocations() { + this.http.get('resultSelection/getLocations', {}) + } + + createLocationParams(application: Application, page: Page) { + let now: Date = new Date(); + let fourWeeksAgo: Date = new Date(); + fourWeeksAgo.setDate(fourWeeksAgo.getDate() - 28); + return { + jobGroupIds: application.id.toString(), + pageIds: page.id.toString(), + from: fourWeeksAgo.toISOString(), + to: now.toISOString() + }; + } + + private createParams(applicationId: number) { + return { + applicationId: applicationId ? applicationId.toString() : "" + }; + } + + private getPerfAspectParams(): Observable { + return combineLatest(this.selectedApplication$, this.selectedPage$) + .pipe( + mergeMap(([application, page]: [Application, Page]) => { + const params = this.createLocationParams(application, page); + return this.http.get('/resultSelection/getLocations', {params}).pipe( + map((locations: LocationDto[]) => this.generateParams(application, page, locations))) + }) + ); } - private generateParams(application: Application, page: Page) { + private generateParams(application: Application, page: Page, locations: LocationDto[]) { return { applicationId: application.id, - pageId: page.id + pageId: page.id, + browserIds: locations.map(loc => loc.parent.id) } } @@ -148,11 +187,27 @@ export class ApplicationService { } private getPerformanceAspects(params): Observable[]> { - this.performanceAspectForPage$.next([]); - return this.http.get('/applicationDashboard/rest/getPerformanceAspectsForApplication', {params}).pipe( - handleError(), - map(dtos => dtos.map(dto => ({isLoading: false, data: dto}))) - ); + return this.http.get('/applicationDashboard/rest/getPerformanceAspectsForApplication', {params}) + .pipe( + handleError(), + map((aspects: PerformanceAspect[]) => this.filterOneBrowser(aspects).map(aspect => ({ + isLoading: false, + data: aspect + }))) + ) + ; + } + + /** + * Just for the moment. If we support one aspect per browser in the UI this should be removed. + * @param aspects + */ + filterOneBrowser(aspects: PerformanceAspect[]) { + if (aspects.length < 1) { + return aspects + } + const arbitraryBrowserId = aspects[0].browserId; + return aspects.filter((aspect: PerformanceAspect) => aspect.browserId == arbitraryBrowserId) } createOrUpdatePerformanceAspect(perfAspectToCreateOrUpdate: PerformanceAspect) { @@ -161,6 +216,7 @@ export class ApplicationService { performanceAspectId: perfAspectToCreateOrUpdate.id, pageId: perfAspectToCreateOrUpdate.pageId, applicationId: perfAspectToCreateOrUpdate.jobGroupId, + browserId: perfAspectToCreateOrUpdate.browserId, performanceAspectType: perfAspectToCreateOrUpdate.performanceAspectType, metricIdentifier: perfAspectToCreateOrUpdate.measurand.id }; @@ -216,12 +272,6 @@ export class ApplicationService { ); } - private createParams(applicationId: number) { - return { - applicationId: applicationId ? applicationId.toString() : "" - }; - } - createCsiConfiguration(applicationDto: ApplicationDTO) { return this.http.post('/applicationDashboard/rest/createCsiConfiguration', {applicationId: applicationDto.id}) .pipe(handleError()) diff --git a/grails-app/controllers/de/iteratec/osm/result/ApplicationDashboardController.groovy b/grails-app/controllers/de/iteratec/osm/result/ApplicationDashboardController.groovy index 3910cb44fb..f9b25cf640 100644 --- a/grails-app/controllers/de/iteratec/osm/result/ApplicationDashboardController.groovy +++ b/grails-app/controllers/de/iteratec/osm/result/ApplicationDashboardController.groovy @@ -4,6 +4,7 @@ package de.iteratec.osm.result import de.iteratec.osm.api.dto.ApplicationCsiDto import de.iteratec.osm.api.dto.PageCsiDto import de.iteratec.osm.csi.Page +import de.iteratec.osm.measurement.environment.Browser import de.iteratec.osm.measurement.environment.wptserver.Protocol import de.iteratec.osm.measurement.schedule.JobGroup import de.iteratec.osm.measurement.schedule.JobGroupService @@ -50,10 +51,12 @@ class ApplicationDashboardController { } def getPerformanceAspectsForApplication(PerformanceAspectManagementRequestCommand command) { + Long jobGroupId = command.applicationId Long pageId = command.pageId - List performanceAspects = applicationDashboardService.getPerformanceAspectsForJobGroup(jobGroupId, pageId) + List browserIds = command.browserIds + List performanceAspects = applicationDashboardService.getPerformanceAspects(jobGroupId, pageId, browserIds) return ControllerUtils.sendObjectAsJSON(response, performanceAspects) } @@ -62,10 +65,15 @@ class ApplicationDashboardController { SelectedMeasurand metric = new SelectedMeasurand(command.metricIdentifier, CachedView.UNCACHED) Page page = Page.findById(command.pageId) JobGroup jobGroup = JobGroup.findById(command.applicationId) + Browser browser = Browser.findById(command.browserId) PerformanceAspect performanceAspect if(!command.performanceAspectId){ - performanceAspect = new PerformanceAspect(performanceAspectType: performanceAspectType, page: page,jobGroup: jobGroup) + performanceAspect = new PerformanceAspect( + performanceAspectType: performanceAspectType, + page: page, + jobGroup: jobGroup, + browser: browser) } else { performanceAspect = PerformanceAspect.findById(command.performanceAspectId) } @@ -79,6 +87,7 @@ class ApplicationDashboardController { performanceAspectDto.performanceAspectType = performanceAspect.performanceAspectType.toString() performanceAspectDto.pageId = performanceAspect.page.id performanceAspectDto.jobGroupId = performanceAspect.jobGroup.id + performanceAspectDto.browserId = performanceAspect.browser.id return ControllerUtils.sendObjectAsJSON(response, performanceAspectDto) } catch (Exception e) { return ControllerUtils.sendSimpleResponseAsStream(response, HttpStatus.BAD_REQUEST, e.toString()) @@ -191,6 +200,7 @@ class PerformanceAspectCreationCommand implements Validateable { Long performanceAspectId Long applicationId Long pageId + Long browserId String metricIdentifier String performanceAspectType @@ -198,6 +208,7 @@ class PerformanceAspectCreationCommand implements Validateable { performanceAspectId(nullable: true) applicationId(nullable: false) pageId(nullable: false) + browserId(nullable: false) metricIdentifier(nullable: false) performanceAspectType(nullable: false) } @@ -206,6 +217,7 @@ class PerformanceAspectCreationCommand implements Validateable { class PerformanceAspectManagementRequestCommand implements Validateable { Long applicationId Long pageId + List browserIds static constraints = { applicationId(nullable: false) diff --git a/grails-app/services/de/iteratec/osm/result/ApplicationDashboardService.groovy b/grails-app/services/de/iteratec/osm/result/ApplicationDashboardService.groovy index a9e60cef35..ca97d544cd 100644 --- a/grails-app/services/de/iteratec/osm/result/ApplicationDashboardService.groovy +++ b/grails-app/services/de/iteratec/osm/result/ApplicationDashboardService.groovy @@ -6,6 +6,7 @@ import de.iteratec.osm.api.dto.ApplicationCsiDto import de.iteratec.osm.api.dto.CsiDto import de.iteratec.osm.api.dto.PageCsiDto import de.iteratec.osm.csi.* +import de.iteratec.osm.measurement.environment.Browser import de.iteratec.osm.measurement.schedule.Job import de.iteratec.osm.measurement.schedule.JobDaoService import de.iteratec.osm.measurement.schedule.JobGroup @@ -86,59 +87,82 @@ class ApplicationDashboardService { jobGroupService.getPagesWithResultsOrActiveJobsForJobGroup(jobGroupId) .findAll { Page page -> page.name != Page.UNDEFINED } .each { Page page -> - Map entry = recentMetrics.find { - it.pageId == page.id - } - if (!entry) { - recentMetrics.add( - [ - 'pageId' : page.id, - 'pageName': page.name - ] - ) - } else { - entry.pageName = page.name - } - } + Map entry = recentMetrics.find { + it.pageId == page.id + } + if (!entry) { + recentMetrics.add( + [ + 'pageId' : page.id, + 'pageName': page.name + ] + ) + } else { + entry.pageName = page.name + } + } return recentMetrics } - List getPerformanceAspectsForJobGroup(Long jobGroupId, Long pageId) { + List getPerformanceAspects(Long jobGroupId, Long pageId, List browserIds) { + + List performanceAspects = [] + JobGroup jobGroup = JobGroup.findById(jobGroupId) Page page = Page.findById(pageId) - List performanceAspects = PerformanceAspect.createCriteria().list { + List browsers = browserIds.collect { Browser.get(it) } + + browsers.each { browser -> + List aspectsOfBrowser = getAspectsAsMap(jobGroup, page, browser) + addDefaultsForMissing(aspectsOfBrowser, page, jobGroup, browser) + formatMeasurand(aspectsOfBrowser) + performanceAspects.addAll(aspectsOfBrowser) + } + + return performanceAspects.sort { it.performanceAspectTypeIndex } + + } + + private formatMeasurand(List performanceAspects) { + return performanceAspects.each { + SelectedMeasurand selectedMetric = new SelectedMeasurand(it.metricIdentifier, CachedView.UNCACHED) + it.remove('metricIdentifier') + it.measurand = [name: selectedMetric.name, id: selectedMetric.optionValue] + it.performanceAspectTypeIndex = it.performanceAspectType + it.performanceAspectType = it.performanceAspectType.toString(); + } + } + + private addDefaultsForMissing(List performanceAspects, Page page, JobGroup jobGroup, Browser browser) { + return PerformanceAspectType.values().each { PerformanceAspectType performanceAspectType -> + if (!performanceAspects.collect { it.performanceAspectType }.contains(performanceAspectType)) { + performanceAspects.add([ + id : null, + pageId : page.id, + jobGroupId : jobGroup.id, + browserId : browser.id, + performanceAspectType: performanceAspectType, + metricIdentifier : performanceAspectType.defaultMetric.toString()]) + } + } + } + + private getAspectsAsMap(JobGroup jobGroup, Page page, Browser browser) { + return PerformanceAspect.createCriteria().list { resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP) eq 'jobGroup', jobGroup eq 'page', page + eq 'browser', browser projections { property 'id', 'id' property 'page.id', 'pageId' property 'jobGroup.id', 'jobGroupId' + property 'browser.id', 'browserId' property 'metricIdentifier', 'metricIdentifier' property 'performanceAspectType', 'performanceAspectType' } } - PerformanceAspectType.values().each { PerformanceAspectType performanceAspectType -> - if(!performanceAspects.collect{it.performanceAspectType}.contains(performanceAspectType)){ - performanceAspects.add([ - id : null, - pageId : page.id, - jobGroupId : jobGroup.id, - performanceAspectType: performanceAspectType, - metricIdentifier : performanceAspectType.defaultMetric.toString()]) - } - } - performanceAspects.each { - SelectedMeasurand selectedMetric = new SelectedMeasurand(it.metricIdentifier, CachedView.UNCACHED) - it.remove('metricIdentifier') - it.measurand = [name: selectedMetric.name, id: selectedMetric.optionValue] - it.performanceAspectTypeIndex = it.performanceAspectType - it.performanceAspectType = it.performanceAspectType.toString(); - } - - return performanceAspects.sort { it.performanceAspectTypeIndex } - } def createOrReturnCsiConfiguration(Long jobGroupId) { @@ -196,8 +220,8 @@ class ApplicationDashboardService { .getOrCalculateShopCsiAggregations(startDate, today, dailyInterval, csiGroups) .groupBy { csiAggregation -> csiAggregation.jobGroup.id } .collectEntries { jobGroupId, csiAggregations -> - [(jobGroupId): csiAggregationsToDto(jobGroupId, csiAggregations, startDate)] - } as Map + [(jobGroupId): csiAggregationsToDto(jobGroupId, csiAggregations, startDate)] + } as Map dtosById.putAll(dtosByIdWithValues) } return dtosById @@ -255,7 +279,7 @@ class ApplicationDashboardService { resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP) projections { property 'id', 'job_id' - jobGroup { property 'name', 'application'} + jobGroup { property 'name', 'application' } script { property 'label', 'script' } location { property 'location', 'location' } location { browser { property 'name', 'browser' } } From 0b7d9bcca5c69967bc29a058bb3f19a650f22841 Mon Sep 17 00:00:00 2001 From: Nils Kuhn Date: Thu, 2 May 2019 13:08:05 +0200 Subject: [PATCH 15/16] Get aspects for all browsers with one db request --- .../result/ApplicationDashboardService.groovy | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/grails-app/services/de/iteratec/osm/result/ApplicationDashboardService.groovy b/grails-app/services/de/iteratec/osm/result/ApplicationDashboardService.groovy index ca97d544cd..af1103e826 100644 --- a/grails-app/services/de/iteratec/osm/result/ApplicationDashboardService.groovy +++ b/grails-app/services/de/iteratec/osm/result/ApplicationDashboardService.groovy @@ -107,18 +107,13 @@ class ApplicationDashboardService { List getPerformanceAspects(Long jobGroupId, Long pageId, List browserIds) { - List performanceAspects = [] - JobGroup jobGroup = JobGroup.findById(jobGroupId) Page page = Page.findById(pageId) List browsers = browserIds.collect { Browser.get(it) } - browsers.each { browser -> - List aspectsOfBrowser = getAspectsAsMap(jobGroup, page, browser) - addDefaultsForMissing(aspectsOfBrowser, page, jobGroup, browser) - formatMeasurand(aspectsOfBrowser) - performanceAspects.addAll(aspectsOfBrowser) - } + List performanceAspects = getAspectsAsMap(jobGroup, page, browsers) + addDefaultsForMissing(performanceAspects, page, jobGroup, browsers) + formatMeasurand(performanceAspects) return performanceAspects.sort { it.performanceAspectTypeIndex } @@ -134,26 +129,28 @@ class ApplicationDashboardService { } } - private addDefaultsForMissing(List performanceAspects, Page page, JobGroup jobGroup, Browser browser) { - return PerformanceAspectType.values().each { PerformanceAspectType performanceAspectType -> - if (!performanceAspects.collect { it.performanceAspectType }.contains(performanceAspectType)) { - performanceAspects.add([ - id : null, - pageId : page.id, - jobGroupId : jobGroup.id, - browserId : browser.id, - performanceAspectType: performanceAspectType, - metricIdentifier : performanceAspectType.defaultMetric.toString()]) + private void addDefaultsForMissing(List performanceAspects, Page page, JobGroup jobGroup, List browsers) { + PerformanceAspectType.values().each { PerformanceAspectType type -> + browsers.each { browser -> + if (!performanceAspects.any { it.performanceAspectType == type && it.browserId == browser.id }) { + performanceAspects.add([ + id : null, + pageId : page.id, + jobGroupId : jobGroup.id, + browserId : browser.id, + performanceAspectType: type, + metricIdentifier : type.defaultMetric.toString()]) + } } } } - private getAspectsAsMap(JobGroup jobGroup, Page page, Browser browser) { + private getAspectsAsMap(JobGroup jobGroup, Page page, List browsers) { return PerformanceAspect.createCriteria().list { resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP) eq 'jobGroup', jobGroup eq 'page', page - eq 'browser', browser + 'in' 'browser', browsers projections { property 'id', 'id' property 'page.id', 'pageId' From acb675aa9ef3e178506737bad9464f066191969c Mon Sep 17 00:00:00 2001 From: Nils Kuhn Date: Thu, 2 May 2019 13:08:44 +0200 Subject: [PATCH 16/16] Removed unused class --- .../de/iteratec/osm/result/dao/query/AspectUtil.groovy | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/groovy/de/iteratec/osm/result/dao/query/AspectUtil.groovy b/src/main/groovy/de/iteratec/osm/result/dao/query/AspectUtil.groovy index bb2f02385d..3ce1af2e9e 100644 --- a/src/main/groovy/de/iteratec/osm/result/dao/query/AspectUtil.groovy +++ b/src/main/groovy/de/iteratec/osm/result/dao/query/AspectUtil.groovy @@ -38,9 +38,3 @@ class AspectUtil { } } - -@EqualsAndHashCode -class AspectMetric { - PerformanceAspectType aspectType - SelectedMeasurand measurand -}