Skip to content

Commit

Permalink
Etag support for users and channels
Browse files Browse the repository at this point in the history
A new optional parameter `ifMatchesEtag` is added to `setUUIDMetadata` and `setChannelMetadata`.
When provided, the server checks the argument value with the ETag on the server and if they don't match a HTTP 412 error is returned.
  • Loading branch information
wkal-pubnub committed Jan 17, 2025
1 parent 11f1678 commit 44659f7
Show file tree
Hide file tree
Showing 19 changed files with 180 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.pubnub.api.java.endpoints.BuilderSteps;
import com.pubnub.api.java.endpoints.Endpoint;
import com.pubnub.api.java.models.consumer.objects_api.channel.PNSetChannelMetadataResult;
import org.jetbrains.annotations.Nullable;

import java.util.Map;

Expand All @@ -19,6 +20,8 @@ public interface SetChannelMetadata extends Endpoint<PNSetChannelMetadataResult>

SetChannelMetadata includeCustom(boolean includeCustom);

SetChannelMetadata ifMatchesEtag(@Nullable String ifMatchesEtag);

interface Builder extends BuilderSteps.ChannelStep<SetChannelMetadata> {
@Override
SetChannelMetadata channel(String channel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.pubnub.api.java.endpoints.Endpoint;
import com.pubnub.api.java.models.consumer.objects_api.uuid.PNSetUUIDMetadataResult;
import org.jetbrains.annotations.Nullable;

import java.util.Map;

Expand All @@ -23,4 +24,6 @@ public interface SetUUIDMetadata extends Endpoint<PNSetUUIDMetadataResult> {
SetUUIDMetadata type(String type);

SetUUIDMetadata status(String status);

SetUUIDMetadata ifMatchesEtag(@Nullable String etag);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import lombok.Setter;
import lombok.experimental.Accessors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -46,7 +47,8 @@ protected Endpoint<PNChannelMetadataResult> createRemoteAction() {
custom,
includeCustom,
type,
status
status,
ifMatchesEtag
);
}

Expand All @@ -69,6 +71,10 @@ protected Endpoint<PNChannelMetadataResult> createRemoteAction() {
@Setter
private boolean includeCustom;

@Setter
@Nullable
private String ifMatchesEtag;

@Override
public SetChannelMetadata custom(Map<String, Object> custom) {
final HashMap<String, Object> customHashMap = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import lombok.Setter;
import lombok.experimental.Accessors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;
Expand All @@ -37,6 +38,10 @@ public class SetUUIDMetadataImpl extends DelegatingEndpoint<PNUUIDMetadataResult
@Setter
private String status;

@Setter
@Nullable
private String ifMatchesEtag;

public SetUUIDMetadataImpl(final PubNub pubnub) {
super(pubnub);
}
Expand All @@ -63,7 +68,8 @@ protected Endpoint<PNUUIDMetadataResult> createRemoteAction() {
custom,
includeCustom,
type,
status
status,
ifMatchesEtag
);
}

Expand Down
34 changes: 14 additions & 20 deletions pubnub-kotlin/pubnub-kotlin-api/config/ktlint/baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
<file name="src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/membership/MembershipInclude.kt">
<error line="20" column="5" source="standard:function-naming" />
</file>
<file name="src/commonTest/kotlin/com/pubnub/test/integration/MembersTest.kt">
<error line="18" column="1" source="standard:blank-line-before-declaration" />
</file>
<file name="src/jsMain/kotlin/Pubnub.d.kt">
<error line="156" column="40" source="standard:comment-wrapping" />
<error line="411" column="15" source="standard:class-naming" />
Expand All @@ -22,19 +19,17 @@
<error line="849" column="13" source="standard:property-naming" />
<error line="1034" column="15" source="standard:class-naming" />
<error line="1040" column="15" source="standard:class-naming" />
<error line="1161" column="13" source="standard:property-naming" />
<error line="1169" column="13" source="standard:property-naming" />
<error line="1163" column="13" source="standard:property-naming" />
<error line="1171" column="13" source="standard:property-naming" />
<error line="1234" column="39" source="standard:comment-wrapping" />
<error line="1319" column="15" source="standard:class-naming" />
<error line="1347" column="42" source="standard:comment-wrapping" />
<error line="1397" column="37" source="standard:comment-wrapping" />
<error line="1418" column="13" source="standard:property-naming" />
<error line="1419" column="13" source="standard:property-naming" />
<error line="1423" column="13" source="standard:property-naming" />
<error line="1424" column="13" source="standard:property-naming" />
<error line="1443" column="9" source="standard:property-naming" />
<error line="1444" column="9" source="standard:property-naming" />
<error line="1173" column="13" source="standard:property-naming" />
<error line="1236" column="39" source="standard:comment-wrapping" />
<error line="1321" column="15" source="standard:class-naming" />
<error line="1349" column="42" source="standard:comment-wrapping" />
<error line="1399" column="37" source="standard:comment-wrapping" />
<error line="1420" column="13" source="standard:property-naming" />
<error line="1421" column="13" source="standard:property-naming" />
<error line="1425" column="13" source="standard:property-naming" />
<error line="1426" column="13" source="standard:property-naming" />
<error line="1445" column="9" source="standard:property-naming" />
<error line="1446" column="9" source="standard:property-naming" />
<error line="1447" column="9" source="standard:property-naming" />
Expand All @@ -46,8 +41,8 @@
<error line="1453" column="9" source="standard:property-naming" />
<error line="1454" column="9" source="standard:property-naming" />
<error line="1455" column="9" source="standard:property-naming" />
<error line="1459" column="9" source="standard:property-naming" />
<error line="1460" column="9" source="standard:property-naming" />
<error line="1456" column="9" source="standard:property-naming" />
<error line="1457" column="9" source="standard:property-naming" />
<error line="1461" column="9" source="standard:property-naming" />
<error line="1462" column="9" source="standard:property-naming" />
<error line="1463" column="9" source="standard:property-naming" />
Expand Down Expand Up @@ -82,8 +77,7 @@
<error line="1492" column="9" source="standard:property-naming" />
<error line="1493" column="9" source="standard:property-naming" />
<error line="1494" column="9" source="standard:property-naming" />
</file>
<file name="src/jsMain/kotlin/com/pubnub/api/PubNubImpl.kt">
<error line="1446" column="12" source="standard:no-multi-spaces" />
<error line="1495" column="9" source="standard:property-naming" />
<error line="1496" column="9" source="standard:property-naming" />
</file>
</baseline>
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,8 @@ class PubNubImpl(private val pubNubObjC: KMPPubNub) : PubNub {
custom: CustomObject?,
includeCustom: Boolean,
type: String?,
status: String?
status: String?,
ifMatchesEtag: String?,
): SetChannelMetadata {
return SetChannelMetadataImpl(
pubnub = pubNubObjC,
Expand All @@ -523,7 +524,8 @@ class PubNubImpl(private val pubNubObjC: KMPPubNub) : PubNub {
custom = custom,
includeCustom = includeCustom,
type = type,
status = status
status = status,
ifMatchesEtag = ifMatchesEtag,
)
}

Expand Down Expand Up @@ -570,7 +572,8 @@ class PubNubImpl(private val pubNubObjC: KMPPubNub) : PubNub {
custom: CustomObject?,
includeCustom: Boolean,
type: String?,
status: String?
status: String?,
ifMatchesEtag: String?,
): SetUUIDMetadata {
return SetUUIDMetadataImpl(
pubnub = pubNubObjC,
Expand All @@ -582,7 +585,8 @@ class PubNubImpl(private val pubNubObjC: KMPPubNub) : PubNub {
custom = custom,
includeCustom = includeCustom,
type = type,
status = status
status = status,
ifMatchesEtag = ifMatchesEtag,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ class SetChannelMetadataImpl(
private val custom: CustomObject?,
private val includeCustom: Boolean,
private val type: String?,
private val status: String?
private val status: String?,
private val ifMatchesEtag: String? = null,
) : SetChannelMetadata {
override fun async(callback: Consumer<Result<PNChannelMetadataResult>>) {
pubnub.setChannelMetadataWithMetadataId(
Expand All @@ -43,6 +44,7 @@ class SetChannelMetadataImpl(
),
type = type,
status = status,
ifMatchesEtag = ifMatchesEtag,
onSuccess = callback.onSuccessHandler {
PNChannelMetadataResult(
status = 200,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class SetUUIDMetadataImpl(
private val custom: CustomObject?,
private val includeCustom: Boolean,
private val type: String?,
private val status: String?
private val status: String?,
private val ifMatchesEtag: String? = null,
) : SetUUIDMetadata {
override fun async(callback: Consumer<Result<PNUUIDMetadataResult>>) {
pubnub.setUserMetadataWithMetadataId(
Expand All @@ -47,6 +48,7 @@ class SetUUIDMetadataImpl(
),
type = type,
status = status,
ifMatchesEtag = ifMatchesEtag,
onSuccess = callback.onSuccessHandler {
PNUUIDMetadataResult(
status = 200,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ expect interface PubNub {
includeCustom: Boolean = false,
type: String? = null,
status: String? = null,
ifMatchesEtag: String? = null,
): SetChannelMetadata

fun removeChannelMetadata(channel: String): RemoveChannelMetadata
Expand Down Expand Up @@ -296,6 +297,7 @@ expect interface PubNub {
includeCustom: Boolean = false,
type: String? = null,
status: String? = null,
ifMatchesEtag: String? = null,
): SetUUIDMetadata

fun removeUUIDMetadata(uuid: String? = null): RemoveUUIDMetadata
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,58 @@ class ChannelMetadataTest : BaseIntegrationTest() {
assertEquals(description, pnuuidMetadata.description?.value)
}

@Test
fun set_metadata_ifMatch_allows_change() = runTest {
// given
val result = pubnub.setChannelMetadata(
channel,
name = name,
status = status,
custom = custom,
includeCustom = includeCustom,
type = type,
description = description
).await()

val pnChannelMetadata = result.data

// when
val newData = pubnub.setChannelMetadata(
channel,
status = "someNewStatus",
ifMatchesEtag = pnChannelMetadata.eTag?.value
).await().data

// then
assertEquals("someNewStatus", newData.status?.value)
}

@Test
fun set_metadata_ifMatch_prohibits_change() = runTest {
// given
val result = pubnub.setChannelMetadata(
channel,
name = name,
status = status,
custom = custom,
includeCustom = includeCustom,
type = type,
description = description
).await()

val pnChannelMetadata = result.data

pubnub.setChannelMetadata(channel, name = "someNewName").await()

// when
val ex = assertFailsWith<PubNubException> {
pubnub.setChannelMetadata(channel, status = "someNewStatus", ifMatchesEtag = pnChannelMetadata.eTag?.value).await()
}

// then
assertEquals(HTTP_PRECONDITION_FAILED, ex.statusCode)
}

@Test
fun can_receive_set_metadata_event() = runTest {
pubnub.test(backgroundScope) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.time.Duration.Companion.seconds

internal const val HTTP_PRECONDITION_FAILED = 412

class UserMetadataTest : BaseIntegrationTest() {
private val uuid = "myUser" + randomString()
private val name = randomString()
Expand Down Expand Up @@ -60,6 +62,57 @@ class UserMetadataTest : BaseIntegrationTest() {
assertEquals(type, pnuuidMetadata.type?.value)
}

@Test
fun set_metadata_ifMatch_allows_change() = runTest {
// given
val result = pubnub.setUUIDMetadata(
uuid,
name = name,
externalId = externalId,
profileUrl = profileUrl,
email = email,
status = status,
custom = custom,
includeCustom = includeCustom,
type = type
).await()

val pnuuidMetadata = result.data

// when
val newData = pubnub.setUUIDMetadata(uuid, externalId = "someNewId", ifMatchesEtag = pnuuidMetadata.eTag?.value).await().data

// then
assertEquals("someNewId", newData.externalId?.value)
}

@Test
fun set_metadata_ifMatch_prohibits_change() = runTest {
// given
val result = pubnub.setUUIDMetadata(
uuid,
name = name,
externalId = externalId,
profileUrl = profileUrl,
email = email,
status = status,
custom = custom,
includeCustom = includeCustom,
type = type
).await()

val pnuuidMetadata = result.data
pubnub.setUUIDMetadata(uuid, name = "someNewName").await()

// when
val ex = assertFailsWith<PubNubException> {
pubnub.setUUIDMetadata(uuid, externalId = "someNewId", ifMatchesEtag = pnuuidMetadata.eTag?.value).await()
}

// then
assertEquals(HTTP_PRECONDITION_FAILED, ex.statusCode)
}

@Test
fun can_receive_set_metadata_event() = runTest {
pubnub.test(backgroundScope) {
Expand Down
2 changes: 2 additions & 0 deletions pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/Pubnub.d.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,7 @@ open external class PubNub(config: Any /* UUID | UserId */) {

var data: UUIDMetadata
var include: UuidIncludeCustom?
var ifMatchesEtag: String?
}

interface RemoveUUIDMetadataParameters {
Expand Down Expand Up @@ -1121,6 +1122,7 @@ open external class PubNub(config: Any /* UUID | UserId */) {
var channel: String
var data: ChannelMetadata
var include: UuidIncludeCustom?
var ifMatchesEtag: String?
}

interface RemoveChannelMetadataParameters {
Expand Down
Loading

0 comments on commit 44659f7

Please sign in to comment.