Skip to content

Commit 9608181

Browse files
feat: Add Swagger UI pages for ContentService and AnalysisService
Adds /ContentService and /AnalysisService routes that render interactive Swagger UI API documentation, loaded lazily from vendored swagger-ui-dist assets. The servers section is hidden since there is only one server. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 44c39eb commit 9608181

8 files changed

Lines changed: 106 additions & 0 deletions

File tree

package-lock.json

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"rxjs": "~7.8.0",
7171
"svg-pan-zoom": "^3.6.1",
7272
"svgcanvas": "^2.6.0",
73+
"swagger-ui-dist": "^5.32.0",
7374
"tinacms": "^3.4.1",
7475
"tslib": "^2.3.0",
7576
"vectorious": "^6.1.12",

projects/website-angular/src/app/app.routes.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ export const routes: Routes = [
4040
{ path: 'content/query', loadComponent: () => import('./search/search.component').then(m => m.SearchComponent) },
4141
{ path: 'tools/site-search', loadComponent: () => import('./site-search/site-search.component').then(m => m.SiteSearchComponent) },
4242

43+
//API Documentation (Swagger UI)
44+
{ path: 'ContentService', loadComponent: () => import('./swagger-page/swagger-page.component').then(m => m.SwaggerPageComponent), data: { serviceName: 'ContentService' } },
45+
{ path: 'AnalysisService', loadComponent: () => import('./swagger-page/swagger-page.component').then(m => m.SwaggerPageComponent), data: { serviceName: 'AnalysisService' } },
46+
4347
//404 Page
4448
{ path: '404', loadComponent: () => import('./page-not-found/page-not-found.component').then(m => m.PageNotFoundComponent) }, //TODO: Remove?
4549
/* Non - CMS Pages Above this Line */
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div #swaggerContainer class="swagger-container"></div>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.swagger-container {
2+
max-width: 1460px;
3+
margin: 0 auto;
4+
padding: 1rem;
5+
}
6+
7+
:host ::ng-deep .scheme-container {
8+
display: none;
9+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Component, AfterViewInit, OnDestroy, ElementRef, ViewChild, PLATFORM_ID, Inject } from '@angular/core';
2+
import { ActivatedRoute } from '@angular/router';
3+
import { isPlatformBrowser } from '@angular/common';
4+
5+
declare const SwaggerUIBundle: any;
6+
7+
@Component({
8+
selector: 'app-swagger-page',
9+
templateUrl: './swagger-page.component.html',
10+
styleUrl: './swagger-page.component.scss',
11+
})
12+
export class SwaggerPageComponent implements AfterViewInit, OnDestroy {
13+
@ViewChild('swaggerContainer', { static: true }) swaggerContainer!: ElementRef<HTMLDivElement>;
14+
15+
private serviceName = '';
16+
private isBrowser: boolean;
17+
18+
constructor(
19+
private route: ActivatedRoute,
20+
@Inject(PLATFORM_ID) platformId: Object,
21+
) {
22+
this.isBrowser = isPlatformBrowser(platformId);
23+
this.serviceName = this.route.snapshot.data['serviceName'] || 'ContentService';
24+
}
25+
26+
ngAfterViewInit() {
27+
if (!this.isBrowser) return;
28+
this.loadSwaggerUI();
29+
}
30+
31+
private async loadSwaggerUI() {
32+
await this.loadCss('assets/swagger-ui/swagger-ui.css');
33+
await this.loadScript('assets/swagger-ui/swagger-ui-bundle.js');
34+
35+
const url = `https://dev.reactome.org/${this.serviceName}/v3/api-docs`;
36+
SwaggerUIBundle({
37+
domNode: this.swaggerContainer.nativeElement,
38+
url,
39+
});
40+
}
41+
42+
private loadScript(src: string): Promise<void> {
43+
if (document.querySelector(`script[src="${src}"]`)) return Promise.resolve();
44+
return new Promise((resolve, reject) => {
45+
const script = document.createElement('script');
46+
script.src = src;
47+
script.onload = () => resolve();
48+
script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
49+
document.head.appendChild(script);
50+
});
51+
}
52+
53+
private loadCss(href: string): Promise<void> {
54+
if (document.querySelector(`link[href="${href}"]`)) return Promise.resolve();
55+
return new Promise((resolve) => {
56+
const link = document.createElement('link');
57+
link.rel = 'stylesheet';
58+
link.href = href;
59+
link.onload = () => resolve();
60+
document.head.appendChild(link);
61+
});
62+
}
63+
64+
ngOnDestroy() {
65+
if (this.swaggerContainer?.nativeElement) {
66+
this.swaggerContainer.nativeElement.innerHTML = '';
67+
}
68+
}
69+
}

projects/website-angular/src/assets/swagger-ui/swagger-ui-bundle.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

projects/website-angular/src/assets/swagger-ui/swagger-ui.css

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)