-
Notifications
You must be signed in to change notification settings - Fork 0
feat(getRandomUser): endpoint get Random user #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6ba4df2
8fecac6
69771e8
06ba29a
5bdb66c
5339826
86b3bac
ef40e33
216a8f8
b57fd22
89f6860
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| application-local.properties | ||
| .env | ||
| HELP.md | ||
| target/ | ||
| .mvn/wrapper/maven-wrapper.jar | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -58,6 +58,54 @@ cp src/test/resources/application-test.properties.template src/test/resources/ap | |||||
| ``` | ||||||
|
|
||||||
| > ⚠️ This file is also git-ignored. | ||||||
| But for credentials and local overrides, use: | ||||||
|
|
||||||
| ``` | ||||||
| src/main/resources/application-local.properties | ||||||
| ``` | ||||||
|
|
||||||
| ### Example for application.properties | ||||||
|
|
||||||
| ```properties | ||||||
| spring.application.name=spring_boot_java_random_user | ||||||
| springdoc.swagger-ui.path=/api | ||||||
| spring.datasource.url=jdbc:postgresql://localhost:5432/<database_name> | ||||||
| spring.datasource.driver-class-name=org.postgresql.Driver | ||||||
| ``` | ||||||
|
|
||||||
| ### Local credentials & security | ||||||
|
|
||||||
| To avoid exposing database credentials in source code, create a `.env` file at the project root. | ||||||
|
|
||||||
| Then, in `src/main/resources/application-local.properties`: | ||||||
|
|
||||||
| ```properties | ||||||
| spring.datasource.username=${DB_USER} | ||||||
| spring.datasource.password=${DB_PASSWORD} | ||||||
| ``` | ||||||
|
|
||||||
| Both `.env` and `application-local.properties` are in `.gitignore` (already set). | ||||||
|
|
||||||
|
Theo-lbg marked this conversation as resolved.
|
||||||
| To activate the local profile in IntelliJ or VS Code, add this environment variable to your run configuration: | ||||||
|
|
||||||
| ``` | ||||||
| SPRING_PROFILES_ACTIVE=local | ||||||
| ``` | ||||||
|
|
||||||
| This way, each developer can use their own credentials without risk of leaking them to GitHub. | ||||||
|
|
||||||
|
|
||||||
| > ⚠️ **Common startup error**: | ||||||
| > ``` | ||||||
| > Failed to configure a DataSource: 'url' attribute is not specified | ||||||
| > and no embedded datasource could be configured. | ||||||
| > Reason: Failed to determine a suitable driver class | ||||||
| > ``` | ||||||
| > This error occurs because the PostgreSQL driver is present in the dependencies but the datasource URL is not configured. | ||||||
| > **Fix**: either add the `spring.datasource.*` properties above, or exclude the DataSource auto-configuration if no database is needed: | ||||||
| > ```properties | ||||||
| > spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration | ||||||
| > ``` | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
|
|
@@ -231,11 +279,11 @@ This project consumes the public **Random User Generator** API: | |||||
|
|
||||||
| - [x] [Add Sonarqube in the project](https://github.com/XPEHO/spring_boot_java_random_user/issues/2) | ||||||
| - [x] [Add PostgreSQL database with docker](https://github.com/XPEHO/spring_boot_java_random_user/issues/6) | ||||||
| - [ ] [Add this endpoint GET /user/random](https://github.com/XPEHO/spring_boot_java_random_user/issues/5) | ||||||
| - [ ] [Add this endpoint GET /user/{id}](https://github.com/XPEHO/spring_boot_java_random_user/issues/8) | ||||||
| - [ ] [Add this endpoint PUT /user/{id}](https://github.com/XPEHO/spring_boot_java_random_user/issues/9) | ||||||
| - [ ] [Add this endpoint DELETE /user/{id}](https://github.com/XPEHO/spring_boot_java_random_user/issues/10) | ||||||
| - [ ] [Add this endpoint POST /user](https://github.com/XPEHO/spring_boot_java_random_user/issues/11) | ||||||
| - [X] [Add this endpoint get /user/random](https://github.com/XPEHO/spring_boot_java_random_user/issues/5) | ||||||
|
||||||
| - [X] [Add this endpoint get /user/random](https://github.com/XPEHO/spring_boot_java_random_user/issues/5) | |
| - [X] [Add this endpoint get /random-users](https://github.com/XPEHO/spring_boot_java_random_user/issues/5) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| package com.xpeho.spring_boot_java_random_user.data.converters; | ||
| import com.xpeho.spring_boot_java_random_user.data.models.api.RandomUserResultDAO; | ||
| import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity; | ||
| import com.xpeho.spring_boot_java_random_user.data.models.db.User; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
|
|
||
| @Service | ||
| public class UserConverter { | ||
| // Domain -> DAO | ||
| public User toDao(UserEntity entity) { | ||
| User user = new User(); | ||
| user.setId(entity.id()); | ||
| user.setGender(entity.gender()); | ||
| user.setFirstname(entity.firstname()); | ||
| user.setLastname(entity.lastname()); | ||
| user.setCivility(entity.civility()); | ||
| user.setEmail(entity.email()); | ||
| user.setPhone(entity.phone()); | ||
| user.setPicture(entity.picture()); | ||
| user.setNationality(entity.nat()); | ||
| return user; | ||
| } | ||
| // DAO -> Domain | ||
| public UserEntity toDomain(User user) { | ||
| return new UserEntity( | ||
|
Theo-lbg marked this conversation as resolved.
|
||
| user.getId(), | ||
| user.getGender(), | ||
| user.getFirstname(), | ||
| user.getLastname(), | ||
| user.getCivility(), | ||
| user.getEmail(), | ||
| user.getPhone(), | ||
| user.getPicture(), | ||
| user.getNationality() | ||
| ); | ||
|
Theo-lbg marked this conversation as resolved.
|
||
| } | ||
| // API -> Domain | ||
| public UserEntity fromApiModel(RandomUserResultDAO model) { | ||
| return new UserEntity( | ||
| null, | ||
| model.getGender(), | ||
| model.getFirstName(), | ||
| model.getLastName(), | ||
| null, | ||
| model.getEmail(), | ||
| model.getPhone(), | ||
| model.getImage(), | ||
| null | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package com.xpeho.spring_boot_java_random_user.data.models.api; | ||
|
|
||
| public class RandomUserNameDAO { | ||
|
Theo-lbg marked this conversation as resolved.
|
||
| private String title; | ||
| private String first; | ||
| private String last; | ||
|
|
||
| public String getTitle() { return title; } | ||
|
Comment on lines
+3
to
+8
|
||
| public void setTitle(String title) { this.title = title; } | ||
| public String getFirst() { return first; } | ||
| public void setFirst(String first) { this.first = first; } | ||
| public String getLast() { return last; } | ||
| public void setLast(String last) { this.last = last; } | ||
| } | ||
|
Theo-lbg marked this conversation as resolved.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package com.xpeho.spring_boot_java_random_user.data.models.api; | ||
|
|
||
| public class RandomUserPictureDAO { | ||
| private String medium; | ||
|
|
||
| public String getMedium() { | ||
|
Comment on lines
+3
to
+6
|
||
| return medium; | ||
| } | ||
|
|
||
| public void setMedium(String medium) { | ||
| this.medium = medium; | ||
| } | ||
| } | ||
|
Theo-lbg marked this conversation as resolved.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.xpeho.spring_boot_java_random_user.data.models.api; | ||
|
|
||
| import com.google.gson.annotations.SerializedName; | ||
| import java.util.List; | ||
|
|
||
| public class RandomUserResponse { | ||
| @SerializedName("users") | ||
| private List<RandomUserResultDAO> users; | ||
|
|
||
| public List<RandomUserResultDAO> getUsers() { | ||
| return users; | ||
| } | ||
|
|
||
| public void setUsers(List<RandomUserResultDAO> users) { | ||
| this.users = users; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.xpeho.spring_boot_java_random_user.data.models.api; | ||
|
|
||
| public class RandomUserResultDAO { | ||
|
Theo-lbg marked this conversation as resolved.
|
||
| private String gender; | ||
| private String firstName; | ||
| private String lastName; | ||
| private String email; | ||
| private String phone; | ||
| private String image; | ||
|
|
||
| public String getGender() { return gender; } | ||
| public void setGender(String gender) { this.gender = gender; } | ||
| public String getFirstName() { return firstName; } | ||
| public void setFirstName(String firstName) { this.firstName = firstName; } | ||
| public String getLastName() { return lastName; } | ||
| public void setLastName(String lastName) { this.lastName = lastName; } | ||
| public String getEmail() { return email; } | ||
| public void setEmail(String email) { this.email = email; } | ||
| public String getPhone() { return phone; } | ||
| public void setPhone(String phone) { this.phone = phone; } | ||
| public String getImage() { return image; } | ||
| public void setImage(String image) { this.image = image; } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| package com.xpeho.spring_boot_java_random_user.data.models.db; | ||
|
|
||
| import org.springframework.data.annotation.Id; | ||
| import org.springframework.data.relational.core.mapping.Column; | ||
| import org.springframework.data.relational.core.mapping.Table; | ||
|
|
||
| @Table("users") | ||
|
Theo-lbg marked this conversation as resolved.
|
||
| public class User { | ||
| @Id | ||
| private Long id; | ||
| private String gender; | ||
| @Column("firstname") | ||
| private String firstname; | ||
| @Column("lastname") | ||
| private String lastname; | ||
| private String civility; | ||
| private String email; | ||
| private String phone; | ||
| private String picture; | ||
| private String nationality; | ||
|
|
||
|
Theo-lbg marked this conversation as resolved.
|
||
| // Required by Spring Data JDBC to instantiate the entity via reflection | ||
| public User() { | ||
| // No initialization needed; fields are populated by Spring Data JDBC after instantiation | ||
| } | ||
|
|
||
| public Long getId() { return id; } | ||
| public void setId(Long id) { this.id = id; } | ||
| public String getGender() { return gender; } | ||
| public void setGender(String gender) { this.gender = gender; } | ||
| public String getFirstname() { return firstname; } | ||
| public void setFirstname(String firstname) { this.firstname = firstname; } | ||
| public String getLastname() { return lastname; } | ||
| public void setLastname(String lastname) { this.lastname = lastname; } | ||
| public String getCivility() { return civility; } | ||
| public void setCivility(String civility) { this.civility = civility; } | ||
| public String getEmail() { return email; } | ||
| public void setEmail(String email) { this.email = email; } | ||
| public String getPhone() { return phone; } | ||
| public void setPhone(String phone) { this.phone = phone; } | ||
| public String getPicture() { return picture; } | ||
| public void setPicture(String picture) { this.picture = picture; } | ||
| public String getNationality() { return nationality; } | ||
| public void setNationality(String nationality) { this.nationality = nationality; } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,32 @@ | ||||||||||||
| package com.xpeho.spring_boot_java_random_user.data.services; | ||||||||||||
|
|
||||||||||||
| import com.xpeho.spring_boot_java_random_user.data.sources.database.UserRepository; | ||||||||||||
| import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity; | ||||||||||||
| import com.xpeho.spring_boot_java_random_user.domain.services.UserService; | ||||||||||||
| import com.xpeho.spring_boot_java_random_user.data.models.db.User; | ||||||||||||
| import com.xpeho.spring_boot_java_random_user.data.converters.UserConverter; | ||||||||||||
| import org.springframework.stereotype.Service; | ||||||||||||
|
|
||||||||||||
| import java.util.List; | ||||||||||||
| import java.util.stream.StreamSupport; | ||||||||||||
|
|
||||||||||||
| @Service | ||||||||||||
|
|
||||||||||||
| public class UserServiceImpl implements UserService { | ||||||||||||
| private final UserRepository userRepository; | ||||||||||||
| private final UserConverter userConverter; | ||||||||||||
|
|
||||||||||||
| public UserServiceImpl(UserRepository userRepository, UserConverter userConverter) { | ||||||||||||
| this.userRepository = userRepository; | ||||||||||||
| this.userConverter = userConverter; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| @Override | ||||||||||||
| public List<UserEntity> saveAll(List<UserEntity> users) { | ||||||||||||
|
Theo-lbg marked this conversation as resolved.
|
||||||||||||
| public List<UserEntity> saveAll(List<UserEntity> users) { | |
| public List<UserEntity> saveAll(List<UserEntity> users) { | |
| if (users == null) { | |
| throw new IllegalArgumentException("users must not be null"); | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.xpeho.spring_boot_java_random_user.data.sources.api; | ||
|
|
||
| import com.xpeho.spring_boot_java_random_user.data.models.api.RandomUserResponse; | ||
| import retrofit2.Call; | ||
| import retrofit2.http.GET; | ||
| import retrofit2.http.Query; | ||
|
|
||
| public interface RandomUserApi { | ||
| @GET("/users") | ||
| Call<RandomUserResponse> getRandomUsers(@Query("limit") int limit); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package com.xpeho.spring_boot_java_random_user.data.sources.api; | ||
|
|
||
| import okhttp3.OkHttpClient; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.core.env.Environment; | ||
| import retrofit2.Retrofit; | ||
| import retrofit2.converter.gson.GsonConverterFactory; | ||
|
|
||
| @Configuration | ||
| public class RandomUserApiConfig { | ||
| @Bean | ||
| public Retrofit randomUserRetrofit(Environment env) { | ||
| String baseUrl = env.getProperty("randomuser.api.base-url", "https://dummyjson.com/"); | ||
| OkHttpClient client = new OkHttpClient.Builder().build(); | ||
| return new Retrofit.Builder() | ||
| .baseUrl(baseUrl) | ||
| .addConverterFactory(GsonConverterFactory.create()) | ||
|
Theo-lbg marked this conversation as resolved.
|
||
| .client(client) | ||
| .build(); | ||
| } | ||
|
|
||
| @Bean | ||
| public RandomUserApi randomUserApi(Retrofit randomUserRetrofit) { | ||
| return randomUserRetrofit.create(RandomUserApi.class); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package com.xpeho.spring_boot_java_random_user.data.sources.api; | ||
|
|
||
| import com.xpeho.spring_boot_java_random_user.data.models.api.RandomUserResponse; | ||
| import com.xpeho.spring_boot_java_random_user.data.models.api.RandomUserResultDAO; | ||
| import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity; | ||
| import com.xpeho.spring_boot_java_random_user.domain.services.RandomUserProvider; | ||
| import com.xpeho.spring_boot_java_random_user.data.converters.UserConverter; | ||
| import org.springframework.stereotype.Service; | ||
| import retrofit2.Response; | ||
|
|
||
| import java.io.IOException; | ||
| import java.util.List; | ||
|
|
||
| @Service | ||
| public class RandomUserProviderImpl implements RandomUserProvider { | ||
| private final RandomUserApi randomUserApi; | ||
| private final UserConverter userConverter; | ||
|
|
||
| public RandomUserProviderImpl(RandomUserApi randomUserApi, UserConverter userConverter) { | ||
| this.randomUserApi = randomUserApi; | ||
| this.userConverter = userConverter; | ||
| } | ||
|
|
||
| @Override | ||
| public List<UserEntity> fetchRandomUsers(int count) throws IOException { | ||
| Response<RandomUserResponse> response = randomUserApi.getRandomUsers(count).execute(); | ||
| if (!response.isSuccessful() || response.body() == null) { | ||
| throw new IOException("Failed to fetch users: " + response.code()); | ||
| } | ||
| List<RandomUserResultDAO> users = response.body().getUsers(); | ||
| if (users == null) { | ||
|
Theo-lbg marked this conversation as resolved.
|
||
| throw new IOException("Failed to parse users from response"); | ||
| } | ||
| return users.stream() | ||
| .map(userConverter::fromApiModel) | ||
| .toList(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.xpeho.spring_boot_java_random_user.data.sources.database; | ||
|
|
||
| import com.xpeho.spring_boot_java_random_user.data.models.db.User; | ||
| import org.springframework.data.repository.CrudRepository; | ||
|
|
||
| public interface UserRepository extends CrudRepository<User, Long> { | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.