diff --git a/README.md b/README.md index a4fea7f..edc9c59 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,18 @@ dependencies { After starting the application, visit: `localhost:8080/springwolf/asyncapi-ui.html`. +## Contribution + +You are welcome to raise issues and contribute code. + +### Code contribution + +Just fork the project, write code and open a PR. + +This repository uses github actions. We recommend activating them in your fork and open a PR within your fork to trigger them. + +Also, to build a preview for your branch, you can sign up to [Netlify's](https://app.netlify.com) free tier with your github account, import a new site from your github fork and set the build command as described in `src/_redirects`. Open a PR in within your fork and update the code to trigger a build. + ## Development 1. Run `npm i` 2. Run `ng serve` @@ -27,6 +39,11 @@ It contains multiple mocks - including the ones from the springwolf-core example To update the mock data, run `npm run update-mocks`. +### Code Formatting + +The project uses spotless together with prettier for code formatting. +Using the gradle task `spotlessApply`, the code is reformatted to match the style. + ## Release Releasing is done by running the gradle task `publish`. For local development, use `publishToMavenLocal`. diff --git a/build.gradle b/build.gradle index 3ae0403..55a1d05 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,7 @@ plugins { id 'com.github.node-gradle.node' version '2.2.4' id 'signing' id 'maven-publish' + id "com.diffplug.spotless" version "6.11.0" } def isSnapshot = Boolean.valueOf(project.findProperty('SNAPSHOT')) @@ -18,6 +19,8 @@ node { } npm_run_build { + dependsOn spotlessCheck + inputs.files fileTree("src") inputs.file 'angular.json' inputs.file 'package.json' @@ -97,3 +100,14 @@ signing { sign publishing.publications.mavenJava } +spotless { + encoding 'UTF-8' + + format 'Angular', { + target 'src/**/*.ts', 'src/**/*.css', 'src/**/*.html' + + trimTrailingWhitespace() + endWithNewline() + prettier().config(["singleQuote": true]) + } +} diff --git a/src/app/app.component.css b/src/app/app.component.css index 7088344..bef014d 100644 --- a/src/app/app.component.css +++ b/src/app/app.component.css @@ -1,10 +1,10 @@ app-header { - position: fixed; - z-index: 100; - width: 100%; + position: fixed; + z-index: 100; + width: 100%; } main { - margin: 0 64px; - padding: 64px 0; -} \ No newline at end of file + margin: 0 64px; + padding: 64px 0; +} diff --git a/src/app/app.component.html b/src/app/app.component.html index 66fe861..342ba2a 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,7 +1,7 @@
- - - - + + + +
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index bcc9c1a..0f24331 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -3,7 +3,7 @@ import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', - styleUrls: ['./app.component.css'] + styleUrls: ['./app.component.css'], }) export class AppComponent { title = 'springwolf'; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c672b17..fccd5b0 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -20,7 +20,7 @@ import { MockServer } from './shared/mock/mock-server'; import { PublisherService } from './shared/publisher.service'; import { FormsModule } from '@angular/forms'; import { JsonComponent } from './shared/components/json/json.component'; -import {AsyncApiMapperService} from "./shared/asyncapi-mapper.service"; +import { AsyncApiMapperService } from './shared/asyncapi-mapper.service'; @NgModule({ declarations: [ @@ -42,7 +42,9 @@ import {AsyncApiMapperService} from "./shared/asyncapi-mapper.service"; HighlightModule, HttpClientModule, FormsModule, - environment.production ? [] : HttpClientInMemoryWebApiModule.forRoot(MockServer, { delay: 100 }) + environment.production + ? [] + : HttpClientInMemoryWebApiModule.forRoot(MockServer, { delay: 100 }), ], providers: [ AsyncApiService, @@ -52,11 +54,13 @@ import {AsyncApiMapperService} from "./shared/asyncapi-mapper.service"; provide: HIGHLIGHT_OPTIONS, useValue: { languages: function () { - return { typescript: () => import('highlight.js/lib/languages/typescript') }; - } - } - } + return { + typescript: () => import('highlight.js/lib/languages/typescript'), + }; + }, + }, + }, ], - bootstrap: [AppComponent] + bootstrap: [AppComponent], }) -export class AppModule { } +export class AppModule {} diff --git a/src/app/channels/channel-main/channel-main.component.css b/src/app/channels/channel-main/channel-main.component.css index 4d904c5..3fe8099 100644 --- a/src/app/channels/channel-main/channel-main.component.css +++ b/src/app/channels/channel-main/channel-main.component.css @@ -1,34 +1,34 @@ textarea { - margin-top: 16px; - padding: 8px; - border-radius: 4px; - border-width: 0; - line-height: 24px; - background-color: #282c34; - color: #abb2bf; + margin-top: 16px; + padding: 8px; + border-radius: 4px; + border-width: 0; + line-height: 24px; + background-color: #282c34; + color: #abb2bf; } button { - margin-top: 8px; + margin-top: 8px; } .property-badge { - background-color: #C5CAE9; - border-radius: 4px; - padding: 6px; - font-size: small; - font-weight: bold; + background-color: #c5cae9; + border-radius: 4px; + padding: 6px; + font-size: small; + font-weight: bold; } .payload-name { - background-color: #E0E0E0; - border-radius: 4px; - padding: 6px; - font-weight: normal; + background-color: #e0e0e0; + border-radius: 4px; + padding: 6px; + font-weight: normal; } .header-name { - background-color: #E0E0E0; + background-color: #e0e0e0; border-radius: 4px; padding: 6px; font-weight: normal; diff --git a/src/app/channels/channel-main/channel-main.component.html b/src/app/channels/channel-main/channel-main.component.html index f2a94bd..3c0f902 100644 --- a/src/app/channels/channel-main/channel-main.component.html +++ b/src/app/channels/channel-main/channel-main.component.html @@ -1,58 +1,80 @@
+ +
+

{{ operation.message.description }}

-
-

{{ operation.message.description }}

- -
- - -
- -
- - - -
+
+ + +
+ +
+ + +
- - -

- {{ schemaName }} - - {{ operation.message.payload.name }} - -

- -
- -

- {{ headersSchemaName }} - - {{ operation.message.headers.name }} - -

- -
- -
-
- - - - +
+
+ +

+ {{ schemaName }} + + {{ + operation.message.payload.name + }} + +

+ +
+ +

+ {{ headersSchemaName }} + + {{ + operation.message.headers.name + }} + +

+ +
+ +
+
+ + + +
diff --git a/src/app/channels/channel-main/channel-main.component.ts b/src/app/channels/channel-main/channel-main.component.ts index 02189d1..4cfe011 100644 --- a/src/app/channels/channel-main/channel-main.component.ts +++ b/src/app/channels/channel-main/channel-main.component.ts @@ -10,10 +10,9 @@ import { STATUS } from 'angular-in-memory-web-api'; @Component({ selector: 'app-channel-main', templateUrl: './channel-main.component.html', - styleUrls: ['./channel-main.component.css'] + styleUrls: ['./channel-main.component.css'], }) export class ChannelMainComponent implements OnInit { - @Input() docName: string; @Input() channelName: string; @Input() operation: Operation; @@ -34,23 +33,24 @@ export class ChannelMainComponent implements OnInit { private snackBar: MatSnackBar ) {} - ngOnInit(): void { - this.asyncApiService.getAsyncApi().subscribe( - asyncapi => { - let schemas: Map = asyncapi.components.schemas; - this.schemaName = this.operation.message.payload.name.slice(this.operation.message.payload.name.lastIndexOf('/') + 1) - this.schema = schemas.get(this.schemaName); + this.asyncApiService.getAsyncApi().subscribe((asyncapi) => { + let schemas: Map = asyncapi.components.schemas; + this.schemaName = this.operation.message.payload.name.slice( + this.operation.message.payload.name.lastIndexOf('/') + 1 + ); + this.schema = schemas.get(this.schemaName); - this.defaultExample = this.schema.example; - this.exampleTextAreaLineCount = this.defaultExample?.lineCount || 0; + this.defaultExample = this.schema.example; + this.exampleTextAreaLineCount = this.defaultExample?.lineCount || 0; - this.headersSchemaName = this.operation.message.headers.name.slice(this.operation.message.headers.name.lastIndexOf('/') + 1) - this.headers = schemas.get(this.headersSchemaName); - this.headersExample = this.headers.example; - this.headersTextAreaLineCount = this.headersExample?.lineCount || 0; - } - ); + this.headersSchemaName = this.operation.message.headers.name.slice( + this.operation.message.headers.name.lastIndexOf('/') + 1 + ); + this.headers = schemas.get(this.headersSchemaName); + this.headersExample = this.headers.example; + this.headersTextAreaLineCount = this.headersExample?.lineCount || 0; + }); this.protocolName = Object.keys(this.operation.bindings)[0]; } @@ -61,7 +61,7 @@ export class ChannelMainComponent implements OnInit { this.exampleTextAreaLineCount = text.split('\n').length; break; case 'headers': - this.headersTextAreaLineCount = text.split('\n').length + this.headersTextAreaLineCount = text.split('\n').length; break; } } @@ -69,34 +69,39 @@ export class ChannelMainComponent implements OnInit { publish(example: string, headers: string): void { try { const payloadJson = JSON.parse(example); - const headersJson = JSON.parse(headers) - - this.publisherService.publish(this.protocolName, this.channelName, payloadJson, headersJson).subscribe( - _ => this.handlePublishSuccess(), - err => this.handlePublishError(err) - ); - } catch(error) { + const headersJson = JSON.parse(headers); + + this.publisherService + .publish(this.protocolName, this.channelName, payloadJson, headersJson) + .subscribe( + (_) => this.handlePublishSuccess(), + (err) => this.handlePublishError(err) + ); + } catch (error) { this.snackBar.open('Example payload is not valid', 'ERROR', { - duration: 3000 - }) + duration: 3000, + }); } } private handlePublishSuccess() { - return this.snackBar.open('Example payload sent to: ' + this.channelName, 'PUBLISHED', { - duration: 3000 - }); + return this.snackBar.open( + 'Example payload sent to: ' + this.channelName, + 'PUBLISHED', + { + duration: 3000, + } + ); } - private handlePublishError(err: {status?: number}) { + private handlePublishError(err: { status?: number }) { let msg = 'Publish failed'; if (err?.status === STATUS.NOT_FOUND) { msg += ': no publisher was provided for ' + this.protocolName; } return this.snackBar.open(msg, 'ERROR', { - duration: 4000 + duration: 4000, }); } - } diff --git a/src/app/channels/channels.component.css b/src/app/channels/channels.component.css index aea69f0..0658b4b 100644 --- a/src/app/channels/channels.component.css +++ b/src/app/channels/channels.component.css @@ -1,37 +1,36 @@ - .badge { - border-radius: 4px; - padding: 8px; - font-size: small; - font-weight: bold; - display: inline-block; + border-radius: 4px; + padding: 8px; + font-size: small; + font-weight: bold; + display: inline-block; } .subscribe-badge { - background-color: #FFD580; + background-color: #ffd580; } .publish-badge { - background-color: #9BD279; + background-color: #9bd279; } .protocol-badge { - background-color: #347AEB; - color: #fff + background-color: #347aeb; + color: #fff; } .badge .fa { - padding-left: 2 px; + padding-left: 2px; } mat-divider { - height: 20px; + height: 20px; } .payload-name { - background-color: #E0E0E0; - border-radius: 4px; - padding: 4px; - font-weight: normal; - font-size: small; + background-color: #e0e0e0; + border-radius: 4px; + padding: 4px; + font-weight: normal; + font-size: small; } diff --git a/src/app/channels/channels.component.html b/src/app/channels/channels.component.html index ad30a90..9a91fff 100644 --- a/src/app/channels/channels.component.html +++ b/src/app/channels/channels.component.html @@ -1,6 +1,10 @@

Channels

-Semantics of publish and subscribe: +Semantics of publish and subscribe: - - - -
- {{ channel.operation.protocol }} -
-
- {{ channel.operation.operation }} - -
-

{{ channel.name }}

-
{{ channel.operation.message.title }}
-
-
- - -
+ + + +
+ {{ channel.operation.protocol }} +
+
+ {{ channel.operation.operation }} + +
+

{{ channel.name }}

+
{{ channel.operation.message.title }}
+
+
+ + +
diff --git a/src/app/channels/channels.component.ts b/src/app/channels/channels.component.ts index 0bc24a3..21f9a69 100644 --- a/src/app/channels/channels.component.ts +++ b/src/app/channels/channels.component.ts @@ -1,27 +1,29 @@ import { Component, OnInit } from '@angular/core'; import { AsyncApiService } from '../shared/asyncapi.service'; -import {Channel, CHANNEL_ANCHOR_PREFIX} from '../shared/models/channel.model'; -import { Location } from "@angular/common"; +import { Channel, CHANNEL_ANCHOR_PREFIX } from '../shared/models/channel.model'; +import { Location } from '@angular/common'; @Component({ selector: 'app-channels', templateUrl: './channels.component.html', - styleUrls: ['./channels.component.css'] + styleUrls: ['./channels.component.css'], }) export class ChannelsComponent implements OnInit { - channels: Channel[]; selectedChannel: string; docName: string; - constructor(private asyncApiService: AsyncApiService, private location: Location) { - this.setChannelSelectionFromLocation() + constructor( + private asyncApiService: AsyncApiService, + private location: Location + ) { + this.setChannelSelectionFromLocation(); } ngOnInit(): void { - this.location.subscribe((): void => this.setChannelSelectionFromLocation()) + this.location.subscribe((): void => this.setChannelSelectionFromLocation()); - this.asyncApiService.getAsyncApi().subscribe(asyncapi => { + this.asyncApiService.getAsyncApi().subscribe((asyncapi) => { this.channels = this.sortChannels(asyncapi.channels); }); } @@ -30,8 +32,10 @@ export class ChannelsComponent implements OnInit { return channels.sort((a, b) => { if (a.operation.protocol === b.operation.protocol) { if (a.operation.operation === b.operation.operation) { - if(a.name === b.name) { - return a.operation.message.name.localeCompare(b.operation.message.name) + if (a.name === b.name) { + return a.operation.message.name.localeCompare( + b.operation.message.name + ); } else { return a.name.localeCompare(b.name); } @@ -45,7 +49,7 @@ export class ChannelsComponent implements OnInit { } setChannelSelection(channel: Channel): void { - window.location.hash = channel.anchorIdentifier + window.location.hash = channel.anchorIdentifier; } setChannelSelectionFromLocation(): void { const anchor = window.location.hash; diff --git a/src/app/header/header.component.css b/src/app/header/header.component.css index f8129b0..da6cab3 100644 --- a/src/app/header/header.component.css +++ b/src/app/header/header.component.css @@ -1,19 +1,19 @@ .logo { - height: 24px; - display:block; + height: 24px; + display: block; } a { - text-decoration: none; - color: white; + text-decoration: none; + color: white; } a:hover { - color: lightgray; + color: lightgray; } .select-doc { - width: 200px; - height: 24px; - display:block; -} \ No newline at end of file + width: 200px; + height: 24px; + display: block; +} diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index 516e750..8a37cd2 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -1,6 +1,6 @@ -

springwolf

- - - -
\ No newline at end of file +

springwolf

+ + + + diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index 4745ee2..a2a14cc 100644 --- a/src/app/header/header.component.ts +++ b/src/app/header/header.component.ts @@ -3,7 +3,6 @@ import { Component } from '@angular/core'; @Component({ selector: 'app-header', templateUrl: './header.component.html', - styleUrls: ['./header.component.css'] + styleUrls: ['./header.component.css'], }) -export class HeaderComponent { -} +export class HeaderComponent {} diff --git a/src/app/info/info.component.ts b/src/app/info/info.component.ts index 9739905..7d84e7e 100644 --- a/src/app/info/info.component.ts +++ b/src/app/info/info.component.ts @@ -6,17 +6,16 @@ import { AsyncApiService } from '../shared/asyncapi.service'; @Component({ selector: 'app-info', templateUrl: './info.component.html', - styleUrls: ['./info.component.css'] + styleUrls: ['./info.component.css'], }) export class InfoComponent implements OnInit { - asyncApiData: AsyncApi; info: Info; - constructor(private asyncApiService: AsyncApiService) { } + constructor(private asyncApiService: AsyncApiService) {} ngOnInit(): void { - this.asyncApiService.getAsyncApi().subscribe(asyncapi => { + this.asyncApiService.getAsyncApi().subscribe((asyncapi) => { this.asyncApiData = asyncapi; this.info = asyncapi.info; }); @@ -31,5 +30,4 @@ export class InfoComponent implements OnInit { return false; } - } diff --git a/src/app/material.module.ts b/src/app/material.module.ts index 5d3875c..acf557e 100644 --- a/src/app/material.module.ts +++ b/src/app/material.module.ts @@ -13,21 +13,21 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; const modules = [ - MatButtonModule, - MatIconModule, - MatToolbarModule, - MatCardModule, - MatExpansionModule, - MatTabsModule, - MatDividerModule, - ClipboardModule, - MatSnackBarModule, - MatFormFieldModule, - MatSelectModule -] + MatButtonModule, + MatIconModule, + MatToolbarModule, + MatCardModule, + MatExpansionModule, + MatTabsModule, + MatDividerModule, + ClipboardModule, + MatSnackBarModule, + MatFormFieldModule, + MatSelectModule, +]; @NgModule({ - imports: modules, - exports: modules + imports: modules, + exports: modules, }) -export class MaterialModule { } +export class MaterialModule {} diff --git a/src/app/schemas/schema/schema.component.css b/src/app/schemas/schema/schema.component.css index b769e7f..dba9b09 100644 --- a/src/app/schemas/schema/schema.component.css +++ b/src/app/schemas/schema/schema.component.css @@ -1,21 +1,21 @@ -.schema { - font-weight: 500; -} - -.key { - width: 12em; - vertical-align: top; -} - -.required { - color: red; -} - -.type { - color: #55A; -} - -.example { - color: #6B6B6B; - font-style: italic; -} +.schema { + font-weight: 500; +} + +.key { + width: 12em; + vertical-align: top; +} + +.required { + color: red; +} + +.type { + color: #55a; +} + +.example { + color: #6b6b6b; + font-style: italic; +} diff --git a/src/app/schemas/schema/schema.component.html b/src/app/schemas/schema/schema.component.html index d7e9da3..b8de417 100644 --- a/src/app/schemas/schema/schema.component.html +++ b/src/app/schemas/schema/schema.component.html @@ -1,21 +1,32 @@ - - - - - - -
- {{ property.key }} - * - - {{ property.value.type }} - - {{ property.value.name }} - - ({{ property.value.format }}) -
{{ property.value.description }}
- example: {{ property.value.example.value }} - - {{ enum }} - -
+ + + + + + + +
+ {{ property.key }} + * + + {{ + property.value.type + }} + + {{ property.value.name }} + + ({{ property.value.format }}) +
{{ property.value.description }}
+ example: {{ property.value.example.value }} + + {{ + enum + }} + +
diff --git a/src/app/schemas/schema/schema.component.ts b/src/app/schemas/schema/schema.component.ts index f764ec0..9b60557 100644 --- a/src/app/schemas/schema/schema.component.ts +++ b/src/app/schemas/schema/schema.component.ts @@ -4,10 +4,8 @@ import { Schema } from 'src/app/shared/models/schema.model'; @Component({ selector: 'app-schema', templateUrl: './schema.component.html', - styleUrls: ['./schema.component.css'] + styleUrls: ['./schema.component.css'], }) export class SchemaComponent { - @Input() schema: Schema; - } diff --git a/src/app/schemas/schemas.component.css b/src/app/schemas/schemas.component.css index 201f9ff..a4fa5a8 100644 --- a/src/app/schemas/schemas.component.css +++ b/src/app/schemas/schemas.component.css @@ -1,3 +1,3 @@ -h3 { - margin: 0; -} +h3 { + margin: 0; +} diff --git a/src/app/schemas/schemas.component.html b/src/app/schemas/schemas.component.html index 9014375..2ab3ff9 100644 --- a/src/app/schemas/schemas.component.html +++ b/src/app/schemas/schemas.component.html @@ -1,14 +1,19 @@

Schemas

- - - -

{{ schema.key }}

-
- - {{ schema.value.description }} - -
- -
+ + + +

{{ schema.key }}

+
+ + {{ schema.value.description }} + +
+ +
diff --git a/src/app/schemas/schemas.component.ts b/src/app/schemas/schemas.component.ts index f46db1c..1abb392 100644 --- a/src/app/schemas/schemas.component.ts +++ b/src/app/schemas/schemas.component.ts @@ -6,25 +6,28 @@ import { Schema } from '../shared/models/schema.model'; @Component({ selector: 'app-schemas', templateUrl: './schemas.component.html', - styleUrls: ['./schemas.component.css'] + styleUrls: ['./schemas.component.css'], }) export class SchemasComponent implements OnInit { - - schemas: Map; selectedSchema: string; - constructor(private asyncApiService: AsyncApiService, private location: Location) { - this.setSchemaSelectionFromLocation() + constructor( + private asyncApiService: AsyncApiService, + private location: Location + ) { + this.setSchemaSelectionFromLocation(); } ngOnInit(): void { - this.location.subscribe(() : void => this.setSchemaSelectionFromLocation()) - this.asyncApiService.getAsyncApi().subscribe(asyncapi => this.schemas = asyncapi.components.schemas); + this.location.subscribe((): void => this.setSchemaSelectionFromLocation()); + this.asyncApiService + .getAsyncApi() + .subscribe((asyncapi) => (this.schemas = asyncapi.components.schemas)); } setSchemaSelection(schema: Schema): void { - window.location.hash = schema.anchorIdentifier + window.location.hash = schema.anchorIdentifier; } setSchemaSelectionFromLocation(): void { this.selectedSchema = window.location.hash; diff --git a/src/app/servers/servers.component.html b/src/app/servers/servers.component.html index 067ae35..0e8eac6 100644 --- a/src/app/servers/servers.component.html +++ b/src/app/servers/servers.component.html @@ -1,7 +1,7 @@

Servers

- {{ server.key }} - - {{ server.value.url }} - - \ No newline at end of file + {{ server.key }} + + {{ server.value.url }} + + diff --git a/src/app/servers/servers.component.ts b/src/app/servers/servers.component.ts index 49d2727..7a73a64 100644 --- a/src/app/servers/servers.component.ts +++ b/src/app/servers/servers.component.ts @@ -5,16 +5,16 @@ import { Server } from '../shared/models/server.model'; @Component({ selector: 'app-servers', templateUrl: './servers.component.html', - styleUrls: ['./servers.component.css'] + styleUrls: ['./servers.component.css'], }) export class ServersComponent implements OnInit { - servers: Map; - constructor(private asyncApiService: AsyncApiService) { } + constructor(private asyncApiService: AsyncApiService) {} ngOnInit(): void { - this.asyncApiService.getAsyncApi().subscribe(asyncapi => this.servers = asyncapi.servers ); + this.asyncApiService + .getAsyncApi() + .subscribe((asyncapi) => (this.servers = asyncapi.servers)); } - } diff --git a/src/app/shared/asyncapi-mapper.service.ts b/src/app/shared/asyncapi-mapper.service.ts index ae9fbc8..ef24b52 100644 --- a/src/app/shared/asyncapi-mapper.service.ts +++ b/src/app/shared/asyncapi-mapper.service.ts @@ -1,10 +1,16 @@ import { AsyncApi } from './models/asyncapi.model'; import { Server } from './models/server.model'; -import {Channel, CHANNEL_ANCHOR_PREFIX, Message, Operation, OperationType} from './models/channel.model'; +import { + Channel, + CHANNEL_ANCHOR_PREFIX, + Message, + Operation, + OperationType, +} from './models/channel.model'; import { Schema } from './models/schema.model'; import { Injectable } from '@angular/core'; -import {Example} from "./models/example.model"; -import {Info} from "./models/info.model"; +import { Example } from './models/example.model'; +import { Info } from './models/info.model'; interface ServerAsyncApiSchema { description?: string; @@ -33,159 +39,198 @@ interface ServerAsyncApiInfo { description?: string; } -export type ServerAsyncApiChannelMessage = ServerAsyncApiMessage | { oneOf: ServerAsyncApiMessage[] }; +export type ServerAsyncApiChannelMessage = + | ServerAsyncApiMessage + | { oneOf: ServerAsyncApiMessage[] }; export interface ServerAsyncApi { - asyncapi: string; - info: ServerAsyncApiInfo; - servers: { - [key: string]: { - url: string; - protocol: string; - }; - }; - channels: { - [key: string]: { - description?: string; - subscribe?: { - message: ServerAsyncApiChannelMessage; - bindings?: any; - }; - publish?: { - message: ServerAsyncApiChannelMessage; - bindings?: any; - }; - }; + asyncapi: string; + info: ServerAsyncApiInfo; + servers: { + [key: string]: { + url: string; + protocol: string; }; - components: { - schemas: Map; + }; + channels: { + [key: string]: { + description?: string; + subscribe?: { + message: ServerAsyncApiChannelMessage; + bindings?: any; + }; + publish?: { + message: ServerAsyncApiChannelMessage; + bindings?: any; + }; }; + }; + components: { + schemas: Map; + }; } @Injectable() export class AsyncApiMapperService { - static BASE_URL = window.location.pathname + window.location.search + "#"; - - constructor() { - } - - public toAsyncApi(item: ServerAsyncApi): AsyncApi { + static BASE_URL = window.location.pathname + window.location.search + '#'; + + constructor() {} + + public toAsyncApi(item: ServerAsyncApi): AsyncApi { + return { + info: this.mapInfo(item), + servers: this.mapServers(item.servers), + channels: this.mapChannels(item.channels), + components: { + schemas: this.mapSchemas(item.components.schemas), + }, + }; + } + + private mapInfo(item: ServerAsyncApi): Info { + return { + title: item.info.title, + version: item.info.version, + description: item.info.description, + asyncApiJson: item, + }; + } + + private mapServers(servers: ServerAsyncApi['servers']): Map { + const s = new Map(); + Object.entries(servers).forEach(([k, v]) => s.set(k, v)); + return s; + } + + private mapChannels(channels: ServerAsyncApi['channels']): Channel[] { + const s = new Array(); + Object.entries(channels).forEach(([k, v]) => { + const subscriberChannels = this.mapChannel( + k, + v.description, + v.subscribe, + 'subscribe' + ); + subscriberChannels.forEach((channel) => s.push(channel)); + + const publisherChannels = this.mapChannel( + k, + v.description, + v.publish, + 'publish' + ); + publisherChannels.forEach((channel) => s.push(channel)); + }); + return s; + } + + private mapChannel( + topicName: string, + description: ServerAsyncApi['channels']['']['description'], + serverOperation: + | ServerAsyncApi['channels']['']['subscribe'] + | ServerAsyncApi['channels']['']['publish'], + operationType: OperationType + ): Channel[] { + if (serverOperation !== undefined) { + let messages: Message[] = this.mapMessages(serverOperation.message); + + return messages.map((message) => { + const operation = this.mapOperation( + operationType, + message, + serverOperation.bindings + ); return { - info: this.mapInfo(item), - servers: this.mapServers(item.servers), - channels: this.mapChannels(item.channels), - components: { - schemas: this.mapSchemas(item.components.schemas) - } + name: topicName, + anchorIdentifier: + CHANNEL_ANCHOR_PREFIX + + [ + operation.protocol, + topicName, + operation.operation, + operation.message.title, + ].join('-'), + description: description, + operation: operation, }; + }); } + return []; + } - private mapInfo(item: ServerAsyncApi): Info { - return { - title: item.info.title, - version: item.info.version, - description: item.info.description, - asyncApiJson: item, - }; - } - - private mapServers(servers: ServerAsyncApi["servers"]): Map { - const s = new Map(); - Object.entries(servers).forEach(([k, v]) => s.set(k, v)); - return s; - } - - private mapChannels(channels: ServerAsyncApi["channels"]): Channel[] { - const s = new Array(); - Object.entries(channels).forEach(([k, v]) => { - const subscriberChannels = this.mapChannel(k, v.description, v.subscribe, "subscribe") - subscriberChannels.forEach(channel => s.push(channel)) - - const publisherChannels = this.mapChannel(k, v.description, v.publish, "publish") - publisherChannels.forEach(channel => s.push(channel)) - }); - return s; - } - - private mapChannel( - topicName: string, - description: ServerAsyncApi["channels"][""]["description"], - serverOperation: ServerAsyncApi["channels"][""]["subscribe"] | ServerAsyncApi["channels"][""]["publish"], - operationType: OperationType): Channel[] - { - if(serverOperation !== undefined) { - let messages: Message[] = this.mapMessages(serverOperation.message) - - return messages.map(message => { - const operation = this.mapOperation(operationType, message, serverOperation.bindings) - return { - name: topicName, - anchorIdentifier: CHANNEL_ANCHOR_PREFIX + [operation.protocol, topicName, operation.operation, operation.message.title].join( "-"), - description: description, - operation: operation, - } - }) - } - return []; - } - - private mapMessages(message: ServerAsyncApiChannelMessage): Message[] { - if('oneOf' in message) { - return this.mapServerAsyncApiMessages(message.oneOf) - } - return this.mapServerAsyncApiMessages([message]); + private mapMessages(message: ServerAsyncApiChannelMessage): Message[] { + if ('oneOf' in message) { + return this.mapServerAsyncApiMessages(message.oneOf); } + return this.mapServerAsyncApiMessages([message]); + } - private mapServerAsyncApiMessages(messages: ServerAsyncApiMessage[]): Message[] { - return messages.map((v) => { - return { - name: v.name, - title: v.title, - description: v.description, - payload: { - name: v.payload.$ref, - anchorUrl: AsyncApiMapperService.BASE_URL +v.payload.$ref?.split('/')?.pop() - }, - headers: { - name: v.headers.$ref, - anchorUrl: AsyncApiMapperService.BASE_URL + v.headers.$ref?.split('/')?.pop() - } - } - }) - } - - private mapOperation(operationType: OperationType, message: Message, bindings?: any): Operation { - return { - protocol: this.getProtocol(bindings), - operation: operationType, - message: message, - bindings: bindings - } - } - - private getProtocol(bindings?: any): string { - return Object.keys(bindings)[0]; - } - - private mapSchemas(schemas: Map): Map { - const s = new Map(); - Object.entries(schemas).forEach(([k, v]) => s.set(k, this.mapSchema(k, v))); - return s; - } - - private mapSchema(schemaName: string, schema: ServerAsyncApiSchema): Schema { - const properties = schema.properties !== undefined ? this.mapSchemas(schema.properties) : undefined - const example = schema.example !== undefined ? new Example(schema.example) : undefined + private mapServerAsyncApiMessages( + messages: ServerAsyncApiMessage[] + ): Message[] { + return messages.map((v) => { return { - name: schema.$ref, - description: schema.description, - anchorIdentifier: '#' + schemaName, - anchorUrl: AsyncApiMapperService.BASE_URL + schema.$ref?.split('/')?.pop(), - type: schema.type, - format: schema.format, - enum: schema.enum, - properties: properties, - required: schema.required, - example: example, - } - } + name: v.name, + title: v.title, + description: v.description, + payload: { + name: v.payload.$ref, + anchorUrl: + AsyncApiMapperService.BASE_URL + v.payload.$ref?.split('/')?.pop(), + }, + headers: { + name: v.headers.$ref, + anchorUrl: + AsyncApiMapperService.BASE_URL + v.headers.$ref?.split('/')?.pop(), + }, + }; + }); + } + + private mapOperation( + operationType: OperationType, + message: Message, + bindings?: any + ): Operation { + return { + protocol: this.getProtocol(bindings), + operation: operationType, + message: message, + bindings: bindings, + }; + } + + private getProtocol(bindings?: any): string { + return Object.keys(bindings)[0]; + } + + private mapSchemas( + schemas: Map + ): Map { + const s = new Map(); + Object.entries(schemas).forEach(([k, v]) => s.set(k, this.mapSchema(k, v))); + return s; + } + + private mapSchema(schemaName: string, schema: ServerAsyncApiSchema): Schema { + const properties = + schema.properties !== undefined + ? this.mapSchemas(schema.properties) + : undefined; + const example = + schema.example !== undefined ? new Example(schema.example) : undefined; + return { + name: schema.$ref, + description: schema.description, + anchorIdentifier: '#' + schemaName, + anchorUrl: + AsyncApiMapperService.BASE_URL + schema.$ref?.split('/')?.pop(), + type: schema.type, + format: schema.format, + enum: schema.enum, + properties: properties, + required: schema.required, + example: example, + }; + } } diff --git a/src/app/shared/asyncapi.service.ts b/src/app/shared/asyncapi.service.ts index 568cfe8..548ea0f 100644 --- a/src/app/shared/asyncapi.service.ts +++ b/src/app/shared/asyncapi.service.ts @@ -4,24 +4,30 @@ import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; import { map } from 'rxjs/operators'; import { Endpoints } from './endpoints'; -import {AsyncApiMapperService, ServerAsyncApi} from "./asyncapi-mapper.service"; +import { + AsyncApiMapperService, + ServerAsyncApi, +} from './asyncapi-mapper.service'; @Injectable() export class AsyncApiService { + private docs: AsyncApi; - private docs: AsyncApi; + constructor( + private http: HttpClient, + private asyncApiMapperService: AsyncApiMapperService + ) {} - constructor(private http: HttpClient, private asyncApiMapperService: AsyncApiMapperService) { + public getAsyncApi(): Observable { + if (this.docs) { + return of(this.docs); } - public getAsyncApi(): Observable { - if (this.docs) { - return of(this.docs); - } - - return this.http.get(Endpoints.docs).pipe(map(item => { - this.docs = this.asyncApiMapperService.toAsyncApi(item); - return this.docs; - })); - } + return this.http.get(Endpoints.docs).pipe( + map((item) => { + this.docs = this.asyncApiMapperService.toAsyncApi(item); + return this.docs; + }) + ); + } } diff --git a/src/app/shared/components/json/json.component.ts b/src/app/shared/components/json/json.component.ts index e587d00..c53e499 100644 --- a/src/app/shared/components/json/json.component.ts +++ b/src/app/shared/components/json/json.component.ts @@ -1,21 +1,23 @@ -import { Component, OnInit, Input } from '@angular/core'; - -@Component({ - selector: 'app-json', - template: '
', - styles: [`code { - margin-top: 16px; - padding: 8px; - border-radius: 4px; - }`] -}) -export class JsonComponent implements OnInit { - - @Input() data: any; - json: string; - - ngOnInit(): void { - this.json = JSON.stringify(this.data, null, 2); - } - -} +import { Component, OnInit, Input } from '@angular/core'; + +@Component({ + selector: 'app-json', + template: '
', + styles: [ + ` + code { + margin-top: 16px; + padding: 8px; + border-radius: 4px; + } + `, + ], +}) +export class JsonComponent implements OnInit { + @Input() data: any; + json: string; + + ngOnInit(): void { + this.json = JSON.stringify(this.data, null, 2); + } +} diff --git a/src/app/shared/endpoints.ts b/src/app/shared/endpoints.ts index aeef861..7183062 100644 --- a/src/app/shared/endpoints.ts +++ b/src/app/shared/endpoints.ts @@ -1,17 +1,14 @@ export class Endpoints { + private static contextPath = Endpoints.getContextPath(); - private static contextPath = Endpoints.getContextPath(); + private static getContextPath(): string { + let url = document.location.pathname; + return url.split('/asyncapi-ui.html')[0]; + } - private static getContextPath(): string { - let url = document.location.pathname; - return url.split("/asyncapi-ui.html")[0]; - } - - public static docs = Endpoints.contextPath + '/docs'; - - public static getPublishEndpoint(protocol: string): string { - return Endpoints.contextPath + `/${protocol}/publish`; - } + public static docs = Endpoints.contextPath + '/docs'; + public static getPublishEndpoint(protocol: string): string { + return Endpoints.contextPath + `/${protocol}/publish`; + } } - diff --git a/src/app/shared/mock/mock-server.ts b/src/app/shared/mock/mock-server.ts index 5ef0211..ffd2e91 100644 --- a/src/app/shared/mock/mock-server.ts +++ b/src/app/shared/mock/mock-server.ts @@ -1,4 +1,8 @@ -import { InMemoryDbService, RequestInfo, STATUS } from 'angular-in-memory-web-api'; +import { + InMemoryDbService, + RequestInfo, + STATUS, +} from 'angular-in-memory-web-api'; import mockSpringwolfApp from './mock.springwolf-app.json'; import mockSpringwolfAmqp from './mock.springwolf-amqp-example.json'; import mockSpringwolfKafka from './mock.springwolf-kafka-example.json'; @@ -7,21 +11,21 @@ const mockAsyncApi = { ...mockSpringwolfApp, ...mockSpringwolfAmqp, ...mockSpringwolfKafka, -} +}; export class MockServer implements InMemoryDbService { createDb() { - return {kafka: []}; + return { kafka: [] }; } get(reqInfo: RequestInfo) { - console.log("Returning mock data") + console.log('Returning mock data'); if (reqInfo.req.url.endsWith('/docs')) { return reqInfo.utils.createResponse$(() => { return { status: STATUS.OK, - body: mockSpringwolfKafka - } + body: mockSpringwolfKafka, + }; }); } @@ -32,12 +36,11 @@ export class MockServer implements InMemoryDbService { if (reqInfo.req.url.endsWith('/publish')) { return reqInfo.utils.createResponse$(() => { return { - status: STATUS.OK - } - }) + status: STATUS.OK, + }; + }); } return undefined; } - } diff --git a/src/app/shared/models/asyncapi.model.ts b/src/app/shared/models/asyncapi.model.ts index 8c99988..cbb60e3 100644 --- a/src/app/shared/models/asyncapi.model.ts +++ b/src/app/shared/models/asyncapi.model.ts @@ -4,8 +4,8 @@ import { Channel } from './channel.model'; import { Schema } from './schema.model'; export interface AsyncApi { - info: Info; - servers: Map; - channels: Channel[]; - components: { schemas: Map } -} \ No newline at end of file + info: Info; + servers: Map; + channels: Channel[]; + components: { schemas: Map }; +} diff --git a/src/app/shared/models/channel.model.ts b/src/app/shared/models/channel.model.ts index f4d6aea..4c6a172 100644 --- a/src/app/shared/models/channel.model.ts +++ b/src/app/shared/models/channel.model.ts @@ -1,29 +1,29 @@ -export const CHANNEL_ANCHOR_PREFIX = "#channel-" +export const CHANNEL_ANCHOR_PREFIX = '#channel-'; export interface Channel { - name: string; - anchorIdentifier: string; - description?: string; - operation: Operation; + name: string; + anchorIdentifier: string; + description?: string; + operation: Operation; } -export type OperationType = "publish" | "subscribe"; +export type OperationType = 'publish' | 'subscribe'; export interface Operation { - message: Message; - bindings?: { [type: string]: any }; - protocol: string; - operation: OperationType; + message: Message; + bindings?: { [type: string]: any }; + protocol: string; + operation: OperationType; } export interface Message { + name: string; + title: string; + description?: string; + payload: { + name: string; + anchorUrl: string; + }; + headers: { name: string; - title: string; - description?: string; - payload: { - name: string; - anchorUrl: string; - }; - headers: { - name: string - anchorUrl: string; - }; + anchorUrl: string; + }; } diff --git a/src/app/shared/models/example.model.ts b/src/app/shared/models/example.model.ts index f60b3e1..1aea0dc 100644 --- a/src/app/shared/models/example.model.ts +++ b/src/app/shared/models/example.model.ts @@ -1,5 +1,4 @@ export class Example { - public value: string; public lineCount: number; @@ -7,5 +6,4 @@ export class Example { this.value = JSON.stringify(exampleObject, null, 2); this.lineCount = this.value.split('\n').length; } - -} \ No newline at end of file +} diff --git a/src/app/shared/models/info.model.ts b/src/app/shared/models/info.model.ts index 4aad896..0a534f7 100644 --- a/src/app/shared/models/info.model.ts +++ b/src/app/shared/models/info.model.ts @@ -1,6 +1,6 @@ export interface Info { - title: string; - version: string; - description?: string; - asyncApiJson: object; + title: string; + version: string; + description?: string; + asyncApiJson: object; } diff --git a/src/app/shared/models/schema.model.ts b/src/app/shared/models/schema.model.ts index e2e24af..1dcc4e5 100644 --- a/src/app/shared/models/schema.model.ts +++ b/src/app/shared/models/schema.model.ts @@ -1,14 +1,14 @@ import { Example } from './example.model'; export interface Schema { - name?: string; - description?: string; - anchorIdentifier?: string; - anchorUrl?: string; - type?: string; - format?: string; - enum?: string[]; - properties?: Map; - example?: Example; - required?: string[]; + name?: string; + description?: string; + anchorIdentifier?: string; + anchorUrl?: string; + type?: string; + format?: string; + enum?: string[]; + properties?: Map; + example?: Example; + required?: string[]; } diff --git a/src/app/shared/models/server.model.ts b/src/app/shared/models/server.model.ts index a51c33c..27c5cf3 100644 --- a/src/app/shared/models/server.model.ts +++ b/src/app/shared/models/server.model.ts @@ -1,4 +1,4 @@ export interface Server { - url: string; - protocol: string; + url: string; + protocol: string; } diff --git a/src/app/shared/publisher.service.ts b/src/app/shared/publisher.service.ts index 436dea9..f0bc426 100644 --- a/src/app/shared/publisher.service.ts +++ b/src/app/shared/publisher.service.ts @@ -1,19 +1,22 @@ import { Injectable } from '@angular/core'; -import {HttpClient, HttpParams} from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable } from 'rxjs'; import { Endpoints } from './endpoints'; @Injectable() export class PublisherService { + constructor(private http: HttpClient) {} - constructor(private http: HttpClient) { } - - publish(protocol: string, topic: string, payload: object, headers: object): Observable { + publish( + protocol: string, + topic: string, + payload: object, + headers: object + ): Observable { const url = Endpoints.getPublishEndpoint(protocol); const params = new HttpParams().set('topic', topic); - const body = {"payload" : payload, "headers" : headers } + const body = { payload: payload, headers: headers }; console.log(`Publishing to ${url}`); return this.http.post(url, body, { params }); } - } diff --git a/src/asyncapi-ui.html b/src/asyncapi-ui.html index c81ca54..f9309aa 100644 --- a/src/asyncapi-ui.html +++ b/src/asyncapi-ui.html @@ -1,15 +1,21 @@ - + - - - springwolf - - - - - - - - - + + + springwolf + + + + + + + + + diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 3612073..c966979 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,3 +1,3 @@ export const environment = { - production: true + production: true, }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 7b4f817..99c3763 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -3,7 +3,7 @@ // The list of file replacements can be found in `angular.json`. export const environment = { - production: false + production: false, }; /* diff --git a/src/main.ts b/src/main.ts index c7b673c..d9a2e7e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,5 +8,6 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/src/polyfills.ts b/src/polyfills.ts index 03711e5..e49856e 100644 --- a/src/polyfills.ts +++ b/src/polyfills.ts @@ -55,8 +55,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. - +import 'zone.js/dist/zone'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS diff --git a/src/styles.css b/src/styles.css index 48c4c61..52c8453 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,13 +1,19 @@ /* You can add global styles to this file, and also import other style files */ @import '~highlight.js/styles/atom-one-dark.css'; -html, body { height: 100%; } -body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } +html, +body { + height: 100%; +} +body { + margin: 0; + font-family: Roboto, 'Helvetica Neue', sans-serif; +} .mat-card { - background-color: #F5F5F5; + background-color: #f5f5f5; } .mat-expansion-panel { - background-color: #F5F5F5; -} \ No newline at end of file + background-color: #f5f5f5; +} diff --git a/src/test.ts b/src/test.ts index 50193eb..bb60ce8 100644 --- a/src/test.ts +++ b/src/test.ts @@ -4,11 +4,15 @@ import 'zone.js/dist/zone-testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, - platformBrowserDynamicTesting + platformBrowserDynamicTesting, } from '@angular/platform-browser-dynamic/testing'; declare const require: { - context(path: string, deep?: boolean, filter?: RegExp): { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { keys(): string[]; (id: string): T; };