Skip to content

Commit 3c47d09

Browse files
committed
Merge branch 'alex/groups' into hibernate-refactor/roblex-buffer
2 parents a03b893 + 5b266d7 commit 3c47d09

17 files changed

+366
-140
lines changed

CONTRIBUTING.md

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ Please note we have a code of conduct, please follow it in all your interactions
6565
#### General
6666
1. DB via Test Containers - no in-memory DB or OS specific services
6767
2. No dependencies on any external services (ie. production micro-service)
68+
3. Tests **DO NOT** clear their data between runs, meaning that no test should rely on or expect a clean DB when running
6869

6970
##### Unit Testing
7071

pom.xml

+7-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,13 @@
159159
<version>2.9.5</version>
160160
</dependency>
161161

162-
<!-- JSON Testing -->
162+
<!-- Testing -->
163+
<dependency>
164+
<groupId>org.assertj</groupId>
165+
<artifactId>assertj-core</artifactId>
166+
<version>3.11.1</version>
167+
<scope>test</scope>
168+
</dependency>
163169
<dependency>
164170
<groupId>net.javacrumbs.json-unit</groupId>
165171
<artifactId>json-unit-fluent</artifactId>

src/main/java/bio/overture/ego/controller/GroupController.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,7 @@
5757
@RequestMapping("/groups")
5858
public class GroupController {
5959

60-
/** Dependencies */
6160
private final GroupService groupService;
62-
6361
private final ApplicationService applicationService;
6462
private final UserService userService;
6563

@@ -359,6 +357,17 @@ public void deleteAppsFromGroup(
359357
}
360358
}
361359

360+
@AdminScoped
361+
@RequestMapping(method = RequestMethod.POST, value = "/{id}/users")
362+
@ApiResponses(
363+
value = {@ApiResponse(code = 200, message = "Add Users to Group", response = Group.class)})
364+
public @ResponseBody Group addUsersToGroups(
365+
@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = true) final String accessToken,
366+
@PathVariable(value = "id", required = true) String grpId,
367+
@RequestBody(required = true) List<String> users) {
368+
return groupService.addUsersToGroup(grpId, users);
369+
}
370+
362371
@ExceptionHandler({EntityNotFoundException.class})
363372
public ResponseEntity<Object> handleEntityNotFoundException(
364373
HttpServletRequest req, EntityNotFoundException ex) {

src/main/java/bio/overture/ego/model/entity/Group.java

+23
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,25 @@
4040
@EqualsAndHashCode(of = {"id"})
4141
@ToString(exclude = {"users", "applications", "permissions"})
4242
@JsonPropertyOrder({"id", "name", "description", "status", "applications", "groupPermissions"})
43+
@NamedEntityGraph(
44+
name = "group-entity-with-relationships",
45+
attributeNodes = {
46+
@NamedAttributeNode("id"),
47+
@NamedAttributeNode("name"),
48+
@NamedAttributeNode("description"),
49+
@NamedAttributeNode("status"),
50+
@NamedAttributeNode(value = "users", subgraph = "users-subgraph"),
51+
@NamedAttributeNode(value = "applications", subgraph = "relationship-subgraph"),
52+
},
53+
subgraphs = {
54+
@NamedSubgraph(
55+
name = "relationship-subgraph",
56+
attributeNodes = {
57+
@NamedAttributeNode("id")
58+
}
59+
)
60+
}
61+
)
4362
public class Group implements PolicyOwner, Identifiable<UUID> {
4463

4564
@Id
@@ -67,6 +86,7 @@ public class Group implements PolicyOwner, Identifiable<UUID> {
6786
joinColumns = {@JoinColumn(name = Fields.GROUPID_JOIN)},
6887
inverseJoinColumns = {@JoinColumn(name = Fields.APPID_JOIN)})
6988
@JsonIgnore
89+
@Builder.Default
7090
private Set<Application> applications = new HashSet<>();
7191

7292
@ManyToMany(
@@ -77,14 +97,17 @@ public class Group implements PolicyOwner, Identifiable<UUID> {
7797
joinColumns = {@JoinColumn(name = Fields.GROUPID_JOIN)},
7898
inverseJoinColumns = {@JoinColumn(name = Fields.USERID_JOIN)})
7999
@JsonIgnore
100+
@Builder.Default
80101
private Set<User> users = new HashSet<>();
81102

82103
@JsonIgnore
104+
@Builder.Default
83105
@JoinColumn(name = Fields.OWNER)
84106
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
85107
private Set<Policy> policies = new HashSet<>();
86108

87109
@JsonIgnore
110+
@Builder.Default
88111
@JoinColumn(name = Fields.GROUPID_JOIN)
89112
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
90113
private Set<GroupPermission> permissions = new HashSet<>();

src/main/java/bio/overture/ego/model/entity/User.java

+20-11
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,7 @@
3535
import lombok.extern.slf4j.Slf4j;
3636
import org.hibernate.annotations.GenericGenerator;
3737

38-
import javax.persistence.CascadeType;
39-
import javax.persistence.Column;
40-
import javax.persistence.Entity;
41-
import javax.persistence.FetchType;
42-
import javax.persistence.GeneratedValue;
43-
import javax.persistence.Id;
44-
import javax.persistence.JoinColumn;
45-
import javax.persistence.JoinTable;
46-
import javax.persistence.ManyToMany;
47-
import javax.persistence.OneToMany;
48-
import javax.persistence.Table;
38+
import javax.persistence.*;
4939
import java.util.Date;
5040
import java.util.List;
5141
import java.util.Set;
@@ -87,6 +77,25 @@
8777
@AllArgsConstructor
8878
@NoArgsConstructor
8979
@JsonView(Views.REST.class)
80+
@NamedEntityGraph(
81+
name = "user-entity-with-relationships",
82+
attributeNodes = {
83+
@NamedAttributeNode("id"),
84+
@NamedAttributeNode("name"),
85+
@NamedAttributeNode("email"),
86+
@NamedAttributeNode("status"),
87+
@NamedAttributeNode(value = "groups", subgraph = "users-subgraph"),
88+
@NamedAttributeNode(value = "applications", subgraph = "relationship-subgraph"),
89+
},
90+
subgraphs = {
91+
@NamedSubgraph(
92+
name = "relationship-subgraph",
93+
attributeNodes = {
94+
@NamedAttributeNode("id")
95+
}
96+
)
97+
}
98+
)
9099
public class User implements PolicyOwner, Identifiable<UUID> {
91100

92101
// TODO: find JPA equivalent for GenericGenerator

src/main/java/bio/overture/ego/repository/GroupRepository.java

+9
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,23 @@
1717
package bio.overture.ego.repository;
1818

1919
import bio.overture.ego.model.entity.Group;
20+
import org.springframework.data.jpa.repository.EntityGraph;
21+
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
2022

23+
import java.util.List;
2124
import java.util.Optional;
25+
import java.util.Set;
2226
import java.util.UUID;
2327

2428
public interface GroupRepository extends NamedRepository<Group, UUID> {
2529

30+
@EntityGraph(value = "group-entity-with-relationships", type = EntityGraphType.FETCH)
31+
Group findOneByNameIgnoreCase(String name);
32+
2633
Optional<Group> getGroupByNameIgnoreCase(String name);
2734

35+
Set<Group> findAllByIdIn(List<UUID> groupIds);
36+
2837
@Override
2938
default Optional<Group> findByName(String name) {
3039
return getGroupByNameIgnoreCase(name);

src/main/java/bio/overture/ego/repository/UserRepository.java

+9
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,22 @@
1717
package bio.overture.ego.repository;
1818

1919
import bio.overture.ego.model.entity.User;
20+
import org.springframework.data.domain.Page;
21+
import org.springframework.data.domain.Pageable;
22+
import org.springframework.data.jpa.repository.EntityGraph;
23+
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
24+
import org.springframework.data.repository.PagingAndSortingRepository;
25+
2026
import java.util.Optional;
2127
import java.util.UUID;
2228

2329
public interface UserRepository extends NamedRepository<User, UUID> {
2430

2531
Optional<User> getUserByNameIgnoreCase(String name);
2632

33+
@EntityGraph(value = "user-entity-with-relationships", type = EntityGraph.EntityGraphType.FETCH)
34+
User findOneByNameIgnoreCase(String name);
35+
2736
boolean existsUserByNameIgnoreCase(String name);
2837

2938
@Override

src/main/java/bio/overture/ego/repository/queryspecification/GroupSpecification.java

+2
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@ public static Specification<Group> containsText(@Nonnull String text) {
3636

3737
public static Specification<Group> containsApplication(@Nonnull UUID appId) {
3838
return (root, query, builder) -> {
39+
query.distinct(true);
3940
Join<Application, Group> groupJoin = root.join("applications");
4041
return builder.equal(groupJoin.<Integer>get("id"), appId);
4142
};
4243
}
4344

4445
public static Specification<Group> containsUser(@Nonnull UUID userId) {
4546
return (root, query, builder) -> {
47+
query.distinct(true);
4648
Join<User, Group> groupJoin = root.join("users");
4749
return builder.equal(groupJoin.<Integer>get("id"), userId);
4850
};

src/main/java/bio/overture/ego/service/ApplicationService.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public Optional<Application> findApplicationByClientId(@NonNull String clientId)
142142
return applicationRepository.getApplicationByClientIdIgnoreCase(clientId);
143143
}
144144

145-
public Application getApplicationByClientId(@NonNull String clientId) {
145+
public Application getByClientId(@NonNull String clientId) {
146146
val result = findApplicationByClientId(clientId);
147147
checkNotFound(
148148
result.isPresent(),
@@ -167,15 +167,15 @@ public Application findByBasicToken(@NonNull String token) {
167167
val parts = contents.split(":");
168168
val clientId = parts[0];
169169
log.error(format("Extracted client id '%s'", clientId));
170-
return getApplicationByClientId(clientId);
170+
return getByClientId(clientId);
171171
}
172172

173173
@Override
174174
public ClientDetails loadClientByClientId(@NonNull String clientId)
175175
throws ClientRegistrationException {
176176
// find client using clientid
177177

178-
val application = getApplicationByClientId(clientId);
178+
val application = getByClientId(clientId);
179179

180180
if (application == null) {
181181
throw new ClientRegistrationException("Client ID not found.");

src/main/java/bio/overture/ego/service/GroupService.java

+38-5
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919
import bio.overture.ego.model.entity.Group;
2020
import bio.overture.ego.model.entity.GroupPermission;
2121
import bio.overture.ego.model.enums.AccessLevel;
22+
import bio.overture.ego.model.exceptions.NotFoundException;
2223
import bio.overture.ego.model.exceptions.PostWithIdentifierException;
2324
import bio.overture.ego.model.params.PolicyIdStringWithAccessLevel;
2425
import bio.overture.ego.model.search.SearchFilter;
26+
import bio.overture.ego.repository.ApplicationRepository;
2527
import bio.overture.ego.repository.GroupRepository;
28+
import bio.overture.ego.repository.UserRepository;
2629
import bio.overture.ego.repository.queryspecification.GroupSpecification;
2730
import com.google.common.collect.ImmutableList;
2831
import lombok.NonNull;
@@ -44,21 +47,27 @@
4447
public class GroupService extends AbstractNamedService<Group, UUID> {
4548

4649
private final GroupRepository groupRepository;
50+
private final UserRepository userRepository;
51+
private final ApplicationRepository applicationRepository;
4752
private final ApplicationService applicationService;
4853
private final PolicyService policyService;
4954
private final GroupPermissionService permissionService;
5055

5156
@Autowired
5257
public GroupService(
5358
@NonNull GroupRepository groupRepository,
54-
@NonNull ApplicationService applicationService,
59+
@NonNull UserRepository userRepository,
60+
@NonNull ApplicationRepository applicationRepository,
5561
@NonNull PolicyService policyService,
62+
@NonNull ApplicationService applicationService,
5663
@NonNull GroupPermissionService permissionService) {
5764
super(Group.class, groupRepository);
65+
this.groupRepository = groupRepository;
66+
this.userRepository = userRepository;
67+
this.applicationRepository = applicationRepository;
5868
this.applicationService = applicationService;
5969
this.policyService = policyService;
6070
this.permissionService = permissionService;
61-
this.groupRepository = groupRepository;
6271
}
6372

6473
public Group create(@NonNull Group groupInfo) {
@@ -79,6 +88,17 @@ public Group addAppsToGroup(@NonNull String grpId, @NonNull List<String> appIDs)
7988
return getRepository().save(group);
8089
}
8190

91+
public Group addUsersToGroup(@NonNull String grpId, @NonNull List<String> userIds) {
92+
val group = getById(fromString(grpId));
93+
userIds.forEach(
94+
userId -> {
95+
val user = userRepository.findById(fromString(userId)).orElseThrow(() -> new NotFoundException(String.format("Could not find User with ID: %s", userId)));
96+
group.getUsers().add(user);
97+
user.getGroups().add(group);
98+
});
99+
return groupRepository.save(group);
100+
}
101+
82102
public Group addGroupPermissions(
83103
@NonNull String groupId, @NonNull List<PolicyIdStringWithAccessLevel> permissions) {
84104
val group = getById(fromString(groupId));
@@ -98,7 +118,19 @@ public Group get(@NonNull String groupId) {
98118
}
99119

100120
public Group update(@NonNull Group other) {
101-
return groupRepository.save(other);
121+
val existingGroup = getById(other.getId());
122+
123+
val updatedGroup =
124+
Group.builder()
125+
.id(existingGroup.getId())
126+
.name(other.getName())
127+
.description(other.getDescription())
128+
.status(other.getStatus())
129+
.applications(existingGroup.getApplications())
130+
.users(existingGroup.getUsers())
131+
.build();;
132+
133+
return groupRepository.save(updatedGroup);
102134
}
103135

104136
// TODO - this was the original update - will use an improved version of this for the PATCH
@@ -191,8 +223,9 @@ public void deleteAppsFromGroup(@NonNull String grpId, @NonNull List<String> app
191223
// TODO - Properly handle invalid IDs here
192224
appIDs.forEach(
193225
appId -> {
194-
// TODO if app id not valid (does not exist) we need to throw EntityNotFoundException
195-
group.getApplications().remove(applicationService.get(appId));
226+
val app = applicationRepository.findById(fromString(appId)).orElseThrow(() -> new NotFoundException(String.format("Could not find Application with ID: %s", appId)));
227+
group.getApplications().remove(app);
228+
app.getGroups().remove(group);
196229
});
197230
groupRepository.save(group);
198231
}

src/main/java/bio/overture/ego/token/CustomTokenEnhancer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ private UserJWTAccessToken getUserAccessToken(String userName) {
5656
}
5757

5858
private AppJWTAccessToken getApplicationAccessToken(String clientId) {
59-
val app = applicationService.getApplicationByClientId(clientId);
59+
val app = applicationService.getByClientId(clientId);
6060
val token = tokenService.generateAppToken(app);
6161

6262
return tokenService.getAppAccessToken(token);

0 commit comments

Comments
 (0)