{"id":23402627,"url":"https://github.com/martincastroalvarez/java-spring-boot","last_synced_at":"2026-04-12T05:34:10.978Z","repository":{"id":106552621,"uuid":"570338835","full_name":"MartinCastroAlvarez/java-spring-boot","owner":"MartinCastroAlvarez","description":"Spring Boot Application","archived":false,"fork":false,"pushed_at":"2022-11-26T14:43:59.000Z","size":3385,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-08T22:25:45.319Z","etag":null,"topics":["beans","gradle","java","maven","mysql","springboot"],"latest_commit_sha":null,"homepage":"https://martincastroalvarez.com","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MartinCastroAlvarez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-24T23:58:04.000Z","updated_at":"2025-01-10T07:15:08.000Z","dependencies_parsed_at":"2023-07-16T02:00:36.188Z","dependency_job_id":null,"html_url":"https://github.com/MartinCastroAlvarez/java-spring-boot","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/MartinCastroAlvarez/java-spring-boot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MartinCastroAlvarez%2Fjava-spring-boot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MartinCastroAlvarez%2Fjava-spring-boot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MartinCastroAlvarez%2Fjava-spring-boot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MartinCastroAlvarez%2Fjava-spring-boot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MartinCastroAlvarez","download_url":"https://codeload.github.com/MartinCastroAlvarez/java-spring-boot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MartinCastroAlvarez%2Fjava-spring-boot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31705574,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-12T05:11:36.334Z","status":"ssl_error","status_checked_at":"2026-04-12T05:11:27.332Z","response_time":58,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["beans","gradle","java","maven","mysql","springboot"],"created_at":"2024-12-22T12:29:47.938Z","updated_at":"2026-04-12T05:34:10.950Z","avatar_url":"https://github.com/MartinCastroAlvarez.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Java Sprint Boot\nAuthentication, Authorization, SQL, Async tasks, Unit Tests, Code Coverage.\n\n![wallpaper](./wallpaper.jpg)\n\n## Rerferences\n\n- [Building an Application with Spring Boot](https://spring.io/guides/gs/spring-boot/)\n- [Spring Boot Examples](https://github.com/mkyong/spring-boot)\n- [Spring Boot – Starters](https://www.geeksforgeeks.org/spring-boot-starters/)\n- [Spring Boot Actuator](https://www.baeldung.com/spring-boot-actuators)\n- [Using Logback with Spring Boot](https://springframework.guru/using-logback-spring-boot/)\n- [@Controller and @RestController Annotations in Spring Boot](https://stackabuse.com/controller-and-restcontroller-annotations-in-spring-boot/)\n- [Spring Data JPA - Reference Documentation](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.limit-query-result)\n- [Jackson Project Home @github](https://github.com/FasterXML/jackson)\n- [Java Map Class](https://docs.oracle.com/javase/8/docs/api/java/util/Map.html)\n- [Spring Boot Connect to PostgreSQL Database Examples](https://www.codejava.net/frameworks/spring-boot/connect-to-postgresql-database-examples)\n- [Spring Boot PostgreSQL](https://zetcode.com/springboot/postgresql/)\n- [Spring Boot + PostgreSQL + JPA/Hibernate CRUD Restful API Tutorial](https://www.javaguides.net/2019/01/springboot-postgresql-jpa-hibernate-crud-restful-api-tutorial.html)\n- [Java 8 – Convert Optional\u003cString\u003e to String](https://mkyong.com/java8/java-8-convert-optionalstring-to-string/)\n- [Personalizar las relaciones con @JoinColumn](https://www.oscarblancarteblog.com/2018/12/13/Personalizar-las-relaciones-con-joincolumn/)\n- [Securing a Web Application](https://spring.io/guides/gs/securing-web/)\n- [Hashing a Password in Java](https://www.baeldung.com/java-password-hashing)\n- [Password authentication in Java](https://stackoverflow.com/questions/2860943/how-can-i-hash-a-password-in-java)\n- [Pagination and Sorting using Spring Data JPA](https://www.baeldung.com/spring-data-jpa-pagination-sorting)\n- [Spring @Async Annotation for Asynchronous Processing](https://www.digitalocean.com/community/tutorials/spring-async-annotation)\n- [Introduction to Spring Testing](https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html)\n- [AssertJ Core](https://joel-costigliola.github.io/assertj/assertj-core-quick-start.html)\n- [How to configure maven surefire plugin work with JUnit 5](https://ahmadatwi.me/2018/08/16/tutorial-how-to-configure-maven-surefire-plugin-work-with-junit-5/)\n\n## Data Model\n\n#### User\n| Attribute | Type | Description |\n| ---  | --- | --- |\n| *id* | *int* | User unique identifier. |\n| *name* | *string* | User name. |\n| *password* | *string* | User hashed password. |\n\n#### Job\n| Attribute | Type | Description |\n| ---  | --- | --- |\n| *id* | *int* | Job id. |\n| *createdAt* | *date* | Job creation date. |\n| *startedAt* | *date* | Job start date. |\n| *endedAt* | *date* | Job end date. |\n| *status* | *int* | Job status (Pending, Started, Ended, Failed). |\n\n#### Person\n| Attribute | Type | Description |\n| ---  | --- | --- |\n| *id* | *int* | Person unique identifier. |\n| *name* | *string* | Person name. |\n\n#### Property\n| Attribute | Type | Description |\n| ---  | --- | --- |\n| *id* | *int* | Property unique identifier. |\n| *ownerId* | *int* | Owner unique identifier. |\n| *name* | *string* | Property name. |\n\n## API Documentation\n\n#### Users\n| Methd | Endpoint | Description |\n| ---  | --- | --- |\n| *POST* | */api/v1/auth/signup/* | Create an User. |\n| *POST* | */api/v1/auth/logout/* | Logout endpoint. |\n| *POST* | */api/v1/auth/login/* | Login endpoint. |\n| *GET* | */api/v1/auth/session/* | Get session details. |\n| *PUT* | */api/v1/auth/session/* | Update your User. |\n\n#### Users\n| Methd | Endpoint | Description |\n| ---  | --- | --- |\n| *GET* | */api/v1/jobs/* | List the latest jobs. |\n| *GET* | */api/v1/jobs/:id* | Get one job by id. |\n\n#### Persons\n| Methd | Endpoint | Description |\n| ---  | --- | --- |\n| *GET* | */api/v1/persons/* | Lists persons. |\n| *POST* | */api/v1/persons/* | Create a person. |\n| *GET* | */api/v1/persons/:id/* | Person details. |\n| *PUT* | */api/v1/persons/:id/* | Update person. |\n| *DELETE* | */api/v1/persons/:id/* | Delete a person. |\n\n#### Properties\n| Methd | Endpoint | Description |\n| ---  | --- | --- |\n| *GET* | */api/v1/properties/* | Lists properties. |\n| *POST* | */api/v1/properties/* | Create a Property. |\n| *GET* | */api/v1/properties/:id/* | Property details. |\n| *PUT* | */api/v1/properties/:id/* | Update Property. |\n| *DELETE* | */api/v1/properties/:id/* | Delete a Property. |\n\n## Software Architecture\n\n#### Configuration\n\n| File | Description |\n| ---   | --- |\n| [pom.xml](pom.xml) | Maven Configuration. |\n| [application.properties](src/main/resources/application.properties) | Application Properties. |\n| [Application.java](src/main/java/com/martincastroalvarez/london/Application.java) | Application Context. |\n| [GlobalProperties.java](./src/main/java/com/martincastroalvarez/london/config/GlobalProperties.java) | Application Configuration. |\n\n#### Controllers\n| File | Description |\n| ---   | --- |\n| [RootController.java](./src/main/java/com/martincastroalvarez/london/controllers/RootController.java) | Root Endpoint. |\n| [PersonController.java](./src/main/java/com/martincastroalvarez/london/controllers/PersonController.java) | Person API Resource. |\n| [PropertyController.java](./src/main/java/com/martincastroalvarez/london/controllers/PropertyController.java) | Property API Resource. |\n| [UserController.java](./src/main/java/com/martincastroalvarez/london/controllers/UserController.java) | User API Resource. |\n| [JobController.java](./src/main/java/com/martincastroalvarez/london/controllers/JobController.java) | Job API Resource. |\n\n#### Models\n| File | Description |\n| ---   | --- |\n| [Person.java](./src/main/java/com/martincastroalvarez/london/models/Person.java) | Person model. |\n| [Property.java](./src/main/java/com/martincastroalvarez/london/models/Property.java) | Property model. |\n| [User.java](./src/main/java/com/martincastroalvarez/london/models/User.java) | User model. |\n| [Job.java](./src/main/java/com/martincastroalvarez/london/models/Job.java) | Job model. |\n\n#### Tasks\n| File | Description |\n| ---   | --- |\n| [PersonTasks.java](./src/main/java/com/martincastroalvarez/london/tasks/PersonTasks.java) | Person tasks. |\n| [PropertyTasks.java](./src/main/java/com/martincastroalvarez/london/tasks/PropertyTasks.java) | Property tasks. |\n\n#### Repositories\n| File | Description |\n| ---   | --- |\n| [PersonRepository.java](./src/main/java/com/martincastroalvarez/london/repositories/PersonRepository.java) | Person model repository. |\n| [PropertyRepository.java](./src/main/java/com/martincastroalvarez/london/repositories/PropertyRepository.java) | Property model repository. |\n| [UserRepository.java](./src/main/java/com/martincastroalvarez/london/repositories/UserRepository.java) | User model repository. |\n| [JobRepository.java](./src/main/java/com/martincastroalvarez/london/repositories/JobRepository.java) | Job model repository. |\n\n#### Services\n| File | Description |\n| ---   | --- |\n| [PersonService.java](./src/main/java/com/martincastroalvarez/london/services/PersonService.java) | Person business logic. |\n| [PropertyService.java](./src/main/java/com/martincastroalvarez/london/services/PropertyService.java) | Property business logic. |\n| [UserService.java](./src/main/java/com/martincastroalvarez/london/services/UserService.java) | User business logic. |\n| [JobService.java](./src/main/java/com/martincastroalvarez/london/services/JobService.java) | Job business logic. |\n\n#### Exceptions\n| File | Description |\n| ---   | --- |\n| [GlobalErrorHandler.java](./src/main/java/com/martincastroalvarez/london/errors/GlobalErrorHandler.java) | Catches exceptions globally and generates pretty JSON responses. |\n| [ErrorResponse.java](./src/main/java/com/martincastroalvarez/london/responses/ErrorResponse.java) | Pretty representation of a Java exception. |\n| [PersonNotFoundError.java](./src/main/java/com/martincastroalvarez/london/errors/PersonNotFoundError.java) | Person not found. |\n| [PropertyNotFoundError.java](./src/main/java/com/martincastroalvarez/london/errors/PropertyNotFoundError.java) | Property not found. |\n| [UserNotFoundError.java](./src/main/java/com/martincastroalvarez/london/errors/UserNotFoundError.java) | User not found. |\n| [JobNotFoundError.java](./src/main/java/com/martincastroalvarez/london/errors/JobNotFoundError.java) | Job not found. |\n\n#### Unit Tests\n| File | Description |\n| ---   | --- |\n| [PersonApiTests.java](./src/test/java/com/martincastroalvarez/london/PersonApiTests.java) | Testing the Person API. |\n| [PersonControllerTests.java](./src/test/java/com/martincastroalvarez/london/PersonControllerTests.java) | Testing the Person controller. |\n| [PersonRepositoryTests.java](./src/test/java/com/martincastroalvarez/london/PersonRepositoryTests.java) | Testing the Person repository. |\n| [PersonServiceTests.java](./src/test/java/com/martincastroalvarez/london/PersonServiceTests.java) | Testing the Person service. |\n\n#### Utils\n| File | Description |\n| ---   | --- |\n| [HashUtil.java](./src/main/java/com/martincastroalvarez/london/utils/HashUtil.java) | Utilities for hashsing password. |\n\n## Instructions\n\n#### Installation\n\nStarting related servies\n```bash\ndocker-compose up\n```\n```bash\n[...]\ndb_1  | 2022-11-24 22:30:48.577 UTC [1] LOG:  listening on IPv4 address \"0.0.0.0\", port 5432\ndb_1  | 2022-11-24 22:30:48.577 UTC [1] LOG:  listening on IPv6 address \"::\", port 5432\ndb_1  | 2022-11-24 22:30:48.602 UTC [1] LOG:  listening on Unix socket \"/var/run/postgresql/.s.PGSQL.5432\"\ndb_1  | 2022-11-24 22:30:48.639 UTC [48] LOG:  database system was shut down at 2022-11-24 22:30:48 UTC\ndb_1  | 2022-11-24 22:30:48.659 UTC [1] LOG:  database system is ready to accept connections\n```\n\nRunning the application with Maven:\n```bash\nSALT=\"asdfasdf\" DEBUG=\"true\" ENVIRONMENT=\"martin\" ./mvnw spring-boot:run\n```\n\nChecking the application health status:\n```bash\ncurl -s http://localhost:8080/actuator/health\n```\n```bash\n{\n  \"environment\": \"martin\",\n  \"debug\": \"true\"\n}\n```\n\nRunning unit tests:\n```bash\n./mvnw clean test\n```\n\nOpening the Code Coverage HTML:\n```bash\ngoogle-chrome target/site/jacoco/index.html \n```\n![jacoco.png](./jacoco.png)\n\n#### Authentication\n\nRequests before authenticating are restricted.\n```bash\ncurl --cookie-jar \"/tmp/london\" -X POST -s http://localhost:8080/api/v1/login/ -d 'username=user\u0026password=password'\n```\n```bash\nHTTP/1.1 403 \nSet-Cookie: JSESSIONID=773BEA4F294E1D05CD15CC80870F4F19; Path=/; HttpOnly\nX-Content-Type-Options: nosniff\nX-XSS-Protection: 1; mode=block\nCache-Control: no-cache, no-store, max-age=0, must-revalidate\n[...]\n```\n\nSign up as a new User.\n```bash\ncurl --cookie-jar \"/tmp/london\" -X POST -s http://localhost:8080/api/v1/signup/ -H 'content-type: application/json' -d '{\"name\": \"lorem\", \"password\": \"ipsum\"}'\n```\n```bash\n{\n  \"id\": 1,\n  \"name\": \"Lorem Ipsum\"\n}\n```\n\n\nAuthenticating using username and password:\n\n# TODO\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n#### Persons\n\nRetrieving a non-existent Person:\n```bash\ncurl --cookie-jar \"/tmp/london\" -s http://localhost:8080/api/v1/persons/123/\n```\n```bash\n{\n  \"timestamp\": \"2022-11-25T14:58:55.492+00:00\",\n  \"type\": \"com.martincastroalvarez.london.PersonNotFoundError\",\n  \"message\": null\n}\n```\n\nCreating a Person:\n```bash\ncurl --cookie-jar \"/tmp/london\" -X POST -s http://localhost:8080/api/v1/persons/ -H 'content-type: application/json' -d '{\"name\": \"Lorem Ipsum\"}'\n```\n```bash\n{\n  \"id\": 1,\n  \"name\": \"Lorem Ipsum\"\n}\n```\n\nListing existing Persons:\n```bash\ncurl --cookie-jar \"/tmp/london\" -X GET -s \"http://localhost:8080/api/v1/persons/?sort_key=\"name\u0026limit=10\u0026offset=0\"\n```\n```bash\n[\n  {\n    \"id\": 3,\n    \"name\": \"Lorem Ipsum\"\n  },\n  {\n    \"id\": 1,\n    \"name\": \"Sit Amet\"\n  },\n  {\n    \"id\": 2,\n    \"name\": \"Nisman\"\n  }\n]\n```\n\nUpdating an existing Person:\n```bash\ncurl --cookie-jar \"/tmp/london\" -X PUT -s http://localhost:8080/api/v1/persons/1 -H 'content-type: application/json' -d '{\"name\": \"Sit Amet\"}'\n```\n```bash\n{\n  \"id\": 1,\n  \"name\": \"Sit Amet\"\n}\n```\n\nGetting Person details:\n```bash\ncurl --cookie-jar \"/tmp/london\" -s http://localhost:8080/api/v1/persons/2\n```\n```bash\n{\n  \"id\" : 2,\n  \"name\" : \"Nisman\"\n}\n```\n\nDeleting a Person:\n```bash\ncurl --cookie-jar \"/tmp/london\" -X DELETE -s http://localhost:8080/api/v1/persons/3\n```\n```bash\n{\n  \"id\" : 21,\n  \"createdAt\" : null,\n  \"endedAt\" : null,\n  \"startedAt\" : null,\n  \"status\" : null,\n  \"message\" : null\n}\n```\n\nSearching for Persons by name:\n```bash\ncurl --cookie-jar \"/tmp/london\" -X GET -s \"http://localhost:8080/api/v1/persons/?name=Sit\u0026sort_key=name\"\n```\n```bash\n[\n  {\n    \"id\": 1,\n    \"name\": \"Sit Amet\"\n  }\n]\n```\n\n#### Jobs\n\nListing jobs:\n```bash\ncurl --cookie-jar \"/tmp/london\" -X GET -s \"http://localhost:8080/api/v1/persons/?name=Sit\u0026sort_key=name\"\n```\n```bash\n[\n  {\n    \"id\": 1,\n    \"name\": \"Sit Amet\"\n  }\n]\n```\n\n#### Jobs\n\nListing jobs:\n```bash\ncurl --cookie-jar \"/tmp/london\" -X GET -s \"http://localhost:8080/api/v1/jobs/?limit=2\u0026offset=0\"\n```\n```bash\n[\n  {\n    \"id\": 12,\n    \"createdAt\": null,\n    \"endedAt\": \"2022-11-25T22:54:34.484+00:00\",\n    \"startedAt\": \"2022-11-25T22:54:34.461+00:00\",\n    \"status\": \"FAILED\",\n    \"message\": \"Person now found!\"\n  },\n  {\n    \"id\": 13,\n    \"createdAt\": null,\n    \"endedAt\": \"2022-11-25T22:58:39.070+00:00\",\n    \"startedAt\": \"2022-11-25T22:58:39.045+00:00\",\n    \"status\": \"FAILED\",\n    \"message\": \"Person now found!\"\n  },\n  {\n    \"id\": 11,\n    \"createdAt\": null,\n    \"endedAt\": \"2022-11-25T22:54:11.839+00:00\",\n    \"startedAt\": \"2022-11-25T22:54:11.819+00:00\",\n    \"status\": \"FAILED\",\n    \"message\": \"Person now found!\"\n  }\n]\n```\n\n#### Properties\n\nRetrieving a non-existent Propertes:\n```bash\ncurl --cookie-jar \"/tmp/london\" -s http://localhost:8080/api/v1/properties/123/\n```\n```bash\n{\n  \"timestamp\": \"2022-11-25T14:58:55.492+00:00\",\n  \"type\": \"com.martincastroalvarez.london.PropertyNotFoundError\",\n  \"message\": null\n}\n```\n\nCreating a Property:\n```bash\ncurl --cookie-jar \"/tmp/london\" -X POST -s http://localhost:8080/api/v1/properties/ -H 'content-type: application/json' -d '{\"name\": \"Lorem Ipsum\", \"ownerId\": 1}'\n```\n```bash\n{\n  \"id\" : 10,\n  \"owner\" : {\n    \"id\" : 1,\n    \"name\" : \"Sit Amet\"\n  },\n  \"name\" : \"Lorem Ipsum\"\n}\n```\n\nListing existing properties:\n```bash\ncurl --cookie-jar \"/tmp/london\" -X GET -s \"http://localhost:8080/api/v1/properties/?sort_key=name\"\n```\n```bash\n[\n  {\n    \"id\" : 10,\n    \"owner\" : {\n      \"id\" : 1,\n      \"name\" : \"Sit Amet\"\n    },\n    \"name\" : \"Lorem Ipsum\"\n  }\n]\n```\n\nUpdating an existing Property:\n```bash\ncurl --cookie-jar \"/tmp/london\" -X PUT -s http://localhost:8080/api/v1/properties/1 -H 'content-type: application/json' -d '{\"name\": \"Sit Amet\", \"ownerId\": 2}'\n```\n```bash\n{\n  \"id\" : 10,\n  \"owner\" : {\n    \"id\" : 2,\n    \"name\" : \"Nisman\"\n  },\n  \"name\" : \"Sit Amet\"\n}\n```\n\nGetting Property details:\n```bash\ncurl --cookie-jar \"/tmp/london\" -s http://localhost:8080/api/v1/properties/1\n```\n```bash\n{\n  \"id\" : 2,\n  \"name\" : \"Nisman\"\n}\n```\n\nDeleting a Property:\n```bash\ncurl --cookie-jar \"/tmp/london\" -X DELETE -s http://localhost:8080/api/v1/properties/10\n```\n```bash\n{\n  \"id\" : 43,\n  \"createdAt\" : null,\n  \"endedAt\" : null,\n  \"startedAt\" : null,\n  \"status\" : null,\n  \"message\" : null\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartincastroalvarez%2Fjava-spring-boot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmartincastroalvarez%2Fjava-spring-boot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartincastroalvarez%2Fjava-spring-boot/lists"}