Skip to content

Commit 91cafb0

Browse files
authored
Merge pull request #273 from pagopa/SELC-7067_add_otp_email_endpoint
[#SELC-7067] Add Send OTP mail endpoint
2 parents 8cd1115 + 55e4b72 commit 91cafb0

File tree

12 files changed

+328
-87
lines changed

12 files changed

+328
-87
lines changed

apps/user-ms/src/main/docs/openapi.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,6 +1579,55 @@
15791579
} ]
15801580
}
15811581
},
1582+
"/users/{userId}/send-mail-otp" : {
1583+
"post" : {
1584+
"tags" : [ "User" ],
1585+
"summary" : "Send an email with OTP",
1586+
"description" : "The sendEmailOtp function is used to send email containing a One Time Password for account verification",
1587+
"operationId" : "sendEmailOtp",
1588+
"parameters" : [ {
1589+
"name" : "userId",
1590+
"in" : "path",
1591+
"required" : true,
1592+
"schema" : {
1593+
"type" : "string"
1594+
}
1595+
} ],
1596+
"requestBody" : {
1597+
"content" : {
1598+
"application/json" : {
1599+
"schema" : {
1600+
"$ref" : "#/components/schemas/SendEmailOtpDto"
1601+
}
1602+
}
1603+
}
1604+
},
1605+
"responses" : {
1606+
"202" : {
1607+
"description" : "Email send accepted!"
1608+
},
1609+
"400" : {
1610+
"description" : "Bad Request",
1611+
"content" : {
1612+
"application/problem+json" : {
1613+
"schema" : {
1614+
"$ref" : "#/components/schemas/Problem"
1615+
}
1616+
}
1617+
}
1618+
},
1619+
"401" : {
1620+
"description" : "Not Authorized"
1621+
},
1622+
"403" : {
1623+
"description" : "Not Allowed"
1624+
}
1625+
},
1626+
"security" : [ {
1627+
"SecurityScheme" : [ ]
1628+
} ]
1629+
}
1630+
},
15821631
"/users/{userId}/send-mail-request" : {
15831632
"post" : {
15841633
"tags" : [ "User" ],
@@ -1891,6 +1940,20 @@
18911940
}
18921941
}
18931942
},
1943+
"SendEmailOtpDto" : {
1944+
"required" : [ "otp", "institutionalEmail" ],
1945+
"type" : "object",
1946+
"properties" : {
1947+
"otp" : {
1948+
"minLength" : 1,
1949+
"type" : "string"
1950+
},
1951+
"institutionalEmail" : {
1952+
"minLength" : 1,
1953+
"type" : "string"
1954+
}
1955+
}
1956+
},
18941957
"SendMailDto" : {
18951958
"required" : [ "userMailUuid", "institutionName", "productId", "role" ],
18961959
"type" : "object",

apps/user-ms/src/main/docs/openapi.yaml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,40 @@ paths:
11221122
description: Not Allowed
11231123
security:
11241124
- SecurityScheme: []
1125+
/users/{userId}/send-mail-otp:
1126+
post:
1127+
tags:
1128+
- User
1129+
summary: Send an email with OTP
1130+
description: The sendEmailOtp function is used to send email containing a One
1131+
Time Password for account verification
1132+
operationId: sendEmailOtp
1133+
parameters:
1134+
- name: userId
1135+
in: path
1136+
required: true
1137+
schema:
1138+
type: string
1139+
requestBody:
1140+
content:
1141+
application/json:
1142+
schema:
1143+
$ref: "#/components/schemas/SendEmailOtpDto"
1144+
responses:
1145+
"202":
1146+
description: Email send accepted!
1147+
"400":
1148+
description: Bad Request
1149+
content:
1150+
application/problem+json:
1151+
schema:
1152+
$ref: "#/components/schemas/Problem"
1153+
"401":
1154+
description: Not Authorized
1155+
"403":
1156+
description: Not Allowed
1157+
security:
1158+
- SecurityScheme: []
11251159
/users/{userId}/send-mail-request:
11261160
post:
11271161
tags:
@@ -1374,6 +1408,18 @@ components:
13741408
properties:
13751409
fiscalCode:
13761410
type: string
1411+
SendEmailOtpDto:
1412+
required:
1413+
- otp
1414+
- institutionalEmail
1415+
type: object
1416+
properties:
1417+
otp:
1418+
minLength: 1
1419+
type: string
1420+
institutionalEmail:
1421+
minLength: 1
1422+
type: string
13771423
SendMailDto:
13781424
required:
13791425
- userMailUuid

apps/user-ms/src/main/java/it/pagopa/selfcare/user/constant/TemplateMailConstant.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ public class TemplateMailConstant {
1717
public static final String CREATE_TEMPLATE_MULTIPLE_ROLE = "user_added_multi_role.ftlh";
1818
public static final String CREATE_SUBJECT = "Hai un nuovo ruolo per un prodotto PagoPA";
1919
public static final String CREATE_TEMPLATE_SINGLE_ROLE = "user_added_single_role.ftlh";
20+
public static final String OTP_SUBJECT = "Conferma la tua identità";
21+
public static final String OTP_TEMPLATE = "user_otp.ftlh";
2022
}

apps/user-ms/src/main/java/it/pagopa/selfcare/user/controller/UserController.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import it.pagopa.selfcare.user.controller.request.AddUserRoleDto;
1010
import it.pagopa.selfcare.user.controller.request.CreateUserDto;
1111
import it.pagopa.selfcare.user.controller.request.SendMailDto;
12+
import it.pagopa.selfcare.user.controller.request.SendEmailOtpDto;
1213
import it.pagopa.selfcare.user.controller.response.*;
1314
import it.pagopa.selfcare.user.controller.response.product.SearchUserDto;
1415
import it.pagopa.selfcare.user.exception.InvalidRequestException;
@@ -431,6 +432,25 @@ public Uni<Void> sendMailRequest(@PathParam("userId") String userId,
431432
.onItem().transformToUni(loggedUser -> userService.sendMail(userId, sendMailDto.getUserMailUuid(), sendMailDto.getInstitutionName(), sendMailDto.getProductId(), sendMailDto.getRole(), loggedUser));
432433
}
433434

435+
@APIResponses({
436+
@APIResponse(responseCode = "202", description = "Email send accepted!"),
437+
@APIResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = Problem.class), mediaType = "application/problem+json"))
438+
})
439+
@Operation(description = "The sendEmailOtp function is used to send email containing a One Time Password for account verification", summary = "Send an email with OTP")
440+
@POST
441+
@Path(value = "/{userId}/send-mail-otp")
442+
@Tag(name = "User")
443+
@Consumes(MediaType.APPLICATION_JSON)
444+
@Produces(MediaType.APPLICATION_JSON)
445+
public Uni<Response> sendEmailOtp(@PathParam(value = "userId") String userId,
446+
@Valid SendEmailOtpDto sendEmailOtpDto,
447+
@Context SecurityContext ctx) {
448+
return readUserIdFromToken(ctx)
449+
.onItem().transformToUni(loggedUser -> userService.sendEmailOtp(userId, sendEmailOtpDto.getInstitutionalEmail(), sendEmailOtpDto.getOtp()))
450+
.map(response -> Response
451+
.status(HttpStatus.SC_ACCEPTED).build());
452+
}
453+
434454
private Uni<LoggedUser> readUserIdFromToken(SecurityContext ctx) {
435455
return currentIdentityAssociation.getDeferredIdentity()
436456
.onItem().transformToUni(identity -> {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package it.pagopa.selfcare.user.controller.request;
2+
3+
import jakarta.validation.constraints.NotEmpty;
4+
import lombok.Data;
5+
6+
@Data
7+
public class SendEmailOtpDto {
8+
9+
@NotEmpty(message = "otp is required")
10+
private String otp;
11+
12+
@NotEmpty(message = "institutionalEmail is required")
13+
private String institutionalEmail;
14+
}

apps/user-ms/src/main/java/it/pagopa/selfcare/user/service/UserNotificationService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@ public interface UserNotificationService {
1919
Uni<Void> sendCreateUserNotification(String institutionDescription, List<String> roleLabels, UserResource userResource, UserInstitution userInstitution, Product product, LoggedUser loggedUser);
2020

2121
Uni<Void> buildDataModelRequestAndSendEmail(UserResource user, UserInstitution institution, Product product, PartyRole productRole, String loggedUserName, String loggedUserSurname);
22+
23+
Uni<Void> sendOtpNotification(String institutionalEmail, String name, String otp);
2224
}

apps/user-ms/src/main/java/it/pagopa/selfcare/user/service/UserNotificationServiceImpl.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,16 @@ public Uni<Void> sendCreateUserNotification(String institutionDescription, List<
126126
.onItem().invoke(() -> log.debug("sendCreateNotification end"));
127127
}
128128

129+
@Override
130+
public Uni<Void> sendOtpNotification(String institutionalEmail, String name, String otp) {
131+
log.debug("sendOtpNotification start");
132+
133+
Map<String, String> dataModel = Map.of("name",name, "otp", otp);
134+
135+
return this.sendEmailNotification(OTP_TEMPLATE, OTP_SUBJECT, institutionalEmail, dataModel)
136+
.onItem().invoke(() -> log.debug("sendOtpNotification end"));
137+
}
138+
129139
private Map<String, String> buildCreateEmailDataModel(LoggedUser loggedUser, Product product, String institutionDescription, List<String> productRoleCodes) {
130140
Map<String, String> dataModel = new HashMap<>();
131141
dataModel.put(REQUESTER_NAME, Optional.ofNullable(loggedUser.getName()).orElse(""));

apps/user-ms/src/main/java/it/pagopa/selfcare/user/service/UserService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,6 @@ public interface UserService {
6767
Uni<Boolean> checkUser(String fiscalCode, String institutionId, String productId);
6868

6969
Uni<Void> sendMail(String userId, String userMailUuid, String institutionName, String productId, PartyRole productRole, LoggedUser loggedUser);
70+
71+
Uni<Void> sendEmailOtp(String userId, String institutionalEmail, String otp);
7072
}

apps/user-ms/src/main/java/it/pagopa/selfcare/user/service/UserServiceImpl.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,14 @@ public Uni<Boolean> checkUser(String fiscalCode, String institutionId, String pr
916916
.recoverWithItem(false);
917917
}
918918

919+
@Override
920+
public Uni<Void> sendEmailOtp(String userId, String institutionalEmail, String otp) {
921+
return userRegistryService.findByIdUsingGET(USERS_WORKS_FIELD_LIST, userId)
922+
.map(userResource -> userResource.getName().getValue()).chain(name ->
923+
userNotificationService.sendOtpNotification(institutionalEmail,name, otp)
924+
);
925+
}
926+
919927
private boolean isNotFound(Throwable throwable) {
920928
return ((WebApplicationException) throwable).getResponse().getStatus() == 404;
921929
}

apps/user-ms/src/test/java/it/pagopa/selfcare/user/controller/UserControllerTest.java

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import it.pagopa.selfcare.user.controller.request.AddUserRoleDto;
1313
import it.pagopa.selfcare.user.controller.request.CreateUserDto;
1414
import it.pagopa.selfcare.user.controller.request.SendMailDto;
15+
import it.pagopa.selfcare.user.controller.request.SendEmailOtpDto;
1516
import it.pagopa.selfcare.user.controller.response.*;
1617
import it.pagopa.selfcare.user.controller.response.product.SearchUserDto;
1718
import it.pagopa.selfcare.user.entity.UserInfo;
@@ -340,7 +341,7 @@ void testGetUserProductsInfoOk() {
340341
.then()
341342
.statusCode(200);
342343

343-
Mockito.verify(userService).retrieveBindings(any(), any(), any());
344+
verify(userService).retrieveBindings(any(), any(), any());
344345

345346
}
346347

@@ -355,7 +356,7 @@ void testGetUserProductsInfoNotAuthorized() {
355356
.then()
356357
.statusCode(401);
357358

358-
Mockito.verify(userService, Mockito.never()).retrieveBindings(any(), any(), any());
359+
verify(userService, Mockito.never()).retrieveBindings(any(), any(), any());
359360
}
360361

361362

@@ -802,6 +803,40 @@ void testSendMail_invalidRequestBody() {
802803
.statusCode(400);
803804
}
804805

806+
@Test
807+
@TestSecurity(user = "userJwt")
808+
void testSendMailOtp() {
809+
String PATH_USER_ID = "userId";
810+
String PATH_SEND_MAIL = "/{userId}/send-mail-otp";
811+
SendEmailOtpDto mailDto = createSendEmailOtpDto();
812+
given()
813+
.when()
814+
.contentType(ContentType.JSON)
815+
.pathParam(PATH_USER_ID, "userId")
816+
.body(mailDto)
817+
.post(PATH_SEND_MAIL)
818+
.then()
819+
.statusCode(202);
820+
821+
verify(userService, times(1)).sendEmailOtp(anyString(), anyString(), anyString());
822+
}
823+
824+
@Test
825+
@TestSecurity(user = "userJwt")
826+
void testSendMailOtp_invalidRequestBody() {
827+
String PATH_USER_ID = "userId";
828+
String PATH_SEND_MAIL = "/{userId}/send-mail-otp";
829+
SendEmailOtpDto mailDto = new SendEmailOtpDto();
830+
given()
831+
.when()
832+
.contentType(ContentType.JSON)
833+
.body(mailDto)
834+
.pathParam(PATH_USER_ID, "userId")
835+
.post(PATH_SEND_MAIL)
836+
.then()
837+
.statusCode(400);
838+
}
839+
805840
private CreateUserDto buildCreateUserDto() {
806841
CreateUserDto userDto = new CreateUserDto();
807842
userDto.setInstitutionId("institutionId");
@@ -873,4 +908,11 @@ private SendMailDto createSendMailDto() {
873908
sendMailDto.setRole(MANAGER);
874909
return sendMailDto;
875910
}
911+
912+
private SendEmailOtpDto createSendEmailOtpDto() {
913+
SendEmailOtpDto sendMailDto = new SendEmailOtpDto();
914+
sendMailDto.setOtp("123456");
915+
sendMailDto.setInstitutionalEmail("[email protected]");
916+
return sendMailDto;
917+
}
876918
}

0 commit comments

Comments
 (0)