103 lines
4.0 KiB
Markdown
103 lines
4.0 KiB
Markdown
# SwisssignChallenge
|
|
|
|
## Requirements
|
|
|
|
- [GraalVM 23](https://www.graalvm.org/)
|
|
- PostgreSQL 17, but a Docker Compose file is available for your convenience.
|
|
If you decide to run it locally, **please check application.yml for default values**.
|
|
|
|
## Generate sources
|
|
|
|
To generate code from the API contract, run `./mvnw generate-sources`
|
|
|
|
## Build native executable
|
|
|
|
1. Make sure your `JAVA_HOME` or `GRAALVM_HOME` environment variable is correctly
|
|
pointing to your JDK.
|
|
1. Run `./mvnw -Pnative native:compile`.
|
|
|
|
Note that you don't need to build a native executable to run a development
|
|
environment.
|
|
|
|
## Retrieving PDFs
|
|
|
|
You must have 2 PDF files with the names `cv-actual.pdf` and `cv-stackoverflow.pdf`
|
|
as this is what is referenced by the rows in the database. By default, they must
|
|
be placed in `/srv/pdfs/` but feel free to modify the constant before running the
|
|
code.
|
|
|
|
For your convenience, those files are placed at the root of the project.
|
|
|
|
## Choices
|
|
|
|
### GraalVM
|
|
|
|
It's my first time using GraalVM, but I took the opportunity of using it in this
|
|
project because of its small scale. Its goals are to improve the performance of
|
|
JVM-based applications to match native languages.
|
|
|
|
Startup times are also greatly improved; less than 0.5 seconds. The drawback is
|
|
that the build times are greatly lengthened; around 1 minute for an empty
|
|
project.
|
|
|
|
### PostgreSQL
|
|
|
|
I don't think I have to explain much about PostgreSQL, but it's well-suited for
|
|
both small and large-scale projects. Also, support with Spring Boot is, in my
|
|
experience, flawless.
|
|
|
|
### Flyway
|
|
|
|
I'm experienced with Liquibase, but after quickly reading about errors related
|
|
to GraalVM, I understood it was unsupported. Flyway is another popular migration
|
|
tool, so I gave it a go. I have nothing to say for or against it.
|
|
|
|
### JWT
|
|
|
|
I knew the basics but never implemented the authentication layer myself. JWT
|
|
is a standard, but in an enterprise environment there would probably be an
|
|
OIDC-compliant service to be plugged into.
|
|
|
|
After initializing the correct beans, it worked really well.
|
|
|
|
As I don't specify the JWT secret, all issued tokens are not recognized after
|
|
a restart and must be regenerated (i.e. all users are logged out).
|
|
|
|
LLMs were a great help here too, along with good ol' Baeldung.
|
|
|
|
### Files
|
|
|
|
At first, I wanted to store the files as a BYTEA in the database. I thought
|
|
this would be much simpler and minimalistic, but I was wrong on the first point.
|
|
I gave up after I couldn't find the root of the error "cannot convert to long",
|
|
despite not having a single long in my source code. Anyway, files are stored
|
|
on the filesystem and are retrieved by the `PdfServiceImpl`. This service makes
|
|
sure that the file name doesn't contain `".."` in the file name in order to
|
|
prevent [directory traversal attacks](https://en.wikipedia.org/wiki/Directory_traversal_attack).
|
|
Files are not uploaded by the user so there is virtually 0 chance of it containing
|
|
malicious characters, but I thought it was important to safeguard against this.
|
|
|
|
### UUID
|
|
|
|
As there is insanely little chance of collision, UUID are most useful in
|
|
distributed systems where we could give the responsibility of generating
|
|
the UUID to the clients. For a system like this, I doubt that the max value
|
|
of 64-bit integers would be reached, but the maintainability cost isn't much
|
|
higher than using integers. If anything, it at least prevents the user from
|
|
using the ID in the wrong place (i.e. `DELETE /person/1` instead of
|
|
`DELETE /city/1`) as no entity shares the same ID.
|
|
|
|
### json-patch
|
|
|
|
I used [json-patch](https://github.com/java-json-tools/json-patch) to confirm
|
|
the status of documents. Instead of sending the whole representation, only
|
|
the operation (`op`), the path (`path`) to apply it to and the new value
|
|
(`value`) have to be supplied.
|
|
|
|
## Potential improvements
|
|
|
|
A lot more tests. Integration tests, fuzz tests and benchmarks are those that come
|
|
to mind.
|
|
|
|
~~I would also spend a lot more time understanding cyclic dependencies when (de)serializing
|
|
JSON. For now there's a manual workaround in `SigningRequestDocumentServiceImpl.java`~~. |