diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/AttributeBundleController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/AttributeBundleController.java index e438b77ae..a9ac9160a 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/AttributeBundleController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/AttributeBundleController.java @@ -17,6 +17,7 @@ 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.bind.annotation.RestController; @@ -27,12 +28,6 @@ public class AttributeBundleController { @Autowired AttributeBundleService attributeBundleService; - @GetMapping - @Transactional(readOnly = true) - public ResponseEntity getAll() { - return ResponseEntity.ok(attributeBundleService.findAll()); - } - @Secured("ROLE_ADMIN") @PostMapping @Transactional @@ -49,5 +44,17 @@ public ResponseEntity delete(@PathVariable String resourceId) throws EntityNo return ResponseEntity.noContent().build(); } - //PUT + @GetMapping + @Transactional(readOnly = true) + public ResponseEntity getAll() { + return ResponseEntity.ok(attributeBundleService.findAll()); + } + + @Secured("ROLE_ADMIN") + @PutMapping + @Transactional + public ResponseEntity update(@RequestBody AttributeBundle bundle) throws EntityNotFoundException { + AttributeBundle result = attributeBundleService.updateBundle(bundle); + return ResponseEntity.ok(result); + } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/AttributeBundleExceptionHandler.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/AttributeBundleExceptionHandler.java index 21940d84f..9f5266c3c 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/AttributeBundleExceptionHandler.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/AttributeBundleExceptionHandler.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.controller; +import edu.internet2.tier.shibboleth.admin.ui.exception.EntityNotFoundException; import edu.internet2.tier.shibboleth.admin.ui.exception.ObjectIdExistsException; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -7,9 +8,15 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @ControllerAdvice(assignableTypes = {AttributeBundleController.class}) -public class AttributeBundleExceptionHandler { +public class AttributeBundleExceptionHandler extends ResponseEntityExceptionHandler { + @ExceptionHandler({ EntityNotFoundException.class }) + public ResponseEntity handleEntityNotFoundException(EntityNotFoundException e, WebRequest request) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorResponse(HttpStatus.NOT_FOUND, e.getMessage())); + } + @ExceptionHandler({ ObjectIdExistsException.class }) public ResponseEntity handleObjectIdExistsException(ObjectIdExistsException e, WebRequest request) { HttpHeaders headers = new HttpHeaders(); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/AttributeBundleService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/AttributeBundleService.java index 15611a8b1..f246c7d1b 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/AttributeBundleService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/AttributeBundleService.java @@ -8,6 +8,7 @@ import org.springframework.stereotype.Service; import java.util.List; +import java.util.Optional; @Service public class AttributeBundleService { @@ -31,4 +32,15 @@ public void deleteDefinition(String resourceId) throws EntityNotFoundException { } attributeBundleRepository.deleteById(resourceId); } + + public AttributeBundle updateBundle(AttributeBundle bundle) throws EntityNotFoundException { + Optional dbBundle = attributeBundleRepository.findByResourceId(bundle.getResourceId()); + if (dbBundle.isEmpty()) { + throw new EntityNotFoundException(String.format("Unable to find attribute bundle with resource id: [%s] for update", bundle.getResourceId())); + } + AttributeBundle bundleToUpdate = dbBundle.get(); + bundleToUpdate.setName(bundle.getName()); + bundleToUpdate.setAttributes(bundle.getAttributes()); + return attributeBundleRepository.save(bundleToUpdate); + } } \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/AttributeBundleControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/AttributeBundleControllerTests.groovy index a9180071f..00e624b7e 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/AttributeBundleControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/AttributeBundleControllerTests.groovy @@ -20,14 +20,13 @@ import org.springframework.transaction.annotation.Transactional import org.springframework.web.util.NestedServletException import spock.lang.Specification -import static org.hamcrest.CoreMatchers.containsString import static org.hamcrest.Matchers.containsInAnyOrder import static org.springframework.http.MediaType.APPLICATION_JSON import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status @@ -42,10 +41,7 @@ class AttributeBundleControllerTests extends Specification { @Autowired AttributeBundleRepository attributeBundleRepository - ObjectMapper objectMapper = new ObjectMapper().with { - it.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS) - it - } + ObjectMapper objectMapper = new ObjectMapper() def MockMvc @@ -163,6 +159,49 @@ class AttributeBundleControllerTests extends Specification { attributeBundleRepository.findAll().isEmpty() } + def "Update checks" () { + expect: + attributeBundleRepository.findAll().isEmpty() + + when: "add a bundle" + def json = """ + { + "name": "bundleName", + "resourceId": "randomIDVal", + "attributes": ["eduPersonPrincipalName", "surname", "givenName"] + } + """ + AttributeBundle bundle = objectMapper.readValue(json, AttributeBundle.class) + attributeBundleRepository.saveAndFlush(bundle) + + then: "bundle doesn't exist" + bundle.setResourceId("foo") + try { + mockMvc.perform(put('/api/custom/entity/bundles').contentType(APPLICATION_JSON).content(objectMapper.writeValueAsString(bundle))) + false + } catch (NestedServletException expected) { + expected.getCause() instanceof EntityNotFoundException + } + + when: "update bundle" + json = """ + { + "name": "bundle2", + "resourceId": "randomIDVal", + "attributes": ["eduPersonUniqueId", "employeeNumber", "givenName"] + } + """ + + def result = mockMvc.perform(put('/api/custom/entity/bundles').contentType(APPLICATION_JSON).content(json)) + + then: + result.andExpect(status().isOk()) + .andExpect(content().contentType(APPLICATION_JSON)) + .andExpect(jsonPath("\$.name").value("bundle2")) + .andExpect(jsonPath("\$.resourceId").value("randomIDVal")) + .andExpect(jsonPath("\$.attributes", containsInAnyOrder("eduPersonUniqueId", "employeeNumber", "givenName"))) + } + // can go away with merge to develop and this extends the base test class @TestConfiguration private static class ABCTConfig { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/AttributeBundleRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/AttributeBundleRepositoryTests.groovy index e1c23c70a..0db6a9555 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/AttributeBundleRepositoryTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/AttributeBundleRepositoryTests.groovy @@ -19,10 +19,7 @@ class AttributeBundleRepositoryTests extends Specification { @Autowired AttributeBundleRepository attributeBundleRepository - ObjectMapper objectMapper = new ObjectMapper().with { - it.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS) - it - } + ObjectMapper objectMapper = new ObjectMapper() def "test create and fetch" () { given: