Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix flaky GET {entity}/{entityId}/images Content-Type bug (FINERACT-1265) #1506

Merged
merged 1 commit into from Nov 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -82,8 +82,8 @@ public ImagesApiResource(final PlatformSecurityContext context, final ImageReadP
* Upload images through multi-part form upload
*/
@POST
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@Produces({ MediaType.APPLICATION_JSON })
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public String addNewClientImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId,
@HeaderParam("Content-Length") final Long fileSize, @FormDataParam("file") final InputStream inputStream,
@FormDataParam("file") final FormDataContentDisposition fileDetails, @FormDataParam("file") final FormDataBodyPart bodyPart) {
Expand All @@ -106,7 +106,7 @@ public String addNewClientImage(@PathParam("entity") final String entityName, @P
*/
@POST
@Consumes({ MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Produces(MediaType.APPLICATION_JSON)
public String addNewClientImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId,
final String jsonRequestBody) {
validateEntityTypeforImage(entityName);
Expand All @@ -119,27 +119,35 @@ public String addNewClientImage(@PathParam("entity") final String entityName, @P
}

/**
* Returns a base 64 encoded client image Data URI
* Returns a images, either as Base64 encoded text/plain or as inline or attachment with image MIME type as
* Content-Type.
*/
@GET
@Consumes({ MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.TEXT_PLAIN })
@Consumes(MediaType.APPLICATION_JSON)
// FINERACT-1265: Do NOT specify @Produces(TEXT_PLAIN) here - it may actually not (if it calls the next methods it's
// octet-stream)
public Response retrieveImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId,
@QueryParam("maxWidth") final Integer maxWidth, @QueryParam("maxHeight") final Integer maxHeight,
@QueryParam("output") final String output) {
@QueryParam("output") final String output, @HeaderParam("Accept") String acceptHeader) {
validateEntityTypeforImage(entityName);
if (EntityTypeForImages.CLIENTS.toString().equalsIgnoreCase(entityName)) {
this.context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
} else if (EntityTypeForImages.STAFF.toString().equalsIgnoreCase(entityName)) {
this.context.authenticatedUser().validateHasReadPermission("STAFFIMAGE");
}

if (output != null && (output.equals("octet") || output.equals("inline_octet"))) {
return downloadClientImage(entityName, entityId, maxWidth, maxHeight, output);
}

final FileData imageData = this.imageReadPlatformService.retrieveImage(entityName, entityId);
final FileData resizedImage = imageResizer.resize(imageData, maxWidth, maxHeight);

// If client wants (Accept header) octet-stream, or output="octet" or "inline_octet", then send that instead of
// text
if ("application/octet-stream".equalsIgnoreCase(acceptHeader)
|| (output != null && (output.equals("octet") || output.equals("inline_octet")))) {
return ContentResources.fileDataToResponse(resizedImage, resizedImage.name() + ImageFileExtension.JPEG,
"inline_octet".equals(output) ? "inline" : "attachment");
}

// Else return response with Base64 encoded
// TODO: Need a better way of determining image type
String imageDataURISuffix = ContentRepositoryUtils.ImageDataURIsuffix.JPEG.getValue();
if (StringUtils.endsWith(imageData.name(), ContentRepositoryUtils.ImageFileExtension.GIF.getValue())) {
Expand All @@ -148,42 +156,22 @@ public Response retrieveImage(@PathParam("entity") final String entityName, @Pat
imageDataURISuffix = ContentRepositoryUtils.ImageDataURIsuffix.PNG.getValue();
}

FileData resizedImage = imageResizer.resize(imageData, maxWidth, maxHeight);
try {
byte[] resizedImageBytes = resizedImage.getByteSource().read();
final String clientImageAsBase64Text = imageDataURISuffix + Base64.getMimeEncoder().encodeToString(resizedImageBytes);
return Response.ok(clientImageAsBase64Text).build();
return Response.ok(clientImageAsBase64Text, MediaType.TEXT_PLAIN_TYPE).build();
} catch (IOException e) {
throw new ContentManagementException(imageData.name(), e.getMessage(), e);
}
}

@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_OCTET_STREAM })
public Response downloadClientImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId,
@QueryParam("maxWidth") final Integer maxWidth, @QueryParam("maxHeight") final Integer maxHeight,
@QueryParam("output") String output) {
validateEntityTypeforImage(entityName);
if (EntityTypeForImages.CLIENTS.toString().equalsIgnoreCase(entityName)) {
this.context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
} else if (EntityTypeForImages.STAFF.toString().equalsIgnoreCase(entityName)) {
this.context.authenticatedUser().validateHasReadPermission("STAFFIMAGE");
}

final FileData imageData = this.imageReadPlatformService.retrieveImage(entityName, entityId);
final FileData resizedImage = imageResizer.resize(imageData, maxWidth, maxHeight);
return ContentResources.fileDataToResponse(resizedImage, resizedImage.name() + ImageFileExtension.JPEG,
"inline_octet".equals(output) ? "inline" : "attachment");
}

/**
* This method is added only for consistency with other URL patterns and for maintaining consistency of usage of the
* HTTP "verb" at the client side
*/
@PUT
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@Produces({ MediaType.APPLICATION_JSON })
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public String updateClientImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId,
@HeaderParam("Content-Length") final Long fileSize, @FormDataParam("file") final InputStream inputStream,
@FormDataParam("file") final FormDataContentDisposition fileDetails, @FormDataParam("file") final FormDataBodyPart bodyPart) {
Expand All @@ -198,15 +186,15 @@ public String updateClientImage(@PathParam("entity") final String entityName, @P
*/
@PUT
@Consumes({ MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Produces(MediaType.APPLICATION_JSON)
public String updateClientImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId,
final String jsonRequestBody) {
return addNewClientImage(entityName, entityId, jsonRequestBody);
}

@DELETE
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public String deleteClientImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId) {
validateEntityTypeforImage(entityName);
this.imageWritePlatformService.deleteImage(entityName, entityId);
Expand Down Expand Up @@ -238,5 +226,4 @@ private static boolean checkValidEntityType(final String entityType) {
}
return false;
}

}
Expand Up @@ -163,7 +163,7 @@ public Response retrieveImage(@PathParam("clientId") @Parameter(description = "c

validateAppuserClientsMapping(clientId);

return this.imagesApiResource.retrieveImage("clients", clientId, maxWidth, maxHeight, output);
return this.imagesApiResource.retrieveImage("clients", clientId, maxWidth, maxHeight, output, MediaType.TEXT_PLAIN);
}

@GET
Expand Down