diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/CustomAttribute.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/CustomAttribute.java new file mode 100644 index 000000000..9c1574be6 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/CustomAttribute.java @@ -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") +@Audited +@Data +public class CustomAttribute { + @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_values", joinColumns = @JoinColumn(name = "name")) + @Column(name = "value", nullable = false) + Set customAttrValues = 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 +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/CustomAttributeRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/CustomAttributeRepository.java new file mode 100644 index 000000000..edbc4cd9b --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/CustomAttributeRepository.java @@ -0,0 +1,26 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository; + +import java.util.Date; +import java.util.List; + +import javax.transaction.Transactional; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import edu.internet2.tier.shibboleth.admin.ui.domain.CustomAttribute; + +/** + * Repository to manage {@link CustomAttribute} instances. + */ +public interface CustomAttributeRepository extends JpaRepository { + + List findAll(); + + CustomAttribute findByName(String name); + + @SuppressWarnings("unchecked") + CustomAttribute save(CustomAttribute attribute); +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/CustomAttributeRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/CustomAttributeRepositoryTests.groovy new file mode 100644 index 000000000..9aa14cab0 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/CustomAttributeRepositoryTests.groovy @@ -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.CustomAttribute +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(["val1", "val2", "val3"]) + def ca = new CustomAttribute().with { + it.name = "ca-name" + it.attributeType = "SELECTION_LIST" + it.customAttrValues = 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(CustomAttribute) + 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(CustomAttribute) + caFromDb2.equals(ca) == false + caFromDb2.equals(caFromDb1) == true + + // delete tests + when: + def delByName = new CustomAttribute().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(["val2", "val1"]) + def setItems3 = new HashSet(["val1", "val2", "val3"]) + def setItems4 = new HashSet(["val1", "val2", "val3", "val4"]) + def ca2 = new CustomAttribute().with { + it.name = "ca-name" + it.attributeType = "SELECTION_LIST" + it.customAttrValues = setItems2 + it + } + def ca3 = new CustomAttribute().with { + it.name = "ca-name" + it.attributeType = "SELECTION_LIST" + it.customAttrValues = setItems3 + it + } + def ca4 = new CustomAttribute().with { + it.name = "ca-name" + it.attributeType = "SELECTION_LIST" + it.customAttrValues = setItems4 + it + } + + when: + repo.save(ca3) + entityManager.flush() + entityManager.clear() + + then: + def cas = repo.findAll() + cas.size() == 1 + def caFromDb = cas.get(0).asType(CustomAttribute) + caFromDb.equals(ca3) == true + + // now update the attribute list items + caFromDb.with { + it.customAttrValues = setItems4 + it + } + repo.save(caFromDb) + entityManager.flush() + entityManager.clear() + + def caFromDb4 = repo.findAll().get(0).asType(CustomAttribute) + caFromDb4.equals(ca4) == true + + // now remove items + caFromDb.with { + it.customAttrValues = setItems2 + it + } + repo.save(caFromDb) + entityManager.flush() + entityManager.clear() + + def caFromDb2 = repo.findAll().get(0).asType(CustomAttribute) + caFromDb2.equals(ca2) == true + } +} \ No newline at end of file