Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into mc_presenceEE_into_…
Browse files Browse the repository at this point in the history
…subscribe_based_on_branch_mc_PresenceEE_effects
  • Loading branch information
kleewho committed Aug 23, 2023
2 parents dd8786e + 24571aa commit 12966fa
Show file tree
Hide file tree
Showing 28 changed files with 404 additions and 140 deletions.
13 changes: 9 additions & 4 deletions .pubnub.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: kotlin
version: 7.5.0
version: 7.6.0
schema: 1
scm: github.com/pubnub/kotlin
files:
- build/libs/pubnub-kotlin-7.5.0-all.jar
- build/libs/pubnub-kotlin-7.6.0-all.jar
sdks:
-
type: library
Expand All @@ -23,8 +23,8 @@ sdks:
-
distribution-type: library
distribution-repository: maven
package-name: pubnub-kotlin-7.5.0
location: https://repo.maven.apache.org/maven2/com/pubnub/pubnub-kotlin/7.5.0/pubnub-kotlin-7.5.0.jar
package-name: pubnub-kotlin-7.6.0
location: https://repo.maven.apache.org/maven2/com/pubnub/pubnub-kotlin/7.6.0/pubnub-kotlin-7.6.0.jar
supported-platforms:
supported-operating-systems:
Android:
Expand Down Expand Up @@ -121,6 +121,11 @@ sdks:
license-url: https://github.com/stleary/JSON-java/blob/20210307/LICENSE
is-required: Required
changelog:
- date: 2023-08-14
version: v7.6.0
changes:
- type: feature
text: "Kotlin SDK doesn't depend on Jackson library anymore. This reduces the size of required dependencies which should result in size reduction of applications that use Kotlin SDK."
- date: 2023-04-19
version: v7.5.0
changes:
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## v7.6.0
August 14 2023

#### Added
- Kotlin SDK doesn't depend on Jackson library anymore. This reduces the size of required dependencies which should result in size reduction of applications that use Kotlin SDK.

## v7.5.0
April 19 2023

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ You will need the publish and subscribe keys to authenticate your app. Get your
<dependency>
<groupId>com.pubnub</groupId>
<artifactId>pubnub-kotlin</artifactId>
<version>7.5.0</version>
<version>7.6.0</version>
</dependency>
```

* for Gradle, add the following dependency in your `gradle.build`:
```groovy
implementation 'com.pubnub:pubnub-kotlin:7.5.0'
implementation 'com.pubnub:pubnub-kotlin:7.6.0'
```

2. Configure your keys:
Expand Down
5 changes: 2 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,12 @@ dependencies {
implementation "com.squareup.retrofit2:converter-gson:2.9.0"

implementation 'org.json:json:20230227'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.14.2'
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.14.2'

implementation "org.slf4j:slf4j-api:1.7.30"

api 'com.squareup.okhttp3:logging-interceptor:4.9.3'
api 'com.google.code.gson:gson:2.9.0'
implementation 'co.nstant.in:cbor:0.9'

allTest "com.github.tomakehurst:wiremock:2.27.2"

Expand All @@ -84,6 +82,7 @@ dependencies {
testImplementation group: 'io.cucumber', name: 'cucumber-java', version: '6.10.4'
testImplementation group: 'io.cucumber', name: 'cucumber-junit', version: '6.10.4'
testImplementation group: 'io.cucumber', name: 'cucumber-picocontainer', version: '6.10.4'
testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2'
integrationTestImplementation(testFixtures(project(":")))

allTest "org.awaitility:awaitility-kotlin:4.0.3"
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ SONATYPE_HOST=DEFAULT
SONATYPE_AUTOMATIC_RELEASE=true
GROUP=com.pubnub
POM_ARTIFACT_ID=pubnub-kotlin
VERSION_NAME=7.5.0
VERSION_NAME=7.6.0
POM_PACKAGING=jar

POM_NAME=PubNub Android Chat Components
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/pubnub/api/PubNub.kt
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class PubNub internal constructor(

companion object {
private const val TIMESTAMP_DIVIDER = 1000
private const val SDK_VERSION = "7.5.0"
private const val SDK_VERSION = "7.6.0"
private const val MAX_SEQUENCE = 65535

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ open class PublishFileMessage(

@Throws(PubNubException::class)
override fun doWork(queryParams: HashMap<String, String>): Call<List<Any>> {
val stringifiedMessage: String = pubnub.mapper.toJsonUsingJackson(FileUploadNotification(message, pnFile))
val stringifiedMessage: String = pubnub.mapper.toJson(FileUploadNotification(message, pnFile))
val messageAsString = if (pubnub.configuration.cipherKey.isValid()) {
val crypto = Crypto(pubnub.configuration.cipherKey, pubnub.configuration.useRandomInitializationVector)
crypto.encrypt(stringifiedMessage).quoted()
} else {
stringifiedMessage
}
meta?.let {
val stringifiedMeta: String = pubnub.mapper.toJsonUsingJackson(it)
val stringifiedMeta: String = pubnub.mapper.toJson(it)
queryParams["meta"] = stringifiedMeta
}
shouldStore?.numericString?.let { queryParams["store"] = it }
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/pubnub/api/enums/PNStatusCategory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ enum class PNStatusCategory {
PNDisconnectedCategory,

/**
* Previously started subscribe loop did fail and at this moment client disconnected from real-time data channels.
* Previously started subscribe loop failed, and at this moment client disconnected from real-time data channels.
*/
PNConnectionError,
}
12 changes: 0 additions & 12 deletions src/main/kotlin/com/pubnub/api/managers/MapperManager.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.pubnub.api.managers

import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonArray
Expand Down Expand Up @@ -29,7 +27,6 @@ class MapperManager {

private val objectMapper: Gson
internal val converterFactory: Converter.Factory
private val jacksonObjectMapper = jacksonObjectMapper()

init {
val booleanAsIntAdapter = object : TypeAdapter<Boolean>() {
Expand Down Expand Up @@ -63,15 +60,6 @@ class MapperManager {
converterFactory = GsonConverterFactory.create(objectMapper)
}

@Throws(PubNubException::class)
fun toJsonUsingJackson(input: Any): String {
return try {
this.jacksonObjectMapper.writeValueAsString(input)
} catch (e: JsonProcessingException) {
throw PubNubException(PubNubError.JSON_ERROR).copy(errorMessage = e.message ?: PubNubError.JSON_ERROR.message)
}
}

fun hasField(element: JsonElement, field: String) = element.asJsonObject.has(field)

fun getField(element: JsonElement?, field: String): JsonElement? {
Expand Down
120 changes: 106 additions & 14 deletions src/main/kotlin/com/pubnub/api/managers/TokenParser.kt
Original file line number Diff line number Diff line change
@@ -1,29 +1,121 @@
package com.pubnub.api.managers

import com.fasterxml.jackson.annotation.JsonSetter
import com.fasterxml.jackson.annotation.Nulls
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.cbor.CBORFactory
import co.nstant.`in`.cbor.CborDecoder
import co.nstant.`in`.cbor.model.ByteString
import co.nstant.`in`.cbor.model.NegativeInteger
import co.nstant.`in`.cbor.model.UnsignedInteger
import com.pubnub.api.PubNubError
import com.pubnub.api.PubNubException
import com.pubnub.api.models.consumer.access_manager.v3.PNToken
import com.pubnub.api.vendor.Base64
import java.io.IOException
import java.math.BigInteger
import java.nio.charset.StandardCharsets
import kotlin.collections.Map
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set
import co.nstant.`in`.cbor.model.Map as CborMap

internal class TokenParser {
private val mapper = ObjectMapper(CBORFactory()).apply {
configOverride(Map::class.java).setterInfo = JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY)
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
}

fun unwrapToken(token: String): PNToken {
val byteArray = Base64.decode(token.toByteArray(StandardCharsets.UTF_8), Base64.URL_SAFE)
val firstElement = CborDecoder(byteArray.inputStream()).decode().firstOrNull() ?: throw PubNubException(
pubnubError = PubNubError.INVALID_ACCESS_TOKEN, errorMessage = "Empty token"
)
val firstLevelMap = (firstElement as? CborMap)?.toJvmMap() ?: throw PubNubException(
pubnubError = PubNubError.INVALID_ACCESS_TOKEN, errorMessage = "First element is not a map"
)
val version = firstLevelMap[VERSION_KEY]?.toString()?.toInt() ?: throw PubNubException(
pubnubError = PubNubError.INVALID_ACCESS_TOKEN, errorMessage = "Couldn't parse version"
)
val timestamp = firstLevelMap[TIMESTAMP_KEY]?.toString()?.toLong() ?: throw PubNubException(
pubnubError = PubNubError.INVALID_ACCESS_TOKEN, errorMessage = "Couldn't parse timestamp"
)
val ttl = firstLevelMap[TTL_KEY]?.toString()?.toLong() ?: throw PubNubException(
pubnubError = PubNubError.INVALID_ACCESS_TOKEN, errorMessage = "Couldn't parse ttl"
)
val resourcesValue = firstLevelMap[RESOURCES_KEY] as? Map<*, *> ?: throw PubNubException(
pubnubError = PubNubError.INVALID_ACCESS_TOKEN, errorMessage = "Resources are not present or are not map"
)
val patternsValue = firstLevelMap[PATTERNS_KEY] as? Map<*, *> ?: throw PubNubException(
pubnubError = PubNubError.INVALID_ACCESS_TOKEN, errorMessage = "Patterns are not present or are not map"
)

return try {
val byteArray = Base64.decode(token.toByteArray(StandardCharsets.UTF_8), Base64.URL_SAFE)
mapper.readValue(byteArray, PNToken::class.java)
} catch (e: IOException) {
throw PubNubException(pubnubError = PubNubError.INVALID_ACCESS_TOKEN, errorMessage = e.message)

PNToken(
version = version,
timestamp = timestamp,
ttl = ttl,
authorizedUUID = firstLevelMap[AUTHORIZED_UUID_KEY]?.toString(),
resources = resourcesValue.toPNTokenResources(),
patterns = patternsValue.toPNTokenResources(),
meta = firstLevelMap[META_KEY]
)
} catch (e: Exception) {
if (e is PubNubException) throw e
throw PubNubException(
pubnubError = PubNubError.INVALID_ACCESS_TOKEN, errorMessage = "Couldn't parse token: ${e.message}"
)
}
}

private fun CborMap.toJvmMap(depth: Int = 0): MutableMap<String, Any> {
if (depth > 3) {
throw PubNubException(pubnubError = PubNubError.INVALID_ACCESS_TOKEN, errorMessage = "Token is too deep")
}
val result = mutableMapOf<String, Any>()
for (key in this.keys) {
val value = this.get(key)
val keyString = when (key) {
is ByteString -> key.bytes.toString(StandardCharsets.UTF_8)
else -> key.toString()
}

when (value) {
is CborMap -> result[keyString] = value.toJvmMap(depth + 1)
is ByteString -> result[keyString] = value.bytes
is List<*> -> result[keyString] = value.map { it.toString() }
is UnsignedInteger -> result[keyString] = value.value
is NegativeInteger -> result[keyString] = value.value
else -> result[keyString] = value.toString()
}
}
return result
}

private fun Map<*, *>.toMapOfStringToInt(): Map<String, Int> {
return mapNotNull { (k, v) ->
when (v) {
is BigInteger -> k.toString() to v.toInt()
else -> v.toString().toIntOrNull()?.let { k.toString() to it }
}
}.toMap()
}

private fun Map<*, *>.toPNTokenResources(): PNToken.PNTokenResources {
val channels = (this[CHANNELS_KEY] as? Map<*, *>)?.toMapOfStringToInt() ?: emptyMap()
val groups = (this[GROUPS_KEY] as? Map<*, *>)?.toMapOfStringToInt() ?: emptyMap()
val uuids = (this[UUIDS_KEY] as? Map<*, *>)?.toMapOfStringToInt() ?: emptyMap()

return PNToken.PNTokenResources(
channels = channels.mapValues { (_, v) -> PNToken.PNResourcePermissions(v) },
channelGroups = groups.mapValues { (_, v) -> PNToken.PNResourcePermissions(v) },
uuids = uuids.mapValues { (_, v) -> PNToken.PNResourcePermissions(v) }
)
}

companion object {
private const val VERSION_KEY = "v"
private const val TIMESTAMP_KEY = "t"
private const val TTL_KEY = "ttl"
private const val AUTHORIZED_UUID_KEY = "uuid"
private const val RESOURCES_KEY = "res"
private const val PATTERNS_KEY = "pat"
private const val META_KEY = "meta"
private const val CHANNELS_KEY = "chan"
private const val GROUPS_KEY = "grp"
private const val UUIDS_KEY = "uuid"
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
package com.pubnub.api.models.consumer.access_manager.v3

import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import com.pubnub.api.models.TokenBitmask

data class PNToken(
@JsonProperty("v") val version: Int = 0,
@JsonProperty("t") val timestamp: Long = 0,
@JsonProperty("ttl") val ttl: Long = 0,
@JsonProperty("uuid") val authorizedUUID: String? = null,
@JsonProperty("res") val resources: PNTokenResources,
@JsonProperty("pat") val patterns: PNTokenResources,
@JsonProperty("meta") val meta: Any? = null
val version: Int = 0,
val timestamp: Long = 0,
val ttl: Long = 0,
val authorizedUUID: String? = null,
val resources: PNTokenResources,
val patterns: PNTokenResources,
val meta: Any? = null
) {

data class PNTokenResources(
@JsonProperty("chan") val channels: Map<String, PNResourcePermissions> = emptyMap(),
@JsonProperty("grp") val channelGroups: Map<String, PNResourcePermissions> = emptyMap(),
@JsonProperty("uuid") val uuids: Map<String, PNResourcePermissions> = emptyMap()
val channels: Map<String, PNResourcePermissions> = emptyMap(),
val channelGroups: Map<String, PNResourcePermissions> = emptyMap(),
val uuids: Map<String, PNResourcePermissions> = emptyMap()
)

data class PNResourcePermissions(
Expand All @@ -30,7 +28,6 @@ data class PNToken(
val join: Boolean = false
) {

@JsonCreator
constructor(grant: Int) : this(
grant and TokenBitmask.READ != 0,
grant and TokenBitmask.WRITE != 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import com.pubnub.api.eventengine.ManagedEffect
import com.pubnub.api.eventengine.Sink
import com.pubnub.api.presence.eventengine.event.PresenceEvent
import com.pubnub.api.subscribe.eventengine.effect.RetryPolicy
import com.pubnub.extension.scheduleWithDelay
import org.slf4j.LoggerFactory
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit

internal class DelayedHeartbeatEffect(
private val heartbeatRemoteAction: RemoteAction<Boolean>,
Expand Down Expand Up @@ -39,15 +39,15 @@ internal class DelayedHeartbeatEffect(
return
}

scheduled = executorService.schedule({
scheduled = executorService.scheduleWithDelay(delay) {
heartbeatRemoteAction.async { _, status ->
if (status.error) {
presenceEventSink.add(PresenceEvent.HeartbeatFailure(status.exception ?: PubNubException("Unknown error")))
} else {
presenceEventSink.add(PresenceEvent.HeartbeatSuccess)
}
}
}, delay.toMillis(), TimeUnit.MILLISECONDS)
}
}

@Synchronized
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.pubnub.api.presence.eventengine.effect

import com.pubnub.api.PubNubException
import com.pubnub.api.endpoints.remoteaction.RemoteAction
import com.pubnub.api.eventengine.Effect
import org.slf4j.LoggerFactory
Expand All @@ -14,7 +13,7 @@ class LeaveEffect(
log.trace("Running LeaveEffect")
leaveRemoteAction.async { _, status ->
if (status.error) {
throw PubNubException("LeaveEffect failed") // todo how to handle this?
log.error("LeaveEffect failed", status.exception)
}
}
}
Expand Down
Loading

0 comments on commit 12966fa

Please sign in to comment.