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 70ef6cb80..2764fe1b5 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 @@ -63,32 +63,32 @@ protected DSLContext getDslContext(Context ctx) { } @OpenApi( - queryParams = { - @OpenApiParam(name = OFFICE, - description = "Specifies the owning office. If this field is not " - + "specified, matching information from all offices shall be " - + "returned."), - @OpenApiParam(name = PAGE, - description = "This end point can return a lot of data, this " - + "identifies where in the request you are. This is an opaque" - + " value, and can be obtained from the 'next-page' value in " - + "the response."), - @OpenApiParam(name = PAGE_SIZE, - type = Integer.class, - description = "How many entries per page returned. Default " - + DEFAULT_PAGE_SIZE + "."), - @OpenApiParam(name = LIKE, - description = "Posix regular expression " - + "describing the blob id's you want") - }, - 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), - }) - }, - tags = {TAG} + queryParams = { + @OpenApiParam(name = OFFICE, + description = "Specifies the owning office. If this field is not " + + "specified, matching information from all offices shall be " + + "returned."), + @OpenApiParam(name = PAGE, + description = "This end point can return a lot of data, this " + + "identifies where in the request you are. This is an opaque" + + " value, and can be obtained from the 'next-page' value in " + + "the response."), + @OpenApiParam(name = PAGE_SIZE, + type = Integer.class, + description = "How many entries per page returned. Default " + + DEFAULT_PAGE_SIZE + "."), + @OpenApiParam(name = LIKE, + description = "Posix regular expression " + + "describing the blob id's you want") + }, + 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), + }) + }, + tags = {TAG} ) @Override public void getAll(@NotNull Context ctx) { @@ -130,14 +130,20 @@ public void getAll(@NotNull Context ctx) { description = "Returns the binary value of the requested blob as a seekable stream with the " + "appropriate media type.", queryParams = { - @OpenApiParam(name = OFFICE, description = "Specifies the owning office."), + @OpenApiParam(name = OFFICE, description = "Specifies the owning office."), + @OpenApiParam(name = BLOB_ID, description = "If this _query_ parameter is provided the id _path_ parameter " + + "is ignored and the value of the query parameter is used. " + + "Note: this query parameter is necessary for id's that contain '/' or other special " + + "characters. This is due to limitations in path pattern matching. " + + "We will likely add support for encoding the ID in the path in the future. For now use the id field for those IDs. " + + "Client libraries should detect slashes and choose the appropriate field. \"ignored\" is suggested for the path endpoint."), }, responses = { - @OpenApiResponse(status = STATUS_200, - description = "Returns requested blob.", - content = { - @OpenApiContent(type = "application/octet-stream") - }) + @OpenApiResponse(status = STATUS_200, + description = "Returns requested blob.", + content = { + @OpenApiContent(type = "application/octet-stream") + }) }, tags = {TAG} ) @@ -145,6 +151,10 @@ public void getAll(@NotNull Context ctx) { public void getOne(@NotNull Context ctx, @NotNull String blobId) { try (final Timer.Context ignored = markAndTime(GET_ONE)) { + String idQueryParam = ctx.queryParam(CLOB_ID); + if (idQueryParam != null) { + blobId = idQueryParam; + } DSLContext dsl = getDslContext(ctx); BlobDao dao = new BlobDao(dsl); String officeQP = ctx.queryParam(OFFICE); @@ -175,13 +185,13 @@ public void getOne(@NotNull Context ctx, @NotNull String blobId) { @OpenApi( description = "Create new Blob", requestBody = @OpenApiRequestBody( - content = { - @OpenApiContent(from = Blob.class, type = Formats.JSONV2) - }, - required = true), + content = { + @OpenApiContent(from = Blob.class, type = Formats.JSONV2) + }, + required = true), queryParams = { - @OpenApiParam(name = FAIL_IF_EXISTS, type = Boolean.class, - description = "Create will fail if provided ID already exists. Default: true") + @OpenApiParam(name = FAIL_IF_EXISTS, type = Boolean.class, + description = "Create will fail if provided ID already exists. Default: true") }, method = HttpMethod.POST, tags = {TAG} @@ -203,20 +213,32 @@ public void create(@NotNull Context ctx) { @OpenApi( description = "Update an existing Blob", pathParams = { - @OpenApiParam(name = BLOB_ID, description = "The blob identifier to be deleted"), + @OpenApiParam(name = BLOB_ID, description = "The blob identifier to be deleted"), }, requestBody = @OpenApiRequestBody( - content = { - @OpenApiContent(from = Blob.class, type = Formats.JSONV2), - @OpenApiContent(from = Blob.class, type = Formats.JSON) - }, - required = true), + content = { + @OpenApiContent(from = Blob.class, type = Formats.JSONV2), + @OpenApiContent(from = Blob.class, type = Formats.JSON) + }, + required = true), + queryParams = { + @OpenApiParam(name = BLOB_ID, description = "If this _query_ parameter is provided the id _path_ parameter " + + "is ignored and the value of the query parameter is used. " + + "Note: this query parameter is necessary for id's that contain '/' or other special " + + "characters. This is due to limitations in path pattern matching. " + + "We will likely add support for encoding the ID in the path in the future. For now use the id field for those IDs. " + + "Client libraries should detect slashes and choose the appropriate field. \"ignored\" is suggested for the path endpoint."), + }, method = HttpMethod.PATCH, tags = {TAG} ) @Override public void update(@NotNull Context ctx, @NotNull String blobId) { try (final Timer.Context ignored = markAndTime(UPDATE)) { + String idQueryParam = ctx.queryParam(CLOB_ID); + if (idQueryParam != null) { + blobId = idQueryParam; + } DSLContext dsl = getDslContext(ctx); String reqContentType = ctx.req.getContentType(); @@ -247,11 +269,17 @@ public void update(@NotNull Context ctx, @NotNull String blobId) { @OpenApi( description = "Deletes requested blob", pathParams = { - @OpenApiParam(name = BLOB_ID, description = "The blob identifier to be deleted"), + @OpenApiParam(name = BLOB_ID, description = "The blob identifier to be deleted"), }, queryParams = { - @OpenApiParam(name = OFFICE, required = true, description = "Specifies the " - + "owning office of the blob to be deleted"), + @OpenApiParam(name = OFFICE, required = true, description = "Specifies the " + + "owning office of the blob to be deleted"), + @OpenApiParam(name = BLOB_ID, description = "If this _query_ parameter is provided the id _path_ parameter " + + "is ignored and the value of the query parameter is used. " + + "Note: this query parameter is necessary for id's that contain '/' or other special " + + "characters. This is due to limitations in path pattern matching. " + + "We will likely add support for encoding the ID in the path in the future. For now use the id field for those IDs. " + + "Client libraries should detect slashes and choose the appropriate field. \"ignored\" is suggested for the path endpoint."), }, method = HttpMethod.DELETE, tags = {TAG} @@ -259,6 +287,10 @@ public void update(@NotNull Context ctx, @NotNull String blobId) { @Override public void delete(@NotNull Context ctx, @NotNull String blobId) { try (Timer.Context ignored = markAndTime(DELETE)) { + String idQueryParam = ctx.queryParam(CLOB_ID); + if (idQueryParam != null) { + blobId = idQueryParam; + } DSLContext dsl = getDslContext(ctx); String office = requiredParam(ctx, OFFICE); BlobDao dao = new BlobDao(dsl); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/ClobController.java b/cwms-data-api/src/main/java/cwms/cda/api/ClobController.java index b106afabf..62e5ed6a2 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/ClobController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/ClobController.java @@ -75,36 +75,36 @@ protected DSLContext getDslContext(Context ctx) { } @OpenApi( - queryParams = { - @OpenApiParam(name = OFFICE, - description = "Specifies the owning office. If this field is not " - + "specified, matching information from all offices shall be " - + "returned."), - @OpenApiParam(name = PAGE, - description = "This end point can return a lot of data, this " - + "identifies where in the request you are. This is an opaque" - + " value, and can be obtained from the 'next-page' value in " - + "the response."), - @OpenApiParam(name = PAGE_SIZE, - type = Integer.class, - description = "How many entries per page returned. Default " - + DEFAULT_PAGE_SIZE + "."), - @OpenApiParam(name = INCLUDE_VALUES, - type = Boolean.class, - description = "Do you want the value associated with this particular " - + "clob (default: false)"), - @OpenApiParam(name = LIKE, - description = "Posix regular expression " - + "matching against the id") - }, - responses = {@OpenApiResponse(status = STATUS_200, - description = "A list of clobs.", - content = { - @OpenApiContent(type = Formats.JSONV2, from = Clobs.class), - @OpenApiContent(type = Formats.XMLV2, from = Clobs.class) - }) - }, - tags = {TAG} + queryParams = { + @OpenApiParam(name = OFFICE, + description = "Specifies the owning office. If this field is not " + + "specified, matching information from all offices shall be " + + "returned."), + @OpenApiParam(name = PAGE, + description = "This end point can return a lot of data, this " + + "identifies where in the request you are. This is an opaque" + + " value, and can be obtained from the 'next-page' value in " + + "the response."), + @OpenApiParam(name = PAGE_SIZE, + type = Integer.class, + description = "How many entries per page returned. Default " + + DEFAULT_PAGE_SIZE + "."), + @OpenApiParam(name = INCLUDE_VALUES, + type = Boolean.class, + description = "Do you want the value associated with this particular " + + "clob (default: false)"), + @OpenApiParam(name = LIKE, + description = "Posix regular expression " + + "matching against the id") + }, + responses = {@OpenApiResponse(status = STATUS_200, + description = "A list of clobs.", + content = { + @OpenApiContent(type = Formats.JSONV2, from = Clobs.class), + @OpenApiContent(type = Formats.XMLV2, from = Clobs.class) + }) + }, + tags = {TAG} ) @Override public void getAll(@NotNull Context ctx) { @@ -156,11 +156,9 @@ public void getAll(@NotNull Context ctx) { @OpenApiParam(name = CLOB_ID, description = "If this _query_ parameter is provided the id _path_ parameter " + "is ignored and the value of the query parameter is used. " + "Note: this query parameter is necessary for id's that contain '/' or other special " - + "characters. Because of abuse even properly escaped '/' in url paths are blocked. " - + "When using this query parameter a valid path parameter must still be provided for the request" - + " to be properly routed. If your clob id contains '/' you can't specify the clob-id query " - + "parameter and also specify the id path parameter because firewall and/or server rules will " - + "deny the request even though you are specifying this override. \"ignored\" is suggested.") + + "characters. This is due to limitations in path pattern matching. " + + "We will likely add support for encoding the ID in the path in the future. For now use the id field for those IDs. " + + "Client libraries should detect slashes and choose the appropriate field. \"ignored\" is suggested for the path endpoint."), }, responses = {@OpenApiResponse(status = STATUS_200, description = "Returns requested clob.", @@ -221,19 +219,19 @@ public void getOne(@NotNull Context ctx, @NotNull String clobId) { } @OpenApi( - description = "Create new Clob", - requestBody = @OpenApiRequestBody( - content = { - @OpenApiContent(from = Clob.class, type = Formats.JSONV2), - @OpenApiContent(from = Clob.class, type = Formats.XMLV2) - }, - required = true), + description = "Create new Clob", + requestBody = @OpenApiRequestBody( + content = { + @OpenApiContent(from = Clob.class, type = Formats.JSONV2), + @OpenApiContent(from = Clob.class, type = Formats.XMLV2) + }, + required = true), queryParams = { @OpenApiParam(name = FAIL_IF_EXISTS, type = Boolean.class, - description = "Create will fail if provided ID already exists. Default: true") + description = "Create will fail if provided ID already exists. Default: true") }, - method = HttpMethod.POST, - tags = {TAG} + method = HttpMethod.POST, + tags = {TAG} ) @Override public void create(@NotNull Context ctx) { @@ -250,25 +248,31 @@ public void create(@NotNull Context ctx) { } @OpenApi( - pathParams = { - @OpenApiParam(name = CLOB_ID, required = true, - description = "Specifies the id of the clob to be updated"), - }, - queryParams = { - @OpenApiParam(name = IGNORE_NULLS, type = Boolean.class, - description = "If true, null and empty fields in the provided clob " - + "will be ignored and the existing value of those fields " - + "left in place. Default: true") + pathParams = { + @OpenApiParam(name = CLOB_ID, required = true, + description = "Specifies the id of the clob to be updated"), + }, + queryParams = { + @OpenApiParam(name = IGNORE_NULLS, type = Boolean.class, + description = "If true, null and empty fields in the provided clob " + + "will be ignored and the existing value of those fields " + + "left in place. Default: true"), + @OpenApiParam(name = CLOB_ID, description = "If this _query_ parameter is provided the id _path_ parameter " + + "is ignored and the value of the query parameter is used. " + + "Note: this query parameter is necessary for id's that contain '/' or other special " + + "characters. This is due to limitations in path pattern matching. " + + "We will likely add support for encoding the ID in the path in the future. For now use the id field for those IDs. " + + "Client libraries should detect slashes and choose the appropriate field. \"ignored\" is suggested for the path endpoint."), + }, + requestBody = @OpenApiRequestBody( + content = { + @OpenApiContent(from = Clob.class, type = Formats.JSONV2), + @OpenApiContent(from = Clob.class, type = Formats.XMLV2) }, - requestBody = @OpenApiRequestBody( - content = { - @OpenApiContent(from = Clob.class, type = Formats.JSONV2), - @OpenApiContent(from = Clob.class, type = Formats.XMLV2) - }, - required = true), - description = "Update clob", - method = HttpMethod.PATCH, - tags = {TAG} + required = true), + description = "Update clob", + method = HttpMethod.PATCH, + tags = {TAG} ) @Override public void update(@NotNull Context ctx, @NotNull String clobId) { @@ -276,6 +280,10 @@ public void update(@NotNull Context ctx, @NotNull String clobId) { boolean ignoreNulls = ctx.queryParamAsClass(IGNORE_NULLS, Boolean.class).getOrDefault(true); try (final Timer.Context ignored = markAndTime(UPDATE)) { + String idQueryParam = ctx.queryParam(CLOB_ID); + if (idQueryParam != null) { + clobId = idQueryParam; + } DSLContext dsl = getDslContext(ctx); String formatHeader = ctx.req.getContentType(); @@ -321,23 +329,33 @@ private Clob fillOutClob(Clob clob, String reqId) { } @OpenApi( - pathParams = { - @OpenApiParam(name = CLOB_ID, required = true, - description = "Specifies the id of the clob to be deleted"), - }, - queryParams = { - @OpenApiParam(name = OFFICE, required = true, - description = "Specifies the office of the clob.") - }, - description = "Delete clob", - method = HttpMethod.DELETE, - tags = {TAG} + pathParams = { + @OpenApiParam(name = CLOB_ID, required = true, + description = "Specifies the id of the clob to be deleted"), + }, + queryParams = { + @OpenApiParam(name = OFFICE, required = true, + description = "Specifies the office of the clob."), + @OpenApiParam(name = CLOB_ID, description = "If this _query_ parameter is provided the id _path_ parameter " + + "is ignored and the value of the query parameter is used. " + + "Note: this query parameter is necessary for id's that contain '/' or other special " + + "characters. This is due to limitations in path pattern matching. " + + "We will likely add support for encoding the ID in the path in the future. For now use the id field for those IDs. " + + "Client libraries should detect slashes and choose the appropriate field. \"ignored\" is suggested for the path endpoint."), + }, + description = "Delete clob", + method = HttpMethod.DELETE, + tags = {TAG} ) @Override public void delete(@NotNull Context ctx, @NotNull String clobId) { String office = requiredParam(ctx, OFFICE); try (final Timer.Context ignored = markAndTime(DELETE)) { + String idQueryParam = ctx.queryParam(CLOB_ID); + if (idQueryParam != null) { + clobId = idQueryParam; + } DSLContext dsl = getDslContext(ctx); ClobDao dao = new ClobDao(dsl); dao.delete(office, clobId);