mirror of
https://github.com/ethauvin/kobalt.git
synced 2025-04-26 08:27:12 -07:00
Progress bars for uploads.
This commit is contained in:
parent
3a9ca294e4
commit
c986fc6d65
4 changed files with 129 additions and 84 deletions
|
@ -1,8 +1,9 @@
|
|||
package com.beust.kobalt.maven
|
||||
|
||||
import com.beust.kobalt.misc.CountingFileRequestBody
|
||||
import com.beust.kobalt.misc.log
|
||||
import com.squareup.okhttp.*
|
||||
import java.io.File
|
||||
import retrofit.mime.TypedFile
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import javax.inject.Singleton
|
||||
|
@ -47,15 +48,34 @@ public class Http {
|
|||
return result
|
||||
}
|
||||
|
||||
public fun uploadFile(user: String?, password: String?, url: String, file: File,
|
||||
success: (Response) -> Unit,
|
||||
error: (Response) -> Unit) {
|
||||
fun percentProgressCallback(totalSize: Long) : (Long) -> Unit {
|
||||
return { num: Long ->
|
||||
val progress = num * 100 / totalSize
|
||||
log(1, "\rUploaded: $progress%", newLine = false)
|
||||
}
|
||||
}
|
||||
|
||||
val DEFAULT_ERROR_RESPONSE = { r: Response ->
|
||||
error("Couldn't upload file: " + r.message())
|
||||
}
|
||||
|
||||
public fun uploadFile(user: String? = null, password: String? = null, url: String, file: TypedFile,
|
||||
progressCallback: (Long) -> Unit = {},
|
||||
headers: Headers = Headers.of(),
|
||||
success: (Response) -> Unit = {},
|
||||
error: (Response) -> Unit = DEFAULT_ERROR_RESPONSE) {
|
||||
|
||||
val fullHeaders = Headers.Builder()
|
||||
fullHeaders.set("Content-Type", file.mimeType())
|
||||
headers.names().forEach { fullHeaders.set(it, headers.get(it)) }
|
||||
|
||||
val request = builder(user, password)
|
||||
.headers(fullHeaders.build())
|
||||
.url(url)
|
||||
.put(RequestBody.create(MEDIA_TYPE_BINARY, file))
|
||||
.post(CountingFileRequestBody(file.file(), file.mimeType(), progressCallback))
|
||||
.build()
|
||||
|
||||
log(2, "Uploading $file to $url")
|
||||
log(1, "Uploading $file to $url")
|
||||
val response = OkHttpClient().newCall(request).execute()
|
||||
if (! response.isSuccessful) {
|
||||
error(response)
|
||||
|
@ -63,17 +83,6 @@ public class Http {
|
|||
success(response)
|
||||
}
|
||||
}
|
||||
|
||||
// private val JSON = MediaType.parse("application/json; charset=utf-8")
|
||||
//
|
||||
// fun post(user: String?, password: String?, url: String, payload: String) : String {
|
||||
// val request = builder(user, password)
|
||||
// .url(url)
|
||||
// .post(RequestBody.create(JSON, payload))
|
||||
// .build()
|
||||
// val response = OkHttpClient().newCall(request).execute()
|
||||
// return response.body().string()
|
||||
// }
|
||||
}
|
||||
|
||||
class KobaltException(s: String? = null, ex: Throwable? = null) : RuntimeException(s, ex) {
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package com.beust.kobalt.misc
|
||||
|
||||
import com.squareup.okhttp.MediaType
|
||||
import com.squareup.okhttp.RequestBody
|
||||
import okio.BufferedSink
|
||||
import okio.Okio
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* An OkHttp RequestBody subclass that counts the outgoing bytes and offers progress callbacks.
|
||||
*/
|
||||
class CountingFileRequestBody(val file: File, val contentType: String,
|
||||
val listenerCallback: (Long) -> Unit) : RequestBody() {
|
||||
|
||||
val SEGMENT_SIZE = 4096L
|
||||
|
||||
override fun contentLength() = file.length()
|
||||
|
||||
override fun contentType() = MediaType.parse(contentType)
|
||||
|
||||
override fun writeTo(sink: BufferedSink) {
|
||||
Okio.source(file).use { source ->
|
||||
var total = 0L
|
||||
var read: Long = source.read(sink.buffer(), SEGMENT_SIZE)
|
||||
|
||||
while (read != -1L) {
|
||||
total += read
|
||||
sink.flush();
|
||||
listenerCallback(total)
|
||||
read = source.read(sink.buffer(), SEGMENT_SIZE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// companion object {
|
||||
// private val MEDIA_TYPE_BINARY = MediaType.parse("application/octet-stream")
|
||||
//
|
||||
// fun progressUpload(file: File, url: String) {
|
||||
// val totalSize = file.length()
|
||||
//
|
||||
// val progressListener = object : ProgressListener {
|
||||
// override fun transferred(num: Long) {
|
||||
// val progress: Float = (num.toFloat() * 100) / totalSize
|
||||
// print("\rProgress: $progress")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// val request = Request.Builder()
|
||||
// .url(url)
|
||||
// // .post(RequestBody.create(MEDIA_TYPE_BINARY, file))
|
||||
// .put(CountingFileRequestBody(file, "application/octet-stream", progressListener))
|
||||
// // .post(requestBody)
|
||||
// .build();
|
||||
//
|
||||
// val response = OkHttpClient().newCall(request).execute()
|
||||
// if (! response.isSuccessful) {
|
||||
// println("ERROR")
|
||||
// } else {
|
||||
// println("SUCCESS")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
|
@ -1,18 +1,21 @@
|
|||
package com.beust.kobalt.misc
|
||||
|
||||
import com.beust.kobalt.maven.Http
|
||||
import com.beust.kobalt.maven.KobaltException
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.squareup.okhttp.Headers
|
||||
import com.squareup.okhttp.OkHttpClient
|
||||
import retrofit.RestAdapter
|
||||
import retrofit.RetrofitError
|
||||
import retrofit.client.OkClient
|
||||
import retrofit.client.Response
|
||||
import retrofit.http.*
|
||||
import retrofit.mime.MimeUtil
|
||||
import retrofit.http.Body
|
||||
import retrofit.http.POST
|
||||
import retrofit.http.Path
|
||||
import retrofit.http.Query
|
||||
import retrofit.mime.TypedByteArray
|
||||
import retrofit.mime.TypedFile
|
||||
import rx.Observable
|
||||
|
@ -29,7 +32,7 @@ import javax.inject.Inject
|
|||
* Retrieve Kobalt's latest release version from github.
|
||||
*/
|
||||
public class GithubApi @Inject constructor(val executors: KobaltExecutors,
|
||||
val localProperties: LocalProperties) {
|
||||
val localProperties: LocalProperties, val http: Http) {
|
||||
companion object {
|
||||
const val RELEASES_URL = "https://api.github.com/repos/cbeust/kobalt/releases"
|
||||
const val PROPERTY_ACCESS_TOKEN = "github.accessToken"
|
||||
|
@ -41,24 +44,24 @@ public class GithubApi @Inject constructor(val executors: KobaltExecutors,
|
|||
|
||||
private fun parseRetrofitError(e: Throwable) : RetrofitErrorsResponse {
|
||||
val re = e as RetrofitError
|
||||
val body = e.body
|
||||
val json = String((re.response.body as TypedByteArray).bytes)
|
||||
return Gson().fromJson(json, RetrofitErrorsResponse::class.java)
|
||||
}
|
||||
|
||||
fun uploadRelease(packageName: String, tagName: String, zipFile: File) {
|
||||
log(1, "Uploading release ${zipFile.name}")
|
||||
|
||||
val username = localProperties.get(PROPERTY_USERNAME)
|
||||
val accessToken = localProperties.get(PROPERTY_ACCESS_TOKEN)
|
||||
try {
|
||||
service.createRelease(username, accessToken, packageName, CreateRelease(tagName))
|
||||
.flatMap { response ->
|
||||
uploadService.uploadAsset(username, accessToken,
|
||||
packageName, response.id!!, zipFile.name, TypedFile("application/zip", zipFile))
|
||||
uploadAsset(accessToken, response.uploadUrl!!, TypedFile("application/zip", zipFile),
|
||||
tagName)
|
||||
}
|
||||
.toBlocking()
|
||||
.forEach { action ->
|
||||
log(1, "Release successfully uploaded ${zipFile.name}")
|
||||
log(1, "\nRelease successfully uploaded ${zipFile.name}")
|
||||
}
|
||||
} catch(e: RetrofitError) {
|
||||
val error = parseRetrofitError(e)
|
||||
|
@ -67,6 +70,18 @@ public class GithubApi @Inject constructor(val executors: KobaltExecutors,
|
|||
}
|
||||
}
|
||||
|
||||
private fun uploadAsset(token: String, uploadUrl: String, typedFile: TypedFile, tagName: String)
|
||||
: Observable<UploadAssetResponse> {
|
||||
val strippedUrl = uploadUrl.substring(0, uploadUrl.indexOf("{"))
|
||||
val url = "$strippedUrl?name=$tagName&label=$tagName"
|
||||
val headers = Headers.of("Authorization", "token $token")
|
||||
val totalSize = typedFile.file().length()
|
||||
http.uploadFile(url = url, file = typedFile, headers = headers,
|
||||
progressCallback = http.percentProgressCallback(totalSize))
|
||||
|
||||
return Observable.just(UploadAssetResponse(tagName, tagName))
|
||||
}
|
||||
|
||||
//
|
||||
// Read only Api
|
||||
//
|
||||
|
@ -78,57 +93,20 @@ public class GithubApi @Inject constructor(val executors: KobaltExecutors,
|
|||
.build()
|
||||
.create(Api::class.java)
|
||||
|
||||
class Release {
|
||||
var name: String? = null
|
||||
var prerelease: Boolean? = null
|
||||
}
|
||||
|
||||
//
|
||||
// JSON mapped classes that get sent up and down
|
||||
//
|
||||
class CreateRelease(@SerializedName("tag_name") var tagName: String? = null,
|
||||
var name: String? = tagName)
|
||||
class CreateReleaseResponse(var id: String? = null)
|
||||
class GetReleaseResponse(var id: String? = null,
|
||||
@SerializedName("upload_url") var uploadUrl: String? = null)
|
||||
class CreateReleaseResponse(var id: String? = null, @SerializedName("upload_url") var uploadUrl: String?)
|
||||
class UploadAssetResponse(var id: String? = null, val name: String? = null)
|
||||
|
||||
interface Api {
|
||||
|
||||
@GET("/repos/{owner}/{repo}/releases/tags/{tag}")
|
||||
fun getReleaseByTagName(@Path("owner") owner: String, @Path("repo") repo: String,
|
||||
@Path("tag") tagName: String): GetReleaseResponse
|
||||
|
||||
@GET("/repos/{owner}/{repo}/releases")
|
||||
fun releases(@Path("owner") owner: String, @Path("repo") repo: String): List<Release>
|
||||
|
||||
@POST("/repos/{owner}/{repo}/releases")
|
||||
fun createRelease(@Path("owner") owner: String,
|
||||
@Query("access_token") accessToken: String,
|
||||
@Path("repo") repo: String,
|
||||
@Body createRelease: CreateRelease
|
||||
): Observable<CreateReleaseResponse>
|
||||
}
|
||||
|
||||
//
|
||||
// Upload Api
|
||||
//
|
||||
|
||||
val uploadService = RestAdapter.Builder()
|
||||
.setEndpoint("https://uploads.github.com/")
|
||||
// .setLogLevel(RestAdapter.LogLevel.FULL)
|
||||
.setClient(OkClient(OkHttpClient()))
|
||||
.build()
|
||||
.create(UploadApi::class.java)
|
||||
|
||||
class UploadReleaseResponse(var id: String? = null, val name: String? = null)
|
||||
|
||||
interface UploadApi {
|
||||
@POST("/repos/{owner}/{repo}/releases/{id}/assets")
|
||||
fun uploadAsset(@Path("owner") owner: String,
|
||||
@Query("access_token") accessToken: String,
|
||||
@Path("repo") repo: String,
|
||||
@Path("id") id: String,
|
||||
@Query("name") name: String,
|
||||
@Body file: TypedFile)
|
||||
// @Query("Content-Type") contentType: String = "text/plain")//"application/zip")
|
||||
: Observable<UploadReleaseResponse>
|
||||
@Body createRelease: CreateRelease): Observable<CreateReleaseResponse>
|
||||
}
|
||||
|
||||
val latestKobaltVersion: Future<String>
|
||||
|
@ -140,7 +118,6 @@ public class GithubApi @Inject constructor(val executors: KobaltExecutors,
|
|||
@Suppress("UNCHECKED_CAST")
|
||||
val reader = BufferedReader(InputStreamReader(ins))
|
||||
val jo = JsonParser().parse(reader) as JsonArray
|
||||
// val jo = Parser().parse(ins) as JsonArray<JsonObject>
|
||||
if (jo.size() > 0) {
|
||||
var versionName = (jo.get(0) as JsonObject).get("name").asString
|
||||
if (Strings.isEmpty(versionName)) {
|
||||
|
@ -158,12 +135,3 @@ public class GithubApi @Inject constructor(val executors: KobaltExecutors,
|
|||
return executors.miscExecutor.submit(callable)
|
||||
}
|
||||
}
|
||||
|
||||
fun Response.bodyContent() : String {
|
||||
val bodyBytes = (body as TypedByteArray).bytes
|
||||
val bodyMime = body.mimeType()
|
||||
val bodyCharset = MimeUtil.parseCharset(bodyMime, "utf-8")
|
||||
val result = String(bodyBytes, bodyCharset)
|
||||
return result
|
||||
// return new Gson().fromJson(data, type);
|
||||
}
|
||||
|
|
|
@ -10,11 +10,13 @@ import com.beust.kobalt.misc.KobaltExecutors
|
|||
import com.beust.kobalt.misc.error
|
||||
import com.beust.kobalt.misc.log
|
||||
import com.beust.kobalt.misc.warn
|
||||
import com.google.common.net.MediaType
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import com.google.inject.assistedinject.Assisted
|
||||
import com.squareup.okhttp.Response
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import retrofit.mime.TypedFile
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -147,9 +149,10 @@ public class JCenterApi @Inject constructor (@Nullable @Assisted("username") val
|
|||
|
||||
val results = arrayListOf<Boolean>()
|
||||
filesToUpload.forEach { file ->
|
||||
http.uploadFile(username, password, fileToPath(file) + optionPath, file,
|
||||
{ r: Response -> results.add(true)},
|
||||
{ r: Response ->
|
||||
http.uploadFile(username, password, fileToPath(file) + optionPath,
|
||||
TypedFile(MediaType.ANY_APPLICATION_TYPE.toString(), file),
|
||||
success = { r: Response -> results.add(true) },
|
||||
error = { r: Response ->
|
||||
results.add(false)
|
||||
val jo = parseResponse(r.body().string())
|
||||
errorMessages.add(jo.get("message").asString ?: "No message found")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue