Skip to content

Commit

Permalink
feat: customMessageType (#128)
Browse files Browse the repository at this point in the history
* * `custom_message_type` support for subscription, publish, signal and files features.
* `custom_message_type` field parsing support in history apis.
* acceptance tests steps implementation for custom_message_type

* set default retry policy to `Exponential` with default configuration.
Fixed issue pointed out by PR #127 for limiting retry delay within maximum delay for linear policy

* fix: linting and warnings

* fix channel test for customMessageType

* PubNub SDK v5.0.0 release.

* update changelog

---------

Co-authored-by: PubNub Release Bot <[email protected]>
  • Loading branch information
mohitpubnub and pubnub-release-bot authored Dec 10, 2024
1 parent 13ff777 commit a333aaa
Show file tree
Hide file tree
Showing 29 changed files with 291 additions and 24 deletions.
11 changes: 10 additions & 1 deletion .pubnub.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
---
changelog:
- date: 2024-12-10
version: v5.0.0
changes:
- type: feature
text: "BREAKING CHANGES: Default retry policy for subscription is set to Exponential."
- type: feature
text: "support for customMessageType in subscription, history, publish, signal and files features."
- type: bug
text: "Limiting delay to maximum allowable value for Linear retry policy."
- date: 2024-04-15
version: v4.3.4
changes:
Expand Down Expand Up @@ -452,7 +461,7 @@ supported-platforms:
platforms:
- "Dart SDK >=2.6.0 <3.0.0"
version: "PubNub Dart SDK"
version: "4.3.4"
version: "5.0.0"
sdks:
-
full-name: PubNub Dart SDK
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import '../../world.dart';
import 'package:gherkin/gherkin.dart';

import 'step_given_keyset.dart';
import 'step_then_error_response.dart';
import 'step_then_messagesContainsType.dart';
import 'step_then_receive.dart';
import 'step_then_success_response.dart';
import 'step_when_publish_with_type.dart';
import 'step_when_sendFile.dart';
import 'step_when_signal_with_type.dart';
import 'step_when_subscribe.dart';

final List<StepDefinitionGeneric<PubNubWorld>> customMessageTypeSteps = [
StepGivenTheDemoKeyset(),
StepWhenIPublishWithCustomType(),
StepThenIReceiveSuccessfulResponsePublish(),
StepThenIReceivePublishErrorResponse(),
StepWhenISignalWithCustomType(),
StepWhenISubscribeChannalForCustomMessageType(),
StepThenIReceiveMessagesInSubscriptionResponse(),
StepThenReceivedMessagesHasMessageTypes(),
StepWhenISendFileCustomType(),
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:gherkin/gherkin.dart';
import 'package:pubnub/pubnub.dart';

import '../../world.dart';

class StepGivenTheDemoKeyset extends GivenWithWorld<PubNubWorld> {
@override
RegExp get pattern => RegExp(r'the demo keyset');

@override
Future<void> executeStep() async {
world.keyset = Keyset(
publishKey: 'demo',
subscribeKey: 'demo',
userId: UserId('testCustomType')
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:gherkin/gherkin.dart';
import 'package:pubnub/pubnub.dart';
import 'package:test/test.dart';

import '../../world.dart';

class StepThenIReceivePublishErrorResponse extends ThenWithWorld<PubNubWorld> {
@override
RegExp get pattern => RegExp(r'I receive an error response');

@override
Future<void> executeStep() async {
if(world.latestResultType == 'sendFile') {
var result = world.latestResult as PublishFileMessageResult;
this.expect(result.description?.toLowerCase(), contains('invalid_type'));
} else {
var result = world.latestResult as PublishException;
this.expect(result.message.toLowerCase(), contains('invalid_type'));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:gherkin/gherkin.dart';
import 'package:test/expect.dart';

import '../../world.dart';

class StepThenReceivedMessagesHasMessageTypes
extends Then2WithWorld<String, String, PubNubWorld> {
@override
RegExp get pattern => RegExp(r'response contains messages with {string} and {string} types');

@override
Future<void> executeStep(String customMessageTypeOne, String customMessageTypeTwo) async {
world.messages.forEach((message) {
this.expect(message.customMessageType, anyOf([customMessageTypeOne, customMessageTypeTwo]));
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:gherkin/gherkin.dart';

import '../../world.dart';

class StepThenIReceiveMessagesInSubscriptionResponse
extends Then1WithWorld<int, PubNubWorld> {
@override
RegExp get pattern => RegExp(r'I receive {int} messages in my subscribe response');

@override
Future<void> executeStep(int count) async {
expect(world.messages.length, 2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:gherkin/gherkin.dart';
import 'package:test/expect.dart';

import '../../world.dart';

class StepThenIReceiveSuccessfulResponsePublish extends ThenWithWorld<PubNubWorld> {
@override
RegExp get pattern => RegExp(r'I receive a successful response');

@override
Future<void> executeStep() async {
this.expect(world.latestResultType, isNotNull);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:gherkin/gherkin.dart';

import '../../world.dart';

class StepWhenIPublishWithCustomType extends When1WithWorld<String, PubNubWorld> {
@override
RegExp get pattern => RegExp(r'I publish message with {string} customMessageType');

@override
Future<void> executeStep(String customMesageType) async {
try {
world.latestResult = await world.pubnub.publish(
'test',
'hello',
keyset: world.keyset,
customMessageType: customMesageType,
);
world.latestResultType = 'publish';
} catch (e) {
world.latestResultType = 'publishWithCustomTypeFailure';
world.latestResult = e;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:gherkin/gherkin.dart';

import '../../world.dart';

class StepWhenISendFileCustomType
extends When1WithWorld<String, PubNubWorld> {
@override
RegExp get pattern =>
RegExp(r'I send a file with {string} customMessageType');

@override
Future<void> executeStep(String customMesageType) async {
try {
world.latestResult = await world.pubnub.files.sendFile('test', 'helloFile.txt', [12,16], customMessageType: customMesageType);
world.latestResultType = 'sendFile';
} catch (e) {
world.latestResultType = 'sendFileFailure';
world.latestResult = e;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:gherkin/gherkin.dart';

import '../../world.dart';

class StepWhenISignalWithCustomType
extends When1WithWorld<String, PubNubWorld> {
@override
RegExp get pattern =>
RegExp(r'I send a signal with {string} customMessageType');

@override
Future<void> executeStep(String customMesageType) async {
try {
world.latestResult = await world.pubnub.signal(
'test',
'hello',
keyset: world.keyset,
customMessageType: customMesageType,
);
world.latestResultType = 'publish';
} catch (e) {
world.latestResultType = 'publishWithCustomTypeFailure';
world.latestResult = e;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:gherkin/gherkin.dart';

import '../../world.dart';

class StepWhenISubscribeChannalForCustomMessageType
extends When1WithWorld<String, PubNubWorld> {
@override
RegExp get pattern =>
RegExp(r'I subscribe to {string} channel');

@override
Future<void> executeStep(String channel) async {
try {
var subscription = world.pubnub.subscribe(channels: {channel});
subscription.messages.listen((messageEnvelope) {
world.messages.add(messageEnvelope);
});
await Future.delayed(Duration(seconds: 2), () {
subscription.dispose();
});
world.latestResultType = 'subscription';
} catch (e) {
world.latestResultType = 'subscriptionFailure';
world.latestResult = e;
}
}
}
2 changes: 2 additions & 0 deletions acceptance_tests/lib/src/steps/steps.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:acceptance_tests/src/steps/customMessageType/customMessageType_steps.dart';
import 'package:gherkin/gherkin.dart';

import '../world.dart';
Expand Down Expand Up @@ -26,6 +27,7 @@ import 'steps_push.dart';
final List<StepDefinitionGeneric<PubNubWorld>> steps = [
...cryptoSteps,
...pamv3Steps,
...customMessageTypeSteps,
StepGivenChannel(),
StepGivenDemoKeyset(),
StepWhenIAddAMessageAction(),
Expand Down
1 change: 1 addition & 0 deletions acceptance_tests/lib/src/world.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class PubNubWorld extends World {
),
networking: NetworkingModule(origin: 'localhost:8090', ssl: false),
);
pubnub.keysets.defaultKeyset.fileMessagePublishRetryLimit = 0;
}

Future<void> cleanup() async {
Expand Down
6 changes: 6 additions & 0 deletions pubnub/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## v5.0.0
December 10 2024

#### Added
- BREAKING CHANGES: support for customMessageType in subscription, history, publish, signal and files features.

## v4.3.4
April 15 2024

Expand Down
2 changes: 1 addition & 1 deletion pubnub/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ To add the package to your Dart or Flutter project, add `pubnub` as a dependency

```yaml
dependencies:
pubnub: ^4.3.4
pubnub: ^5.0.0
```
After adding the dependency to `pubspec.yaml`, run the `dart pub get` command in the root directory of your project (the same that the `pubspec.yaml` is in).
Expand Down
2 changes: 1 addition & 1 deletion pubnub/lib/src/core/core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Core {
/// Internal module responsible for supervising.
SupervisorModule supervisor = SupervisorModule();

static String version = '4.3.4';
static String version = '5.0.0';

Core(
{Keyset? defaultKeyset,
Expand Down
9 changes: 5 additions & 4 deletions pubnub/lib/src/core/policies/retry_policy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ class LinearRetryPolicy extends RetryPolicy {
const LinearRetryPolicy({int? backoff, int? maxRetries, int? maximumDelay})
: backoff = backoff ?? 5,
maximumDelay = maximumDelay ?? 60000,
super(maxRetries ?? 5);
super(maxRetries ?? 10);

@override
Duration getDelay(Fiber fiber) {
return Duration(
milliseconds: (fiber.tries * backoff) + Random().nextInt(1000));
milliseconds: min(
maximumDelay, (fiber.tries * backoff) + Random().nextInt(1000)));
}
}

Expand All @@ -53,8 +54,8 @@ class ExponentialRetryPolicy extends RetryPolicy {
final int maximumDelay;

const ExponentialRetryPolicy({int? maxRetries, int? maximumDelay})
: maximumDelay = maximumDelay ?? 60000,
super(maxRetries ?? 5);
: maximumDelay = maximumDelay ?? 150000,
super(maxRetries ?? 6);

@override
Duration getDelay(Fiber fiber) {
Expand Down
7 changes: 5 additions & 2 deletions pubnub/lib/src/dx/_endpoints/files.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,11 @@ class PublishFileMessageParams extends Parameters {
bool? storeMessage;
int? ttl;
String? meta;
String? customMessageType;

PublishFileMessageParams(this.keyset, this.channel, this.message,
{this.storeMessage, this.meta, this.ttl});
{this.storeMessage, this.meta, this.ttl, this.customMessageType});

@override
Request toRequest() {
var pathSegments = [
Expand All @@ -98,7 +100,8 @@ class PublishFileMessageParams extends Parameters {
'store': '0',
'uuid': keyset.uuid.value,
if (ttl != null) 'ttl': ttl.toString(),
if (meta != null) 'meta': meta
if (meta != null) 'meta': meta,
if (customMessageType != null) 'custom_message_type': customMessageType,
};
return Request.get(
uri: Uri(pathSegments: pathSegments, queryParameters: queryParameters));
Expand Down
18 changes: 15 additions & 3 deletions pubnub/lib/src/dx/_endpoints/history.dart
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,19 @@ class BatchHistoryResultEntry {
/// for given `message`.
PubNubException? error;

BatchHistoryResultEntry._(this.message, this.timetoken, this.uuid,
this.messageType, this.actions, this.meta, this.error);
/// If message has customMessageType, this will contain it.
/// Otherwise, it will be `null`.
String? customMessageType;

BatchHistoryResultEntry._(
this.message,
this.timetoken,
this.uuid,
this.messageType,
this.actions,
this.meta,
this.error,
this.customMessageType);

/// @nodoc
factory BatchHistoryResultEntry.fromJson(Map<String, dynamic> object,
Expand Down Expand Up @@ -195,7 +206,8 @@ class BatchHistoryResultEntry {
MessageTypeExtension.fromInt(object['message_type']),
object['actions'],
object['meta'] == '' ? null : object['meta'],
error);
error,
object['custom_message_type']);
}
}

Expand Down
7 changes: 6 additions & 1 deletion pubnub/lib/src/dx/_endpoints/publish.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ class PublishParams extends Parameters {
bool? storeMessage;
int? ttl;
bool? noReplication;
String? customMessageType;

PublishParams(this.keyset, this.channel, this.message,
{this.storeMessage, this.ttl, this.noReplication});
{this.storeMessage,
this.ttl,
this.noReplication,
this.customMessageType});

@override
Request toRequest() {
Expand All @@ -33,6 +37,7 @@ class PublishParams extends Parameters {
if (meta != null) 'meta': meta,
if (noReplication != null && noReplication == true) 'norep': 'true',
if (keyset.authKey != null) 'auth': keyset.authKey,
if (customMessageType != null) 'custom_message_type': customMessageType,
'uuid': keyset.uuid.value,
if (ttl != null) 'ttl': ttl.toString()
};
Expand Down
Loading

0 comments on commit a333aaa

Please sign in to comment.