responseData, Context context) {
+
+ MessageTemplateType inviteMessageTemplate = MessageTemplateType.builder()
+ .emailMessage(String.format("""
+
+
+ Hello Sir/Madam
+ Welcome to our Task Management System!
+ Your username is: {username} and your temporary password is: {####}
+ Click here to sign in.
+
+
+ """, loginUrl))
+ .emailSubject("Task Management System")
+ .build();
+
+ cognitoIdentityProviderClient.updateUserPool(UpdateUserPoolRequest.builder()
+ .userPoolId(userPoolId)
+ .adminCreateUserConfig(AdminCreateUserConfigType.builder()
+ .inviteMessageTemplate(inviteMessageTemplate)
+ .build())
+ .build());
+
+ context.getLogger().log("UserPool updated successfully");
+ responseData.put("Message", "UserPool updated successfully");
+ }
+
+
+ private void createAdminUser(String adminEmail, String adminUsername, String userPoolId, Map responseData, Context context) {
+
+ if (adminEmail != null && !adminEmail.trim().equals("None") && !adminEmail.isEmpty()) {
+ try {
+ cognitoIdentityProviderClient.adminGetUser(AdminGetUserRequest.builder()
+ .userPoolId(userPoolId)
+ .username(adminEmail)
+ .build());
+
+ context.getLogger().log("User already exists: " + adminEmail);
+ responseData.put("Message", "User already exists: " + adminEmail);
+ } catch (UserNotFoundException e) {
+ List userAttributes = new ArrayList<>();
+ userAttributes.add(AttributeType.builder().name("email").value(adminEmail).build());
+ userAttributes.add(AttributeType.builder().name("name").value(adminUsername).build());
+ userAttributes.add(AttributeType.builder().name("custom:role").value(Role.ADMIN.toString()).build());
+ userAttributes.add(AttributeType.builder().name("email_verified").value("true").build());
+
+ AdminCreateUserRequest createUserRequest = AdminCreateUserRequest.builder()
+ .userPoolId(userPoolId)
+ .username(adminEmail)
+ .userAttributes(userAttributes)
+ .temporaryPassword(PasswordGenerator.generatePassword())
+ .desiredDeliveryMediums(DeliveryMediumType.EMAIL)
+ .build();
+
+ cognitoIdentityProviderClient.adminCreateUser(createUserRequest);
+
+ }
+ }
+
+
+ }
+
+ private void configureNotificationTopicsSubscription(String snsTopic, String adminEmail, Context context) {
+ subscribeToTopic(snsClient, context, snsTopic, adminEmail);
+ }
+
+ private void sendResponse(String url, CloudFormationCustomResourceEvent event, Context context, String status, Map data) {
+ LambdaLogger logger = context.getLogger();
+ ObjectMapper objectMapper = new ObjectMapper();
+ try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+
+ Map responseBody = Map.of(
+ "Status", status,
+ "Reason", "See the details in CloudWatch Log Stream: " + context.getLogStreamName(),
+ "PhysicalResourceId", event.getPhysicalResourceId() != null ? event.getPhysicalResourceId() : context.getLogStreamName(),
+ "StackId", event.getStackId(),
+ "RequestId", event.getRequestId(),
+ "LogicalResourceId", event.getLogicalResourceId(),
+ "Data", data
+ );
+
+ try {
+ StringEntity entity = new StringEntity(objectMapper.writeValueAsString(responseBody));
+ HttpPut request = new HttpPut(url);
+ request.setEntity(entity);
+ request.setHeader("Content-Type", "application/json");
+
+ httpClient.execute(request, response -> {
+ EntityUtils.consume(response.getEntity());
+ logger.log("Response sent to CloudFormation successfully.");
+ return null;
+ });
+ logger.log("Response sent to CloudFormation successfully.");
+ } catch (IOException e) {
+ logger.log("Failed to send response to CloudFormation: " + e.getMessage());
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/org/umaxcode/config/SecurityConfig.java b/src/main/java/org/umaxcode/config/SecurityConfig.java
index eb13184..9b34ce9 100644
--- a/src/main/java/org/umaxcode/config/SecurityConfig.java
+++ b/src/main/java/org/umaxcode/config/SecurityConfig.java
@@ -29,7 +29,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, DelegatedEntry
.cors(Customizer.withDefaults())
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
- .requestMatchers("/ping").permitAll()
+ .requestMatchers("/ping", "/pong").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt
@@ -48,7 +48,7 @@ public CorsConfigurationSource corsConfigurationSource() {
cors.addAllowedHeader("*");
cors.addAllowedMethod("*");
cors.setAllowCredentials(true);
- cors.setAllowedOrigins(List.of("http://localhost:3000"));
+ cors.setAllowedOrigins(List.of("http://localhost:3000", "https://dev.d2x151q2vf1tfl.amplifyapp.com", "https://main.d1m5j798vicdd1.amplifyapp.com"));
source.registerCorsConfiguration("/**", cors);
return source;
diff --git a/src/main/java/org/umaxcode/controller/TaskManagementController.java b/src/main/java/org/umaxcode/controller/TaskManagementController.java
index 4bbb6e7..7f2803a 100644
--- a/src/main/java/org/umaxcode/controller/TaskManagementController.java
+++ b/src/main/java/org/umaxcode/controller/TaskManagementController.java
@@ -1,5 +1,6 @@
package org.umaxcode.controller;
+import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -23,12 +24,13 @@ public class TaskManagementController {
@PostMapping
@PreAuthorize(value = "hasRole('ADMIN')")
@ResponseStatus(HttpStatus.CREATED)
- public SuccessResponse createTask(@RequestBody TasksCreationDto request, @AuthenticationPrincipal Jwt jwt) {
+ public SuccessResponse createTask(@Valid @RequestBody TasksCreationDto request, @AuthenticationPrincipal Jwt jwt) {
String adminEmail = jwt.getClaimAsString("email");
- taskManagementService.createItem(request, adminEmail);
+ TaskDto createdTask = taskManagementService.createAndAssignTask(request, adminEmail);
return SuccessResponse.builder()
.message("Task created successfully")
+ .data(createdTask)
.build();
}
@@ -37,7 +39,7 @@ public SuccessResponse createTask(@RequestBody TasksCreationDto request, @Authen
@ResponseStatus(HttpStatus.OK)
public SuccessResponse retrieveTask(@PathVariable("id") String taskId) {
- TaskDto taskDto = taskManagementService.readItem(taskId);
+ TaskDto taskDto = taskManagementService.fetchTask(taskId);
return SuccessResponse.builder()
.message("Task retrieved successfully")
.data(taskDto)
@@ -45,7 +47,7 @@ public SuccessResponse retrieveTask(@PathVariable("id") String taskId) {
}
@GetMapping("/users/{email}")
- @PreAuthorize(value= "hasAnyRole('ADMIN', 'USER')")
+ @PreAuthorize(value = "hasAnyRole('ADMIN', 'USER')")
@ResponseStatus(HttpStatus.OK)
public SuccessResponse retrieveUserTasks(@PathVariable String email) {
@@ -71,7 +73,7 @@ public SuccessResponse makeTaskAsCompleted(@PathVariable("id") String id, @Authe
@PatchMapping("/{id}/reopen")
@PreAuthorize(value = "hasRole('ADMIN')")
@ResponseStatus(HttpStatus.OK)
- public SuccessResponse reopenTask(@PathVariable("id") String id, @RequestBody TaskReopenDto request) {
+ public SuccessResponse reopenTask(@PathVariable("id") String id, @Valid @RequestBody TaskReopenDto request) {
TaskDto updatedTask = taskManagementService.reopenTask(id, request);
return SuccessResponse.builder()
@@ -84,7 +86,7 @@ public SuccessResponse reopenTask(@PathVariable("id") String id, @RequestBody Ta
@PreAuthorize(value = "hasRole('USER')")
@ResponseStatus(HttpStatus.OK)
public SuccessResponse updateTaskComment(@PathVariable("id") String id,
- @RequestBody TaskCommentUpdateDto request
+ @Valid @RequestBody TaskCommentUpdateDto request
) {
TaskDto updatedTask = taskManagementService.updateTaskComment(id, request);
@@ -97,12 +99,12 @@ public SuccessResponse updateTaskComment(@PathVariable("id") String id,
@PatchMapping("/{id}/reassign")
@PreAuthorize(value = "hasRole('ADMIN')")
@ResponseStatus(HttpStatus.OK)
- public SuccessResponse reAssignTask(@PathVariable("id") String id, @RequestBody ReassignTaskDto request
+ public SuccessResponse reAssignTask(@PathVariable("id") String id, @Valid @RequestBody ReassignTaskDto request
) {
TaskDto updatedTask = taskManagementService.reAssignTask(id, request);
return SuccessResponse.builder()
- .message("Task comment updated successfully")
+ .message("Task reassigned successfully")
.data(updatedTask)
.build();
}
@@ -110,12 +112,12 @@ public SuccessResponse reAssignTask(@PathVariable("id") String id, @RequestBody
@PatchMapping("/{id}")
@PreAuthorize(value = "hasRole('ADMIN')")
@ResponseStatus(HttpStatus.OK)
- public SuccessResponse updateTaskDetails(@PathVariable("id") String id, @RequestBody TaskDetailsUpdateDto request
+ public SuccessResponse updateTaskDetails(@PathVariable("id") String id, @Valid @RequestBody TaskDetailsUpdateDto request
) {
TaskDto updatedTask = taskManagementService.updateTaskDetails(id, request);
return SuccessResponse.builder()
- .message("Task comment updated successfully")
+ .message("Task details updated successfully")
.data(updatedTask)
.build();
}
diff --git a/src/main/java/org/umaxcode/controller/UserAuthController.java b/src/main/java/org/umaxcode/controller/UserAuthController.java
index 68bca81..0b3543d 100644
--- a/src/main/java/org/umaxcode/controller/UserAuthController.java
+++ b/src/main/java/org/umaxcode/controller/UserAuthController.java
@@ -1,5 +1,6 @@
package org.umaxcode.controller;
+import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -21,11 +22,12 @@ public class UserAuthController {
@PostMapping("/signup")
@PreAuthorize(value = "hasRole('ADMIN')")
@ResponseStatus(HttpStatus.CREATED)
- public SuccessResponse signup(@RequestBody UserCreationDto request) {
+ public SuccessResponse signup(@Valid @RequestBody UserCreationDto request) {
- String message = userAuthService.register(request);
+ UserDto registeredUser = userAuthService.register(request);
return SuccessResponse.builder()
- .message(message)
+ .message("User created successfully")
+ .data(registeredUser)
.build();
}
diff --git a/src/main/java/org/umaxcode/domain/dto/request/ReassignTaskDto.java b/src/main/java/org/umaxcode/domain/dto/request/ReassignTaskDto.java
index 2cdb20e..137fb37 100644
--- a/src/main/java/org/umaxcode/domain/dto/request/ReassignTaskDto.java
+++ b/src/main/java/org/umaxcode/domain/dto/request/ReassignTaskDto.java
@@ -1,6 +1,12 @@
package org.umaxcode.domain.dto.request;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+
public record ReassignTaskDto(
+
+ @NotBlank(message = "User email is required")
+ @Email(message = "Invalid email")
String userEmail
) {
}
diff --git a/src/main/java/org/umaxcode/domain/dto/request/TaskCommentUpdateDto.java b/src/main/java/org/umaxcode/domain/dto/request/TaskCommentUpdateDto.java
index 9415edd..d39e63e 100644
--- a/src/main/java/org/umaxcode/domain/dto/request/TaskCommentUpdateDto.java
+++ b/src/main/java/org/umaxcode/domain/dto/request/TaskCommentUpdateDto.java
@@ -1,6 +1,10 @@
package org.umaxcode.domain.dto.request;
+import jakarta.validation.constraints.NotBlank;
+
public record TaskCommentUpdateDto(
+
+ @NotBlank(message = "Comment is required")
String comment
) {
}
diff --git a/src/main/java/org/umaxcode/domain/dto/request/TaskDetailsUpdateDto.java b/src/main/java/org/umaxcode/domain/dto/request/TaskDetailsUpdateDto.java
index 172efa7..b1e276a 100644
--- a/src/main/java/org/umaxcode/domain/dto/request/TaskDetailsUpdateDto.java
+++ b/src/main/java/org/umaxcode/domain/dto/request/TaskDetailsUpdateDto.java
@@ -1,7 +1,13 @@
package org.umaxcode.domain.dto.request;
+import jakarta.validation.constraints.NotBlank;
+
public record TaskDetailsUpdateDto(
+
+ @NotBlank(message = "Name is required")
String name,
+
+ @NotBlank(message = "Description is required")
String description
) {
}
diff --git a/src/main/java/org/umaxcode/domain/dto/request/TaskReopenDto.java b/src/main/java/org/umaxcode/domain/dto/request/TaskReopenDto.java
index 5d18973..c741779 100644
--- a/src/main/java/org/umaxcode/domain/dto/request/TaskReopenDto.java
+++ b/src/main/java/org/umaxcode/domain/dto/request/TaskReopenDto.java
@@ -1,8 +1,14 @@
package org.umaxcode.domain.dto.request;
+import jakarta.validation.constraints.Future;
+import jakarta.validation.constraints.NotBlank;
+
import java.time.LocalDateTime;
public record TaskReopenDto(
+
+ @NotBlank(message = "Deadline is required")
+ @Future(message = "Deadline must be in the future")
LocalDateTime deadline
) {
}
diff --git a/src/main/java/org/umaxcode/domain/dto/request/TasksCreationDto.java b/src/main/java/org/umaxcode/domain/dto/request/TasksCreationDto.java
index bd25c74..854b3d9 100644
--- a/src/main/java/org/umaxcode/domain/dto/request/TasksCreationDto.java
+++ b/src/main/java/org/umaxcode/domain/dto/request/TasksCreationDto.java
@@ -1,11 +1,25 @@
package org.umaxcode.domain.dto.request;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.Future;
+import jakarta.validation.constraints.NotBlank;
+
import java.time.LocalDateTime;
public record TasksCreationDto(
+
+ @NotBlank(message = "Name is required")
String name,
+
+ @NotBlank(message = "Description is required")
String description,
+
+ @NotBlank(message = "Deadline is required")
+ @Future(message = "Deadline must be in the future")
LocalDateTime deadline,
+
+ @NotBlank(message = "Deadline is required")
+ @Email(message = "Invalid email")
String responsibility
) {
}
diff --git a/src/main/java/org/umaxcode/domain/dto/request/UserCreationDto.java b/src/main/java/org/umaxcode/domain/dto/request/UserCreationDto.java
index 0745f07..912fd14 100644
--- a/src/main/java/org/umaxcode/domain/dto/request/UserCreationDto.java
+++ b/src/main/java/org/umaxcode/domain/dto/request/UserCreationDto.java
@@ -1,7 +1,15 @@
package org.umaxcode.domain.dto.request;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+
public record UserCreationDto(
+
+ @NotBlank(message = "Username is required")
String username,
+
+ @NotBlank(message = "Email is required")
+ @Email(message = "Invalid email")
String email
) {
}
diff --git a/src/main/java/org/umaxcode/domain/dto/response/UserDto.java b/src/main/java/org/umaxcode/domain/dto/response/UserDto.java
index 2e42648..56e655a 100644
--- a/src/main/java/org/umaxcode/domain/dto/response/UserDto.java
+++ b/src/main/java/org/umaxcode/domain/dto/response/UserDto.java
@@ -5,6 +5,8 @@
@Builder
public record UserDto(
String userId,
- String email
+ String username,
+ String email,
+ String role
) {
}
diff --git a/src/main/java/org/umaxcode/exception/ErrorMessage.java b/src/main/java/org/umaxcode/exception/ErrorMessage.java
index 3e77f14..0596ede 100644
--- a/src/main/java/org/umaxcode/exception/ErrorMessage.java
+++ b/src/main/java/org/umaxcode/exception/ErrorMessage.java
@@ -10,6 +10,6 @@
public class ErrorMessage {
private String path;
- private String message;
+ private Object message;
private String timestamp;
}
diff --git a/src/main/java/org/umaxcode/exception/GlobalExceptionHandler.java b/src/main/java/org/umaxcode/exception/GlobalExceptionHandler.java
index e744276..5e86161 100644
--- a/src/main/java/org/umaxcode/exception/GlobalExceptionHandler.java
+++ b/src/main/java/org/umaxcode/exception/GlobalExceptionHandler.java
@@ -2,11 +2,17 @@
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@@ -22,6 +28,45 @@ public ErrorMessage taskManagementExceptionHandler(TaskManagementException ex, H
.build();
}
+ @ExceptionHandler(AuthenticationException.class)
+ @ResponseStatus(HttpStatus.UNAUTHORIZED)
+ public ErrorMessage authenticationExceptionHandler(AuthenticationException ex, HttpServletRequest request) {
+
+ return ErrorMessage.builder()
+ .path(request.getRequestURI())
+ .message(ex.getMessage())
+ .timestamp(LocalDateTime.now().toString())
+ .build();
+ }
+
+ @ExceptionHandler(AccessDeniedException.class)
+ @ResponseStatus(HttpStatus.FORBIDDEN)
+ public ErrorMessage accessDeniedHandler(AccessDeniedException ex, HttpServletRequest request) {
+
+ return ErrorMessage.builder()
+ .path(request.getRequestURI())
+ .message("You do not have permission to access this resource.")
+ .timestamp(LocalDateTime.now().toString())
+ .build();
+ }
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public ErrorMessage handleArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request) {
+
+ Map errors = new HashMap<>();
+
+ for (FieldError error : ex.getBindingResult().getFieldErrors()) {
+ errors.put(error.getField(), error.getDefaultMessage());
+ }
+
+ return ErrorMessage.builder()
+ .path(request.getRequestURI())
+ .message(errors)
+ .timestamp(LocalDateTime.now().toString())
+ .build();
+ }
+
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorMessage exceptionHandler(Exception ex, HttpServletRequest request) {
diff --git a/src/main/java/org/umaxcode/mapper/UserMapper.java b/src/main/java/org/umaxcode/mapper/UserMapper.java
index 6532110..7733d77 100644
--- a/src/main/java/org/umaxcode/mapper/UserMapper.java
+++ b/src/main/java/org/umaxcode/mapper/UserMapper.java
@@ -19,8 +19,15 @@ public static List toUserDto(List users) {
.email(u.attributes().stream()
.filter(a -> "email".equalsIgnoreCase(a.name()))
.map(AttributeType::value)
- .findFirst().orElse(null)
- )
+ .findFirst().orElse(null))
+ .username(u.attributes().stream()
+ .filter(a -> "name".equalsIgnoreCase(a.name()))
+ .map(AttributeType::value)
+ .findFirst().orElse(null))
+ .role(u.attributes().stream()
+ .filter(a -> "custom:role".equalsIgnoreCase(a.name()))
+ .map(AttributeType::value)
+ .findFirst().orElse(null))
.build()
).toList();
}
diff --git a/src/main/java/org/umaxcode/service/SNSService.java b/src/main/java/org/umaxcode/service/SNSService.java
new file mode 100644
index 0000000..153ca8b
--- /dev/null
+++ b/src/main/java/org/umaxcode/service/SNSService.java
@@ -0,0 +1,5 @@
+package org.umaxcode.service;
+
+public interface SNSService {
+
+}
diff --git a/src/main/java/org/umaxcode/service/TaskManagementService.java b/src/main/java/org/umaxcode/service/TaskManagementService.java
index e300627..bb2cada 100644
--- a/src/main/java/org/umaxcode/service/TaskManagementService.java
+++ b/src/main/java/org/umaxcode/service/TaskManagementService.java
@@ -8,9 +8,9 @@
public interface TaskManagementService {
- void createItem(TasksCreationDto item, String email);
+ TaskDto createAndAssignTask(TasksCreationDto item, String email);
- TaskDto readItem(String id);
+ TaskDto fetchTask(String id);
List getAllTasks();
diff --git a/src/main/java/org/umaxcode/service/UserAuthService.java b/src/main/java/org/umaxcode/service/UserAuthService.java
index 506761d..9612dd1 100644
--- a/src/main/java/org/umaxcode/service/UserAuthService.java
+++ b/src/main/java/org/umaxcode/service/UserAuthService.java
@@ -7,7 +7,7 @@
public interface UserAuthService {
- String register(UserCreationDto request);
+ UserDto register(UserCreationDto request);
List fetchAllUsers();
}
diff --git a/src/main/java/org/umaxcode/service/impl/SNSServiceImpl.java b/src/main/java/org/umaxcode/service/impl/SNSServiceImpl.java
new file mode 100644
index 0000000..69f6c5e
--- /dev/null
+++ b/src/main/java/org/umaxcode/service/impl/SNSServiceImpl.java
@@ -0,0 +1,52 @@
+package org.umaxcode.service.impl;
+
+import com.amazonaws.services.lambda.runtime.Context;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.umaxcode.service.SNSService;
+import software.amazon.awssdk.services.sns.SnsClient;
+import software.amazon.awssdk.services.sns.model.SetSubscriptionAttributesRequest;
+import software.amazon.awssdk.services.sns.model.SubscribeRequest;
+import software.amazon.awssdk.services.sns.model.SubscribeResponse;
+
+@Service
+@RequiredArgsConstructor
+public class SNSServiceImpl implements SNSService {
+
+ public static void subscribeToTopic(SnsClient snsClient, Context context, String topic, String email) {
+
+ // Create SNS subscription request
+ SubscribeRequest subscribeRequest = SubscribeRequest.builder()
+ .protocol("email") // Protocol is email to receive notifications
+ .endpoint(email) // User's email address to subscribe
+ .returnSubscriptionArn(true)
+ .topicArn(topic) // SNS Topic ARN
+ .build();
+
+ try {
+ SubscribeResponse response = snsClient.subscribe(subscribeRequest);
+ context.getLogger().log("Subscription result: " + response);
+
+ // Define a filter policy
+ String filterPolicy = String.format("{ \"endpointEmail\": [\"%s\"] }", email);
+
+ // Set the filter policy for the subscription
+ String subscriptionArn = response.subscriptionArn();
+
+ System.out.println("SubscriptionArn" + subscriptionArn);
+ SetSubscriptionAttributesRequest filterPolicyRequest = SetSubscriptionAttributesRequest.builder()
+ .subscriptionArn(subscriptionArn)
+ .attributeName("FilterPolicy")
+ .attributeValue(filterPolicy)
+ .build();
+
+ snsClient.setSubscriptionAttributes(filterPolicyRequest);
+
+ context.getLogger().log("Filter policy set for subscription: " + subscriptionArn);
+ context.getLogger().log("Successfully subscribed " + email + " to the SNS topic with filter policy: " + topic);
+
+ } catch (Exception e) {
+ context.getLogger().log("Error subscribing user: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/org/umaxcode/service/impl/TaskManagementServiceImpl.java b/src/main/java/org/umaxcode/service/impl/TaskManagementServiceImpl.java
index be4c2a9..a3f72aa 100644
--- a/src/main/java/org/umaxcode/service/impl/TaskManagementServiceImpl.java
+++ b/src/main/java/org/umaxcode/service/impl/TaskManagementServiceImpl.java
@@ -46,7 +46,7 @@ public TaskManagementServiceImpl(DynamoDbClient dynamoDbClient, CognitoIdentityP
}
@Override
- public void createItem(TasksCreationDto request, String email) {
+ public TaskDto createAndAssignTask(TasksCreationDto request, String email) {
Map item = new HashMap<>();
item.put("taskId", AttributeValue.builder().s(UUID.randomUUID().toString()).build());
@@ -62,14 +62,23 @@ public void createItem(TasksCreationDto request, String email) {
PutItemRequest putRequest = PutItemRequest.builder()
.tableName(tasksTableName)
.item(item)
+ .returnValues("ALL_OLD")
.build();
- PutItemResponse putItemResponse = dynamoDbClient.putItem(putRequest);
- System.out.println("results " + putItemResponse.attributes());
+ dynamoDbClient.putItem(putRequest);
+ return TaskDto.builder()
+ .id(item.get("taskId").s())
+ .name(request.name())
+ .description(request.description())
+ .status(TaskStatus.OPEN)
+ .responsibility(request.responsibility())
+ .deadline(request.deadline().toString())
+ .assignedBy(email)
+ .build();
}
@Override
- public TaskDto readItem(String id) {
+ public TaskDto fetchTask(String id) {
Map key = new HashMap<>();
key.put("taskId", AttributeValue.builder().s(id).build());
@@ -219,12 +228,13 @@ public TaskDto reopenTask(String id, TaskReopenDto request) {
UpdateItemRequest updateItemRequest = UpdateItemRequest.builder()
.tableName(tasksTableName)
.key(key)
- .updateExpression("SET #status = :status, deadline = :deadline")
+ .updateExpression("SET #status = :status, deadline = :deadline, isNotifiedForApproachDeadline = :false")
.conditionExpression("#status = :expired")
.expressionAttributeValues(Map.of(
":status", AttributeValue.builder().s("open").build(),
":deadline", AttributeValue.builder().s(request.deadline().toString()).build(),
- ":expired", AttributeValue.builder().s("expired").build()
+ ":expired", AttributeValue.builder().s("expired").build(),
+ ":false", AttributeValue.builder().n("0").build()
))
.expressionAttributeNames(Map.of(
"#status", "status"
@@ -255,7 +265,7 @@ public TaskDto updateTaskDetails(String id, TaskDetailsUpdateDto request) {
.tableName(tasksTableName)
.key(key)
.updateExpression("SET #name = :name, description = :description")
- .conditionExpression("#status = :open")
+ .conditionExpression("#status = :status")
.expressionAttributeValues(Map.of(
":status", AttributeValue.builder().s("open").build(),
":name", AttributeValue.builder().s(request.name()).build(),
diff --git a/src/main/java/org/umaxcode/service/impl/UserAuthServiceImpl.java b/src/main/java/org/umaxcode/service/impl/UserAuthServiceImpl.java
index 50b2db4..187ad10 100644
--- a/src/main/java/org/umaxcode/service/impl/UserAuthServiceImpl.java
+++ b/src/main/java/org/umaxcode/service/impl/UserAuthServiceImpl.java
@@ -32,7 +32,7 @@ public class UserAuthServiceImpl implements UserAuthService {
private String userPoolId;
@Override
- public String register(UserCreationDto request) {
+ public UserDto register(UserCreationDto request) {
try {
AdminCreateUserRequest adminRequest = AdminCreateUserRequest.builder()
@@ -51,7 +51,14 @@ public String register(UserCreationDto request) {
AdminCreateUserResponse response = cognitoClient.adminCreateUser(adminRequest);
startStateMachineForSNSSub(request.email());
System.out.println("User" + response.user());
- return "User created: " + response.user().username();
+
+ return UserDto.builder()
+ .userId(response.user().username())
+ .email(request.email())
+ .username(request.username())
+ .role(Role.USER.toString())
+ .build();
+
} catch (CognitoIdentityProviderException e) {
throw new UserAuthException("Failed to create user: " + e.getMessage());
}
diff --git a/template.yaml b/template.yaml
index ef92c4d..fa9c4be 100644
--- a/template.yaml
+++ b/template.yaml
@@ -14,6 +14,15 @@ Parameters:
Description: User pool group name for API administrators
Type: String
Default: apiAdmins
+ FrontendLoginUrl:
+ Description: Frontend url for login
+ Type: String
+ AdminEmail:
+ Description: Email address of admin
+ Type: String
+ AdminUsername:
+ Description: Username of admin
+ Type: String
Globals:
Api:
@@ -34,7 +43,7 @@ Resources:
- DynamoDBCrudPolicy:
TableName: !Ref TasksTable
- SQSSendMessagePolicy:
- QueueName: tasks-queue
+ QueueName: !Sub ${AWS::StackName}-tasks-queue
- Statement:
Effect: Allow
Action:
@@ -137,7 +146,7 @@ Resources:
Type: AWS::Cognito::UserPoolGroup
Properties:
Description: User group for API Administrators
- GroupName: !Ref UserPoolAdminGroupName
+ GroupName: !Sub ${AWS::StackName}-${UserPoolAdminGroupName}
Precedence: 0
UserPoolId: !Ref UserPool
@@ -145,31 +154,31 @@ Resources:
TasksAssignmentNotificationTopic:
Type: AWS::SNS::Topic
Properties:
- TopicName: tasks-assignment-notifications
+ TopicName: !Sub ${AWS::StackName}-tasks-assignment-notifications
DisplayName: "Task Assignment Notifications"
TasksDeadlineNotificationTopic:
Type: AWS::SNS::Topic
Properties:
- TopicName: tasks-deadline-notifications
+ TopicName: !Sub ${AWS::StackName}-tasks-deadline-notifications
DisplayName: "Task Deadline Notifications"
ClosedTasksNotificationTopic:
Type: AWS::SNS::Topic
Properties:
- TopicName: closed-tasks-notifications
+ TopicName: !Sub ${AWS::StackName}-closed-tasks-notifications
DisplayName: "Closed Tasks Notifications"
ReopenedTasksNotificationTopic:
Type: AWS::SNS::Topic
Properties:
- TopicName: reopened-tasks-notifications
+ TopicName: !Sub ${AWS::StackName}-reopened-tasks-notifications
DisplayName: "Reopened Tasks Notifications"
TaskCompleteNotificationTopic:
Type: AWS::SNS::Topic
Properties:
- TopicName: task-complete-notifications
+ TopicName: !Sub ${AWS::StackName}-task-complete-notifications
DisplayName: "task completion Notifications"
SNSTopicSubscriptionFunction:
@@ -406,7 +415,7 @@ Resources:
CodeUri: .
Policies:
- SQSSendMessagePolicy: # Predefined policy to send messages
- QueueName: tasks-queue
+ QueueName: !Sub ${AWS::StackName}-tasks-queue
- DynamoDBStreamReadPolicy: # Predefined policy to read from DynamoDB streams
TableName: !Ref TasksTable
StreamName: !GetAtt TasksTable.StreamArn
@@ -464,7 +473,7 @@ Resources:
TasksQueue:
Type: AWS::SQS::Queue
Properties:
- QueueName: tasks-queue
+ QueueName: !Sub ${AWS::StackName}-tasks-queue
TaskQueuePolicy:
Type: AWS::SQS::QueuePolicy
@@ -492,7 +501,7 @@ Resources:
CodeUri: .
Policies:
- SQSSendMessagePolicy: # Predefined policy to send messages
- QueueName: tasks-queue
+ QueueName: !Sub ${AWS::StackName}-tasks-queue
- DynamoDBCrudPolicy:
TableName: !Ref TasksTable
Environment:
@@ -541,6 +550,41 @@ Resources:
Variables:
TASKS_CLOSED_NOTIFICATION_TOPIC_ARN: !Ref ClosedTasksNotificationTopic
+ UpdateInviteMessageAdminCreationLambdaHandler:
+ Type: AWS::Serverless::Function
+ Properties:
+ Handler: org.umaxcode.UpdateInviteMessageAdminCreationLambdaHandler::handleRequest
+ CodeUri: .
+ Policies:
+ - AWSLambdaBasicExecutionRole
+ - Statement:
+ Effect: Allow
+ Action:
+ - cognito-idp:UpdateUserPool
+ - cognito-idp:AdminCreateUser
+ - cognito-idp:AdminGetUser
+ Resource: !GetAtt UserPool.Arn
+ - Statement:
+ Effect: Allow
+ Action:
+ - sns:Subscribe
+ - sns:SetSubscriptionAttributes
+ Resource:
+ - !Ref ClosedTasksNotificationTopic
+ - !Ref TaskCompleteNotificationTopic
+
+ UpdateInviteMessageTemplateAndCreateAdminUserCustomResource:
+ Type: Custom::UpdateInviteMessageTemplate
+ Properties:
+ ServiceToken: !GetAtt UpdateInviteMessageAdminCreationLambdaHandler.Arn
+ ServiceTimeout: 30
+ UserPoolId: !Ref UserPool
+ FrontendLoginUrl: !Ref FrontendLoginUrl
+ AdminEmail: !Ref AdminEmail
+ AdminUsername: !Ref AdminUsername
+ TaskCompleteTopicArn: !Ref TaskCompleteNotificationTopic
+ ClosedTaskTopicArn: !Ref ClosedTasksNotificationTopic
+
Outputs:
TaskManagementSystemApi:
Description: URL for application