Skip to content

Commit

Permalink
[Kotlin] Change PNChannelMetadata and PNUUIDMetadata to contain parti…
Browse files Browse the repository at this point in the history
…al update information (#266)

* Add partial updates when querying channel metadata

* Add partial updates when querying user metadata

* Deprecate TTL parameter in PubNub.fire()
  • Loading branch information
wkal-pubnub authored Aug 6, 2024
1 parent 3e82af3 commit f5d37b8
Show file tree
Hide file tree
Showing 27 changed files with 429 additions and 227 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
package com.pubnub.api.models.consumer.objects.channel

import com.pubnub.api.utils.PatchValue

data class PNChannelMetadata(
val id: String,
val name: String?,
val description: String?,
val custom: Map<String, Any?>?,
val updated: String?,
val eTag: String?,
val type: String?,
val status: String?,
)
val name: PatchValue<String?>? = null,
val description: PatchValue<String?>? = null,
val custom: PatchValue<Map<String, Any?>?>? = null,
val updated: PatchValue<String>? = null,
val eTag: PatchValue<String>? = null,
val type: PatchValue<String?>? = null,
val status: PatchValue<String?>? = null,
) {
/**
* Merge information from this `PNChannelMetadata` with new data from `update`, returning a new `PNChannelMetadata` instance.
*/
operator fun plus(update: PNChannelMetadata): PNChannelMetadata {
return copy(
name = update.name ?: name,
description = update.description ?: description,
custom = update.custom ?: custom,
updated = update.updated ?: updated,
eTag = update.eTag ?: eTag,
type = update.type ?: type,
status = update.status ?: status,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ package com.pubnub.api.models.consumer.objects.channel

data class PNChannelMetadataResult(
val status: Int,
val data: PNChannelMetadata?,
val data: PNChannelMetadata,
)
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
package com.pubnub.api.models.consumer.objects.uuid

import com.pubnub.api.utils.PatchValue

// TODO add a test that checks sending patch values to server and reading them back when we have the "sending" part
data class PNUUIDMetadata(
val id: String,
val name: String?,
val externalId: String?,
val profileUrl: String?,
val email: String?,
val custom: Map<String, Any?>?,
val updated: String?,
val eTag: String?,
val type: String?,
val status: String?,
)
val name: PatchValue<String?>? = null,
val externalId: PatchValue<String?>? = null,
val profileUrl: PatchValue<String?>? = null,
val email: PatchValue<String?>? = null,
val custom: PatchValue<Map<String, Any?>?>? = null,
val updated: PatchValue<String>? = null,
val eTag: PatchValue<String>? = null,
val type: PatchValue<String?>? = null,
val status: PatchValue<String?>? = null,
) {
/**
* Merge information from this `PNUUIDMetadata` with new data from `update`, returning a new `PNUUIDMetadata` instance.
*/
operator fun plus(update: PNUUIDMetadata): PNUUIDMetadata {
return copy(
name = update.name ?: name,
externalId = update.externalId ?: externalId,
profileUrl = update.profileUrl ?: profileUrl,
email = update.email ?: email,
custom = update.custom ?: custom,
updated = update.updated ?: updated,
eTag = update.eTag ?: eTag,
type = update.type ?: type,
status = update.status ?: status,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.pubnub.api.utils

/**
* An optional that accepts nullable values. Thus, it can represent two (`PatchValue<T>`) or three (`PatchValue<T>?`) states:
* * `PatchValue.of(someValue)` - value is present and that value is `someValue`
* * `PatchValue.of(null)` - value is present and that value is `null`
* * `null` - lack of information about value (no update for this field)
*/
data class PatchValue<out T> internal constructor(val value: T) {
companion object {
/**
* Create an optional with the specified value (which can be null).
*/
@JvmStatic
fun <T> of(value: T): PatchValue<T> = PatchValue(value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -298,15 +298,13 @@ class PubNubCore internal constructor(
message: Any,
meta: Any? = null,
usePost: Boolean = false,
ttl: Int? = null,
) = publish(
channel = channel,
message = message,
meta = meta,
shouldStore = false,
usePost = usePost,
replicate = false,
ttl = ttl,
)

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@ import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import com.google.gson.ToNumberPolicy
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import com.pubnub.api.PubNubError
import com.pubnub.api.PubNubException
import com.pubnub.api.utils.PatchValue
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import retrofit2.Converter
import retrofit2.converter.gson.GsonConverterFactory
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

class MapperManager {
Expand All @@ -42,7 +46,7 @@ class MapperManager {
}
}

override fun read(_in: JsonReader): Boolean? {
override fun read(_in: JsonReader): Boolean {
val peek: JsonToken = _in.peek()
return when (peek) {
JsonToken.BOOLEAN -> _in.nextBoolean()
Expand All @@ -53,13 +57,66 @@ class MapperManager {
}
}

val patchValueTypeFactory = object : TypeAdapterFactory {
override fun <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
if (type.rawType != PatchValue::class.java) {
return null
}
val factory = this
return object : TypeAdapter<T>() {
override fun write(out: JsonWriter, patchValue: T) {
val writeNulls = out.serializeNulls
try {
if (patchValue == null) {
// value is PatchValue.none(), skip it (serializeNulls is false)
out.nullValue()
} else {
patchValue as PatchValue<T>
if (patchValue.value == null) {
// value is PatchValue.of(null), write it out to JSON:
out.serializeNulls = true
out.nullValue()
} else {
// value is PatchValue.of(something), write it out using the right adapter:
val delegate = gson.getDelegateAdapter(
factory,
TypeToken.get(patchValue.value!!::class.java)
) as TypeAdapter<Any>
delegate.write(out, patchValue.value)
}
}
} finally {
out.serializeNulls = writeNulls
}
}

override fun read(reader: JsonReader): T {
val token = reader.peek()
if (token == JsonToken.NULL) {
reader.nextNull()
@Suppress("UNCHECKED_CAST")
return PatchValue.of(null) as T
} else {
val delegate = gson.getDelegateAdapter(
factory,
TypeToken.get((type.type as ParameterizedType).actualTypeArguments.first())
)
@Suppress("UNCHECKED_CAST")
return PatchValue.of(delegate.read(reader)) as T
}
}
}
}
}

objectMapper =
GsonBuilder()
.registerTypeAdapter(Boolean::class.javaObjectType, booleanAsIntAdapter)
.registerTypeAdapter(Boolean::class.javaPrimitiveType, booleanAsIntAdapter)
.registerTypeAdapter(Boolean::class.java, booleanAsIntAdapter)
.registerTypeAdapter(JSONObject::class.java, JSONObjectAdapter())
.registerTypeAdapter(JSONArray::class.java, JSONArrayAdapter())
.registerTypeAdapterFactory(patchValueTypeFactory)
.disableHtmlEscaping()
.setObjectToNumberStrategy(ToNumberPolicy.LAZILY_PARSED_NUMBER)
.create()
Expand Down Expand Up @@ -144,7 +201,7 @@ class MapperManager {
clazz: Class<T>,
): T {
return try {
this.objectMapper.fromJson<T>(input, clazz)
this.objectMapper.fromJson(input, clazz)
} catch (e: JsonParseException) {
throw PubNubException(
pubnubError = PubNubError.PARSING_ERROR,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.pubnub.internal.models.consumer.objects.membership
import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata

data class PNChannelMembership(
val channel: PNChannelMetadata?,
val channel: PNChannelMetadata,
val custom: Map<String, Any?>?,
val updated: String,
val eTag: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ package com.pubnub.internal.models.server.objects_api

data class EntityEnvelope<T>(
val status: Int,
val data: T? = null,
val data: T,
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ class WhenSteps(
val channelMetadata = channelMetadataState.channelMetadata!!
world.pubnub.pubNubCore.setChannelMetadata(
channel = channelMetadata.id,
name = channelMetadata.name,
description = channelMetadata.description,
custom = channelMetadata.custom,
type = channelMetadata.type,
status = channelMetadata.status,
name = channelMetadata.name?.value,
description = channelMetadata.description?.value,
custom = channelMetadata.custom?.value,
type = channelMetadata.type?.value,
status = channelMetadata.status?.value,
).sync().let {
channelMetadataState.channelMetadata = it.data
world.responseStatus = it.status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ class WhenSteps(
world.pubnub.pubNubCore.setUUIDMetadata(
uuid = uuidMetadata.id,
custom = uuidMetadata.custom,
externalId = uuidMetadata.externalId,
profileUrl = uuidMetadata.profileUrl,
email = uuidMetadata.email,
name = uuidMetadata.name,
status = uuidMetadata.status,
type = uuidMetadata.type,
).sync()?.let {
externalId = uuidMetadata.externalId?.value,
profileUrl = uuidMetadata.profileUrl?.value,
email = uuidMetadata.email?.value,
name = uuidMetadata.name?.value,
status = uuidMetadata.status?.value,
type = uuidMetadata.type?.value,
).sync().let {
uuidMetadataState.uuidMetadata = it.data
world.responseStatus = it.status
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.pubnub.internal.managers

import com.pubnub.api.PubNubException
import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test

internal class MapperManagerTest {
Expand Down Expand Up @@ -94,6 +97,29 @@ internal class MapperManagerTest {
assertEquals(map["f"], decodedMap["f"])
assertEquals(map["f"].toString().toLong(), decodedMap["f"].tryLong())
}

@Test
fun fromJson_optionalsAndNulls() {
val mapperManager = MapperManager()
val input = """
{ "id" : "myId", "name": null, "description": "myDescription", "eTag": "myEtag", "custom": { "a" : "b" } }
""".trimIndent()

val output: PNChannelMetadata = mapperManager.fromJson(input, PNChannelMetadata::class.java)

assertEquals("myId", output.id)
assertNotNull(output.name)
assertNull(output.name?.value)

assertEquals("myDescription", output.description?.value)

assertNull(output.updated)
assertNull(output.status)

assertEquals(mapOf("a" to "b"), output.custom?.value)

assertEquals("myEtag", output.eTag?.value)
}
}

private fun Any?.tryLong(): Long? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collection;
Expand Down Expand Up @@ -39,26 +39,25 @@ public PNChannelMetadata setCustom(Object custom) {
return this;
}

@Nullable
public static PNChannelMetadata from(@Nullable com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata data) {
if (data == null) {
return null;
}
@NotNull
public static PNChannelMetadata from(@NotNull com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata data) {
PNChannelMetadata newData = new PNChannelMetadata(
data.getId(), data.getName(), data.getDescription()
data.getId(),
data.getName() != null ? data.getName().getValue() : null,
data.getDescription() != null ? data.getDescription().getValue() : null
);
newData.setETag(data.getETag());
newData.setType(data.getType());
newData.setStatus(data.getStatus());
newData.setCustom(data.getCustom());
newData.setUpdated(data.getUpdated());
newData.setETag(data.getETag() != null ? data.getETag().getValue() : null);
newData.setType(data.getType() != null ? data.getType().getValue() : null);
newData.setStatus(data.getStatus() != null ? data.getStatus().getValue() : null);
newData.setCustom(data.getCustom() != null ? data.getCustom().getValue() : null);
newData.setUpdated(data.getUpdated() != null ? data.getUpdated().getValue() : null);
return newData;
}

public static List<PNChannelMetadata> from(Collection<com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata> data) {
List<PNChannelMetadata> channels = new ArrayList<>(data.size());
for (com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata datum : data) {
channels.add(PNChannelMetadata.from(datum));
channels.add(from(datum));
}
return channels;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,16 @@ public static PNUUIDMetadata from(com.pubnub.api.models.consumer.objects.uuid.PN
if (data == null) {
return null;
}
PNUUIDMetadata newData = new PNUUIDMetadata(data.getId(), data.getName())
.setProfileUrl(data.getProfileUrl())
.setEmail(data.getEmail())
.setExternalId(data.getExternalId())
.setStatus(data.getStatus())
.setType(data.getType())
.setCustom(data.getCustom());
PNUUIDMetadata newData = new PNUUIDMetadata(data.getId(), data.getName() != null ? data.getName().getValue() : null)
.setProfileUrl(data.getProfileUrl() != null ? data.getProfileUrl().getValue() : null)
.setEmail(data.getEmail() != null ? data.getEmail().getValue() : null)
.setExternalId(data.getExternalId() != null ? data.getExternalId().getValue() : null)
.setStatus(data.getStatus() != null ? data.getStatus().getValue() : null)
.setType(data.getType() != null ? data.getType().getValue() : null)
.setCustom(data.getCustom() != null ? data.getCustom().getValue() : null);

newData.setETag(data.getETag());
newData.setUpdated(data.getUpdated());
newData.setETag(data.getETag() != null ? data.getETag().getValue() : null);
newData.setUpdated(data.getUpdated() != null ? data.getUpdated().getValue() : null);
return newData;
}
}
Expand Down
Loading

0 comments on commit f5d37b8

Please sign in to comment.