Skip to content

Commit eb2fa80

Browse files
[SELC-7737] feat: modify addMembersToUserGroupWithParentInstitutionId to create a group if it doesn't exist (#305)
1 parent 8d0691c commit eb2fa80

File tree

15 files changed

+199
-240
lines changed

15 files changed

+199
-240
lines changed

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -869,9 +869,13 @@
869869
"schemas" : {
870870
"AddMembersToUserGroupDto" : {
871871
"title" : "AddMembersToUserGroupDto",
872-
"required" : [ "institutionId", "members", "parentInstitutionId", "productId" ],
872+
"required" : [ "description", "institutionId", "members", "name", "parentInstitutionId", "productId" ],
873873
"type" : "object",
874874
"properties" : {
875+
"description" : {
876+
"type" : "string",
877+
"description" : "Users group's description"
878+
},
875879
"institutionId" : {
876880
"type" : "string",
877881
"description" : "Users group's institutionId"
@@ -885,6 +889,10 @@
885889
"format" : "uuid"
886890
}
887891
},
892+
"name" : {
893+
"type" : "string",
894+
"description" : "Users group's name"
895+
},
888896
"parentInstitutionId" : {
889897
"type" : "string",
890898
"description" : "Users group's parent institutionId"
@@ -921,10 +929,6 @@
921929
"type" : "string",
922930
"description" : "Users group's name"
923931
},
924-
"parentInstitutionId" : {
925-
"type" : "string",
926-
"description" : "Users group's parent institutionId"
927-
},
928932
"productId" : {
929933
"type" : "string",
930934
"description" : "Users group's productId"

apps/user-group-ms/src/main/java/it/pagopa/selfcare/user_group/controller/UserGroupV1Controller.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ public void addMembersToUserGroupWithParentInstitutionId(@RequestBody
238238
log.trace("addMembersToUserGroup start");
239239
log.debug("addMembersToUserGroup institutionId = {}, parentInstitutionId = {}, productId = {}",
240240
Encode.forJava(groupDto.getInstitutionId()), Encode.forJava(groupDto.getParentInstitutionId()), Encode.forJava(groupDto.getProductId()));
241-
groupService.addMembers(groupDto.getInstitutionId(), groupDto.getParentInstitutionId(), groupDto.getProductId(), groupDto.getMembers());
241+
groupService.createGroupOrAddMembers(userGroupMapper.toUserGroupOperations(groupDto));
242242
log.trace("addMemberToUserGroup end");
243243
}
244244

apps/user-group-ms/src/main/java/it/pagopa/selfcare/user_group/model/AddMembersToUserGroupDto.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ public class AddMembersToUserGroupDto {
2727
@NotBlank
2828
private String productId;
2929

30+
@ApiModelProperty(value = "${swagger.user-group.model.name}", required = true)
31+
@JsonProperty(required = true)
32+
@NotBlank
33+
private String name;
34+
35+
@ApiModelProperty(value = "${swagger.user-group.model.description}", required = true)
36+
@JsonProperty(required = true)
37+
@NotBlank
38+
private String description;
39+
3040
@ApiModelProperty(value = "${swagger.user-group.model.members}", required = true)
3141
@JsonProperty(required = true)
3242
@NotEmpty

apps/user-group-ms/src/main/java/it/pagopa/selfcare/user_group/model/CreateUserGroupDto.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ public class CreateUserGroupDto {
1919
@NotBlank
2020
private String institutionId;
2121

22-
@ApiModelProperty(value = "${swagger.user-group.model.parentInstitutionId}")
23-
private String parentInstitutionId;
24-
2522
@ApiModelProperty(value = "${swagger.user-group.model.productId}", required = true)
2623
@JsonProperty(required = true)
2724
@NotBlank

apps/user-group-ms/src/main/java/it/pagopa/selfcare/user_group/model/UserGroupEntity.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ public UserGroupEntity(UserGroupOperations userGroup) {
4040
private String parentInstitutionId;
4141
@FieldNameConstants.Include
4242
private String productId;
43+
@FieldNameConstants.Include
4344
private String name;
45+
@FieldNameConstants.Include
4446
private String description;
4547
@FieldNameConstants.Include
4648
private UserGroupStatus status;

apps/user-group-ms/src/main/java/it/pagopa/selfcare/user_group/model/mapper/UserGroupMapper.java

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package it.pagopa.selfcare.user_group.model.mapper;
22

33
import it.pagopa.selfcare.user_group.api.UserGroupOperations;
4-
import it.pagopa.selfcare.user_group.model.CreateUserGroupDto;
5-
import it.pagopa.selfcare.user_group.model.GroupDto;
6-
import it.pagopa.selfcare.user_group.model.UpdateUserGroupDto;
7-
import it.pagopa.selfcare.user_group.model.UserGroupResource;
4+
import it.pagopa.selfcare.user_group.model.*;
85
import org.mapstruct.Mapper;
96
import org.mapstruct.Mapping;
107
import org.mapstruct.Named;
@@ -20,31 +17,25 @@ public interface UserGroupMapper {
2017
@Mapping(source = ".", target = "members", qualifiedByName = "getMembersUUID")
2118
UserGroupResource toResource(UserGroupOperations entity);
2219

23-
@Mapping(source = ".", target = "members", qualifiedByName = "getMembers")
20+
@Mapping(target = "members", expression = "java(getMembers(dto.getMembers()))")
2421
GroupDto fromDto(CreateUserGroupDto dto);
2522

26-
@Mapping(source = ".", target = "members", qualifiedByName = "getOperationMembers")
23+
@Mapping(target = "members", expression = "java(getMembers(dto.getMembers()))")
2724
GroupDto toUserGroupOperations(UpdateUserGroupDto dto);
2825

26+
@Mapping(target = "members", expression = "java(getMembers(dto.getMembers()))")
27+
GroupDto toUserGroupOperations(AddMembersToUserGroupDto dto);
28+
2929
@Named("getMembersUUID")
3030
default List<UUID> getMembersUUID(UserGroupOperations entity) {
3131
return entity.getMembers()
3232
.stream()
3333
.map(UUID::fromString)
34-
.collect(Collectors.toList());
35-
}
36-
37-
@Named("getMembers")
38-
default Set<String> getMembers(CreateUserGroupDto entity) {
39-
return entity.getMembers()
40-
.stream()
41-
.map(UUID::toString)
42-
.collect(Collectors.toSet());
34+
.toList();
4335
}
4436

45-
@Named("getOperationMembers")
46-
default Set<String> getOperationMembers(UpdateUserGroupDto entity) {
47-
return entity.getMembers()
37+
default Set<String> getMembers(Set<UUID> members) {
38+
return members
4839
.stream()
4940
.map(UUID::toString)
5041
.collect(Collectors.toSet());

apps/user-group-ms/src/main/java/it/pagopa/selfcare/user_group/service/UserGroupService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public interface UserGroupService {
1414

1515
void addMember(String id, UUID memberId);
1616

17-
void addMembers(String institutionId, String parentInstitutionId, String productId, Set<UUID> members);
17+
void createGroupOrAddMembers(UserGroupOperations group);
1818

1919
void deleteMembersWithParentInstitutionId(String institutionId, String parentInstitutionId, String productId, Set<UUID> members);
2020

apps/user-group-ms/src/main/java/it/pagopa/selfcare/user_group/service/UserGroupServiceImpl.java

Lines changed: 44 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.springframework.data.domain.AuditorAware;
2020
import org.springframework.data.domain.Page;
2121
import org.springframework.data.domain.Pageable;
22+
import org.springframework.data.mongodb.core.FindAndModifyOptions;
2223
import org.springframework.data.mongodb.core.MongoTemplate;
2324
import org.springframework.data.mongodb.core.query.Criteria;
2425
import org.springframework.data.mongodb.core.query.Query;
@@ -43,9 +44,11 @@ public class UserGroupServiceImpl implements UserGroupService {
4344
private static final String USER_GROUP_INSTITUTION_ID_REQUIRED_MESSAGE = "A user group institution id is required";
4445
private static final String USER_GROUP_PARENT_INSTITUTION_ID_REQUIRED_MESSAGE = "A user group parent institution id is required";
4546
private static final String MEMBERS_REQUIRED = "Members are required";
47+
private static final String GROUP_NAME_REQUIRED = "A group name is required";
4648
private static final String MEMBER_ID_REQUIRED = "A member id is required";
49+
private static final String GROUP_NAME_FORBIDDEN = "Group name cannot start with 'Ente Aggregatore'";
4750
private static final String GROUP_NAME_ALREADY_EXISTS = "A group with the same name already exists in ACTIVE or SUSPENDED state";
48-
private static final String GROUP_PARENT_ALREADY_EXISTS = "A group with the same institutionId-parentInstitutionId-productId already exists in ACTIVE or SUSPENDED state";
51+
private static final String ENTE_AGGREGATORE_PLACEHOLDER = "Ente Aggregatore ";
4952
private final List<String> allowedSortingParams;
5053
private final UserGroupRepository repository;
5154
private final MongoTemplate mongoTemplate;
@@ -70,7 +73,7 @@ public UserGroupOperations createGroup(UserGroupOperations group) {
7073
Assert.state(authentication.getPrincipal() instanceof SelfCareUser, "Not SelfCareUser principal");
7174
Assert.notNull(group, "A group is required");
7275

73-
checkGroupUniqueness(group.getId(), group.getName(), group.getProductId(), group.getInstitutionId(), group.getParentInstitutionId());
76+
checkGroupUniqueness(group.getId(), group.getName(), group.getProductId(), group.getInstitutionId());
7477
return insertUserGroupEntity(group);
7578
}
7679

@@ -89,21 +92,30 @@ public void addMember(String id, UUID memberId) {
8992
}
9093

9194
@Override
92-
public void addMembers(String institutionId, String parentInstitutionId, String productId, Set<UUID> members) {
93-
log.trace("addMembers start");
94-
log.debug("addMembers institutionId = {}, parentInstitutionId = {}, members = {}",
95-
Encode.forJava(institutionId), Encode.forJava(parentInstitutionId), Encode.forJava(members.toString()));
96-
Assert.notNull(institutionId, USER_GROUP_INSTITUTION_ID_REQUIRED_MESSAGE);
97-
Assert.notNull(parentInstitutionId, USER_GROUP_PARENT_INSTITUTION_ID_REQUIRED_MESSAGE);
98-
Assert.notNull(members, MEMBERS_REQUIRED);
99-
UserGroupFilter userGroupFilter = UserGroupFilter.builder()
100-
.institutionId(institutionId)
101-
.parentInstitutionId(parentInstitutionId)
102-
.productId(productId)
103-
.build();
104-
String groupId = findGroupId(userGroupFilter);
105-
insertMembers(groupId, members);
106-
log.trace("addMembers end");
95+
public void createGroupOrAddMembers(UserGroupOperations userGroupOperations) {
96+
log.trace("createGroupOrAddMembers start");
97+
log.debug("createGroupOrAddMembers institutionId = {}, parentInstitutionId = {}, members = {}",
98+
Encode.forJava(userGroupOperations.getInstitutionId()), Encode.forJava(userGroupOperations.getParentInstitutionId()), Encode.forJava(userGroupOperations.getMembers().toString()));
99+
Assert.notNull(userGroupOperations.getInstitutionId(), USER_GROUP_INSTITUTION_ID_REQUIRED_MESSAGE);
100+
Assert.notNull(userGroupOperations.getParentInstitutionId(), USER_GROUP_PARENT_INSTITUTION_ID_REQUIRED_MESSAGE);
101+
Assert.notNull(userGroupOperations.getMembers(), MEMBERS_REQUIRED);
102+
103+
Query query = new Query(Criteria.where(UserGroupEntity.Fields.institutionId).is(userGroupOperations.getInstitutionId())
104+
.and(UserGroupEntity.Fields.productId).is(userGroupOperations.getProductId())
105+
.and(UserGroupEntity.Fields.parentInstitutionId).is(userGroupOperations.getParentInstitutionId())
106+
.and(UserGroupEntity.Fields.status).in(List.of(UserGroupStatus.ACTIVE, UserGroupStatus.SUSPENDED)));
107+
108+
Update update = new Update()
109+
.setOnInsert(UserGroupEntity.Fields.name, ENTE_AGGREGATORE_PLACEHOLDER + userGroupOperations.getName())
110+
.setOnInsert(UserGroupEntity.Fields.description, userGroupOperations.getDescription())
111+
.setOnInsert(UserGroupEntity.Fields.status, UserGroupStatus.ACTIVE)
112+
.addToSet(UserGroupEntity.Fields.members).each(userGroupOperations.getMembers());
113+
114+
FindAndModifyOptions options = FindAndModifyOptions.options()
115+
.upsert(true);
116+
117+
mongoTemplate.findAndModify(query, update, options, UserGroupEntity.class);
118+
log.trace("createGroupOrAddMembers end");
107119
}
108120

109121
@Override
@@ -213,7 +225,8 @@ public UserGroupOperations updateGroup(String id, UserGroupOperations group) {
213225
if (UserGroupStatus.SUSPENDED.equals(foundGroup.getStatus())) {
214226
throw new ResourceUpdateException(TRYING_TO_MODIFY_SUSPENDED_GROUP);
215227
}
216-
checkGroupUniqueness(id, group.getName(), foundGroup.getProductId(), foundGroup.getInstitutionId(), foundGroup.getParentInstitutionId());
228+
229+
checkGroupUniqueness(id, group.getName(), foundGroup.getProductId(), foundGroup.getInstitutionId());
217230

218231
foundGroup.setMembers(group.getMembers());
219232
foundGroup.setName(group.getName());
@@ -238,36 +251,26 @@ private UserGroupEntity insertUserGroupEntity(UserGroupOperations group) {
238251
return insert;
239252
}
240253

241-
private void checkGroupUniqueness(String currentGroupId, String groupName, String productId, String institutionId, String parentInstitutionId) {
242-
UserGroupFilter filter = new UserGroupFilter();
243-
filter.setProductId(productId);
244-
filter.setInstitutionId(institutionId);
245-
filter.setStatus(Arrays.asList(UserGroupStatus.ACTIVE, UserGroupStatus.SUSPENDED));
254+
private void checkGroupUniqueness(String currentGroupId, String groupName, String productId, String institutionId) {
246255

247-
Page<UserGroupOperations> existingGroups = findAll(filter, Pageable.unpaged());
256+
Assert.notNull(groupName, GROUP_NAME_REQUIRED);
257+
Assert.isTrue(!groupName.startsWith(ENTE_AGGREGATORE_PLACEHOLDER), GROUP_NAME_FORBIDDEN);
248258

249-
// verify if there's no other group with the same name (but different groupId)
250-
boolean isSameName = existingGroups.stream().anyMatch(g ->
251-
g.getName().equals(groupName) && !g.getId().equals(currentGroupId));
259+
Query query = new Query(
260+
Criteria.where(UserGroupEntity.Fields.institutionId).is(institutionId)
261+
.and(UserGroupEntity.Fields.productId).is(productId)
262+
.and(UserGroupEntity.Fields.status).in(List.of(UserGroupStatus.ACTIVE, UserGroupStatus.SUSPENDED))
263+
);
252264

253-
// when parentInsitutionId is not null, verify if there's no other group with same institutionId-parentInstitutionId-productId (but different groupId)
254-
boolean isDuplicate = parentInstitutionId != null && existingGroups.stream().anyMatch(g ->
255-
Objects.equals(g.getInstitutionId(), institutionId)
256-
&& Objects.equals(g.getProductId(), productId)
257-
&& Objects.equals(g.getParentInstitutionId(), parentInstitutionId)
258-
&& !g.getId().equals(currentGroupId));
265+
List<UserGroupEntity> foundGroups = mongoTemplate.find(query, UserGroupEntity.class);
266+
267+
boolean isSameName = foundGroups.stream().anyMatch(g ->
268+
g.getName().equals(groupName) && !g.getId().equals(currentGroupId));
259269

260270
if (isSameName) {
261271
log.warn("Attempted to create/update group with duplicate name: {}", groupName);
262272
throw new ResourceAlreadyExistsException(GROUP_NAME_ALREADY_EXISTS);
263-
}
264-
265-
if (isDuplicate) {
266-
log.warn("Attempted to create/update group with duplicate institutionId: {}, parentInstitutionId: {} and productId: {}",
267-
institutionId, parentInstitutionId, productId);
268-
throw new ResourceAlreadyExistsException(GROUP_PARENT_ALREADY_EXISTS);
269-
}
270-
}
273+
} }
271274

272275
private Query createActiveGroupQuery(String id) {
273276
return Query.query(Criteria.where(UserGroupEntity.Fields.ID).is(id)
@@ -303,26 +306,6 @@ private String findGroupId(UserGroupFilter userGroupFilter) {
303306
return foundGroups.getContent().get(0).getId();
304307
}
305308

306-
private void insertMembers(String id, Set<UUID> memberIds) {
307-
log.trace("insertMembers start");
308-
log.debug("insertMembers id = {}, memberIds = {}", Encode.forJava(id), Encode.forJava(memberIds.toString()));
309-
310-
// convert UUIDs
311-
Object[] userIdsAsStrings = memberIds.stream()
312-
.map(UUID::toString)
313-
.toArray();
314-
315-
mongoTemplate.updateFirst(
316-
createActiveGroupQuery(id),
317-
new Update()
318-
.addToSet(UserGroupEntity.Fields.members).each(userIdsAsStrings)
319-
.set(UserGroupEntity.Fields.modifiedBy, auditorAware.getCurrentAuditor().orElse(null))
320-
.currentDate(UserGroupEntity.Fields.modifiedAt),
321-
UserGroupEntity.class);
322-
323-
log.trace("insertMembers end");
324-
}
325-
326309
private void removeMembersWithParentInstitutionId(String id, Set<UUID> memberIds) {
327310
log.trace("removeMembersWithParentInstitutionId start");
328311
log.debug("removeMembersWithParentInstitutionId id = {}, memberIds = {}", Encode.forJava(id), Encode.forJava(memberIds.toString()));

apps/user-group-ms/src/test/java/it/pagopa/selfcare/user_group/controller/UserGroupV1ControllerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ void addMembers() throws Exception {
384384
//then
385385
assertEquals(0, result.getResponse().getContentLength());
386386
verify(groupServiceMock, times(1))
387-
.addMembers(anyString(), anyString(), anyString(), anySet());
387+
.createGroupOrAddMembers(any());
388388
Mockito.verifyNoMoreInteractions(groupServiceMock);
389389
}
390390

apps/user-group-ms/src/test/java/it/pagopa/selfcare/user_group/integration_test/steps/RetrieveUserGroupSteps.java

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import io.restassured.response.Response;
1414
import io.restassured.response.ResponseOptions;
1515
import io.restassured.specification.RequestSpecification;
16-
import it.pagopa.selfcare.user_group.model.AddMembersToUserGroupDto;
1716
import it.pagopa.selfcare.user_group.model.DeleteMembersFromUserGroupDto;
1817
import it.pagopa.selfcare.user_group.model.UserGroupEntity;
1918
import it.pagopa.selfcare.user_group.model.UserGroupStatus;
@@ -39,23 +38,6 @@ public void afterFeature() {
3938
userGroupRepository.deleteAllById(userGroupsIds);
4039
}
4140

42-
@DataTableType
43-
public AddMembersToUserGroupDto convertAddMembersRequest(Map<String, String> entry) {
44-
AddMembersToUserGroupDto request = new AddMembersToUserGroupDto();
45-
request.setInstitutionId(entry.get("institutionId"));
46-
request.setParentInstitutionId(entry.get("parentInstitutionId"));
47-
request.setProductId(entry.get("productId"));
48-
49-
Set<UUID> members = Optional.ofNullable(entry.get("members"))
50-
.map(s -> Arrays.stream(s.split(","))
51-
.map(UUID::fromString)
52-
.collect(Collectors.toSet()))
53-
.orElse(Set.of());
54-
request.setMembers(members);
55-
56-
return request;
57-
}
58-
5941
@DataTableType
6042
public DeleteMembersFromUserGroupDto convertDeleteMembersRequest(Map<String, String> entry) {
6143
DeleteMembersFromUserGroupDto request = new DeleteMembersFromUserGroupDto();
@@ -273,5 +255,10 @@ public void iShouldReceiveAResponseWithStatusCode(int expectedStatusCode) {
273255
public void createUserLoginWithUsernameAndPassword(String user, String pass) {
274256
super.login(user, pass);
275257
}
258+
259+
@Then("[RETRIEVE] the response should contain a valid user group resource with name {string}")
260+
public void verifyUserGroupName(String expectedName) {
261+
Assertions.assertEquals(expectedName, userGroupEntityResponse.getName());
262+
}
276263
}
277264

0 commit comments

Comments
 (0)