diff --git a/README.md b/README.md
index 3a45c1a..bd9162d 100644
--- a/README.md
+++ b/README.md
@@ -155,6 +155,78 @@ Flyway is used to manage database migrations. The SQL scripts are located in `sr
- `id`: The ID of the user to reactivate
- **Response:** A `ResponseDto` object containing the result of the operation.
+
+ Get user profile
+
+- **URL:** `/api/users/profile/{userId}`
+- **Method:** `GET`
+- **Description:** Retrieve the profile information for a specific user.
+- **Path Parameters:**
+ - `id` (required): The ID of the user whose profile is to be retrieved.
+ - **Response:**
+ - **Status Code:** `200 OK`
+ - **Body:**
+ ```json
+ {
+ "status": "OK",
+ "success": true,
+ "data": {
+ "id": 160,
+ "firstName": "Teddy",
+ "lastName": "Johnson",
+ "title": null,
+ "description": null,
+ "hourlyRate": null,
+ "location": null
+ },
+ "error": null
+ }
+ ```
+
+
+ Update user profile
+
+- **URL:** `/api/users/profile/{id}`
+- **Method:** `PUT`
+- **Description:** Update the profile information for a specific user.
+- **Path Parameters:**
+ - `id` (required): The ID of the user whose currently logged in.
+ - **Request Body:**
+ - **Content-Type:** `application/json`
+ - **Body Example:**
+ ```json
+ {
+ "id": 160,
+ "firstName": "string",
+ "lastName": "string",
+ "title": "string",
+ "description": "string",
+ "hourlyRate": 0,
+ "location": "string"
+ }
+ ```
+- **Response:**
+ - **Status Code:** `200 OK`
+ - **Body Example:**
+ ```json
+ {
+ "status": "OK",
+ "success": true,
+ "data": {
+ "id": 160,
+ "firstName": "string",
+ "lastName": "string",
+ "title": "string",
+ "description": "string",
+ "hourlyRate": 0,
+ "location": "string"
+ },
+ "error": null
+ }
+
+ ```
+
+
### Password Management
diff --git a/src/main/java/com/activecourses/upwork/UpworkApplication.java b/src/main/java/com/activecourses/upwork/UpworkApplication.java
index 96ca35d..07b656c 100644
--- a/src/main/java/com/activecourses/upwork/UpworkApplication.java
+++ b/src/main/java/com/activecourses/upwork/UpworkApplication.java
@@ -6,6 +6,7 @@
@SpringBootApplication
@EnableJpaAuditing
+
public class UpworkApplication {
public static void main(String[] args) {
diff --git a/src/main/java/com/activecourses/upwork/controller/auth/AuthController.java b/src/main/java/com/activecourses/upwork/controller/auth/AuthController.java
index 73ece45..9de71dc 100644
--- a/src/main/java/com/activecourses/upwork/controller/auth/AuthController.java
+++ b/src/main/java/com/activecourses/upwork/controller/auth/AuthController.java
@@ -50,7 +50,7 @@ public ResponseEntity registerUser(@Valid @RequestBody Registration
description = "Login",
security = @SecurityRequirement(name = "")
)
- @PostMapping("login")
+ @PostMapping("login")
public ResponseEntity> login(@Valid @RequestBody LoginRequestDto loginRequestDto) {
ResponseDto responseDto = authService.login(loginRequestDto);
Map cookies = (Map) responseDto.getData();
diff --git a/src/main/java/com/activecourses/upwork/controller/auth/TokenController.java b/src/main/java/com/activecourses/upwork/controller/auth/TokenController.java
index 0e74b65..265ecc7 100644
--- a/src/main/java/com/activecourses/upwork/controller/auth/TokenController.java
+++ b/src/main/java/com/activecourses/upwork/controller/auth/TokenController.java
@@ -4,7 +4,7 @@
import com.activecourses.upwork.exception.TokenRefreshException;
import com.activecourses.upwork.model.User;
import com.activecourses.upwork.service.authentication.AuthService;
-import com.activecourses.upwork.service.authentication.RefreshTokenService;
+import com.activecourses.upwork.service.RefreshTokenService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
diff --git a/src/main/java/com/activecourses/upwork/controller/user/UserController.java b/src/main/java/com/activecourses/upwork/controller/user/UserController.java
index e076e50..f96e565 100644
--- a/src/main/java/com/activecourses/upwork/controller/user/UserController.java
+++ b/src/main/java/com/activecourses/upwork/controller/user/UserController.java
@@ -1,30 +1,30 @@
package com.activecourses.upwork.controller.user;
import com.activecourses.upwork.dto.ResponseDto;
+import com.activecourses.upwork.dto.user.UserProfileDto;
import com.activecourses.upwork.dto.user.UserResponseDto;
+import com.activecourses.upwork.service.user.UserProfileService;
import com.activecourses.upwork.service.user.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
+import org.springframework.web.bind.annotation.*;
+@Tag(name = "User", description = "User API")
@RestController
-@RequestMapping("/api/users")
@RequiredArgsConstructor
-@Tag(name = "User", description = "User API")
+@RequestMapping("/api/users/")
public class UserController {
-
private final UserService userService;
+ private final UserProfileService userProfileService;
// TODO: update it so than it can be sorted by multiple fields
@Operation(summary = "Get all users",
@@ -54,4 +54,37 @@ public ResponseEntity getAllUsers(
.status(HttpStatus.OK)
.build());
}
-}
\ No newline at end of file
+
+ @Operation(summary = "Get User Profile",
+ description = "Retrieve the profile information of the user specified by the userId.")
+ @GetMapping("/profile/{userId}")
+ public ResponseEntity> getUserProfile(@PathVariable int userId, HttpServletRequest httpRequest) {
+ try {
+ return userProfileService.getUserProfile(userId);
+ } catch (Exception e) {
+ return ResponseEntity.badRequest().body(ResponseDto.builder()
+ .status(HttpStatus.BAD_REQUEST)
+ .success(false)
+ .data(e.getMessage())
+ .build());
+ }
+ }
+
+ @Operation(summary = "Update User Profile",
+ description = "Update the profile information of the user specified by the userId")
+ @PutMapping("/profile/{userId}")
+ public ResponseEntity> updateUserProfile(
+ @PathVariable int userId,
+ @RequestBody @Valid UserProfileDto updateRequest) {
+ try {
+ return userProfileService.UpdateUserProfile(userId,updateRequest);
+ } catch (Exception e) {
+ return ResponseEntity.badRequest().body(ResponseDto.builder()
+ .status(HttpStatus.BAD_REQUEST)
+ .success(false)
+ .data(e.getMessage())
+ .build());
+ }
+
+ }
+}
diff --git a/src/main/java/com/activecourses/upwork/dto/user/UserProfileDto.java b/src/main/java/com/activecourses/upwork/dto/user/UserProfileDto.java
new file mode 100644
index 0000000..43cb4a9
--- /dev/null
+++ b/src/main/java/com/activecourses/upwork/dto/user/UserProfileDto.java
@@ -0,0 +1,27 @@
+package com.activecourses.upwork.dto.user;
+
+import jakarta.persistence.Id;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Builder;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@Builder
+public class UserProfileDto {
+ @Id
+ private int id;
+ @NotBlank(message = "First Name is required")
+ private String firstName;
+ @NotBlank(message = "Last Name is required")
+ private String lastName;
+
+ private String title;
+
+ private String description;
+
+ private BigDecimal hourlyRate;
+
+ private String location;
+}
diff --git a/src/main/java/com/activecourses/upwork/dto/user/UserResponseDto.java b/src/main/java/com/activecourses/upwork/dto/user/UserResponseDto.java
index f93d0cb..1a2a506 100644
--- a/src/main/java/com/activecourses/upwork/dto/user/UserResponseDto.java
+++ b/src/main/java/com/activecourses/upwork/dto/user/UserResponseDto.java
@@ -1,12 +1,14 @@
package com.activecourses.upwork.dto.user;
import lombok.AllArgsConstructor;
+import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
+@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserResponseDto {
diff --git a/src/main/java/com/activecourses/upwork/mapper/user/UserProfileDtoMapper.java b/src/main/java/com/activecourses/upwork/mapper/user/UserProfileDtoMapper.java
new file mode 100644
index 0000000..d4de4d8
--- /dev/null
+++ b/src/main/java/com/activecourses/upwork/mapper/user/UserProfileDtoMapper.java
@@ -0,0 +1,30 @@
+package com.activecourses.upwork.mapper.user;
+
+import com.activecourses.upwork.dto.user.UserProfileDto;
+import com.activecourses.upwork.mapper.Mapper;
+import com.activecourses.upwork.model.User;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Component;
+
+@Component
+@AllArgsConstructor
+public class UserProfileDtoMapper implements Mapper {
+ @Override
+ public UserProfileDto mapTo(User user) {
+ return UserProfileDto.builder()
+ .id(user.getId())
+ .title(user.getUserProfile().getTitle())
+ .description(user.getUserProfile().getDescription())
+ .hourlyRate(user.getUserProfile().getHourlyRate())
+ .location(user.getUserProfile().getLocation())
+ .firstName(user.getFirstName())
+ .lastName(user.getLastName())
+ .build();
+ }
+
+ @Override
+ public User mapFrom(UserProfileDto userProfileDto) {
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/activecourses/upwork/model/User.java b/src/main/java/com/activecourses/upwork/model/User.java
index 9c83263..7b310db 100644
--- a/src/main/java/com/activecourses/upwork/model/User.java
+++ b/src/main/java/com/activecourses/upwork/model/User.java
@@ -66,7 +66,8 @@ public class User implements UserDetails, Principal {
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL, optional = true)
private RefreshToken refreshToken;
-
+ @OneToOne(mappedBy = "user",cascade = CascadeType.ALL)
+ private UserProfile userProfile;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "user_roles",
diff --git a/src/main/java/com/activecourses/upwork/model/UserProfile.java b/src/main/java/com/activecourses/upwork/model/UserProfile.java
new file mode 100644
index 0000000..7b8151c
--- /dev/null
+++ b/src/main/java/com/activecourses/upwork/model/UserProfile.java
@@ -0,0 +1,38 @@
+package com.activecourses.upwork.model;
+
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import java.math.BigDecimal;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Entity
+@Builder
+@EntityListeners(AuditingEntityListener.class)
+@Table(name = "user_profiles")
+public class UserProfile {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Integer profileId;
+
+ private String title;
+
+ private String description;
+
+ @Column(precision = 19, scale = 2) // Example precision and scale
+ private BigDecimal hourlyRate;
+
+
+ private String location;
+
+ @OneToOne(cascade = CascadeType.ALL)
+ @JoinColumn(name = "user_id", unique = true)
+ private User user;
+
+}
diff --git a/src/main/java/com/activecourses/upwork/service/authentication/RefreshTokenService.java b/src/main/java/com/activecourses/upwork/service/RefreshTokenService.java
similarity index 96%
rename from src/main/java/com/activecourses/upwork/service/authentication/RefreshTokenService.java
rename to src/main/java/com/activecourses/upwork/service/RefreshTokenService.java
index 0b315c6..7485161 100644
--- a/src/main/java/com/activecourses/upwork/service/authentication/RefreshTokenService.java
+++ b/src/main/java/com/activecourses/upwork/service/RefreshTokenService.java
@@ -1,4 +1,4 @@
-package com.activecourses.upwork.service.authentication;
+package com.activecourses.upwork.service;
import com.activecourses.upwork.config.security.jwt.JwtService;
import com.activecourses.upwork.dto.ResponseDto;
@@ -78,6 +78,9 @@ public ResponseEntity refreshToken(HttpServletRequest request) {
private String getRefreshTokenFromRequest(HttpServletRequest request) {
return jwtService.getJwtRefreshFromCookies(request);
}
+ private String getTokenFromRequest(HttpServletRequest request) {
+ return jwtService.getJwtFromCookies(request);
+ }
private RefreshToken getRefreshTokenEntity(String refreshToken) {
return findByToken(refreshToken)
diff --git a/src/main/java/com/activecourses/upwork/service/authentication/AuthServiceImpl.java b/src/main/java/com/activecourses/upwork/service/authentication/AuthServiceImpl.java
index 914959b..218e245 100644
--- a/src/main/java/com/activecourses/upwork/service/authentication/AuthServiceImpl.java
+++ b/src/main/java/com/activecourses/upwork/service/authentication/AuthServiceImpl.java
@@ -8,9 +8,11 @@
import com.activecourses.upwork.mapper.Mapper;
import com.activecourses.upwork.model.RefreshToken;
import com.activecourses.upwork.model.User;
+import com.activecourses.upwork.model.UserProfile;
import com.activecourses.upwork.repository.user.UserRepository;
import com.activecourses.upwork.config.security.jwt.JwtService;
+import com.activecourses.upwork.service.RefreshTokenService;
import lombok.RequiredArgsConstructor;
import java.util.Map;
@@ -49,6 +51,7 @@ public class AuthServiceImpl implements AuthService {
public RegistrationResponseDto registerUser(RegistrationRequestDto registrationRequestDto) {
User user = userMapper.mapFrom(registrationRequestDto);
user.setPassword(passwordEncoder.encode(user.getPassword()));
+ user.setUserProfile(new UserProfile());
userRepository.save(user);
return RegistrationResponseDto
diff --git a/src/main/java/com/activecourses/upwork/service/user/ImplUserProfileService.java b/src/main/java/com/activecourses/upwork/service/user/ImplUserProfileService.java
new file mode 100644
index 0000000..6c34eb4
--- /dev/null
+++ b/src/main/java/com/activecourses/upwork/service/user/ImplUserProfileService.java
@@ -0,0 +1,73 @@
+package com.activecourses.upwork.service.user;
+
+import com.activecourses.upwork.dto.ResponseDto;
+import com.activecourses.upwork.dto.user.UserProfileDto;
+import com.activecourses.upwork.mapper.Mapper;
+import com.activecourses.upwork.model.User;
+import com.activecourses.upwork.model.UserProfile;
+import com.activecourses.upwork.repository.user.UserRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class ImplUserProfileService implements UserProfileService{
+ private final UserRepository userRepository;
+ private final Mapper userProfileMapper;
+ @Override
+ public ResponseEntity> getUserProfile(int id) {
+ User user = userRepository.findById(id).
+ orElseThrow(() -> new UsernameNotFoundException("user Not Found"));
+ UserProfileDto userProfileDto = userProfileMapper.mapTo(user);
+
+ return ResponseEntity.ok()
+ .body(ResponseDto.builder()
+ .status(HttpStatus.OK)
+ .success(true)
+ .data(userProfileDto)
+ .build());
+ }
+
+ public UserProfile createUserProfile(User user){
+ return UserProfile.builder()
+ .user(user).build();
+ }
+
+ @Override
+ public ResponseEntity> UpdateUserProfile(int userId, UserProfileDto updateRequest) {
+ User user = userRepository.findById(updateRequest.getId()).
+ orElseThrow(() -> new UsernameNotFoundException("user Not Found"));
+ // check if this user try to update another user profile
+ if(userId!=user.getId()){
+ throw new RuntimeException("this User can't update another user profile");
+ }
+ return ResponseEntity.ok()
+ .body(ResponseDto.builder()
+ .status(HttpStatus.OK)
+ .success(true)
+ .data(doUpdate(updateRequest,user))
+ .build());
+ }
+
+ private UserProfileDto doUpdate(UserProfileDto updateRequest, User user) {
+ //update fields that in user table
+ user.setFirstName(updateRequest.getFirstName());
+ user.setLastName(updateRequest.getLastName());
+
+ //get userProfile and update it.
+ UserProfile userProfile=user.getUserProfile();
+ userProfile.setDescription(updateRequest.getDescription());
+ userProfile.setLocation(updateRequest.getLocation());
+ userProfile.setTitle(userProfile.getTitle());
+ userProfile.setHourlyRate(updateRequest.getHourlyRate());
+
+ // update user in database
+ user.setUserProfile(userProfile);
+ userRepository.save(user);
+ return updateRequest;
+ }
+
+}
diff --git a/src/main/java/com/activecourses/upwork/service/user/UserProfileService.java b/src/main/java/com/activecourses/upwork/service/user/UserProfileService.java
new file mode 100644
index 0000000..0d50830
--- /dev/null
+++ b/src/main/java/com/activecourses/upwork/service/user/UserProfileService.java
@@ -0,0 +1,13 @@
+package com.activecourses.upwork.service.user;
+
+import com.activecourses.upwork.dto.user.UserProfileDto;
+import com.activecourses.upwork.model.User;
+import com.activecourses.upwork.model.UserProfile;
+import org.springframework.http.ResponseEntity;
+
+public interface UserProfileService {
+ ResponseEntity> getUserProfile(int id);
+ UserProfile createUserProfile(User user);
+
+ ResponseEntity> UpdateUserProfile(int userId, UserProfileDto updateRequest);
+}
diff --git a/src/main/java/com/activecourses/upwork/service/user/UserService.java b/src/main/java/com/activecourses/upwork/service/user/UserService.java
index 432fca0..15cba7b 100644
--- a/src/main/java/com/activecourses/upwork/service/user/UserService.java
+++ b/src/main/java/com/activecourses/upwork/service/user/UserService.java
@@ -1,8 +1,11 @@
package com.activecourses.upwork.service.user;
import com.activecourses.upwork.dto.user.UserResponseDto;
+import com.activecourses.upwork.model.User;
public interface UserService {
UserResponseDto getAllUsers(int pageNo, int pageSize, String sortBy, String sortDir);
+ User findByEmail(String email);
+
}
diff --git a/src/main/java/com/activecourses/upwork/service/user/userServiceImpl.java b/src/main/java/com/activecourses/upwork/service/user/userServiceImpl.java
index 5bac67b..72afd00 100644
--- a/src/main/java/com/activecourses/upwork/service/user/userServiceImpl.java
+++ b/src/main/java/com/activecourses/upwork/service/user/userServiceImpl.java
@@ -5,24 +5,24 @@
import com.activecourses.upwork.mapper.user.UserDtoMapper;
import com.activecourses.upwork.model.User;
import com.activecourses.upwork.repository.user.UserRepository;
+import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
-public class userServiceImpl implements UserService {
+@RequiredArgsConstructor
+public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final UserDtoMapper userDtoMapper;
- public userServiceImpl(UserRepository userRepository, UserDtoMapper userDtoMapper) {
- this.userRepository = userRepository;
- this.userDtoMapper = userDtoMapper;
- }
+
@Override
public UserResponseDto getAllUsers(int pageNo, int pageSize, String sortBy, String sortDir) {
@@ -54,4 +54,11 @@ public UserResponseDto getAllUsers(int pageNo, int pageSize, String sortBy, Stri
userResponseDto.setLast(pagedResult.isLast());
return userResponseDto;
}
+ @Override
+ public User findByEmail(String email) {
+ return userRepository.findByEmail(email).
+ orElseThrow(()->new UsernameNotFoundException("User Not Found"));
+ }
+
+
}
diff --git a/src/main/resources/db/migration/V5_Create_user_profile_to_all_inserted_users.sql b/src/main/resources/db/migration/V5_Create_user_profile_to_all_inserted_users.sql
new file mode 100644
index 0000000..db51e7b
--- /dev/null
+++ b/src/main/resources/db/migration/V5_Create_user_profile_to_all_inserted_users.sql
@@ -0,0 +1,5 @@
+INSERT INTO user_profiles (user_id)
+SELECT u.id
+FROM users u
+ LEFT JOIN user_profiles up ON u.id = up.user_id
+WHERE up.user_id IS NULL;
\ No newline at end of file