diff --git a/pom.xml b/pom.xml
index 1c8dd9c27..d6e746855 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,225 +1,237 @@
- 4.0.0
-
- org.springframework.boot
- spring-boot-starter-parent
- 3.3.1
-
-
- com.hng
- hng-java-boilerplate
- 0.0.1-SNAPSHOT
- hng-java-boilerplate
- Hng java boiler plate
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 17
-
-
-
- org.springframework.boot
- spring-boot-starter-data-jpa
-
-
- org.springframework.boot
- spring-boot-starter-security
-
-
- org.springframework.boot
- spring-boot-devtools
- runtime
- true
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.springframework.boot
- spring-boot-starter-actuator
-
-
- io.micrometer
- micrometer-registry-prometheus
- 1.10.0
-
-
- org.flywaydb
- flyway-core
-
-
- org.flywaydb
- flyway-database-postgresql
-
-
- org.postgresql
- postgresql
- runtime
-
-
- org.projectlombok
- lombok
- true
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
- org.springframework.security
- spring-security-test
- test
-
-
- com.fasterxml.jackson.core
- jackson-databind
- 2.16.1
-
-
- com.fasterxml.jackson.core
- jackson-core
- 2.16.1
-
-
- com.fasterxml.jackson.core
- jackson-annotations
- 2.16.1
-
-
- io.jsonwebtoken
- jjwt-impl
- 0.12.3
- runtime
-
-
- io.jsonwebtoken
- jjwt-api
- 0.12.3
-
-
- io.jsonwebtoken
- jjwt-jackson
- 0.12.3
- runtime
-
-
- org.springframework.boot
- spring-boot-starter-validation
-
-
- org.junit.jupiter
- junit-jupiter-engine
- 5.10.2
- test
-
-
- org.junit.jupiter
- junit-jupiter-api
- 5.10.2
- test
-
-
- org.mockito
- mockito-core
- 5.12.0
- test
-
-
- org.mapstruct
- mapstruct
- 1.4.2.Final
-
-
- org.mapstruct
- mapstruct-processor
- 1.4.2.Final
- provided
-
-
- io.rest-assured
- rest-assured
- test
-
-
- jakarta.validation
- jakarta.validation-api
- 3.0.2
-
-
- org.hibernate.validator
- hibernate-validator
- 8.0.1.Final
-
-
- org.springdoc
- springdoc-openapi-starter-webmvc-ui
- 2.5.0
-
-
- org.springframework.boot
- spring-boot-starter-amqp
-
-
- org.springframework.boot
- spring-boot-starter-mail
-
-
- com.restfb
- restfb
- 2024.9.0
-
-
- com.google.api-client
- google-api-client
- 2.6.0
-
-
- com.beust
- jcommander
- 1.82
-
-
- com.stripe
- stripe-java
- 26.7.0
-
-
- dev.samstevens.totp
- totp
- 1.7.1
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.11.0
-
- 17
- 17
-
-
-
-
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.3.1
+
+
+ com.hng
+ hng-java-boilerplate
+ 0.0.1-SNAPSHOT
+ hng-java-boilerplate
+ Hng java boiler plate
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 17
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+ 1.10.0
+
+
+ org.flywaydb
+ flyway-core
+
+
+ org.flywaydb
+ flyway-database-postgresql
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.16.1
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ 2.16.1
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ 2.16.1
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.12.3
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.12.3
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.12.3
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.10.2
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.10.2
+ test
+
+
+ org.mockito
+ mockito-core
+ 5.12.0
+ test
+
+
+ org.mapstruct
+ mapstruct
+ 1.4.2.Final
+
+
+ org.mapstruct
+ mapstruct-processor
+ 1.4.2.Final
+ provided
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+ jakarta.validation
+ jakarta.validation-api
+ 3.0.2
+
+
+ org.hibernate.validator
+ hibernate-validator
+ 8.0.1.Final
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.5.0
+
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+
+
+ org.springframework.boot
+ spring-boot-starter-mail
+
+
+ com.restfb
+ restfb
+ 2024.9.0
+
+
+ com.google.api-client
+ google-api-client
+ 2.6.0
+
+
+ com.beust
+ jcommander
+ 1.82
+
+
+ com.stripe
+ stripe-java
+ 26.7.0
+
+
+ dev.samstevens.totp
+ totp
+ 1.7.1
+
+
+ org.springframework.cloud
+ spring-cloud-starter-aws
+ 2.2.6.RELEASE
+
+
+ software.amazon.awssdk
+ s3
+ 2.30.30
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+ 17
+ 17
+
+
+
+
\ No newline at end of file
diff --git a/profile_photos/6e6bbd89-a83d-44a8-994f-9949c48b0f59_oskar-smethurst-B1GtwanCbiw-unsplash.jpg b/profile_photos/6e6bbd89-a83d-44a8-994f-9949c48b0f59_oskar-smethurst-B1GtwanCbiw-unsplash.jpg
new file mode 100644
index 000000000..f249c15cd
Binary files /dev/null and b/profile_photos/6e6bbd89-a83d-44a8-994f-9949c48b0f59_oskar-smethurst-B1GtwanCbiw-unsplash.jpg differ
diff --git a/profile_photos/9f947e39-fe38-44dd-85d3-927253847576_oskar-smethurst-B1GtwanCbiw-unsplash.jpg b/profile_photos/9f947e39-fe38-44dd-85d3-927253847576_oskar-smethurst-B1GtwanCbiw-unsplash.jpg
new file mode 100644
index 000000000..f249c15cd
Binary files /dev/null and b/profile_photos/9f947e39-fe38-44dd-85d3-927253847576_oskar-smethurst-B1GtwanCbiw-unsplash.jpg differ
diff --git a/profile_photos/c3ae0d7a-fecb-4145-9992-be8b8c4f1e2a_oskar-smethurst-B1GtwanCbiw-unsplash.jpg b/profile_photos/c3ae0d7a-fecb-4145-9992-be8b8c4f1e2a_oskar-smethurst-B1GtwanCbiw-unsplash.jpg
new file mode 100644
index 000000000..f249c15cd
Binary files /dev/null and b/profile_photos/c3ae0d7a-fecb-4145-9992-be8b8c4f1e2a_oskar-smethurst-B1GtwanCbiw-unsplash.jpg differ
diff --git a/profile_photos/c72cde4e-e8a6-46a8-a010-5b8a8d185bc9_test.jpg b/profile_photos/c72cde4e-e8a6-46a8-a010-5b8a8d185bc9_test.jpg
new file mode 100644
index 000000000..016a09f16
--- /dev/null
+++ b/profile_photos/c72cde4e-e8a6-46a8-a010-5b8a8d185bc9_test.jpg
@@ -0,0 +1 @@
+dummy image content
\ No newline at end of file
diff --git a/profile_photos/d53189d2-ae88-4bee-a4ac-d71d1994bfe1_oskar-smethurst-B1GtwanCbiw-unsplash.jpg b/profile_photos/d53189d2-ae88-4bee-a4ac-d71d1994bfe1_oskar-smethurst-B1GtwanCbiw-unsplash.jpg
new file mode 100644
index 000000000..f249c15cd
Binary files /dev/null and b/profile_photos/d53189d2-ae88-4bee-a4ac-d71d1994bfe1_oskar-smethurst-B1GtwanCbiw-unsplash.jpg differ
diff --git a/profile_photos/f28a3ff5-7685-4feb-acae-aaafa0946da2_oskar-smethurst-B1GtwanCbiw-unsplash.jpg b/profile_photos/f28a3ff5-7685-4feb-acae-aaafa0946da2_oskar-smethurst-B1GtwanCbiw-unsplash.jpg
new file mode 100644
index 000000000..f249c15cd
Binary files /dev/null and b/profile_photos/f28a3ff5-7685-4feb-acae-aaafa0946da2_oskar-smethurst-B1GtwanCbiw-unsplash.jpg differ
diff --git a/profile_photos/ff8588cb-395e-4168-a069-8c108280fe6b_test.jpg b/profile_photos/ff8588cb-395e-4168-a069-8c108280fe6b_test.jpg
new file mode 100644
index 000000000..016a09f16
--- /dev/null
+++ b/profile_photos/ff8588cb-395e-4168-a069-8c108280fe6b_test.jpg
@@ -0,0 +1 @@
+dummy image content
\ No newline at end of file
diff --git a/src/main/java/hng_java_boilerplate/config/AwsConfig.java b/src/main/java/hng_java_boilerplate/config/AwsConfig.java
new file mode 100644
index 000000000..86c8ebf71
--- /dev/null
+++ b/src/main/java/hng_java_boilerplate/config/AwsConfig.java
@@ -0,0 +1,32 @@
+package hng_java_boilerplate.config;
+
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.AmazonS3ClientBuilder;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class AwsConfig {
+
+ @Value("${aws.s3.access-key}")
+ private String accessKey;
+
+ @Value("${aws.s3.secret-key}")
+ private String secretKey;
+
+ @Value("${aws.s3.region}")
+ private String region;
+
+ @Bean
+ public AmazonS3 amazonS3() {
+ BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey);
+ return AmazonS3ClientBuilder.standard()
+ .withRegion(region)
+ .withCredentials(new AWSStaticCredentialsProvider(awsCreds))
+ .build();
+ }
+}
+
diff --git a/src/main/java/hng_java_boilerplate/config/WebSecurityConfig.java b/src/main/java/hng_java_boilerplate/config/WebSecurityConfig.java
index 9af46f59e..f467e71b8 100644
--- a/src/main/java/hng_java_boilerplate/config/WebSecurityConfig.java
+++ b/src/main/java/hng_java_boilerplate/config/WebSecurityConfig.java
@@ -107,7 +107,8 @@ public SecurityFilterChain httpSecurity(HttpSecurity httpSecurity) throws Except
"/api/v1/categories",
"/api/v1/payment/plans",
"/api/v1/payment/webhook",
- "/api/v1/notification-settings"
+ "/api/v1/notification-settings",
+ "/api/v1/profile/upload-image"
).permitAll()
.requestMatchers(
diff --git a/src/main/java/hng_java_boilerplate/profile/controller/ProfileController.java b/src/main/java/hng_java_boilerplate/profile/controller/ProfileController.java
index 5e5432842..c2203e6a7 100644
--- a/src/main/java/hng_java_boilerplate/profile/controller/ProfileController.java
+++ b/src/main/java/hng_java_boilerplate/profile/controller/ProfileController.java
@@ -10,16 +10,21 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import java.io.IOException;
import java.util.Optional;
@RestController
@RequestMapping("/api/v1/profile")
@Tag(name = "User Profile Management", description = "APIs for managing user profiles")
+@CrossOrigin("*")
+@Slf4j
public class ProfileController {
private final ProfileService profileService;
@@ -54,4 +59,10 @@ public ResponseEntity deactivateUser(@RequestBody @Valid
public ResponseEntity getUserProfile(@PathVariable String userId) {
return ResponseEntity.ok(profileService.getUserProfile(userId));
}
+
+ @PostMapping("/upload-image")
+ public ResponseEntity> updateProfilePicture(@RequestParam("image") MultipartFile file) throws IOException {
+ log.info("New Profile Image received");
+ return profileService.uploadProfileImage(file);
+ }
}
diff --git a/src/main/java/hng_java_boilerplate/profile/dto/response/ProfilePictureResponse.java b/src/main/java/hng_java_boilerplate/profile/dto/response/ProfilePictureResponse.java
new file mode 100644
index 000000000..33c81acd4
--- /dev/null
+++ b/src/main/java/hng_java_boilerplate/profile/dto/response/ProfilePictureResponse.java
@@ -0,0 +1,13 @@
+package hng_java_boilerplate.profile.dto.response;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+
+public class ProfilePictureResponse {
+ private boolean success;
+ private String message;
+ private String imageUrl;
+}
diff --git a/src/main/java/hng_java_boilerplate/profile/service/ProfileService.java b/src/main/java/hng_java_boilerplate/profile/service/ProfileService.java
index 8999966cc..1bdae19da 100644
--- a/src/main/java/hng_java_boilerplate/profile/service/ProfileService.java
+++ b/src/main/java/hng_java_boilerplate/profile/service/ProfileService.java
@@ -4,11 +4,16 @@
import hng_java_boilerplate.profile.dto.request.UpdateUserProfileDto;
import hng_java_boilerplate.profile.dto.response.DeactivateUserResponse;
import hng_java_boilerplate.profile.dto.response.ProfileResponse;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.Authentication;
+import org.springframework.web.multipart.MultipartFile;
+import java.io.IOException;
import java.util.Optional;
public interface ProfileService {
public DeactivateUserResponse deactivateUser(DeactivateUserRequest request);
Optional> updateUserProfile(String userId, UpdateUserProfileDto updateUserProfileDto);
ProfileResponse getUserProfile(String userId);
+ ResponseEntity> uploadProfileImage(MultipartFile file) throws IOException;
}
diff --git a/src/main/java/hng_java_boilerplate/profile/serviceImpl/ProfileServiceImpl.java b/src/main/java/hng_java_boilerplate/profile/serviceImpl/ProfileServiceImpl.java
index 6a79b858e..036f89a21 100644
--- a/src/main/java/hng_java_boilerplate/profile/serviceImpl/ProfileServiceImpl.java
+++ b/src/main/java/hng_java_boilerplate/profile/serviceImpl/ProfileServiceImpl.java
@@ -1,13 +1,13 @@
package hng_java_boilerplate.profile.serviceImpl;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.model.ObjectMetadata;
+import com.amazonaws.services.s3.model.PutObjectRequest;
import hng_java_boilerplate.exception.BadRequestException;
import hng_java_boilerplate.exception.NotFoundException;
import hng_java_boilerplate.profile.dto.request.DeactivateUserRequest;
import hng_java_boilerplate.profile.dto.request.UpdateUserProfileDto;
-import hng_java_boilerplate.profile.dto.response.DeactivateUserResponse;
-import hng_java_boilerplate.profile.dto.response.ProfileDto;
-import hng_java_boilerplate.profile.dto.response.ProfileResponse;
-import hng_java_boilerplate.profile.dto.response.ProfileUpdateResponseDto;
+import hng_java_boilerplate.profile.dto.response.*;
import hng_java_boilerplate.profile.entity.Profile;
import hng_java_boilerplate.profile.repository.ProfileRepository;
import hng_java_boilerplate.profile.service.ProfileService;
@@ -15,10 +15,21 @@
import hng_java_boilerplate.user.repository.UserRepository;
import hng_java_boilerplate.user.service.UserService;
import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
-
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Optional;
+import java.util.UUID;
@Service
@RequiredArgsConstructor
@@ -27,6 +38,24 @@ public class ProfileServiceImpl implements ProfileService {
private final UserRepository userRepository;
private final ProfileRepository profileRepository;
+ private final AmazonS3 amazonS3;
+
+ @Value("${aws.s3.bucket-name}")
+ private String bucketName;
+
+ public void uploadFileToS3(MultipartFile file, String keyName) {
+ try (InputStream inputStream = file.getInputStream()) {
+ ObjectMetadata metadata = new ObjectMetadata();
+ metadata.setContentLength(file.getSize());
+ metadata.setContentType(file.getContentType());
+
+ amazonS3.putObject(new PutObjectRequest(bucketName, keyName, inputStream, metadata));
+ } catch (IOException e) {
+ throw new RuntimeException("Error uploading file to S3", e);
+ }
+ }
+
+
@Override
public DeactivateUserResponse deactivateUser(DeactivateUserRequest request) {
User authUser = userService.getLoggedInUser();
@@ -37,7 +66,8 @@ public DeactivateUserResponse deactivateUser(DeactivateUserRequest request) {
throw new BadRequestException("User has been deactivated");
}
- if (!confirmation.equals("true")) throw new BadRequestException("Confirmation needs to be true for deactivation");
+ if (!confirmation.equals("true"))
+ throw new BadRequestException("Confirmation needs to be true for deactivation");
authUser.setIsDeactivated(true);
userRepository.save(authUser);
@@ -50,30 +80,30 @@ public DeactivateUserResponse deactivateUser(DeactivateUserRequest request) {
@Override
public Optional> updateUserProfile(String id, UpdateUserProfileDto updateUserProfileDto) {
- Optional user = userRepository.findById(id);
- if (user.isPresent()) {
- Profile profile = user.get().getProfile();
-
- profile.setFirstName(updateUserProfileDto.getFirstName());
- profile.setLastName(updateUserProfileDto.getLastName());
- profile.setJobTitle(updateUserProfileDto.getJobTitle());
- profile.setPronouns(updateUserProfileDto.getPronouns());
- profile.setJobTitle(updateUserProfileDto.getJobTitle());
- profile.setDepartment(updateUserProfileDto.getDepartment());
- profile.setSocial(updateUserProfileDto.getSocial());
- profile.setBio(updateUserProfileDto.getBio());
- profile.setPhone(updateUserProfileDto.getPhoneNumber());
- profile.setAvatarUrl(updateUserProfileDto.getAvatarUrl());
-
- profile = profileRepository.save(profile);
- return Optional.of(ProfileUpdateResponseDto.builder()
- .statusCode(HttpStatus.OK.value())
- .message("Profile updated successfully")
- .data(profile)
- .build()
- );
- }
- throw new NotFoundException("User not found");
+ Optional user = userRepository.findById(id);
+ if (user.isPresent()) {
+ Profile profile = user.get().getProfile();
+
+ profile.setFirstName(updateUserProfileDto.getFirstName());
+ profile.setLastName(updateUserProfileDto.getLastName());
+ profile.setJobTitle(updateUserProfileDto.getJobTitle());
+ profile.setPronouns(updateUserProfileDto.getPronouns());
+ profile.setJobTitle(updateUserProfileDto.getJobTitle());
+ profile.setDepartment(updateUserProfileDto.getDepartment());
+ profile.setSocial(updateUserProfileDto.getSocial());
+ profile.setBio(updateUserProfileDto.getBio());
+ profile.setPhone(updateUserProfileDto.getPhoneNumber());
+ profile.setAvatarUrl(updateUserProfileDto.getAvatarUrl());
+
+ profile = profileRepository.save(profile);
+ return Optional.of(ProfileUpdateResponseDto.builder()
+ .statusCode(HttpStatus.OK.value())
+ .message("Profile updated successfully")
+ .data(profile)
+ .build()
+ );
+ }
+ throw new NotFoundException("User not found");
}
@Override
@@ -97,4 +127,28 @@ public ProfileResponse getUserProfile(String userId) {
return new ProfileResponse(200, "user profile", profileDto);
}
-}
\ No newline at end of file
+
+ @Override
+ public ResponseEntity uploadProfileImage(MultipartFile file) {
+ if (file.isEmpty() || !isValidImage(file)) {
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST)
+ .body(new ProfilePictureResponse(false, "Invalid file type or missing image. Only JPG or JPEG formats are allowed.", null));
+ }
+ try {
+ String filename = UUID.randomUUID() + "_" + file.getOriginalFilename();
+ uploadFileToS3(file, filename);
+
+ String fileUrl = amazonS3.getUrl(bucketName, filename).toString();
+ return ResponseEntity.ok(new ProfilePictureResponse(true, "Profile image uploaded successfully", fileUrl));
+ } catch (Exception e) {
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
+ .body(new ProfilePictureResponse(false, "An error occurred while uploading your profile image. Please try again later.", null));
+ }
+ }
+
+
+ private boolean isValidImage(MultipartFile file) {
+ String contentType = file.getContentType();
+ return contentType != null && (contentType.equals("image/jpeg") || contentType.equals("image/jpg"));
+ }
+}
diff --git a/src/main/java/hng_java_boilerplate/user/controller/AuthController.java b/src/main/java/hng_java_boilerplate/user/controller/AuthController.java
index 964006423..0519b6ea7 100644
--- a/src/main/java/hng_java_boilerplate/user/controller/AuthController.java
+++ b/src/main/java/hng_java_boilerplate/user/controller/AuthController.java
@@ -18,6 +18,7 @@
import org.springframework.web.bind.annotation.*;
@RestController
+@CrossOrigin("*")
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
@Tag(name="Authentication")
diff --git a/src/main/resources/application-example.properties b/src/main/resources/application-example.properties
index 97212a210..225ab3ed3 100644
--- a/src/main/resources/application-example.properties
+++ b/src/main/resources/application-example.properties
@@ -76,3 +76,11 @@ flutterwave.secret.key=
stripe.api.key=
client.url=
stripe.secret.key=
+
+# AWS S3 Configuration
+aws.s3.bucket-name=
+aws.s3.region=
+aws.s3.access-key=
+aws.s3.secret-key=
+
+
diff --git a/src/test/java/hng_java_boilerplate/profile/controller/ProfileControllerTest.java b/src/test/java/hng_java_boilerplate/profile/controller/ProfileControllerTest.java
index ca891d31f..688fb70b6 100644
--- a/src/test/java/hng_java_boilerplate/profile/controller/ProfileControllerTest.java
+++ b/src/test/java/hng_java_boilerplate/profile/controller/ProfileControllerTest.java
@@ -13,7 +13,10 @@
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.web.multipart.MultipartFile;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@@ -62,4 +65,6 @@ void shouldDeactivateUser() throws Exception {
.andExpect(jsonPath("$.status_code").value(200))
.andExpect(jsonPath("$.message").value(response.message()));
}
+
+
}
\ No newline at end of file
diff --git a/src/test/java/hng_java_boilerplate/profile/serviceImpl/ProfileServiceImplTest.java b/src/test/java/hng_java_boilerplate/profile/serviceImpl/ProfileServiceImplTest.java
index cbebdaf9c..79af2ef71 100644
--- a/src/test/java/hng_java_boilerplate/profile/serviceImpl/ProfileServiceImplTest.java
+++ b/src/test/java/hng_java_boilerplate/profile/serviceImpl/ProfileServiceImplTest.java
@@ -14,6 +14,7 @@
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.web.multipart.MultipartFile;
import java.util.Optional;
@@ -136,4 +137,6 @@ void shouldGetUserProfile() {
verify(userRepository).findById(anyString());
}
+
+
}
\ No newline at end of file
diff --git a/src/test/java/hng_java_boilerplate/profile/serviceImpl/UserProfileUnitTest.java b/src/test/java/hng_java_boilerplate/profile/serviceImpl/UserProfileUnitTest.java
index fe358b2d5..a357ce0f6 100644
--- a/src/test/java/hng_java_boilerplate/profile/serviceImpl/UserProfileUnitTest.java
+++ b/src/test/java/hng_java_boilerplate/profile/serviceImpl/UserProfileUnitTest.java
@@ -2,6 +2,7 @@
import hng_java_boilerplate.exception.NotFoundException;
import hng_java_boilerplate.profile.dto.request.UpdateUserProfileDto;
+import hng_java_boilerplate.profile.dto.response.ProfilePictureResponse;
import hng_java_boilerplate.profile.dto.response.ProfileUpdateResponseDto;
import hng_java_boilerplate.profile.entity.Profile;
import hng_java_boilerplate.profile.repository.ProfileRepository;
@@ -15,6 +16,8 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.mock.web.MockMultipartFile;
import java.util.Optional;
@@ -80,4 +83,33 @@ public void test_that_updateUserProfile_returns_error_with_status_400_when_user_
.hasMessage("User not found");
}
+ @Test
+ public void test_uploadProfileImage_returns_successful_response() {
+ MockMultipartFile file = new MockMultipartFile(
+ "file", "test.jpg", "image/jpeg", "dummy image content".getBytes()
+ );
+
+ ResponseEntity response = underTest.uploadProfileImage(file);
+
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+ assertThat(response.getBody()).isNotNull();
+ assertThat(response.getBody().isSuccess()).isTrue();
+ assertThat(response.getBody().getMessage()).isEqualTo("Profile image uploaded successfully");
+ assertThat(response.getBody().getImageUrl()).isNotBlank();
+ }
+
+ @Test
+ public void test_uploadProfileImage_returns_error_for_invalid_file() {
+ MockMultipartFile file = new MockMultipartFile(
+ "file", "test.txt", "text/plain", "invalid file content".getBytes()
+ );
+
+ ResponseEntity response = underTest.uploadProfileImage(file);
+
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
+ assertThat(response.getBody()).isNotNull();
+ assertThat(response.getBody().isSuccess()).isFalse();
+ assertThat(response.getBody().getMessage()).isEqualTo("Invalid file type or missing image. Only JPG or JPEG formats are allowed.");
+ }
+
}