Skip to content

Commit

Permalink
System diagram (#443)
Browse files Browse the repository at this point in the history
* Added component, linked into menu

* basic functionality is in

* improved behaviour

* Added hover functionality

* Added hateful lazy-loaded js fix

* Avoided error logs

* reflecting lazy chunks

* We didn't end up using this

* Avoided no-node failure

* fixed package repo
  • Loading branch information
therealryan authored Jul 8, 2023
1 parent 8496e1a commit fc253a8
Show file tree
Hide file tree
Showing 17 changed files with 1,182 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,78 @@ public JsApp( String name, Path resDir ) {
.orElseThrow(
() -> new IllegalStateException( "Failed to find index.html in " + resDir ) );
indexTemplate = new Template( originalIndexPath );
Files.delete( originalIndexPath );
}
catch( IOException ioe ) {
throw new UncheckedIOException( "Failed to extract index.html", ioe );
}

fixLazyChunkLoadingPath( resDir );
}

/**
* <p>
* I hate this with the heat of a thousand suns, but I <i>really</i> want the
* report file structure to be like:
* </p>
*
* <pre>
* /report
* ├ index.html
* ├ /detail
* │ ├ &lt;hash&gt;.html
* │ ├ &lt;hash&gt;.html
* │ ├ ...
* │ └ &lt;hash&gt;.html
* └ /res
* ├ main.&lt;hash&gt;.js
* ├ polyfills.&lt;hash&gt;.js
* ├ &lt;whatever>.&lt;hash&gt;.js
* ├ ...
* └ styles.&lt;hash&gt;.css
* </pre>
* <p>
* This makes the report entrypoint super-obvious. Angular seems to have other
* ideas though, and all the stuff that I want in <code>/res</code> is just
* splatted into the root directory.
* </p>
* <p>
* We're working around this in two ways:
* </p>
* <ul>
* <li>Updating index.html to add <code>/res</code> to resource reference paths.
* That's what is happening in {@link Template#insert(Object, Path)}</li>
* <li>This method, which is dealing with lazy-loaded javascript chunks by
* updating <code>runtime.js</code> to add <code>/res</code>.</li>
* </ul>
* <p>
* <b>N.B.:</b> This second fix will work OK for the index page, but it will
* fail for the for detail pages, as we can apply the first fix dynamically in
* the detail pages, but they all share the same <code>runtime.js</code> file,
* and we can only fix the relative path once. For the moment we only need the
* runtime.js fix for mermaid, which is only used in the index page, so this
* isn't a problem yet.
* </p>
*
* @param resDir
*/
private static void fixLazyChunkLoadingPath( Path resDir ) {
try( Stream<Path> files = Files.find( resDir, 1,
( path, attr ) -> path.getFileName().toString().startsWith( "runtime." ) ) ) {
Path runtime = files.findFirst()
.orElse( null );
if( runtime != null ) {
String content = new String( Files.readAllBytes( runtime ), UTF_8 );
String fixed = content.replaceAll( "(\\(\\d+===e\\?\"common\":e\\))", "\"res/\" + $1" );
if( fixed.equals( content ) ) {
throw new IllegalStateException( "Failed to fix chunk load path" );
}
Files.write( runtime, fixed.getBytes( UTF_8 ) );
}
}
catch( IOException ioe ) {
throw new UncheckedIOException( "Failed to update lazy chunk loading", ioe );
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,35 @@ void write() throws Exception {
+ "detail/4C5FFE22176C7ABC272D95A0E5D62262.html\n"
+ "detail/823B8031950E57346DCE6FFD4BE56F54.html\n"
+ "index.html\n"
+ "res/100.<hash>.js\n"
+ "res/104.<hash>.js\n"
+ "res/124.<hash>.js\n"
+ "res/136.<hash>.js\n"
+ "res/14.<hash>.js\n"
+ "res/170.<hash>.js\n"
+ "res/220.<hash>.js\n"
+ "res/297.<hash>.js\n"
+ "res/331.<hash>.js\n"
+ "res/373.<hash>.js\n"
+ "res/377.<hash>.js\n"
+ "res/3rdpartylicenses.txt\n"
+ "res/435.<hash>.js\n"
+ "res/458.<hash>.js\n"
+ "res/486.<hash>.js\n"
+ "res/487.<hash>.js\n"
+ "res/491.<hash>.js\n"
+ "res/531.<hash>.js\n"
+ "res/552.<hash>.js\n"
+ "res/673.<hash>.js\n"
+ "res/688.<hash>.js\n"
+ "res/703.<hash>.js\n"
+ "res/726.<hash>.js\n"
+ "res/759.<hash>.js\n"
+ "res/829.<hash>.js\n"
+ "res/835.<hash>.js\n"
+ "res/985.<hash>.js\n"
+ "res/common.<hash>.js\n"
+ "res/favicon.ico\n"
+ "res/index.html\n"
+ "res/main.<hash>.js\n"
+ "res/polyfills.<hash>.js\n"
+ "res/runtime.<hash>.js\n"
Expand Down
5 changes: 4 additions & 1 deletion report/report-ng/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@
"@angular/platform-browser-dynamic": "^14.1.2",
"@angular/router": "^14.1.2",
"@babel/core": "^7.22.5",
"@types/d3": "^7.4.0",
"@types/diff-match-patch": "^1.0.32",
"@types/dompurify": "^3.0.2",
"diff-match-patch": "^1.0.5",
"mermaid": "^10.2.4",
"ngx-markdown": "^14.0.1",
"rxjs": "~7.8.1",
"tslib": "^2.6.0",
Expand All @@ -45,4 +48,4 @@
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~4.8.4"
}
}
}
4 changes: 3 additions & 1 deletion report/report-ng/projects/report/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ import { RouterModule, Routes } from '@angular/router';
import { TagSummaryComponent } from './tag-summary/tag-summary.component';
import { ResidueViewComponent } from './residue-view/residue-view.component';
import { MsgSearchInputComponent } from './msg-search-input/msg-search-input.component';
import { MatRipple, MatRippleModule } from '@angular/material/core';
import { MatRippleModule } from '@angular/material/core';
import { HighlightedTextComponent } from './highlighted-text/highlighted-text.component';
import { TextDiffComponent } from './text-diff/text-diff.component';
import { SystemDiagramComponent } from './system-diagram/system-diagram.component';

const routes: Routes = [
{ path: "diff", component: ModelDiffComponent },
Expand Down Expand Up @@ -100,6 +101,7 @@ const routes: Routes = [
MsgSearchInputComponent,
HighlightedTextComponent,
TextDiffComponent,
SystemDiagramComponent,
],
imports: [
BrowserAnimationsModule,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';

import { EntryHoverService } from './entry-hover.service';

describe('EntryHoverService', () => {
let service: EntryHoverService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(EntryHoverService);
});

it('should be created', () => {
expect(service).toBeTruthy();
});
});
23 changes: 23 additions & 0 deletions report/report-ng/projects/report/src/app/entry-hover.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Injectable } from '@angular/core';
import { Entry } from './types';

/**
* Mechanism for watching which index entries are hovered over
*/
@Injectable({
providedIn: 'root'
})
export class EntryHoverService {

listeners: ((entry: Entry | null) => void)[] = [];

constructor() { }

onHover(callback: (entry: Entry | null) => void): void {
this.listeners.push(callback);
}

hovered(entry: Entry | null): void {
this.listeners.forEach(cb => cb(entry));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Component, OnInit, Input } from '@angular/core';
import { Component, OnInit, Input, HostListener } from '@angular/core';
import { Entry } from '../types';
import { IconEmbedService } from '../icon-embed.service';
import { EntryHoverService } from '../entry-hover.service';

@Component({
selector: 'app-flow-nav-item',
Expand All @@ -20,7 +21,10 @@ export class FlowNavItemComponent implements OnInit {

lineClass: string = "";

constructor(private icons: IconEmbedService,) {
constructor(
icons: IconEmbedService,
private hover: EntryHoverService,
) {
icons.register("check_circle_outline", "error_outline", "help_outline", "new_releases");
}

Expand Down Expand Up @@ -48,6 +52,14 @@ export class FlowNavItemComponent implements OnInit {
}
}

@HostListener('mouseenter') onMouseEnter() {
this.hover.hovered(this.entry);
}

@HostListener('mouseleave') onMouseLeave() {
this.hover.hovered(null);
}

tags(): string[] {
return this.entry.tags.filter(t => {
return this.showResult || (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export class IndexRouteComponent implements OnInit {
this.indexData.set(this.index);
}

// looking for the route definitions? They're in app.module.ts
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

<app-tag-summary [entries]="filteredFlows"></app-tag-summary>

<app-system-diagram></app-system-diagram>

<app-flow-nav-list [entries]="filteredFlows" *ngIf="!error">
</app-flow-nav-list>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<mat-icon svgIcon="menu"></mat-icon>
</button>
<mat-menu #menu="matMenu">
<a mat-menu-item *ngFor="let item of displayItems()" routerLink="{{item.href}}">
<a mat-menu-item *ngFor="let item of displayItems()" routerLink="/{{item.href}}">
<mat-icon svgIcon="{{item.icon}}"></mat-icon>
{{item.text}}
</a>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#container {
text-align: center;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<mat-expansion-panel *ngIf="loadProgress != 0">
<mat-expansion-panel-header>
<mat-panel-title>Interactions</mat-panel-title>
<mat-panel-description>{{summary}}</mat-panel-description>
</mat-expansion-panel-header>
<div #myTestDiv id="container">
<mat-progress-bar *ngIf="loadProgress != 100" [value]="loadProgress"></mat-progress-bar>
<pre class="mermaid"></pre>
</div>
</mat-expansion-panel>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { SystemDiagramComponent } from './system-diagram.component';
import { ModelDiffDataService } from '../model-diff-data.service';
import { RouterTestingModule } from '@angular/router/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatExpansionModule } from '@angular/material/expansion';

describe('SystemDiagramComponent', () => {
let component: SystemDiagramComponent;
let fixture: ComponentFixture<SystemDiagramComponent>;
let mockMdds;

beforeEach(async () => {
mockMdds = jasmine.createSpyObj([
'path', 'index', 'onFlow', 'flowLoadProgress', 'flowFor']);
await TestBed.configureTestingModule({
declarations: [SystemDiagramComponent],
providers: [
{ provide: ModelDiffDataService, useValue: mockMdds },
],
imports: [
RouterTestingModule,
BrowserAnimationsModule,
MatExpansionModule,
]
})
.compileComponents();

fixture = TestBed.createComponent(SystemDiagramComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading

0 comments on commit fc253a8

Please sign in to comment.