Skip to content

Commit

Permalink
Merged in feature/SHIBUI-1734 (pull request #477)
Browse files Browse the repository at this point in the history
Feature/SHIBUI-1734
  • Loading branch information
chasegawa authored and Jonathan Johnson committed May 20, 2021
2 parents b9052e4 + a9699c1 commit 8fda70e
Show file tree
Hide file tree
Showing 6 changed files with 377 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package edu.internet2.tier.shibboleth.admin.ui.controller;

import java.util.List;

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.CustomAttributeDefinition;
import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor;
import edu.internet2.tier.shibboleth.admin.ui.service.CustomAttributesService;

@Controller
@RequestMapping(value = "/api/custom")
public class CustomAttributesController {
@Autowired
private CustomAttributesService caService;

@PostMapping("/attribute")
@Transactional
public ResponseEntity<?> create(@RequestBody CustomAttributeDefinition definition) {
// If already defined, we can't create a new one, nor will this call update the definition
CustomAttributeDefinition cad = caService.find(definition.getName());

if (cad != null) {
HttpHeaders headers = new HttpHeaders();
headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/custom/attribute").build().toUri());

return ResponseEntity.status(HttpStatus.CONFLICT).headers(headers)
.body(new ErrorResponse(String.valueOf(HttpStatus.CONFLICT.value()),
String.format("The custom attribute definition with name: [%s] already exists.", definition.getName())));
}

CustomAttributeDefinition result = caService.createOrUpdateDefinition(definition);
return ResponseEntity.status(HttpStatus.CREATED).body(result);
}

@PutMapping("/attribute")
@Transactional
public ResponseEntity<?> update(@RequestBody CustomAttributeDefinition definition) {
CustomAttributeDefinition cad = caService.find(definition.getName());
if (cad == null) {
HttpHeaders headers = new HttpHeaders();
headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/custom/attribute").build().toUri());

return ResponseEntity.status(HttpStatus.NOT_FOUND).headers(headers)
.body(new ErrorResponse(String.valueOf(HttpStatus.NOT_FOUND.value()),
String.format("The custom attribute definition with name: [%s] does not already exist.", definition.getName())));
}

CustomAttributeDefinition result = caService.createOrUpdateDefinition(definition);
return ResponseEntity.ok(result);
}

@GetMapping("/attributes")
@Transactional(readOnly = true)
public ResponseEntity<?> getAll() {
return ResponseEntity.ok(caService.getAllDefinitions());
}

@GetMapping("/attribute/{name}")
@Transactional(readOnly = true)
public ResponseEntity<?> getOne(@PathVariable String name) {
CustomAttributeDefinition cad = caService.find(name);
if (cad == null) {
HttpHeaders headers = new HttpHeaders();
headers.setLocation(
ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/custom/attribute/" + name).build().toUri());

return ResponseEntity.status(HttpStatus.NOT_FOUND).headers(headers)
.body(new ErrorResponse(String.valueOf(HttpStatus.NOT_FOUND.value()),
String.format("The custom attribute definition with name: [%s] does not already exist.", name)));
}
return ResponseEntity.ok(cad);
}

@DeleteMapping("/attribute/{name}")
@Transactional
public ResponseEntity<?> delete(@PathVariable String name) {
CustomAttributeDefinition cad = caService.find(name);
if (cad == null) {
HttpHeaders headers = new HttpHeaders();
headers.setLocation(
ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/custom/attribute/" + name).build().toUri());

return ResponseEntity.status(HttpStatus.NOT_FOUND).headers(headers)
.body(new ErrorResponse(String.valueOf(HttpStatus.NOT_FOUND.value()),
String.format("The custom attribute definition with name: [%s] does not already exist.", name)));
}
caService.deleteDefinition(cad);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package edu.internet2.tier.shibboleth.admin.ui.domain;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;

import org.hibernate.envers.Audited;

import lombok.Data;

@Entity(name = "custom_attribute_definition")
@Audited
@Data
public class CustomAttributeDefinition {
@Id
@Column(nullable = false)
String name;

@Column(name = "help_text", nullable = true)
String helpText;

@Column(name = "attribute_type", nullable = false)
CustomAttributeType attributeType;

@Column(name = "default_value", nullable = true)
String defaultValue;

@ElementCollection
@CollectionTable(name = "custom_attr_list_defs", joinColumns = @JoinColumn(name = "name"))
@Column(name = "value", nullable = false)
Set<String> customAttrListDefinitions = new HashSet<>();

// @TODO: logic to ensure defaultValue matches an item from the list of values when SELECTION_LIST is the type ??
}

enum CustomAttributeType {
BOOLEAN, INTEGER, LONG, DOUBLE, DURATION, SELECTION_LIST, SPRING_BEAN_ID
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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.CustomAttributeDefinition;

/**
* Repository to manage {@link CustomAttributeDefinition} instances.
*/
public interface CustomAttributeRepository extends JpaRepository<CustomAttributeDefinition, String> {

List<CustomAttributeDefinition> findAll();

CustomAttributeDefinition findByName(String name);

@SuppressWarnings("unchecked")
CustomAttributeDefinition save(CustomAttributeDefinition attribute);
}
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.CustomAttributeDefinition;

public interface CustomAttributesService {

CustomAttributeDefinition createOrUpdateDefinition(CustomAttributeDefinition definition);

void deleteDefinition(CustomAttributeDefinition definition);

CustomAttributeDefinition find(String name);

List<CustomAttributeDefinition> getAllDefinitions();

}
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.CustomAttributeDefinition;
import edu.internet2.tier.shibboleth.admin.ui.repository.CustomAttributeRepository;

@Service
public class CustomAttributesServiceImpl implements CustomAttributesService {
@Autowired
private CustomAttributeRepository repository;

@Override
public CustomAttributeDefinition createOrUpdateDefinition(CustomAttributeDefinition definition) {
return repository.save(definition);
}

@Override
public void deleteDefinition(CustomAttributeDefinition definition) {
repository.delete(definition);
}

@Override
public CustomAttributeDefinition find(String name) {
return repository.findByName(name);
}

@Override
public List<CustomAttributeDefinition> getAllDefinitions() {
return repository.findAll();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
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.CustomAttributeDefinition
import spock.lang.Specification

/**
* Tests to validate the repo and model for custom entity attributes
* @author chasegawa
*/
@DataJpaTest
@ContextConfiguration(classes=[InternationalizationConfiguration])
@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"])
@EntityScan("edu.internet2.tier.shibboleth.admin.ui")
class CustomAttributeRepositoryTests extends Specification {

@Autowired
CustomAttributeRepository repo

@Autowired
EntityManager entityManager

def "basic CRUD operations validated"() {
given:
def setItems = new HashSet<String>(["val1", "val2", "val3"])
def ca = new CustomAttributeDefinition().with {
it.name = "ca-name"
it.attributeType = "SELECTION_LIST"
it.customAttrListDefinitions = setItems
it
}

// Confirm empty state
when:
def atts = repo.findAll()

then:
atts.size() == 0

// save check
when:
repo.save(ca)
entityManager.flush()
entityManager.clear()

then:
// save check
def cas = repo.findAll()
cas.size() == 1
def caFromDb1 = cas.get(0).asType(CustomAttributeDefinition)
caFromDb1.equals(ca) == true

// fetch checks
repo.findByName("not a name") == null
repo.findByName("ca-name").equals(ca)

// update check
caFromDb1.with {
it.helpText = "some new text that wasn't there before"
}
caFromDb1.equals(ca) == false

when:
repo.save(caFromDb1)
entityManager.flush()
entityManager.clear()

then:
def cas2 = repo.findAll()
cas2.size() == 1
def caFromDb2 = cas2.get(0).asType(CustomAttributeDefinition)
caFromDb2.equals(ca) == false
caFromDb2.equals(caFromDb1) == true

// delete tests
when:
def delByName = new CustomAttributeDefinition().with {
it.name = "ca-name"
it
}
repo.delete(delByName)
entityManager.flush()
entityManager.clear()

then:
repo.findAll().size() == 0
}

def "attribute list tests"() {
given:
def setItems2 = new HashSet<String>(["val2", "val1"])
def setItems3 = new HashSet<String>(["val1", "val2", "val3"])
def setItems4 = new HashSet<String>(["val1", "val2", "val3", "val4"])
def ca2 = new CustomAttributeDefinition().with {
it.name = "ca-name"
it.attributeType = "SELECTION_LIST"
it.customAttrListDefinitions = setItems2
it
}
def ca3 = new CustomAttributeDefinition().with {
it.name = "ca-name"
it.attributeType = "SELECTION_LIST"
it.customAttrListDefinitions = setItems3
it
}
def ca4 = new CustomAttributeDefinition().with {
it.name = "ca-name"
it.attributeType = "SELECTION_LIST"
it.customAttrListDefinitions = setItems4
it
}

when:
repo.save(ca3)
entityManager.flush()
entityManager.clear()

then:
def cas = repo.findAll()
cas.size() == 1
def caFromDb = cas.get(0).asType(CustomAttributeDefinition)
caFromDb.equals(ca3) == true

// now update the attribute list items
caFromDb.with {
it.customAttrListDefinitions = setItems4
it
}
repo.save(caFromDb)
entityManager.flush()
entityManager.clear()

def caFromDb4 = repo.findAll().get(0).asType(CustomAttributeDefinition)
caFromDb4.equals(ca4) == true

// now remove items
caFromDb.with {
it.customAttrListDefinitions = setItems2
it
}
repo.save(caFromDb)
entityManager.flush()
entityManager.clear()

def caFromDb2 = repo.findAll().get(0).asType(CustomAttributeDefinition)
caFromDb2.equals(ca2) == true
}
}

0 comments on commit 8fda70e

Please sign in to comment.