Skip to content

Commit

Permalink
Merge pull request #680 from RyanM-RMA/bugfix/content_type_blob
Browse files Browse the repository at this point in the history
Adding JSON aliases to blob endpoints, and the Blob and Blobs classes
  • Loading branch information
RyanM-RMA authored Jun 7, 2024
2 parents 9e3995d + 6f1fd51 commit 958dd06
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 69 deletions.
9 changes: 4 additions & 5 deletions cwms-data-api/src/main/java/cwms/cda/api/BlobController.java
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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<Blob> blobList = dao.getAll(office, like);
Expand Down Expand Up @@ -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 = {
Expand All @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion cwms-data-api/src/main/java/cwms/cda/data/dto/Blob.java
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion cwms-data-api/src/main/java/cwms/cda/data/dto/Blobs.java
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
170 changes: 108 additions & 62 deletions cwms-data-api/src/test/java/cwms/cda/api/BlobControllerTestIT.java
Original file line number Diff line number Diff line change
@@ -1,117 +1,163 @@
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;
import cwms.cda.formatters.Formats;
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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<? extends CwmsDTOBase> _class;
Expand Down

0 comments on commit 958dd06

Please sign in to comment.