Code Monkey home page Code Monkey logo

backity's Introduction

Code Soapbox Logo

Backity

Backity Logo

Quality Gate Status Maintainability Rating Reliability Rating

Bugs Coverage Vulnerabilities Code Smells Technical Debt

GitHub

Backity is a web service for automatically downloading backups of your games from external clients such as GOG.

For instructions on how to use Backity, consult the Wiki.

⚠️Warning: Backity is not yet production-ready! Anything may break at any time.⚠️

The information below is aimed at developers who want to extend this project's functionality.


Getting Started

First, clone this repository.

Then, build it locally with:

mvn clean install

You can run the project with the following command:

mvn spring-boot:run -Dspring-boot.run.profiles=dev

As a result, you should be able to visit the home page on http://localhost:8080/:

home page screenshot

Profiles summary

The project can be built with various different profiles to allow for flexible configuration. Below you'll find a short summary of the available profiles.

Spring profiles

  • dev - for local development. Allows things like handling requests from http://localhost:4200/.
  • angular - special profile used for client code generation. Applied automatically when the angular Maven profile is enabled.

Maven profiles

  • sonar-cloud - for code analysis on push to master
  • frontend-pre-sonar - for including code coverage reports from the frontend module during a sonar analysis
  • frontend-sonar - for running only a sonar analysis for the frontend module
  • angular - for generating client code

API documentation

First, build and run the application. Then you'll be able to reach the API docs.

Swagger

The Swagger UI page: http://localhost:8080/swagger-ui.html.

OpenAPI

The OpenAPI description is available at the following urls:

Client code generation

To run client code generation using the openapi-generator-maven-plugin, execute the following command:

mvn clean verify -Pangular -DskipTests

The application will be started so that the API specification can be obtained from the Open API endpoint.

The generated code will be available in the frontend/src/main/angular/src/backend directory. Don't edit those files manually.

Working with frontend on a local environment

If you want to see how changes you make in the frontend code affect the application you don't need to build it together with the backend module every time. Use the following commands:

cd frontend/src/main/angular
ng serve

and visit http://localhost:4200/. The application reloads automatically which speeds up your work.

In order to incorporate changes into the project, rebuild the whole application from the main project directory with:

mvn clean package

Running test suites

Backend

Run unit tests with the following command in the project directory:

mvn test

Run all tests with the following command in the project directory:

mvn verify

Frontend

Run all tests for the Angular code with:

cd frontend/src/main/angular
ng test

SonarQube analysis on a local environment

This project is configured so that its integration tests are taken into consideration when calculating code coverage. SonarQube will show these tests as unit tests on the dashboard, as there is currently no native integration test support within Sonar.

Prerequisites

  • You need to have Docker and Docker-Compose installed.
  • You need Chrome installed on your machine to run a frontend analysis with code coverage.

Launching SonarQube on a local environment

To start a local instance of SonarQube, use the following command in the root of this repository:

docker-compose -f docker/sonarqube/docker-compose_sonar.yml --env-file docker/sonarqube/.env up -d

You should only need to do this once.

Note that the first run may take a while before SonarQube is fully configured - you may want to check the Docker logs for sonarqube_first_run_setup_backend to confirm whether the setup is finished successfully.

The Java analysis profile is stored in docker/sonarqube/java_profile.xml and is automatically restored when first launching the Docker instance.

The quality gate is defined in an init script (docker/sonarqube/import_data.sh) and is automatically restored when first launching the Docker instance.

The imported profile and quality gate are set as default.

Authentication is disabled by default.

The SonarQube instance will become available at http://localhost:9000.

Full analysis

You can run analysis for the whole project (both backend and frontend) by running the following command from the root of this repository:

mvn clean verify sonar:sonar -Pfrontend-pre-sonar -Ppitest-full

Backend analysis

You can run a separate analysis for the backend module:

cd backend
mvn clean verify sonar:sonar -Ppitest-full

Frontend analysis

You can run a separate analysis for the frontend module:

cd frontend
mvn sonar:sonar -Pfrontend-pre-sonar

Verifying results

Visit the Projects SonarQube page and choose the right project.

Mutation testing

Mutation testing is the act of automatically modifying existing code in small ways, then checking if our tests will fail. This project supports Java mutation testing through Pitest. Bear in mind that mutation testing may take a while.

To generate a full mutation coverage report, use the command:

mvn clean test-compile -Ppitest-full

Navigate to ./backend/target/pit-reports and open index.html to view the report.

The most efficient way to generate a local coverage report during development is:

mvn clean test-compile -Ppitest-new-code

This will only analyse code that has been changed compared to the main Git branch.

To use Pitest as part of Continuous Integration, use the following command in the CI script:

mvn clean test-compile -Ppitest-new-code -Ppitest-strict

This will fail the build if the mutation threshold is below a certain value.

Built With

Table of contents generated with markdown-toc

backity's People

Contributors

daniel-frak avatar dependabot[bot] avatar little-pinecone avatar vishalsingh2972 avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar

backity's Issues

Downloads - Download queue and Processed files should update automatically when "file download finished" event is sent

When a file finishes downloading, a message is sent to frontend which removes it from the "currently downloading" component.
However, the "download queue" and "processed files" table are not updated.

  1. When the "download start" message is received, the file should be removed from the "download queue".
  2. When the "download finished" message is received, the file should be added to the "processed files" list.

Investigate possible memory leak

Found in logs:

2023-02-26 15:42:58.691  INFO 3632 --- [MessageBroker-6] d.c.b.c.f.d.d.services.FileDownloader    : Downloading game file 1714 (url=/downloads/virtuaverse/en1installer0)...
2023-02-26 15:43:07.844  INFO 3632 --- [MessageBroker-8] o.s.w.s.c.WebSocketMessageBrokerStats    : WebSocketSession[1 current WS(1)-HttpStream(0)-HttpPoll(0), 1 total, 0 closed abnormally (0 connect failure, 0 send limit, 0 transport error)], stompSubProtocol[processed CONNECT(1)-CONNECTED(1)-DISCONNECT(0)], stompBrokerRelay[null], inboundChannel[pool size = 32, active threads = 0, queued tasks = 0, completed tasks = 48], outboundChannel[pool size = 32, active threads = 0, queued tasks = 0, completed tasks = 2141], sockJsScheduler[pool size = 16, active threads = 2, queued tasks = 3, completed tasks = 24]
2023-02-26 15:43:24.507 ERROR 3632 --- [ctor-http-nio-4] io.netty.util.ResourceLeakDetector       : LEAK: ByteBuf.release() was not called before it's garbage-collected. See https://netty.io/wiki/reference-counted-objects.html for more information.
Recent access records: 
Created at:
	io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:403)
	io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:188)
	io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:179)
	io.netty.buffer.AbstractByteBufAllocator.buffer(AbstractByteBufAllocator.java:116)
	io.netty.handler.ssl.SslHandler.allocate(SslHandler.java:2248)
	io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1337)
	io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1235)
	io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1284)
	io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:510)
	io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:449)
	io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:279)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
	io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:722)
	io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:658)
	io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:584)
	io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496)
	io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
	io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	java.base/java.lang.Thread.run(Thread.java:833)

Redownloading files

There should be a possibility to redownload already processed files.

A file that is being redownloaded should REPLACE the old file (no suffix should be added to its name).

Clean up architecture

The code is a bit of a mess now, with, e.g., Hibernate annotations in domain code.

The codebase should be thoroughly cleaned up and made more easily understandable.

  • Domain should be isolated from Hibernate/JPA representations
  • Domain entities should protect their invariants (non-null properties etc.) - it should not be possible to create aggregates in an invalid state.
  • Controllers and domain should use their own PaginationJson/PageJson objects.
  • Remove use of INSTANCE for mappers.
  • The domain should not care if the files are stored on an HDD or on S3. "File management" should just be another adapter and it should be easy to write a custom one.
  • Controller pagination variables should be called paginationHttpDto.
  • Add Facade for GameFileDetails.
  • Add Facade to backup/discovery
  • FileBackupProgress should have its own HttpDto.
  • Move @IncludeInDocumentation to adapters.driving.http.
  • Consider if GameFileDetailsHttpDto needs to be shared between adapters.
  • Backend Sonarcloud analysis should not fail
  • All controller and repository tests should resuse contexts
  • All JPA repositories should have @Transactional(readOnly=true) on top of the class, instead of on the methods.
  • Pitest-strict should work (to confirm)
  • Pitest-strict should not fail
  • Create a LogCreatedWsMessage for LogCreatedMessage.
  • Move IncludeInDocumentation out of domain?
  • SourceFileBackupService is returning a String. Does it have to? If so, make it return a value object.
  • Rename GameFileDetails to just FileDetails?
  • Entire API should only use HttpDto objects (write ArchUnit test)
  • SonarQube mutation test reporting should work correctly
  • Rename *Message to *Event
    • On backend
    • On frontend
  • Frontend Sonarcloud analysis should not fail
  • Entire API should be RESTful/resource-based.
  • Consider taking some services out of domain and into application

Refactor abstractions around FileDownloader

E.g. FileDownloader updates GameFileVersion with tempFilePath, but FileDownloadQueueProcessor updates it with the final file path. Those things should ideally happen in the same place.

File path creation should go straight into FileManager, as an implementation detail.

Perhaps ask ChatGPT how to simplify the situation (with simplified versions of the affected classes).

FileDownloader::downloadToDisk uses SourceDownloader which renames the file after download to whatever it chooses. Then FileDownloader passes the new path to gameFileVersion.markAsDownloaded.

Need to investigate whether final renaming could be left to FileDownloader.

Sending e-mails

At regular intervals, Backity should send e-mails with information about discovered, downloaded, ignored and failed files.

Group files by game

Discovered files, especially, should be grouped by game so that it's easier to pick which files the user wants to download.

Handling Game File Versions

  1. A new Game File Version might need to replace an existing older version. This should be handled.
  2. Backity should be able to notice that there is a newer version of a file available and inform the user about it.
  3. There should be a choice whether the file should be replaced or not.

File size should be stored as Long

Currently, filesize is stored as string, which requires some additional magic to handle. It should be stored as Long.

Perhaps filesize should also be taken from the URL header, instead of JSON field?

URL-decode file names

Currently files are being downloaded like patch_virtuaverse_v._1.29_%2841922%29_to_1.29a_%2842043%29.exe.
File names should be URL decoded.

Add ability to ignore files

There should be the ability to ignore files instead of enqueuing them.

They should show up in the "processed files" list as "ignored".

Clean up temporary files on startup

The application should find all GameFileVersions marked as DOWNLOAD_IN_PROGRESS and delete their files if they exist, as well as either marking them as FAILED or ENQUEUED_FOR_DOWNLOAD.

Perhaps each file should have a counter in the database of how many attempts were made to redownload and if less than 3 then retry, but more than that is a fail?

Retrieve filename during discovery

Currently, filenames are retrieved after downloading. They should be retrieved prior to downloading, then updated when starting downloading.

If the filename has changed between discovery and download, then the file should be downloaded using the original filename (and so also replace any existing files), then renamed. If a file with the new name already exists, it should be removed (probably want a Spring property to adjust this behavior).

Dark theme

The application might look better if the bg-dark color is applied to the body and the cards (making it so that the navbar is the same color as the rest of the page).

Maven build - fix clean verify

Generating client code per the instructions in README.md breaks the build - npm can't build the app because the generated code doesn't exist yet.
The only fix is to temporarily disable the npm build plugin in the pom.

Must find a better way.

Show download speed

The download speed (mbps) should be shown next to the remaining time when downloading a file.

Downloads - view for showing downloaded files

There should be a view showing a paginated list of all downloaded files, i.e. the newest versions of Game Files.

A SELECT query could look like this:

SELECT gf.*
FROM GameFileVersion gf
JOIN (
  SELECT url, MAX(date_modified) AS max_date_modified
  FROM GameFileVersion
  GROUP BY url
) latest ON gf.url = latest.url AND gf.date_modified = latest.max_date_modified

Recommended indexes:

CREATE INDEX idx_gamefiles_url ON GameFiles (url);
CREATE INDEX idx_gamefiles_date_modified ON GameFiles (date_modified);

A Spring JPA query might look like this (maybe):

    @Query("SELECT gf FROM GameFileVersion gf "
           + "JOIN (SELECT url, MAX(dateModified) AS maxDateModified "
           + "FROM GameFileVersion GROUP BY url) latest "
           + "ON gf.url = latest.url AND gf.dateModified = latest.maxDateModified")
    Page<GameFileVersion> findNewestVersionsOfGameFiles(Pageable pageable);
```java

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.