Skip to content
This repository has been archived by the owner on Aug 13, 2020. It is now read-only.

Commit

Permalink
Add support for multipart POST to framework
Browse files Browse the repository at this point in the history
  • Loading branch information
mapingo committed Mar 10, 2017
1 parent ac89a5c commit 08d3fff
Show file tree
Hide file tree
Showing 46 changed files with 2,018 additions and 56 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ on [Keep a CHANGELOG](http://keepachangelog.com/). This project adheres to
- MultipartRestClient in test-utils: client for multipart file uploads
- LiquibaseDatabaseBootstrapper in test-utils: bootstraps a database using your liquibase scripts
- Dependency on utilities-core
- Ability for multipart endpoint to handle multiple files
- Generators for generating multipart endpoints from RAML

### Fixed
- Generate random ZonedDateTime in different time zones and provide option to generate in UTC specific timezone
Expand Down
5 changes: 5 additions & 0 deletions framework-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@
<artifactId>rest-adapter-generator</artifactId>
<version>${cpp.framework.version}</version>
</dependency>
<dependency>
<groupId>uk.gov.justice.services</groupId>
<artifactId>rest-adapter-file-service</artifactId>
<version>${cpp.framework.version}</version>
</dependency>
<dependency>
<groupId>uk.gov.justice.services</groupId>
<artifactId>messaging-adapter-generator</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package uk.gov.justice.services.generators.commons.validator;

import static java.lang.String.format;
import static javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA;

import java.util.List;
import java.util.Map;

import org.raml.model.Action;
import org.raml.model.ActionType;
import org.raml.model.MimeType;
import org.raml.model.ParamType;
import org.raml.model.Resource;
import org.raml.model.parameter.FormParameter;

public class MultipartHasFormParameters extends AbstractResourceRamlValidator {

@Override
protected void validate(final Resource resource) {
final Map<ActionType, Action> actions = resource.getActions();

if (!actions.isEmpty()) {
actions.values().forEach(this::extractBodyMimeTypes);
}
}

private void extractBodyMimeTypes(final Action action) {
final Map<String, MimeType> body = action.getBody();
if (body != null) {
body.values().stream()
.filter(mimeType -> MULTIPART_FORM_DATA.equals(mimeType.getType()))
.forEach(this::validateFormParameters);
}
}

private void validateFormParameters(final MimeType mimeType) {
final Map<String, List<FormParameter>> formParameters = mimeType.getFormParameters();

if (null == formParameters || formParameters.isEmpty()) {
throw new RamlValidationException("Multipart form must contain form parameters");
}

formParameters.values().forEach(this::validateFormParameter);
}

private void validateFormParameter(final List<FormParameter> values) {
final FormParameter formParameter = values.get(0);

if (!ParamType.FILE.equals(formParameter.getType())) {
throw new RamlValidationException(format("Multipart form parameter is expected to be of type FILE, instead was %s", values.get(0).getType()));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package uk.gov.justice.services.generators.commons.validator;

import static org.raml.model.ActionType.POST;
import static org.raml.model.ParamType.STRING;
import static uk.gov.justice.services.generators.test.utils.builder.HttpActionBuilder.httpAction;
import static uk.gov.justice.services.generators.test.utils.builder.MimeTypeBuilder.multipartMimeType;
import static uk.gov.justice.services.generators.test.utils.builder.MimeTypeBuilder.multipartWithFileFormParameter;
import static uk.gov.justice.services.generators.test.utils.builder.RamlBuilder.restRamlWithDefaults;
import static uk.gov.justice.services.generators.test.utils.builder.ResourceBuilder.resource;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.raml.model.Raml;

public class MultipartHasFormParametersTest {

@Rule
public ExpectedException exception = ExpectedException.none();

private RamlValidator validator = new MultipartHasFormParameters();

@Test
public void shouldPassIfMultipartContainsCorrectFormParameter() throws Exception {
final Raml raml = restRamlWithDefaults()
.with(resource("/some/path")
.with(httpAction()
.withHttpActionType(POST)
.withMediaTypeWithoutSchema(multipartWithFileFormParameter("photoId")))
).build();

validator.validate(raml);
}

@Test
public void shouldFailIfMultipartHasNoFormParameters() throws Exception {
exception.expect(RamlValidationException.class);
exception.expectMessage("Multipart form must contain form parameters");

final Raml raml = restRamlWithDefaults()
.with(resource("/some/path")
.with(httpAction()
.withHttpActionType(POST)
.withMediaTypeWithoutSchema(multipartMimeType()))
).build();

validator.validate(raml);
}

@Test
public void shouldFailIfMultipartHasFormParameterWithIncorrectType() throws Exception {
exception.expect(RamlValidationException.class);
exception.expectMessage("Multipart form parameter is expected to be of type FILE, instead was STRING");

final Raml raml = restRamlWithDefaults()
.with(resource("/some/path")
.with(httpAction()
.withHttpActionType(POST)
.withMediaTypeWithoutSchema(multipartMimeType()
.withFormParameter("photoId", STRING, true)))
).build();

validator.validate(raml);
}

@Test
public void shouldPassIfNoMultipartPresent() throws Exception {
final Raml raml = restRamlWithDefaults()
.with(resource("/some/path")
.withDefaultPostAction()
).build();

validator.validate(raml);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ public HttpActionBuilder withMediaTypeWithoutSchema(final MimeType mimeType) {
return withMediaType(mimeType, Optional.empty());
}

public HttpActionBuilder withMediaTypeWithoutSchema(final MimeTypeBuilder mimeType) {
return withMediaType(mimeType.build(), Optional.empty());
}

public HttpActionBuilder withMediaType(final String stringMimeType, final Optional<String> schema) {
return withMediaType(new MimeType(stringMimeType), schema);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package uk.gov.justice.services.generators.test.utils.builder;

import static java.util.Collections.singletonList;
import static javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA;
import static org.raml.model.ParamType.FILE;

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

import org.raml.model.MimeType;
import org.raml.model.ParamType;
import org.raml.model.parameter.FormParameter;

public class MimeTypeBuilder {

final Map<String, List<FormParameter>> formParameters = new HashMap<>();
private final String type;

public MimeTypeBuilder(final String type) {
this.type = type;
}

public static MimeTypeBuilder multipartMimeType() {
return new MimeTypeBuilder(MULTIPART_FORM_DATA);
}

public static MimeTypeBuilder multipartWithFileFormParameter(final String fieldName) {
final MimeTypeBuilder mimeTypeBuilder = multipartMimeType();
mimeTypeBuilder.withRequiredFileTypeFormParameter(fieldName);
return mimeTypeBuilder;
}

public MimeTypeBuilder withRequiredFileTypeFormParameter(final String fieldName) {
return withFormParameter(fieldName, FILE, true);
}

public MimeTypeBuilder withRequiredFormParameter(final String fieldName, final ParamType paramType) {
return withFormParameter(fieldName, paramType, true);
}

public MimeTypeBuilder withOptionalFormParameter(final String fieldName, final ParamType paramType) {
return withFormParameter(fieldName, paramType, false);
}

public MimeTypeBuilder withNoDisplayNameFormParameter(final String fieldName, final ParamType paramType) {
final FormParameter formParameter = new FormParameter();
formParameter.setType(paramType);
formParameter.setRequired(true);

formParameters.put(fieldName, singletonList(formParameter));
return this;
}

public MimeTypeBuilder withFormParameter(final String fieldName, final ParamType paramType, final boolean required) {
final FormParameter formParameter = new FormParameter();
formParameter.setType(paramType);
formParameter.setRequired(required);

formParameters.put(fieldName, singletonList(formParameter));
return this;
}

public MimeType build() {
final MimeType mimeType = new MimeType(type);
mimeType.setFormParameters(formParameters);
return mimeType;
}
}
3 changes: 2 additions & 1 deletion generators/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
<module>rest-client-core</module>
<module>rest-client-generator</module>
<module>rest-core</module>

<module>rest-adapter-file-service</module>

</modules>

<artifactId>generators</artifactId>
Expand Down
19 changes: 14 additions & 5 deletions generators/rest-adapter-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,20 @@
<artifactId>slf4j-api</artifactId>
</dependency>

<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
</dependency>
<dependency>
<groupId>uk.gov.justice.services</groupId>
<artifactId>file-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand All @@ -64,11 +78,6 @@
<artifactId>javax.json</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package uk.gov.justice.services.adapter.rest.interceptor;

public class FileStoreFailedException extends RuntimeException {

public FileStoreFailedException(final String message, final Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package uk.gov.justice.services.adapter.rest.mutipart;

import static uk.gov.justice.services.adapter.rest.mutipart.FileInputDetails.FILE_INPUT_DETAILS_LIST;
import static uk.gov.justice.services.core.interceptor.InterceptorContext.interceptorContextWithInput;

import uk.gov.justice.services.core.interceptor.InterceptorContext;
import uk.gov.justice.services.messaging.JsonEnvelope;

import java.util.List;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class FileBasedInterceptorContextFactory {

public InterceptorContext create(final List<FileInputDetails> fileInputDetails, final JsonEnvelope envelope) {
final InterceptorContext interceptorContext = interceptorContextWithInput(envelope);
interceptorContext.setInputParameter(FILE_INPUT_DETAILS_LIST, fileInputDetails);

return interceptorContext;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package uk.gov.justice.services.adapter.rest.mutipart;

import java.io.InputStream;

import javax.ws.rs.core.MediaType;

public class FileInputDetails {

public static final String FILE_INPUT_DETAILS_LIST = "fileInputDetailsList";

private final String fileName;
private final String fieldName;
private final MediaType mediaType;
private final InputStream inputStream;

public FileInputDetails(
final String fileName,
final String fieldName,
final MediaType mediaType,
final InputStream inputStream) {
this.fileName = fileName;
this.fieldName = fieldName;
this.mediaType = mediaType;
this.inputStream = inputStream;
}

public String getFileName() {
return fileName;
}

public String getFieldName() {
return fieldName;
}

public MediaType getMediaType() {
return mediaType;
}

public InputStream getInputStream() {
return inputStream;
}
}
Loading

0 comments on commit 08d3fff

Please sign in to comment.