diff --git a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-conclusion/conclusion.adoc b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-conclusion/conclusion.adoc index 0863eacf1..149e8a437 100644 --- a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-conclusion/conclusion.adoc +++ b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-conclusion/conclusion.adoc @@ -14,8 +14,8 @@ Then, we focused on developing several isolated microservices. Some written in pure JAX-RS (such as the Villain) others with Reactive JAX-RS and Reactive Hibernate (such as the Hero). These microservices return data in JSON, validate data thanks to Bean Validation, store and retrieve data from a relational database with the help of JPA, Panache and JTA. -You then installed an already coded Angular application on another instance of Quarkus. -At this stage, the Angular application couldn't access the microservices because of CORS issues that we quickly fixed. +You then installed an already coded React application on another instance of Quarkus. +At this stage, the React application couldn't access the microservices because of CORS issues that we quickly fixed. Then, we made the microservices communicate with each other in HTTP thanks to REST Client. But HTTP-related technologies usually use synchronous communication and therefore need to deal with invocation failure. diff --git a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-introduction/introduction-preparing.adoc b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-introduction/introduction-preparing.adoc index 160eb6da1..1ed8931e3 100644 --- a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-introduction/introduction-preparing.adoc +++ b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-introduction/introduction-preparing.adoc @@ -48,7 +48,7 @@ quarkus-workshop-super-heroes ++ rest-heroes | Reactive REST API for CRUD operations on Heroes (you will create it) ++ rest-narration | REST API invoking an AI to narrate the fight (you will create it) ++ rest-villains | REST API for CRUD operations on Villains (you will create it) -++ ui-super-heroes | Angular application so we can fight visually +++ ui-super-heroes | React application so we can fight visually } } @endsalt diff --git a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-introduction/introduction-presentation.adoc b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-introduction/introduction-presentation.adoc index 1ffdf8468..5ab497918 100644 --- a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-introduction/introduction-presentation.adoc +++ b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-introduction/introduction-presentation.adoc @@ -22,7 +22,7 @@ endif::give-solution[] In this workshop, you will develop an application that allows superheroes to fight against supervillains. You will be developing several microservices communicating with each other: -* _Super Hero UI_: an Angular application to pick up a random superhero, a random supervillain, and makes them fight. +* _Super Hero UI_: a React application to pick up a random superhero, a random supervillain, and makes them fight. The Super Hero UI is exposed via Quarkus and invokes the Fight REST API * _Villain REST API_: A classical HTTP microservice exposing CRUD operations on Villains, stored in a PostgreSQL database * _Hero REST API_: A reactive HTTP microservice exposing CRUD operations on Heroes, stored in a Postgres database @@ -68,16 +68,16 @@ endif::use-ai[] The table at the bottom shows the list of the previous fights. ifdef::use-ai[] -image::angular-ui-ai.png[role=half-size] +image::react-ui-ai.png[role=half-size] endif::use-ai[] ifndef::use-ai[] -image::angular-ui.png[role=half-size] +image::react-ui.png[role=half-size] endif::use-ai[] The Statistics UI shows the number of fights per hero and villain. -image::angular-ui-stats.png[role=half-size] +image::react-ui-stats.png[role=half-size] == How Does This Workshop Work? @@ -87,7 +87,7 @@ The structure of this workshop is as follows: * _Installing all the needed tools_: in this section, you will install all the tools and code to be able to develop, compile and execute our application * _Developing microservices with Quarkus_: -in this section, you will develop a microservice architecture by creating several Maven projects, write some Java code, add JPA entities, JAX-RS REST endpoints, write some tests, use an Angular web application, and all that on Quarkus +in this section, you will develop a microservice architecture by creating several Maven projects, write some Java code, add JPA entities, JAX-RS REST endpoints, write some tests, use a React web application, and all that on Quarkus ifdef::use-ai[] * _Artificial Intelligence_: in this section, you will use OpenAI or Azure OpenAI to invoke an AI so you can generate a random narration of the fight diff --git a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-microservices/microservices.adoc b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-microservices/microservices.adoc index 5aa66d5e1..230f0ce28 100644 --- a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-microservices/microservices.adoc +++ b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-microservices/microservices.adoc @@ -5,7 +5,7 @@ So far we've built two microservices: the villains and heroes microservices. In the following sections you will develop an extra microservice: a _fight_ microservice where heroes and villains fight. -We will also add an Angular front-end, so we can fight graphically. +We will also add a React front-end, so we can fight graphically. But as you can notice in the diagram below, these microservices still not communicate with each other. You will have to wait the next chapter for that ;o) diff --git a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-ui/ui.adoc b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-ui/ui.adoc index 5a7cce552..5fd74b8c3 100644 --- a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-ui/ui.adoc +++ b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/core-ui/ui.adoc @@ -3,9 +3,9 @@ Now that we have the three main microservices, time to have a decent user interface to start fighting. The purpose of this workshop is not to develop a web interface and learn _yet another web framework_. -This time you will just execute another Quarkus instance with an already Angular application. +This time you will just execute another Quarkus instance with an already React application. We will be using https://quarkiverse.github.io/quarkiverse-docs/quarkus-quinoa/dev/[Quinoa] -to handle building and serving the Angular application. +to handle building and serving the React application. == Quinoa? @@ -16,21 +16,20 @@ It also helps with both packaging and serving, and if you want it to, abstracts When enabled in development mode, Quinoa will start the UI live coding server provided by the target framework and forward relevant requests to it. In production mode, Quinoa will run the build and process the generated files to serve them at runtime. -Quinoa is framework-agnostic, and works with React, Angular, Vue, Lit, and others. - …) alongside other Quarkus services (REST, GraphQL, Security, Events, etc). +Quinoa is framework-agnostic, and works with Angular, React, Vue, Lit, and others alongside other Quarkus services (REST, GraphQL, Security, Events, etc). == The Web Application Navigate to the `super-heroes/ui-super-heroes` directory. It contains the code of the application. -The Angular application is in `src/main/webui`. -Being an Angular application, you will find a `package.json` file which defines all the needed dependencies. -All the Angular code (graphical components, model, services) is located under `src/main/webui/src/app`. +The React application is in `src/main/webui`. +Being a React application, you will find a `package.json` file which defines all the needed dependencies. +All the React code (graphical components, model, services) is located under `src/main/webui/src/app`. === Running the Web Application -We don't need to worry too much about the Angular code. +We don't need to worry too much about the React code. [example, role="cta"] -- @@ -52,8 +51,8 @@ You should see console output like the following [source,shell,subs="attributes+"] ---- -2023-05-18 14:22:02,745 INFO [io.qua.qui.dep.ForwardedDevProcessor] (build-5) Quinoa package manager live coding is up and running on port: 4200 (in 18118ms) -2023-05-18 14:22:02,749 INFO [io.qua.qui.dep.ForwardedDevProcessor] (build-40) Quinoa is forwarding unhandled requests to port: 4200 +2023-05-18 14:22:02,745 INFO [io.qua.qui.dep.ForwardedDevProcessor] (build-5) Quinoa package manager live coding is up and running on port: 3000 (in 18118ms) +2023-05-18 14:22:02,749 INFO [io.qua.qui.dep.ForwardedDevProcessor] (build-40) Quinoa is forwarding unhandled requests to port: 3000 __ ____ __ _____ ___ __ ____ ______ --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \ @@ -67,8 +66,8 @@ because we need to fix a few things and add some more implementation. image::blank-ui.png[] -If you want, you can visit http://localhost:4200 (the usual Angular port) to confirm that Quinoa is forwarding what's on port 4200 to port 8080. -The version of the application on http://localhost:4200 will always stay disappointingly blank, +If you want, you can visit http://localhost:3000 (the usual React port) to confirm that Quinoa is forwarding what's on port 3000 to port 8080. +The version of the application on http://localhost:3000 will always stay disappointingly blank, because it's looking for its config on the wrong port. So don't use that one! You might want to create a fuller Quarkus BFF for the UI, and use something like https://quarkus.io/guides/stork[Stork] for service discovery. diff --git a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/images/angular-ui-ai.png b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/images/react-ui-ai.png similarity index 100% rename from quarkus-workshop-super-heroes/docs/src/docs/asciidoc/images/angular-ui-ai.png rename to quarkus-workshop-super-heroes/docs/src/docs/asciidoc/images/react-ui-ai.png diff --git a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/images/angular-ui-stats.png b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/images/react-ui-stats.png similarity index 100% rename from quarkus-workshop-super-heroes/docs/src/docs/asciidoc/images/angular-ui-stats.png rename to quarkus-workshop-super-heroes/docs/src/docs/asciidoc/images/react-ui-stats.png diff --git a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/images/angular-ui.png b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/images/react-ui.png similarity index 100% rename from quarkus-workshop-super-heroes/docs/src/docs/asciidoc/images/angular-ui.png rename to quarkus-workshop-super-heroes/docs/src/docs/asciidoc/images/react-ui.png diff --git a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-ai/ai.adoc b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-ai/ai.adoc index ce39eb3cc..858a2c942 100644 --- a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-ai/ai.adoc +++ b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-ai/ai.adoc @@ -420,4 +420,4 @@ The result of the fight is displayed, and below, you have a "Narrate the fight" Click on it, wait a few seconds (remember that the Narration microservice access a remote AI service that takes time) and the narration of the fight is displayed. Being _Generative AI_, you can click several times on the button and you will get different narrations. -image::angular-ui.png[role=half-size] +image::react-ui.png[role=half-size] diff --git a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-azure-container-apps/azure-aca-running-app.adoc b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-azure-container-apps/azure-aca-running-app.adoc index 41620bdeb..1e66e1e18 100644 --- a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-azure-container-apps/azure-aca-running-app.adoc +++ b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-azure-container-apps/azure-aca-running-app.adoc @@ -368,7 +368,7 @@ include::{projectdir}/infrastructure/azure-deploy-aca.sh[tags=adocAppFightLogs] === Super Hero UI Like for the previous microservices, we will be deploying the UI as Docker image as we did for the previous microservices. -But we could have also deployed the Super Hero UI using Azure Static Webapps which is suited for Angular applications. +But we could have also deployed the Super Hero UI using Azure Static Webapps which is suited for React applications. If you are interested in this approach, you can check https://azure.microsoft.com/services/app-service/static/[Azure Static Webapps]. [example, role="cta"] diff --git a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-azure-container-apps/azure-local-running-app.adoc b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-azure-container-apps/azure-local-running-app.adoc index d8826355f..1d2e27ffc 100644 --- a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-azure-container-apps/azure-local-running-app.adoc +++ b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-azure-container-apps/azure-local-running-app.adoc @@ -132,10 +132,10 @@ Once all the containers are started, you can: On http://localhost:8080 you should see the user interface, and you should be able to fight super heroes against super villains: ifdef::use-ai[] -image::angular-ui-ai.png[] +image::react-ui-ai.png[] endif::use-ai[] ifndef::use-ai[] -image::angular-ui.png[] +image::react-ui.png[] endif::use-ai[] @@ -143,7 +143,7 @@ On http://localhost:8085 you should see the statistics of the fights. When super heroes and super heroes are fights, the statistics shows which one has won the most fights, and the percentage of fights won by the two groups. The UI is automatically updated at each fight: -image::angular-ui-stats.png[] +image::react-ui-stats.png[] You should see the user interface and everything should work. Remember to shutdown the entire application with: diff --git a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-contract-testing/contract-testing-pact.adoc b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-contract-testing/contract-testing-pact.adoc index 6f9e941e0..22ca4f395 100644 --- a/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-contract-testing/contract-testing-pact.adoc +++ b/quarkus-workshop-super-heroes/docs/src/docs/asciidoc/optional-contract-testing/contract-testing-pact.adoc @@ -34,7 +34,7 @@ The heroes, villains, and fights tests should all be running clean. A successful refactoring! -- -Now visit the UI at http://localhost:4200/. +Now visit the UI at http://localhost:8080/. What's going on? The hero's level is always 0. Why didn't the tests catch this? diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/README.md b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/README.md index 0f274e34c..45866a29d 100644 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/README.md +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/README.md @@ -1,8 +1,8 @@ -# Angular UI +# React UI ## Introduction -This is the main user interface for the application. The application is an Angular application served via Quarkus +This is the main user interface for the application. The application is a React application served via Quarkus Quinoa. ## TLDR @@ -13,8 +13,8 @@ mvn quarkus:dev ## Building and running the application -Builds are served using a Quarkus server. This server serves the compiled Angular application and an `env.js` file. -This `env.js` file is generated at startup, and adds a `window.NG_CONFIG` property that the Angular application can read +Builds are served using a Quarkus server. This server serves the compiled React application and an `env.js` file. +This `env.js` file is generated at startup, and adds a `window.APP_CONFIG` property that the React application can read from. ### Configuration @@ -49,8 +49,8 @@ Then use the following command: quarkus dev ``` -This starts the Angular hot reloading server at http://localhost:4200, and the Quarkus server to supply the `env.js` -file. The Quarkus server will proxy requests to the Angular development server. +This starts the React hot reloading server at http://localhost:3000, and the Quarkus server to supply the `env.js` +file. The Quarkus server will proxy requests to the React development server. You can then access the application on http://localhost:8080. @@ -114,7 +114,7 @@ system, [follow these instructions](../README.md#deploying-to-kubernetes). ### Routing -There are 2 environment variables that can be set on this application to control how the Angular UI communicates with +There are 2 environment variables that can be set on this application to control how the React UI communicates with the [`rest-fights`](../rest-fights) application: | Env Var | Default Value | Description | @@ -122,63 +122,10 @@ the [`rest-fights`](../rest-fights) application: | `API_BASE_URL` | `http://localhost:8082` | The base URL for the [`rest-fights`](../rest-fights) application. Set this to a fully qualified URL (i.e. http://www.example.com or http://somehost.com:someport) to define the URL for the [`rest-fights`](../rest-fights) application. | | `CALCULATE_API_BASE_URL` | `false` on Minikube/Kubernetes. `true` on OpenShift. | If `true`, look at the URL in the browser and replace the `ui-super-heroes` host name with -# Under the covers with Angular +# Under the covers with React -Although you don't need to know Angular to run this component, the following commands may be helpful for making changes. - -## Angular CLI commands - -### Initiliaze - -``` -$ ng new super-heroes --directory super-heroes-ui --prefix hero --routing false --skipTests true --inlineStyle true --commit false --minimal true --style css -``` - -### Admin components - -``` -$ ng generate component fight-list --spec false --inline-style true -$ ng generate component fight --spec false --inline-style true -``` - -### Material design - -``` -$ ng add @angular/material -``` - -### Swagger Codegen - -``` -$ swagger-codegen generate -i http://localhost:8082/openapi -l typescript-angular -o src/app/shared -``` - -## Angular CLI documentation - -## Development server - -Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change -any of the source files. - -## Code scaffolding - -Run `ng generate component component-name` to generate a new component. You can also -use `ng generate directive|pipe|service|class|guard|interface|enum|module`. - -## Build - -Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag -for a production build. +Although you don't need to know React to run this component, the following commands may be helpful for making changes. ## Running unit tests -Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). - -## Running end-to-end tests - -Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). - -## Further help - -To get more help on the Angular CLI use `ng help` or go check out -the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). +Run `npm test` to execute the unit tests via Jest. diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/java/io/quarkus/workshop/superheroes/ui/EnvResource.java b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/java/io/quarkus/workshop/superheroes/ui/EnvResource.java index 806a49e13..78d460451 100644 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/java/io/quarkus/workshop/superheroes/ui/EnvResource.java +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/java/io/quarkus/workshop/superheroes/ui/EnvResource.java @@ -34,7 +34,7 @@ public String getConfig() throws JsonProcessingException { calculateApiBaseUrl); // We could just return the Config object, but that would be json, and we want a // javascript snippet we can include with + + + + +
+ + + + diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/App.js b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/App.js new file mode 100644 index 000000000..6916799d1 --- /dev/null +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/App.js @@ -0,0 +1,16 @@ +import {FightList} from "./fight-list/FightList" +import Fight from "./fight/Fight" + +function App() { + return ( + <> +

+ Welcome to Super Heroes Fight! +

+ + + + ) +} + +export default App diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/App.test.js b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/App.test.js new file mode 100644 index 000000000..a6da4ac63 --- /dev/null +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/App.test.js @@ -0,0 +1,10 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import App from "./App"; +import "@testing-library/jest-dom"; + +test("renders a suitable title", () => { + render(); + const titleElement = screen.getByText(/Super Heroes/i); + expect(titleElement).toBeInTheDocument(); +}); diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/app-routing.module.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/app-routing.module.ts deleted file mode 100644 index 06c734263..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/app-routing.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; - - -const routes: Routes = []; - -@NgModule({ - imports: [RouterModule.forRoot(routes)], - exports: [RouterModule] -}) -export class AppRoutingModule { } diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/app.component.html b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/app.component.html deleted file mode 100644 index a60102154..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/app.component.html +++ /dev/null @@ -1,8 +0,0 @@ -
-

- Welcome to {{title}}! -

- - - -
diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/app.component.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/app.component.ts deleted file mode 100644 index 3884559a2..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/app.component.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {Component} from '@angular/core'; - -@Component({ - selector: 'hero-root', - templateUrl: './app.component.html' -}) -export class AppComponent { - title = 'Super Heroes Fight'; -} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/app.module.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/app.module.ts deleted file mode 100644 index b0f083fd9..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/app.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; - -import { AppComponent } from './app.component'; -import { FightListComponent } from './fight-list/fight-list.component'; -import { FightComponent } from './fight/fight.component'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatCardModule } from '@angular/material/card'; -import { MatButtonModule } from '@angular/material/button'; -import { MatTableModule } from '@angular/material/table'; -import { FightService } from './shared'; -import { HttpClientModule } from '@angular/common/http'; -import { MatGridListModule } from '@angular/material/grid-list'; - -@NgModule({ - declarations: [ - AppComponent, - FightListComponent, - FightComponent - ], - imports: [ - BrowserModule, - BrowserAnimationsModule, - HttpClientModule, - MatDividerModule, - MatCardModule, - MatButtonModule, - MatGridListModule, - MatTableModule - ], - providers: [FightService], - bootstrap: [AppComponent] -}) -export class AppModule { } diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight-list/FightList.js b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight-list/FightList.js new file mode 100644 index 000000000..e9eaa66b4 --- /dev/null +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight-list/FightList.js @@ -0,0 +1,38 @@ +import {useEffect, useState} from "react" +import {getFights} from "../shared/api/fight-service" + +export function FightList() { + + + const [fights, setFights] = useState() + + useEffect(() => { + getFights().then(answer => setFights(answer)) + }, [] + ) + + return ( + + + + + + + + + + + + + {fights && fights.map(element => ( + + + + + + ))} + +
IdFight DateWinnerLoser
{element.id} {element.fightDate} {element.winnerName} {element.loserName}
+ ) +} + diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight-list/FightList.test.js b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight-list/FightList.test.js new file mode 100644 index 000000000..451d5871d --- /dev/null +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight-list/FightList.test.js @@ -0,0 +1,55 @@ +import React from "react" +import {render, screen} from "@testing-library/react" +import "@testing-library/jest-dom" +import {FightList} from "./FightList" +import {getFights} from "../shared/api/fight-service" +import {act} from "react-dom/test-utils" + +jest.mock("../shared/api/fight-service") + +const fight = { + fightDate: "2023-10-24T21:34:47.617598Z", + id: 200, + loserLevel: 1, + loserName: "Fake hero", + loserPicture: "https://dummyimage.com/280x380/1e8fff/ffffff&text=Mock+Hero", + loserPowers: "Being fake", + loserTeam: "heroes", + winnerLevel: 42, + winnerName: "Fake villain", + winnerPicture: "https://dummyimage.com/280x380/b22222/ffffff&text=Mock+Villain", + winnerPowers: "Dissimulation", + winnerTeam: "villains" +} + +describe("the fight list", () => { + beforeEach(() => { + getFights.mockResolvedValue([fight]) + }) + + afterAll(() => { + jest.resetAllMocks() + }) + + + it("renders a table with column headings", async () => { + await act(async () => { + render() + }) + expect(screen.getByText(/Winner/i)).toBeInTheDocument() + expect(screen.getByText(/Loser/i)).toBeInTheDocument() + expect(screen.getByText(/Fight Date/i)).toBeInTheDocument() + + }) + + it("renders rows for the fights", async () => { + await act(async () => { + render() + }) + + expect(screen.getByText("Fake hero")).toBeInTheDocument() + expect(screen.getByText("Fake villain")).toBeInTheDocument() + expect(screen.getByText("2023-10-24T21:34:47.617598Z")).toBeInTheDocument() + + }) +}) diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight-list/fight-list.component.html b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight-list/fight-list.component.html deleted file mode 100644 index 4bb1dc1d9..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight-list/fight-list.component.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -
Id {{element.id}} Fight Date {{element.fightDate | date: 'medium'}} Winner {{element.winnerName}} Loser {{element.loserName}}
diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight-list/fight-list.component.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight-list/fight-list.component.ts deleted file mode 100644 index 19e6ef322..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight-list/fight-list.component.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {Component, OnInit} from '@angular/core'; -import { Fight, FightService } from '../shared'; -import {MatTableDataSource} from "@angular/material/table"; - -@Component({ - selector: 'hero-fight-list', - templateUrl: './fight-list.component.html', - styles: [] -}) -export class FightListComponent implements OnInit { - - dataSource: MatTableDataSource < Fight > ; - displayedColumns: string[] = ['id', 'fightDate', 'winnerName', 'loserName']; - - constructor(private fightService: FightService) { - this.dataSource = new MatTableDataSource(); - fightService.emitter.subscribe(fight => { - const data = this.dataSource.data; - data.unshift(fight); - this.dataSource.data = data; - }) - } - - ngOnInit() { - this.fightService.apiFightsGet().subscribe(fights => { - this.dataSource.data = fights.reverse(); - }); - } -} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight/Fight.js b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight/Fight.js new file mode 100644 index 000000000..756dbca69 --- /dev/null +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight/Fight.js @@ -0,0 +1,109 @@ +import {getRandomFighters, narrateFight, startFight} from "../shared/api/fight-service" +import {useEffect, useState} from "react" +import {faComment} from "@fortawesome/free-solid-svg-icons" +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome" + + +function Fight() { + const [fighters, setFighters] = useState() + const [fightResult, setFightResult] = useState() + const [narration, setNarration] = useState() + const [showVillainPowers, setShowVillainPowers] = useState() + const [showHeroPowers, setShowHeroPowers] = useState() + + + const newFighters = () => { + getRandomFighters().then(answer => setFighters(answer)) + } + + const narrate = () => { + narrateFight(fightResult).then(answer => setNarration(answer)) + } + + const fight = () => { + startFight(fighters).then(response => setFightResult(response)) + } + + // This initialises the component on its initial load with a call to get fighters + useEffect(newFighters, [] + ) + + const winner = fightResult?.winnerName + + if (!fighters) { + return ( +
No back-end is available. Do you need to start some services?
+ ) + } else + return ( +
+
+
+

+ {fighters.hero.name} +

+
+ the hero + +

{fighters.hero.level}

+

+ +
+ {fighters.hero.powers} +
+
+
+
+ +
+
+
+ + +
+ + {winner && (
+ Winner is {winner} + + {narration && (
{narration}
)} +
+ )} +
+
+ +
+
+

+ {fighters.villain.name} +

+
+ the villain + +

{fighters.villain.level}

+

+ +
+ {fighters.villain.powers} +
+ +
+
+
+ +
+ ) +} + +export default Fight diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight/Fight.test.js b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight/Fight.test.js new file mode 100644 index 000000000..d1cd7df96 --- /dev/null +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight/Fight.test.js @@ -0,0 +1,127 @@ +import React from "react" +import {fireEvent, render, screen} from "@testing-library/react" +import "@testing-library/jest-dom" +import Fight from "./Fight" +import {getRandomFighters, narrateFight, startFight} from "../shared/api/fight-service" +import {act} from "react-dom/test-utils" + +jest.mock("../shared/api/fight-service") + +const fighters = { + hero: { + name: 'Fake hero', + level: 1, + picture: 'https://dummyimage.com/280x380/1e8fff/ffffff&text=Fake+Hero', + powers: 'Fake hero powers' + }, + villain: { + name: 'Fake villain', + level: 42, + picture: 'https://dummyimage.com/280x380/b22222/ffffff&text=Fake+Villain', + powers: 'Fake villain powers' + } +} + +const fight = { + fightDate: "2023-10-24T21:34:47.617598Z", + id: 200, + loserLevel: 1, + loserName: "Fake hero", + loserPicture: "https://dummyimage.com/280x380/1e8fff/ffffff&text=Mock+Hero", + loserPowers: "Being fake", + loserTeam: "heroes", + winnerLevel: 42, + winnerName: "Fake villain", + winnerPicture: "https://dummyimage.com/280x380/b22222/ffffff&text=Mock+Villain", + winnerPowers: "Dissimulation", + winnerTeam: "villains" +} + +const narration = "Ooh, it was a close fight but in the end the villain prevailed by sitting on the hero." + +describe("the fight visualisation", () => { + + describe("when the back end is missing", () => { + + beforeEach(() => { + // Return undefined + getRandomFighters.mockResolvedValue() + }) + + afterAll(() => { + jest.resetAllMocks() + }) + + it("renders a helpful message", async () => { + await act(async () => { + render() + }) + expect(screen.getByText(/back-end/i)).toBeInTheDocument() + }) + }) + + describe("when a back end is available", () => { + beforeEach(() => { + getRandomFighters.mockResolvedValue(fighters) + startFight.mockResolvedValue(fight) + narrateFight.mockResolvedValue(narration) + }) + + afterAll(() => { + jest.resetAllMocks() + }) + + it("renders fighters", async () => { + await act(async () => { + render() + }) + expect(screen.getByText("Fake hero")).toBeInTheDocument() + expect(screen.getByText("Fake villain")).toBeInTheDocument() + }) + + it("renders a fight button", async () => { + await act(async () => { + render() + }) + const button = screen.getByText(/FIGHT !/i) + expect(button).toBeInTheDocument() + }) + + it("renders winners when the fight button is clicked", async () => { + await act(async () => { + render() + }) + + const nameCount = screen.getAllByText("Fake villain").length + + await act(async () => { + fireEvent.click(screen.getByText(/FIGHT !/i)) + }) + expect(startFight).toHaveBeenLastCalledWith(fighters) + expect(screen.getByText(/Winner is/i)).toBeInTheDocument() + // The winner name is in a span by itself but there should be more occurrences of the name count + expect(screen.getAllByText("Fake villain")).toHaveLength(nameCount + 1) + }) + + it("renders narration when the narrate button is clicked", async () => { + await act(async () => { + render() + }) + + const nameCount = screen.getAllByText("Fake villain").length + + await act(async () => { + fireEvent.click(screen.getByText(/FIGHT !/i)) + }) + expect(screen.queryByText(narration)).not.toBeInTheDocument() + await act(async () => { + fireEvent.click(screen.getByText(/Narrate/i)) + }) + expect(narrateFight).toHaveBeenLastCalledWith(fight) + expect(screen.getByText(narration)).toBeInTheDocument() + }) + + + }) + +}) diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight/fight.component.html b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight/fight.component.html deleted file mode 100644 index b991295e4..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight/fight.component.html +++ /dev/null @@ -1,62 +0,0 @@ -
-
-
-

- {{fighters.hero.name}} -

-
- - -

{{fighters.hero.level}}

-

- -
- {{fighters.hero.powers}} -
-
-
-
- -
-
-
- - -
- -
- Winner is {{winner}} - -
{{narration}}
-
-
-
- -
-
-

- {{fighters.villain.name}} -

-
- - -

{{fighters.villain.level}}

-

- -
- {{fighters.villain.powers}} -
- -
-
-
- -
diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight/fight.component.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight/fight.component.ts deleted file mode 100644 index bbab2000e..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/fight/fight.component.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {Component, OnInit} from '@angular/core'; -import {Fight, Fighters, FightService} from '../shared'; - -@Component({ - selector: 'hero-fight', - templateUrl: './fight.component.html' -}) -export class FightComponent implements OnInit { - - fighters: Fighters = new Fighters(); - wonFight: Fight; - winner: String; - narration: String; - - constructor(private fightService: FightService) { - } - - ngOnInit() { - this.newFighters(); - } - - fight() { - this.fightService.apiFightsPost(this.fighters).subscribe( - fight => { - this.fightService.onNewFight(fight); - this.winner = fight.winnerName; - this.wonFight = fight; - this.narration = ""; - } - ); - } - - narrate() { - this.fightService.apiNarrateFightPost(this.wonFight).subscribe( - narration => { - this.fightService.onNewFightNarration(narration); - this.narration = narration; - } - ); - } - - newFighters() { - this.winner = null; - this.fightService.apiFightsRandomfightersGet().subscribe(fighters => this.fighters = fighters); - } -} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/.swagger-codegen-ignore b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/.swagger-codegen-ignore deleted file mode 100644 index c5fa491b4..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/.swagger-codegen-ignore +++ /dev/null @@ -1,23 +0,0 @@ -# Swagger Codegen Ignore -# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen - -# Use this file to prevent files from being overwritten by the generator. -# The patterns follow closely to .gitignore or .dockerignore. - -# As an example, the C# client generator defines ApiClient.cs. -# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: -#ApiClient.cs - -# You can match any string of characters against a directory, file or extension with a single asterisk (*): -#foo/*/qux -# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux - -# You can recursively match patterns against a directory, file or extension with a double asterisk (**): -#foo/**/qux -# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux - -# You can also negate patterns with an exclamation (!). -# For example, you can ignore all files in a docs folder with the file extension .md: -#docs/*.md -# Then explicitly reverse the ignore rule for a single file: -#!docs/README.md diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/.swagger-codegen/VERSION b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/.swagger-codegen/VERSION deleted file mode 100644 index e4a0720dd..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/.swagger-codegen/VERSION +++ /dev/null @@ -1 +0,0 @@ -3.0.11 \ No newline at end of file diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api.module.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api.module.ts deleted file mode 100644 index 93410d7e1..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { NgModule, ModuleWithProviders, SkipSelf, Optional } from '@angular/core'; -import { Configuration } from './configuration'; -import { HttpClient } from '@angular/common/http'; - - -import { FightService } from './api/fight.service'; - -@NgModule({ - imports: [], - declarations: [], - exports: [], - providers: [ - FightService ] -}) -export class ApiModule { - public static forRoot(configurationFactory: () => Configuration): ModuleWithProviders { - return { - ngModule: ApiModule, - providers: [ { provide: Configuration, useFactory: configurationFactory } ] - }; - } - - constructor( @Optional() @SkipSelf() parentModule: ApiModule, - @Optional() http: HttpClient) { - if (parentModule) { - throw new Error('ApiModule is already loaded. Import in your base AppModule only.'); - } - if (!http) { - throw new Error('You need to import the HttpClientModule in your AppModule! \n' + - 'See also https://github.com/angular/angular/issues/20575'); - } - } -} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api/api.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api/api.ts deleted file mode 100644 index 779aad8c2..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api/api.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './fight.service'; -import { FightService } from './fight.service'; -export const APIS = [FightService]; diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api/fight-service.js b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api/fight-service.js new file mode 100644 index 000000000..6eb7832bf --- /dev/null +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api/fight-service.js @@ -0,0 +1,101 @@ +/** + * Fight API + * This API allows a hero and a villain to fight + */ + + +import axios from "axios" + +const defaultHeaders = {'Content-Type': "application/json"} + +let basePath = window.APP_CONFIG.API_BASE_URL +const calculateApiBaseUrl = window.APP_CONFIG.CALCULATE_API_BASE_URL + +if (calculateApiBaseUrl) { + // If calculateApiBaseUrl then just replace "ui-super-heroes" with "rest-fights" in the current URL + basePath = window.location.protocol + "//" + window.location.host.replace('ui-super-heroes', 'rest-fights') +} + +// Fallback to whatever is in the browser if basePath isn't set +if (!basePath) { + basePath = window.location.protocol + "//" + window.location.host +} + + +/** + * Returns all the fights from the database + * + */ +export async function getFights() { + + const response = await axios.get(`${basePath}/api/fights`, + { + headers: defaultHeaders, + } + ) + + return response.data +} + +/** + * Creates a fight between two fighters + * + * @param body The two fighters fighting + */ +export async function startFight(body) { + + if (body === null || body === undefined) { + throw new Error('Required parameter body was null or undefined when calling startFight.') + } + + const response = await axios.post( + `${basePath}/api/fights`, body, { + crossDomain: true, + defaultHeaders, + } + ) + return response.data +} + +/** + * Returns two random fighters + */ +export async function getRandomFighters() { + + + // Explicitly catch errors and return undefined, since this API is used first + try { + const response = await axios.get(`${basePath}/api/fights/randomfighters`, + { + headers: defaultHeaders, + } + ) + return response.data + } catch (error) { + console.error(error) + } +} + +/** + * Generations a narration, given a pre-built fight + * + * @param body a fight + */ +export async function narrateFight(body) { + + if (body === null || body === undefined) { + throw new Error('Required parameter body was null or undefined when calling narrateFight.') + } + + const headers = {...defaultHeaders, 'Accept': 'text/plain'} + + const response = await axios.post(`${basePath}/api/fights/narrate`, + body, + { + responseType: 'text', + headers, + } + ) + + return response.data +} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api/fight-service.test.js b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api/fight-service.test.js new file mode 100644 index 000000000..5a2af30e1 --- /dev/null +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api/fight-service.test.js @@ -0,0 +1,182 @@ +import {getFights, getRandomFighters, narrateFight, startFight} from "./fight-service" +import axios from "axios" + +jest.mock('axios') + + +const fightersData = { + hero: { + name: 'Fallback hero', + level: 1, + picture: 'https://dummyimage.com/280x380/1e8fff/ffffff&text=Fallback+Hero', + powers: 'Fallback hero powers' + }, + villain: { + name: 'Fallback villain', + level: 42, + picture: 'https://dummyimage.com/280x380/b22222/ffffff&text=Fallback+Villain', + powers: 'Fallback villain powers' + } +} + +const fightersResponse = { + data: fightersData + , + status: 200, + statusText: 'OK', + headers: { + 'content-type': 'application/json;charset=UTF-8', + 'content-length': '314' + }, + config: { + transitional: { + silentJSONParsing: true, + forcedJSONParsing: true, + clarifyTimeoutError: false + }, + adapter: ['xhr', 'http'], + + headers: { + Accept: 'application/json, text/plain, */*', + 'Content-Type': null + }, + method: 'get', + url: 'http://localhost:8082/api/fights/randomfighters', + data: undefined + }, +} + + +const fightData = { + fightDate: "2023-10-24T21:34:47.617598Z", + id: 200, + loserLevel: 1, + loserName: "Mock hero", + loserPicture: "https://dummyimage.com/280x380/1e8fff/ffffff&text=Mock+Hero", + loserPowers: "Being fake", + loserTeam: "heroes", + winnerLevel: 42, + winnerName: "Mock villain", + winnerPicture: "https://dummyimage.com/280x380/b22222/ffffff&text=Mock+Villain", + winnerPowers: "Dissimulation", + winnerTeam: "villains" +} + +const fightResponse = { + data: fightData, + headers: {"content-length": "433", "content-type": "application/json;charset=UTF-8"} +} + +const fightsList = { + data: [fightData], + headers: {"content-length": "433", "content-type": "application/json;charset=UTF-8"} +} + +const narrationData = "It was a dark and stormy night" +const narrationResponse = { + data: narrationData, + headers: {"content-length": "433", "content-type": "text/plain"} +} + + +describe("the fight service", () => { + + describe("getting random fighters", () => { + + beforeEach(() => { + axios.get.mockResolvedValue(fightersResponse) + }) + + afterEach(() => { + jest.resetAllMocks() + }) + + it("invokes the remote api", async () => { + await getRandomFighters({}) + expect(axios.get).toHaveBeenCalled() + }) + + it("returns fighters", async () => { + const answer = await getRandomFighters({}) + expect(answer).toStrictEqual(fightersData) + }) + + describe("when back-end services are missing", () => { + beforeEach(() => { + axios.get.mockRejectedValue(new Error('Deliberate error: No Java services available')) + }) + + afterEach(() => { + jest.resetAllMocks() + }) + + it("gets new fighters", async () => { + const answer = await getRandomFighters({}) + expect(answer).toBeUndefined() + }) + }) + }) + + describe("triggering a fight", () => { + beforeEach(() => { + axios.post.mockResolvedValue(fightResponse) + }) + + afterEach(() => { + jest.resetAllMocks() + }) + + it("invokes the remote api", async () => { + const response = await startFight(fightersData) + expect(axios.post).toHaveBeenCalled() + expect(axios.post).toHaveBeenLastCalledWith(expect.anything(), fightersData, expect.anything()) + }) + + it("returns the data", async () => { + const answer = await startFight(fightersData) + expect(answer).toStrictEqual(fightData) + }) + }) + + describe("narration", () => { + + beforeEach(() => { + axios.post.mockResolvedValue(narrationResponse) + }) + + afterEach(() => { + jest.resetAllMocks() + }) + + it("invokes the remote api", async () => { + await narrateFight(fightData) + expect(axios.post).toHaveBeenCalled() + expect(axios.post).toHaveBeenLastCalledWith(expect.anything(), fightData, expect.anything()) + }) + + it("narrates the fight", async () => { + const answer = await narrateFight(fightData) + expect(answer).toStrictEqual(narrationData) + }) + }) + + describe("listing fights", () => { + beforeEach(() => { + axios.get.mockResolvedValue(fightsList) + }) + + afterEach(() => { + jest.resetAllMocks() + }) + + it("invokes the remote api", async () => { + const response = await getFights(fightersData) + expect(axios.get).toHaveBeenCalled() + }) + + it("returns the data", async () => { + const answer = await getFights(fightersData) + expect(answer).toStrictEqual([fightData]) + }) + }) +}) diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api/fight.service.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api/fight.service.ts deleted file mode 100644 index 03ea5ca21..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/api/fight.service.ts +++ /dev/null @@ -1,326 +0,0 @@ -/** - * Fight API - * This API allows a hero and a villain to fight - * - * OpenAPI spec version: 1.0 - * - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - *//* tslint:disable:no-unused-variable member-ordering */ - -import {EventEmitter, Inject, Injectable, Optional, Output} from '@angular/core'; -import {HttpClient, HttpEvent, HttpHeaders, HttpResponse} from '@angular/common/http'; - -import {Observable} from 'rxjs'; - -import {Fight} from '../model/fight'; -import {Fighters} from '../model/fighters'; -import {ModelLong} from '../model/modelLong'; -import {ModelString} from '../model/modelString'; -import {URI} from '../model/uRI'; - -import {BASE_PATH} from '../variables'; -import {Configuration} from '../configuration'; - -@Injectable() -export class FightService { - - protected basePath = (window as any).NG_CONFIG.API_BASE_URL; - protected calculateApiBaseUrl = (window as any).NG_CONFIG.CALCULATE_API_BASE_URL; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - - @Output() emitter = new EventEmitter(); - @Output() narrationEmitter = new EventEmitter(); - - constructor(protected httpClient: HttpClient, @Optional() @Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (basePath) { - this.basePath = basePath; - } - - if (configuration) { - this.configuration = configuration; - this.basePath = basePath || configuration.basePath || this.basePath; - } - - if (this.calculateApiBaseUrl) { - // If calculateApiBaseUrl then just replace "ui-super-heroes" with "rest-fights" in the current URL - this.basePath = window.location.protocol + "//" + window.location.host.replace('ui-super-heroes', 'rest-fights'); - } - - // Fallback to whatever is in the browser if basePath isn't set - if (!this.basePath) { - this.basePath = window.location.protocol + "//" + window.location.host; - } - } - - /** - * @param consumes string[] mime-types - * @return true: consumes contains 'multipart/form-data', false: otherwise - */ - private canConsumeForm(consumes: string[]): boolean { - const form = 'multipart/form-data'; - for (const consume of consumes) { - if (form === consume) { - return true; - } - } - return false; - } - - - /** - * Returns all the fights from the database - * - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - // tag::adocService[] - public apiFightsGet(observe?: 'body', reportProgress?: boolean): Observable>; - public apiFightsGet(observe?: 'response', reportProgress?: boolean): Observable>>; - public apiFightsGet(observe?: 'events', reportProgress?: boolean): Observable>>; - - // end::adocService[] - public apiFightsGet(observe: any = 'body', reportProgress: boolean = false): Observable { - - let headers = this.defaultHeaders; - - // to determine the Accept header - let httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); - if (httpHeaderAcceptSelected != undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - // to determine the Content-Type header - const consumes: string[] = []; - - return this.httpClient.get>(`${this.basePath}/api/fights`, - { - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * - * - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public apiFightsHelloGet(observe?: 'body', reportProgress?: boolean): Observable; - public apiFightsHelloGet(observe?: 'response', reportProgress?: boolean): Observable>; - public apiFightsHelloGet(observe?: 'events', reportProgress?: boolean): Observable>; - public apiFightsHelloGet(observe: any = 'body', reportProgress: boolean = false): Observable { - - let headers = this.defaultHeaders; - - // to determine the Accept header - let httpHeaderAccepts: string[] = [ - 'text/plain' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); - if (httpHeaderAcceptSelected != undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - // to determine the Content-Type header - const consumes: string[] = []; - - return this.httpClient.get(`${this.basePath}/api/fights/hello`, - { - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns a fight for a given identifier - * - * @param id Fight identifier - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public apiFightsIdGet(id: ModelLong, observe?: 'body', reportProgress?: boolean): Observable; - public apiFightsIdGet(id: ModelLong, observe?: 'response', reportProgress?: boolean): Observable>; - public apiFightsIdGet(id: ModelLong, observe?: 'events', reportProgress?: boolean): Observable>; - public apiFightsIdGet(id: ModelLong, observe: any = 'body', reportProgress: boolean = false): Observable { - - if (id === null || id === undefined) { - throw new Error('Required parameter id was null or undefined when calling apiFightsIdGet.'); - } - - let headers = this.defaultHeaders; - - // to determine the Accept header - let httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); - if (httpHeaderAcceptSelected != undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - // to determine the Content-Type header - const consumes: string[] = []; - - return this.httpClient.get(`${this.basePath}/api/fights/${encodeURIComponent(String(id))}`, - { - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - public onNewFight(fight: Fight) { - this.emitter.emit(fight); - } - - public onNewFightNarration(narration: string) { - this.narrationEmitter.emit(narration); - } - - /** - * Creates a fight between two fighters - * - * @param body The two fighters fighting - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public apiFightsPost(body: Fighters, observe?: 'body', reportProgress?: boolean): Observable; - public apiFightsPost(body: Fighters, observe?: 'response', reportProgress?: boolean): Observable>; - public apiFightsPost(body: Fighters, observe?: 'events', reportProgress?: boolean): Observable>; - public apiFightsPost(body: Fighters, observe: any = 'body', reportProgress: boolean = false): Observable { - - if (body === null || body === undefined) { - throw new Error('Required parameter body was null or undefined when calling apiFightsPost.'); - } - - let headers = this.defaultHeaders; - - // to determine the Accept header - let httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); - if (httpHeaderAcceptSelected != undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected != undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - return this.httpClient.post(`${this.basePath}/api/fights`, - body, - { - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Returns two random fighters - * - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - // tag::adocService[] - public apiFightsRandomfightersGet(observe?: 'body', reportProgress?: boolean): Observable; - public apiFightsRandomfightersGet(observe?: 'response', reportProgress?: boolean): Observable>; - public apiFightsRandomfightersGet(observe?: 'events', reportProgress?: boolean): Observable>; - // end::adocService[] - public apiFightsRandomfightersGet(observe: any = 'body', reportProgress: boolean = false): Observable { - - let headers = this.defaultHeaders; - - // to determine the Accept header - let httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); - if (httpHeaderAcceptSelected != undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - // to determine the Content-Type header - const consumes: string[] = []; - - return this.httpClient.get(`${this.basePath}/api/fights/randomfighters`, - { - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Creates a fight between two fighters - * - * @param body The two fighters fighting - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public apiNarrateFightPost(body: Fight, observe?: 'body', reportProgress?: boolean): Observable; - public apiNarrateFightPost(body: Fight, observe?: 'response', reportProgress?: boolean): Observable>; - public apiNarrateFightPost(body: Fight, observe?: 'events', reportProgress?: boolean): Observable>; - public apiNarrateFightPost(body: Fight, observe: any = 'body', reportProgress: boolean = false): Observable { - - if (body === null || body === undefined) { - throw new Error('Required parameter body was null or undefined when calling apiNarrateFightPost.'); - } - - let headers = this.defaultHeaders; - - // to determine the Accept header - let httpHeaderAccepts: string[] = [ - 'text/plain' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); - if (httpHeaderAcceptSelected != undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected != undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - return this.httpClient.post(`${this.basePath}/api/fights/narrate`, - body, - { - responseType: 'text' as const, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress, - } - ); - } -} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/configuration.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/configuration.ts deleted file mode 100644 index 82e8458f3..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/configuration.ts +++ /dev/null @@ -1,79 +0,0 @@ -export interface ConfigurationParameters { - apiKeys?: {[ key: string ]: string}; - username?: string; - password?: string; - accessToken?: string | (() => string); - basePath?: string; - withCredentials?: boolean; -} - -export class Configuration { - apiKeys?: {[ key: string ]: string}; - username?: string; - password?: string; - accessToken?: string | (() => string); - basePath?: string; - withCredentials?: boolean; - - constructor(configurationParameters: ConfigurationParameters = {}) { - this.apiKeys = configurationParameters.apiKeys; - this.username = configurationParameters.username; - this.password = configurationParameters.password; - this.accessToken = configurationParameters.accessToken; - this.basePath = configurationParameters.basePath; - this.withCredentials = configurationParameters.withCredentials; - } - - /** - * Select the correct content-type to use for a request. - * Uses {@link Configuration#isJsonMime} to determine the correct content-type. - * If no content type is found return the first found type if the contentTypes is not empty - * @param contentTypes - the array of content types that are available for selection - * @returns the selected content-type or undefined if no selection could be made. - */ - public selectHeaderContentType (contentTypes: string[]): string | undefined { - if (contentTypes.length == 0) { - return undefined; - } - - let type = contentTypes.find(x => this.isJsonMime(x)); - if (type === undefined) { - return contentTypes[0]; - } - return type; - } - - /** - * Select the correct accept content-type to use for a request. - * Uses {@link Configuration#isJsonMime} to determine the correct accept content-type. - * If no content type is found return the first found type if the contentTypes is not empty - * @param accepts - the array of content types that are available for selection. - * @returns the selected content-type or undefined if no selection could be made. - */ - public selectHeaderAccept(accepts: string[]): string | undefined { - if (accepts.length == 0) { - return undefined; - } - - let type = accepts.find(x => this.isJsonMime(x)); - if (type === undefined) { - return accepts[0]; - } - return type; - } - - /** - * Check if the given MIME is a JSON MIME. - * JSON MIME examples: - * application/json - * application/json; charset=UTF8 - * APPLICATION/JSON - * application/vnd.company+json - * @param mime - MIME (Multipurpose Internet Mail Extensions) - * @return True if the given MIME is JSON, false otherwise. - */ - public isJsonMime(mime: string): boolean { - const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i'); - return mime != null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json'); - } -} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/encoder.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/encoder.ts deleted file mode 100644 index f1c6b78c9..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/encoder.ts +++ /dev/null @@ -1,18 +0,0 @@ - import { HttpUrlEncodingCodec } from '@angular/common/http'; - -/** -* CustomHttpUrlEncodingCodec -* Fix plus sign (+) not encoding, so sent as blank space -* See: https://github.com/angular/angular/issues/11058#issuecomment-247367318 -*/ -export class CustomHttpUrlEncodingCodec extends HttpUrlEncodingCodec { - encodeKey(k: string): string { - k = super.encodeKey(k); - return k.replace(/\+/gi, '%2B'); - } - encodeValue(v: string): string { - v = super.encodeValue(v); - return v.replace(/\+/gi, '%2B'); - } -} - diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/index.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/index.ts deleted file mode 100644 index c312b70fa..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './api/api'; -export * from './model/models'; -export * from './variables'; -export * from './configuration'; -export * from './api.module'; \ No newline at end of file diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/fight.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/fight.ts deleted file mode 100644 index 4d8395e33..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/fight.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Fight API - * This API allows a hero and a villain to fight - * - * OpenAPI spec version: 1.0 - * - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */import { FightFightDate } from './fightFightDate'; - - -/** - * Each fight has a winner and a loser - */ -export interface Fight { - id?: number; - fightDate: FightFightDate; - winnerName: string; - winnerLevel: number; - winnerPowers: string; - winnerPicture: string; - loserName: string; - loserLevel: number; - loserPowers: string; - loserPicture: string; -} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/fightFightDate.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/fightFightDate.ts deleted file mode 100644 index 5e676017b..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/fightFightDate.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Fight API - * This API allows a hero and a villain to fight - * - * OpenAPI spec version: 1.0 - * - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -export interface FightFightDate { - nanos?: number; - seconds?: number; - epochSecond?: number; - nano?: number; -} \ No newline at end of file diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/fighters.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/fighters.ts deleted file mode 100644 index b56635067..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/fighters.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Fight API - * This API allows a hero and a villain to fight - * - * OpenAPI spec version: 1.0 - * - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ -import { Hero } from './hero'; -import { Villain } from './villain'; - - -/** - * A fight between one hero and one villain - */ -export class Fighters { - constructor( - public hero?: Hero, - public villain?: Villain, - ) { - } -} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/hero.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/hero.ts deleted file mode 100644 index 46e24d06b..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/hero.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Fight API - * This API allows a hero and a villain to fight - * - * OpenAPI spec version: 1.0 - * - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/** - * The hero fighting against the villain - */ -export class Hero { - constructor( - public name?: string, - public picture?: string, - public powers?: string, - public level?: bigint -) { -} -} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/modelLong.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/modelLong.ts deleted file mode 100644 index 28ee4ca82..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/modelLong.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Fight API - * This API allows a hero and a villain to fight - * - * OpenAPI spec version: 1.0 - * - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -export type ModelLong = number; \ No newline at end of file diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/modelString.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/modelString.ts deleted file mode 100644 index ef0e5dfd9..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/modelString.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Fight API - * This API allows a hero and a villain to fight - * - * OpenAPI spec version: 1.0 - * - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -export type ModelString = string; \ No newline at end of file diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/models.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/models.ts deleted file mode 100644 index 8db0db6f2..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/models.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * from './fight'; -export * from './fightFightDate'; -export * from './fighters'; -export * from './hero'; -export * from './villain'; -export * from './modelLong'; -export * from './modelString'; -export * from './uRI'; diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/uRI.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/uRI.ts deleted file mode 100644 index f6d244156..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/uRI.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Fight API - * This API allows a hero and a villain to fight - * - * OpenAPI spec version: 1.0 - * - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -export interface URI { - string?: string; - rawAuthority?: string; - rawFragment?: string; - rawPath?: string; - rawQuery?: string; - rawSchemeSpecificPart?: string; - rawUserInfo?: string; - absolute?: boolean; - opaque?: boolean; -} \ No newline at end of file diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/villain.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/villain.ts deleted file mode 100644 index 70db242b3..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/model/villain.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Fight API - * This API allows a hero and a villain to fight - * - * OpenAPI spec version: 1.0 - * - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/** - * The villain fighting against the hero - */ -export class Villain { - constructor( - public name?: string, - public picture?: string, - public powers?: string, - public level?: bigint - ) { - } -} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/variables.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/variables.ts deleted file mode 100644 index 6fe58549f..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/app/shared/variables.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { InjectionToken } from '@angular/core'; - -export const BASE_PATH = new InjectionToken('basePath'); -export const COLLECTION_FORMATS = { - 'csv': ',', - 'tsv': ' ', - 'ssv': ' ', - 'pipes': '|' -} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/env.d.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/env.d.ts deleted file mode 100644 index ff164de78..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/env.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -declare var process: { - env: { - // Provides auto complete when typing process.env.YOUR_VAR_NAME. These - // are always string type, since they're read from env - NG_APP_ENV: string; - // Replace the line below with your environment variable for better type checking - [key: string]: any; - }; -}; diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/environments/environment.prod.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/environments/environment.prod.ts deleted file mode 100644 index 3612073bc..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/environments/environment.prod.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const environment = { - production: true -}; diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/environments/environment.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/environments/environment.ts deleted file mode 100644 index 7b4f817ad..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/environments/environment.ts +++ /dev/null @@ -1,16 +0,0 @@ -// This file can be replaced during build by using the `fileReplacements` array. -// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. -// The list of file replacements can be found in `angular.json`. - -export const environment = { - production: false -}; - -/* - * For easier debugging in development mode, you can import the following file - * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. - * - * This import should be commented out in production mode because it will have a negative impact - * on performance if an error is thrown. - */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/index.html b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/index.html deleted file mode 100644 index 0b7efd1e7..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - SuperHeroes - - - - - - - - - - - - - -
- -
- - - - - - diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/index.js b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/index.js new file mode 100644 index 000000000..6661a2717 --- /dev/null +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/index.js @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import './styles.css'; +import App from './app/App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/main.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/main.ts deleted file mode 100644 index 2e16606de..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/main.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {enableProdMode} from '@angular/core'; -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; - -import {AppModule} from './app/app.module'; -import {environment} from './environments/environment'; - -if (environment.production) { - enableProdMode(); -} - -console.log('Initialising with NG_APP_ENV: ', process.env.NG_APP_ENV) - -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/polyfills.ts b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/polyfills.ts deleted file mode 100644 index aa665d6b8..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/polyfills.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * This file includes polyfills needed by Angular and is loaded before the app. - * You can add your own extra polyfills to this file. - * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * - * The current setup is for so-called "evergreen" browsers; the last versions of browsers that - * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), - * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. - * - * Learn more in https://angular.io/guide/browser-support - */ - -/*************************************************************************************************** - * BROWSER POLYFILLS - */ - -/** IE10 and IE11 requires the following for NgClass support on SVG elements */ -// import 'classlist.js'; // Run `npm install --save classlist.js`. - -/** - * Web Animations `@angular/platform-browser/animations` - * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. - * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). - */ -// import 'web-animations-js'; // Run `npm install --save web-animations-js`. - -/** - * By default, zone.js will patch all possible macroTask and DomEvents - * user can disable parts of macroTask/DomEvents patch by setting following flags - * because those flags need to be set before `zone.js` being loaded, and webpack - * will put import in the top of bundle, so user need to create a separate file - * in this directory (for example: zone-flags.ts), and put the following flags - * into that file, and then add the following code before importing zone.js. - * import './zone-flags.ts'; - * - * The flags allowed in zone-flags.ts are listed here. - * - * The following flags will work for all browsers. - * - * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame - * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick - * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames - * - * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js - * with the following flag, it will bypass `zone.js` patch for IE/Edge - * - * (window as any).__Zone_enable_cross_context_check = true; - * - */ - -/*************************************************************************************************** - * Zone JS is required by default for Angular itself. - */ -import 'zone.js/dist/zone'; // Included with Angular CLI. - - -/*************************************************************************************************** - * APPLICATION IMPORTS - */ diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/styles.css b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/styles.css index b2e2061de..d99f70446 100644 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/styles.css +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/src/styles.css @@ -1,16 +1,59 @@ /* You can add global styles to this file, and also import other style files */ -@import '@angular/material/prebuilt-themes/deeppurple-amber.css'; +html, body { + height: 100%; +} + +body { + margin: 0; +} + +#root { + display: flex; + flex-direction: column; + align-items: center; +} -html, body { height: 100%; } -body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } +h1 { + text-align: center; + margin-left: 0px; + margin-right: 0px; +} .hero-card { +} + +.card-pf { max-width: 400px; + min-width: 300px; + margin-left: 3rem; + margin-right: 3rem; + display: flex; + flex-direction: column; + align-items: center; +} + +.card-pf-body { + display: flex; + flex-direction: column; + align-items: center; } -table { - width: 100%; +.btn-block { + padding-left: 4rem; + padding-right: 4rem; +} + +.table { + margin-left: 6rem; + margin-right: 6rem; + width: 80%; + border-spacing: 0; + box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12); +} + +td { + padding-left: 24px !important; } .rounded { @@ -33,12 +76,14 @@ table { padding: 20px; background: dodgerblue; color: white; + text-align: center; } .villain-name { padding: 20px; background: firebrick; color: white; + text-align: center; } .winner-text { @@ -55,27 +100,31 @@ table { } .hero-winner-card { - box-shadow: - inset 0 0 10px #fff, - inset 20px 0 50px #77b4f0, - inset -20px 0 60px #5fc3d7, - inset 20px 0 100px #4a54ff, - inset -20px 0 100px #0ff, - 0 0 50px #fff, - -10px 0 50px #4a54ff, - 10px 0 50px #0ff; + box-shadow: inset 0 0 10px #fff, + inset 20px 0 50px #77b4f0, + inset -20px 0 60px #5fc3d7, + inset 20px 0 100px #4a54ff, + inset -20px 0 100px #0ff, + 0 0 50px #fff, + -10px 0 50px #4a54ff, + 10px 0 50px #0ff; } .villain-winner-card { - box-shadow: - inset 0 0 10px #fff, - inset 20px 0 50px #F078C2, - inset -20px 0 60px #D73B46, - inset 20px 0 100px #FF5B68, - inset -20px 0 100px #FF5A71, - 0 0 50px #fff, - -10px 0 50px #FF5B68, - 10px 0 50px #FF5B68; + box-shadow: inset 0 0 10px #fff, + inset 20px 0 50px #F078C2, + inset -20px 0 60px #D73B46, + inset 20px 0 100px #FF5B68, + inset -20px 0 100px #FF5A71, + 0 0 50px #fff, + -10px 0 50px #FF5B68, + 10px 0 50px #FF5B68; +} + +.row { + display: flex; + flex-direction: row; + justify-content: center; } #fight-row { @@ -83,16 +132,13 @@ table { } .fight-list-header { - text-align: center !important; + text-align: center; vertical-align: middle !important; background: darkgray; color: white; font-size: medium; font-weight: bold; -} - -.mat-cell { - vertical-align: middle !important; + height: 56px; } .powers { diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/tsconfig.app.json b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/tsconfig.app.json deleted file mode 100644 index 55785a3d7..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/tsconfig.app.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "./out-tsc/app", - "types": [] - }, - "files": [ - "src/main.ts", - "src/polyfills.ts" - ], - "include": [ - "src/**/*.ts" - ], - "exclude": [ - "src/test.ts", - "src/**/*.spec.ts" - ] -} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/tsconfig.json b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/tsconfig.json deleted file mode 100644 index 30956ae7e..000000000 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/main/webui/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compileOnSave": false, - "compilerOptions": { - "baseUrl": "./", - "outDir": "./dist/out-tsc", - "sourceMap": true, - "declaration": false, - "downlevelIteration": true, - "experimentalDecorators": true, - "module": "esnext", - "moduleResolution": "node", - "importHelpers": true, - "target": "es2015", - "typeRoots": [ - "node_modules/@types" - ], - "lib": [ - "es2018", - "dom" - ] - }, - "angularCompilerOptions": { - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true - } -} diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/test/java/io/quarkus/workshop/superheroes/ui/EnvResourceTests.java b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/test/java/io/quarkus/workshop/superheroes/ui/EnvResourceTests.java index 85c75bcb3..f5e496f9d 100644 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/test/java/io/quarkus/workshop/superheroes/ui/EnvResourceTests.java +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/test/java/io/quarkus/workshop/superheroes/ui/EnvResourceTests.java @@ -14,7 +14,7 @@ public class EnvResourceTests { /** - * Checks that the angular app would be able to do + * Checks that the react app would be able to do * * and get something sensible back */ @@ -24,7 +24,7 @@ public void javascriptEndpoint() { .then() .statusCode(OK.getStatusCode()) .body( - is("window.NG_CONFIG={\"API_BASE_URL\":\"http://localhost:8082\"," + + is("window.APP_CONFIG={\"API_BASE_URL\":\"http://localhost:8082\"," + "\"CALCULATE_API_BASE_URL\":false}")); // This content is javascript, not json. Doing a simple equality check like this // is brittle, but we can update it to something more flexible if we start to see issues diff --git a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/test/java/io/quarkus/workshop/superheroes/ui/WebUITests.java b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/test/java/io/quarkus/workshop/superheroes/ui/WebUITests.java index 62b13fa5b..2312f8c5d 100644 --- a/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/test/java/io/quarkus/workshop/superheroes/ui/WebUITests.java +++ b/quarkus-workshop-super-heroes/super-heroes/ui-super-heroes/src/test/java/io/quarkus/workshop/superheroes/ui/WebUITests.java @@ -28,7 +28,7 @@ public void webApplicationEndpoint() { .then() .statusCode(OK.getStatusCode()) .contentType(HTML) - .body(matchesPattern(Pattern.compile(".*.*", Pattern.DOTALL))); + .body(matchesPattern(Pattern.compile(".*
.*", Pattern.DOTALL))); // We don't want to do full HTML parsing here, because that should // be in the javascript tests. Instead, just confirm that we // are successfully serving the HTML content on /