Skip to content

Commit

Permalink
[LEIP-202] Android Upload Does Not Work (#173)
Browse files Browse the repository at this point in the history
Task/#leip 202 android upload does not work
  • Loading branch information
muthenberg authored May 3, 2024
2 parents 8cf51f3 + 739b599 commit af59db6
Show file tree
Hide file tree
Showing 26 changed files with 666 additions and 394 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/gradle_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ jobs:
echo "Try to reach Keycloak at http://localhost:8081/ ..."
curl -s http://localhost:8081/ | grep -q "Keycloak" #; echo $?
# Grep exits with exit code 0 when the API is started and fails with exit code 1 otherwise
echo "Try to reach the Collector API at http://localhost:8080/api/v4/ ..."
curl -s http://localhost:8080/api/v4/ | grep -q "Cyface Data Collector" #; echo $?
echo "Try to reach the Collector API at http://localhost:8080/ ..."
curl -s http://localhost:8080/ | grep -q "Cyface Data Collector" #; echo $?
- name: Debugging output (if last step failed)
if: failure()
Expand Down Expand Up @@ -128,5 +128,5 @@ jobs:
echo
[ -f build/docker/logs/collector.log ] && echo "----- collector.log -----" && cat build/docker/logs/collector.log
[ -f build/docker/logs/collector-out.log ] && echo "----- collector-out.log -----" && cat build/docker/logs/collector-out.log
echo && echo "----- curl ***/api/v4"
curl http://localhost:8080/api/v4/
echo && echo "----- curl ***/"
curl http://localhost:8080/
8 changes: 4 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import java.net.URL
*
* @author Klemens Muthmann
* @author Armin Schnabel
* @version 2.0.2
* @version 2.0.3
* @since 1.0.0
*/
buildscript {
Expand Down Expand Up @@ -87,12 +87,12 @@ kotlin {
jvmToolchain(17)
}

val vertxVersion = "4.5.0"
val vertxVersion = "4.5.7"
val micrometerVersion = "1.10.6"
val commonsLangVersion = "3.12.0"
val logbackVersion = "1.4.6"
val logbackVersion = "1.4.14"
val gradleWrapperVersion = "7.6.1"
val googleCloudLibrariesVersion = "26.29.0"
val googleCloudLibrariesVersion = "26.35.0"

// Versions of testing dependencies
val junitVersion = "5.9.2"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023 Cyface GmbH
* Copyright 2023-2024 Cyface GmbH
*
* This file is part of the Cyface Data Collector.
*
Expand All @@ -19,13 +19,14 @@
package de.cyface.collector.auth

import io.vertx.core.Future
import io.vertx.ext.web.Router
import io.vertx.ext.web.handler.OAuth2AuthHandler

/**
* Interface for the builder which creates an [OAuth2AuthHandler] to allow mocking.
*
* @author Armin Schnabel
* @version 1.0.0
* @version 2.0.0
* @since 7.0.0
*/
interface AuthHandlerBuilder {
Expand All @@ -34,5 +35,5 @@ interface AuthHandlerBuilder {
* Start the creation process of a [AuthHandlerBuilder] and provide a [Future], that will be notified about
* successful or failed completion.
*/
fun create(): Future<OAuth2AuthHandler>
fun create(apiRouter: Router): Future<OAuth2AuthHandler>
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023 Cyface GmbH
* Copyright 2023-2024 Cyface GmbH
*
* This file is part of the Cyface Data Collector.
*
Expand All @@ -22,6 +22,7 @@ import io.vertx.core.Future
import io.vertx.core.json.JsonObject
import io.vertx.ext.auth.impl.UserImpl
import io.vertx.ext.web.Route
import io.vertx.ext.web.Router
import io.vertx.ext.web.RoutingContext
import io.vertx.ext.web.handler.OAuth2AuthHandler
import java.util.UUID
Expand All @@ -30,12 +31,12 @@ import java.util.UUID
* Mocked OAuth2 builder which creates an OAuth2 handler for testing.
*
* @author Armin Schnabel
* @version 1.0.0
* @version 2.0.0
* @since 7.0.0
*/
class MockedHandlerBuilder : AuthHandlerBuilder {

override fun create(): Future<OAuth2AuthHandler> {
override fun create(apiRouter: Router): Future<OAuth2AuthHandler> {
val handler: OAuth2AuthHandler = object : OAuth2AuthHandler {
override fun handle(event: RoutingContext) {
val principal = JsonObject()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023 Cyface GmbH
* Copyright 2023-2024 Cyface GmbH
*
* This file is part of the Cyface Data Collector.
*
Expand Down Expand Up @@ -31,21 +31,19 @@ import java.net.URL
* Keycloak OAuth2 builder which creates an OAuth2 handler.
*
* @author Armin Schnabel
* @version 1.0.1
* @version 2.0.0
* @since 7.0.0
* @property vertx
* @property apiRouter
* @property callbackUrl The callback URL you entered in your provider admin console.
* @property options the oauth configuration.
*/
class OAuth2HandlerBuilder(
private val vertx: Vertx,
private val apiRouter: Router,
private val callbackUrl: URL,
private val options: OAuth2Options,
) : AuthHandlerBuilder {

override fun create(): Future<OAuth2AuthHandler> {
override fun create(apiRouter: Router): Future<OAuth2AuthHandler> {
val promise = Promise.promise<OAuth2AuthHandler>()

KeycloakAuth.discover(vertx, options)
Expand Down
82 changes: 37 additions & 45 deletions src/main/kotlin/de/cyface/collector/configuration/Configuration.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022-2023 Cyface GmbH
* Copyright 2022-2024 Cyface GmbH
*
* This file is part of the Cyface Data Collector.
*
Expand Down Expand Up @@ -29,32 +29,27 @@ import java.nio.file.Path
*
* @author Klemens Muthmann
* @author Armin Schnabel
* @version 3.0.0
* @property serviceHttpAddress The [URL] hosting the collector service.
* @version 4.0.0
* @since 1.0.0
* @property httpHost The host name of the server serving this collector service.
* @property httpPort The Port providing this collector service.
* @property mongoDb The Mongo database configuration as described in the Vert.x documentation.
* @property uploadExpiration The time an upload session stays valid to be resumed in the future, in milliseconds.
* @property measurementPayloadLimit The maximum size in bytes accepted for a single measurement.
* @property metricsEnabled `true` if prometheus metrics should be collected; `false` otherwise.
* @property storageType The type of storage to use for storing the binary data blobs.
* @property oauthCallback The callback URL you entered in your provider admin console.
* @property oauthClient The name of the oauth client to contact.
* @property oauthSecret The secret of the oauth client to contact.
* @property oauthSite The Root URL for the provider without trailing slashes.
* @property oauthTenant The name of the oauth realm to contact.
* @property oauthConfig The configuration for the OAuth authentication.
*/
data class Configuration(
val serviceHttpAddress: URL,
val httpHost: String,
val httpPort: Int,
val mongoDb: JsonObject,
val uploadExpiration: Long,
val measurementPayloadLimit: Long,
val metricsEnabled: Boolean,
val storageType: StorageType,
val authType: AuthType,
val oauthCallback: URL,
val oauthClient: String,
val oauthSecret: String,
val oauthSite: URL,
val oauthTenant: String
val oauthConfig: OAuthConfig
) {
companion object {
/**
Expand All @@ -64,8 +59,6 @@ data class Configuration(
try {
val httpHost = Validate.notEmpty(json.get<String>("http.host"))
val httpPort = json.get<Int>("http.port")
val httpEndpoint = vertxEndpoint(json["http.endpoint"])
val serviceHttpAddress = URL("https", httpHost, httpPort, httpEndpoint)
val mongoDb = json.get<JsonObject>("mongo.db")
val uploadExpiration = json.get<Long>("upload.expiration")
val measurementPayloadLimit = json.get<Long>("measurement.payload.limit")
Expand All @@ -80,18 +73,21 @@ data class Configuration(
val oauthTenant = json.get<String>("oauth.tenant")

return Configuration(
serviceHttpAddress,
httpHost,
httpPort,
mongoDb,
uploadExpiration,
measurementPayloadLimit,
metricsEnabled,
storageType,
authType,
oauthCallback,
oauthClient,
oauthSecret,
oauthSite,
oauthTenant
OAuthConfig(
oauthCallback,
oauthClient,
oauthSecret,
oauthSite,
oauthTenant
)
)
} catch (@Suppress("TooGenericExceptionCaught") e: NullPointerException) {
throw InvalidConfig("Some parameters are missing. Refer to the documentation or an example file.", e)
Expand Down Expand Up @@ -129,28 +125,24 @@ data class Configuration(
else -> throw InvalidConfig("Invalid storage type $storageTypeString!")
}
}

/**
* Provide the HTTP endpoint configured for use with a Vertx router.
*
* This means a '*' is appended as the last path element to match all
* requests.
*
* @param endpoint The original endpoint provided by the configuration.
*/
private fun vertxEndpoint(endpoint: String): String {
Validate.notEmpty(
endpoint,
"Endpoint not found. Please use the parameter $endpoint."
)
val builder = StringBuilder(endpoint)
val lastChar = endpoint.last()
if (lastChar == '/') {
builder.append("*")
} else if (lastChar != '*') {
builder.append("/*")
}
return builder.toString()
}
}

/**
* Wrapper class for all the parameters relevant to initialize OAuth.
*
* @author Klemens Muthmann
* @version 1.0.0
* @property callback The callback URL you entered in your provider admin console.
* @property client The name of the oauth client to contact.
* @property secret The secret of the oauth client to contact.
* @property site The Root URL for the provider without trailing slashes.
* @property tenant The name of the oauth realm to contact.
*/
data class OAuthConfig(
val callback: URL,
val client: String,
val secret: String,
val site: URL,
val tenant: String
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import java.util.UUID
*
* @author Armin Schnabel
* @author Klemens Muthmann
* @version 1.0.2
* @version 1.0.3
* @since 6.0.0
*/
class MeasurementHandler(
Expand Down Expand Up @@ -148,16 +148,13 @@ class MeasurementHandler(
context.response().putHeader("Content-Length", "0")
context.response().setStatusCode(RESUME_INCOMPLETE).end()
}

StatusType.COMPLETE -> {
// In case the response does not arrive at the client, the client will receive a 409 on a reupload.
// In case of the 409, the client can handle the successful upload. Therefore, the session is removed
// here to avoid dangling session references.
session.remove<Any>(UPLOAD_PATH_FIELD)
storageService.clean(status.uploadIdentifier).onSuccess {
context.response().setStatusCode(HTTPStatus.CREATED).end()
}.onFailure { cause ->
context.fail(cause)
}
context.response().setStatusCode(HTTPStatus.CREATED).end()
}
}
}
Expand Down Expand Up @@ -223,6 +220,7 @@ class MeasurementHandler(
ret.complete(Status(uploadIdentifier, StatusType.INCOMPLETE, byteSize))
} else {
val uploadMetaData = UploadMetaData(user, contentRange, uploadIdentifier, metaData)
LOGGER.debug("Storing $byteSize bytes to storage service.")
val acceptUploadResult = storageService.store(pipe, uploadMetaData)
acceptUploadResult.onSuccess { result -> ret.complete(result) }
acceptUploadResult.onFailure { cause -> ret.fail(cause) }
Expand Down
Loading

0 comments on commit af59db6

Please sign in to comment.