diff --git a/cwms-data-api/src/main/java/cwms/cda/api/BlobController.java b/cwms-data-api/src/main/java/cwms/cda/api/BlobController.java index 386e601e8..eb8e28158 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/BlobController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/BlobController.java @@ -85,8 +85,8 @@ protected DSLContext getDslContext(Context ctx) { responses = {@OpenApiResponse(status = STATUS_200, description = "A list of blobs.", content = { + @OpenApiContent(type = Formats.JSON, from = Blobs.class), @OpenApiContent(type = Formats.JSONV2, from = Blobs.class), - @OpenApiContent(type = Formats.XMLV2, from = Blobs.class) }) }, tags = {TAG} @@ -115,7 +115,7 @@ public void getAll(@NotNull Context ctx) { String like = ctx.queryParamAsClass(LIKE, String.class).getOrDefault(".*"); String formatHeader = ctx.header(Header.ACCEPT); - ContentType contentType = Formats.parseHeaderAndQueryParm(formatHeader, ""); + ContentType contentType = Formats.parseHeader(formatHeader, Blobs.class); BlobDao dao = new BlobDao(dsl); List blobList = dao.getAll(office, like); @@ -169,8 +169,7 @@ public void getOne(@NotNull Context ctx, @NotNull String blobId) { description = "Create new Blob", requestBody = @OpenApiRequestBody( content = { - @OpenApiContent(from = Blob.class, type = Formats.JSONV2), - @OpenApiContent(from = Blob.class, type = Formats.XMLV2) + @OpenApiContent(from = Blob.class, type = Formats.JSONV2) }, required = true), queryParams = { @@ -189,7 +188,7 @@ public void create(@NotNull Context ctx) { String formatHeader = reqContentType != null ? reqContentType : Formats.JSON; boolean failIfExists = ctx.queryParamAsClass(FAIL_IF_EXISTS, Boolean.class).getOrDefault(true); - ContentType contentType = Formats.parseHeader(formatHeader); + ContentType contentType = Formats.parseHeader(formatHeader, Blob.class); Blob blob = Formats.parseContent(contentType, ctx.bodyAsInputStream(), Blob.class); if (blob.getOfficeId() == null) { diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/Blob.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/Blob.java index e3200f44d..d7444d4a9 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/Blob.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/Blob.java @@ -7,9 +7,10 @@ import cwms.cda.formatters.Formats; import cwms.cda.formatters.annotations.FormattableWith; import cwms.cda.formatters.json.JsonV2; +import cwms.cda.formatters.xml.XMLv2; @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class) -@FormattableWith(contentType = Formats.JSONV2, formatter = JsonV2.class) +@FormattableWith(contentType = Formats.JSONV2, formatter = JsonV2.class, aliases = {Formats.DEFAULT, Formats.JSON}) public class Blob extends CwmsDTO { @JsonProperty(required=true) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/Blobs.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/Blobs.java index 504ad0a64..6c380e4b7 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/Blobs.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/Blobs.java @@ -13,7 +13,7 @@ import java.util.List; @JsonRootName("blobs") -@FormattableWith(contentType = Formats.JSONV2, formatter = JsonV2.class) +@FormattableWith(contentType = Formats.JSONV2, formatter = JsonV2.class, aliases = {Formats.DEFAULT, Formats.JSON}) public class Blobs extends CwmsDTOPaginated { @JacksonXmlElementWrapper(localName = "blobs") @JacksonXmlProperty(localName = "blob") diff --git a/cwms-data-api/src/test/java/cwms/cda/api/BlobControllerTestIT.java b/cwms-data-api/src/test/java/cwms/cda/api/BlobControllerTestIT.java index 890297026..0bc059aaa 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/BlobControllerTestIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/BlobControllerTestIT.java @@ -1,8 +1,5 @@ package cwms.cda.api; -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.is; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import cwms.cda.data.dto.Blob; @@ -10,108 +7,157 @@ import cwms.cda.formatters.json.JsonV2; import fixtures.TestAccounts; import io.restassured.filter.log.LogDetail; -import java.io.UnsupportedEncodingException; -import javax.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import javax.servlet.http.HttpServletResponse; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; @Tag("integration") public class BlobControllerTestIT extends DataApiTestIT { public static final String SPK = "SPK"; + private static final String EXISTING_BLOB_ID = "TEST_BLOBIT2"; + private static final String EXISTING_BLOB_VALUE = "test value"; + + @BeforeAll + static void createExistingBlob() throws Exception + { + String origDesc = "test description"; + byte[] origBytes = EXISTING_BLOB_VALUE.getBytes(); + + String mediaType = "application/octet-stream"; + Blob blob = new Blob(SPK, EXISTING_BLOB_ID, origDesc, mediaType, origBytes); + ObjectMapper om = JsonV2.buildObjectMapper(); + String serializedBlob = om.writeValueAsString(blob); + TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; + + given() + .log().ifValidationFails(LogDetail.ALL,true) + .contentType(Formats.JSONV2) + .body(serializedBlob) + .header("Authorization",user.toHeaderValue()) + .queryParam("office",SPK) + .queryParam("fail-if-exists",false) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/blobs/") + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_CREATED)); + } @Test void test_getOne_not_found() throws UnsupportedEncodingException { String blobId = "TEST"; - String urlencoded = java.net.URLEncoder.encode(blobId, "UTF-8"); + String urlencoded = URLEncoder.encode(blobId, "UTF-8"); given() - .log().ifValidationFails(LogDetail.ALL,true) + .log().ifValidationFails(LogDetail.ALL,true) .accept(Formats.JSONV2) .queryParam(Controllers.OFFICE, SPK) .when() .get("/blobs/" + urlencoded) .then() - .log().ifValidationFails(LogDetail.ALL,true) + .log().ifValidationFails(LogDetail.ALL,true) .assertThat() .statusCode(is(HttpServletResponse.SC_NOT_FOUND)); } @Test - void test_create_getOne() throws JsonProcessingException { - String blobId = "TEST_BLOBIT2"; + void test_create_getOne() throws JsonProcessingException + { +// /* There is an issue with how javalin handles / in the path that are actually part +// of the object name (NOTE: good candidate for actually having a GUID or other "code" +// as part of the path and the actual name as a query parameter. +// */ - String origDesc = "test description"; - String origValue = "test value"; - byte[] origBytes = origValue.getBytes(); + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV2) + .queryParam(Controllers.OFFICE, SPK) + .when() + .get("/blobs/" + EXISTING_BLOB_ID) + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body(is(EXISTING_BLOB_VALUE)); + } - String mediaType = "application/octet-stream"; - Blob blob = new Blob(SPK, blobId, origDesc, mediaType, origBytes); - ObjectMapper om = JsonV2.buildObjectMapper(); - String serializedBlob = om.writeValueAsString(blob); - TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; + @Test + void test_blob_get_one_default() + { + given() + .log() + .ifValidationFails(LogDetail.ALL, true) + .queryParam(Controllers.OFFICE, SPK) + .when() + .get("/blobs/" + EXISTING_BLOB_ID) + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body(is(EXISTING_BLOB_VALUE)); + } + @Test + void test_blob_range() + { + // We can now do Range requests! given() .log().ifValidationFails(LogDetail.ALL,true) - .accept(Formats.JSONV2) - .contentType(Formats.JSONV2) - .body(serializedBlob) - .header("Authorization",user.toHeaderValue()) - .queryParam("office",SPK) - .queryParam("fail-if-exists",false) + .queryParam(Controllers.OFFICE, SPK) + .header("Range"," bytes=3-") .when() - .redirects().follow(true) - .redirects().max(3) - .post("/blobs/") + .get("/blobs/" + EXISTING_BLOB_ID) .then() .log().ifValidationFails(LogDetail.ALL,true) .assertThat() - .statusCode(is(HttpServletResponse.SC_CREATED)); - -// /* There is an issue with how javalin handles / in the path that are actually part -// of the object name (NOTE: good candidate for actually having a GUID or other "code" -// as part of the path and the actual name as a query parameter. -// */ + .statusCode(is(HttpServletResponse.SC_PARTIAL_CONTENT)) + .body( is("t value")); + } + @ParameterizedTest + @EnumSource(GetAllTest.class) + void test_blob_get_all_default_alias(GetAllTest test) + { given() - .accept(Formats.JSONV2) .log().ifValidationFails(LogDetail.ALL,true) + .accept(test._accept) .queryParam(Controllers.OFFICE, SPK) .when() - .get("/blobs/" + blobId) + .get("/blobs/") .then() .log().ifValidationFails(LogDetail.ALL,true) .assertThat() .statusCode(is(HttpServletResponse.SC_OK)) - .body( is(origValue)); - - - given() - .log().ifValidationFails(LogDetail.ALL,true) - .queryParam(Controllers.OFFICE, SPK) - .when() - .get("/blobs/" + blobId) - .then() - .log().ifValidationFails(LogDetail.ALL,true) - .assertThat() - .statusCode(is(HttpServletResponse.SC_OK)) - .body( is(origValue)) - ; + .contentType(is(test._expectedContentType)); + } - // We can now do Range requests! - given() - .log().ifValidationFails(LogDetail.ALL,true) - .queryParam(Controllers.OFFICE, SPK) - .header("Range"," bytes=3-") - .when() - .get("/blobs/" + blobId) - .then() - .log().ifValidationFails(LogDetail.ALL,true) - .assertThat() - .statusCode(is(HttpServletResponse.SC_PARTIAL_CONTENT)) - .body( is("t value")) + enum GetAllTest + { + DEFAULT(Formats.DEFAULT, Formats.JSONV2), + JSON(Formats.JSON, Formats.JSONV2), + JSONV2(Formats.JSONV2, Formats.JSONV2), ; - + final String _accept; + final String _expectedContentType; + + GetAllTest(String accept, String expectedContentType) + { + _accept = accept; + _expectedContentType = expectedContentType; + } } } diff --git a/cwms-data-api/src/test/java/cwms/cda/formatters/FormatsTest.java b/cwms-data-api/src/test/java/cwms/cda/formatters/FormatsTest.java index 8d18e441d..2194f6a54 100644 --- a/cwms-data-api/src/test/java/cwms/cda/formatters/FormatsTest.java +++ b/cwms-data-api/src/test/java/cwms/cda/formatters/FormatsTest.java @@ -4,6 +4,7 @@ import java.util.Arrays; import java.util.Map; +import cwms.cda.data.dto.Blob; import cwms.cda.data.dto.County; import cwms.cda.data.dto.CwmsDTOBase; import cwms.cda.data.dto.Office; @@ -189,6 +190,14 @@ enum ParseHeaderClassAliasTest STATE_DEFAULT(State.class, Formats.DEFAULT, Formats.JSONV2), STATE_JSON(State.class, Formats.JSON, Formats.JSONV2), STATE_JSONV2(State.class, Formats.JSONV2, Formats.JSONV2), + OFFICE_DEFAULT(Office.class, Formats.JSONV2, Formats.JSONV2), + OFFICE_JSON(Office.class, Formats.JSONV2, Formats.JSONV2), + OFFICE_JSONV2(Office.class, Formats.JSONV2, Formats.JSONV2), + OFFICE_XML(Office.class, Formats.XML, Formats.XMLV2), + OFFICE_XMLV2(Office.class, Formats.XMLV2, Formats.XMLV2), + BLOB_DEFAULT(Blob.class, Formats.DEFAULT, Formats.JSONV2), + BLOB_JSON(Blob.class, Formats.JSON, Formats.JSONV2), + BLOB_JSONV2(Blob.class, Formats.JSONV2, Formats.JSONV2), ; final Class _class;