* #20466 file upload - first example test done * #20466 second test from FileUploadDirectivesExample passing in Java * #20466 FileUploadDirectives examples file rename plus docs * #20466 post-review changes - adding better JAVA Multipart API * #20466 Multipart Java conversions fix and added spec for the Java side of the model
This commit is contained in:
parent
91eb27947b
commit
06aaa273f1
8 changed files with 387 additions and 9 deletions
|
|
@ -0,0 +1,134 @@
|
|||
/**
|
||||
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package docs.http.javadsl.server.directives;
|
||||
|
||||
import akka.http.impl.engine.rendering.BodyPartRenderer;
|
||||
import akka.http.javadsl.model.*;
|
||||
import akka.http.javadsl.server.Route;
|
||||
import akka.http.javadsl.server.Unmarshaller;
|
||||
import akka.http.javadsl.server.directives.FileInfo;
|
||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||
import akka.stream.javadsl.Framing;
|
||||
import akka.stream.javadsl.Source;
|
||||
import akka.util.ByteString;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class FileUploadDirectivesExamplesTest extends JUnitRouteTest {
|
||||
|
||||
@Test
|
||||
public void testUploadedFile() {
|
||||
//#uploadedFile
|
||||
// function (FileInfo, File) => Route to process the file metadata and file itself
|
||||
BiFunction<FileInfo, File, Route> infoFileRoute =
|
||||
(info, file) -> {
|
||||
// do something with the file and file metadata ...
|
||||
file.delete();
|
||||
return complete(StatusCodes.OK);
|
||||
};
|
||||
|
||||
|
||||
final Route route = uploadedFile("csv", infoFileRoute);
|
||||
|
||||
Map<String, String> filenameMapping = new HashMap<>();
|
||||
filenameMapping.put("filename", "data.csv");
|
||||
|
||||
akka.http.javadsl.model.Multipart.FormData multipartForm =
|
||||
Multiparts.createStrictFormDataFromParts(Multiparts.createFormDataBodyPartStrict("csv",
|
||||
HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8,
|
||||
"1,5,7\n11,13,17"), filenameMapping));
|
||||
|
||||
// test:
|
||||
testRoute(route).run(HttpRequest.POST("/").withEntity(
|
||||
multipartForm.toEntity(HttpCharsets.UTF_8, BodyPartRenderer
|
||||
.randomBoundaryWithDefaults())))
|
||||
.assertStatusCode(StatusCodes.OK);
|
||||
//#
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileUpload() {
|
||||
//#fileUpload
|
||||
final Route route = extractRequestContext(ctx -> {
|
||||
// function (FileInfo, Source<ByteString,Object>) => Route to process the file contents
|
||||
BiFunction<FileInfo, Source<ByteString, Object>, Route> processUploadedFile =
|
||||
(metadata, byteSource) -> {
|
||||
CompletionStage<Integer> sumF = byteSource.via(Framing.delimiter(
|
||||
ByteString.fromString("\n"), 1024))
|
||||
.mapConcat(bs -> Arrays.asList(bs.utf8String().split(",")))
|
||||
.map(s -> Integer.parseInt(s))
|
||||
.runFold(0, (acc, n) -> acc + n, ctx.getMaterializer());
|
||||
return onSuccess(() -> sumF, sum -> complete("Sum: " + sum));
|
||||
};
|
||||
return fileUpload("csv", processUploadedFile);
|
||||
});
|
||||
|
||||
Map<String, String> filenameMapping = new HashMap<>();
|
||||
filenameMapping.put("filename", "primes.csv");
|
||||
|
||||
akka.http.javadsl.model.Multipart.FormData multipartForm =
|
||||
Multiparts.createStrictFormDataFromParts(
|
||||
Multiparts.createFormDataBodyPartStrict("csv",
|
||||
HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8,
|
||||
"2,3,5\n7,11,13,17,23\n29,31,37\n"), filenameMapping));
|
||||
|
||||
// test:
|
||||
testRoute(route).run(HttpRequest.POST("/").withEntity(
|
||||
multipartForm.toEntity(HttpCharsets.UTF_8, BodyPartRenderer.randomBoundaryWithDefaults())))
|
||||
.assertStatusCode(StatusCodes.OK).assertEntityAs(Unmarshaller.entityToString(), "Sum: 178");
|
||||
//#
|
||||
}
|
||||
|
||||
@Ignore("compileOnly")
|
||||
@Test
|
||||
public void testFileProcessing() {
|
||||
//#fileProcessing
|
||||
final Route route = extractRequestContext(ctx -> {
|
||||
// function (FileInfo, Source<ByteString,Object>) => Route to process the file contents
|
||||
BiFunction<FileInfo, Source<ByteString, Object>, Route> processUploadedFile =
|
||||
(metadata, byteSource) -> {
|
||||
CompletionStage<Integer> sumF = byteSource.via(Framing.delimiter(
|
||||
ByteString.fromString("\n"), 1024))
|
||||
.mapConcat(bs -> Arrays.asList(bs.utf8String().split(",")))
|
||||
.map(s -> Integer.parseInt(s))
|
||||
.runFold(0, (acc, n) -> acc + n, ctx.getMaterializer());
|
||||
return onSuccess(() -> sumF, sum -> complete("Sum: " + sum));
|
||||
};
|
||||
return fileUpload("csv", processUploadedFile);
|
||||
});
|
||||
|
||||
Map<String, String> filenameMapping = new HashMap<>();
|
||||
filenameMapping.put("filename", "primes.csv");
|
||||
|
||||
String prefix = "primes";
|
||||
String suffix = ".csv";
|
||||
|
||||
File tempFile = null;
|
||||
try {
|
||||
tempFile = File.createTempFile(prefix, suffix);
|
||||
tempFile.deleteOnExit();
|
||||
Files.write(tempFile.toPath(), Arrays.asList("2,3,5", "7,11,13,17,23", "29,31,37"), Charset.forName("UTF-8"));
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
|
||||
akka.http.javadsl.model.Multipart.FormData multipartForm =
|
||||
Multiparts.createFormDataFromPath("csv", ContentTypes.TEXT_PLAIN_UTF8, tempFile.toPath());
|
||||
|
||||
// test:
|
||||
testRoute(route).run(HttpRequest.POST("/").withEntity(
|
||||
multipartForm.toEntity(HttpCharsets.UTF_8, BodyPartRenderer.randomBoundaryWithDefaults())))
|
||||
.assertStatusCode(StatusCodes.OK).assertEntityAs(Unmarshaller.entityToString(), "Sum: 178");
|
||||
//#
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,8 @@ with the same name, the first one will be used and the subsequent ones ignored.
|
|||
|
||||
Example
|
||||
-------
|
||||
TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 <https://github.com/akka/akka/issues/20466>`_.
|
||||
.. includecode2:: ../../../../code/docs/http/javadsl/server/directives/FileUploadDirectivesExamplesTest.java
|
||||
:snippet: fileUpload
|
||||
|
||||
::
|
||||
|
||||
|
|
|
|||
|
|
@ -20,4 +20,5 @@ one will be used and the subsequent ones ignored.
|
|||
|
||||
Example
|
||||
-------
|
||||
TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 <https://github.com/akka/akka/issues/20466>`_.
|
||||
.. includecode2:: ../../../../code/docs/http/javadsl/server/directives/FileUploadDirectivesExamplesTest.java
|
||||
:snippet: uploadedFile
|
||||
|
|
@ -87,7 +87,7 @@ public final class HttpEntities {
|
|||
(akka.http.scaladsl.model.ContentType) contentType,
|
||||
toScala(data));
|
||||
}
|
||||
|
||||
|
||||
private static akka.stream.scaladsl.Source<ByteString,Object> toScala(Source<ByteString, ?> javaSource) {
|
||||
return (akka.stream.scaladsl.Source<ByteString,Object>)javaSource.asScala();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package akka.http.javadsl.model;
|
||||
|
||||
import scala.collection.immutable.List;
|
||||
import scala.collection.immutable.Nil$;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static akka.http.impl.util.Util.convertArray;
|
||||
import static akka.http.impl.util.Util.convertMapToScala;
|
||||
import static akka.http.impl.util.Util.emptyMap;
|
||||
|
||||
/**
|
||||
* Constructors for Multipart instances
|
||||
*/
|
||||
public final class Multiparts {
|
||||
/**
|
||||
* Constructor for `multipart/form-data` content as defined in http://tools.ietf.org/html/rfc2388.
|
||||
* All parts must have distinct names. (This is not verified!)
|
||||
*/
|
||||
public static Multipart.FormData createFormDataFromParts(Multipart.FormData.BodyPart... parts) {
|
||||
return akka.http.scaladsl.model.Multipart.FormData$.MODULE$.createNonStrict(convertArray(parts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for `multipart/form-data` content as defined in http://tools.ietf.org/html/rfc2388.
|
||||
* All parts must have distinct names. (This is not verified!)
|
||||
*/
|
||||
public static Multipart.FormData.Strict createStrictFormDataFromParts(Multipart.FormData.BodyPart.Strict... parts) {
|
||||
return akka.http.scaladsl.model.Multipart.FormData$.MODULE$.createStrict(convertArray(parts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for `multipart/form-data` content as defined in http://tools.ietf.org/html/rfc2388.
|
||||
* All parts must have distinct names. (This is not verified!)
|
||||
*/
|
||||
public static Multipart.FormData.Strict createFormDataFromFields(Map<String, HttpEntity.Strict> fields) {
|
||||
return akka.http.scaladsl.model.Multipart.FormData$.MODULE$.createStrict(toScalaMap(fields));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a FormData instance that contains a single part backed by the given file.
|
||||
*
|
||||
* To create an instance with several parts or for multiple files, use
|
||||
* `Multiparts.createFormDataFromParts(Multiparts.createFormDataPartFromPath("field1", ...), Multiparts.createFormDataPartFromPath("field2", ...)`
|
||||
*/
|
||||
public static Multipart.FormData createFormDataFromPath(String name, ContentType contentType, Path path, int chunkSize) {
|
||||
return akka.http.scaladsl.model.Multipart.FormData$.MODULE$.fromPath(name, (akka.http.scaladsl.model.ContentType) contentType, path, chunkSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a FormData instance that contains a single part backed by the given file.
|
||||
*
|
||||
* To create an instance with several parts or for multiple files, use
|
||||
* `Multiparts.createFormDataFromParts(Multiparts.createFormDataPartFromPath("field1", ...), Multiparts.createFormDataPartFromPath("field2", ...)`
|
||||
*/
|
||||
public static Multipart.FormData createFormDataFromPath(String name, ContentType contentType, Path path) {
|
||||
return akka.http.scaladsl.model.Multipart.FormData$.MODULE$.fromPath(name, (akka.http.scaladsl.model.ContentType) contentType, path, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BodyPart backed by a file that will be streamed using a FileSource.
|
||||
*/
|
||||
public static Multipart.FormData.BodyPart createFormDataPartFromPath(String name, ContentType contentType, Path path, int chunkSize) {
|
||||
return akka.http.scaladsl.model.Multipart$FormData$BodyPart$.MODULE$.fromPath(name, (akka.http.scaladsl.model.ContentType) contentType, path, chunkSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BodyPart backed by a file that will be streamed using a FileSource.
|
||||
*/
|
||||
public static Multipart.FormData.BodyPart createFormDataPartFromPath(String name, ContentType contentType, Path path) {
|
||||
return akka.http.scaladsl.model.Multipart$FormData$BodyPart$.MODULE$.fromPath(name, (akka.http.scaladsl.model.ContentType) contentType, path, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BodyPart.
|
||||
*/
|
||||
public static Multipart.FormData.BodyPart createFormDataBodyPart(String name, BodyPartEntity entity) {
|
||||
List nil = Nil$.MODULE$;
|
||||
Map<String, String> additionalDispositionParams = Collections.emptyMap();
|
||||
return akka.http.scaladsl.model.Multipart$FormData$BodyPart$Builder$.MODULE$.create(name, (akka.http.scaladsl.model.BodyPartEntity) entity,
|
||||
convertMapToScala(additionalDispositionParams), nil);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BodyPart.
|
||||
*/
|
||||
public static Multipart.FormData.BodyPart createFormDataBodyPart(String name, BodyPartEntity entity, Map<String, String> additionalDispositionParams) {
|
||||
List nil = Nil$.MODULE$;
|
||||
return akka.http.scaladsl.model.Multipart$FormData$BodyPart$Builder$.MODULE$.create(name, (akka.http.scaladsl.model.BodyPartEntity) entity,
|
||||
convertMapToScala(additionalDispositionParams), nil);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BodyPart.
|
||||
*/
|
||||
public static Multipart.FormData.BodyPart createFormDataBodyPart(String name, BodyPartEntity entity, Map<String, String> additionalDispositionParams, java.util.List<HttpHeader> headers) {
|
||||
return akka.http.scaladsl.model.Multipart$FormData$BodyPart$Builder$.MODULE$.create(name, (akka.http.scaladsl.model.BodyPartEntity) entity,
|
||||
convertMapToScala(additionalDispositionParams), toScalaSeq(headers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BodyPart.Strict.
|
||||
*/
|
||||
public static Multipart.FormData.BodyPart.Strict createFormDataBodyPartStrict(String name, HttpEntity.Strict entity) {
|
||||
List nil = Nil$.MODULE$;
|
||||
Map<String, String> additionalDispositionParams = Collections.emptyMap();
|
||||
return akka.http.scaladsl.model.Multipart$FormData$BodyPart$StrictBuilder$.MODULE$.createStrict(name, (akka.http.scaladsl.model.HttpEntity.Strict) entity,
|
||||
convertMapToScala(additionalDispositionParams), nil);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BodyPart.Strict.
|
||||
*/
|
||||
public static Multipart.FormData.BodyPart.Strict createFormDataBodyPartStrict(String name, HttpEntity.Strict entity, Map<String, String> additionalDispositionParams) {
|
||||
List nil = Nil$.MODULE$;
|
||||
return akka.http.scaladsl.model.Multipart$FormData$BodyPart$StrictBuilder$.MODULE$.createStrict(name, (akka.http.scaladsl.model.HttpEntity.Strict) entity,
|
||||
convertMapToScala(additionalDispositionParams), nil);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BodyPart.Strict.
|
||||
*/
|
||||
public static Multipart.FormData.BodyPart.Strict createFormDataBodyPartStrict(String name, HttpEntity.Strict entity, Map<String, String> additionalDispositionParams, java.util.List<HttpHeader> headers) {
|
||||
return akka.http.scaladsl.model.Multipart$FormData$BodyPart$StrictBuilder$.MODULE$.createStrict(name, (akka.http.scaladsl.model.HttpEntity.Strict) entity,
|
||||
convertMapToScala(additionalDispositionParams), toScalaSeq(headers));
|
||||
}
|
||||
|
||||
private static scala.collection.immutable.Map<String, HttpEntity.Strict> toScalaMap(Map<String, HttpEntity.Strict> map) {
|
||||
return emptyMap.$plus$plus(scala.collection.JavaConverters.mapAsScalaMapConverter(map).asScala());
|
||||
}
|
||||
|
||||
private static scala.collection.Iterable<HttpHeader> toScalaSeq(java.util.List<HttpHeader> _headers) {
|
||||
return scala.collection.JavaConverters.collectionAsScalaIterableConverter(_headers).asScala();
|
||||
}
|
||||
}
|
||||
|
|
@ -139,4 +139,14 @@ private[http] object BodyPartRenderer {
|
|||
random.nextBytes(array)
|
||||
Base64.custom.encodeToString(array, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new random number of default length and base64 encodes it (using a custom "safe" alphabet).
|
||||
*/
|
||||
def randomBoundaryWithDefaults(): String = randomBoundary()
|
||||
|
||||
/**
|
||||
* Creates a new random number of the given length and base64 encodes it (using a custom "safe" alphabet).
|
||||
*/
|
||||
def randomBoundaryWithDefaultRandom(length: Int): String = randomBoundary(length)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ object Multipart {
|
|||
|
||||
/** Java API */
|
||||
override def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[jm.Multipart.General.Strict] =
|
||||
super.toStrict(timeoutMillis, materializer).asInstanceOf[Future[jm.Multipart.General.Strict]].toJava
|
||||
super.toStrict(timeoutMillis, materializer).toScala.asInstanceOf[Future[jm.Multipart.General.Strict]].toJava
|
||||
}
|
||||
object General {
|
||||
def apply(mediaType: MediaType.Multipart, parts: BodyPart.Strict*): Strict = Strict(mediaType, parts.toVector)
|
||||
|
|
@ -258,7 +258,7 @@ object Multipart {
|
|||
|
||||
/** Java API */
|
||||
override def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[jm.Multipart.General.BodyPart.Strict] =
|
||||
super.toStrict(timeoutMillis, materializer).asInstanceOf[Future[jm.Multipart.General.BodyPart.Strict]].toJava
|
||||
super.toStrict(timeoutMillis, materializer).toScala.asInstanceOf[Future[jm.Multipart.General.BodyPart.Strict]].toJava
|
||||
|
||||
private[BodyPart] def tryCreateFormDataBodyPart[T](f: (String, Map[String, String], immutable.Seq[HttpHeader]) ⇒ T): Try[T] = {
|
||||
val params = dispositionParams
|
||||
|
|
@ -323,12 +323,22 @@ object Multipart {
|
|||
|
||||
/** Java API */
|
||||
override def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[jm.Multipart.FormData.Strict] =
|
||||
super.toStrict(timeoutMillis, materializer).asInstanceOf[Future[jm.Multipart.FormData.Strict]].toJava
|
||||
super.toStrict(timeoutMillis, materializer).toScala.asInstanceOf[Future[jm.Multipart.FormData.Strict]].toJava
|
||||
}
|
||||
object FormData {
|
||||
def apply(parts: Multipart.FormData.BodyPart.Strict*): Multipart.FormData.Strict = Strict(parts.toVector)
|
||||
def apply(parts: Multipart.FormData.BodyPart*): Multipart.FormData = Multipart.FormData(Source(parts.toVector))
|
||||
|
||||
// FIXME: SI-2991 workaround - two functions below. Remove when (hopefully) this issue is fixed
|
||||
/** INTERNAL API */
|
||||
private[akka] def createStrict(parts: Multipart.FormData.BodyPart.Strict*): Multipart.FormData.Strict = Strict(parts.toVector)
|
||||
/** INTERNAL API */
|
||||
private[akka] def createNonStrict(parts: Multipart.FormData.BodyPart*): Multipart.FormData = Multipart.FormData(Source(parts.toVector))
|
||||
/** INTERNAL API */
|
||||
private[akka] def createStrict(fields: Map[String, akka.http.javadsl.model.HttpEntity.Strict]): Multipart.FormData.Strict = Multipart.FormData.Strict {
|
||||
fields.map { case (name, entity: akka.http.scaladsl.model.HttpEntity.Strict) ⇒ Multipart.FormData.BodyPart.Strict(name, entity) }(collection.breakOut)
|
||||
}
|
||||
|
||||
def apply(fields: Map[String, HttpEntity.Strict]): Multipart.FormData.Strict = Multipart.FormData.Strict {
|
||||
fields.map { case (name, entity) ⇒ Multipart.FormData.BodyPart.Strict(name, entity) }(collection.breakOut)
|
||||
}
|
||||
|
|
@ -426,7 +436,7 @@ object Multipart {
|
|||
|
||||
/** Java API */
|
||||
override def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[jm.Multipart.FormData.BodyPart.Strict] =
|
||||
super.toStrict(timeoutMillis, materializer).asInstanceOf[Future[jm.Multipart.FormData.BodyPart.Strict]].toJava
|
||||
super.toStrict(timeoutMillis, materializer).toScala.asInstanceOf[Future[jm.Multipart.FormData.BodyPart.Strict]].toJava
|
||||
}
|
||||
object BodyPart {
|
||||
def apply(_name: String, _entity: BodyPartEntity,
|
||||
|
|
@ -467,6 +477,26 @@ object Multipart {
|
|||
FastFuture.successful(this)
|
||||
override def productPrefix = "FormData.BodyPart.Strict"
|
||||
}
|
||||
|
||||
/** INTERNAL API */
|
||||
private[akka] object Builder {
|
||||
def create(_name: String, _entity: BodyPartEntity,
|
||||
_additionalDispositionParams: Map[String, String],
|
||||
_additionalHeaders: Iterable[akka.http.javadsl.model.HttpHeader]): Multipart.FormData.BodyPart = {
|
||||
val _headers = _additionalHeaders.to[immutable.Seq] map { case h: akka.http.scaladsl.model.HttpHeader ⇒ h }
|
||||
apply(_name, _entity, _additionalDispositionParams, _headers)
|
||||
}
|
||||
}
|
||||
|
||||
/** INTERNAL API */
|
||||
private[akka] object StrictBuilder {
|
||||
def createStrict(_name: String, _entity: HttpEntity.Strict,
|
||||
_additionalDispositionParams: Map[String, String],
|
||||
_additionalHeaders: Iterable[akka.http.javadsl.model.HttpHeader]): Multipart.FormData.BodyPart.Strict = {
|
||||
val _headers = _additionalHeaders.to[immutable.Seq] map { case h: akka.http.scaladsl.model.HttpHeader ⇒ h }
|
||||
Strict(_name, _entity, _additionalDispositionParams, _headers)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -488,7 +518,7 @@ object Multipart {
|
|||
|
||||
/** Java API */
|
||||
override def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[jm.Multipart.ByteRanges.Strict] =
|
||||
super.toStrict(timeoutMillis, materializer).asInstanceOf[Future[jm.Multipart.ByteRanges.Strict]].toJava
|
||||
super.toStrict(timeoutMillis, materializer).toScala.asInstanceOf[Future[jm.Multipart.ByteRanges.Strict]].toJava
|
||||
}
|
||||
object ByteRanges {
|
||||
def apply(parts: Multipart.ByteRanges.BodyPart.Strict*): Strict = Strict(parts.toVector)
|
||||
|
|
@ -563,7 +593,7 @@ object Multipart {
|
|||
|
||||
/** Java API */
|
||||
override def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[jm.Multipart.ByteRanges.BodyPart.Strict] =
|
||||
super.toStrict(timeoutMillis, materializer).asInstanceOf[Future[jm.Multipart.ByteRanges.BodyPart.Strict]].toJava
|
||||
super.toStrict(timeoutMillis, materializer).toScala.asInstanceOf[Future[jm.Multipart.ByteRanges.BodyPart.Strict]].toJava
|
||||
}
|
||||
object BodyPart {
|
||||
def apply(_contentRange: ContentRange, _entity: BodyPartEntity, _rangeUnit: RangeUnit = RangeUnits.Bytes,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package akka.http.javadsl.model
|
||||
|
||||
import java.util
|
||||
import com.typesafe.config.{ Config, ConfigFactory }
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration._
|
||||
import org.scalatest.{ BeforeAndAfterAll, Inside, Matchers, WordSpec }
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.actor.ActorSystem
|
||||
import scala.compat.java8.FutureConverters
|
||||
|
||||
class MultipartsSpec extends WordSpec with Matchers with Inside with BeforeAndAfterAll {
|
||||
|
||||
val testConf: Config = ConfigFactory.parseString("""
|
||||
akka.event-handlers = ["akka.testkit.TestEventListener"]
|
||||
akka.loglevel = WARNING""")
|
||||
implicit val system = ActorSystem(getClass.getSimpleName, testConf)
|
||||
implicit val materializer = ActorMaterializer()
|
||||
override def afterAll() = system.terminate()
|
||||
|
||||
"Multiparts.createFormDataFromParts" should {
|
||||
"create a model from Multiparts.createFormDataBodyPartparts" in {
|
||||
val streamed = Multiparts.createFormDataFromParts(
|
||||
Multiparts.createFormDataBodyPart("foo", HttpEntities.create("FOO")),
|
||||
Multiparts.createFormDataBodyPart("bar", HttpEntities.create("BAR")))
|
||||
val strictCS = streamed.toStrict(1000, materializer)
|
||||
val strict = Await.result(FutureConverters.toScala(strictCS), 1.second)
|
||||
|
||||
strict shouldEqual akka.http.scaladsl.model.Multipart.FormData(
|
||||
Map("foo" → akka.http.scaladsl.model.HttpEntity("FOO"), "bar" → akka.http.scaladsl.model.HttpEntity("BAR")))
|
||||
}
|
||||
}
|
||||
|
||||
"Multiparts.createFormDataFromFields" should {
|
||||
"create a model from a map of fields" in {
|
||||
val fields = new util.HashMap[String, HttpEntity.Strict]
|
||||
fields.put("foo", HttpEntities.create("FOO"))
|
||||
val streamed = Multiparts.createFormDataFromFields(fields)
|
||||
val strictCS = streamed.toStrict(1000, materializer)
|
||||
val strict = Await.result(FutureConverters.toScala(strictCS), 1.second)
|
||||
|
||||
strict shouldEqual akka.http.scaladsl.model.Multipart.FormData(
|
||||
Map("foo" → akka.http.scaladsl.model.HttpEntity("FOO")))
|
||||
}
|
||||
}
|
||||
|
||||
"Multiparts.createStrictFormDataFromParts" should {
|
||||
"create a strict model from Multiparts.createFormDataBodyPartStrict parts" in {
|
||||
val streamed = Multiparts.createStrictFormDataFromParts(
|
||||
Multiparts.createFormDataBodyPartStrict("foo", HttpEntities.create("FOO")),
|
||||
Multiparts.createFormDataBodyPartStrict("bar", HttpEntities.create("BAR")))
|
||||
val strict = streamed
|
||||
|
||||
strict shouldEqual akka.http.scaladsl.model.Multipart.FormData(
|
||||
Map("foo" → akka.http.scaladsl.model.HttpEntity("FOO"), "bar" → akka.http.scaladsl.model.HttpEntity("BAR")))
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue