diff --git a/.env b/.env index d2f8d852f..131b17e4f 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -teamname="asw2324_0" \ No newline at end of file +teamname="wiq_0" \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 86d2429e0..000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,62 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "github-actions" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "monthly" - # Maintain the npm dependencies of the frontend - - package-ecosystem: "docker" # See documentation for possible values - directory: "/webapp" # Location of package manifests - schedule: - interval: "weekly" - ignore: # Ignore the non-LTS node versions (17/01/2024 >20) - - dependency-name: "node" - versions: ["21.x","22.x","23.x","24.x"] - - package-ecosystem: "npm" # See documentation for possible values - directory: "/webapp" # Location of package manifests - schedule: - interval: "weekly" - ignore: # Avoid puppeteer+jest version updates to avoid incompatibilities/problems in CI-CD - - dependency-name: "puppeteer*" - - dependency-name: "jest*" - # Maintain the npm dependencies of the GatewayService - - package-ecosystem: "docker" # See documentation for possible values - directory: "/gatewayservice" # Location of package manifests - schedule: - interval: "weekly" - ignore: # Ignore the non-LTS node versions (17/01/2024 >20) - - dependency-name: "node" - versions: ["21.x","22.x","23.x","24.x"] - - package-ecosystem: "npm" # See documentation for possible values - directory: "/gatewayservice" # Location of package manifests - schedule: - interval: "weekly" - # Maintain the npm dependencies of the authservice - - package-ecosystem: "docker" # See documentation for possible values - directory: "/users/authservice" # Location of package manifests - schedule: - interval: "weekly" - ignore: # Ignore the non-LTS node versions (17/01/2024 >20) - - dependency-name: "node" - versions: ["21.x","22.x","23.x","24.x"] - - package-ecosystem: "npm" # See documentation for possible values - directory: "/users/authservice" # Location of package manifests - schedule: - interval: "weekly" - # Maintain the npm dependencies of the userservice - - package-ecosystem: "docker" # See documentation for possible values - directory: "/users/userservice" # Location of package manifests - schedule: - interval: "weekly" - ignore: # Ignore the non-LTS node versions (17/01/2024 >20) - - dependency-name: "node" - versions: ["21.x","22.x","23.x","24.x"] - - package-ecosystem: "npm" # See documentation for possible values - directory: "/users/userservice" # Location of package manifests - schedule: - interval: "weekly" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..c92657031 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,30 @@ +name: Build +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] +jobs: + unit-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - run: npm --prefix users/authservice ci + - run: npm --prefix users/userservice ci + - run: npm --prefix gatewayservice ci + - run: npm --prefix webapp ci + - run: npm --prefix users/authservice test -- --coverage + - run: npm --prefix users/userservice test -- --coverage + - run: npm --prefix gatewayservice test -- --coverage + - run: npm --prefix webapp test -- --coverage + - name: Analyze with SonarCloud + uses: sonarsource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/asw2324.yml b/.github/workflows/release.yml similarity index 84% rename from .github/workflows/asw2324.yml rename to .github/workflows/release.yml index 41a4bad79..48a92d372 100644 --- a/.github/workflows/asw2324.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: CI for ASW2324 +name: Deploy on release on: release: @@ -53,7 +53,7 @@ jobs: env: API_URI: http://${{ secrets.DEPLOY_HOST }}:8000 with: - name: pglez82/asw2324_0/webapp + name: arquisoft/wiq_0/webapp username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io @@ -71,7 +71,7 @@ jobs: - name: Publish to Registry uses: elgohr/Publish-Docker-Github-Action@v5 with: - name: pglez82/asw2324_0/authservice + name: arquisoft/wiq_0/authservice username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io @@ -88,7 +88,7 @@ jobs: - name: Publish to Registry uses: elgohr/Publish-Docker-Github-Action@v5 with: - name: pglez82/asw2324_0/userservice + name: arquisoft/wiq_0/userservice username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io @@ -102,10 +102,14 @@ jobs: needs: [e2e-tests] steps: - uses: actions/checkout@v4 + - name: Update OpenAPI configuration + run: | + DEPLOY_HOST=${{ secrets.DEPLOY_HOST }} + sed -i "s/SOMEIP/${DEPLOY_HOST}/g" gatewayservice/openapi.yaml - name: Publish to Registry uses: elgohr/Publish-Docker-Github-Action@v5 with: - name: pglez82/asw2324_0/gatewayservice + name: arquisoft/wiq_0/gatewayservice username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io @@ -122,7 +126,7 @@ jobs: user: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_KEY }} command: | - wget https://raw.githubusercontent.com/pglez82/asw2324_0/master/docker-compose.yml - wget https://raw.githubusercontent.com/pglez82/asw2324_0/master/docker-compose-deploy.override.yml - docker compose down - docker compose -f docker-compose.yml -f docker-compose-deploy.override.yml up -d + wget https://raw.githubusercontent.com/arquisoft/wiq_0/master/docker-compose.yml -O docker-compose.yml + wget https://raw.githubusercontent.com/arquisoft/wiq_0/master/.env -O .env + docker compose --profile prod down + docker compose --profile prod up -d --pull always diff --git a/README.md b/README.md index acafdad8d..e78fdd6cf 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,16 @@ -# asw2324_0 +# wiq_0 -[![Actions Status](https://github.com/pglez82/asw2324_0/workflows/CI%20for%20ASW2324/badge.svg)](https://github.com/pglez82/asw2324_0/actions) -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=pglez82_asw2324_0&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=pglez82_asw2324_0) -[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=pglez82_asw2324_0&metric=coverage)](https://sonarcloud.io/summary/new_code?id=pglez82_asw2324_0) +[![Deploy on release](https://github.com/Arquisoft/wiq_0/actions/workflows/release.yml/badge.svg)](https://github.com/Arquisoft/wiq_0/actions/workflows/release.yml) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Arquisoft_wiq_0&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=Arquisoft_wiq_0) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Arquisoft_wiq_0&metric=coverage)](https://sonarcloud.io/summary/new_code?id=Arquisoft_wiq_0) -

- - -

+This is a base repo for the [Software Architecture course](http://arquisoft.github.io/) in [2023/2024 edition](https://arquisoft.github.io/course2324.html). -This is a base project for the Software Architecture course in 2023/2024. It is a basic application composed of several components. +This repo is a basic application composed of several components. +- **Gateway service**. Express service that is exposed to the public and serves as a proxy to the two previous ones. - **User service**. Express service that handles the insertion of new users in the system. - **Auth service**. Express service that handles the authentication of users. -- **Gateway service**. Express service that is exposed to the public and serves as a proxy to the two previous ones. - **Webapp**. React web application that uses the gateway service to allow basic login and new user features. Both the user and auth service share a Mongo database that is accessed with mongoose. @@ -23,27 +20,49 @@ Both the user and auth service share a Mongo database that is accessed with mong ### Using docker The fastest way for launching this sample project is using docker. Just clone the project: -```git clone git@github.com:pglez82/asw2324_0.git``` + +```sh +git clone https://github.com/Arquisoft/wiq_0.git +``` + and launch it with docker compose: -```docker compose -f docker-compose.yml -f docker-compose-dev.override.yml up --build``` -### Component by component start +```sh +docker compose --profile dev up --build +``` + +and tear it down: + +```sh +docker compose --profile dev down +``` + +### Starting Component by component + First, start the database. Either install and run Mongo or run it using docker: ```docker run -d -p 27017:27017 --name=my-mongo mongo:latest``` -You can use also services like Mongo Altas for running a Mongo database in the cloud. +You can also use services like Mongo Altas for running a Mongo database in the cloud. -Now launch the auth, user and gateway services. Just go to each directory and run `npm install` followed by `npm start`. +Now, launch the auth, user and gateway services. Just go to each directory and run `npm install` followed by `npm start`. Lastly, go to the webapp directory and launch this component with `npm install` followed by `npm start`. After all the components are launched, the app should be available in localhost in port 3000. ## Deployment -For the deployment, we have several options. The first and more flexible is to deploy to a virtual machine using SSH. This will work with any cloud service (or with our own server). Other options include using the container services that all the cloud services provide. This means, deploying our Docker containers directly. Here I am going to use the first approach. I am going to create a virtual machine in a cloud service and after installing docker and docker-compose, deploy our containers there using GitHub Actions and SSH. + +For the deployment, we have several options. + +The first and more flexible is to deploy to a virtual machine using SSH. This will work with any cloud service (or with our own server). + +Other options include using the container services that most cloud services provide. This means, deploying our Docker containers directly. + +We are going to use the first approach, creating a virtual machine in a cloud service and after installing docker and docker-compose, deploy our containers there using GitHub Actions and SSH. ### Machine requirements for deployment + The machine for deployment can be created in services like Microsoft Azure or Amazon AWS. These are in general the settings that it must have: - Linux machine with Ubuntu > 20.04. @@ -65,9 +84,10 @@ sudo chmod +x /usr/local/bin/docker-compose ``` ### Continuous delivery (GitHub Actions) -Once we have our machine ready, we could deploy by hand the application, taking our docker-compose file and executing it in the remote machine. In this repository, this process is done automatically using **GitHub Actions**. The idea is to trigger a series of actions when some condition is met in the repository. The precondition to trigger a deployment is going to be: "create a new release". The actions to execute are the following: -![imagen](https://github.com/pglez82/asw2324_0/assets/10683040/44d0fe21-a93f-4b01-b458-ab7af4267fa3) +Once we have our machine ready, we could deploy by hand the application, taking our docker-compose file and executing it in the remote machine. + +In this repository, this process is done automatically using **GitHub Actions**. The idea is to trigger a series of actions when some condition is met in the repository. As you can see, unitary tests of each module and e2e tests are executed before pushing the docker images and deploying them. Using this approach we avoid deploying versions that do not pass the tests. @@ -86,9 +106,10 @@ deploy: user: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_KEY }} command: | - wget https://raw.githubusercontent.com/pglez82/asw2324_0/master/docker-compose-deploy.yml -O docker-compose.yml - docker compose down --volumes - docker compose -f docker-compose.yml -f docker-compose-deploy.override.yml up -d + wget https://raw.githubusercontent.com/arquisoft/wiq_0/master/docker-compose.yml -O docker-compose.yml + wget https://raw.githubusercontent.com/arquisoft/wiq_0/master/.env -O .env + docker compose --profile prod down + docker compose --profile prod up -d --pull always ``` This action uses three secrets that must be configured in the repository: @@ -96,4 +117,5 @@ This action uses three secrets that must be configured in the repository: - DEPLOY_USER: user with permission to execute the commands in the remote machine. - DEPLOY_KEY: key to authenticate the user in the remote machine. -Note that this action logs in the remote machine and downloads the docker-compose file from the repository and launches it. Obviously, previous actions have been executed which have uploaded the docker images to the GitHub Packages repository. +Note that this action logs in the remote machine and downloads the docker-compose file from the repository and launches it. +Obviously, previous actions have been executed which have uploaded the docker images to the GitHub Packages repository. diff --git a/docker-compose-deploy.override.yml b/docker-compose-deploy.override.yml deleted file mode 100644 index 0dab27448..000000000 --- a/docker-compose-deploy.override.yml +++ /dev/null @@ -1,41 +0,0 @@ -version: '3' -services: - mongodb: - ports: - - "27017:27017" - networks: - - mynetwork - - authservice: - ports: - - "8002:8002" - networks: - - mynetwork - environment: - MONGODB_URI: mongodb://mongodb:27017/userdb - - userservice: - ports: - - "8001:8001" - networks: - - mynetwork - environment: - MONGODB_URI: mongodb://mongodb:27017/userdb - - gatewayservice: - ports: - - "8000:8000" - networks: - - mynetwork - environment: - AUTH_SERVICE_URL: http://authservice:8002 - USER_SERVICE_URL: http://userservice:8001 - - webapp: - ports: - - "3000:3000" - -networks: - mynetwork: - driver: bridge - diff --git a/docker-compose-dev.override.yml b/docker-compose-dev.override.yml deleted file mode 100644 index 4df99e0cb..000000000 --- a/docker-compose-dev.override.yml +++ /dev/null @@ -1,81 +0,0 @@ -version: '3' -services: - mongodb: - image: mongo - ports: - - "27017:27017" - networks: - - mynetwork - volumes: - - mongodb_data:/data/db - - authservice: - build: ./users/authservice - ports: - - "8002:8002" - networks: - - mynetwork - environment: - MONGODB_URI: mongodb://mongodb:27017/userdb - - userservice: - build: ./users/userservice - ports: - - "8001:8001" - networks: - - mynetwork - environment: - MONGODB_URI: mongodb://mongodb:27017/userdb - - gatewayservice: - build: ./gatewayservice - ports: - - "8000:8000" - networks: - - mynetwork - environment: - AUTH_SERVICE_URL: http://authservice:8002 - USER_SERVICE_URL: http://userservice:8001 - - webapp: - build: ./webapp - ports: - - "3000:3000" - - prometheus: - image: prom/prometheus - networks: - - mynetwork - volumes: - - ./gatewayservice/monitoring/prometheus:/etc/prometheus - - prometheus_data:/prometheus - ports: - - "9090:9090" - depends_on: - - gatewayservice - - grafana: - image: grafana/grafana - networks: - - mynetwork - volumes: - - grafana_data:/var/lib/grafana - - ./gatewayservice/monitoring/grafana/provisioning:/etc/grafana/provisioning - environment: - - GF_SERVER_HTTP_PORT=9091 - - GF_AUTH_DISABLE_LOGIN_FORM=true - - GF_AUTH_ANONYMOUS_ENABLED=true - - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin - ports: - - "9091:9091" - depends_on: - - prometheus - -networks: - mynetwork: - driver: bridge - -volumes: - mongodb_data: - prometheus_data: - grafana_data: diff --git a/docker-compose.yml b/docker-compose.yml index eddc74328..c105ed506 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,38 +3,108 @@ services: mongodb: container_name: mongodb-${teamname:-defaultASW} image: mongo + profiles: ["dev", "prod"] volumes: - mongodb_data:/data/db + ports: + - "27017:27017" + networks: + - mynetwork authservice: container_name: authservice-${teamname:-defaultASW} - image: ghcr.io/pglez82/asw2324_0/authservice:latest + image: ghcr.io/arquisoft/wiq_0/authservice:latest + profiles: ["dev", "prod"] build: ./users/authservice depends_on: - mongodb + ports: + - "8002:8002" + networks: + - mynetwork + environment: + MONGODB_URI: mongodb://mongodb:27017/userdb userservice: container_name: userservice-${teamname:-defaultASW} - image: ghcr.io/pglez82/asw2324_0/userservice:latest + image: ghcr.io/arquisoft/wiq_0/userservice:latest + profiles: ["dev", "prod"] build: ./users/userservice depends_on: - mongodb + ports: + - "8001:8001" + networks: + - mynetwork + environment: + MONGODB_URI: mongodb://mongodb:27017/userdb gatewayservice: container_name: gatewayservice-${teamname:-defaultASW} - image: ghcr.io/pglez82/asw2324_0/gatewayservice:latest + image: ghcr.io/arquisoft/wiq_0/gatewayservice:latest + profiles: ["dev", "prod"] build: ./gatewayservice depends_on: - mongodb - userservice - authservice + ports: + - "8000:8000" + networks: + - mynetwork + environment: + AUTH_SERVICE_URL: http://authservice:8002 + USER_SERVICE_URL: http://userservice:8001 webapp: container_name: webapp-${teamname:-defaultASW} - image: ghcr.io/pglez82/asw2324_0/webapp:latest + image: ghcr.io/arquisoft/wiq_0/webapp:latest + profiles: ["dev", "prod"] build: ./webapp depends_on: - - gatewayservice + - gatewayservice + ports: + - "3000:3000" + + prometheus: + image: prom/prometheus + container_name: prometheus-${teamname:-defaultASW} + profiles: ["dev"] + networks: + - mynetwork + volumes: + - ./gatewayservice/monitoring/prometheus:/etc/prometheus + - prometheus_data:/prometheus + ports: + - "9090:9090" + depends_on: + - gatewayservice + + grafana: + image: grafana/grafana + container_name: grafana-${teamname:-defaultASW} + profiles: ["dev"] + networks: + - mynetwork + volumes: + - grafana_data:/var/lib/grafana + - ./gatewayservice/monitoring/grafana/provisioning:/etc/grafana/provisioning + environment: + - GF_SERVER_HTTP_PORT=9091 + - GF_AUTH_DISABLE_LOGIN_FORM=true + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + ports: + - "9091:9091" + depends_on: + - prometheus + volumes: mongodb_data: + prometheus_data: + grafana_data: + +networks: + mynetwork: + driver: bridge diff --git a/docs/README.md b/docs/README.md index 5c9cc11b9..61766e49b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,12 +1,12 @@ ## The documentation In this project, the documentation is compiled locally and deployed to GitHub pages. -The deployment url is: [https://pglez82.github.io/asw2324_0/](https://pglez82.github.io/asw2324_0/). +The deployment url is: [https://arquisoft.github.io/wiq_0/](https://arquisoft.github.io/wiq_0/). ### Documentation build For the documentation, we are going to use [AsciiDoc](https://asciidoc.org/) and [PlantUML](https://plantuml.com) and follow the [Arc42](https://github.com/arc42/arc42-template) template. If you want to be able to generate the doc locally you need to install Ruby, Java and some dependencies to translate the AsciiDoc code into html. If you are in Linux you can install Ruby and Java simply by executing: ```shell -apt-get install ruby openjdk-8-jre +apt-get install ruby default-jre ``` On Windows, you can use [these instructions](https://www.ruby-lang.org/en/documentation/installation). Probably you will have Java already installed in your system, if not, you can download it [here](https://www.oracle.com/es/java/technologies/javase/javase8-archive-downloads.html) @@ -30,6 +30,6 @@ npm run build The documentation will be generated under the `docs/build` directory. ### Documentation deployment -If we want to deploy it to GitHub pages, so it is accessible via [https://pglez82.github.io/asw2324_0/](https://pglez82.github.io/asw2324_0/), we need to execute `npm run deploy`. +If we want to deploy it to GitHub pages, so it is accessible via [https://arquisoft.github.io/wiq_0/](https://arquisoft.github.io/wiq_0/), we need to execute `npm run deploy`. If you check the `package.json` in this directory you can see how deploying is as easy as executing `gh-pages -d build`, which can be directly executed using `npm run deploy` in the docs directory. The `gh-pages` package is in charge of pushing the documentation generated directory (basically some htmls) to a special github branch called gh-pages. Everything pushed to this branch is accessible on the repository page. Note that we only want to push there the documentation. Also is important that the documentation build is not pushed to the other branches of the project. \ No newline at end of file diff --git a/gatewayservice/README.md b/gatewayservice/README.md new file mode 100644 index 000000000..5871df98a --- /dev/null +++ b/gatewayservice/README.md @@ -0,0 +1,23 @@ +### Monitoring (Prometheus and Grafana) +In this step we are going use [Prometheus](https://prometheus.io/) and [Grafana](https://grafana.com/) to monitor the restapi. First step is modifying the restapi launch to capture profiling data. In nodejs this is very easy. After installing the required packages (express-prom-bundle and prom-client), we need to modify the `restapi/server.js` in order to capture the profiling data adding: +```javascript +const metricsMiddleware = promBundle({includeMethod: true}); +app.use(metricsMiddleware); +``` +Now when we launch the api, in [http://localhost:8000/metrics](http://localhost:8000/metrics) we have a metrics endpoint from which get the profiling data. The idea here is to have another piece of software running (called [Prometheus](https://prometheus.io/)) that will get this data, let say, every five seconds. Then, another software called [Grafana](https://grafana.com/) will display this using beautiful charts. + +For running Prometheus and Grafana we can use several docker images. Check `docker-compose.yml` to see how these images are launched. + +Note: in the `prometheus.yml` we are telling prometheus where is our restapi metrics end point. In Grafana `datasources/datasource.yml` we are telling where to find prometheus data. + +In both configuration files we need to stablish the uris of restapi metrics and the prometheus datasource. Right now they are configured to work using docker-compose network. If you want to use these individual docker commands, you need to change these uris to point to localhost + +Once launched all the system is launched (see the Quick Start Guide), we can simulate a few petitions to our webservice: + +``` +sudo apt-get install apache2-utils +ab -m GET -n 10000 -c 100 http://localhost:8000/health +``` +In the Grafana dashboard we can see how the number of petitions increases dramatically after the call. + +A good reference with good explanations about monitoring in nodejs can be found [here](https://github.com/coder-society/nodejs-application-monitoring-with-prometheus-and-grafana). diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 8dc24ba88..444b71dc7 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -2,6 +2,11 @@ const express = require('express'); const axios = require('axios'); const cors = require('cors'); const promBundle = require('express-prom-bundle'); +//libraries required for OpenAPI-Swagger +const swaggerUi = require('swagger-ui-express'); +const fs = require("fs") +const YAML = require('yaml') + const app = express(); const port = 8000; @@ -17,7 +22,7 @@ const metricsMiddleware = promBundle({includeMethod: true}); app.use(metricsMiddleware); // Health check endpoint -app.get('/health', (req, res) => { +app.get('/health', (_req, res) => { res.json({ status: 'OK' }); }); @@ -41,9 +46,26 @@ app.post('/adduser', async (req, res) => { } }); +// Read the OpenAPI YAML file synchronously +openapiPath='./openapi.yaml' +if (fs.existsSync(openapiPath)) { + const file = fs.readFileSync(openapiPath, 'utf8'); + + // Parse the YAML content into a JavaScript object representing the Swagger document + const swaggerDocument = YAML.parse(file); + + // Serve the Swagger UI documentation at the '/api-doc' endpoint + // This middleware serves the Swagger UI files and sets up the Swagger UI page + // It takes the parsed Swagger document as input + app.use('/api-doc', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); +} else { + console.log("Not configuring OpenAPI. Configuration file not present.") +} + // Start the gateway service const server = app.listen(port, () => { console.log(`Gateway Service listening at http://localhost:${port}`); }); + module.exports = server diff --git a/gatewayservice/openapi.yaml b/gatewayservice/openapi.yaml new file mode 100644 index 000000000..67bffccbb --- /dev/null +++ b/gatewayservice/openapi.yaml @@ -0,0 +1,144 @@ +openapi: 3.0.0 +info: + title: Gatewayservice API + description: Gateway OpenAPI specification. + version: 0.2.0 +servers: + - url: http://localhost:8000 + description: Development server + - url: http://SOMEIP:8000 + description: Production server +paths: + /adduser: + post: + summary: Add a new user to the database. + operationId: addUser + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + username: + type: string + description: User ID. + example: student + password: + type: string + description: User password. + example: pass + responses: + '200': + description: User added successfully. + content: + application/json: + schema: + type: object + properties: + username: + type: string + description: User ID + password: + type: string + description: Hashed password + example: $2b$10$ZKdNYLWFQxzt5Rei/YTc/OsZNi12YiWz30JeUFHNdAt7MyfmkTuvC + _id: + type: string + description: Identification + example: 65f756db3fa22d227a4b7c7d + createdAt: + type: string + description: Creation date. + example: '2024-03-17T20:47:23.935Z' + ___v: + type: integer + example: '0' + '400': + description: Failed to add user. + content: + application/json: + schema: + type: object + properties: + error: + type: string + description: Error information. + example: getaddrinfo EAI_AGAIN mongodb + /health: + get: + summary: Check the health status of the service. + operationId: checkHealth + responses: + '200': + description: Service is healthy. + content: + application/json: + schema: + type: object + properties: + status: + type: string + description: Health status. + example: OK + /login: + post: + summary: Log in to the system. + operationId: loginUser + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + username: + type: string + description: User ID. + example: student + password: + type: string + description: User password. + example: pass + responses: + '200': + description: Login successful. Returns user token, username, and creation date. + content: + application/json: + schema: + type: object + properties: + token: + type: string + description: User token. + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NWY3NTZkYjNmYTIyZDIyN2E0YjdjN2QiLCJpYXQiOjE3MTA3MDg3NDUsImV4cCI6MTcxMDcxMjM0NX0.VMG_5DOyQ4GYlJQRcu1I6ICG1IGzuo2Xuei093ONHxw + username: + type: string + description: Username. + example: student + createdAt: + type: string + description: Creation date. + example: '2024-03-17T20:47:23.935Z' + '401': + description: Invalid credentials. + content: + application/json: + schema: + type: object + properties: + error: + type: string + description: Shows the error info.. + example: Invalid credentials + '500': + description: Internal server error. + content: + application/json: + schema: + type: object + properties: + error: + type: string + description: Error information. + example: Internal Server Error diff --git a/gatewayservice/package-lock.json b/gatewayservice/package-lock.json index fc5f2d60c..430cbe997 100644 --- a/gatewayservice/package-lock.json +++ b/gatewayservice/package-lock.json @@ -12,7 +12,10 @@ "axios": "^1.6.5", "cors": "^2.8.5", "express": "^4.18.2", - "express-prom-bundle": "^7.0.0" + "express-openapi": "^12.1.3", + "express-prom-bundle": "^7.0.0", + "swagger-ui-express": "^5.0.0", + "yaml": "^2.4.1" }, "devDependencies": { "jest": "^29.7.0", @@ -1277,6 +1280,37 @@ "node": ">= 0.6" } }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1333,7 +1367,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "dependencies": { "sprintf-js": "~1.0.2" } @@ -1474,8 +1507,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/bintrees": { "version": "1.0.2", @@ -1510,7 +1542,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1751,8 +1782,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/content-disposition": { "version": "0.5.4", @@ -1942,6 +1972,14 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/difunc": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/difunc/-/difunc-0.0.4.tgz", + "integrity": "sha512-zBiL4ALDmviHdoLC0g0G6wVme5bwAow9WfhcZLLopXCAWgg3AEf7RYTs2xugszIGulRHzEVDF/SHl9oyQU07Pw==", + "dependencies": { + "esprima": "^4.0.0" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2015,7 +2053,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -2121,6 +2158,21 @@ "node": ">= 0.10.0" } }, + "node_modules/express-normalize-query-params-middleware": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/express-normalize-query-params-middleware/-/express-normalize-query-params-middleware-0.5.1.tgz", + "integrity": "sha512-KUBjEukYL9KJkrphVX3ZgMHgMTdgaSJe+FIOeWwJIJpCw8UZQPIylt0MYddSyUwbms4LQ8RC4wmavcLUP9uduA==" + }, + "node_modules/express-openapi": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/express-openapi/-/express-openapi-12.1.3.tgz", + "integrity": "sha512-F570dVC5ENSkLu1SpDFPRQ13Y3a/7Udh0rfHyn3O1QrE81fPmlhnAo1JRgoNtbMRJ6goHNymxU1TVSllgFZBlQ==", + "dependencies": { + "express-normalize-query-params-middleware": "^0.5.0", + "openapi-framework": "^12.1.3", + "openapi-types": "^12.1.3" + } + }, "node_modules/express-prom-bundle": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/express-prom-bundle/-/express-prom-bundle-7.0.0.tgz", @@ -2138,6 +2190,11 @@ "prom-client": ">=15.0.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2264,11 +2321,18 @@ "node": ">= 0.6" } }, + "node_modules/fs-routes": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/fs-routes/-/fs-routes-12.1.3.tgz", + "integrity": "sha512-Vwxi5StpKj/pgH7yRpNpVFdaZr16z71KNTiYuZEYVET+MfZ31Zkf7oxUmNgyZxptG8BolRtdMP90agIhdyiozg==", + "peerDependencies": { + "glob": ">=7.1.6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -2349,7 +2413,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2526,7 +2589,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2563,6 +2625,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-dir/-/is-dir-1.0.0.tgz", + "integrity": "sha512-vLwCNpTNkFC5k7SBRxPubhOCryeulkOsSkjbGyZ8eOzZmzMS+hSEO/Kn9ZOVhFNAlRZTFc4ZKql48hESuYUPIQ==" + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -3328,7 +3395,6 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -3355,6 +3421,11 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -3403,6 +3474,11 @@ "node": ">=8" } }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3552,7 +3628,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3643,7 +3718,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -3663,6 +3737,97 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi-default-setter": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-default-setter/-/openapi-default-setter-12.1.3.tgz", + "integrity": "sha512-wHKwvEuOWwke5WcQn8pyCTXT5WQ+rm9FpJmDeEVECEBWjEyB/MVLYfXi+UQeSHTTu2Tg4VDHHmzbjOqN6hYeLQ==", + "dependencies": { + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-framework": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-framework/-/openapi-framework-12.1.3.tgz", + "integrity": "sha512-p30PHWVXda9gGxm+t/1X2XvEcufW1YhzeDQwc5SsgDnBXt8gkuu1SwrioGJ66wxVYEzfSRTTf/FMLhI49ut8fQ==", + "dependencies": { + "difunc": "0.0.4", + "fs-routes": "^12.1.3", + "glob": "*", + "is-dir": "^1.0.0", + "js-yaml": "^3.10.0", + "openapi-default-setter": "^12.1.3", + "openapi-request-coercer": "^12.1.3", + "openapi-request-validator": "^12.1.3", + "openapi-response-validator": "^12.1.3", + "openapi-schema-validator": "^12.1.3", + "openapi-security-handler": "^12.1.3", + "openapi-types": "^12.1.3", + "ts-log": "^2.1.4" + } + }, + "node_modules/openapi-jsonschema-parameters": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-jsonschema-parameters/-/openapi-jsonschema-parameters-12.1.3.tgz", + "integrity": "sha512-aHypKxWHwu2lVqfCIOCZeJA/2NTDiP63aPwuoIC+5ksLK5/IQZ3oKTz7GiaIegz5zFvpMDxDvLR2DMQQSkOAug==", + "dependencies": { + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-request-coercer": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-request-coercer/-/openapi-request-coercer-12.1.3.tgz", + "integrity": "sha512-CT2ZDhBmAZpHhAzHhEN+/J5oMK3Ds99ayLLdXh2Aw1DCcn72EM8VuIGVwG5fSjvkMsgtn7FgltFosHqeM6PRFQ==", + "dependencies": { + "openapi-types": "^12.1.3", + "ts-log": "^2.1.4" + } + }, + "node_modules/openapi-request-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-request-validator/-/openapi-request-validator-12.1.3.tgz", + "integrity": "sha512-HW1sG00A9Hp2oS5g8CBvtaKvRAc4h5E4ksmuC5EJgmQ+eAUacL7g+WaYCrC7IfoQaZrjxDfeivNZUye/4D8pwA==", + "dependencies": { + "ajv": "^8.3.0", + "ajv-formats": "^2.1.0", + "content-type": "^1.0.4", + "openapi-jsonschema-parameters": "^12.1.3", + "openapi-types": "^12.1.3", + "ts-log": "^2.1.4" + } + }, + "node_modules/openapi-response-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-response-validator/-/openapi-response-validator-12.1.3.tgz", + "integrity": "sha512-beZNb6r1SXAg1835S30h9XwjE596BYzXQFAEZlYAoO2imfxAu5S7TvNFws5k/MMKMCOFTzBXSjapqEvAzlblrQ==", + "dependencies": { + "ajv": "^8.4.0", + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-schema-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-schema-validator/-/openapi-schema-validator-12.1.3.tgz", + "integrity": "sha512-xTHOmxU/VQGUgo7Cm0jhwbklOKobXby+/237EG967+3TQEYJztMgX9Q5UE2taZKwyKPUq0j11dngpGjUuxz1hQ==", + "dependencies": { + "ajv": "^8.1.0", + "ajv-formats": "^2.0.2", + "lodash.merge": "^4.6.1", + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-security-handler": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-security-handler/-/openapi-security-handler-12.1.3.tgz", + "integrity": "sha512-25UTAflxqqpjCLrN6rRhINeM1L+MCDixMltiAqtBa9Zz/i7UkWwYwdzqgZY3Cx3vRZElFD09brYxo5VleeP3HQ==", + "dependencies": { + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3753,7 +3918,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3886,6 +4050,14 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, "node_modules/pure-rand": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", @@ -3953,6 +4125,14 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -4171,8 +4351,7 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/stack-utils": { "version": "2.0.6", @@ -4389,6 +4568,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.12.0.tgz", + "integrity": "sha512-Rt1xUpbHulJVGbiQjq9yy9/r/0Pg6TmpcG+fXTaMePDc8z5WUw4LfaWts5qcNv/8ewPvBIbY7DKq7qReIKNCCQ==" + }, + "node_modules/swagger-ui-express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.0.tgz", + "integrity": "sha512-tsU9tODVvhyfkNSvf03E6FAk+z+5cU3lXAzMy6Pv4av2Gt2xA0++fogwC4qo19XuFf6hdxevPuVCSKFuMHJhFA==", + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } + }, "node_modules/tdigest": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", @@ -4447,6 +4645,11 @@ "node": ">=0.6" } }, + "node_modules/ts-log": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.5.tgz", + "integrity": "sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -4523,6 +4726,14 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/url-value-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/url-value-parser/-/url-value-parser-2.2.0.tgz", @@ -4605,8 +4816,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "4.0.2", @@ -4636,6 +4846,17 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/gatewayservice/package.json b/gatewayservice/package.json index d5a3653ed..1d5f9df73 100644 --- a/gatewayservice/package.json +++ b/gatewayservice/package.json @@ -9,19 +9,22 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/pglez82/asw2324_0.git" + "url": "git+https://github.com/arquisoft/wiq_0.git" }, "author": "", "license": "ISC", "bugs": { - "url": "https://github.com/pglez82/asw2324_0/issues" + "url": "https://github.com/arquisoft/wiq_0/issues" }, - "homepage": "https://github.com/pglez82/asw2324_0#readme", + "homepage": "https://github.com/arquisoft/wiq_0#readme", "dependencies": { "axios": "^1.6.5", "cors": "^2.8.5", "express": "^4.18.2", - "express-prom-bundle": "^7.0.0" + "express-openapi": "^12.1.3", + "express-prom-bundle": "^7.0.0", + "swagger-ui-express": "^5.0.0", + "yaml": "^2.4.1" }, "devDependencies": { "jest": "^29.7.0", diff --git a/sonar-project.properties b/sonar-project.properties index abec6f90b..8ce93a6fb 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,12 +1,17 @@ -sonar.organization=pglez82 -sonar.host.url=https://sonarcloud.io -sonar.projectKey=pglez82_asw2324_0 +sonar.projectKey=Arquisoft_wiq_es6b +sonar.organization=arquisoft + +# This is the name and version displayed in the SonarCloud UI. +sonar.projectName=wiq_es6b +sonar.projectVersion=1.0 +# Encoding of the source code. Default is default system encoding +sonar.host.url=https://sonarcloud.io sonar.language=js -sonar.projectName=asw2324_0 +sonar.projectName=wiq_es6b sonar.coverage.exclusions=**/*.test.js sonar.sources=webapp/src/components,users/authservice,users/userservice,gatewayservice sonar.sourceEncoding=UTF-8 sonar.exclusions=node_modules/** -sonar.javascript.lcov.reportPaths=**/coverage/lcov.info \ No newline at end of file +sonar.javascript.lcov.reportPaths=**/coverage/lcov.info diff --git a/users/authservice/package.json b/users/authservice/package.json index 0e9dea930..6b5b62390 100644 --- a/users/authservice/package.json +++ b/users/authservice/package.json @@ -9,14 +9,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/pglez82/asw2324_0.git" + "url": "git+https://github.com/arquisoft/wiq_0.git" }, "author": "", "license": "ISC", "bugs": { - "url": "https://github.com/pglez82/asw2324_0/issues" + "url": "https://github.com/arquisoft/wiq_0/issues" }, - "homepage": "https://github.com/pglez82/asw2324_0#readme", + "homepage": "https://github.com/arquisoft/wiq_0#readme", "dependencies": { "bcrypt": "^5.1.1", "body-parser": "^1.20.2", diff --git a/users/userservice/package.json b/users/userservice/package.json index 554a8691d..2462c8e0f 100644 --- a/users/userservice/package.json +++ b/users/userservice/package.json @@ -9,14 +9,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/pglez82/asw2324_0.git" + "url": "git+https://github.com/arquisoft/wiq_0.git" }, "author": "", "license": "ISC", "bugs": { - "url": "https://github.com/pglez82/asw2324_0/issues" + "url": "https://github.com/arquisoft/wiq_0/issues" }, - "homepage": "https://github.com/pglez82/asw2324_0#readme", + "homepage": "https://github.com/arquisoft/wiq_0#readme", "dependencies": { "bcrypt": "^5.1.1", "body-parser": "^1.20.2", diff --git a/webapp/README.md b/webapp/README.md index 9568101e0..33dd89a5d 100644 --- a/webapp/README.md +++ b/webapp/README.md @@ -108,14 +108,21 @@ E2E tests are maybe the most difficult part to integrate in our system. We have In this project, the E2E testing user stories are defined using Cucumber. Cucumber uses a language called Gherkin to define the user stories. You can find the example in the `features` directory. Then, the actual tests are in the folder `steps`. We are going to configure jest to execute only the tests of this directory (check the `jest.config.ts` file in the `e2e` directory). -The E2E tests have two extra difficulties. The first one, we need a browser to perform the tests as if the user was using the application. For this matter, we use `jest-puppeteer` that will launch a Chromium instance for running the tests. The browser is started in the `beforeAll` function. Note that the browser is launched in a headless mode. This is necessary for the tests to run in the CI environment. If you want to debug the tests you can always turn this feature off. The second problem is that we need all our services at the same time to be able to run the tests. For achieving this, we are going to use the package `start-server-and-test`. This package, allows us to launch multiple servers and then run the tests. No need for any configuration. We can configure it straight in the `package.json` file: +The E2E tests have two extra difficulties. The first one, we need a browser to perform the tests as if the user was using the application. +For this matter, we use `jest-puppeteer` that will launch a Chromium instance for running the tests. +The browser is started in the `beforeAll` function. Note that the browser is launched in a headless mode. +This is necessary for the tests to run in the CI environment. If you want to debug the tests you can always turn this feature off. +The second problem is that we need all our services at the same time to be able to run the tests. +For achieving this, we are going to use the package `start-server-and-test`. +This package, allows us to launch multiple servers and then run the tests. +No need for any configuration. We can configure it straight in the `package.json` file: ```json "test:e2e": "start-server-and-test 'node e2e/test-environment-setup.js' http://localhost:8000/health prod 3000 \"cd e2e && jest\"", ``` -The package accepts pairs of parameters (launch a server and an URL to check if it is running. It also accepts npm commands (for instance prod, for the webapp, that will run `npm run prod`). The last parameter of the task will be launching Jest to run the E2E tests. +The package accepts pairs of parameters (launch a server and an URL to check if it is running. It also accepts npm commands (for instance prod, for the webapp, that will run `npm run prod`). The last parameter of the task will be launching Jest to run the e2e tests. Note that we are handling all the setup for the auth and user microservices using the file `test-environment-setup.js`. This file has the code needed to run everything, including an in-memory Mongo database to be able to execute the tests. diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 27466aee4..bcc358d5f 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -21,6 +21,8 @@ "web-vitals": "^3.5.1" }, "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", "axios-mock-adapter": "^1.22.0", "expect-puppeteer": "^9.0.2", "jest": "^29.3.1", @@ -658,9 +660,17 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, "engines": { "node": ">=6.9.0" }, @@ -1904,6 +1914,17 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", diff --git a/webapp/package.json b/webapp/package.json index 74e31beec..6e59b09bc 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -42,6 +42,8 @@ ] }, "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", "axios-mock-adapter": "^1.22.0", "expect-puppeteer": "^9.0.2", "jest": "^29.3.1", diff --git a/webapp/src/App.js b/webapp/src/App.js index d932005be..910935aba 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -17,7 +17,7 @@ function App() { - Welcome to the 2024 edition of the Software Architecture course + Welcome to wiq_0 {showLogin ? : } diff --git a/webapp/src/App.test.js b/webapp/src/App.test.js index 5e3b73141..9aa277574 100644 --- a/webapp/src/App.test.js +++ b/webapp/src/App.test.js @@ -3,6 +3,6 @@ import App from './App'; test('renders learn react link', () => { render(); - const linkElement = screen.getByText(/Welcome to the 2024 edition of the Software Architecture course/i); + const linkElement = screen.getByText(/Welcome to wiq_0/i); expect(linkElement).toBeInTheDocument(); });