Skip to content

Commit

Permalink
Promote To BETA (#316)
Browse files Browse the repository at this point in the history
Promote changes to BETA

Bug Fixes:
* Pivotal ID # 176052254: JSON Submission Attributes Validation (#310)
* Pivotal ID # 176052264: Inner Folder Mapping (#311)
* Pivotal ID # 172671360: Ignore Empty Value Attributes (#314)
  • Loading branch information
jhoanmanuelms authored Dec 18, 2020
1 parent 91c3a8b commit 64b7017
Show file tree
Hide file tree
Showing 29 changed files with 395 additions and 35 deletions.
15 changes: 15 additions & 0 deletions client/bio-webclient-demo/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Dependencies.KotlinLogging
import Dependencies.KotlinStdLib
import Dependencies.Log4J
import TestDependencies.BaseTestCompileDependencies
import TestDependencies.BaseTestRuntimeDependencies

dependencies {
api(project(":client:bio-webclient"))
implementation(KotlinStdLib)
implementation(Log4J)
implementation(KotlinLogging)

BaseTestCompileDependencies.forEach { testImplementation(it) }
BaseTestRuntimeDependencies.forEach { testImplementation(it) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package ac.uk.ebi.biostd.client.demo

import ac.uk.ebi.biostd.client.integration.web.SecurityWebClient
import ebi.ac.uk.dsl.attribute
import ebi.ac.uk.dsl.file
import ebi.ac.uk.dsl.link
import ebi.ac.uk.dsl.section
import ebi.ac.uk.dsl.submission
import ebi.ac.uk.model.Attribute
import ebi.ac.uk.model.extensions.title
import mu.KotlinLogging
import java.nio.file.Files

private val logger = KotlinLogging.logger {}

/**
* Demonstrate how to submit a simple submission using bio-studies client and kotlin submission dsl. Note that
*
* 1. Client Serialization service is used to serialize class representation into json.
* 2. Files are uploaded into user file directory first.
*/
fun main(args: Array<String>) {
val client = SecurityWebClient
.create(baseUrl = args[0])
.getAuthenticatedClient(args[1], args[2])

val directory = Files.createTempDirectory("submission-files")
val submissionFile = Files.createTempFile(directory, "sub-file-", ".tmp").toFile()

client.uploadFiles(listOf(submissionFile))
val response = client.submitSingle(submission {
title = "my-submission example"
attribute("Attribute", "Attr Value 1")
attribute("Another-Attribute", "Another-Attribute Value")
section("study") {
attributes = listOf(Attribute("attribute-1", "attributeValue"))
link("AF069309") {
attribute("type", "gen")
}

file(submissionFile.name) {
size = 0
attribute("Description", "Data File 1")
}

section("Author") {
attribute("Name", "Mr Doctor")
}
}
})

logger.info { "successfully created submission ${response.body.accNo}" }
}
8 changes: 4 additions & 4 deletions commons/commons-bio/src/main/kotlin/ebi/ac/uk/dsl/SubDsl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package ebi.ac.uk.dsl

import ebi.ac.uk.model.Attributable
import ebi.ac.uk.model.Attribute
import ebi.ac.uk.model.Attributes
import ebi.ac.uk.model.AttributeDetail
import ebi.ac.uk.model.File
import ebi.ac.uk.model.FilesTable
import ebi.ac.uk.model.Link
Expand All @@ -17,12 +17,12 @@ fun Attributable.attribute(
name: String,
value: String,
ref: Boolean = false,
valueAttrs: Attributes = mutableListOf(),
nameAttrs: Attributes = mutableListOf()
valueAttrs: MutableList<AttributeDetail> = mutableListOf(),
nameAttrs: MutableList<AttributeDetail> = mutableListOf()
) = addAttribute(Attribute(name = name, value = value, valueAttrs = valueAttrs, reference = ref, nameAttrs = nameAttrs))

fun Submission.section(type: String, block: Section.() -> Unit) = apply { section = Section(type).apply(block) }
fun submission(accNo: String, block: Submission.() -> Unit): Submission = Submission(accNo).apply(block)
fun submission(accNo: String = "", block: Submission.() -> Unit): Submission = Submission(accNo).apply(block)
fun section(type: String, block: Section.() -> Unit): Section = Section(type).apply(block)
fun sectionsTable(block: SectionsTable.() -> Unit) = SectionsTable().apply(block)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@ package ebi.ac.uk.model
import ebi.ac.uk.base.EMPTY
import java.util.Objects

typealias Attributes = MutableList<AttributeDetail>

data class Attribute(
var name: String,
var value: String,
var reference: Boolean = false,
var nameAttrs: Attributes = mutableListOf(),
var valueAttrs: Attributes = mutableListOf()
var nameAttrs: MutableList<AttributeDetail> = mutableListOf(),
var valueAttrs: MutableList<AttributeDetail> = mutableListOf()
) {

constructor(name: Any, value: Any) : this(name.toString(), value.toString())

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ac.uk.ebi.biostd.json

import ac.uk.ebi.biostd.json.deserialization.AttributeDetailJsonDeserializer
import ac.uk.ebi.biostd.json.deserialization.AttributeJsonDeserializer
import ac.uk.ebi.biostd.json.deserialization.FileJsonDeserializer
import ac.uk.ebi.biostd.json.deserialization.FilesTableJsonDeserializer
Expand All @@ -22,6 +23,7 @@ import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import ebi.ac.uk.model.Attribute
import ebi.ac.uk.model.AttributeDetail
import ebi.ac.uk.model.File
import ebi.ac.uk.model.FilesTable
import ebi.ac.uk.model.Link
Expand Down Expand Up @@ -63,6 +65,7 @@ internal class JsonSerializer {
addDeserializer(Link::class.java, LinkJsonDeserializer())
addDeserializer(File::class.java, FileJsonDeserializer())
addDeserializer(Attribute::class.java, AttributeJsonDeserializer())
addDeserializer(AttributeDetail::class.java, AttributeDetailJsonDeserializer())
addDeserializer(Either::class.java, EitherDeserializer())
addDeserializer(LinksTable::class.java, LinksTableJsonDeserializer())
addDeserializer(SectionsTable::class.java, SectionsTableJsonDeserializer())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ac.uk.ebi.biostd.json.deserialization

import ac.uk.ebi.biostd.json.deserialization.AttributeJsonDeserializerHelper.deserializeNameValue
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import ebi.ac.uk.model.AttributeDetail

internal class AttributeDetailJsonDeserializer : StdDeserializer<AttributeDetail>(AttributeDetail::class.java) {
override fun deserialize(jp: JsonParser, ctxt: DeserializationContext): AttributeDetail {
val mapper = jp.codec as ObjectMapper
val node: JsonNode = mapper.readTree(jp)
val (name, value) = deserializeNameValue(node)

return AttributeDetail(name, value)
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,23 @@
package ac.uk.ebi.biostd.json.deserialization

import ac.uk.ebi.biostd.common.NAME
import ac.uk.ebi.biostd.common.NAME_ATTRIBUTES
import ac.uk.ebi.biostd.common.REFERENCE
import ac.uk.ebi.biostd.common.VALUE
import ac.uk.ebi.biostd.common.VAL_ATTRIBUTES
import ac.uk.ebi.biostd.json.exception.NoAttributeValueException
import ac.uk.ebi.biostd.json.deserialization.AttributeJsonDeserializerHelper.deserializeNameValue
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import com.fasterxml.jackson.databind.node.TextNode
import ebi.ac.uk.base.orFalse
import ebi.ac.uk.model.Attribute
import uk.ac.ebi.serialization.extensions.convertList
import uk.ac.ebi.serialization.extensions.getNode

internal class AttributeJsonDeserializer : StdDeserializer<Attribute>(Attribute::class.java) {
override fun deserialize(jp: JsonParser, ctxt: DeserializationContext): Attribute {
val mapper = jp.codec as ObjectMapper
val node: JsonNode = mapper.readTree(jp)
val name = node.getNode<TextNode>(NAME).textValue()
val value = node.getNode<TextNode>(VALUE).textValue()
require(value.isNotBlank()) { throw NoAttributeValueException(name) }
val (name, value) = deserializeNameValue(node)

return Attribute(
name = name,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ac.uk.ebi.biostd.json.deserialization

import ac.uk.ebi.biostd.common.NAME
import ac.uk.ebi.biostd.common.VALUE
import ac.uk.ebi.biostd.json.exception.NoAttributeValueException
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.node.TextNode
import uk.ac.ebi.serialization.extensions.getNode

internal object AttributeJsonDeserializerHelper {
fun deserializeNameValue(node: JsonNode): Pair<String, String> {
val name = node.getNode<TextNode>(NAME).textValue()
val value = node.getNode<TextNode>(VALUE).textValue()
require(value.isNotBlank()) { throw NoAttributeValueException(name) }

return name to value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,62 @@ class AttributeDeserializerTest {

assertThat(testInstance.deserialize<Attribute>(attributeJson)).isEqualTo(attr)
}

@Test
fun `deserialize attribute with empty value qualifier value`() {
val valDetails = AttributeDetail("t1", "v1")
val nameDetails = AttributeDetail("t2", "v2")

val attr = Attribute(
name = "attr name",
value = "attr value",
reference = true,
nameAttrs = mutableListOf(nameDetails),
valueAttrs = mutableListOf(valDetails))

val attributeJson = jsonObj {
"name" to attr.name
"value" to attr.value
"reference" to attr.reference
"valqual" to jsonArray({
"name" to valDetails.name
"value" to ""
})
"nmqual" to jsonArray({
"name" to nameDetails.name
"value" to nameDetails.value
})
}.toString()

assertThrows<IllegalArgumentException> { testInstance.deserialize<Attribute>(attributeJson) }
}

@Test
fun `deserialize attribute with empty name qualifier value`() {
val valDetails = AttributeDetail("t1", "v1")
val nameDetails = AttributeDetail("t2", "v2")

val attr = Attribute(
name = "attr name",
value = "attr value",
reference = true,
nameAttrs = mutableListOf(nameDetails),
valueAttrs = mutableListOf(valDetails))

val attributeJson = jsonObj {
"name" to attr.name
"value" to attr.value
"reference" to attr.reference
"valqual" to jsonArray({
"name" to valDetails.name
"value" to valDetails.value
})
"nmqual" to jsonArray({
"name" to nameDetails.name
"value" to ""
})
}.toString()

assertThrows<IllegalArgumentException> { testInstance.deserialize<Attribute>(attributeJson) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package ac.uk.ebi.biostd.json.deserialization

import ac.uk.ebi.biostd.json.JsonSerializer
import ac.uk.ebi.biostd.json.exception.NoAttributeValueException
import ebi.ac.uk.model.AttributeDetail
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import uk.ac.ebi.serialization.extensions.deserialize

class AttributeDetailDeserializerTest {
private val testInstance = JsonSerializer.mapper

@Test
fun `deserialize attribute detail`() {
val result = testInstance.deserialize<AttributeDetail>("""{
|"name": "attr name",
|"value": "attr value"
|}""".trimMargin())

assertThat(result.name).isEqualTo("attr name")
assertThat(result.value).isEqualTo("attr value")
}

@Test
fun `deserialize empty`() {
val exception = assertThrows<IllegalStateException> { testInstance.deserialize<AttributeDetail>("{}") }
assertThat(exception.message).isEqualTo("Expecting to find property with 'name' in node '{}'")
}

@Test
fun `deserialize with no value property`() {
val exception = assertThrows<IllegalStateException> { testInstance.deserialize<AttributeDetail>("""{
|"name": "attr name"
|}""".trimMargin())
}

assertThat(exception.message).isEqualTo(
"Expecting to find property with 'value' in node '{\"name\":\"attr name\"}'")
}

@Test
fun `deserialize no value`() {
val exception = assertThrows<NoAttributeValueException> { testInstance.deserialize<AttributeDetail>("""{
|"name": "attr name",
|"value": ""
|}""".trimMargin()) }

assertThat(exception.message).isEqualTo("The value for the attribute 'attr name' can't be empty")
}
}
11 changes: 5 additions & 6 deletions infrastructure/src/main/resources/setup/database/Schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ CREATE TABLE FileAttribute (
name LONGTEXT NULL,
nameQualifierString LONGTEXT NULL,
reference BIT NOT NULL,
value LONGTEXT NULL,
value LONGTEXT NOT NULL,
valueQualifierString LONGTEXT NULL,
file_id BIGINT NULL,
ord INT NULL
Expand Down Expand Up @@ -46,7 +46,7 @@ CREATE TABLE ReferencedFileAttribute (
name LONGTEXT NULL,
nameQualifierString LONGTEXT NULL,
reference BIT NOT NULL,
value LONGTEXT NULL,
value LONGTEXT NOT NULL,
valueQualifierString LONGTEXT NULL,
referenced_file_id BIGINT NULL,
ord INT NULL
Expand Down Expand Up @@ -102,7 +102,7 @@ CREATE TABLE LinkAttribute (
name LONGTEXT NULL,
nameQualifierString LONGTEXT NULL,
reference BIT NOT NULL,
value LONGTEXT NULL,
value LONGTEXT NOT NULL,
valueQualifierString LONGTEXT NULL,
link_id BIGINT NULL,
ord INT NULL,
Expand Down Expand Up @@ -139,7 +139,7 @@ CREATE TABLE SectionAttribute (
name LONGTEXT NULL,
nameQualifierString LONGTEXT NULL,
reference BIT NOT NULL,
value LONGTEXT NULL,
value LONGTEXT NOT NULL,
valueQualifierString LONGTEXT NULL,
section_id BIGINT NULL,
ord INT NULL,
Expand Down Expand Up @@ -184,7 +184,7 @@ CREATE TABLE SubmissionAttribute (
name LONGTEXT NULL,
nameQualifierString LONGTEXT NULL,
reference BIT NOT NULL,
value LONGTEXT NULL,
value LONGTEXT NOT NULL,
valueQualifierString LONGTEXT NULL,
submission_id BIGINT NULL,
ord INT NULL,
Expand Down Expand Up @@ -328,4 +328,3 @@ CREATE TABLE SubmissionRequest
);

CREATE index SubmissionRequest_accNo_version_index on SubmissionRequest (accNo, version);

1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ include("commons:commons-model-extended-mapping")
include("commons:commons-model-extended-serialization")

include("client:bio-webclient")
include("client:bio-webclient-demo")
include("client:bio-commandline")
include("client:fire-webclient")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ internal class AttributeDetailConverter : AttributeConverter<MutableList<Attribu
.split(ATTR_SEP)
.dropWhile { it.isEmpty() }
.map { it.split(ATTR_REL) }
.filterNot { it.second().isBlank() }
.mapTo(mutableListOf()) { AttributeDetail(it.first(), it.second()) }
}
Loading

0 comments on commit 64b7017

Please sign in to comment.