Skip to content

Commit

Permalink
Docs progress
Browse files Browse the repository at this point in the history
  • Loading branch information
jameskleeh committed Oct 9, 2019
1 parent 72e53ea commit 3bb6009
Show file tree
Hide file tree
Showing 7 changed files with 350 additions and 94 deletions.
4 changes: 2 additions & 2 deletions src/main/docs/guide/httpServer/uploads.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Route arguments of type rs:Publisher[Publisher<PartData>] will be treated as onl

If requirements dictate you must have knowledge about the metadata of the file being received, a special class called api:http.multipart.StreamingFileUpload[] has been created that is a rs:Publisher[Publisher<PartData>], but also has file information like the content type and file name.

snippet::io.micronaut.docs.server.upload.UploadController[tags="imports,class,upload", indent=0]
snippet::io.micronaut.docs.server.upload.UploadController[tags="imports,class,upload,endclass", indent=0]

<1> The method is set to consume api:http.MediaType#MULTIPART_FORM_DATA[]
<2> The method parameters match form attribute names. In this case the `file` will match for example an `<input type="file" name="file">`
Expand All @@ -37,7 +37,7 @@ Route arguments that are not publishers will cause the route execution to be del

If requirements dictate you must have knowledge about the metadata of the file being received, a special class called api:http.multipart.CompletedFileUpload[] has been created that has methods to retrieve the data of the file, but also has file information like the content type and file name.

snippet::io.micronaut.docs.server.upload.UploadController[tags="completedImports,class,completedUpload", indent=0]
snippet::io.micronaut.docs.server.upload.UploadController[tags="class,completedUpload,endclass", indent=0]

<1> The method is set to consume api:http.MediaType#MULTIPART_FORM_DATA[]
<2> The method parameters match form attribute names. In this case the `file` will match for example an `<input type="file" name="file">`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,63 +1,38 @@
/*
* Copyright 2017-2019 original authors
*
* 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 io.micronaut.docs.server.upload;
package io.micronaut.docs.server.upload

// tag::imports[]
import io.micronaut.http.HttpResponse
import io.micronaut.http.HttpStatus
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Post
import io.micronaut.http.multipart.CompletedFileUpload
import io.micronaut.http.multipart.StreamingFileUpload
import io.reactivex.Single
import org.reactivestreams.Publisher

import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.multipart.CompletedFileUpload;
import io.micronaut.http.multipart.StreamingFileUpload;
import io.reactivex.Single;
import org.reactivestreams.Publisher;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
// end::imports[]
// tag::completedImports[]
// end::completedImports[]

/**
* @author Graeme Rocher
* @since 1.0
*/
// tag::class[]
@Controller("/upload")
class UploadController {
// end::class[]

// tag::upload[]
@Post(value = "/", consumes = MediaType.MULTIPART_FORM_DATA) // <1>
public Single<HttpResponse<String>> upload(StreamingFileUpload file) throws IOException { // <2>
File tempFile = File.createTempFile(file.getFilename(), "temp")
Single<HttpResponse<String>> upload(StreamingFileUpload file) { // <2>
File tempFile = File.createTempFile(file.filename, "temp")
Publisher<Boolean> uploadPublisher = file.transferTo(tempFile) // <3>
Single.fromPublisher(uploadPublisher) // <4>
.map({ success ->
if (success) {
return HttpResponse.ok("Uploaded")
HttpResponse.ok("Uploaded")
} else {
return HttpResponse.<String> status(HttpStatus.CONFLICT)
.body("Upload Failed");
HttpResponse.<String>status(HttpStatus.CONFLICT)
.body("Upload Failed")
}
})
}
Expand All @@ -67,10 +42,10 @@ class UploadController {
@Post(value = "/completed", consumes = MediaType.MULTIPART_FORM_DATA) // <1>
HttpResponse<String> uploadCompleted(CompletedFileUpload file) { // <2>
try {
File tempFile = File.createTempFile(file.getFilename(), "temp") //<3>
Path path = Paths.get(tempFile.getAbsolutePath())
Files.write(path, file.getBytes())//<3>
return HttpResponse.ok("Uploaded")
File tempFile = File.createTempFile(file.filename, "temp") //<3>
Path path = Paths.get(tempFile.absolutePath)
Files.write(path, file.bytes)//<3>
HttpResponse.ok("Uploaded")
} catch (IOException exception) {
HttpResponse.badRequest("Upload Failed")
}
Expand All @@ -79,12 +54,12 @@ class UploadController {

// tag::bytesUpload[]
@Post(value = "/bytes", consumes = MediaType.MULTIPART_FORM_DATA) // <1>
public HttpResponse<String> uploadBytes(byte[] file, String fileName) { // <2>
HttpResponse<String> uploadBytes(byte[] file, String fileName) { // <2>
try {
File tempFile = File.createTempFile(fileName, "temp")
Path path = Paths.get(tempFile.getAbsolutePath())
Path path = Paths.get(tempFile.absolutePath)
Files.write(path, file) // <3>
return HttpResponse.ok("Uploaded")
HttpResponse.ok("Uploaded")
} catch (IOException exception) {
HttpResponse.badRequest("Upload Failed")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.micronaut.docs.server.upload

// tag::imports[]
import io.micronaut.http.HttpResponse
import io.micronaut.http.HttpStatus
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Post
import io.micronaut.http.multipart.CompletedFileUpload
import io.micronaut.http.multipart.StreamingFileUpload
import io.reactivex.Single

import java.io.File
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Paths
// end::imports[]

// tag::class[]
@Controller("/upload")
class UploadController {
// end::class[]

// tag::upload[]
@Post(value = "/", consumes = [MediaType.MULTIPART_FORM_DATA]) // <1>
fun upload(file: StreamingFileUpload): Single<HttpResponse<String>> { // <2>
val tempFile = File.createTempFile(file.filename, "temp")
val uploadPublisher = file.transferTo(tempFile) // <3>
return Single.fromPublisher(uploadPublisher) // <4>
.map { success ->
if (success) {
HttpResponse.ok("Uploaded")
} else {
HttpResponse.status<String>(HttpStatus.CONFLICT)
.body("Upload Failed")
}
}
}
// end::upload[]

// tag::completedUpload[]
@Post(value = "/completed", consumes = [MediaType.MULTIPART_FORM_DATA]) // <1>
fun uploadCompleted(file: CompletedFileUpload): HttpResponse<String> { // <2>
return try {
val tempFile = File.createTempFile(file.filename, "temp") //<3>
val path = Paths.get(tempFile.absolutePath)
Files.write(path, file.bytes) //<3>
HttpResponse.ok("Uploaded")
} catch (exception: IOException) {
HttpResponse.badRequest("Upload Failed")
}

}
// end::completedUpload[]

// tag::bytesUpload[]
@Post(value = "/bytes", consumes = [MediaType.MULTIPART_FORM_DATA]) // <1>
fun uploadBytes(file: ByteArray, fileName: String): HttpResponse<String> { // <2>
return try {
val tempFile = File.createTempFile(fileName, "temp")
val path = Paths.get(tempFile.absolutePath)
Files.write(path, file) // <3>
HttpResponse.ok("Uploaded")
} catch (exception: IOException) {
HttpResponse.badRequest("Upload Failed")
}

}
// end::bytesUpload[]

// tag::endclass[]
}
// end::endclass[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package io.micronaut.docs.server.upload

import io.kotlintest.shouldBe
import io.kotlintest.shouldThrow
import io.kotlintest.specs.StringSpec
import io.micronaut.context.ApplicationContext
import io.micronaut.http.HttpRequest
import io.micronaut.http.HttpStatus
import io.micronaut.http.MediaType
import io.micronaut.http.client.RxHttpClient
import io.micronaut.http.client.exceptions.HttpClientResponseException
import io.micronaut.http.client.multipart.MultipartBody
import io.micronaut.runtime.server.EmbeddedServer
import io.reactivex.Flowable

class UploadControllerSpec: StringSpec() {

val embeddedServer = autoClose(
ApplicationContext.run(EmbeddedServer::class.java)
)

val client = autoClose(
embeddedServer.applicationContext.createBean(RxHttpClient::class.java, embeddedServer.getURL())
)

init {
"test file upload"() {
val body = MultipartBody.builder()
.addPart("file", "file.json", MediaType.APPLICATION_JSON_TYPE, "{\"title\":\"Foo\"}".toByteArray())
.build()

val flowable = Flowable.fromPublisher(client.exchange(
HttpRequest.POST("/upload", body)
.contentType(MediaType.MULTIPART_FORM_DATA)
.accept(MediaType.TEXT_PLAIN_TYPE),
String::class.java
))
val response = flowable.blockingFirst()

response.status() shouldBe HttpStatus.OK
response.body.get() shouldBe "Uploaded"
}

"test completed file upload with no name but with bytes"() {
val body = MultipartBody.builder()
.addPart("file", "", MediaType.APPLICATION_JSON_TYPE, "{\"title\":\"Foo\"}".toByteArray())
.build()

val flowable = Flowable.fromPublisher(client.exchange(
HttpRequest.POST("/upload/completed", body)
.contentType(MediaType.MULTIPART_FORM_DATA)
.accept(MediaType.TEXT_PLAIN_TYPE),
String::class.java
))

val ex = shouldThrow<HttpClientResponseException> { flowable.blockingFirst() }

ex.message shouldBe "Required argument [CompletedFileUpload file] not specified"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
/*
* Copyright 2017-2019 original authors
*
* 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 io.micronaut.docs.server.upload

import io.micronaut.AbstractMicronautSpec
Expand All @@ -24,10 +9,6 @@ import io.micronaut.http.client.exceptions.HttpClientResponseException
import io.micronaut.http.client.multipart.MultipartBody
import io.reactivex.Flowable

/**
* @author Graeme Rocher
* @since 1.0
*/
class UploadSpec extends AbstractMicronautSpec {

void cleanup() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,6 @@
/*
* Copyright 2017-2019 original authors
*
* 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 io.micronaut.docs.server.upload;

// tag::imports[]

import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
Expand All @@ -32,15 +16,8 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

// end::imports[]
// tag::completedImports[]
// end::completedImports[]

/**
* @author Graeme Rocher
* @since 1.0
*/
// tag::class[]
@Controller("/upload")
public class UploadController {
Expand Down Expand Up @@ -91,4 +68,6 @@ public HttpResponse<String> uploadBytes(byte[] file, String fileName) { // <2>
}
// end::bytesUpload[]

// tag::endclass[]
}
// end::endclass[]
Loading

0 comments on commit 3bb6009

Please sign in to comment.