Skip to content

Commit

Permalink
Make it easier to provide custom extensions for the ContentTypeResolver
Browse files Browse the repository at this point in the history
  • Loading branch information
filiphr committed Jul 26, 2023
1 parent 9029034 commit 8bbfc56
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import java.util.ArrayList;
import java.util.List;

import org.apache.http.entity.ContentType;
import org.flowable.app.api.repository.AppDefinition;
import org.flowable.app.api.repository.AppDeployment;
import org.flowable.app.rest.service.api.repository.AppDefinitionResponse;
Expand Down Expand Up @@ -70,12 +69,7 @@ public List<AppDeploymentResourceResponse> createDeploymentResourceResponseList(
// Add additional metadata to the artifact-strings before returning
List<AppDeploymentResourceResponse> responseList = new ArrayList<>(resourceList.size());
for (String resourceId : resourceList) {
String contentType = null;
if (resourceId.toLowerCase().endsWith(".app")) {
contentType = ContentType.APPLICATION_JSON.getMimeType();
} else {
contentType = contentTypeResolver.resolveContentType(resourceId);
}
String contentType = contentTypeResolver.resolveContentType(resourceId);
responseList.add(createDeploymentResourceResponse(deploymentId, resourceId, contentType, urlBuilder));
}
return responseList;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public void testGetAppDefinition() throws Exception {
/**
* Test getting an unexisting app definition. GET app-repository/app-definitions/{appeDefinitionId}
*/
public void testGetUnexistingCaseDefinition() throws Exception {
public void testGetUnexistingAppDefinition() throws Exception {
HttpGet httpGet = new HttpGet(SERVER_URL_PREFIX + AppRestUrls.createRelativeResourceUrl(AppRestUrls.URL_APP_DEFINITION, "unexisting"));
CloseableHttpResponse response = executeRequest(httpGet, HttpStatus.SC_NOT_FOUND);
closeResponse(response);
Expand All @@ -89,6 +89,7 @@ public void testGetAppDefinitionResourceData() throws Exception {
assertThat(content)
.isNotNull()
.contains("oneApp");
assertThat(response.getFirstHeader("Content-Type").getValue()).isEqualTo("application/json");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,18 @@ public void testGetDeploymentResources() throws Exception {
assertThatJson(responseNode)
.when(Option.IGNORING_EXTRA_FIELDS, Option.IGNORING_EXTRA_ARRAY_ITEMS, Option.IGNORING_ARRAY_ORDER)
.isEqualTo("["
+ " {"
+ " id: 'org/flowable/app/rest/service/api/repository/oneApp.app',"
+ " mediaType: 'application/json',"
+ " type: 'appDefinition'"
+ " },"
+ " {"
+ " id: 'test.txt',"
+ " url: '" + SERVER_URL_PREFIX + AppRestUrls
.createRelativeResourceUrl(AppRestUrls.URL_DEPLOYMENT_RESOURCE, deployment.getId(), "test.txt") + "',"
+ " contentUrl: '" + SERVER_URL_PREFIX + AppRestUrls
.createRelativeResourceUrl(AppRestUrls.URL_DEPLOYMENT_RESOURCE_CONTENT, deployment.getId(), "test.txt") + "',"
+ " mediaType: null,"
+ " mediaType: 'text/plain',"
+ " type: 'resource'"
+ " }"
+ "]");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.flowable.cmmn.api.history.HistoricCaseInstance;
import org.flowable.cmmn.api.history.HistoricMilestoneInstance;
import org.flowable.cmmn.api.history.HistoricPlanItemInstance;
Expand Down Expand Up @@ -183,12 +182,7 @@ public List<DeploymentResourceResponse> createDeploymentResourceResponseList(Str
// Add additional metadata to the artifact-strings before returning
List<DeploymentResourceResponse> responseList = new ArrayList<>(resourceList.size());
for (String resourceId : resourceList) {
String contentType = null;
if (resourceId.toLowerCase().endsWith(".cmmn")) {
contentType = ContentType.TEXT_XML.getMimeType();
} else {
contentType = contentTypeResolver.resolveContentType(resourceId);
}
String contentType = contentTypeResolver.resolveContentType(resourceId);
responseList.add(createDeploymentResourceResponse(deploymentId, resourceId, contentType, urlBuilder));
}
return responseList;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
import java.io.InputStream;
import java.util.List;

import jakarta.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.http.entity.ContentType;
import org.flowable.cmmn.api.CmmnRepositoryService;
import org.flowable.cmmn.api.repository.CmmnDeployment;
import org.flowable.cmmn.rest.service.api.CmmnRestApiInterceptor;
Expand All @@ -27,8 +28,6 @@
import org.flowable.common.rest.resolver.ContentTypeResolver;
import org.springframework.beans.factory.annotation.Autowired;

import jakarta.servlet.http.HttpServletResponse;

/**
* @author Tijs Rademakers
*/
Expand Down Expand Up @@ -65,12 +64,7 @@ protected byte[] getDeploymentResourceData(String deploymentId, String resourceN
List<String> resourceList = repositoryService.getDeploymentResourceNames(deploymentId);

if (resourceList.contains(resourceName)) {
String contentType = null;
if (resourceName.toLowerCase().endsWith(".cmmn")) {
contentType = ContentType.TEXT_XML.getMimeType();
} else {
contentType = contentTypeResolver.resolveContentType(resourceName);
}
String contentType = contentTypeResolver.resolveContentType(resourceName);
response.setContentType(contentType);

try (final InputStream resourceStream = repositoryService.getResourceAsStream(deploymentId, resourceName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import jakarta.servlet.http.HttpServletRequest;

import org.apache.http.entity.ContentType;
import org.flowable.cmmn.api.CmmnRepositoryService;
import org.flowable.cmmn.api.repository.CmmnDeployment;
import org.flowable.cmmn.rest.service.api.CmmnRestResponseFactory;
Expand Down Expand Up @@ -80,13 +79,8 @@ public DeploymentResourceResponse getDeploymentResource(@ApiParam(name = "deploy

if (resourceList.contains(resourceName)) {
// Build resource representation
String contentType = null;
if (resourceName.toLowerCase().endsWith(".cmmn")) {
contentType = ContentType.TEXT_XML.getMimeType();
} else {
contentType = contentTypeResolver.resolveContentType(resourceName);
}

String contentType = contentTypeResolver.resolveContentType(resourceName);

DeploymentResourceResponse response = restResponseFactory.createDeploymentResourceResponse(deploymentId, resourceName, contentType);
return response;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,18 @@ public void testGetDeploymentResources() throws Exception {
// Check URL's for the resource
assertThatJson(responseNode)
.when(Option.IGNORING_EXTRA_FIELDS, Option.IGNORING_ARRAY_ORDER, Option.IGNORING_EXTRA_ARRAY_ITEMS)
.isEqualTo("[ {"
.isEqualTo("["
+ " {"
+ " id: 'org/flowable/cmmn/rest/service/api/repository/oneHumanTaskCase.cmmn',"
+ " mediaType: 'text/xml',"
+ " type: 'caseDefinition'"
+ " },"
+ " {"
+ " url: '" + SERVER_URL_PREFIX + CmmnRestUrls
.createRelativeResourceUrl(CmmnRestUrls.URL_DEPLOYMENT_RESOURCE, deployment.getId(), "test.txt") + "',"
+ " contentUrl: '" + SERVER_URL_PREFIX + CmmnRestUrls
.createRelativeResourceUrl(CmmnRestUrls.URL_DEPLOYMENT_RESOURCE_CONTENT, deployment.getId(), "test.txt") + "',"
+ " mediaType: null,"
+ " mediaType: 'text/plain',"
+ " type: 'resource'"
+ "} ]");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@

package org.flowable.common.rest.resolver;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;

/**
* Default implementation of a {@link ContentTypeResolver}, resolving a limited set of well-known content types used in the engine.
*
Expand All @@ -22,25 +28,41 @@
*/
public class DefaultContentTypeResolver implements ContentTypeResolver {

protected final Map<String, String> fileExtensionToContentType;
protected String unknownFileContentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;

public DefaultContentTypeResolver() {
this.fileExtensionToContentType = new HashMap<>();
this.fileExtensionToContentType.put("png", MediaType.IMAGE_PNG_VALUE);

this.fileExtensionToContentType.put("txt", MediaType.TEXT_PLAIN_VALUE);

this.fileExtensionToContentType.put("xml", MediaType.TEXT_XML_VALUE);
this.fileExtensionToContentType.put("bpmn", MediaType.TEXT_XML_VALUE);
this.fileExtensionToContentType.put("cmmn", MediaType.TEXT_XML_VALUE);
this.fileExtensionToContentType.put("dmn", MediaType.TEXT_XML_VALUE);

this.fileExtensionToContentType.put("app", MediaType.APPLICATION_JSON_VALUE);
this.fileExtensionToContentType.put("event", MediaType.APPLICATION_JSON_VALUE);
this.fileExtensionToContentType.put("form", MediaType.APPLICATION_JSON_VALUE);
this.fileExtensionToContentType.put("channel", MediaType.APPLICATION_JSON_VALUE);
}

@Override
public String resolveContentType(String resourceName) {
String contentType = null;
if (resourceName != null && !resourceName.isEmpty()) {
String lowerResourceName = resourceName.toLowerCase();

if (lowerResourceName.endsWith("png")) {
contentType = "image/png";
} else if (lowerResourceName.endsWith("xml")
|| lowerResourceName.endsWith("bpmn")
|| lowerResourceName.endsWith("dmn")) {
contentType = "text/xml";
} else if (lowerResourceName.endsWith(".app")
|| lowerResourceName.endsWith(".event")
|| lowerResourceName.endsWith(".form")
|| lowerResourceName.endsWith(".channel")) {
contentType = "application/json";
}
String fileExtension = StringUtils.substringAfterLast(lowerResourceName, '.');
return fileExtensionToContentType.getOrDefault(fileExtension, unknownFileContentType);
}
return contentType;
return null;
}

public void addFileExtensionMapping(String fileExtension, String contentType) {
this.fileExtensionToContentType.put(fileExtension, contentType);
}

public void setUnknownFileContentType(String unknownFileContentType) {
this.unknownFileContentType = unknownFileContentType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.flowable.common.rest.resolver;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.Locale;
import java.util.stream.Stream;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

/**
* @author Filip Hrisafov
*/
class DefaultContentTypeResolverTest {

@ParameterizedTest
@MethodSource("knownFileExtensionsParameters")
void knownFileExtensions(String resourceName, String contentType) {
ContentTypeResolver contentTypeResolver = new DefaultContentTypeResolver();
assertThat(contentTypeResolver.resolveContentType(resourceName)).isEqualTo(contentType);
}

@ParameterizedTest
@MethodSource("knownFileExtensionsParameters")
void knownFileExtensionsCaseInsensitive(String resourceName, String contentType) {
ContentTypeResolver contentTypeResolver = new DefaultContentTypeResolver();
assertThat(contentTypeResolver.resolveContentType(resourceName.toUpperCase(Locale.ROOT))).isEqualTo(contentType);
}

@Test
void unknownFileExtension() {
ContentTypeResolver contentTypeResolver = new DefaultContentTypeResolver();
assertThat(contentTypeResolver.resolveContentType("test.docx")).isEqualTo("application/octet-stream");
}

@Test
void unknownFileExtensionWithCustomUnknown() {
DefaultContentTypeResolver contentTypeResolver = new DefaultContentTypeResolver();
contentTypeResolver.setUnknownFileContentType("application/unknown");
assertThat(contentTypeResolver.resolveContentType("test.docx")).isEqualTo("application/unknown");
}

@Test
void addFileExtensionMapping() {
DefaultContentTypeResolver contentTypeResolver = new DefaultContentTypeResolver();
contentTypeResolver.addFileExtensionMapping("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
assertThat(contentTypeResolver.resolveContentType("test.docx")).isEqualTo("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
assertThat(contentTypeResolver.resolveContentType("test.doc")).isEqualTo("application/octet-stream");
}

@Test
void missingResourceName() {
ContentTypeResolver contentTypeResolver = new DefaultContentTypeResolver();
assertThat(contentTypeResolver.resolveContentType(null)).isNull();
assertThat(contentTypeResolver.resolveContentType("")).isNull();
}

static Stream<Arguments> knownFileExtensionsParameters() {
return Stream.of(
Arguments.of("oneTaskProcess.bpmn", "text/xml"),
Arguments.of("oneTaskProcess.bpmn20.xml", "text/xml"),
Arguments.of("oneTaskCase.cmmn.xml", "text/xml"),
Arguments.of("oneTaskCase.cmmn", "text/xml"),
Arguments.of("test.dmn", "text/xml"),
Arguments.of("test.dmn.xml", "text/xml"),
Arguments.of("test.app", "application/json"),
Arguments.of("test.channel", "application/json"),
Arguments.of("test.event", "application/json"),
Arguments.of("test.form", "application/json"),
Arguments.of("test.png", "image/png"),
Arguments.of("test.txt", "text/plain")
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import java.util.ArrayList;
import java.util.List;

import org.apache.http.entity.ContentType;
import org.flowable.common.rest.resolver.ContentTypeResolver;
import org.flowable.common.rest.util.RestUrlBuilder;
import org.flowable.eventregistry.api.ChannelDefinition;
Expand Down Expand Up @@ -64,12 +63,7 @@ public List<DeploymentResourceResponse> createDeploymentResourceResponseList(Str
// Add additional metadata to the artifact-strings before returning
List<DeploymentResourceResponse> responseList = new ArrayList<>(resourceList.size());
for (String resourceId : resourceList) {
String contentType = null;
if (resourceId.toLowerCase().endsWith(".cmmn")) {
contentType = ContentType.TEXT_XML.getMimeType();
} else {
contentType = contentTypeResolver.resolveContentType(resourceId);
}
String contentType = contentTypeResolver.resolveContentType(resourceId);
responseList.add(createDeploymentResourceResponse(deploymentId, resourceId, contentType, urlBuilder));
}
return responseList;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
import java.io.InputStream;
import java.util.List;

import jakarta.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.http.entity.ContentType;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
Expand All @@ -27,8 +28,6 @@
import org.flowable.eventregistry.rest.service.api.EventRegistryRestApiInterceptor;
import org.springframework.beans.factory.annotation.Autowired;

import jakarta.servlet.http.HttpServletResponse;

/**
* @author Tijs Rademakers
*/
Expand Down Expand Up @@ -65,12 +64,7 @@ protected byte[] getDeploymentResourceData(String deploymentId, String resourceN
List<String> resourceList = repositoryService.getDeploymentResourceNames(deploymentId);

if (resourceList.contains(resourceName)) {
String contentType = null;
if (resourceName.toLowerCase().endsWith(".event") || resourceName.toLowerCase().endsWith(".channel")) {
contentType = ContentType.APPLICATION_JSON.getMimeType();
} else {
contentType = contentTypeResolver.resolveContentType(resourceName);
}
String contentType = contentTypeResolver.resolveContentType(resourceName);
response.setContentType(contentType);

try (final InputStream resourceStream = repositoryService.getResourceAsStream(deploymentId, resourceName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import jakarta.servlet.http.HttpServletRequest;

import org.apache.http.entity.ContentType;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.rest.resolver.ContentTypeResolver;
import org.flowable.eventregistry.api.EventDeployment;
Expand Down Expand Up @@ -80,13 +79,8 @@ public DeploymentResourceResponse getDeploymentResource(@ApiParam(name = "deploy

if (resourceList.contains(resourceName)) {
// Build resource representation
String contentType = null;
if (resourceName.toLowerCase().endsWith(".event") || resourceName.toLowerCase().endsWith(".channel")) {
contentType = ContentType.APPLICATION_JSON.getMimeType();
} else {
contentType = contentTypeResolver.resolveContentType(resourceName);
}

String contentType = contentTypeResolver.resolveContentType(resourceName);

DeploymentResourceResponse response = restResponseFactory.createDeploymentResourceResponse(deploymentId, resourceName, contentType);
return response;

Expand Down
Loading

0 comments on commit 8bbfc56

Please sign in to comment.