-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Basic controller and supporting items for CRUD operations around user groups
- Loading branch information
Showing
6 changed files
with
354 additions
and
0 deletions.
There are no files selected for viewing
115 changes: 115 additions & 0 deletions
115
backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/GroupController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| package edu.internet2.tier.shibboleth.admin.ui.controller; | ||
|
|
||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.http.HttpHeaders; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.stereotype.Controller; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
| import org.springframework.web.bind.annotation.DeleteMapping; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.PostMapping; | ||
| import org.springframework.web.bind.annotation.PutMapping; | ||
| import org.springframework.web.bind.annotation.RequestBody; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.servlet.support.ServletUriComponentsBuilder; | ||
|
|
||
| import edu.internet2.tier.shibboleth.admin.ui.domain.Group; | ||
| import edu.internet2.tier.shibboleth.admin.ui.service.IGroupService; | ||
|
|
||
| @Controller | ||
| @RequestMapping(value = "/api/groups") | ||
| public class GroupController { | ||
| @Autowired | ||
| private IGroupService groupService; | ||
|
|
||
| @PostMapping | ||
| @Transactional | ||
| public ResponseEntity<?> create(@RequestBody Group group) { | ||
| // If already defined, we can't create a new one, nor will this call update the definition | ||
| Group g = groupService.find(group.getResourceId()); | ||
|
|
||
| if (g != null) { | ||
| HttpHeaders headers = new HttpHeaders(); | ||
| headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/groups").build().toUri()); | ||
|
|
||
| return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).headers(headers) | ||
| .body(new ErrorResponse(String.valueOf(HttpStatus.METHOD_NOT_ALLOWED.value()), | ||
| String.format("The group with resource id: [%s] and name: [%s] already exists.", | ||
| group.getResourceId(), group.getName()))); | ||
| } | ||
|
|
||
| Group result = groupService.createOrUpdateGroup(g); | ||
| return ResponseEntity.status(HttpStatus.CREATED).body(result); | ||
| } | ||
|
|
||
| @PutMapping | ||
| @Transactional | ||
| public ResponseEntity<?> update(@RequestBody Group group) { | ||
| Group g = groupService.find(group.getResourceId()); | ||
|
|
||
| if (g == null) { | ||
| HttpHeaders headers = new HttpHeaders(); | ||
| headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/groups").build().toUri()); | ||
|
|
||
| return ResponseEntity.status(HttpStatus.NOT_FOUND).headers(headers) | ||
| .body(new ErrorResponse(String.valueOf(HttpStatus.NOT_FOUND.value()), | ||
| String.format("Unable to find group with resource id: [%s] and name: [%s]", | ||
| group.getResourceId(), group.getName()))); | ||
| } | ||
|
|
||
| Group result = groupService.createOrUpdateGroup(g); | ||
| return ResponseEntity.ok(result); | ||
| } | ||
|
|
||
| @GetMapping | ||
| @Transactional(readOnly = true) | ||
| public ResponseEntity<?> getAll() { | ||
| return ResponseEntity.ok(groupService.findAll()); | ||
| } | ||
|
|
||
| @GetMapping("/{resourceId}") | ||
| @Transactional(readOnly = true) | ||
| public ResponseEntity<?> getOne(@PathVariable String resourceId) { | ||
| Group g = groupService.find(resourceId); | ||
|
|
||
| if (g == null) { | ||
| HttpHeaders headers = new HttpHeaders(); | ||
| headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/groups").build().toUri()); | ||
|
|
||
| return ResponseEntity.status(HttpStatus.NOT_FOUND).headers(headers) | ||
| .body(new ErrorResponse(String.valueOf(HttpStatus.NOT_FOUND.value()), | ||
| String.format("Unable to find group with resource id: [%s]", resourceId))); | ||
| } | ||
| return ResponseEntity.ok(g); | ||
| } | ||
|
|
||
| @DeleteMapping("/{resourceId}") | ||
| @Transactional | ||
| public ResponseEntity<?> delete(@PathVariable String resourceId) { | ||
| Group g = groupService.find(resourceId); | ||
|
|
||
| if (g == null) { | ||
| HttpHeaders headers = new HttpHeaders(); | ||
| headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/groups").build().toUri()); | ||
|
|
||
| return ResponseEntity.status(HttpStatus.NOT_FOUND).headers(headers) | ||
| .body(new ErrorResponse(String.valueOf(HttpStatus.NOT_FOUND.value()), | ||
| String.format("Unable to find group with resource id: [%s]", resourceId))); | ||
| } | ||
| try { | ||
| groupService.deleteDefinition(g); | ||
| } | ||
| catch (Exception e) { | ||
| HttpHeaders headers = new HttpHeaders(); | ||
| headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/groups").build().toUri()); | ||
|
|
||
| return ResponseEntity.status(HttpStatus.CONFLICT).headers(headers) | ||
| .body(new ErrorResponse(String.valueOf(HttpStatus.CONFLICT.value()), String.format( | ||
| "Unable to delete group with resource id: [%s] - remove all users from group", | ||
| resourceId))); | ||
| } | ||
| return ResponseEntity.noContent().build(); | ||
| } | ||
| } |
26 changes: 26 additions & 0 deletions
26
backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Group.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package edu.internet2.tier.shibboleth.admin.ui.domain; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| import javax.persistence.Column; | ||
| import javax.persistence.Entity; | ||
| import javax.persistence.Id; | ||
|
|
||
| import org.hibernate.envers.Audited; | ||
|
|
||
| import lombok.Data; | ||
|
|
||
| @Entity(name = "user_groups") | ||
| @Audited | ||
| @Data | ||
| public class Group { | ||
| @Column(name = "group_description", nullable = true) | ||
| String description; | ||
|
|
||
| @Column(nullable = false) | ||
| String name; | ||
|
|
||
| @Id | ||
| @Column(name = "resource_id", nullable = false) | ||
| String resourceId = UUID.randomUUID().toString(); | ||
| } |
16 changes: 16 additions & 0 deletions
16
backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/GroupRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package edu.internet2.tier.shibboleth.admin.ui.repository; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
|
|
||
| import edu.internet2.tier.shibboleth.admin.ui.domain.Group; | ||
|
|
||
| public interface GroupRepository extends JpaRepository<Group, String> { | ||
| List<Group> findAll(); | ||
|
|
||
| Group findByResourceId(String id); | ||
|
|
||
| @SuppressWarnings("unchecked") | ||
| Group save(Group group); | ||
| } |
36 changes: 36 additions & 0 deletions
36
backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/GroupServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package edu.internet2.tier.shibboleth.admin.ui.service; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| import edu.internet2.tier.shibboleth.admin.ui.domain.Group; | ||
| import edu.internet2.tier.shibboleth.admin.ui.repository.GroupRepository; | ||
|
|
||
| @Service | ||
| public class GroupServiceImpl implements IGroupService { | ||
| @Autowired | ||
| private GroupRepository repo; | ||
|
|
||
| @Override | ||
| public Group createOrUpdateGroup(Group group) { | ||
| return repo.save(group); | ||
| } | ||
|
|
||
| @Override | ||
| public void deleteDefinition(Group group) { | ||
| repo.delete(group); | ||
| } | ||
|
|
||
| @Override | ||
| public Group find(String resourceId) { | ||
| return repo.findByResourceId(resourceId); | ||
| } | ||
|
|
||
| @Override | ||
| public List<Group> findAll() { | ||
| return repo.findAll(); | ||
| } | ||
|
|
||
| } |
17 changes: 17 additions & 0 deletions
17
backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/IGroupService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package edu.internet2.tier.shibboleth.admin.ui.service; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import edu.internet2.tier.shibboleth.admin.ui.domain.Group; | ||
|
|
||
| public interface IGroupService { | ||
|
|
||
| Group createOrUpdateGroup(Group g); | ||
|
|
||
| void deleteDefinition(Group g); | ||
|
|
||
| Group find(String resourceId); | ||
|
|
||
| List<Group> findAll(); | ||
|
|
||
| } |
144 changes: 144 additions & 0 deletions
144
...test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/GroupRepositoryTests.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| package edu.internet2.tier.shibboleth.admin.ui.repository | ||
|
|
||
| import javax.persistence.EntityManager | ||
|
|
||
| import org.springframework.beans.factory.annotation.Autowired | ||
| import org.springframework.boot.autoconfigure.domain.EntityScan | ||
| import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest | ||
| import org.springframework.data.jpa.repository.config.EnableJpaRepositories | ||
| import org.springframework.test.context.ContextConfiguration | ||
|
|
||
| import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration | ||
| import edu.internet2.tier.shibboleth.admin.ui.domain.CustomEntityAttributeDefinition | ||
| import edu.internet2.tier.shibboleth.admin.ui.domain.Group | ||
| import spock.lang.Specification | ||
|
|
||
| /** | ||
| * Tests to validate the repo and model for groups | ||
| * @author chasegawa | ||
| */ | ||
| @DataJpaTest | ||
| @ContextConfiguration(classes=[InternationalizationConfiguration]) | ||
| @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) | ||
| @EntityScan("edu.internet2.tier.shibboleth.admin.ui") | ||
| class GroupRepositoryTests extends Specification { | ||
| @Autowired | ||
| GroupRepository repo | ||
|
|
||
| @Autowired | ||
| EntityManager entityManager | ||
|
|
||
| def "simple create test"() { | ||
| given: | ||
| def group = new Group().with { | ||
| it.name = "group-name" | ||
| it.description = "some description" | ||
| it | ||
| } | ||
|
|
||
| // Confirm empty state | ||
| when: | ||
| def groups = repo.findAll() | ||
|
|
||
| then: | ||
| groups.size() == 0 | ||
|
|
||
| // save check | ||
| when: | ||
| repo.save(group) | ||
| entityManager.flush() | ||
| entityManager.clear() | ||
|
|
||
| then: | ||
| // save check | ||
| def gList = repo.findAll() | ||
| gList.size() == 1 | ||
| def groupFromDb = gList.get(0).asType(Group) | ||
| groupFromDb.equals(group) == true | ||
|
|
||
| // fetch checks | ||
| repo.findByResourceId("not an id") == null | ||
| repo.findByResourceId(groupFromDb.resourceId).equals(group) | ||
| } | ||
|
|
||
| def "expected error"() { | ||
| given: | ||
| def group = new Group().with { | ||
| it.description = "some description" | ||
| it | ||
| } | ||
|
|
||
| // Confirm empty state | ||
| when: | ||
| def gList = repo.findAll() | ||
|
|
||
| then: | ||
| gList.size() == 0 | ||
|
|
||
| // save check | ||
| when: | ||
| repo.save(group) | ||
| entityManager.flush() | ||
| entityManager.clear() | ||
|
|
||
| then: | ||
| // Missing non-nullable field (name) should thrown error | ||
| final def exception = thrown(javax.persistence.PersistenceException) | ||
| } | ||
|
|
||
| def "basic CRUD operations validated"() { | ||
| given: | ||
| def group = new Group().with { | ||
| it.name = "group-name" | ||
| it.description = "some description" | ||
| it | ||
| } | ||
|
|
||
| // Confirm empty state | ||
| when: | ||
| def groups = repo.findAll() | ||
|
|
||
| then: | ||
| groups.size() == 0 | ||
|
|
||
| // save check | ||
| when: | ||
| repo.save(group) | ||
| entityManager.flush() | ||
| entityManager.clear() | ||
|
|
||
| then: | ||
| // save check | ||
| def gList = repo.findAll() | ||
| gList.size() == 1 | ||
| def groupFromDb = gList.get(0).asType(Group) | ||
| groupFromDb.equals(group) == true | ||
|
|
||
| // update check | ||
| groupFromDb.with { | ||
| it.description = "some new text that wasn't there before" | ||
| } | ||
| groupFromDb.equals(group) == false | ||
|
|
||
| when: | ||
| repo.save(groupFromDb) | ||
| entityManager.flush() | ||
| entityManager.clear() | ||
|
|
||
| then: | ||
| def gList2 = repo.findAll() | ||
| gList2.size() == 1 | ||
| def groupFromDb2 = gList2.get(0).asType(Group) | ||
| groupFromDb2.equals(group) == false | ||
| groupFromDb2.equals(groupFromDb) == true | ||
|
|
||
| // delete tests | ||
| when: | ||
| repo.delete(groupFromDb2) | ||
| entityManager.flush() | ||
| entityManager.clear() | ||
|
|
||
| then: | ||
| repo.findAll().size() == 0 | ||
| } | ||
| } |