Skip to content

Commit c7d54d1

Browse files
committed
Etag support for users and channels
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.
1 parent 11f1678 commit c7d54d1

File tree

19 files changed

+196
-32
lines changed

19 files changed

+196
-32
lines changed

pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/channel/SetChannelMetadata.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.pubnub.api.java.endpoints.BuilderSteps;
44
import com.pubnub.api.java.endpoints.Endpoint;
55
import com.pubnub.api.java.models.consumer.objects_api.channel.PNSetChannelMetadataResult;
6+
import org.jetbrains.annotations.Nullable;
67

78
import java.util.Map;
89

@@ -19,6 +20,14 @@ public interface SetChannelMetadata extends Endpoint<PNSetChannelMetadataResult>
1920

2021
SetChannelMetadata includeCustom(boolean includeCustom);
2122

23+
/**
24+
* Optional entity tag from a previously received `PNChannelMetadata`. The request
25+
* will fail if this parameter is specified and the ETag value on the server doesn't match.
26+
* @param etag from PNChannelMetadata
27+
* @return this builder
28+
*/
29+
SetChannelMetadata ifMatchesEtag(@Nullable String etag);
30+
2231
interface Builder extends BuilderSteps.ChannelStep<SetChannelMetadata> {
2332
@Override
2433
SetChannelMetadata channel(String channel);

pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/SetUUIDMetadata.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.pubnub.api.java.endpoints.Endpoint;
44
import com.pubnub.api.java.models.consumer.objects_api.uuid.PNSetUUIDMetadataResult;
5+
import org.jetbrains.annotations.Nullable;
56

67
import java.util.Map;
78

@@ -23,4 +24,12 @@ public interface SetUUIDMetadata extends Endpoint<PNSetUUIDMetadataResult> {
2324
SetUUIDMetadata type(String type);
2425

2526
SetUUIDMetadata status(String status);
27+
28+
/**
29+
* Optional entity tag from a previously received `PNUUIDMetadata`. The request
30+
* will fail if this parameter is specified and the ETag value on the server doesn't match.
31+
* @param etag from PNUUIDMetadata
32+
* @return this builder
33+
*/
34+
SetUUIDMetadata ifMatchesEtag(@Nullable String etag);
2635
}

pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/channel/SetChannelMetadataImpl.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import lombok.Setter;
1313
import lombok.experimental.Accessors;
1414
import org.jetbrains.annotations.NotNull;
15+
import org.jetbrains.annotations.Nullable;
1516

1617
import java.util.HashMap;
1718
import java.util.Map;
@@ -46,7 +47,8 @@ protected Endpoint<PNChannelMetadataResult> createRemoteAction() {
4647
custom,
4748
includeCustom,
4849
type,
49-
status
50+
status,
51+
ifMatchesEtag
5052
);
5153
}
5254

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

74+
@Setter
75+
@Nullable
76+
private String ifMatchesEtag;
77+
7278
@Override
7379
public SetChannelMetadata custom(Map<String, Object> custom) {
7480
final HashMap<String, Object> customHashMap = new HashMap<>();

pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/uuid/SetUUIDMetadataImpl.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import lombok.Setter;
1313
import lombok.experimental.Accessors;
1414
import org.jetbrains.annotations.NotNull;
15+
import org.jetbrains.annotations.Nullable;
1516

1617
import java.util.HashMap;
1718
import java.util.Map;
@@ -37,6 +38,10 @@ public class SetUUIDMetadataImpl extends DelegatingEndpoint<PNUUIDMetadataResult
3738
@Setter
3839
private String status;
3940

41+
@Setter
42+
@Nullable
43+
private String ifMatchesEtag;
44+
4045
public SetUUIDMetadataImpl(final PubNub pubnub) {
4146
super(pubnub);
4247
}
@@ -63,7 +68,8 @@ protected Endpoint<PNUUIDMetadataResult> createRemoteAction() {
6368
custom,
6469
includeCustom,
6570
type,
66-
status
71+
status,
72+
ifMatchesEtag
6773
);
6874
}
6975

pubnub-kotlin/pubnub-kotlin-api/config/ktlint/baseline.xml

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
<file name="src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/membership/MembershipInclude.kt">
77
<error line="20" column="5" source="standard:function-naming" />
88
</file>
9-
<file name="src/commonTest/kotlin/com/pubnub/test/integration/MembersTest.kt">
10-
<error line="18" column="1" source="standard:blank-line-before-declaration" />
11-
</file>
129
<file name="src/jsMain/kotlin/Pubnub.d.kt">
1310
<error line="156" column="40" source="standard:comment-wrapping" />
1411
<error line="411" column="15" source="standard:class-naming" />
@@ -22,19 +19,17 @@
2219
<error line="849" column="13" source="standard:property-naming" />
2320
<error line="1034" column="15" source="standard:class-naming" />
2421
<error line="1040" column="15" source="standard:class-naming" />
25-
<error line="1161" column="13" source="standard:property-naming" />
26-
<error line="1169" column="13" source="standard:property-naming" />
22+
<error line="1163" column="13" source="standard:property-naming" />
2723
<error line="1171" column="13" source="standard:property-naming" />
28-
<error line="1234" column="39" source="standard:comment-wrapping" />
29-
<error line="1319" column="15" source="standard:class-naming" />
30-
<error line="1347" column="42" source="standard:comment-wrapping" />
31-
<error line="1397" column="37" source="standard:comment-wrapping" />
32-
<error line="1418" column="13" source="standard:property-naming" />
33-
<error line="1419" column="13" source="standard:property-naming" />
34-
<error line="1423" column="13" source="standard:property-naming" />
35-
<error line="1424" column="13" source="standard:property-naming" />
36-
<error line="1443" column="9" source="standard:property-naming" />
37-
<error line="1444" column="9" source="standard:property-naming" />
24+
<error line="1173" column="13" source="standard:property-naming" />
25+
<error line="1236" column="39" source="standard:comment-wrapping" />
26+
<error line="1321" column="15" source="standard:class-naming" />
27+
<error line="1349" column="42" source="standard:comment-wrapping" />
28+
<error line="1399" column="37" source="standard:comment-wrapping" />
29+
<error line="1420" column="13" source="standard:property-naming" />
30+
<error line="1421" column="13" source="standard:property-naming" />
31+
<error line="1425" column="13" source="standard:property-naming" />
32+
<error line="1426" column="13" source="standard:property-naming" />
3833
<error line="1445" column="9" source="standard:property-naming" />
3934
<error line="1446" column="9" source="standard:property-naming" />
4035
<error line="1447" column="9" source="standard:property-naming" />
@@ -46,8 +41,8 @@
4641
<error line="1453" column="9" source="standard:property-naming" />
4742
<error line="1454" column="9" source="standard:property-naming" />
4843
<error line="1455" column="9" source="standard:property-naming" />
49-
<error line="1459" column="9" source="standard:property-naming" />
50-
<error line="1460" column="9" source="standard:property-naming" />
44+
<error line="1456" column="9" source="standard:property-naming" />
45+
<error line="1457" column="9" source="standard:property-naming" />
5146
<error line="1461" column="9" source="standard:property-naming" />
5247
<error line="1462" column="9" source="standard:property-naming" />
5348
<error line="1463" column="9" source="standard:property-naming" />
@@ -82,8 +77,7 @@
8277
<error line="1492" column="9" source="standard:property-naming" />
8378
<error line="1493" column="9" source="standard:property-naming" />
8479
<error line="1494" column="9" source="standard:property-naming" />
85-
</file>
86-
<file name="src/jsMain/kotlin/com/pubnub/api/PubNubImpl.kt">
87-
<error line="1446" column="12" source="standard:no-multi-spaces" />
80+
<error line="1495" column="9" source="standard:property-naming" />
81+
<error line="1496" column="9" source="standard:property-naming" />
8882
</file>
8983
</baseline>

pubnub-kotlin/pubnub-kotlin-api/src/appleMain/kotlin/com/pubnub/api/PubNubImpl.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,8 @@ class PubNubImpl(private val pubNubObjC: KMPPubNub) : PubNub {
513513
custom: CustomObject?,
514514
includeCustom: Boolean,
515515
type: String?,
516-
status: String?
516+
status: String?,
517+
ifMatchesEtag: String?,
517518
): SetChannelMetadata {
518519
return SetChannelMetadataImpl(
519520
pubnub = pubNubObjC,
@@ -523,7 +524,8 @@ class PubNubImpl(private val pubNubObjC: KMPPubNub) : PubNub {
523524
custom = custom,
524525
includeCustom = includeCustom,
525526
type = type,
526-
status = status
527+
status = status,
528+
ifMatchesEtag = ifMatchesEtag,
527529
)
528530
}
529531

@@ -570,7 +572,8 @@ class PubNubImpl(private val pubNubObjC: KMPPubNub) : PubNub {
570572
custom: CustomObject?,
571573
includeCustom: Boolean,
572574
type: String?,
573-
status: String?
575+
status: String?,
576+
ifMatchesEtag: String?,
574577
): SetUUIDMetadata {
575578
return SetUUIDMetadataImpl(
576579
pubnub = pubNubObjC,
@@ -582,7 +585,8 @@ class PubNubImpl(private val pubNubObjC: KMPPubNub) : PubNub {
582585
custom = custom,
583586
includeCustom = includeCustom,
584587
type = type,
585-
status = status
588+
status = status,
589+
ifMatchesEtag = ifMatchesEtag,
586590
)
587591
}
588592

pubnub-kotlin/pubnub-kotlin-api/src/appleMain/kotlin/com/pubnub/api/endpoints/objects/channel/SetChannelMetadata.ios.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class SetChannelMetadataImpl(
2828
private val custom: CustomObject?,
2929
private val includeCustom: Boolean,
3030
private val type: String?,
31-
private val status: String?
31+
private val status: String?,
32+
private val ifMatchesEtag: String? = null,
3233
) : SetChannelMetadata {
3334
override fun async(callback: Consumer<Result<PNChannelMetadataResult>>) {
3435
pubnub.setChannelMetadataWithMetadataId(
@@ -43,6 +44,7 @@ class SetChannelMetadataImpl(
4344
),
4445
type = type,
4546
status = status,
47+
ifMatchesEtag = ifMatchesEtag,
4648
onSuccess = callback.onSuccessHandler {
4749
PNChannelMetadataResult(
4850
status = 200,

pubnub-kotlin/pubnub-kotlin-api/src/appleMain/kotlin/com/pubnub/api/endpoints/objects/uuid/SetUUIDMetadata.ios.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ class SetUUIDMetadataImpl(
3030
private val custom: CustomObject?,
3131
private val includeCustom: Boolean,
3232
private val type: String?,
33-
private val status: String?
33+
private val status: String?,
34+
private val ifMatchesEtag: String? = null,
3435
) : SetUUIDMetadata {
3536
override fun async(callback: Consumer<Result<PNUUIDMetadataResult>>) {
3637
pubnub.setUserMetadataWithMetadataId(
@@ -47,6 +48,7 @@ class SetUUIDMetadataImpl(
4748
),
4849
type = type,
4950
status = status,
51+
ifMatchesEtag = ifMatchesEtag,
5052
onSuccess = callback.onSuccessHandler {
5153
PNUUIDMetadataResult(
5254
status = 200,

pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/PubNub.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ expect interface PubNub {
268268
includeCustom: Boolean = false,
269269
type: String? = null,
270270
status: String? = null,
271+
ifMatchesEtag: String? = null,
271272
): SetChannelMetadata
272273

273274
fun removeChannelMetadata(channel: String): RemoveChannelMetadata
@@ -296,6 +297,7 @@ expect interface PubNub {
296297
includeCustom: Boolean = false,
297298
type: String? = null,
298299
status: String? = null,
300+
ifMatchesEtag: String? = null,
299301
): SetUUIDMetadata
300302

301303
fun removeUUIDMetadata(uuid: String? = null): RemoveUUIDMetadata

pubnub-kotlin/pubnub-kotlin-api/src/commonTest/kotlin/com/pubnub/test/integration/ChannelMetadataTest.kt

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,58 @@ class ChannelMetadataTest : BaseIntegrationTest() {
5252
assertEquals(description, pnuuidMetadata.description?.value)
5353
}
5454

55+
@Test
56+
fun set_metadata_ifMatch_allows_change() = runTest {
57+
// given
58+
val result = pubnub.setChannelMetadata(
59+
channel,
60+
name = name,
61+
status = status,
62+
custom = custom,
63+
includeCustom = includeCustom,
64+
type = type,
65+
description = description
66+
).await()
67+
68+
val pnChannelMetadata = result.data
69+
70+
// when
71+
val newData = pubnub.setChannelMetadata(
72+
channel,
73+
status = "someNewStatus",
74+
ifMatchesEtag = pnChannelMetadata.eTag?.value
75+
).await().data
76+
77+
// then
78+
assertEquals("someNewStatus", newData.status?.value)
79+
}
80+
81+
@Test
82+
fun set_metadata_ifMatch_prohibits_change() = runTest {
83+
// given
84+
val result = pubnub.setChannelMetadata(
85+
channel,
86+
name = name,
87+
status = status,
88+
custom = custom,
89+
includeCustom = includeCustom,
90+
type = type,
91+
description = description
92+
).await()
93+
94+
val pnChannelMetadata = result.data
95+
96+
pubnub.setChannelMetadata(channel, name = "someNewName").await()
97+
98+
// when
99+
val ex = assertFailsWith<PubNubException> {
100+
pubnub.setChannelMetadata(channel, status = "someNewStatus", ifMatchesEtag = pnChannelMetadata.eTag?.value).await()
101+
}
102+
103+
// then
104+
assertEquals(HTTP_PRECONDITION_FAILED, ex.statusCode)
105+
}
106+
55107
@Test
56108
fun can_receive_set_metadata_event() = runTest {
57109
pubnub.test(backgroundScope) {

pubnub-kotlin/pubnub-kotlin-api/src/commonTest/kotlin/com/pubnub/test/integration/UserMetadataTest.kt

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import kotlin.test.assertNotNull
2020
import kotlin.test.assertNull
2121
import kotlin.time.Duration.Companion.seconds
2222

23+
internal const val HTTP_PRECONDITION_FAILED = 412
24+
2325
class UserMetadataTest : BaseIntegrationTest() {
2426
private val uuid = "myUser" + randomString()
2527
private val name = randomString()
@@ -60,6 +62,57 @@ class UserMetadataTest : BaseIntegrationTest() {
6062
assertEquals(type, pnuuidMetadata.type?.value)
6163
}
6264

65+
@Test
66+
fun set_metadata_ifMatch_allows_change() = runTest {
67+
// given
68+
val result = pubnub.setUUIDMetadata(
69+
uuid,
70+
name = name,
71+
externalId = externalId,
72+
profileUrl = profileUrl,
73+
email = email,
74+
status = status,
75+
custom = custom,
76+
includeCustom = includeCustom,
77+
type = type
78+
).await()
79+
80+
val pnuuidMetadata = result.data
81+
82+
// when
83+
val newData = pubnub.setUUIDMetadata(uuid, externalId = "someNewId", ifMatchesEtag = pnuuidMetadata.eTag?.value).await().data
84+
85+
// then
86+
assertEquals("someNewId", newData.externalId?.value)
87+
}
88+
89+
@Test
90+
fun set_metadata_ifMatch_prohibits_change() = runTest {
91+
// given
92+
val result = pubnub.setUUIDMetadata(
93+
uuid,
94+
name = name,
95+
externalId = externalId,
96+
profileUrl = profileUrl,
97+
email = email,
98+
status = status,
99+
custom = custom,
100+
includeCustom = includeCustom,
101+
type = type
102+
).await()
103+
104+
val pnuuidMetadata = result.data
105+
pubnub.setUUIDMetadata(uuid, name = "someNewName").await()
106+
107+
// when
108+
val ex = assertFailsWith<PubNubException> {
109+
pubnub.setUUIDMetadata(uuid, externalId = "someNewId", ifMatchesEtag = pnuuidMetadata.eTag?.value).await()
110+
}
111+
112+
// then
113+
assertEquals(HTTP_PRECONDITION_FAILED, ex.statusCode)
114+
}
115+
63116
@Test
64117
fun can_receive_set_metadata_event() = runTest {
65118
pubnub.test(backgroundScope) {

pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/Pubnub.d.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,7 @@ open external class PubNub(config: Any /* UUID | UserId */) {
10701070

10711071
var data: UUIDMetadata
10721072
var include: UuidIncludeCustom?
1073+
var ifMatchesEtag: String?
10731074
}
10741075

10751076
interface RemoveUUIDMetadataParameters {
@@ -1121,6 +1122,7 @@ open external class PubNub(config: Any /* UUID | UserId */) {
11211122
var channel: String
11221123
var data: ChannelMetadata
11231124
var include: UuidIncludeCustom?
1125+
var ifMatchesEtag: String?
11241126
}
11251127

11261128
interface RemoveChannelMetadataParameters {

0 commit comments

Comments
 (0)