From 24d6dcabb0a1e8342f8a487756fff0248473f0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Kalici=C5=84ski?= Date: Fri, 16 Aug 2024 11:27:53 +0200 Subject: [PATCH] Java SDK now depends on Kotlin SDK --- .../gradle/PubNubKotlinLibraryPlugin.kt | 5 +- .../gradle/PubNubKotlinMultiplatformPlugin.kt | 1 + build.gradle.kts | 1 + gradle.properties | 4 +- migration_utils/replace_in_file.sh | 13 + migration_utils/replacements.txt | 193 + migration_utils/upgrade_v10.sh | 40 + .../api/endpoints/HasOverridableConfig.kt | 5 +- .../com/pubnub/api/v2/BasePNConfiguration.kt | 6 +- .../api/v2/BasePNConfigurationOverride.kt | 2 +- pubnub-core/pubnub-core-impl/build.gradle.kts | 2 +- .../internal/vendor/AppEngineFactory.java | 10 +- .../com/pubnub/internal/BasePubNubImpl.kt | 8 +- .../com/pubnub/internal/EndpointCore.kt | 9 +- .../kotlin/com/pubnub/internal/PubNubCore.kt | 23 +- .../kotlin/com/pubnub/internal/PubNubUtil.kt | 7 +- .../endpoints/access/GrantEndpoint.kt | 2 +- .../endpoints/access/GrantTokenEndpoint.kt | 2 +- .../endpoints/access/RevokeTokenEndpoint.kt | 2 +- .../interceptor/SignatureInterceptor.kt | 5 +- .../internal/managers/BasePathManager.kt | 5 +- .../internal/managers/DuplicationManager.kt | 3 +- .../internal/managers/ListenerManager.kt | 15 +- .../internal/managers/RetrofitManager.kt | 5 +- .../internal/v2/BasePNConfigurationImpl.kt | 9 +- .../internal/v2/callbacks/EventEmitterImpl.kt | 25 +- .../v2/callbacks/EventListenerCore.kt | 13 +- .../v2/callbacks/StatusListenerCore.kt | 3 +- .../workers/SubscribeMessageProcessor.kt | 5 +- .../pubnub/api/BasePNConfigurationImplTest.kt | 6 +- .../api/endpoints/access/GrantTokenTest.kt | 4 +- .../api/endpoints/pubsub/PublishTest.kt | 6 +- .../api/legacy/endpoints/EndpointCoreTest.kt | 6 +- .../legacy/endpoints/files/SendFileTest.kt | 3 +- .../message_actions/ReceiveMessageActions.kt | 21 +- .../api/legacy/endpoints/pubsub/SignalTest.kt | 11 +- .../managers/SubscriptionManagerTest.kt | 139 +- .../pubnub/contract/access/step/ThenSteps.kt | 8 +- .../pubnub/contract/access/step/WhenSteps.kt | 14 +- .../channelmetadata/step/WhenSteps.kt | 12 +- .../pubnub/contract/member/step/WhenSteps.kt | 12 +- .../contract/membership/step/WhenSteps.kt | 16 +- .../step/PresenceEventEngineSteps.kt | 13 +- .../eventEngine/step/EventEngineSteps.kt | 33 +- .../contract/uuidmetadata/step/WhenSteps.kt | 14 +- .../kotlin/com/pubnub/internal/TestPubNub.kt | 3 +- .../v2/callbacks/EventEmitterImplTest.kt | 33 +- .../v2/entities/BaseChannelGroupImplTest.kt | 4 +- .../v2/entities/BaseChannelImplTest.kt | 4 +- .../subscription/BaseSubscriptionImplTest.kt | 23 +- .../BaseSubscriptionSetImplTest.kt | 25 +- .../workers/SubscribeMessageProcessorTest.kt | 9 +- .../kotlin/com/pubnub/test/SignatureUtils.kt | 7 +- .../pubnub/test/TestPNConfigurationImpl.kt | 10 +- pubnub-gson/build.gradle.kts | 4 +- pubnub-gson/pubnub-gson-api/build.gradle.kts | 4 +- .../java/com/pubnub/api/PNConfiguration.kt | 700 ---- .../api/callbacks/SubscribeCallback.java | 15 - .../com/pubnub/api/endpoints/Endpoint.java | 23 - .../java/com/pubnub/api/endpoints/Time.java | 6 - .../api/endpoints/access/GrantToken.java | 18 - .../channel_groups/ListAllChannelGroup.java | 7 - .../memberships/GetMemberships.java | 22 - .../objects_api/uuid/GetUUIDMetadata.java | 10 - .../objects_api/uuid/RemoveUUIDMetadata.java | 8 - .../api/endpoints/presence/WhereNow.java | 8 - .../api/{PubNub.kt => java/PubNubForJava.kt} | 276 +- .../{ => java}/PubNubRuntimeException.java | 3 +- .../com/pubnub/api/{ => java}/SpaceId.java | 2 +- .../{ => java}/builder/PresenceBuilder.java | 6 +- .../builder/PubNubErrorBuilder.java | 2 +- .../api/{ => java}/builder/PubSubBuilder.java | 8 +- .../{ => java}/builder/SubscribeBuilder.java | 6 +- .../builder/UnsubscribeBuilder.java | 6 +- .../ChangeTemporaryUnavailableOperation.java | 2 +- .../builder/dto/PresenceOperation.java | 2 +- .../builder/dto/PubSubOperation.java | 2 +- .../builder/dto/StateOperation.java | 2 +- .../dto/TimetokenAndRegionOperation.java | 2 +- .../builder/dto/UnsubscribeOperation.java | 2 +- .../api/{ => java}/callbacks/PNCallback.java | 2 +- .../api/java/callbacks/SubscribeCallback.kt | 13 + .../{ => java}/endpoints/BuilderSteps.java | 2 +- .../{ => java}/endpoints/DeleteMessages.java | 2 +- .../com/pubnub/api/java/endpoints/Endpoint.kt | 25 + .../{ => java}/endpoints/FetchMessages.java | 2 +- .../api/{ => java}/endpoints/History.java | 2 +- .../{ => java}/endpoints/MessageCounts.java | 2 +- .../{ => java}/endpoints/access/Grant.java | 6 +- .../api/java/endpoints/access/GrantToken.java | 23 + .../endpoints/access/RevokeToken.java | 4 +- .../builder/AbstractGrantTokenBuilder.java | 4 +- .../access/builder/GrantTokenBuilder.java | 16 +- .../builder/GrantTokenEntitiesBuilder.java | 8 +- .../builder/GrantTokenObjectsBuilder.java | 12 +- .../AddChannelChannelGroup.java | 4 +- .../AllChannelsChannelGroup.java | 4 +- .../channel_groups/DeleteChannelGroup.java | 4 +- .../RemoveChannelChannelGroup.java | 4 +- .../endpoints/files/DeleteFile.java | 8 +- .../endpoints/files/DownloadFile.java | 8 +- .../endpoints/files/GetFileUrl.java | 8 +- .../{ => java}/endpoints/files/ListFiles.java | 6 +- .../endpoints/files/PublishFileMessage.java | 8 +- .../{ => java}/endpoints/files/SendFile.java | 6 +- .../FilesBuilderSteps.java | 4 +- .../message_actions/AddMessageAction.java | 4 +- .../message_actions/GetMessageActions.java | 4 +- .../message_actions/RemoveMessageAction.java | 4 +- .../channel/GetAllChannelsMetadata.java | 9 +- .../channel/GetChannelMetadata.java | 8 +- .../channel/RemoveChannelMetadata.java | 8 +- .../channel/SetChannelMetadata.java | 8 +- .../members/GetChannelMembers.java | 14 +- .../members/ManageChannelMembers.java | 16 +- .../members/RemoveChannelMembers.java | 16 +- .../members/SetChannelMembers.java | 16 +- .../memberships/GetMemberships.java | 24 + .../memberships/ManageMemberships.java | 20 +- .../memberships/RemoveMemberships.java | 18 +- .../memberships/SetMemberships.java | 14 +- .../endpoints/objects_api/utils/Include.java | 2 +- .../utils/ObjectsBuilderSteps.java | 8 +- .../objects_api/utils/PNSortKey.java | 6 +- .../objects_api/uuid/GetAllUUIDMetadata.java | 9 +- .../objects_api/uuid/GetUUIDMetadata.java | 10 + .../objects_api/uuid/RemoveUUIDMetadata.java | 8 + .../objects_api/uuid/SetUUIDMetadata.java | 6 +- .../endpoints/presence/GetState.java | 4 +- .../endpoints/presence/Heartbeat.java | 4 +- .../endpoints/presence/HereNow.java | 4 +- .../{ => java}/endpoints/presence/Leave.java | 4 +- .../endpoints/presence/SetState.java | 4 +- .../api/java/endpoints/presence/WhereNow.java | 8 + .../{ => java}/endpoints/pubsub/Publish.java | 4 +- .../{ => java}/endpoints/pubsub/Signal.java | 4 +- .../endpoints/push/AddChannelsToPush.java | 4 +- .../endpoints/push/ListPushProvisions.java | 4 +- .../push/RemoveAllPushChannelsForDevice.java | 4 +- .../push/RemoveChannelsFromPush.java | 4 +- .../PNAccessManagerGrantResult.java | 10 +- .../PNAccessManagerKeyData.java | 4 +- .../PNAccessManagerKeysData.java | 2 +- .../access_manager/sum/SpacePermissions.java | 6 +- .../access_manager/sum/UserPermissions.java | 4 +- .../access_manager/v3/ChannelGrant.java | 2 +- .../access_manager/v3/ChannelGroupGrant.java | 2 +- .../access_manager/v3/PNResource.java | 2 +- .../v3/PNRevokeTokenResult.java | 2 +- .../consumer/access_manager/v3/UUIDGrant.java | 2 +- .../objects_api/EntityArrayEnvelope.java | 2 +- .../consumer/objects_api/EntityEnvelope.java | 2 +- .../models/consumer/objects_api/PNObject.java | 2 +- .../channel/PNChannelMetadata.java | 4 +- .../channel/PNChannelMetadataResult.java | 2 +- .../PNGetAllChannelsMetadataResult.java | 6 +- .../channel/PNGetChannelMetadataResult.java | 4 +- .../PNRemoveChannelMetadataResult.java | 4 +- .../channel/PNSetChannelMetadataResult.java | 4 +- .../member/PNGetChannelMembersResult.java | 6 +- .../member/PNManageChannelMembersResult.java | 6 +- .../objects_api/member/PNMembers.java | 6 +- .../member/PNRemoveChannelMembersResult.java | 6 +- .../member/PNSetChannelMembersResult.java | 6 +- .../consumer/objects_api/member/PNUUID.java | 2 +- .../membership/PNChannelMembership.java | 2 +- .../membership/PNGetMembershipsResult.java | 6 +- .../membership/PNManageMembershipResult.java | 6 +- .../objects_api/membership/PNMembership.java | 12 +- .../membership/PNMembershipResult.java | 2 +- .../membership/PNRemoveMembershipResult.java | 6 +- .../membership/PNSetMembershipResult.java | 6 +- .../uuid/PNGetAllUUIDMetadataResult.java | 6 +- .../uuid/PNGetUUIDMetadataResult.java | 4 +- .../uuid/PNRemoveUUIDMetadataResult.java | 4 +- .../uuid/PNSetUUIDMetadataResult.java | 4 +- .../objects_api/uuid/PNUUIDMetadata.java | 4 +- .../uuid/PNUUIDMetadataResult.java | 2 +- .../api/{ => java}/v2/PNConfiguration.kt | 301 +- .../api/java/v2/callbacks/EventEmitter.kt | 243 ++ .../api/java/v2/callbacks/EventListener.kt | 46 + .../api/java/v2/callbacks/StatusEmitter.kt | 28 + .../api/java/v2/callbacks/StatusListener.kt | 22 + .../handlers/OnChannelMetadataHandler.java | 4 +- .../v2/callbacks/handlers/OnFileHandler.java | 2 +- .../handlers/OnMembershipHandler.java | 4 +- .../handlers/OnMessageActionHandler.java | 2 +- .../callbacks/handlers/OnMessageHandler.java | 2 +- .../callbacks/handlers/OnPresenceHandler.java | 2 +- .../callbacks/handlers/OnSignalHandler.java | 2 +- .../handlers/OnUuidMetadataHandler.java | 4 +- .../v2/endpoints/pubsub/PublishBuilder.java | 4 +- .../v2/endpoints/pubsub/SignalBuilder.java | 6 + .../api/{ => java}/v2/entities/Channel.kt | 43 +- .../api/java/v2/entities/ChannelGroup.kt | 60 + .../api/java/v2/entities/ChannelMetadata.kt | 41 + .../api/java/v2/entities/Subscribable.kt | 18 + .../api/java/v2/entities/UserMetadata.kt | 41 + .../v2/subscriptions/Subscription.kt | 13 +- .../java/v2/subscriptions/SubscriptionSet.kt | 65 + .../pubnub/api/v2/callbacks/EventEmitter.java | 223 -- .../api/v2/callbacks/EventListener.java | 45 - .../api/v2/callbacks/StatusEmitter.java | 8 - .../api/v2/callbacks/StatusListener.java | 14 - .../v2/endpoints/pubsub/SignalBuilder.java | 7 - .../pubnub/api/v2/entities/ChannelGroup.kt | 23 - .../pubnub/api/v2/entities/ChannelMetadata.kt | 23 - .../pubnub/api/v2/entities/UserMetadata.kt | 23 - .../api/v2/subscriptions/SubscriptionSet.kt | 26 - pubnub-gson/pubnub-gson-impl/build.gradle.kts | 4 +- .../config/ktlint/baseline.xml | 6 + .../integration/FilesIntegrationTests.java | 7 +- .../integration/HeartbeatIntegrationTest.java | 9 +- .../integration/HistoryIntegrationTest.java | 15 +- .../api/integration/MessageActionsTest.java | 53 +- .../integration/PAMFilesIntegrationTests.java | 5 +- .../PNConfigurationIntegrationTests.java | 7 +- .../PresenceEventsIntegrationTests.java | 164 +- .../integration/PresenceIntegrationTests.java | 27 +- .../integration/PublishIntegrationTests.java | 294 +- .../RetryConfigurationIntegrationTest.java | 6 +- .../integration/SignalIntegrationTests.java | 57 +- .../StreamFilteringIntegrationTests.java | 194 +- .../SubscribeIntegrationTests.java | 145 +- .../AbstractReconnectionProblemIT.java | 115 +- ...ectionProblemWithReconnectionPolicyIT.java | 27 +- ...ionProblemWithoutReconnectionPolicyIT.java | 26 +- .../SubscribeCallbackAdapter.java | 61 - .../integration/objects/ObjectsApiBaseIT.java | 17 +- .../objects/ObjectsApiSubscriptionIT.java | 63 +- .../objects/channel/ChannelMetadataIT.java | 10 +- .../objects/members/ChannelMembersIT.java | 16 +- .../CustomMetadataInMembersPropagationIT.java | 10 +- ...stomMetadataInMembershipPropagationIT.java | 28 +- .../objects/memberships/MembershipIT.java | 14 +- .../objects/uuid/UUIDMetadataIT.java | 8 +- .../pam/AccessManagerIntegrationTest.java | 50 +- .../pubnub/api/integration/pam/GrantIT.java | 4 +- .../api/integration/pam/GrantTokenIT.java | 16 +- .../integration/util/BaseIntegrationTest.java | 104 +- .../pubnub/api/integration/util/Utils.java | 7 +- .../java/com/pubnub/internal/PubNubImpl.java | 673 ---- .../pubnub/internal/endpoints/TimeImpl.java | 20 - .../ListAllChannelGroupImpl.java | 23 - .../pubnub/internal/java/PubNubForJavaImpl.kt | 429 +++ .../endpoints/DelegatingEndpoint.kt | 13 +- .../endpoints/DelegatingRemoteAction.kt | 8 +- .../endpoints/DeleteMessagesImpl.java | 14 +- .../endpoints/FetchMessagesImpl.java | 15 +- .../{ => java}/endpoints/HistoryImpl.java | 14 +- .../endpoints/MessageCountsImpl.java | 14 +- .../endpoints/access/GrantImpl.java | 22 +- .../endpoints/access/GrantTokenImpl.java | 62 +- .../endpoints/access/RevokeTokenImpl.java | 16 +- .../AddChannelChannelGroupImpl.java | 16 +- .../AllChannelsChannelGroupImpl.java | 16 +- .../DeleteChannelGroupImpl.java | 16 +- .../RemoveChannelChannelGroupImpl.java | 16 +- .../endpoints/files/DeleteFileImpl.java | 28 +- .../endpoints/files/DownloadFileImpl.java | 26 +- .../endpoints/files/GetFileUrlImpl.java | 26 +- .../endpoints/files/ListFilesImpl.java | 20 +- .../files/PublishFileMessageImpl.java | 26 +- .../endpoints/files/SendFileImpl.java | 22 +- .../ChannelFileNameFileIdBuilder.java | 8 +- .../message_actions/AddMessageActionImpl.java | 22 +- .../GetMessageActionsImpl.java | 14 +- .../RemoveMessageActionImpl.java | 14 +- .../channel/GetAllChannelsMetadataImpl.java | 34 +- .../channel/GetChannelMetadataImpl.java | 26 +- .../channel/RemoveChannelMetadataImpl.java | 26 +- .../channel/SetChannelMetadataImpl.java | 26 +- .../members/GetChannelMembersImpl.java | 30 +- .../members/ManageChannelMembersImpl.java | 38 +- .../members/RemoveChannelMembersImpl.java | 34 +- .../members/SetChannelMembersImpl.java | 50 +- .../memberships/GetMembershipsImpl.java | 22 +- .../memberships/ManageMembershipsImpl.java | 36 +- .../memberships/RemoveMembershipsImpl.java | 32 +- .../memberships/SetMembershipsImpl.java | 52 +- .../uuid/GetAllUUIDMetadataImpl.java | 22 +- .../objects_api/uuid/GetUUIDMetadataImpl.java | 20 +- .../uuid/RemoveUUIDMetadataImpl.java | 18 +- .../objects_api/uuid/SetUUIDMetadataImpl.java | 23 +- .../endpoints/presence/GetStateImpl.java | 14 +- .../endpoints/presence/HeartbeatImpl.java | 17 +- .../endpoints/presence/HereNowImpl.java | 14 +- .../endpoints/presence/LeaveImpl.java | 18 +- .../endpoints/presence/SetStateImpl.java | 25 +- .../endpoints/presence/WhereNowImpl.java | 16 +- .../endpoints/pubsub/PublishImpl.java | 18 +- .../endpoints/pubsub/SignalImpl.java | 18 +- .../endpoints/push/AddChannelsToPushImpl.java | 17 +- .../push/ListPushProvisionsImpl.java | 17 +- .../RemoveAllPushChannelsForDeviceImpl.java | 17 +- .../push/RemoveChannelsFromPushImpl.java | 17 +- .../{ => java}/v2/PNConfigurationImpl.kt | 213 +- .../v2/callbacks/Converters.java} | 128 +- .../v2/callbacks/DelegatingEventListener.kt | 53 + .../v2/callbacks/DelegatingStatusListener.kt | 12 + .../java/v2/callbacks/EventEmitterInternal.kt | 269 ++ .../java/v2/entities/ChannelGroupImpl.kt | 20 + .../internal/java/v2/entities/ChannelImpl.kt | 33 + .../java/v2/entities/ChannelMetadataImpl.kt | 20 + .../java/v2/entities/UserMetadataImpl.kt | 20 + .../java/v2/subscription/EmitterHelper.kt | 96 + .../java/v2/subscription/SubscriptionImpl.kt | 60 + .../v2/subscription/SubscriptionSetImpl.kt | 63 + .../v2/callbacks/DelegatingStatusListener.kt | 16 - .../callbacks/DelegatingSubscribeCallback.kt | 22 - .../internal/v2/entities/ChannelGroupImpl.kt | 20 - .../internal/v2/entities/ChannelImpl.kt | 36 - .../v2/entities/ChannelMetadataImpl.kt | 20 - .../internal/v2/entities/UserMetadataImpl.kt | 20 - .../internal/v2/subscription/EmitterHelper.kt | 96 - .../v2/subscription/SubscriptionImpl.kt | 112 - .../v2/subscription/SubscriptionSetImpl.kt | 92 - .../com/pubnub/api/PNConfigurationTest.kt | 211 +- .../endpoints/DelegatingEndpointTest.kt | 94 +- .../endpoints/DelegatingRemoteActionTest.kt | 2 +- .../endpoints/DeleteMessagesImplTest.kt | 6 +- .../endpoints/FetchMessagesImplTest.kt | 6 +- .../endpoints/access/GrantTokenImplTest.kt | 25 +- .../{ => java}/v2/PNConfigurationImplTest.kt | 25 +- .../v2/entities/ChannelGroupImplTest.kt | 14 +- .../{ => java}/v2/entities/ChannelImplTest.kt | 11 +- .../v2/subscription/SubscriptionImplTest.kt | 187 + .../subscription/SubscriptionSetImplTest.kt | 157 + .../callbacks/DelegatingEventListenerTest.kt | 318 -- .../callbacks/DelegatingStatusListenerTest.kt | 24 - .../DelegatingSubscribeCallbackTest.kt | 38 - .../v2/subscription/SubscriptionImplTest.kt | 160 - .../subscription/SubscriptionSetImplTest.kt | 106 - pubnub-kotlin/build.gradle.kts | 4 +- .../pubnub-kotlin-api/build.gradle.kts | 13 +- .../kotlin/com/pubnub/api/JsonElement.kt | 32 + .../kotlin/com/pubnub/api/PubNub.kt | 2 +- .../kotlin/com/pubnub/api/PubNubError.kt | 244 ++ .../kotlin/com/pubnub/api/PubNubException.kt | 23 + .../kotlin/com/pubnub/api/UserId.kt | 14 + .../com/pubnub/api/callbacks/Listener.kt | 6 + .../endpoints/remoteaction/RemoteAction.kt | 56 + .../enums/PNHeartbeatNotificationOptions.kt | 18 + .../com/pubnub/api/enums/PNLogVerbosity.kt | 17 + .../com/pubnub/api/enums/PNOperationType.kt | 121 + .../com/pubnub/api/enums/PNPushEnvironment.kt | 11 + .../kotlin/com/pubnub/api/enums/PNPushType.kt | 18 + .../pubnub/api/enums/PNReconnectionPolicy.kt | 25 + .../com/pubnub/api/enums/PNStatusCategory.kt | 54 + .../com/pubnub/api/models/TokenBitmask.kt | 12 + .../api/models/consumer/PNBoundedPage.kt | 16 + .../api/models/consumer/PNPublishResult.kt | 14 + .../pubnub/api/models/consumer/PNStatus.kt | 56 + .../api/models/consumer/PNTimeResult.kt | 10 + .../PNAccessManagerGrantResults.kt | 14 + .../consumer/access_manager/v3/Grants.kt | 6 +- .../access_manager/v3/PNGrantTokenResult.kt | 3 + .../consumer/access_manager/v3/PNToken.kt | 39 + .../channel_group/PNChannelGroupsResults.kt | 34 + .../consumer/files/PNDeleteFileResult.kt | 3 + .../consumer/files/PNDownloadFileResult.kt | 8 + .../consumer/files/PNDownloadableFile.kt | 7 + .../api/models/consumer/files/PNFile.kt | 18 + .../consumer/files/PNFileUploadResult.kt | 7 + .../models/consumer/files/PNFileUrlResult.kt | 3 + .../consumer/files/PNListFilesResult.kt | 10 + .../files/PNPublishFileMessageResult.kt | 3 + .../consumer/history/HistoryMessageType.kt | 18 + .../history/PNDeleteMessagesResult.kt | 6 + .../models/consumer/history/PNFetchMessage.kt | 84 + .../consumer/history/PNHistoryResult.kt | 39 + .../consumer/history/PNMessageCountResult.kt | 11 + .../PNAddMessageActionResult.kt | 9 + .../PNGetMessageActionsResult.kt | 18 + .../message_actions/PNMessageAction.kt | 91 + .../PNRemoveMessageActionResult.kt | 6 + .../api/models/consumer/objects/PNPage.kt | 12 + .../objects/channel/PNChannelMetadata.kt | 29 + .../channel/PNChannelMetadataArrayResult.kt | 11 + .../channel/PNChannelMetadataResult.kt | 6 + .../consumer/objects/uuid/PNUUIDMetadata.kt | 34 + .../consumer/presence/PNGetStateResult.kt | 12 + .../api/models/consumer/presence/PNHereNow.kt | 41 + .../consumer/presence/PNSetStateResult.kt | 12 + .../consumer/pubsub/BasePubSubResult.kt | 26 + .../models/consumer/pubsub/MessageResult.kt | 10 + .../api/models/consumer/pubsub/PNEvent.kt | 7 + .../models/consumer/pubsub/PNMessageResult.kt | 46 + .../consumer/pubsub/PNPresenceEventResult.kt | 113 + .../models/consumer/pubsub/PNSignalResult.kt | 11 + .../pubsub/files/PNFileEventResult.kt | 71 + .../message_actions/PNMessageActionResult.kt | 57 + .../consumer/pubsub/objects/ObjectPayload.kt | 11 + .../consumer/pubsub/objects/ObjectResult.kt | 6 + .../consumer/push/PNPushAddChannelResult.kt | 6 + .../push/PNPushListProvisionsResult.kt | 5 + .../push/PNPushRemoveAllChannelsResult.kt | 3 + .../push/PNPushRemoveChannelResult.kt | 3 + .../push/payload/PushPayloadHelper.kt | 465 +++ .../push/payload/PushPayloadSerializer.kt | 5 + .../pubnub/api/retry/RetryConfiguration.kt | 147 + .../api/retry/RetryableEndpointGroup.kt | 67 + .../kotlin/com/pubnub/api/utils/PatchValue.kt | 17 + .../com/pubnub/api/utils/SerializedName.kt | 7 + .../com/pubnub/api/v2/callbacks/Consumer.kt | 5 + .../pubnub/api/v2/callbacks/EventEmitter.kt | 34 +- .../pubnub/api/v2/callbacks/EventListener.kt | 4 +- .../com/pubnub/api/v2/callbacks/Result.kt | 252 ++ .../com/pubnub/api/v2/entities/Channel.kt | 29 +- .../pubnub/api/v2/entities/ChannelGroup.kt | 40 +- .../pubnub/api/v2/entities/ChannelMetadata.kt | 21 +- .../pubnub/api/v2/entities/Subscribable.kt | 19 + .../pubnub/api/v2/entities/UserMetadata.kt | 21 +- .../api/v2/subscriptions/SubscribeCapable.kt | 26 + .../api/v2/subscriptions/Subscription.kt | 3 +- .../v2/subscriptions/SubscriptionCursor.kt | 10 + .../v2/subscriptions/SubscriptionOptions.kt | 61 + .../api/v2/subscriptions/SubscriptionSet.kt | 28 +- .../kotlin/com/pubnub/kmp/Downloadable.kt | 3 + .../kotlin/com/pubnub/kmp/PNFuture.kt | 8 + .../kotlin/com/pubnub/kmp/futures.kt | 149 + .../kotlin/com/pubnub/kmp/JsonElementTest.kt | 17 + .../kotlin/com/pubnub/kmp/PNFutureTest.kt | 63 + .../kotlin/com/pubnub/api/JsonElement.ios.kt | 126 + .../com/pubnub/api/PubNubException.ios.kt | 32 + .../PNGetMessageActionsResult.ios.kt | 8 + .../pubnub/api/v2/callbacks/Consumer.ios.kt | 5 + .../kotlin/com/pubnub/kmp/Downloadable.ios.kt | 9 + .../kotlin/com/pubnub/api/JsonElement.js.kt | 125 + .../com/pubnub/api/PubNubException.js.kt | 33 + .../PNGetMessageActionsResult.js.kt | 8 + .../pubnub/api/v2/callbacks/Consumer.js.kt | 5 + .../kotlin/com/pubnub/kmp/Downloadable.js.kt | 5 + .../src/jsMain/kotlin/com/pubnub/kmp/JsMap.kt | 19 + .../kotlin/com/pubnub/api/JsonElement.jvm.kt | 69 + .../kotlin/com/pubnub/api/PNConfiguration.kt | 394 -- .../jvmMain/kotlin/com/pubnub/api/PubNub.kt | 296 +- .../kotlin/com/pubnub/api/PubNubException.kt | 68 + .../com/pubnub/api/crypto/CryptoModule.kt | 77 + .../com/pubnub/api/crypto/cryptor/Cryptor.kt | 17 + .../pubnub/api/crypto/data/EncryptedData.kt | 6 + .../api/crypto/data/EncryptedStreamData.kt | 8 + .../api/endpoints/push/AddChannelsToPush.kt | 10 +- .../api/endpoints/push/ListPushProvisions.kt | 9 +- .../push/RemoveAllPushChannelsForDevice.kt | 9 +- .../endpoints/push/RemoveChannelsFromPush.kt | 10 +- .../remoteaction/ComposableRemoteAction.kt | 100 + .../remoteaction/MappingRemoteAction.kt | 44 + .../pubnub/api/utils/SerializedName.jvm.kt | 5 + .../com/pubnub/api/v2/PNConfiguration.kt | 676 +++- .../pubnub/api/v2/callbacks/Consumer.jvm.kt | 5 + .../pubnub/api/v2/callbacks/EventListener.kt | 2 +- .../pubnub/api/v2/callbacks/StatusEmitter.kt | 23 +- .../pubnub/api/v2/callbacks/StatusListener.kt | 3 +- .../endpoints/HasOverridableConfig.kt | 9 + .../kotlin/com/pubnub/kmp/Downloadable.jvm.kt | 5 + .../pubnub-kotlin-impl/build.gradle.kts | 53 +- .../config/ktlint/baseline.xml | 157 + .../com/pubnub/api/integration/AppTest.kt | 7 +- .../kotlin/com/pubnub/test/SignatureUtils.kt | 2 +- .../internal/vendor/AppEngineFactory.java | 152 + .../com/pubnub/internal/vendor/Base64.java | 746 ++++ .../com/pubnub/internal/vendor/Crypto.java | 162 + .../com/pubnub/internal/DelegatingEndpoint.kt | 35 - .../com/pubnub/internal/EndpointCore.kt | 458 +++ .../com/pubnub/internal/EndpointImpl.kt | 19 - .../kotlin/com/pubnub/internal/PubNubCore.kt | 672 ++++ .../kotlin/com/pubnub/internal/PubNubImpl.kt | 1609 ++++---- .../internal/PubNubRetryableException.kt | 9 + .../kotlin/com/pubnub/internal/PubNubUtil.kt | 269 ++ .../pubnub/internal/SubscriptionFactory.kt | 11 + .../callbacks/ReconnectionCallback.kt | 7 + .../internal/crypto/CryptoModuleImpl.kt | 196 + .../internal/crypto/cryptor/AesCbcCryptor.kt | 132 + .../internal/crypto/cryptor/CryptorHeader.kt | 51 + .../crypto/cryptor/CryptorHeaderVersion.kt | 12 + .../internal/crypto/cryptor/HeaderParser.kt | 203 + .../crypto/cryptor/InputStreamSeparator.kt | 45 + .../internal/crypto/cryptor/LegacyCryptor.kt | 224 ++ .../endpoints/DeleteMessagesEndpoint.kt | 52 + .../internal/endpoints/DeleteMessagesImpl.kt | 14 - .../endpoints/FetchMessagesEndpoint.kt | 151 + .../internal/endpoints/FetchMessagesImpl.kt | 14 - .../internal/endpoints/HistoryEndpoint.kt | 131 + .../pubnub/internal/endpoints/HistoryImpl.kt | 14 - .../endpoints/MessageCountsEndpoint.kt | 71 + .../internal/endpoints/MessageCountsImpl.kt | 14 - .../pubnub/internal/endpoints/TimeEndpoint.kt | 37 + .../com/pubnub/internal/endpoints/TimeImpl.kt | 14 - .../endpoints/access/GrantEndpoint.kt | 194 + .../internal/endpoints/access/GrantImpl.kt | 54 - .../endpoints/access/GrantTokenEndpoint.kt | 72 + .../endpoints/access/GrantTokenImpl.kt | 10 - .../endpoints/access/RevokeTokenEndpoint.kt | 47 + .../endpoints/access/RevokeTokenImpl.kt | 9 - .../AddChannelChannelGroupEndpoint.kt | 59 + .../AddChannelChannelGroupImpl.kt | 14 - .../AllChannelsChannelGroupEndpoint.kt | 50 + .../AllChannelsChannelGroupImpl.kt | 14 - .../DeleteChannelGroupEndpoint.kt | 44 + .../channel_groups/DeleteChannelGroupImpl.kt | 14 - .../ListAllChannelGroupEndpoint.kt | 36 + .../channel_groups/ListAllChannelGroupImpl.kt | 14 - .../RemoveChannelChannelGroupEndpoint.kt | 59 + .../RemoveChannelChannelGroupImpl.kt | 14 - .../endpoints/files/DeleteFileEndpoint.kt | 61 + .../endpoints/files/DeleteFileImpl.kt | 14 - .../endpoints/files/DownloadFileEndpoint.kt | 72 + .../endpoints/files/DownloadFileImpl.kt | 14 - .../files/GenerateUploadUrlEndpoint.kt | 86 + .../endpoints/files/GetFileUrlEndpoint.kt | 113 + .../endpoints/files/GetFileUrlImpl.kt | 14 - .../endpoints/files/ListFilesEndpoint.kt | 88 + .../internal/endpoints/files/ListFilesImpl.kt | 14 - .../files/PublishFileMessageEndpoint.kt | 106 + .../endpoints/files/PublishFileMessageImpl.kt | 14 - .../endpoints/files/SendFileEndpoint.kt | 136 + .../internal/endpoints/files/SendFileImpl.kt | 9 - .../endpoints/files/UploadFileEndpoint.kt | 208 + .../AddMessageActionEndpoint.kt | 66 + .../message_actions/AddMessageActionImpl.kt | 14 - .../GetMessageActionsEndpoint.kt | 59 + .../message_actions/GetMessageActionsImpl.kt | 14 - .../RemoveMessageActionEndpoint.kt | 51 + .../RemoveMessageActionImpl.kt | 14 - .../channel/GetAllChannelMetadataEndpoint.kt | 51 + .../channel/GetAllChannelMetadataImpl.kt | 26 - .../channel/GetChannelMetadataEndpoint.kt | 44 + .../objects/channel/GetChannelMetadataImpl.kt | 14 - .../channel/RemoveChannelMetadataEndpoint.kt | 31 + .../channel/RemoveChannelMetadataImpl.kt | 20 - .../channel/SetChannelMetadataEndpoint.kt | 59 + .../objects/channel/SetChannelMetadataImpl.kt | 14 - .../internal/CollectionQueryParameters.kt | 37 + .../objects/internal/IncludeQueryParam.kt | 48 + .../member/GetChannelMembersEndpoint.kt | 42 + .../objects/member/GetChannelMembersImpl.kt | 27 - .../member/ManageChannelMembersEndpoint.kt | 60 + .../member/ManageChannelMembersImpl.kt | 27 - .../membership/GetMembershipsEndpoint.kt | 44 + .../objects/membership/GetMembershipsImpl.kt | 27 - .../membership/ManageMembershipsEndpoint.kt | 62 + .../membership/ManageMembershipsImpl.kt | 27 - .../uuid/GetAllUUIDMetadataEndpoint.kt | 51 + .../objects/uuid/GetAllUUIDMetadataImpl.kt | 27 - .../objects/uuid/GetUUIDMetadataEndpoint.kt | 44 + .../objects/uuid/GetUUIDMetadataImpl.kt | 23 - .../uuid/RemoveUUIDMetadataEndpoint.kt | 31 + .../objects/uuid/RemoveUUIDMetadataImpl.kt | 20 - .../objects/uuid/SetUUIDMetadataEndpoint.kt | 64 + .../objects/uuid/SetUUIDMetadataImpl.kt | 24 - .../endpoints/presence/GetStateEndpoint.kt | 72 + .../endpoints/presence/GetStateImpl.kt | 14 - .../endpoints/presence/HeartbeatEndpoint.kt | 68 + .../endpoints/presence/HereNowEndpoint.kt | 142 + .../endpoints/presence/HereNowImpl.kt | 14 - .../endpoints/presence/LeaveEndpoint.kt | 48 + .../endpoints/presence/SetStateEndpoint.kt | 74 + .../endpoints/presence/SetStateImpl.kt | 14 - .../endpoints/presence/WhereNowEndpoint.kt | 35 + .../endpoints/presence/WhereNowImpl.kt | 25 - .../endpoints/pubsub/PublishEndpoint.kt | 108 + .../internal/endpoints/pubsub/PublishImpl.kt | 13 - .../endpoints/pubsub/SignalEndpoint.kt | 51 + .../internal/endpoints/pubsub/SignalImpl.kt | 14 - .../endpoints/pubsub/SubscribeEndpoint.kt | 77 + .../push/AddChannelsToPushEndpoint.kt | 81 + .../endpoints/push/AddChannelsToPushImpl.kt | 14 - .../push/ListPushProvisionsEndpoint.kt | 72 + .../endpoints/push/ListPushProvisionsImpl.kt | 14 - .../RemoveAllPushChannelsForDeviceEndpoint.kt | 72 + .../RemoveAllPushChannelsForDeviceImpl.kt | 15 - .../push/RemoveChannelsFromPushEndpoint.kt | 81 + .../push/RemoveChannelsFromPushImpl.kt | 15 - .../remoteaction/RetryingRemoteAction.kt | 89 + .../com/pubnub/internal/eventengine/Effect.kt | 5 + .../internal/eventengine/EffectDispatcher.kt | 52 + .../internal/eventengine/EffectFactory.kt | 5 + .../internal/eventengine/EffectInvocation.kt | 14 + .../com/pubnub/internal/eventengine/Event.kt | 3 + .../internal/eventengine/EventEngine.kt | 42 + .../internal/eventengine/EventEngineConf.kt | 18 + .../eventengine/EventEngineManager.kt | 21 + .../internal/eventengine/ManagedEffect.kt | 5 + .../internal/eventengine/QueueSinkSource.kt | 16 + .../com/pubnub/internal/eventengine/Sink.kt | 5 + .../com/pubnub/internal/eventengine/Source.kt | 5 + .../com/pubnub/internal/eventengine/State.kt | 30 + .../pubnub/internal/eventengine/Transition.kt | 6 + .../com/pubnub/internal/extension/Boolean.kt | 12 + .../com/pubnub/internal/extension/Int.kt | 15 + .../pubnub/internal/extension/JsonElement.kt | 60 + .../internal/extension/RetrofitResponse.kt | 31 + .../extension/ScheduledExecutorService.kt | 10 + .../com/pubnub/internal/extension/String.kt | 3 + .../interceptor/SignatureInterceptor.kt | 15 + .../internal/managers/BasePathManager.kt | 74 + .../internal/managers/DuplicationManager.kt | 27 + .../internal/managers/ListenerManager.kt | 154 + .../pubnub/internal/managers/MapperManager.kt | 333 ++ .../managers/PresenceEventEngineManager.kt | 9 + .../managers/PublishSequenceManager.kt | 16 + .../internal/managers/RetrofitManager.kt | 182 + .../managers/SubscribeEventEngineManager.kt | 9 + .../pubnub/internal/managers/TokenManager.kt | 14 + .../pubnub/internal/managers/TokenParser.kt | 133 + .../com/pubnub/internal/models/Converters.kt | 467 --- .../pubsub/objects/PNObjectEventResult.kt | 183 + .../pubnub/internal/models/server/Envelope.kt | 14 + .../models/server/FetchMessagesEnvelope.kt | 14 + .../models/server/OriginationMetaData.kt | 10 + .../models/server/PresenceEnvelope.kt | 11 + .../internal/models/server/PublishMetaData.kt | 10 + .../models/server/SubscribeEnvelope.kt | 10 + .../models/server/SubscribeMessage.kt | 33 + .../models/server/SubscribeMetaData.kt | 10 + .../AccessManagerGrantPayload.kt | 28 + .../v3/GrantTokenRequestBody.kt | 97 + .../access_manager/v3/GrantTokenResponse.kt | 5 + .../access_manager/v3/RevokeTokenResponse.kt | 5 + .../server/files/FileUploadNotification.kt | 8 + .../server/files/FileUploadRequestDetails.kt | 13 + .../internal/models/server/files/FormField.kt | 6 + .../server/files/GenerateUploadUrlPayload.kt | 3 + .../files/GeneratedUploadUrlResponse.kt | 20 + .../models/server/files/ListFilesResult.kt | 11 + .../server/history/ServerFetchMessageItem.kt | 15 + .../history/ServerFetchMessagesResult.kt | 8 + .../message_actions/MessageActionsResponse.kt | 10 + .../server/objects_api/ChangeMemberInput.kt | 6 + .../objects_api/ChangeMembershipInput.kt | 6 + .../objects_api/ChannelMetadataInput.kt | 9 + .../server/objects_api/EntityArrayEnvelope.kt | 9 + .../server/objects_api/EntityEnvelope.kt | 6 + .../server/objects_api/ServerMemberInput.kt | 9 + .../objects_api/ServerMembershipInput.kt | 11 + .../server/objects_api/UUIDMetadataInput.kt | 11 + .../models/server/presence/WhereNowPayload.kt | 3 + .../com/pubnub/internal/presence/Presence.kt | 191 + .../eventengine/PresenceEventEngine.kt | 16 + .../presence/eventengine/data/PresenceData.kt | 7 + .../eventengine/effect/HeartbeatEffect.kt | 44 + .../eventengine/effect/LeaveEffect.kt | 20 + .../effect/PresenceEffectFactory.kt | 64 + .../effect/PresenceEffectInvocation.kt | 27 + .../presence/eventengine/effect/WaitEffect.kt | 46 + .../effectprovider/HeartbeatProvider.kt | 11 + .../effectprovider/HeartbeatProviderImpl.kt | 20 + .../effect/effectprovider/LeaveProvider.kt | 10 + .../effectprovider/LeaveProviderImpl.kt | 17 + .../eventengine/event/PresenceEvent.kt | 28 + .../eventengine/state/PresenceState.kt | 211 ++ .../pubnub/internal/retry/RetryableBase.kt | 142 + .../internal/retry/RetryableCallback.kt | 103 + .../internal/retry/RetryableRestCaller.kt | 85 + .../internal/services/AccessManagerService.kt | 35 + .../internal/services/ChannelGroupService.kt | 43 + .../pubnub/internal/services/FilesService.kt | 61 + .../internal/services/HistoryService.kt | 46 + .../internal/services/MessageActionService.kt | 42 + .../internal/services/ObjectsService.kt | 110 + .../internal/services/PresenceService.kt | 61 + .../internal/services/PublishService.kt | 30 + .../pubnub/internal/services/PushService.kt | 53 + .../com/pubnub/internal/services/S3Service.kt | 15 + .../pubnub/internal/services/SignalService.kt | 17 + .../internal/services/SubscribeService.kt | 16 + .../pubnub/internal/services/TimeService.kt | 12 + .../pubnub/internal/subscribe/Subscribe.kt | 247 ++ .../eventengine/SubscribeEventEngine.kt | 21 + .../configuration/EventEnginesConf.kt | 14 + .../eventengine/data/SubscriptionData.kt | 6 + .../eventengine/effect/EmitMessagesEffect.kt | 32 + .../eventengine/effect/EmitStatusEffect.kt | 17 + .../eventengine/effect/HandshakeEffect.kt | 36 + .../eventengine/effect/MessagesConsumer.kt | 22 + .../effect/ReceiveMessagesEffect.kt | 35 + .../eventengine/effect/ReconnectionPolicy.kt | 0 .../eventengine/effect/StatusConsumer.kt | 7 + .../effect/SubscribeEffectFactory.kt | 67 + .../effect/SubscribeEffectInvocation.kt | 39 + .../effectprovider/HandshakeProvider.kt | 12 + .../effectprovider/HandshakeProviderImpl.kt | 23 + .../effectprovider/ReceiveMessagesProvider.kt | 13 + .../ReceiveMessagesProviderImpl.kt | 36 + .../eventengine/event/SubscribeEvent.kt | 36 + .../eventengine/event/SubscriptionCursor.kt | 3 + .../eventengine/state/SubscribeState.kt | 362 ++ .../internal/utils/PolymorphicDeserializer.kt | 38 + .../internal/utils/UnwrapSingleField.kt | 21 + .../pubnub/internal/v2/PNConfigurationImpl.kt | 167 +- .../v2/callbacks/DelegatingEventListener.kt | 74 - .../v2/callbacks/DelegatingStatusListener.kt | 15 - .../callbacks/DelegatingSubscribeCallback.kt | 17 - .../internal/v2/callbacks/EventEmitterImpl.kt | 148 + .../internal/v2/callbacks/EventListener.kt | 84 + .../internal/v2/callbacks/StatusListener.kt | 20 + .../internal/v2/entities/ChannelGroupImpl.kt | 63 +- .../internal/v2/entities/ChannelImpl.kt | 77 +- .../v2/entities/ChannelMetadataImpl.kt | 50 +- .../internal/v2/entities/UserMetadataImpl.kt | 50 +- .../internal/v2/subscription/EmitterHelper.kt | 84 +- .../v2/subscription/SubscriptionImpl.kt | 127 +- .../v2/subscription/SubscriptionSetImpl.kt | 96 +- .../internal/vendor/FileEncryptionUtil.kt | 177 + .../workers/SubscribeMessageProcessor.kt | 251 ++ .../pubnub/api/BasePNConfigurationImplTest.kt | 16 + .../src/test/kotlin/com/pubnub/api/Keys.kt | 24 + .../com/pubnub/api/PNConfigurationTest.kt | 124 - .../kotlin/com/pubnub/api/PubNubUtilTest.kt | 13 + .../test/kotlin/com/pubnub/api/UserIdTest.kt | 10 + .../com/pubnub/api/crypto/CryptoModuleTest.kt | 385 ++ .../api/crypto/algorithm/AesCBCCryptorTest.kt | 147 + .../api/crypto/algorithm/LegacyCryptorTest.kt | 191 + .../api/crypto/cryptor/HeaderParserTest.kt | 125 + .../api/endpoints/access/GrantTokenTest.kt | 115 + .../api/endpoints/pubsub/PublishTest.kt | 109 + .../kotlin/com/pubnub/api/legacy/BaseTest.kt | 72 + .../com/pubnub/api/legacy/PubNubCoreTest.kt | 89 + .../DeleteMessagesCoreEndpointTest.kt | 125 + .../api/legacy/endpoints/EndpointCoreTest.kt | 349 ++ .../endpoints/HeartbeatCoreEndpointTest.kt | 261 ++ .../endpoints/access/GrantEndpointTest.kt | 1964 ++++++++++ ...ChannelBaseChannelGroupCoreEndpointTest.kt | 144 + ...hannelsBaseChannelGroupCoreEndpointTest.kt | 152 + .../DeleteBaseChannelGroupCoreEndpointTest.kt | 114 + ...ListAllBaseChannelGroupCoreEndpointTest.kt | 159 + ...ChannelBaseChannelGroupCoreEndpointTest.kt | 104 + .../legacy/endpoints/files/GetFileUrlTest.kt | 125 + .../legacy/endpoints/files/SendFileTest.kt | 286 ++ .../legacy/endpoints/files/TestsWithFiles.kt | 19 + .../legacy/endpoints/files/UploadFileTest.kt | 225 ++ .../history/FetchMessagesCoreEndpointTest.kt | 557 +++ .../history/HistoryCoreEndpointTest.kt | 508 +++ .../endpoints/history/MessageCountTest.kt | 279 ++ .../AddMessageActionCoreEndpointTest.kt | 378 ++ .../GetMessageActionCoreEndpointTest.kt | 438 +++ .../message_actions/ReceiveMessageActions.kt | 236 ++ .../RemoveMessageActionCoreEndpointTest.kt | 250 ++ .../presence/GetStateCoreEndpointTest.kt | 453 +++ .../presence/HereNowCoreEndpointTest.kt | 537 +++ .../legacy/endpoints/presence/LeaveTest.kt | 297 ++ .../presence/StateSetCoreEndpointEETest.kt | 65 + .../presence/StateSetCoreEndpointTest.kt | 426 +++ .../presence/WhereNowCoreEndpointTest.kt | 389 ++ .../legacy/endpoints/pubsub/PublishTest.kt | 540 +++ .../api/legacy/endpoints/pubsub/SignalTest.kt | 191 + .../pubsub/SubscribeCoreEndpointTest.kt | 561 +++ .../endpoints/push/AddChannelsToPushTest.kt | 186 + .../endpoints/push/ListPushProvisionsTest.kt | 308 ++ .../push/PushPayloadHelperHelperTest.kt | 723 ++++ .../RemoveAllPushChannelsForDeviceTest.kt | 168 + .../push/RemoveChannelsFromPushTest.kt | 192 + .../remoteaction/CancellableRemoteAction.kt | 26 + .../ComposableRemoteActionTest.kt | 179 + .../remoteaction/RetryingRemoteActionTest.kt | 196 + .../remoteaction/TestRemoteAction.kt | 93 + .../legacy/managers/BasePathManagerTest.kt | 123 + .../managers/PublishSequenceManagerTest.kt | 18 + .../managers/SubscriptionManagerTest.kt | 3345 +++++++++++++++++ .../api/legacy/vendor/EncryptDecryptTest.kt | 73 + .../api/retry/RetryConfigurationTest.kt | 44 + .../com/pubnub/contract/ContractTestConfig.kt | 40 + .../test/kotlin/com/pubnub/contract/Hooks.kt | 64 + .../com/pubnub/contract/MockPubnubService.kt | 25 + .../pubnub/contract/RunMainCucumberTest.kt | 21 + .../test/kotlin/com/pubnub/contract/Utils.kt | 32 + .../access/parameter/PermissionType.kt | 18 + .../contract/access/parameter/ResourceType.kt | 31 + .../contract/access/parameter/TTLType.kt | 8 + .../contract/access/state/GrantTokenState.kt | 38 + .../pubnub/contract/access/step/GivenSteps.kt | 278 ++ .../pubnub/contract/access/step/ThenSteps.kt | 112 + .../pubnub/contract/access/step/WhenSteps.kt | 80 + .../state/ChannelMetadataState.kt | 10 + .../channelmetadata/step/GivenSteps.kt | 21 + .../channelmetadata/step/ThenSteps.kt | 38 + .../channelmetadata/step/WhenSteps.kt | 71 + .../contract/crypto/CryptoModuleState.kt | 16 + .../contract/crypto/CryptoModuleSteps.kt | 243 ++ .../contract/member/state/MemberState.kt | 14 + .../pubnub/contract/member/step/GivenSteps.kt | 19 + .../pubnub/contract/member/step/ThenSteps.kt | 33 + .../pubnub/contract/member/step/WhenSteps.kt | 84 + .../membership/state/MembershipState.kt | 13 + .../contract/membership/step/GivenSteps.kt | 19 + .../contract/membership/step/ThenSteps.kt | 41 + .../contract/membership/step/WhenSteps.kt | 123 + .../pubnub/contract/parameter/SpaceIdType.kt | 9 + .../step/PresenceEventEngineSteps.kt | 137 + .../kotlin/com/pubnub/contract/state/World.kt | 29 + .../step/ErrorMessageAndDetailsStep.kt | 18 + .../com/pubnub/contract/step/KeysetStep.kt | 50 + .../com/pubnub/contract/step/ThenSteps.kt | 93 + .../eventEngine/state/EventEngineState.kt | 32 + .../eventEngine/state/TestSinkSource.kt | 31 + .../eventEngine/step/EventEngineSteps.kt | 176 + .../uuidmetadata/state/UUIDMetadataState.kt | 9 + .../contract/uuidmetadata/step/GivenSteps.kt | 31 + .../contract/uuidmetadata/step/ThenSteps.kt | 36 + .../contract/uuidmetadata/step/WhenSteps.kt | 70 + .../endpoints/DelegatingEndpointTest.kt | 94 - .../eventengine/EffectDispatcherTest.kt | 157 + .../eventengine/EventEngineManagerTest.kt | 64 + .../internal/eventengine/EventEngineTest.kt | 43 + .../internal/extension/JsonElementTest.kt | 157 + .../internal/managers/MapperManagerTest.kt | 139 + .../internal/managers/RetrofitManagerTest.kt | 52 + .../internal/managers/TokenParserTest.kt | 69 + .../pubnub/internal/presence/PresenceTest.kt | 180 + .../eventengine/effect/HeartbeatEffectTest.kt | 140 + .../eventengine/effect/LeaveEffectTest.kt | 26 + .../effect/PresenceEffectFactoryTest.kt | 200 + .../eventengine/effect/WaitEffectTest.kt | 85 + .../eventengine/event/PresenceEventTest.kt | 42 + ...ransitionFromHeartbeatCooldownStateTest.kt | 206 + .../TransitionFromHeartbeatFailedStateTest.kt | 159 + ...ransitionFromHeartbeatInactiveStateTest.kt | 62 + ...TransitionFromHeartbeatStoppedStateTest.kt | 124 + .../TransitionFromHeartbeatingStateTest.kt | 183 + .../internal/retry/RetryableCallbackTest.kt | 256 ++ .../internal/retry/RetryableRestCallerTest.kt | 396 ++ .../internal/subscribe/SubscribeTest.kt | 190 + .../effect/EmitMessagesEffectTest.kt | 250 ++ .../effect/EmitStatusEffectTest.kt | 29 + .../eventengine/effect/HandshakeEffectTest.kt | 124 + .../effect/ReceiveMessagesEffectTest.kt | 89 + .../effect/SubscribeEffectFactoryTest.kt | 217 ++ .../eventengine/event/SubscribeEventTest.kt | 48 + ...entConsumerWorkerTransitionFunctionTest.kt | 73 + .../TransitionFromHandshakeFailedStateTest.kt | 146 + ...TransitionFromHandshakeStoppedStateTest.kt | 132 + .../TransitionFromHandshakingStateTest.kt | 234 ++ .../TransitionFromReceiveFailedStateTest.kt | 161 + .../TransitionFromReceiveStoppedStateTest.kt | 141 + .../TransitionFromReceivingStateTest.kt | 240 ++ .../TransitionFromUnsubscribedStateTest.kt | 63 + .../internal/suite/CoreEndpointTestSuite.kt | 350 ++ .../pubnub/internal/suite/TimeTestSuite.kt | 56 + .../AddChannelChannelGroupTestSuite.kt | 47 + .../AllChannelsChannelGroupTestSuite.kt | 100 + .../DeleteChannelGroupTestSuite.kt | 44 + .../ListAllChannelGroupTestSuite.kt | 58 + .../RemoveChannelChannelGroupTestSuite.kt | 51 + .../internal/suite/grant/GrantTestSuite.kt | 71 + .../suite/history/DeleteMessagesTestSuite.kt | 44 + .../history/counts/MessageCountsTestSuite.kt | 48 + .../suite/history/v2/HistoryMetaTestSuite.kt | 101 + .../suite/history/v2/HistoryTestSuite.kt | 72 + .../v3/FetchMessagesMetaActionsTestSuite.kt | 122 + .../history/v3/FetchMessagesTestSuite.kt | 98 + .../AddMessageActionTestSuite.kt | 64 + .../GetMessageActionsMultipleTestSuite.kt | 79 + .../GetMessageActionsTestSuite.kt | 62 + .../RemoveMessageActionsTestSuite.kt | 41 + .../suite/presence/GetStateTestSuite.kt | 68 + .../suite/presence/HeartbeatTestSuite.kt | 45 + .../suite/presence/HereNowTestSuite.kt | 56 + .../internal/suite/presence/LeaveTestSuite.kt | 41 + .../suite/presence/StateSetTestSuite.kt | 73 + .../suite/presence/WhereNowTestSuite.kt | 49 + .../suite/pubsub/PublishGetTestSuite.kt | 56 + .../suite/pubsub/PublishPostTestSuite.kt | 66 + .../internal/suite/pubsub/SignalTestSuite.kt | 45 + .../suite/pubsub/SubscribeTestSuite.kt | 53 + .../push/add/AddChannelsToPushV1TestSuite.kt | 68 + .../push/add/AddChannelsToPushV2TestSuite.kt | 46 + .../list/ListPushProvisionsV1TestSuite.kt | 63 + .../list/ListPushProvisionsV2TestSuite.kt | 64 + .../remove/RemoveAllFromPushV1TestSuite.kt | 43 + .../remove/RemoveAllFromPushV2TestSuite.kt | 44 + .../RemoveChannelsFromPushV1TestSuite.kt | 45 + .../RemoveChannelsFromPushV2TestSuite.kt | 46 + .../utils/PolymorphicDeserializerTest.kt | 81 + .../internal/utils/UnwrapSingleFieldTest.kt | 58 + .../internal/v2/PNConfigurationImplTest.kt | 52 +- .../callbacks/DelegatingEventListenerTest.kt | 23 - .../callbacks/DelegatingStatusListenerTest.kt | 37 - .../DelegatingSubscribeCallbackTest.kt | 38 - .../v2/callbacks/EventEmitterImplTest.kt | 346 ++ .../v2/entities/BaseChannelGroupImplTest.kt | 59 + .../v2/entities/BaseChannelImplTest.kt | 58 + .../subscription/BaseSubscriptionImplTest.kt | 135 + .../BaseSubscriptionSetImplTest.kt | 121 + .../subscription/SubscriptionSetImplTest.kt | 7 +- .../workers/SubscribeMessageProcessorTest.kt | 200 + .../kotlin/com/pubnub/test/CommonUtils.kt | 311 ++ .../test/kotlin/com/pubnub/test/Extensions.kt | 114 + .../src/test/kotlin/com/pubnub/test/Keys.kt | 24 + .../kotlin/com/pubnub/test/SignatureUtils.kt | 179 + .../src/test/resources/entityTooLarge.xml | 9 + .../test/resources/junit-platform.properties | 1 + .../src/test/resources/logback.xml | 18 + .../src/test/resources/special_chars.json | 327 ++ .../pubnub-kotlin-test/build.gradle.kts | 2 +- .../kotlin/com.pubnub.test/FakePubNub.kt | 509 --- settings.gradle.kts | 4 +- 897 files changed, 52539 insertions(+), 9501 deletions(-) create mode 100755 migration_utils/replace_in_file.sh create mode 100644 migration_utils/replacements.txt create mode 100755 migration_utils/upgrade_v10.sh delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/PNConfiguration.kt delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/callbacks/SubscribeCallback.java delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/Endpoint.java delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/Time.java delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/GrantToken.java delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/ListAllChannelGroup.java delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/memberships/GetMemberships.java delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/GetUUIDMetadata.java delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/RemoveUUIDMetadata.java delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/WhereNow.java rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{PubNub.kt => java/PubNubForJava.kt} (72%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/PubNubRuntimeException.java (95%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/SpaceId.java (92%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/builder/PresenceBuilder.java (86%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/builder/PubNubErrorBuilder.java (99%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/builder/PubSubBuilder.java (86%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/builder/SubscribeBuilder.java (92%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/builder/UnsubscribeBuilder.java (69%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/builder/dto/ChangeTemporaryUnavailableOperation.java (91%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/builder/dto/PresenceOperation.java (90%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/builder/dto/PubSubOperation.java (95%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/builder/dto/StateOperation.java (90%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/builder/dto/TimetokenAndRegionOperation.java (80%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/builder/dto/UnsubscribeOperation.java (85%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/callbacks/PNCallback.java (91%) create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/callbacks/SubscribeCallback.kt rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/BuilderSteps.java (73%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/DeleteMessages.java (88%) create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/Endpoint.kt rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/FetchMessages.java (93%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/History.java (91%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/MessageCounts.java (87%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/access/Grant.java (75%) create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/GrantToken.java rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/access/RevokeToken.java (55%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/access/builder/AbstractGrantTokenBuilder.java (62%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/access/builder/GrantTokenBuilder.java (61%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/access/builder/GrantTokenEntitiesBuilder.java (66%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/access/builder/GrantTokenObjectsBuilder.java (61%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/channel_groups/AddChannelChannelGroup.java (75%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/channel_groups/AllChannelsChannelGroup.java (70%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/channel_groups/DeleteChannelGroup.java (70%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/channel_groups/RemoveChannelChannelGroup.java (76%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/files/DeleteFile.java (54%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/files/DownloadFile.java (59%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/files/GetFileUrl.java (54%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/files/ListFiles.java (69%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/files/PublishFileMessage.java (68%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/files/SendFile.java (75%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/files/requiredparambuilder/FilesBuilderSteps.java (76%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/message_actions/AddMessageAction.java (77%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/message_actions/GetMessageActions.java (76%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/message_actions/RemoveMessageAction.java (77%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/channel/GetAllChannelsMetadata.java (57%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/channel/GetChannelMetadata.java (54%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/channel/RemoveChannelMetadata.java (50%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/channel/SetChannelMetadata.java (69%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/members/GetChannelMembers.java (51%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/members/ManageChannelMembers.java (53%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/members/RemoveChannelMembers.java (52%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/members/SetChannelMembers.java (52%) create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/memberships/GetMemberships.java rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/memberships/ManageMemberships.java (50%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/memberships/RemoveMemberships.java (53%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/memberships/SetMemberships.java (52%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/utils/Include.java (94%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/utils/ObjectsBuilderSteps.java (73%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/utils/PNSortKey.java (84%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/uuid/GetAllUUIDMetadata.java (54%) create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/GetUUIDMetadata.java create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/RemoveUUIDMetadata.java rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/objects_api/uuid/SetUUIDMetadata.java (73%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/presence/GetState.java (75%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/presence/Heartbeat.java (69%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/presence/HereNow.java (78%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/presence/Leave.java (64%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/presence/SetState.java (79%) create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/WhereNow.java rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/pubsub/Publish.java (80%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/pubsub/Signal.java (67%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/push/AddChannelsToPush.java (83%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/push/ListPushProvisions.java (82%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/push/RemoveAllPushChannelsForDevice.java (84%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/endpoints/push/RemoveChannelsFromPush.java (84%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/access_manager/PNAccessManagerGrantResult.java (68%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/access_manager/PNAccessManagerKeyData.java (85%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/access_manager/PNAccessManagerKeysData.java (81%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/access_manager/sum/SpacePermissions.java (87%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/access_manager/sum/UserPermissions.java (86%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/access_manager/v3/ChannelGrant.java (94%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/access_manager/v3/ChannelGroupGrant.java (92%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/access_manager/v3/PNResource.java (95%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/access_manager/v3/PNRevokeTokenResult.java (50%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/access_manager/v3/UUIDGrant.java (91%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/EntityArrayEnvelope.java (89%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/EntityEnvelope.java (69%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/PNObject.java (90%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/channel/PNChannelMetadata.java (94%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/channel/PNChannelMetadataResult.java (93%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/channel/PNGetAllChannelsMetadataResult.java (84%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/channel/PNGetChannelMetadataResult.java (71%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/channel/PNRemoveChannelMetadataResult.java (75%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/channel/PNSetChannelMetadataResult.java (71%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/member/PNGetChannelMembersResult.java (84%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/member/PNManageChannelMembersResult.java (85%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/member/PNMembers.java (86%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/member/PNRemoveChannelMembersResult.java (85%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/member/PNSetChannelMembersResult.java (84%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/member/PNUUID.java (96%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/membership/PNChannelMembership.java (95%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/membership/PNGetMembershipsResult.java (84%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/membership/PNManageMembershipResult.java (85%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/membership/PNMembership.java (79%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/membership/PNMembershipResult.java (93%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/membership/PNRemoveMembershipResult.java (82%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/membership/PNSetMembershipResult.java (85%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/uuid/PNGetAllUUIDMetadataResult.java (86%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/uuid/PNGetUUIDMetadataResult.java (81%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/uuid/PNRemoveUUIDMetadataResult.java (81%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/uuid/PNSetUUIDMetadataResult.java (81%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/uuid/PNUUIDMetadata.java (94%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/models/consumer/objects_api/uuid/PNUUIDMetadataResult.java (93%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/v2/PNConfiguration.kt (54%) create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/EventEmitter.kt create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/EventListener.kt create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/StatusEmitter.kt create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/StatusListener.kt rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/v2/callbacks/handlers/OnChannelMetadataHandler.java (86%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/v2/callbacks/handlers/OnFileHandler.java (94%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/v2/callbacks/handlers/OnMembershipHandler.java (85%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/v2/callbacks/handlers/OnMessageActionHandler.java (94%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/v2/callbacks/handlers/OnMessageHandler.java (94%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/v2/callbacks/handlers/OnPresenceHandler.java (94%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/v2/callbacks/handlers/OnSignalHandler.java (94%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/v2/callbacks/handlers/OnUuidMetadataHandler.java (86%) rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/v2/endpoints/pubsub/PublishBuilder.java (78%) create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/endpoints/pubsub/SignalBuilder.java rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/v2/entities/Channel.kt (75%) create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/ChannelGroup.kt create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/ChannelMetadata.kt create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/Subscribable.kt create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/UserMetadata.kt rename pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/{ => java}/v2/subscriptions/Subscription.kt (65%) create mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/subscriptions/SubscriptionSet.kt delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/EventEmitter.java delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/EventListener.java delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/StatusEmitter.java delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/StatusListener.java delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/endpoints/pubsub/SignalBuilder.java delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/ChannelGroup.kt delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/ChannelMetadata.kt delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/UserMetadata.kt delete mode 100644 pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/subscriptions/SubscriptionSet.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/SubscribeCallbackAdapter.java delete mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/PubNubImpl.java delete mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/TimeImpl.java delete mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/ListAllChannelGroupImpl.java create mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/PubNubForJavaImpl.kt rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/DelegatingEndpoint.kt (62%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/DelegatingRemoteAction.kt (87%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/DeleteMessagesImpl.java (71%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/FetchMessagesImpl.java (89%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/HistoryImpl.java (73%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/MessageCountsImpl.java (79%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/access/GrantImpl.java (59%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/access/GrantTokenImpl.java (67%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/access/RevokeTokenImpl.java (61%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/channel_groups/AddChannelChannelGroupImpl.java (66%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/channel_groups/AllChannelsChannelGroupImpl.java (63%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/channel_groups/DeleteChannelGroupImpl.java (61%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/channel_groups/RemoveChannelChannelGroupImpl.java (66%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/files/DeleteFileImpl.java (59%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/files/DownloadFileImpl.java (64%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/files/GetFileUrlImpl.java (61%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/files/ListFilesImpl.java (75%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/files/PublishFileMessageImpl.java (71%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/files/SendFileImpl.java (81%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/files/requiredparambuilder/ChannelFileNameFileIdBuilder.java (82%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/message_actions/AddMessageActionImpl.java (63%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/message_actions/GetMessageActionsImpl.java (70%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/message_actions/RemoveMessageActionImpl.java (72%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/channel/GetAllChannelsMetadataImpl.java (57%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/channel/GetChannelMetadataImpl.java (67%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/channel/RemoveChannelMetadataImpl.java (64%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/channel/SetChannelMetadataImpl.java (77%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/members/GetChannelMembersImpl.java (68%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/members/ManageChannelMembersImpl.java (80%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/members/RemoveChannelMembersImpl.java (71%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/members/SetChannelMembersImpl.java (67%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/memberships/GetMembershipsImpl.java (68%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/memberships/ManageMembershipsImpl.java (77%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/memberships/RemoveMembershipsImpl.java (71%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/memberships/SetMembershipsImpl.java (64%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/uuid/GetAllUUIDMetadataImpl.java (64%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/uuid/GetUUIDMetadataImpl.java (62%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/uuid/RemoveUUIDMetadataImpl.java (63%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/objects_api/uuid/SetUUIDMetadataImpl.java (72%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/presence/GetStateImpl.java (67%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/presence/HeartbeatImpl.java (52%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/presence/HereNowImpl.java (68%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/presence/LeaveImpl.java (53%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/presence/SetStateImpl.java (77%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/presence/WhereNowImpl.java (50%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/pubsub/PublishImpl.java (72%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/pubsub/SignalImpl.java (65%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/push/AddChannelsToPushImpl.java (75%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/push/ListPushProvisionsImpl.java (70%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/push/RemoveAllPushChannelsForDeviceImpl.java (69%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/endpoints/push/RemoveChannelsFromPushImpl.java (73%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{ => java}/v2/PNConfigurationImpl.kt (65%) rename pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/{v2/callbacks/DelegatingEventListener.java => java/v2/callbacks/Converters.java} (58%) create mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/DelegatingEventListener.kt create mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/DelegatingStatusListener.kt create mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/EventEmitterInternal.kt create mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/ChannelGroupImpl.kt create mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/ChannelImpl.kt create mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/ChannelMetadataImpl.kt create mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/UserMetadataImpl.kt create mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/subscription/EmitterHelper.kt create mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/subscription/SubscriptionImpl.kt create mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/subscription/SubscriptionSetImpl.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/callbacks/DelegatingStatusListener.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallback.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/ChannelGroupImpl.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/ChannelImpl.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/ChannelMetadataImpl.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/UserMetadataImpl.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/subscription/EmitterHelper.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/subscription/SubscriptionImpl.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/subscription/SubscriptionSetImpl.kt rename pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/{ => java}/endpoints/DelegatingEndpointTest.kt (68%) rename pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/{ => java}/endpoints/DelegatingRemoteActionTest.kt (98%) rename pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/{ => java}/endpoints/DeleteMessagesImplTest.kt (87%) rename pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/{ => java}/endpoints/FetchMessagesImplTest.kt (92%) rename pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/{ => java}/endpoints/access/GrantTokenImplTest.kt (65%) rename pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/{ => java}/v2/PNConfigurationImplTest.kt (91%) rename pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/{ => java}/v2/entities/ChannelGroupImplTest.kt (51%) rename pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/{ => java}/v2/entities/ChannelImplTest.kt (58%) create mode 100644 pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/subscription/SubscriptionImplTest.kt create mode 100644 pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/subscription/SubscriptionSetImplTest.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingEventListenerTest.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingStatusListenerTest.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallbackTest.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/SubscriptionImplTest.kt delete mode 100644 pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/SubscriptionSetImplTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/JsonElement.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/PubNubError.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/PubNubException.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/UserId.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/callbacks/Listener.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/endpoints/remoteaction/RemoteAction.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNHeartbeatNotificationOptions.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNLogVerbosity.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNOperationType.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNPushEnvironment.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNPushType.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNReconnectionPolicy.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNStatusCategory.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/TokenBitmask.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNBoundedPage.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNPublishResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNStatus.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNTimeResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/v3/PNGrantTokenResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/v3/PNToken.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsResults.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNDeleteFileResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNDownloadFileResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNDownloadableFile.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNFile.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNFileUploadResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNFileUrlResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNListFilesResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNPublishFileMessageResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/HistoryMessageType.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNDeleteMessagesResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNFetchMessage.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNHistoryResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNMessageCountResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNAddMessageActionResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNGetMessageActionsResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNMessageAction.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNRemoveMessageActionResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/PNPage.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/channel/PNChannelMetadata.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/channel/PNChannelMetadataArrayResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/channel/PNChannelMetadataResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/uuid/PNUUIDMetadata.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/presence/PNGetStateResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/presence/PNHereNow.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/presence/PNSetStateResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/BasePubSubResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/MessageResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNEvent.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNMessageResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNPresenceEventResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNSignalResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/files/PNFileEventResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/message_actions/PNMessageActionResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/objects/ObjectPayload.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/objects/ObjectResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushAddChannelResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushListProvisionsResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushRemoveAllChannelsResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushRemoveChannelResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/payload/PushPayloadHelper.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/payload/PushPayloadSerializer.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/retry/RetryConfiguration.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/retry/RetryableEndpointGroup.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/utils/PatchValue.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/utils/SerializedName.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/Result.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/Subscribable.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscribeCapable.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscriptionCursor.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscriptionOptions.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/kmp/Downloadable.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/kmp/PNFuture.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/kmp/futures.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonTest/kotlin/com/pubnub/kmp/JsonElementTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/commonTest/kotlin/com/pubnub/kmp/PNFutureTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/JsonElement.ios.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/PubNubException.ios.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNGetMessageActionsResult.ios.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.ios.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/kmp/Downloadable.ios.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/JsonElement.js.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/PubNubException.js.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNGetMessageActionsResult.js.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.js.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/kmp/Downloadable.js.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/kmp/JsMap.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/JsonElement.jvm.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/PNConfiguration.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/PubNubException.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/CryptoModule.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/cryptor/Cryptor.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/data/EncryptedData.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/data/EncryptedStreamData.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/remoteaction/ComposableRemoteAction.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/remoteaction/MappingRemoteAction.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/utils/SerializedName.jvm.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.jvm.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/internal/endpoints/HasOverridableConfig.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/kmp/Downloadable.jvm.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/java/com/pubnub/internal/vendor/AppEngineFactory.java create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/java/com/pubnub/internal/vendor/Base64.java create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/java/com/pubnub/internal/vendor/Crypto.java delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/DelegatingEndpoint.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/EndpointCore.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/EndpointImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubCore.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubRetryableException.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubUtil.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/SubscriptionFactory.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/callbacks/ReconnectionCallback.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/CryptoModuleImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/AesCbcCryptor.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/CryptorHeader.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/CryptorHeaderVersion.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/HeaderParser.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/InputStreamSeparator.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/LegacyCryptor.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/DeleteMessagesEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/DeleteMessagesImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/FetchMessagesEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/FetchMessagesImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/HistoryEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/HistoryImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/MessageCountsEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/MessageCountsImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/TimeEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/TimeImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantTokenEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantTokenImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/RevokeTokenEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/RevokeTokenImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AddChannelChannelGroupEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AddChannelChannelGroupImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AllChannelsChannelGroupEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AllChannelsChannelGroupImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/DeleteChannelGroupEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/DeleteChannelGroupImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/ListAllChannelGroupEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/ListAllChannelGroupImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/RemoveChannelChannelGroupEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/RemoveChannelChannelGroupImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DeleteFileEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DeleteFileImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DownloadFileEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DownloadFileImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/GenerateUploadUrlEndpoint.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/GetFileUrlEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/GetFileUrlImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/ListFilesEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/ListFilesImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/PublishFileMessageEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/PublishFileMessageImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/SendFileEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/SendFileImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/UploadFileEndpoint.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/AddMessageActionEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/AddMessageActionImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/GetMessageActionsEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/GetMessageActionsImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/RemoveMessageActionEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/RemoveMessageActionImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetAllChannelMetadataEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetAllChannelMetadataImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetChannelMetadataEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetChannelMetadataImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/RemoveChannelMetadataEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/RemoveChannelMetadataImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/SetChannelMetadataEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/SetChannelMetadataImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/internal/CollectionQueryParameters.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/internal/IncludeQueryParam.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/GetChannelMembersEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/GetChannelMembersImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/ManageChannelMembersEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/ManageChannelMembersImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/GetMembershipsEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/GetMembershipsImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/ManageMembershipsEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/ManageMembershipsImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetAllUUIDMetadataEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetAllUUIDMetadataImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetUUIDMetadataEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetUUIDMetadataImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/RemoveUUIDMetadataEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/RemoveUUIDMetadataImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/SetUUIDMetadataEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/SetUUIDMetadataImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/GetStateEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/GetStateImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/HeartbeatEndpoint.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/HereNowEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/HereNowImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/LeaveEndpoint.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/SetStateEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/SetStateImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/WhereNowEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/WhereNowImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/PublishEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/PublishImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/SignalEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/SignalImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/SubscribeEndpoint.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/AddChannelsToPushEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/AddChannelsToPushImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/ListPushProvisionsEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/ListPushProvisionsImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveAllPushChannelsForDeviceEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveAllPushChannelsForDeviceImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveChannelsFromPushEndpoint.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveChannelsFromPushImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/remoteaction/RetryingRemoteAction.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Effect.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EffectDispatcher.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EffectFactory.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EffectInvocation.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Event.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EventEngine.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EventEngineConf.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EventEngineManager.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/ManagedEffect.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/QueueSinkSource.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Sink.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Source.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/State.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Transition.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/Boolean.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/Int.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/JsonElement.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/RetrofitResponse.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/ScheduledExecutorService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/String.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/interceptor/SignatureInterceptor.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/BasePathManager.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/DuplicationManager.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/ListenerManager.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/MapperManager.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/PresenceEventEngineManager.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/PublishSequenceManager.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/RetrofitManager.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/SubscribeEventEngineManager.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/TokenManager.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/TokenParser.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/Converters.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/consumer/pubsub/objects/PNObjectEventResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/Envelope.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/FetchMessagesEnvelope.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/OriginationMetaData.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/PresenceEnvelope.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/PublishMetaData.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/SubscribeEnvelope.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/SubscribeMessage.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/SubscribeMetaData.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/AccessManagerGrantPayload.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/v3/GrantTokenRequestBody.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/v3/GrantTokenResponse.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/v3/RevokeTokenResponse.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/FileUploadNotification.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/FileUploadRequestDetails.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/FormField.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/GenerateUploadUrlPayload.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/GeneratedUploadUrlResponse.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/ListFilesResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/history/ServerFetchMessageItem.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/history/ServerFetchMessagesResult.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/message_actions/MessageActionsResponse.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ChangeMemberInput.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ChangeMembershipInput.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ChannelMetadataInput.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/EntityArrayEnvelope.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/EntityEnvelope.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ServerMemberInput.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ServerMembershipInput.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/UUIDMetadataInput.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/presence/WhereNowPayload.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/Presence.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/PresenceEventEngine.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/data/PresenceData.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/HeartbeatEffect.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/LeaveEffect.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/PresenceEffectFactory.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/PresenceEffectInvocation.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/WaitEffect.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/HeartbeatProvider.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/HeartbeatProviderImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/LeaveProvider.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/LeaveProviderImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/event/PresenceEvent.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/state/PresenceState.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/retry/RetryableBase.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/retry/RetryableCallback.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/retry/RetryableRestCaller.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/AccessManagerService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/ChannelGroupService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/FilesService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/HistoryService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/MessageActionService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/ObjectsService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/PresenceService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/PublishService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/PushService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/S3Service.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/SignalService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/SubscribeService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/TimeService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/Subscribe.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/SubscribeEventEngine.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/configuration/EventEnginesConf.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/data/SubscriptionData.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitMessagesEffect.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitStatusEffect.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/HandshakeEffect.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/MessagesConsumer.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/ReceiveMessagesEffect.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/ReconnectionPolicy.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/StatusConsumer.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/SubscribeEffectFactory.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/SubscribeEffectInvocation.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/HandshakeProvider.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/HandshakeProviderImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/ReceiveMessagesProvider.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/ReceiveMessagesProviderImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/event/SubscribeEvent.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/event/SubscriptionCursor.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/state/SubscribeState.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/utils/PolymorphicDeserializer.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/utils/UnwrapSingleField.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/DelegatingEventListener.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/DelegatingStatusListener.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallback.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImpl.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventListener.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/StatusListener.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/vendor/FileEncryptionUtil.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessor.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/BasePNConfigurationImplTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/Keys.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/PNConfigurationTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/PubNubUtilTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/UserIdTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/CryptoModuleTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/algorithm/AesCBCCryptorTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/algorithm/LegacyCryptorTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/cryptor/HeaderParserTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/endpoints/access/GrantTokenTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/endpoints/pubsub/PublishTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/BaseTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/PubNubCoreTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/DeleteMessagesCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/EndpointCoreTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/HeartbeatCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/access/GrantEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/AddChannelBaseChannelGroupCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/AllChannelsBaseChannelGroupCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/DeleteBaseChannelGroupCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/ListAllBaseChannelGroupCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/RemoveChannelBaseChannelGroupCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/GetFileUrlTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/SendFileTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/TestsWithFiles.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/UploadFileTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/history/FetchMessagesCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/history/HistoryCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/history/MessageCountTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/AddMessageActionCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/GetMessageActionCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/ReceiveMessageActions.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/RemoveMessageActionCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/GetStateCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/HereNowCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/LeaveTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/StateSetCoreEndpointEETest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/StateSetCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/WhereNowCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/PublishTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/SignalTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/SubscribeCoreEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/AddChannelsToPushTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/ListPushProvisionsTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/PushPayloadHelperHelperTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/RemoveAllPushChannelsForDeviceTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/RemoveChannelsFromPushTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/CancellableRemoteAction.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/ComposableRemoteActionTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/RetryingRemoteActionTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/TestRemoteAction.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/managers/BasePathManagerTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/managers/PublishSequenceManagerTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/managers/SubscriptionManagerTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/vendor/EncryptDecryptTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/retry/RetryConfigurationTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/ContractTestConfig.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/Hooks.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/MockPubnubService.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/RunMainCucumberTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/Utils.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/parameter/PermissionType.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/parameter/ResourceType.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/parameter/TTLType.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/state/GrantTokenState.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/step/GivenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/step/ThenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/step/WhenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/state/ChannelMetadataState.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/GivenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/ThenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/WhenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/crypto/CryptoModuleState.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/crypto/CryptoModuleSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/state/MemberState.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/step/GivenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/step/ThenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/step/WhenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/state/MembershipState.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/step/GivenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/step/ThenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/step/WhenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/parameter/SpaceIdType.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/presence/eventEngine/step/PresenceEventEngineSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/state/World.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/step/ErrorMessageAndDetailsStep.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/step/KeysetStep.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/step/ThenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/state/EventEngineState.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/state/TestSinkSource.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/step/EventEngineSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/state/UUIDMetadataState.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/GivenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/ThenSteps.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/WhenSteps.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/endpoints/DelegatingEndpointTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/eventengine/EffectDispatcherTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/eventengine/EventEngineManagerTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/eventengine/EventEngineTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/extension/JsonElementTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/managers/MapperManagerTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/managers/RetrofitManagerTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/managers/TokenParserTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/PresenceTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/HeartbeatEffectTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/LeaveEffectTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/PresenceEffectFactoryTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/WaitEffectTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/event/PresenceEventTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatCooldownStateTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatFailedStateTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatInactiveStateTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatStoppedStateTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatingStateTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/retry/RetryableCallbackTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/retry/RetryableRestCallerTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/SubscribeTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitMessagesEffectTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitStatusEffectTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/HandshakeEffectTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/ReceiveMessagesEffectTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/SubscribeEffectFactoryTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/event/SubscribeEventTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/SubscribeEventConsumerWorkerTransitionFunctionTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromHandshakeFailedStateTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromHandshakeStoppedStateTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromHandshakingStateTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromReceiveFailedStateTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromReceiveStoppedStateTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromReceivingStateTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromUnsubscribedStateTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/CoreEndpointTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/TimeTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/AddChannelChannelGroupTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/AllChannelsChannelGroupTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/DeleteChannelGroupTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/ListAllChannelGroupTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/RemoveChannelChannelGroupTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/grant/GrantTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/DeleteMessagesTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/counts/MessageCountsTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v2/HistoryMetaTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v2/HistoryTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v3/FetchMessagesMetaActionsTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v3/FetchMessagesTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/AddMessageActionTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/GetMessageActionsMultipleTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/GetMessageActionsTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/RemoveMessageActionsTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/GetStateTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/HeartbeatTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/HereNowTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/LeaveTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/StateSetTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/WhereNowTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/PublishGetTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/PublishPostTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/SignalTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/SubscribeTestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/add/AddChannelsToPushV1TestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/add/AddChannelsToPushV2TestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/list/ListPushProvisionsV1TestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/list/ListPushProvisionsV2TestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveAllFromPushV1TestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveAllFromPushV2TestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveChannelsFromPushV1TestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveChannelsFromPushV2TestSuite.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/utils/PolymorphicDeserializerTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/utils/UnwrapSingleFieldTest.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingEventListenerTest.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingStatusListenerTest.kt delete mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallbackTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImplTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelGroupImplTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelImplTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionImplTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionSetImplTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessorTest.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/CommonUtils.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/Extensions.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/Keys.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/SignatureUtils.kt create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/entityTooLarge.xml create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/junit-platform.properties create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/logback.xml create mode 100644 pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/special_chars.json delete mode 100644 pubnub-kotlin/pubnub-kotlin-test/src/commonMain/kotlin/com.pubnub.test/FakePubNub.kt diff --git a/build-logic/gradle-plugins/src/main/kotlin/com/pubnub/gradle/PubNubKotlinLibraryPlugin.kt b/build-logic/gradle-plugins/src/main/kotlin/com/pubnub/gradle/PubNubKotlinLibraryPlugin.kt index c24ed6b89..2daeb8092 100644 --- a/build-logic/gradle-plugins/src/main/kotlin/com/pubnub/gradle/PubNubKotlinLibraryPlugin.kt +++ b/build-logic/gradle-plugins/src/main/kotlin/com/pubnub/gradle/PubNubKotlinLibraryPlugin.kt @@ -4,13 +4,9 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.configure -import org.gradle.kotlin.dsl.dependencies -import org.gradle.kotlin.dsl.project import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile -import org.jlleitschuh.gradle.ktlint.KtlintExtension -import org.jlleitschuh.gradle.ktlint.KtlintPlugin class PubNubKotlinLibraryPlugin : Plugin { override fun apply(target: Project) { @@ -26,6 +22,7 @@ class PubNubKotlinLibraryPlugin : Plugin { tasks.named("compileKotlin", KotlinJvmCompile::class.java) { it.compilerOptions { javaParameters.set(true) + freeCompilerArgs.add("-Xjvm-default=all") } } diff --git a/build-logic/gradle-plugins/src/main/kotlin/com/pubnub/gradle/PubNubKotlinMultiplatformPlugin.kt b/build-logic/gradle-plugins/src/main/kotlin/com/pubnub/gradle/PubNubKotlinMultiplatformPlugin.kt index 714017814..3bd968aa1 100644 --- a/build-logic/gradle-plugins/src/main/kotlin/com/pubnub/gradle/PubNubKotlinMultiplatformPlugin.kt +++ b/build-logic/gradle-plugins/src/main/kotlin/com/pubnub/gradle/PubNubKotlinMultiplatformPlugin.kt @@ -54,6 +54,7 @@ class PubNubKotlinMultiplatformPlugin : Plugin { compilation.compileTaskProvider.configure { task -> task.compilerOptions { javaParameters.set(true) + freeCompilerArgs.add("-Xjvm-default=all") } } } diff --git a/build.gradle.kts b/build.gradle.kts index 8035c5990..42995edc2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,6 +6,7 @@ plugins { alias(libs.plugins.dokka) alias(libs.plugins.gradle.nexus.publish) alias(libs.plugins.kotlinx.atomicfu) apply false + kotlin("plugin.lombok") version "2.0.0" apply false } nexusPublishing { diff --git a/gradle.properties b/gradle.properties index ad479b3ff..bdcc2ebdb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ RELEASE_SIGNING_ENABLED=true SONATYPE_HOST=DEFAULT SONATYPE_AUTOMATIC_RELEASE=false GROUP=com.pubnub -VERSION_NAME=9.2.3 +VERSION_NAME=9.2-DEV POM_PACKAGING=jar POM_NAME=PubNub SDK @@ -43,4 +43,4 @@ SWIFT_PATH=swift ENABLE_TARGET_JS=false ENABLE_TARGET_IOS=false -ENABLE_TARGET_IOS_SIMULATOR=false \ No newline at end of file +ENABLE_TARGET_IOS_SIMULATOR=false diff --git a/migration_utils/replace_in_file.sh b/migration_utils/replace_in_file.sh new file mode 100755 index 000000000..9fd2904a5 --- /dev/null +++ b/migration_utils/replace_in_file.sh @@ -0,0 +1,13 @@ +#!/bin/bash +file="$4" +search="$1" +replace="$2" +dry_run="$3" + +if [[ "$dry_run" == "true" ]]; then + if grep -q "$search" "$file"; then + echo "$file" + fi +else + sed -i '' "s#$search#$replace#g" "$file" +fi diff --git a/migration_utils/replacements.txt b/migration_utils/replacements.txt new file mode 100644 index 000000000..99802c1fd --- /dev/null +++ b/migration_utils/replacements.txt @@ -0,0 +1,193 @@ +com.pubnub.api.models.consumer.objects_api.member.PNGetChannelMembersResult:com.pubnub.api.java.models.consumer.objects_api.member.PNGetChannelMembersResult +com.pubnub.api.v2.entities.ChannelMetadata:com.pubnub.api.java.v2.entities.ChannelMetadata +com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps.ChannelMembershipsStep:com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps.ChannelMembershipsStep +com.pubnub.api.models.consumer.objects_api.EntityEnvelope:com.pubnub.api.java.models.consumer.objects_api.EntityEnvelope +com.pubnub.api.callbacks.SubscribeCallback:com.pubnub.api.java.callbacks.SubscribeCallback +com.pubnub.api.v2.PNConfiguration.Companion:com.pubnub.api.java.v2.PNConfiguration.Companion +com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps.UUIDsStep:com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps.UUIDsStep +com.pubnub.api.v2.entities.Channel:com.pubnub.api.java.v2.entities.Channel +com.pubnub.api.models.consumer.objects_api.member.PNUUID:com.pubnub.api.java.models.consumer.objects_api.member.PNUUID +com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps.RemoveOrSetStep:com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps.RemoveOrSetStep +com.pubnub.api.endpoints.objects_api.channel.SetChannelMetadata.Builder:com.pubnub.api.java.endpoints.objects_api.channel.SetChannelMetadata.Builder +com.pubnub.api.v2.callbacks.handlers.OnUuidMetadataHandler:com.pubnub.api.java.v2.callbacks.handlers.OnUuidMetadataHandler +com.pubnub.api.v2.entities.UserMetadata:com.pubnub.api.java.v2.entities.UserMetadata +com.pubnub.api.PubNubRuntimeException:com.pubnub.api.java.PubNubRuntimeException +com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant:com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGrant +com.pubnub.api.endpoints.presence.GetState:com.pubnub.api.java.endpoints.presence.GetState +com.pubnub.api.endpoints.objects_api.utils.Include:com.pubnub.api.java.endpoints.objects_api.utils.Include +com.pubnub.api.models.consumer.objects_api.member.PNUUID.UUIDWithCustom:com.pubnub.api.java.models.consumer.objects_api.member.PNUUID.UUIDWithCustom +com.pubnub.api.models.consumer.objects_api.membership.PNRemoveMembershipResult:com.pubnub.api.java.models.consumer.objects_api.membership.PNRemoveMembershipResult +com.pubnub.api.callbacks.SubscribeCallback.BaseSubscribeCallback:com.pubnub.api.java.callbacks.SubscribeCallback.BaseSubscribeCallback +com.pubnub.api.models.consumer.access_manager.v3.PNRevokeTokenResult:com.pubnub.api.java.models.consumer.access_manager.v3.PNRevokeTokenResult +com.pubnub.api.endpoints.access.builder.GrantTokenEntitiesBuilder:com.pubnub.api.java.endpoints.access.builder.GrantTokenEntitiesBuilder +com.pubnub.api.endpoints.message_actions.RemoveMessageAction:com.pubnub.api.java.endpoints.message_actions.RemoveMessageAction +com.pubnub.api.endpoints.presence.Heartbeat:com.pubnub.api.java.endpoints.presence.Heartbeat +com.pubnub.api.endpoints.message_actions.GetMessageActions:com.pubnub.api.java.endpoints.message_actions.GetMessageActions +com.pubnub.api.builder.dto.PubSubOperation.ConnectedStatusAnnouncedOperation:com.pubnub.api.java.builder.dto.PubSubOperation.ConnectedStatusAnnouncedOperation +com.pubnub.api.models.consumer.objects_api.member.PNSetChannelMembersResult:com.pubnub.api.java.models.consumer.objects_api.member.PNSetChannelMembersResult +com.pubnub.api.endpoints.pubsub.Publish:com.pubnub.api.java.endpoints.pubsub.Publish +com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult:com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult +com.pubnub.api.models.consumer.objects_api.membership.PNGetMembershipsResult:com.pubnub.api.java.models.consumer.objects_api.membership.PNGetMembershipsResult +com.pubnub.api.models.consumer.access_manager.sum.UserPermissions:com.pubnub.api.java.models.consumer.access_manager.sum.UserPermissions +com.pubnub.api.v2.endpoints.pubsub.SignalBuilder:com.pubnub.api.java.v2.endpoints.pubsub.SignalBuilder +com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeyData:com.pubnub.api.java.models.consumer.access_manager.PNAccessManagerKeyData +com.pubnub.api.endpoints.access.builder.GrantTokenBuilder:com.pubnub.api.java.endpoints.access.builder.GrantTokenBuilder +com.pubnub.api.endpoints.pubsub.Signal:com.pubnub.api.java.endpoints.pubsub.Signal +com.pubnub.api.builder.dto.PubSubOperation.ReconnectOperation:com.pubnub.api.java.builder.dto.PubSubOperation.ReconnectOperation +com.pubnub.api.v2.PNConfigurationOverride.Builder:com.pubnub.api.java.v2.PNConfigurationOverride.Builder +com.pubnub.api.endpoints.presence.Leave:com.pubnub.api.java.endpoints.presence.Leave +com.pubnub.api.builder.dto.TimetokenAndRegionOperation:com.pubnub.api.java.builder.dto.TimetokenAndRegionOperation +com.pubnub.api.models.consumer.access_manager.PNAccessManagerGrantResult.PNAccessManagerGrantResultBuilder:com.pubnub.api.java.models.consumer.access_manager.PNAccessManagerGrantResult.PNAccessManagerGrantResultBuilder +com.pubnub.api.models.consumer.objects_api.member.PNUUID.UUIDId:com.pubnub.api.java.models.consumer.objects_api.member.PNUUID.UUIDId +com.pubnub.api.endpoints.access.GrantToken:com.pubnub.api.java.endpoints.access.GrantToken +com.pubnub.api.models.consumer.objects_api.member.PNMembers:com.pubnub.api.java.models.consumer.objects_api.member.PNMembers +com.pubnub.api.v2.subscriptions.Subscription.DefaultImpls:com.pubnub.api.java.v2.subscriptions.Subscription.DefaultImpls +com.pubnub.api.endpoints.access.builder.GrantTokenObjectsBuilder:com.pubnub.api.java.endpoints.access.builder.GrantTokenObjectsBuilder +com.pubnub.api.endpoints.presence.WhereNow:com.pubnub.api.java.endpoints.presence.WhereNow +com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps:com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps +com.pubnub.api.endpoints.channel_groups.DeleteChannelGroup:com.pubnub.api.java.endpoints.channel_groups.DeleteChannelGroup +com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult:com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult +com.pubnub.api.builder.PubSubBuilder:com.pubnub.api.java.builder.PubSubBuilder +com.pubnub.api.v2.entities.ChannelGroup:com.pubnub.api.java.v2.entities.ChannelGroup +com.pubnub.api.models.consumer.objects_api.channel.PNSetChannelMetadataResult:com.pubnub.api.java.models.consumer.objects_api.channel.PNSetChannelMetadataResult +com.pubnub.api.endpoints.access.RevokeToken:com.pubnub.api.java.endpoints.access.RevokeToken +com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps.InputStreamStep:com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps.InputStreamStep +com.pubnub.api.endpoints.objects_api.channel.GetChannelMetadata:com.pubnub.api.java.endpoints.objects_api.channel.GetChannelMetadata +com.pubnub.api.v2.callbacks.EventListener:com.pubnub.api.java.v2.callbacks.EventListener +com.pubnub.api.models.consumer.objects_api.member.PNUUID.UUIDWithoutCustom:com.pubnub.api.java.models.consumer.objects_api.member.PNUUID.UUIDWithoutCustom +com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps:com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps +com.pubnub.api.endpoints.files.PublishFileMessage:com.pubnub.api.java.endpoints.files.PublishFileMessage +com.pubnub.api.v2.PNConfiguration.Builder:com.pubnub.api.java.v2.PNConfiguration.Builder +com.pubnub.api.v2.PNConfiguration.Builder.DefaultImpls:com.pubnub.api.java.v2.PNConfiguration.Builder.DefaultImpls +com.pubnub.api.v2.callbacks.EventEmitter.DefaultImpls:com.pubnub.api.java.v2.callbacks.EventEmitter.DefaultImpls +com.pubnub.api.models.consumer.objects_api.uuid.PNSetUUIDMetadataResult:com.pubnub.api.java.models.consumer.objects_api.uuid.PNSetUUIDMetadataResult +com.pubnub.api.builder.dto.StateOperation.StateOperationBuilder:com.pubnub.api.java.builder.dto.StateOperation.StateOperationBuilder +com.pubnub.api.endpoints.push.RemoveAllPushChannelsForDevice:com.pubnub.api.java.endpoints.push.RemoveAllPushChannelsForDevice +com.pubnub.api.endpoints.History:com.pubnub.api.java.endpoints.History +com.pubnub.api.builder.dto.UnsubscribeOperation.UnsubscribeOperationBuilder:com.pubnub.api.java.builder.dto.UnsubscribeOperation.UnsubscribeOperationBuilder +com.pubnub.api.SpaceId:com.pubnub.api.java.SpaceId +com.pubnub.api.endpoints.Time:com.pubnub.api.java.endpoints.Time +com.pubnub.api.endpoints.channel_groups.AllChannelsChannelGroup:com.pubnub.api.java.endpoints.channel_groups.AllChannelsChannelGroup +com.pubnub.api.endpoints.objects_api.utils.Include.PNChannelDetailsLevel:com.pubnub.api.java.endpoints.objects_api.utils.Include.PNChannelDetailsLevel +com.pubnub.api.endpoints.push.AddChannelsToPush:com.pubnub.api.java.endpoints.push.AddChannelsToPush +com.pubnub.api.endpoints.objects_api.memberships.RemoveMemberships.Builder:com.pubnub.api.java.endpoints.objects_api.memberships.RemoveMemberships.Builder +com.pubnub.api.endpoints.BuilderSteps.ChannelStep:com.pubnub.api.java.endpoints.BuilderSteps.ChannelStep +com.pubnub.api.v2.callbacks.handlers.OnMessageActionHandler:com.pubnub.api.java.v2.callbacks.handlers.OnMessageActionHandler +com.pubnub.api.endpoints.objects_api.utils.PNSortKey:com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey +com.pubnub.api.PubNubForJava:com.pubnub.api.java.PubNubForJava +com.pubnub.api.models.consumer.objects_api.uuid.PNRemoveUUIDMetadataResult:com.pubnub.api.java.models.consumer.objects_api.uuid.PNRemoveUUIDMetadataResult +com.pubnub.api.v2.PNConfigurationOverride:com.pubnub.api.java.v2.PNConfigurationOverride +com.pubnub.api.endpoints.files.GetFileUrl:com.pubnub.api.java.endpoints.files.GetFileUrl +com.pubnub.api.models.consumer.objects_api.PNObject:com.pubnub.api.java.models.consumer.objects_api.PNObject +com.pubnub.api.v2.subscriptions.SubscriptionSet:com.pubnub.api.java.v2.subscriptions.SubscriptionSet +com.pubnub.api.endpoints.files.ListFiles:com.pubnub.api.java.endpoints.files.ListFiles +com.pubnub.api.endpoints.objects_api.members.RemoveChannelMembers:com.pubnub.api.java.endpoints.objects_api.members.RemoveChannelMembers +com.pubnub.api.endpoints.objects_api.uuid.SetUUIDMetadata:com.pubnub.api.java.endpoints.objects_api.uuid.SetUUIDMetadata +com.pubnub.api.endpoints.access.builder.AbstractGrantTokenBuilder:com.pubnub.api.java.endpoints.access.builder.AbstractGrantTokenBuilder +com.pubnub.api.builder.dto.ChangeTemporaryUnavailableOperation.ChangeTemporaryUnavailableOperationBuilder:com.pubnub.api.java.builder.dto.ChangeTemporaryUnavailableOperation.ChangeTemporaryUnavailableOperationBuilder +com.pubnub.api.models.consumer.objects_api.channel.PNGetAllChannelsMetadataResult:com.pubnub.api.java.models.consumer.objects_api.channel.PNGetAllChannelsMetadataResult +com.pubnub.api.endpoints.push.ListPushProvisions:com.pubnub.api.java.endpoints.push.ListPushProvisions +com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership.ChannelWithCustom:com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership.ChannelWithCustom +com.pubnub.api.endpoints.objects_api.uuid.GetAllUUIDMetadata:com.pubnub.api.java.endpoints.objects_api.uuid.GetAllUUIDMetadata +com.pubnub.api.endpoints.files.GetFileUrl.Builder:com.pubnub.api.java.endpoints.files.GetFileUrl.Builder +com.pubnub.api.endpoints.files.SendFile:com.pubnub.api.java.endpoints.files.SendFile +com.pubnub.api.models.consumer.objects_api.membership.PNManageMembershipResult:com.pubnub.api.java.models.consumer.objects_api.membership.PNManageMembershipResult +com.pubnub.api.endpoints.objects_api.memberships.SetMemberships.Builder:com.pubnub.api.java.endpoints.objects_api.memberships.SetMemberships.Builder +com.pubnub.api.endpoints.files.PublishFileMessage.Builder:com.pubnub.api.java.endpoints.files.PublishFileMessage.Builder +com.pubnub.api.builder.dto.ChangeTemporaryUnavailableOperation:com.pubnub.api.java.builder.dto.ChangeTemporaryUnavailableOperation +com.pubnub.api.endpoints.files.DownloadFile:com.pubnub.api.java.endpoints.files.DownloadFile +com.pubnub.api.models.consumer.objects_api.channel.PNGetChannelMetadataResult:com.pubnub.api.java.models.consumer.objects_api.channel.PNGetChannelMetadataResult +com.pubnub.api.PubNubForJava.DefaultImpls:com.pubnub.api.java.PubNubForJava.DefaultImpls +com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership:com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership +com.pubnub.api.endpoints.objects_api.memberships.RemoveMemberships:com.pubnub.api.java.endpoints.objects_api.memberships.RemoveMemberships +com.pubnub.api.endpoints.objects_api.channel.RemoveChannelMetadata.Builder:com.pubnub.api.java.endpoints.objects_api.channel.RemoveChannelMetadata.Builder +com.pubnub.api.endpoints.objects_api.utils.Include.PNUUIDDetailsLevel:com.pubnub.api.java.endpoints.objects_api.utils.Include.PNUUIDDetailsLevel +com.pubnub.api.v2.PNConfigurationOverride.Companion:com.pubnub.api.java.v2.PNConfigurationOverride.Companion +com.pubnub.api.PubNubRuntimeException.PubNubRuntimeExceptionBuilder:com.pubnub.api.java.PubNubRuntimeException.PubNubRuntimeExceptionBuilder +com.pubnub.api.callbacks.PNCallback:com.pubnub.api.java.callbacks.PNCallback +com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant:com.pubnub.api.java.models.consumer.access_manager.v3.UUIDGrant +com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadata:com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadata +com.pubnub.api.endpoints.objects_api.members.ManageChannelMembers:com.pubnub.api.java.endpoints.objects_api.members.ManageChannelMembers +com.pubnub.api.endpoints.objects_api.members.GetChannelMembers.Builder:com.pubnub.api.java.endpoints.objects_api.members.GetChannelMembers.Builder +com.pubnub.api.builder.dto.PubSubOperation:com.pubnub.api.java.builder.dto.PubSubOperation +com.pubnub.api.v2.callbacks.EventEmitter:com.pubnub.api.java.v2.callbacks.EventEmitter +com.pubnub.api.endpoints.objects_api.members.ManageChannelMembers.Builder:com.pubnub.api.java.endpoints.objects_api.members.ManageChannelMembers.Builder +com.pubnub.api.v2.callbacks.handlers.OnFileHandler:com.pubnub.api.java.v2.callbacks.handlers.OnFileHandler +com.pubnub.api.endpoints.objects_api.memberships.ManageMemberships:com.pubnub.api.java.endpoints.objects_api.memberships.ManageMemberships +com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps.FileNameStep:com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps.FileNameStep +com.pubnub.api.builder.PubNubErrorBuilder:com.pubnub.api.java.builder.PubNubErrorBuilder +com.pubnub.api.models.consumer.objects_api.member.PNRemoveChannelMembersResult:com.pubnub.api.java.models.consumer.objects_api.member.PNRemoveChannelMembersResult +com.pubnub.api.endpoints.channel_groups.ListAllChannelGroup:com.pubnub.api.java.endpoints.channel_groups.ListAllChannelGroup +com.pubnub.api.endpoints.objects_api.members.GetChannelMembers:com.pubnub.api.java.endpoints.objects_api.members.GetChannelMembers +com.pubnub.api.builder.dto.PresenceOperation.PresenceOperationBuilder:com.pubnub.api.java.builder.dto.PresenceOperation.PresenceOperationBuilder +com.pubnub.api.models.consumer.access_manager.PNAccessManagerGrantResult:com.pubnub.api.java.models.consumer.access_manager.PNAccessManagerGrantResult +com.pubnub.api.v2.callbacks.EventListener.DefaultImpls:com.pubnub.api.java.v2.callbacks.EventListener.DefaultImpls +com.pubnub.api.endpoints.objects_api.utils.PNSortKey.Key:com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey.Key +com.pubnub.api.v2.PNConfiguration:com.pubnub.api.java.v2.PNConfiguration +com.pubnub.api.v2.callbacks.handlers.OnPresenceHandler:com.pubnub.api.java.v2.callbacks.handlers.OnPresenceHandler +com.pubnub.api.endpoints.files.DeleteFile.Builder:com.pubnub.api.java.endpoints.files.DeleteFile.Builder +com.pubnub.api.models.consumer.objects_api.membership.PNMembership:com.pubnub.api.java.models.consumer.objects_api.membership.PNMembership +com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant:com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGroupGrant +com.pubnub.api.builder.dto.UnsubscribeOperation:com.pubnub.api.java.builder.dto.UnsubscribeOperation +com.pubnub.api.endpoints.objects_api.members.RemoveChannelMembers.Builder:com.pubnub.api.java.endpoints.objects_api.members.RemoveChannelMembers.Builder +com.pubnub.api.endpoints.DeleteMessages:com.pubnub.api.java.endpoints.DeleteMessages +com.pubnub.api.v2.endpoints.pubsub.PublishBuilder:com.pubnub.api.java.v2.endpoints.pubsub.PublishBuilder +com.pubnub.api.PubNubForJava.Companion:com.pubnub.api.java.PubNubForJava.Companion +com.pubnub.api.endpoints.BuilderSteps:com.pubnub.api.java.endpoints.BuilderSteps +com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeyData.PNAccessManagerKeyDataBuilder:com.pubnub.api.java.models.consumer.access_manager.PNAccessManagerKeyData.PNAccessManagerKeyDataBuilder +com.pubnub.api.v2.subscriptions.SubscriptionSet.DefaultImpls:com.pubnub.api.java.v2.subscriptions.SubscriptionSet.DefaultImpls +com.pubnub.api.v2.callbacks.handlers.OnSignalHandler:com.pubnub.api.java.v2.callbacks.handlers.OnSignalHandler +com.pubnub.api.endpoints.objects_api.channel.SetChannelMetadata:com.pubnub.api.java.endpoints.objects_api.channel.SetChannelMetadata +com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadata:com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadata +com.pubnub.api.endpoints.objects_api.members.SetChannelMembers.Builder:com.pubnub.api.java.endpoints.objects_api.members.SetChannelMembers.Builder +com.pubnub.api.endpoints.files.ListFiles.Builder:com.pubnub.api.java.endpoints.files.ListFiles.Builder +com.pubnub.api.v2.callbacks.handlers.OnMessageHandler:com.pubnub.api.java.v2.callbacks.handlers.OnMessageHandler +com.pubnub.api.endpoints.files.SendFile.Builder:com.pubnub.api.java.endpoints.files.SendFile.Builder +com.pubnub.api.endpoints.channel_groups.RemoveChannelChannelGroup:com.pubnub.api.java.endpoints.channel_groups.RemoveChannelChannelGroup +com.pubnub.api.models.consumer.objects_api.uuid.PNGetAllUUIDMetadataResult:com.pubnub.api.java.models.consumer.objects_api.uuid.PNGetAllUUIDMetadataResult +com.pubnub.api.endpoints.objects_api.memberships.GetMemberships:com.pubnub.api.java.endpoints.objects_api.memberships.GetMemberships +com.pubnub.api.models.consumer.objects_api.uuid.PNGetUUIDMetadataResult:com.pubnub.api.java.models.consumer.objects_api.uuid.PNGetUUIDMetadataResult +com.pubnub.api.models.consumer.access_manager.sum.SpacePermissions:com.pubnub.api.java.models.consumer.access_manager.sum.SpacePermissions +com.pubnub.api.endpoints.objects_api.members.SetChannelMembers:com.pubnub.api.java.endpoints.objects_api.members.SetChannelMembers +com.pubnub.api.endpoints.objects_api.uuid.RemoveUUIDMetadata:com.pubnub.api.java.endpoints.objects_api.uuid.RemoveUUIDMetadata +com.pubnub.api.endpoints.Endpoint:com.pubnub.api.java.endpoints.Endpoint +com.pubnub.api.builder.dto.PubSubOperation.NoOpOperation:com.pubnub.api.java.builder.dto.PubSubOperation.NoOpOperation +com.pubnub.api.endpoints.channel_groups.AddChannelChannelGroup:com.pubnub.api.java.endpoints.channel_groups.AddChannelChannelGroup +com.pubnub.api.endpoints.access.Grant:com.pubnub.api.java.endpoints.access.Grant +com.pubnub.api.builder.PresenceBuilder:com.pubnub.api.java.builder.PresenceBuilder +com.pubnub.api.endpoints.MessageCounts:com.pubnub.api.java.endpoints.MessageCounts +com.pubnub.api.endpoints.objects_api.channel.GetAllChannelsMetadata:com.pubnub.api.java.endpoints.objects_api.channel.GetAllChannelsMetadata +com.pubnub.api.endpoints.files.DownloadFile.Builder:com.pubnub.api.java.endpoints.files.DownloadFile.Builder +com.pubnub.api.v2.PNConfiguration.DefaultImpls:com.pubnub.api.java.v2.PNConfiguration.DefaultImpls +com.pubnub.api.models.consumer.objects_api.member.PNManageChannelMembersResult:com.pubnub.api.java.models.consumer.objects_api.member.PNManageChannelMembersResult +com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps.RemoveOrSetStep.SetStep:com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps.RemoveOrSetStep.SetStep +com.pubnub.api.endpoints.objects_api.uuid.GetUUIDMetadata:com.pubnub.api.java.endpoints.objects_api.uuid.GetUUIDMetadata +com.pubnub.api.endpoints.objects_api.memberships.ManageMemberships.Builder:com.pubnub.api.java.endpoints.objects_api.memberships.ManageMemberships.Builder +com.pubnub.api.builder.dto.PresenceOperation:com.pubnub.api.java.builder.dto.PresenceOperation +com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps.FileIdStep:com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps.FileIdStep +com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult:com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult +com.pubnub.api.v2.subscriptions.Subscription:com.pubnub.api.java.v2.subscriptions.Subscription +com.pubnub.api.models.consumer.objects_api.membership.PNSetMembershipResult:com.pubnub.api.java.models.consumer.objects_api.membership.PNSetMembershipResult +com.pubnub.api.endpoints.files.DeleteFile:com.pubnub.api.java.endpoints.files.DeleteFile +com.pubnub.api.v2.callbacks.handlers.OnMembershipHandler:com.pubnub.api.java.v2.callbacks.handlers.OnMembershipHandler +com.pubnub.api.endpoints.message_actions.AddMessageAction:com.pubnub.api.java.endpoints.message_actions.AddMessageAction +com.pubnub.api.v2.callbacks.handlers.OnChannelMetadataHandler:com.pubnub.api.java.v2.callbacks.handlers.OnChannelMetadataHandler +com.pubnub.api.builder.dto.StateOperation:com.pubnub.api.java.builder.dto.StateOperation +com.pubnub.api.models.consumer.objects_api.EntityArrayEnvelope:com.pubnub.api.java.models.consumer.objects_api.EntityArrayEnvelope +com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership.ChannelId:com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership.ChannelId +com.pubnub.api.endpoints.objects_api.memberships.SetMemberships:com.pubnub.api.java.endpoints.objects_api.memberships.SetMemberships +com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps.RemoveOrSetStep.RemoveStep:com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps.RemoveOrSetStep.RemoveStep +com.pubnub.api.models.consumer.objects_api.channel.PNRemoveChannelMetadataResult:com.pubnub.api.java.models.consumer.objects_api.channel.PNRemoveChannelMetadataResult +com.pubnub.api.endpoints.objects_api.utils.PNSortKey.Dir:com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey.Dir +com.pubnub.api.endpoints.objects_api.channel.RemoveChannelMetadata:com.pubnub.api.java.endpoints.objects_api.channel.RemoveChannelMetadata +com.pubnub.api.builder.SubscribeBuilder:com.pubnub.api.java.builder.SubscribeBuilder +com.pubnub.api.endpoints.FetchMessages:com.pubnub.api.java.endpoints.FetchMessages +com.pubnub.api.endpoints.presence.HereNow:com.pubnub.api.java.endpoints.presence.HereNow +com.pubnub.api.builder.UnsubscribeBuilder:com.pubnub.api.java.builder.UnsubscribeBuilder +com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeysData:com.pubnub.api.java.models.consumer.access_manager.PNAccessManagerKeysData +com.pubnub.api.builder.dto.PubSubOperation.DisconnectOperation:com.pubnub.api.java.builder.dto.PubSubOperation.DisconnectOperation +com.pubnub.api.endpoints.presence.SetState:com.pubnub.api.java.endpoints.presence.SetState +com.pubnub.api.models.consumer.access_manager.v3.PNResource:com.pubnub.api.java.models.consumer.access_manager.v3.PNResource +com.pubnub.api.endpoints.push.RemoveChannelsFromPush:com.pubnub.api.java.endpoints.push.RemoveChannelsFromPush +com.pubnub.api.endpoints.objects_api.channel.GetChannelMetadata.Builder:com.pubnub.api.java.endpoints.objects_api.channel.GetChannelMetadata.Builder +com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership.JustChannel:com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership.JustChannel diff --git a/migration_utils/upgrade_v10.sh b/migration_utils/upgrade_v10.sh new file mode 100755 index 000000000..6915bf143 --- /dev/null +++ b/migration_utils/upgrade_v10.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e +# Function to check if a file contains a search string + +# Main script +if [ $# -lt 2 ]; then + echo "Usage: $0 [-d]" + exit 1 +fi + +dry_run=false +replacements_file="$1" +directory="$2" + +# Check for dry run flag +shift 2 +while [[ $# -gt 0 ]]; do + case "$1" in + -d|--dry-run) + dry_run=true + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac + shift +done + +# Read replacements from file +while IFS= read -r line; do + if [[ $line =~ ^([^:]+):(.+)$ ]]; then + search="${BASH_REMATCH[1]}" + replace="${BASH_REMATCH[2]}" + echo "Replacing '$search' with '$replace'" + + # Find all files and apply the replacement or print + find "$directory" -type f -print0 | xargs -0 -n 1 ./replace_in_file.sh "$search" "$replace" "$dry_run" | sort -u + fi +done < "$replacements_file" \ No newline at end of file diff --git a/pubnub-core/pubnub-core-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/HasOverridableConfig.kt b/pubnub-core/pubnub-core-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/HasOverridableConfig.kt index d396500ce..e39e23b0d 100644 --- a/pubnub-core/pubnub-core-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/HasOverridableConfig.kt +++ b/pubnub-core/pubnub-core-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/HasOverridableConfig.kt @@ -1,9 +1,8 @@ package com.pubnub.api.endpoints -import com.pubnub.api.v2.BasePNConfiguration interface HasOverridableConfig { - fun overrideConfiguration(configuration: BasePNConfiguration) + fun overrideConfiguration(configuration: PNConfiguration) - val configuration: BasePNConfiguration + val configuration: PNConfiguration } diff --git a/pubnub-core/pubnub-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/BasePNConfiguration.kt b/pubnub-core/pubnub-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/BasePNConfiguration.kt index a8c08a5f4..4541f22fb 100644 --- a/pubnub-core/pubnub-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/BasePNConfiguration.kt +++ b/pubnub-core/pubnub-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/BasePNConfiguration.kt @@ -21,7 +21,7 @@ import javax.net.ssl.X509ExtendedTrustManager * allow performing precise PubNub client configuration. * */ -interface BasePNConfiguration : BasePNConfigurationOverride { +interface PNConfiguration : PNConfigurationOverride { /** * The user ID that the PubNub client will use. */ @@ -278,7 +278,7 @@ interface BasePNConfiguration : BasePNConfigurationOverride { * using [PubNubCore.presence]. Should be used only with ACL set on the server side. * For more information please contact PubNub support * @see PubNubCore.presence - * @see BasePNConfiguration.heartbeatInterval + * @see PNConfiguration.heartbeatInterval */ val managePresenceListManually: Boolean @@ -551,7 +551,7 @@ interface BasePNConfiguration : BasePNConfigurationOverride { * using [PubNubCore.presence]. Should be used only with ACL set on the server side. * For more information please contact PubNub support * @see PubNubCore.presence - * @see BasePNConfiguration.heartbeatInterval + * @see PNConfiguration.heartbeatInterval */ val managePresenceListManually: Boolean } diff --git a/pubnub-core/pubnub-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/BasePNConfigurationOverride.kt b/pubnub-core/pubnub-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/BasePNConfigurationOverride.kt index e00a9e6f9..4c64eb63c 100644 --- a/pubnub-core/pubnub-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/BasePNConfigurationOverride.kt +++ b/pubnub-core/pubnub-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/BasePNConfigurationOverride.kt @@ -4,7 +4,7 @@ import com.pubnub.api.UserId import com.pubnub.api.crypto.CryptoModule import com.pubnub.api.retry.RetryConfiguration -interface BasePNConfigurationOverride { +interface PNConfigurationOverride { interface Builder { /** * The subscribe key from the admin panel. diff --git a/pubnub-core/pubnub-core-impl/build.gradle.kts b/pubnub-core/pubnub-core-impl/build.gradle.kts index 8e30fef21..b0632f8cd 100644 --- a/pubnub-core/pubnub-core-impl/build.gradle.kts +++ b/pubnub-core/pubnub-core-impl/build.gradle.kts @@ -20,7 +20,7 @@ val generateVersion = kotlin.sourceSets.getByName("main").kotlin.srcDir(generateVersion) dependencies { - api(project(":pubnub-core:pubnub-core-api")) +// api(project(":pubnub-core:pubnub-core-api")) implementation(libs.retrofit2) implementation(libs.retrofit2.converter.gson) diff --git a/pubnub-core/pubnub-core-impl/src/main/java/com/pubnub/internal/vendor/AppEngineFactory.java b/pubnub-core/pubnub-core-impl/src/main/java/com/pubnub/internal/vendor/AppEngineFactory.java index 66f122f1b..991e170f0 100644 --- a/pubnub-core/pubnub-core-impl/src/main/java/com/pubnub/internal/vendor/AppEngineFactory.java +++ b/pubnub-core/pubnub-core-impl/src/main/java/com/pubnub/internal/vendor/AppEngineFactory.java @@ -1,6 +1,6 @@ package com.pubnub.internal.vendor; -import com.pubnub.api.v2.BasePNConfiguration; +import com.pubnub.api.v2.PNConfiguration; import com.pubnub.internal.PubNubCore; import com.pubnub.internal.PubNubUtil; import okhttp3.Call; @@ -23,9 +23,9 @@ public class AppEngineFactory implements Call { private Request request; - private final BasePNConfiguration configuration; + private final PNConfiguration configuration; - AppEngineFactory(Request request, BasePNConfiguration configuration) { + AppEngineFactory(Request request, PNConfiguration configuration) { this.request = request; this.configuration = configuration; } @@ -137,9 +137,9 @@ public Call clone() { } public static class Factory implements Call.Factory { - private final BasePNConfiguration configuration; + private final PNConfiguration configuration; - public Factory(BasePNConfiguration configuration) { + public Factory(PNConfiguration configuration) { this.configuration = configuration; } diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/BasePubNubImpl.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/BasePubNubImpl.kt index bd289d053..0b79ba245 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/BasePubNubImpl.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/BasePubNubImpl.kt @@ -1,9 +1,7 @@ package com.pubnub.internal -import com.pubnub.api.BasePubNub import com.pubnub.api.callbacks.Listener import com.pubnub.api.models.consumer.access_manager.v3.PNToken -import com.pubnub.api.v2.BasePNConfiguration import com.pubnub.api.v2.callbacks.BaseEventEmitter import com.pubnub.api.v2.callbacks.BaseEventListener import com.pubnub.api.v2.callbacks.BaseStatusEmitter @@ -29,13 +27,13 @@ abstract class BasePubNubImpl< SubscriptionSet : BaseSubscriptionSet, StatusListener : BaseStatusListener, > internal constructor( - configuration: BasePNConfiguration, + configuration: PNConfiguration, pnsdkName: String, eventEnginesConf: EventEnginesConf = EventEnginesConf(), ) : BasePubNub, BaseEventEmitter, BaseStatusEmitter { - constructor(configuration: BasePNConfiguration, pnsdkName: String) : this(configuration, pnsdkName, EventEnginesConf()) + constructor(configuration: PNConfiguration, pnsdkName: String) : this(configuration, pnsdkName, EventEnginesConf()) val listenerManager: ListenerManager = ListenerManager(this) val pubNubCore = PubNubCore(configuration, listenerManager, eventEnginesConf, pnsdkName) @@ -55,7 +53,7 @@ abstract class BasePubNubImpl< /** * Unique id of this PubNub instance. * - * @see [BasePNConfiguration.includeInstanceIdentifier] + * @see [PNConfiguration.includeInstanceIdentifier] */ val instanceId: String get() = pubNubCore.instanceId diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/EndpointCore.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/EndpointCore.kt index f4dd224ce..9dd224007 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/EndpointCore.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/EndpointCore.kt @@ -4,8 +4,7 @@ import com.google.gson.JsonElement import com.pubnub.api.PubNubError import com.pubnub.api.PubNubException import com.pubnub.api.retry.RetryableEndpointGroup -import com.pubnub.api.v2.BasePNConfiguration -import com.pubnub.api.v2.BasePNConfiguration.Companion.isValid +import com.pubnub.api.v2.PNConfiguration.Companion.isValid import com.pubnub.api.v2.callbacks.Consumer import com.pubnub.api.v2.callbacks.Result import com.pubnub.internal.managers.RetrofitManager @@ -29,8 +28,8 @@ import java.net.UnknownHostException */ abstract class EndpointCore protected constructor(protected val pubnub: PubNubCore) : EndpointInterface { - private var configOverride: BasePNConfiguration? = null - final override val configuration: BasePNConfiguration + private var configOverride: PNConfiguration? = null + final override val configuration: PNConfiguration get() = configOverride ?: pubnub.configuration protected val retrofitManager: RetrofitManager @@ -426,7 +425,7 @@ abstract class EndpointCore protected constructor(protected val p } } - override fun overrideConfiguration(configuration: BasePNConfiguration) { + override fun overrideConfiguration(configuration: PNConfiguration) { this.configOverride = configuration } diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/PubNubCore.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/PubNubCore.kt index b6a4b4751..643fa3ff4 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/PubNubCore.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/PubNubCore.kt @@ -10,7 +10,6 @@ import com.pubnub.api.models.consumer.access_manager.v3.PNToken import com.pubnub.api.models.consumer.history.PNHistoryResult import com.pubnub.api.models.consumer.message_actions.PNMessageAction import com.pubnub.api.models.consumer.objects.PNPage -import com.pubnub.api.v2.BasePNConfiguration import com.pubnub.api.v2.callbacks.BaseEventListener import com.pubnub.api.v2.subscriptions.BaseSubscription import com.pubnub.api.v2.subscriptions.EmptyOptions @@ -110,7 +109,7 @@ import java.util.concurrent.ScheduledExecutorService import kotlin.time.Duration.Companion.seconds class PubNubCore internal constructor( - val configuration: BasePNConfiguration, + val configuration: PNConfiguration, val listenerManager: ListenerManager, eventEnginesConf: EventEnginesConf = EventEnginesConf(), private val pnsdkName: String, @@ -178,7 +177,7 @@ class PubNubCore internal constructor( /** * Unique id of this PubNub instance. * - * @see [BasePNConfiguration.includeInstanceIdentifier] + * @see [PNConfiguration.includeInstanceIdentifier] */ val instanceId = UUID.randomUUID().toString() @@ -203,12 +202,12 @@ class PubNubCore internal constructor( /** * Send a message to all subscribers of a channel. * - * To publish a message you must first specify a valid [BasePNConfiguration.publishKey]. + * To publish a message you must first specify a valid [PNConfiguration.publishKey]. * A successfully published message is replicated across the PubNub Real-Time Network and sent * simultaneously to all subscribed clients on a channel. * * Messages in transit can be secured from potential eavesdroppers with SSL/TLS by setting - * [BasePNConfiguration.secure] to `true` during initialization. + * [PNConfiguration.secure] to `true` during initialization. * * **Publish Anytime** * @@ -719,7 +718,7 @@ class PubNubCore internal constructor( * Obtain information about the current list of channels to which a UUID is subscribed to. * * @param uuid UUID of the user to get its current channel subscriptions. Defaults to the UUID of the client. - * @see [BasePNConfiguration.uuid] + * @see [PNConfiguration.uuid] */ fun whereNow(uuid: String = configuration.userId.value) = WhereNowEndpoint(pubnub = this, uuid = uuid) @@ -728,7 +727,7 @@ class PubNubCore internal constructor( * * State information is supplied as a JSON object of key/value pairs. * - * If [BasePNConfiguration.maintainPresenceState] is `true`, and the `uuid` matches [BasePNConfiguration.uuid], the state + * If [PNConfiguration.maintainPresenceState] is `true`, and the `uuid` matches [PNConfiguration.uuid], the state * for channels will be saved in the PubNub client and resent with every heartbeat and initial subscribe request. * In that case, it's not recommended to mix setting state through channels *and* channel groups, as state set * through the channel group will be overwritten after the next heartbeat or subscribe reconnection (e.g. after loss @@ -2022,7 +2021,7 @@ class PubNubCore internal constructor( //region Encryption /** - * Perform Cryptographic decryption of an input string using cipher key provided by [BasePNConfiguration.cipherKey]. + * Perform Cryptographic decryption of an input string using cipher key provided by [PNConfiguration.cipherKey]. * * @param inputString String to be decrypted. * @@ -2036,7 +2035,7 @@ class PubNubCore internal constructor( * Perform Cryptographic decryption of an input string using a cipher key. * * @param inputString String to be decrypted. - * @param cipherKey cipher key to be used for decryption. Default is [BasePNConfiguration.cipherKey] + * @param cipherKey cipher key to be used for decryption. Default is [PNConfiguration.cipherKey] * * @return String containing the decryption of `inputString` using `cipherKey`. * @throws PubNubException throws exception in case of failed decryption. @@ -2066,7 +2065,7 @@ class PubNubCore internal constructor( * Perform Cryptographic encryption of an input string and a cipher key. * * @param inputString String to be encrypted. - * @param cipherKey Cipher key to be used for encryption. Default is [BasePNConfiguration.cipherKey] + * @param cipherKey Cipher key to be used for encryption. Default is [PNConfiguration.cipherKey] * * @return String containing the encryption of `inputString` using `cipherKey`. * @throws PubNubException Throws exception in case of failed encryption. @@ -2246,14 +2245,14 @@ class PubNubCore internal constructor( * Causes the client to create an open TCP socket to the PubNub Real-Time Network and begin listening for messages * on a specified channel. * - * To subscribe to a channel the client must send the appropriate [BasePNConfiguration.subscribeKey] at initialization. + * To subscribe to a channel the client must send the appropriate [PNConfiguration.subscribeKey] at initialization. * * By default, a newly subscribed client will only receive messages published to the channel * after the `subscribe()` call completes. * * If a client gets disconnected from a channel, it can automatically attempt to reconnect to that channel * and retrieve any available messages that were missed during that period. - * This can be achieved by setting [BasePNConfiguration.retryConfiguration] when + * This can be achieved by setting [PNConfiguration.retryConfiguration] when * initializing the client. * * @param channels Channels to subscribe/unsubscribe. Either `channel` or [channelGroups] are required. diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/PubNubUtil.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/PubNubUtil.kt index c531a1cc5..b95a96b8f 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/PubNubUtil.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/PubNubUtil.kt @@ -1,8 +1,7 @@ package com.pubnub.internal import com.pubnub.api.PubNubException -import com.pubnub.api.v2.BasePNConfiguration -import com.pubnub.api.v2.BasePNConfiguration.Companion.isValid +import com.pubnub.api.v2.PNConfiguration.Companion.isValid import okhttp3.Request import okio.Buffer import org.slf4j.LoggerFactory @@ -59,7 +58,7 @@ internal object PubNubUtil { fun signRequest( originalRequest: Request, - pnConfiguration: BasePNConfiguration, + pnConfiguration: PNConfiguration, timestamp: Int, ): Request { // only sign if we have a secret key in place. @@ -81,7 +80,7 @@ internal object PubNubUtil { return originalRequest.newBuilder().url(rebuiltUrl).build() } - fun shouldSignRequest(configuration: BasePNConfiguration): Boolean { + fun shouldSignRequest(configuration: PNConfiguration): Boolean { return configuration.secretKey.isValid() } diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantEndpoint.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantEndpoint.kt index 4b51a26bb..6b1ca191c 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantEndpoint.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantEndpoint.kt @@ -5,7 +5,7 @@ import com.pubnub.api.PubNubError import com.pubnub.api.PubNubException import com.pubnub.api.enums.PNOperationType import com.pubnub.api.retry.RetryableEndpointGroup -import com.pubnub.api.v2.BasePNConfiguration.Companion.isValid +import com.pubnub.api.v2.PNConfiguration.Companion.isValid import com.pubnub.internal.EndpointCore import com.pubnub.internal.PubNubCore import com.pubnub.internal.models.consumer.access_manager.PNAccessManagerGrantResult diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantTokenEndpoint.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantTokenEndpoint.kt index 44888219f..f39e3a0b0 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantTokenEndpoint.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantTokenEndpoint.kt @@ -5,7 +5,7 @@ import com.pubnub.api.PubNubException import com.pubnub.api.enums.PNOperationType import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult import com.pubnub.api.retry.RetryableEndpointGroup -import com.pubnub.api.v2.BasePNConfiguration.Companion.isValid +import com.pubnub.api.v2.PNConfiguration.Companion.isValid import com.pubnub.internal.EndpointCore import com.pubnub.internal.PubNubCore import com.pubnub.internal.models.consumer.access_manager.v3.ChannelGrant diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/RevokeTokenEndpoint.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/RevokeTokenEndpoint.kt index 0fb4d368c..3154ec813 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/RevokeTokenEndpoint.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/RevokeTokenEndpoint.kt @@ -4,7 +4,7 @@ import com.pubnub.api.PubNubError import com.pubnub.api.PubNubException import com.pubnub.api.enums.PNOperationType import com.pubnub.api.retry.RetryableEndpointGroup -import com.pubnub.api.v2.BasePNConfiguration.Companion.isValid +import com.pubnub.api.v2.PNConfiguration.Companion.isValid import com.pubnub.internal.EndpointCore import com.pubnub.internal.PubNubCore import com.pubnub.internal.models.server.access_manager.v3.RevokeTokenResponse diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/interceptor/SignatureInterceptor.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/interceptor/SignatureInterceptor.kt index 28d36f795..568906e9f 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/interceptor/SignatureInterceptor.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/interceptor/SignatureInterceptor.kt @@ -1,15 +1,14 @@ package com.pubnub.internal.interceptor -import com.pubnub.api.v2.BasePNConfiguration import com.pubnub.internal.PubNubCore import com.pubnub.internal.PubNubUtil import okhttp3.Interceptor import okhttp3.Response -class SignatureInterceptor(private val basePNConfiguration: BasePNConfiguration) : Interceptor { +class SignatureInterceptor(private val PNConfiguration: PNConfiguration) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val originalRequest = chain.request() - val request = PubNubUtil.signRequest(originalRequest, basePNConfiguration, PubNubCore.timestamp()) + val request = PubNubUtil.signRequest(originalRequest, PNConfiguration, PubNubCore.timestamp()) return chain.proceed(request) } } diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/BasePathManager.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/BasePathManager.kt index d8853401c..4b390500a 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/BasePathManager.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/BasePathManager.kt @@ -1,9 +1,8 @@ package com.pubnub.internal.managers -import com.pubnub.api.v2.BasePNConfiguration -import com.pubnub.api.v2.BasePNConfiguration.Companion.isValid +import com.pubnub.api.v2.PNConfiguration.Companion.isValid -internal class BasePathManager(private val config: BasePNConfiguration) { +internal class BasePathManager(private val config: PNConfiguration) { /** * for cache busting, the current subdomain number used. */ diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/DuplicationManager.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/DuplicationManager.kt index 15282cdc1..aa1503038 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/DuplicationManager.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/DuplicationManager.kt @@ -1,9 +1,8 @@ package com.pubnub.internal.managers -import com.pubnub.api.v2.BasePNConfiguration import com.pubnub.internal.models.server.SubscribeMessage -internal class DuplicationManager(private val config: BasePNConfiguration) { +internal class DuplicationManager(private val config: PNConfiguration) { private val hashHistory: ArrayList = ArrayList() private fun getKey(message: SubscribeMessage) = diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/ListenerManager.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/ListenerManager.kt index 2c4a54b94..7eb682029 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/ListenerManager.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/ListenerManager.kt @@ -1,6 +1,5 @@ package com.pubnub.internal.managers -import com.pubnub.api.BasePubNub import com.pubnub.api.callbacks.Listener import com.pubnub.api.models.consumer.PNStatus import com.pubnub.api.models.consumer.pubsub.PNEvent @@ -19,7 +18,7 @@ import com.pubnub.internal.v2.callbacks.EventListenerCore import com.pubnub.internal.v2.callbacks.StatusListenerCore import java.util.concurrent.CopyOnWriteArrayList -class ListenerManager(val pubnub: BasePubNub<*, *, *, *, *, *, *, *>) : MessagesConsumer, StatusConsumer, BaseEventEmitter { +class ListenerManager(val pubnub: PubNub) : MessagesConsumer, StatusConsumer, BaseEventEmitter { private val listeners = CopyOnWriteArrayList() private val statusListeners get() = listeners.filterIsInstance() @@ -117,32 +116,32 @@ interface AnnouncementCallback { val phase: Phase fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, envelope: AnnouncementEnvelope, ) fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, envelope: AnnouncementEnvelope, ) fun signal( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, envelope: AnnouncementEnvelope, ) fun messageAction( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, envelope: AnnouncementEnvelope, ) fun objects( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, envelope: AnnouncementEnvelope, ) fun file( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, envelope: AnnouncementEnvelope, ) } diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/RetrofitManager.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/RetrofitManager.kt index d744cfb69..31dd43873 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/RetrofitManager.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/managers/RetrofitManager.kt @@ -1,7 +1,6 @@ package com.pubnub.internal.managers import com.pubnub.api.enums.PNLogVerbosity -import com.pubnub.api.v2.BasePNConfiguration import com.pubnub.internal.PubNubCore import com.pubnub.internal.interceptor.SignatureInterceptor import com.pubnub.internal.services.AccessManagerService @@ -28,7 +27,7 @@ import java.util.concurrent.TimeUnit class RetrofitManager( val pubnub: PubNubCore, - private val configuration: BasePNConfiguration, + private val configuration: PNConfiguration, @get:TestOnly internal var transactionClientInstance: OkHttpClient? = null, @get:TestOnly internal var subscriptionClientInstance: OkHttpClient? = null, @get:TestOnly internal var noSignatureClientInstance: OkHttpClient? = null, @@ -53,7 +52,7 @@ class RetrofitManager( /** * Use to get a new RetrofitManager with shared OkHttpClients while overriding configuration values. */ - constructor(retrofitManager: RetrofitManager, configuration: BasePNConfiguration) : this( + constructor(retrofitManager: RetrofitManager, configuration: PNConfiguration) : this( retrofitManager.pubnub, configuration, retrofitManager.transactionClientInstance, diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/BasePNConfigurationImpl.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/BasePNConfigurationImpl.kt index 295f62953..2168b248e 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/BasePNConfigurationImpl.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/BasePNConfigurationImpl.kt @@ -5,8 +5,7 @@ import com.pubnub.api.crypto.CryptoModule import com.pubnub.api.enums.PNHeartbeatNotificationOptions import com.pubnub.api.enums.PNLogVerbosity import com.pubnub.api.retry.RetryConfiguration -import com.pubnub.api.v2.BasePNConfiguration -import com.pubnub.api.v2.BasePNConfigurationOverride +import com.pubnub.api.v2.PNConfigurationOverride import okhttp3.Authenticator import okhttp3.CertificatePinner import okhttp3.ConnectionSpec @@ -23,7 +22,7 @@ import javax.net.ssl.X509ExtendedTrustManager * allow performing precise PubNub client configuration. * */ -open class BasePNConfigurationImpl internal constructor( +open class PNConfigurationImpl internal constructor( override val userId: UserId, override val subscribeKey: String = "", override val publishKey: String = "", @@ -62,7 +61,7 @@ open class BasePNConfigurationImpl internal constructor( override val pnsdkSuffixes: Map = emptyMap(), override val retryConfiguration: RetryConfiguration = RetryConfiguration.None, override val managePresenceListManually: Boolean = false, -) : BasePNConfiguration { +) : PNConfiguration { companion object { const val DEFAULT_DEDUPE_SIZE = 100 const val PRESENCE_TIMEOUT = 300 @@ -76,7 +75,7 @@ open class BasePNConfigurationImpl internal constructor( userId: UserId, ) : this(userId, "") - abstract class Builder(defaultConfiguration: BasePNConfiguration) : BasePNConfiguration.Builder, BasePNConfigurationOverride.Builder { + abstract class Builder(defaultConfiguration: PNConfiguration) : PNConfiguration.Builder, PNConfigurationOverride.Builder { override val userId: UserId = defaultConfiguration.userId override val subscribeKey: String = defaultConfiguration.subscribeKey diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImpl.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImpl.kt index 7ae454479..1c8c6bf4b 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImpl.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImpl.kt @@ -1,6 +1,5 @@ package com.pubnub.internal.v2.callbacks -import com.pubnub.api.BasePubNub import com.pubnub.api.callbacks.Listener import com.pubnub.api.models.consumer.pubsub.PNEvent import com.pubnub.api.models.consumer.pubsub.PNMessageResult @@ -36,7 +35,7 @@ class EventEmitterImpl( // EventEmitter fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { listeners.forEach { @@ -45,7 +44,7 @@ class EventEmitterImpl( } fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { listeners.forEach { @@ -54,7 +53,7 @@ class EventEmitterImpl( } fun signal( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnSignalResult: PNSignalResult, ) { listeners.forEach { @@ -63,7 +62,7 @@ class EventEmitterImpl( } fun messageAction( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageActionResult: PNMessageActionResult, ) { listeners.forEach { @@ -72,7 +71,7 @@ class EventEmitterImpl( } fun objects( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, objectEvent: PNObjectEventResult, ) { listeners.forEach { @@ -81,7 +80,7 @@ class EventEmitterImpl( } fun file( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnFileEventResult: PNFileEventResult, ) { listeners.forEach { @@ -92,7 +91,7 @@ class EventEmitterImpl( // AnnouncementCallback override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, envelope: AnnouncementEnvelope, ) { if (accepts(envelope)) { @@ -101,7 +100,7 @@ class EventEmitterImpl( } override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, envelope: AnnouncementEnvelope, ) { if (accepts(envelope)) { @@ -110,7 +109,7 @@ class EventEmitterImpl( } override fun signal( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, envelope: AnnouncementEnvelope, ) { if (accepts(envelope)) { @@ -119,7 +118,7 @@ class EventEmitterImpl( } override fun messageAction( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, envelope: AnnouncementEnvelope, ) { if (accepts(envelope)) { @@ -128,7 +127,7 @@ class EventEmitterImpl( } override fun objects( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, envelope: AnnouncementEnvelope, ) { if (accepts(envelope)) { @@ -137,7 +136,7 @@ class EventEmitterImpl( } override fun file( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, envelope: AnnouncementEnvelope, ) { if (accepts(envelope)) { diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventListenerCore.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventListenerCore.kt index 4b348aa48..147109adc 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventListenerCore.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventListenerCore.kt @@ -1,6 +1,5 @@ package com.pubnub.internal.v2.callbacks -import com.pubnub.api.BasePubNub import com.pubnub.api.callbacks.Listener import com.pubnub.api.models.consumer.pubsub.PNMessageResult import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult @@ -19,7 +18,7 @@ interface EventListenerCore : Listener { * @param event Wrapper around the actual message content. */ fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNMessageResult, ) {} @@ -32,7 +31,7 @@ interface EventListenerCore : Listener { * @param event Wrapper around a presence event. */ fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNPresenceEventResult, ) {} @@ -45,7 +44,7 @@ interface EventListenerCore : Listener { * @param event Wrapper around a signal event. */ fun signal( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNSignalResult, ) {} @@ -56,7 +55,7 @@ interface EventListenerCore : Listener { * @param event Wrapper around a message action event. */ fun messageAction( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNMessageActionResult, ) {} @@ -67,7 +66,7 @@ interface EventListenerCore : Listener { * @param event Wrapper around the object event. */ fun objects( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNObjectEventResult, ) {} @@ -78,7 +77,7 @@ interface EventListenerCore : Listener { * @param event Wrapper around the file event. */ fun file( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNFileEventResult, ) {} } diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/StatusListenerCore.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/StatusListenerCore.kt index 02587574a..b17293227 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/StatusListenerCore.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/StatusListenerCore.kt @@ -1,6 +1,5 @@ package com.pubnub.internal.v2.callbacks -import com.pubnub.api.BasePubNub import com.pubnub.api.callbacks.Listener import com.pubnub.api.models.consumer.PNStatus @@ -14,7 +13,7 @@ interface StatusListenerCore : Listener { * @param status Wrapper around the actual message content. */ fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, status: PNStatus, ) } diff --git a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessor.kt b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessor.kt index 8ed6304ce..7d2bdca3d 100644 --- a/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessor.kt +++ b/pubnub-core/pubnub-core-impl/src/main/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessor.kt @@ -12,8 +12,7 @@ import com.pubnub.api.models.consumer.pubsub.PNSignalResult import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult import com.pubnub.api.models.consumer.pubsub.objects.ObjectPayload -import com.pubnub.api.v2.BasePNConfiguration -import com.pubnub.api.v2.BasePNConfiguration.Companion.isValid +import com.pubnub.api.v2.PNConfiguration.Companion.isValid import com.pubnub.internal.PubNubCore import com.pubnub.internal.PubNubUtil import com.pubnub.internal.extension.tryDecryptMessage @@ -215,7 +214,7 @@ internal class SubscribeMessageProcessor( } private fun generateSignature( - configuration: BasePNConfiguration, + configuration: PNConfiguration, url: String, authKey: String?, timestamp: Int, diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/BasePNConfigurationImplTest.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/BasePNConfigurationImplTest.kt index bba619f3a..d2d729bc8 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/BasePNConfigurationImplTest.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/BasePNConfigurationImplTest.kt @@ -2,14 +2,14 @@ package com.pubnub.api import com.pubnub.internal.BasePubNubImpl import com.pubnub.internal.TestPubNub -import com.pubnub.internal.v2.BasePNConfigurationImpl +import com.pubnub.internal.v2.PNConfigurationImpl import org.junit.Assert.assertEquals import org.junit.Test -class BasePNConfigurationImplTest { +class PNConfigurationImplTest { @Test fun testDefaultTimeoutValues() { - val p = TestPubNub(BasePNConfigurationImpl(userId = UserId(BasePubNubImpl.generateUUID()))) + val p = TestPubNub(PNConfigurationImpl(userId = UserId(BasePubNubImpl.generateUUID()))) assertEquals(300, p.pubNubCore.configuration.presenceTimeout) assertEquals(0, p.pubNubCore.configuration.heartbeatInterval) p.forceDestroy() diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/endpoints/access/GrantTokenTest.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/endpoints/access/GrantTokenTest.kt index ab4378e42..b4f610c92 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/endpoints/access/GrantTokenTest.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/endpoints/access/GrantTokenTest.kt @@ -12,7 +12,7 @@ import com.pubnub.internal.models.server.access_manager.v3.GrantTokenData import com.pubnub.internal.models.server.access_manager.v3.GrantTokenRequestBody import com.pubnub.internal.models.server.access_manager.v3.GrantTokenResponse import com.pubnub.internal.services.AccessManagerService -import com.pubnub.internal.v2.BasePNConfigurationImpl +import com.pubnub.internal.v2.PNConfigurationImpl import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK @@ -32,7 +32,7 @@ internal class GrantTokenTest { internal fun setUp() { MockKAnnotations.init(this) val pnConfiguration = - BasePNConfigurationImpl( + PNConfigurationImpl( userId = UserId("myUserId"), subscribeKey = "something", secretKey = "something", diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/endpoints/pubsub/PublishTest.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/endpoints/pubsub/PublishTest.kt index 543c15d8e..4ed5996b1 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/endpoints/pubsub/PublishTest.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/endpoints/pubsub/PublishTest.kt @@ -7,7 +7,7 @@ import com.pubnub.internal.TestPubNub import com.pubnub.internal.endpoints.pubsub.PublishEndpoint import com.pubnub.internal.managers.RetrofitManager import com.pubnub.internal.services.PublishService -import com.pubnub.internal.v2.BasePNConfigurationImpl +import com.pubnub.internal.v2.PNConfigurationImpl import io.mockk.every import io.mockk.mockk import io.mockk.slot @@ -30,14 +30,14 @@ private const val CIPHER_KEY = "enigma" class PublishTest { private val pnConfigurationHardcodedIV = - BasePNConfigurationImpl( + PNConfigurationImpl( userId = UserId(BasePubNubImpl.generateUUID()), subscribeKey = TEST_SUBKEY, publishKey = TEST_PUBKEY, cryptoModule = CryptoModule.createLegacyCryptoModule(CIPHER_KEY, false), ) private val pnConfigurationRandomIV = - BasePNConfigurationImpl( + PNConfigurationImpl( userId = UserId(BasePubNubImpl.generateUUID()), subscribeKey = TEST_SUBKEY, publishKey = TEST_PUBKEY, diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/EndpointCoreTest.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/EndpointCoreTest.kt index 9bcbd9210..768a03db6 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/EndpointCoreTest.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/EndpointCoreTest.kt @@ -14,7 +14,7 @@ import com.pubnub.api.legacy.BaseTest import com.pubnub.api.retry.RetryableEndpointGroup import com.pubnub.internal.BasePubNubImpl import com.pubnub.internal.EndpointCore -import com.pubnub.internal.v2.BasePNConfigurationImpl +import com.pubnub.internal.v2.PNConfigurationImpl import com.pubnub.test.listen import okhttp3.Request import okio.Timeout @@ -104,7 +104,7 @@ class EndpointCoreTest : BaseTest() { assertNull(it["requestid"]) }.apply { overrideConfiguration( - BasePNConfigurationImpl( + PNConfigurationImpl( userId = config.userId, includeRequestIdentifier = false, includeInstanceIdentifier = false, @@ -132,7 +132,7 @@ class EndpointCoreTest : BaseTest() { fakeEndpoint { assertEquals(expectedUuid.value, it["uuid"]) }.apply { - overrideConfiguration(BasePNConfigurationImpl(userId = expectedUuid)) + overrideConfiguration(PNConfigurationImpl(userId = expectedUuid)) }.sync() } diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/SendFileTest.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/SendFileTest.kt index 692449bb8..70298fa69 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/SendFileTest.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/SendFileTest.kt @@ -6,7 +6,6 @@ import com.pubnub.api.models.consumer.files.PNBaseFile import com.pubnub.api.models.consumer.files.PNFileUploadResult import com.pubnub.api.models.consumer.files.PNPublishFileMessageResult import com.pubnub.api.retry.RetryConfiguration -import com.pubnub.api.v2.BasePNConfiguration import com.pubnub.api.v2.callbacks.Result import com.pubnub.internal.PubNubCore import com.pubnub.internal.TestPubNub @@ -204,7 +203,7 @@ class SendFileTest : TestsWithFiles { } private fun getPubNubMock(): TestPubNub { - val mockConfig = mockk() + val mockConfig = mockk() val mockPubNub = mockk() val mockPubNubImpl = mockk() val retryConfiguration = RetryConfiguration.None diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/ReceiveMessageActions.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/ReceiveMessageActions.kt index 9b12782af..9c2b9fdb1 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/ReceiveMessageActions.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/ReceiveMessageActions.kt @@ -4,7 +4,6 @@ import com.github.tomakehurst.wiremock.client.WireMock.aResponse import com.github.tomakehurst.wiremock.client.WireMock.get import com.github.tomakehurst.wiremock.client.WireMock.stubFor import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo -import com.pubnub.api.BasePubNub import com.pubnub.api.legacy.BaseTest import com.pubnub.api.models.consumer.PNStatus import com.pubnub.api.models.consumer.pubsub.PNMessageResult @@ -68,34 +67,34 @@ class ReceiveMessageActions : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { } override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { failTest() } override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { failTest() } override fun signal( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnSignalResult: PNSignalResult, ) { failTest() } override fun messageAction( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageActionResult: PNMessageActionResult, ) { assertEquals(pnMessageActionResult.channel, "coolChannel") @@ -188,13 +187,13 @@ class ReceiveMessageActions : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { } override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { failTest() @@ -202,21 +201,21 @@ class ReceiveMessageActions : BaseTest() { } override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { failTest() } override fun signal( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnSignalResult: PNSignalResult, ) { failTest() } override fun messageAction( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageActionResult: PNMessageActionResult, ) { count.incrementAndGet() diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/SignalTest.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/SignalTest.kt index 988016b4b..a3db6f76f 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/SignalTest.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/SignalTest.kt @@ -6,7 +6,6 @@ import com.github.tomakehurst.wiremock.client.WireMock.get import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor import com.github.tomakehurst.wiremock.client.WireMock.stubFor import com.github.tomakehurst.wiremock.client.WireMock.urlMatching -import com.pubnub.api.BasePubNub import com.pubnub.api.PubNubError import com.pubnub.api.PubNubException import com.pubnub.api.legacy.BaseTest @@ -138,7 +137,7 @@ class SignalTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun signal( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnSignalResult: PNSignalResult, ) { assertEquals("coolChannel", pnSignalResult.channel) @@ -148,22 +147,22 @@ class SignalTest : BaseTest() { } override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) {} override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) {} override fun messageAction( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageActionResult: PNMessageActionResult, ) {} }, diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/managers/SubscriptionManagerTest.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/managers/SubscriptionManagerTest.kt index 85f32051b..f126ac01c 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/managers/SubscriptionManagerTest.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/api/legacy/managers/SubscriptionManagerTest.kt @@ -12,7 +12,6 @@ import com.github.tomakehurst.wiremock.client.WireMock.urlMatching import com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching import com.github.tomakehurst.wiremock.matching.UrlPathPattern import com.google.gson.reflect.TypeToken -import com.pubnub.api.BasePubNub import com.pubnub.api.crypto.CryptoModule import com.pubnub.api.enums.PNHeartbeatNotificationOptions import com.pubnub.api.enums.PNStatusCategory @@ -140,21 +139,21 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { gotMessages.addAndGet(1) } override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { gotMessages.addAndGet(1) } override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { gotMessages.addAndGet(1) @@ -211,21 +210,21 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { gotMessages.addAndGet(1) } override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { gotMessages.addAndGet(1) } override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { gotMessages.addAndGet(1) @@ -356,7 +355,7 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { @@ -365,7 +364,7 @@ class SubscriptionManagerTest : BaseTest() { } override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) @@ -464,14 +463,14 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { gotMessages.addAndGet(1) } override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} }, @@ -559,12 +558,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { gotMessages.addAndGet(1) @@ -675,12 +674,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { gotMessages.addAndGet(1) @@ -730,7 +729,7 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNConnectionError && pnStatus.exception?.statusCode == 403) { @@ -792,7 +791,7 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { @@ -802,7 +801,7 @@ class SubscriptionManagerTest : BaseTest() { } override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) @@ -865,7 +864,7 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { @@ -874,7 +873,7 @@ class SubscriptionManagerTest : BaseTest() { } override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) @@ -942,7 +941,7 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { @@ -951,7 +950,7 @@ class SubscriptionManagerTest : BaseTest() { } override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) @@ -1092,13 +1091,13 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { } override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { println("--==" + pnMessageResult.message) @@ -1175,12 +1174,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) @@ -1246,12 +1245,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) @@ -1338,7 +1337,7 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { // do nothing @@ -1441,12 +1440,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) @@ -1507,12 +1506,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) @@ -1594,12 +1593,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { val requests = @@ -1653,12 +1652,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) @@ -1718,12 +1717,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) @@ -1784,12 +1783,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { val requests = @@ -1855,12 +1854,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { if (atomic.get() == 0) { @@ -1925,12 +1924,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { if (atomic.get() == 0) { @@ -1999,12 +1998,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { if (atomic.get() == 0) { @@ -2073,12 +2072,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { if (atomic.get() == 0) { @@ -2147,12 +2146,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { if (atomic.get() == 0) { @@ -2236,12 +2235,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { if (pnPresenceEventResult.event == "state-change") { @@ -2306,12 +2305,12 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) {} override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) @@ -2344,21 +2343,21 @@ class SubscriptionManagerTest : BaseTest() { val sub1: SubscribeCallback = object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { atomic.addAndGet(1) } override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { atomic.addAndGet(1) } override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { atomic.addAndGet(1) @@ -2464,7 +2463,7 @@ class SubscriptionManagerTest : BaseTest() { val sub1: SubscribeCallback = object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { @@ -2480,7 +2479,7 @@ class SubscriptionManagerTest : BaseTest() { } override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { val requests = @@ -2573,7 +2572,7 @@ class SubscriptionManagerTest : BaseTest() { val sub1: SubscribeCallback = object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNHeartbeatSuccess) { @@ -2617,7 +2616,7 @@ class SubscriptionManagerTest : BaseTest() { val sub1: SubscribeCallback = object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNHeartbeatSuccess) { @@ -2665,7 +2664,7 @@ class SubscriptionManagerTest : BaseTest() { val sub1: SubscribeCallback = object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { println(pnStatus) @@ -2742,7 +2741,7 @@ class SubscriptionManagerTest : BaseTest() { val sub1: SubscribeCallback = object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNHeartbeatFailed) { @@ -2820,7 +2819,7 @@ class SubscriptionManagerTest : BaseTest() { val sub1: SubscribeCallback = object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNHeartbeatFailed) { @@ -2880,7 +2879,7 @@ class SubscriptionManagerTest : BaseTest() { val sub1: SubscribeCallback = object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNHeartbeatSuccess) { @@ -2959,7 +2958,7 @@ class SubscriptionManagerTest : BaseTest() { val sub1: SubscribeCallback = object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category != PNStatusCategory.PNHeartbeatSuccess) { @@ -3027,7 +3026,7 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { @@ -3097,7 +3096,7 @@ class SubscriptionManagerTest : BaseTest() { pubnubBase.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { @@ -3240,7 +3239,7 @@ class SubscriptionManagerTest : BaseTest() { val sub1: SubscribeCallback = object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/access/step/ThenSteps.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/access/step/ThenSteps.kt index 698fb2fdc..d70300ee3 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/access/step/ThenSteps.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/access/step/ThenSteps.kt @@ -29,21 +29,21 @@ class ThenSteps( @Then("the token contains the authorized UUID {string}") fun the_token_contains_the_authorized_uuid(uuid: String) { val result = grantTokenState.result!! - val parsedToken = world.pubnub.pubNubCore.parseToken(result.token) + val parsedToken = world.pubnub.parseToken(result.token) MatcherAssert.assertThat(parsedToken.authorizedUUID, Matchers.`is`(uuid)) } @Then("the token contains the TTL {ttl}") fun the_token_contains_the_ttl(ttl: Long) { val result = grantTokenState.result!! - val token = world.pubnub.pubNubCore.parseToken(result.token) + val token = world.pubnub.parseToken(result.token) MatcherAssert.assertThat(token.ttl, Matchers.`is`(ttl)) } @Then("the token does not contain an authorized uuid") fun the_token_does_not_contain_an_authorized_uuid() { val result = grantTokenState.result!! - val token = world.pubnub.pubNubCore.parseToken(result.token) + val token = world.pubnub.parseToken(result.token) MatcherAssert.assertThat(token.authorizedUUID, Matchers.nullValue()) } @@ -107,6 +107,6 @@ class ThenSteps( private fun parsedToken(): PNToken? { return grantTokenState.parsedToken - ?: grantTokenState.result?.let { world.pubnub.pubNubCore.parseToken(it.token) } + ?: grantTokenState.result?.let { world.pubnub.parseToken(it.token) } } } diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/access/step/WhenSteps.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/access/step/WhenSteps.kt index c249346fc..225e2be41 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/access/step/WhenSteps.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/access/step/WhenSteps.kt @@ -18,7 +18,7 @@ class WhenSteps( val definedGrants = grantTokenState.definedGrants.map { it.evaluate() } @Suppress("DEPRECATION") grantTokenState.result = - world.pubnub.pubNubCore.grantToken( + world.pubnub.grantToken( ttl = grantTokenState.TTL?.toInt() ?: throw RuntimeException("TTL expected"), authorizedUUID = grantTokenState.authorizedUUID, channels = definedGrants.filterIsInstance(ChannelGrant::class.java), @@ -43,13 +43,13 @@ class WhenSteps( @When("I parse the token") fun i_parse_the_token() { - grantTokenState.parsedToken = world.pubnub.pubNubCore.parseToken(grantTokenState.result?.token!!) + grantTokenState.parsedToken = world.pubnub.parseToken(grantTokenState.result?.token!!) } @When("I revoke a token") fun i_revoke_the_token() { try { - world.pubnub.pubNubCore.revokeToken(world.tokenString!!).sync() + world.pubnub.revokeToken(world.tokenString!!).sync() } catch (e: PubNubException) { world.pnException = e } @@ -57,8 +57,8 @@ class WhenSteps( @When("I publish a message using that auth token with channel {string}") fun i_publish_a_message_using_that_auth_token_with_channel(channel: String) { - world.pubnub.pubNubCore.setToken(world.tokenString) - world.pubnub.pubNubCore.publish( + world.pubnub.setToken(world.tokenString) + world.pubnub.publish( channel = channel, message = "Message", ).sync() @@ -66,10 +66,10 @@ class WhenSteps( @When("I attempt to publish a message using that auth token with channel {string}") fun i_attempt_to_publish_a_message_using_that_auth_token_with_channel(channel: String) { - world.pubnub.pubNubCore.setToken(world.tokenString) + world.pubnub.setToken(world.tokenString) try { - world.pubnub.pubNubCore.publish( + world.pubnub.publish( channel = channel, message = "Message", ).sync() diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/WhenSteps.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/WhenSteps.kt index de57d3909..b3f63ac57 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/WhenSteps.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/WhenSteps.kt @@ -10,7 +10,7 @@ class WhenSteps( ) { @When("I get the channel metadata") fun i_get_the_channel_metadata() { - world.pubnub.pubNubCore.getChannelMetadata(channel = channelMetadataState.channelId).sync()?.let { + world.pubnub.getChannelMetadata(channel = channelMetadataState.channelId).sync()?.let { channelMetadataState.channelMetadata = it.data world.responseStatus = it.status } @@ -19,7 +19,7 @@ class WhenSteps( @When("I set the channel metadata") fun i_set_the_channel_metadata() { val channelMetadata = channelMetadataState.channelMetadata!! - world.pubnub.pubNubCore.setChannelMetadata( + world.pubnub.setChannelMetadata( channel = channelMetadata.id, name = channelMetadata.name?.value, description = channelMetadata.description?.value, @@ -34,7 +34,7 @@ class WhenSteps( @When("I get the channel metadata with custom") fun i_get_the_channel_metadata_with_custom() { - world.pubnub.pubNubCore.getChannelMetadata( + world.pubnub.getChannelMetadata( channel = channelMetadataState.channelId, includeCustom = true, ).sync().let { @@ -46,14 +46,14 @@ class WhenSteps( @When("I remove the channel metadata") fun i_remove_the_channel_metadata() { world.responseStatus = - world.pubnub.pubNubCore.removeChannelMetadata( + world.pubnub.removeChannelMetadata( channel = channelMetadataState.channelId, ).sync().status } @When("I get all channel metadata") fun i_get_all_channel_metadata() { - world.pubnub.pubNubCore.getAllChannelMetadata().sync().let { + world.pubnub.getAllChannelMetadata().sync().let { channelMetadataState.channelMetadatas = it.data world.responseStatus = it.status } @@ -61,7 +61,7 @@ class WhenSteps( @When("I get all channel metadata with custom") fun i_get_all_channel_metadata_with_custom() { - world.pubnub.pubNubCore.getAllChannelMetadata( + world.pubnub.getAllChannelMetadata( includeCustom = true, ).sync().let { channelMetadataState.channelMetadatas = it.data diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/member/step/WhenSteps.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/member/step/WhenSteps.kt index 708a95319..1af09ac06 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/member/step/WhenSteps.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/member/step/WhenSteps.kt @@ -12,7 +12,7 @@ class WhenSteps( ) { @When("I get the channel members") fun i_get_the_channel_members() { - world.pubnub.pubNubCore.getChannelMembers(channel = memberState.channelId()).sync()?.let { + world.pubnub.getChannelMembers(channel = memberState.channelId()).sync()?.let { memberState.returnedMembers = it.data world.responseStatus = it.status } @@ -21,7 +21,7 @@ class WhenSteps( @When("I set a channel member including custom and UUID with custom") fun i_set_a_channel_member_including_custom_and_uuid_with_custom() { val uuids = memberState.members.map { PNMember.Partial(uuidId = it.uuid?.id!!) } - world.pubnub.pubNubCore.setChannelMembers( + world.pubnub.setChannelMembers( channel = memberState.channelId(), includeCustom = true, includeUUIDDetails = PNUUIDDetailsLevel.UUID_WITH_CUSTOM, @@ -34,7 +34,7 @@ class WhenSteps( @When("I get the channel members including custom and UUID custom information") fun i_get_the_channel_members_including_custom_and_uuid_custom_information() { - world.pubnub.pubNubCore.getChannelMembers( + world.pubnub.getChannelMembers( channel = memberState.channelId(), includeCustom = true, includeUUIDDetails = PNUUIDDetailsLevel.UUID_WITH_CUSTOM, @@ -47,7 +47,7 @@ class WhenSteps( @When("I set a channel member") fun i_set_a_channel_member() { val uuids = memberState.members.map { PNMember.Partial(uuidId = it.uuid?.id!!) } - world.pubnub.pubNubCore.setChannelMembers( + world.pubnub.setChannelMembers( channel = memberState.channelId(), uuids = uuids, ).sync()?.let { @@ -59,7 +59,7 @@ class WhenSteps( @When("I remove a channel member") fun i_remove_a_channel_member() { val uuids = memberState.membersToRemove.map { it.uuid?.id!! } - world.pubnub.pubNubCore.removeChannelMembers( + world.pubnub.removeChannelMembers( channel = memberState.channelId(), uuids = uuids, ).sync()?.let { @@ -72,7 +72,7 @@ class WhenSteps( fun i_manage_channel_members() { val uuidsToSet = memberState.members.map { PNMember.Partial(uuidId = it.uuid?.id!!) } val uuidsToRemove = memberState.membersToRemove.map { it.uuid?.id!! } - world.pubnub.pubNubCore.manageChannelMembers( + world.pubnub.manageChannelMembers( channel = memberState.channelId(), uuidsToSet = uuidsToSet, uuidsToRemove = uuidsToRemove, diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/membership/step/WhenSteps.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/membership/step/WhenSteps.kt index b9056a3a4..c6b641a17 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/membership/step/WhenSteps.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/membership/step/WhenSteps.kt @@ -12,7 +12,7 @@ class WhenSteps( ) { @When("I get the memberships") fun i_get_the_memberships() { - world.pubnub.pubNubCore.getMemberships(uuid = membershipState.uuid()).sync().let { + world.pubnub.getMemberships(uuid = membershipState.uuid()).sync().let { membershipState.returnedMemberships = it.data world.responseStatus = it.status } @@ -20,7 +20,7 @@ class WhenSteps( @When("I get the memberships for current user") fun i_get_the_memberships_for_current_user() { - world.pubnub.pubNubCore.getMemberships().sync().let { + world.pubnub.getMemberships().sync().let { membershipState.returnedMemberships = it.data world.responseStatus = it.status } @@ -28,7 +28,7 @@ class WhenSteps( @When("I get the memberships including custom and channel custom information") fun i_get_the_memberships_including_custom_and_channel_custom_information() { - world.pubnub.pubNubCore.getMemberships( + world.pubnub.getMemberships( uuid = membershipState.uuid(), includeCustom = true, includeChannelDetails = PNChannelDetailsLevel.CHANNEL_WITH_CUSTOM, @@ -49,7 +49,7 @@ class WhenSteps( ) } - world.pubnub.pubNubCore.setMemberships( + world.pubnub.setMemberships( uuid = membershipState.uuid(), channels = channels, ).sync().let { @@ -69,7 +69,7 @@ class WhenSteps( ) } - world.pubnub.pubNubCore.setMemberships( + world.pubnub.setMemberships( channels = channels, ).sync().let { world.responseStatus = it.status @@ -80,7 +80,7 @@ class WhenSteps( @When("I remove the membership") fun i_remove_the_membership() { val channels = membershipState.memberships.map { it.channel.id } - world.pubnub.pubNubCore.removeMemberships( + world.pubnub.removeMemberships( uuid = membershipState.uuid(), channels = channels, ).sync().let { @@ -92,7 +92,7 @@ class WhenSteps( @When("I remove the membership for current user") fun i_remove_the_membership_for_current_user() { val channels = membershipState.memberships.map { it.channel.id } - world.pubnub.pubNubCore.removeMemberships( + world.pubnub.removeMemberships( channels = channels, ).sync().let { world.responseStatus = it.status @@ -111,7 +111,7 @@ class WhenSteps( ) } val channelsToRemove = membershipState.membershipsToRemove.map { it.channel.id } - world.pubnub.pubNubCore.manageMemberships( + world.pubnub.manageMemberships( channelsToSet = channelsToSet, channelsToRemove = channelsToRemove, uuid = membershipState.uuid(), diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/presence/eventEngine/step/PresenceEventEngineSteps.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/presence/eventEngine/step/PresenceEventEngineSteps.kt index 8c1163648..b2884247c 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/presence/eventEngine/step/PresenceEventEngineSteps.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/presence/eventEngine/step/PresenceEventEngineSteps.kt @@ -1,6 +1,5 @@ package com.pubnub.contract.presence.eventEngine.step -import com.pubnub.api.BasePubNub import com.pubnub.api.models.consumer.PNStatus import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult import com.pubnub.contract.subscribe.eventEngine.state.EventEngineState @@ -37,7 +36,7 @@ class PresenceEventEngineSteps(private val state: EventEngineState) { secondChannel: String, thirdChannel: String, ) { - state.pubnub.pubNubCore.subscribe(channels = listOf(firstChannel, secondChannel, thirdChannel)) + state.pubnub.subscribe(channels = listOf(firstChannel, secondChannel, thirdChannel)) } @When("I join {string}, {string}, {string} channels with presence") @@ -46,7 +45,7 @@ class PresenceEventEngineSteps(private val state: EventEngineState) { secondChannel: String, thirdChannel: String, ) { - state.pubnub.pubNubCore.subscribe( + state.pubnub.subscribe( channels = listOf(firstChannel, secondChannel, thirdChannel), withPresence = true, ) @@ -68,14 +67,14 @@ class PresenceEventEngineSteps(private val state: EventEngineState) { state.pubnub.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { // do nothing } override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { if (pnPresenceEventResult.event == "join" && (pnPresenceEventResult.channel == "first" || pnPresenceEventResult.channel == "second" || pnPresenceEventResult.channel == "third")) { @@ -102,7 +101,7 @@ class PresenceEventEngineSteps(private val state: EventEngineState) { firstChannel: String, secondChannel: String, ) { - state.pubnub.pubNubCore.unsubscribe(channels = listOf(firstChannel, secondChannel)) + state.pubnub.unsubscribe(channels = listOf(firstChannel, secondChannel)) } @Then("I observe the following Events and Invocations of the Presence EE:") @@ -132,6 +131,6 @@ class PresenceEventEngineSteps(private val state: EventEngineState) { firstChannel: String, secondChannel: String, ) { - state.pubnub.pubNubCore.unsubscribe(channels = listOf(firstChannel, secondChannel)) + state.pubnub.unsubscribe(channels = listOf(firstChannel, secondChannel)) } } diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/step/EventEngineSteps.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/step/EventEngineSteps.kt index 2b21d1f35..5c15c40cb 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/step/EventEngineSteps.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/step/EventEngineSteps.kt @@ -1,6 +1,5 @@ package com.pubnub.contract.subscribe.eventEngine.step -import com.pubnub.api.BasePubNub import com.pubnub.api.models.consumer.PNStatus import com.pubnub.api.models.consumer.pubsub.PNMessageResult import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult @@ -35,49 +34,49 @@ class EventEngineSteps(private val state: EventEngineState) { state.pubnub.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { state.statusesList.add(pnStatus) } override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { state.messagesList.add(pnMessageResult) } override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { state.messagesList.add(pnPresenceEventResult) } override fun signal( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnSignalResult: PNSignalResult, ) { state.messagesList.add(pnSignalResult) } override fun messageAction( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageActionResult: PNMessageActionResult, ) { state.messagesList.add(pnMessageActionResult) } override fun objects( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, objectEvent: PNObjectEventResult, ) { state.messagesList.add(objectEvent) } override fun file( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnFileEventResult: PNFileEventResult, ) { state.messagesList.add(pnFileEventResult) @@ -85,7 +84,7 @@ class EventEngineSteps(private val state: EventEngineState) { }, ) - state.pubnub.pubNubCore.subscribe(channels = listOf(state.channelName)) + state.pubnub.subscribe(channels = listOf(state.channelName)) } @When("I subscribe with timetoken {long}") @@ -93,49 +92,49 @@ class EventEngineSteps(private val state: EventEngineState) { state.pubnub.addListener( object : SubscribeCallback { override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnStatus: PNStatus, ) { state.statusesList.add(pnStatus) } override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageResult: PNMessageResult, ) { state.messagesList.add(pnMessageResult) } override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnPresenceEventResult: PNPresenceEventResult, ) { state.messagesList.add(pnPresenceEventResult) } override fun signal( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnSignalResult: PNSignalResult, ) { state.messagesList.add(pnSignalResult) } override fun messageAction( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnMessageActionResult: PNMessageActionResult, ) { state.messagesList.add(pnMessageActionResult) } override fun objects( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, objectEvent: PNObjectEventResult, ) { state.messagesList.add(objectEvent) } override fun file( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, pnFileEventResult: PNFileEventResult, ) { state.messagesList.add(pnFileEventResult) @@ -143,7 +142,7 @@ class EventEngineSteps(private val state: EventEngineState) { }, ) - state.pubnub.pubNubCore.subscribe( + state.pubnub.subscribe( channels = listOf(state.channelName), withTimetoken = timetoken, ) diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/WhenSteps.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/WhenSteps.kt index 8e045606b..a9f20c82b 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/WhenSteps.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/WhenSteps.kt @@ -10,7 +10,7 @@ class WhenSteps( ) { @When("I get the UUID metadata") fun i_get_uuid_metadata() { - world.pubnub.pubNubCore.getUUIDMetadata(uuid = uuidMetadataState.uuid).sync()?.let { + world.pubnub.getUUIDMetadata(uuid = uuidMetadataState.uuid).sync()?.let { uuidMetadataState.uuidMetadata = it.data world.responseStatus = it.status } @@ -18,7 +18,7 @@ class WhenSteps( @When("I get the UUID metadata with custom for current user") fun i_get_uuid_metadata_with_custom_for_current_user() { - world.pubnub.pubNubCore.getUUIDMetadata(includeCustom = true).sync()?.let { + world.pubnub.getUUIDMetadata(includeCustom = true).sync()?.let { uuidMetadataState.uuidMetadata = it.data world.responseStatus = it.status } @@ -27,7 +27,7 @@ class WhenSteps( @When("I set the UUID metadata") fun i_set_the_uuid_metadata() { val uuidMetadata = uuidMetadataState.uuidMetadata!! - world.pubnub.pubNubCore.setUUIDMetadata( + world.pubnub.setUUIDMetadata( uuid = uuidMetadata.id, custom = uuidMetadata.custom, externalId = uuidMetadata.externalId?.value, @@ -44,17 +44,17 @@ class WhenSteps( @When("I remove the UUID metadata") fun i_remove_uuid_metadata_for_id() { - world.responseStatus = world.pubnub.pubNubCore.removeUUIDMetadata(uuid = uuidMetadataState.uuid).sync()?.status + world.responseStatus = world.pubnub.removeUUIDMetadata(uuid = uuidMetadataState.uuid).sync()?.status } @When("I remove the UUID metadata for current user") fun i_remove_uuid_metadata_of_current_user() { - world.responseStatus = world.pubnub.pubNubCore.removeUUIDMetadata().sync()?.status + world.responseStatus = world.pubnub.removeUUIDMetadata().sync()?.status } @When("I get all UUID metadata") fun i_get_all_uuid_metadata() { - world.pubnub.pubNubCore.getAllUUIDMetadata().sync()?.let { + world.pubnub.getAllUUIDMetadata().sync()?.let { uuidMetadataState.uuidMetadatas = it.data world.responseStatus = it.status } @@ -62,7 +62,7 @@ class WhenSteps( @When("I get all UUID metadata with custom") fun i_get_all_uuid_metadata_with_custom() { - world.pubnub.pubNubCore.getAllUUIDMetadata(includeCustom = true).sync()?.let { + world.pubnub.getAllUUIDMetadata(includeCustom = true).sync()?.let { uuidMetadataState.uuidMetadatas = it.data world.responseStatus = it.status } diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/TestPubNub.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/TestPubNub.kt index d60f69405..8f2207267 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/TestPubNub.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/TestPubNub.kt @@ -2,7 +2,6 @@ package com.pubnub.internal import com.pubnub.api.models.consumer.access_manager.v3.PNToken import com.pubnub.api.models.consumer.pubsub.PNMessageResult -import com.pubnub.api.v2.BasePNConfiguration import com.pubnub.api.v2.callbacks.BaseEventListener import com.pubnub.api.v2.callbacks.BaseStatusListener import com.pubnub.internal.callbacks.SubscribeCallback @@ -16,7 +15,7 @@ import com.pubnub.internal.v2.subscription.BaseSubscriptionSetImpl import java.io.InputStream class TestPubNub internal constructor( - configuration: BasePNConfiguration, + configuration: PNConfiguration, eventEnginesConf: EventEnginesConf = EventEnginesConf(), ) : BasePubNubImpl< diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImplTest.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImplTest.kt index 7ea9e98bb..4d5e7f634 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImplTest.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImplTest.kt @@ -1,7 +1,6 @@ package com.pubnub.internal.v2.callbacks import com.google.gson.JsonPrimitive -import com.pubnub.api.BasePubNub import com.pubnub.api.UserId import com.pubnub.api.models.consumer.files.PNDownloadableFile import com.pubnub.api.models.consumer.message_actions.PNMessageAction @@ -16,7 +15,7 @@ import com.pubnub.internal.managers.AnnouncementCallback import com.pubnub.internal.managers.AnnouncementEnvelope import com.pubnub.internal.models.consumer.pubsub.objects.PNDeleteUUIDMetadataEventMessage import com.pubnub.internal.models.consumer.pubsub.objects.PNObjectEventResult -import com.pubnub.internal.v2.BasePNConfigurationImpl +import com.pubnub.internal.v2.PNConfigurationImpl import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach @@ -24,7 +23,7 @@ import org.junit.jupiter.api.Test class EventEmitterImplTest { lateinit var emitterImpl: EventEmitterImpl - val testPubNub = TestPubNub(BasePNConfigurationImpl(UserId("aa"))) + val testPubNub = TestPubNub(PNConfigurationImpl(UserId("aa"))) val basePubSubResult = BasePubSubResult("a", null, null, null, null) val message = JsonPrimitive(1) @@ -71,7 +70,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNMessageResult, ) { success = true @@ -88,7 +87,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNMessageResult, ) { success = true @@ -105,7 +104,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNPresenceEventResult, ) { success = true @@ -124,7 +123,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNPresenceEventResult, ) { success = true @@ -143,7 +142,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun signal( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNSignalResult, ) { success = true @@ -162,7 +161,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun signal( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNSignalResult, ) { success = true @@ -181,7 +180,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun messageAction( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNMessageActionResult, ) { success = true @@ -200,7 +199,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun messageAction( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNMessageActionResult, ) { success = true @@ -219,7 +218,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun objects( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNObjectEventResult, ) { success = true @@ -238,7 +237,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun objects( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNObjectEventResult, ) { success = true @@ -260,7 +259,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun file( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNFileEventResult, ) { success = true @@ -279,7 +278,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun file( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNFileEventResult, ) { success = true @@ -305,7 +304,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNMessageResult, ) { success = true @@ -329,7 +328,7 @@ class EventEmitterImplTest { emitterImpl.addListener( object : EventListenerCore { override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNMessageResult, ) { success = true diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelGroupImplTest.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelGroupImplTest.kt index 7d3be8ab8..43b012f43 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelGroupImplTest.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelGroupImplTest.kt @@ -4,7 +4,7 @@ import com.pubnub.api.UserId import com.pubnub.api.v2.callbacks.BaseEventListener import com.pubnub.api.v2.subscriptions.SubscriptionOptions import com.pubnub.internal.TestPubNub -import com.pubnub.internal.v2.BasePNConfigurationImpl +import com.pubnub.internal.v2.PNConfigurationImpl import com.pubnub.internal.v2.subscription.BaseSubscriptionImpl import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions @@ -18,7 +18,7 @@ class BaseChannelGroupImplTest { @BeforeEach fun setUp() { - pn = TestPubNub(BasePNConfigurationImpl(UserId("uuid"))) + pn = TestPubNub(PNConfigurationImpl(UserId("uuid"))) channelGrp = BaseChannelGroupImpl(pn.pubNubCore, ChannelGroupName(channelGroupname)) { channels, channelGroups, options -> object : BaseSubscriptionImpl(pn.pubNubCore, channels, channelGroups, options) { diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelImplTest.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelImplTest.kt index 5cbf11352..c66f5b991 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelImplTest.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelImplTest.kt @@ -4,7 +4,7 @@ import com.pubnub.api.UserId import com.pubnub.api.v2.callbacks.BaseEventListener import com.pubnub.api.v2.subscriptions.SubscriptionOptions import com.pubnub.internal.TestPubNub -import com.pubnub.internal.v2.BasePNConfigurationImpl +import com.pubnub.internal.v2.PNConfigurationImpl import com.pubnub.internal.v2.subscription.BaseSubscriptionImpl import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions @@ -18,7 +18,7 @@ class BaseChannelImplTest { @BeforeEach fun setUp() { - pn = TestPubNub(BasePNConfigurationImpl(UserId("uuid"))) + pn = TestPubNub(PNConfigurationImpl(UserId("uuid"))) channel = BaseChannelImpl(pn.pubNubCore, ChannelName(channelName)) { channels, channelGroups, options -> object : BaseSubscriptionImpl(pn.pubNubCore, channels, channelGroups, options) { diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionImplTest.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionImplTest.kt index 07eb80fca..f643e4510 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionImplTest.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionImplTest.kt @@ -1,14 +1,13 @@ package com.pubnub.internal.v2.subscriptions import com.google.gson.JsonNull -import com.pubnub.api.BasePubNub import com.pubnub.api.UserId import com.pubnub.api.models.consumer.pubsub.BasePubSubResult import com.pubnub.api.models.consumer.pubsub.PNMessageResult import com.pubnub.api.v2.subscriptions.SubscriptionOptions import com.pubnub.internal.TestEventListener import com.pubnub.internal.TestPubNub -import com.pubnub.internal.v2.BasePNConfigurationImpl +import com.pubnub.internal.v2.PNConfigurationImpl import com.pubnub.internal.v2.callbacks.EventListenerCore import com.pubnub.internal.v2.entities.ChannelName import com.pubnub.internal.v2.subscription.BaseSubscriptionImpl @@ -24,14 +23,14 @@ class BaseSubscriptionImplTest { @BeforeEach fun setUp() { - pubnub = TestPubNub(BasePNConfigurationImpl(UserId("uuid"))) + pubnub = TestPubNub(PNConfigurationImpl(UserId("uuid"))) subscription = - object : BaseSubscriptionImpl(pubnub.pubNubCore, setOf(ChannelName(channelName))) { + object : BaseSubscriptionImpl(pubnub, setOf(ChannelName(channelName))) { override fun addListener(listener: TestEventListener) { addListener( object : EventListenerCore { override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNMessageResult, ) { listener.message(event) @@ -54,8 +53,8 @@ class BaseSubscriptionImplTest { subscription.subscribe() // then - assertEquals(listOf(channelName), pubnub.pubNubCore.getSubscribedChannels()) - assertEquals(emptyList(), pubnub.pubNubCore.getSubscribedChannelGroups()) + assertEquals(listOf(channelName), pubnub.getSubscribedChannels()) + assertEquals(emptyList(), pubnub.getSubscribedChannelGroups()) } @Test @@ -67,8 +66,8 @@ class BaseSubscriptionImplTest { subscription.unsubscribe() // then - assertEquals(emptyList(), pubnub.pubNubCore.getSubscribedChannels()) - assertEquals(emptyList(), pubnub.pubNubCore.getSubscribedChannelGroups()) + assertEquals(emptyList(), pubnub.getSubscribedChannels()) + assertEquals(emptyList(), pubnub.getSubscribedChannelGroups()) } @Test @@ -89,8 +88,8 @@ class BaseSubscriptionImplTest { // then // no exception from listener - assertEquals(emptyList(), pubnub.pubNubCore.getSubscribedChannels()) - assertEquals(emptyList(), pubnub.pubNubCore.getSubscribedChannelGroups()) + assertEquals(emptyList(), pubnub.getSubscribedChannels()) + assertEquals(emptyList(), pubnub.getSubscribedChannelGroups()) } @Test @@ -98,7 +97,7 @@ class BaseSubscriptionImplTest { // given val subWithFilter = object : BaseSubscriptionImpl( - pubnub.pubNubCore, + pubnub, setOf( ChannelName(channelName), ), diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionSetImplTest.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionSetImplTest.kt index 693a7149e..453494230 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionSetImplTest.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionSetImplTest.kt @@ -1,13 +1,12 @@ package com.pubnub.internal.v2.subscriptions import com.google.gson.JsonNull -import com.pubnub.api.BasePubNub import com.pubnub.api.UserId import com.pubnub.api.models.consumer.pubsub.BasePubSubResult import com.pubnub.api.models.consumer.pubsub.PNMessageResult import com.pubnub.internal.TestEventListener import com.pubnub.internal.TestPubNub -import com.pubnub.internal.v2.BasePNConfigurationImpl +import com.pubnub.internal.v2.PNConfigurationImpl import com.pubnub.internal.v2.callbacks.EventListenerCore import com.pubnub.internal.v2.entities.ChannelName import com.pubnub.internal.v2.subscription.BaseSubscriptionImpl @@ -27,15 +26,15 @@ class BaseSubscriptionSetImplTest { @BeforeEach fun setUp() { - pubnub = TestPubNub(BasePNConfigurationImpl(UserId("uuid"))) + pubnub = TestPubNub(PNConfigurationImpl(UserId("uuid"))) subscriptionSet = object : - BaseSubscriptionSetImpl>(pubnub.pubNubCore) { + BaseSubscriptionSetImpl>(pubnub) { override fun addListener(listener: TestEventListener) { addListener( object : EventListenerCore { override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, + pubnub: PubNub, event: PNMessageResult, ) { listener.message(event) @@ -46,7 +45,7 @@ class BaseSubscriptionSetImplTest { } subscriptionSet.add( - object : BaseSubscriptionImpl(pubnub.pubNubCore, setOf(ChannelName(channelName))) { + object : BaseSubscriptionImpl(pubnub, setOf(ChannelName(channelName))) { override fun addListener(listener: TestEventListener) { TODO("Not yet implemented") } @@ -54,7 +53,7 @@ class BaseSubscriptionSetImplTest { ) anotherSubscription = - object : BaseSubscriptionImpl(pubnub.pubNubCore, setOf(ChannelName("anotherChannel"))) { + object : BaseSubscriptionImpl(pubnub, setOf(ChannelName("anotherChannel"))) { override fun addListener(listener: TestEventListener) { TODO("Not yet implemented") } @@ -78,7 +77,7 @@ class BaseSubscriptionSetImplTest { // then assertTrue(subscriptionSet.subscriptions.contains(anotherSubscription)) - assertEquals(setOf(channelName, "anotherChannel"), pubnub.pubNubCore.getSubscribedChannels().toSet()) + assertEquals(setOf(channelName, "anotherChannel"), pubnub.getSubscribedChannels().toSet()) } @Test @@ -92,7 +91,7 @@ class BaseSubscriptionSetImplTest { // then assertFalse(subscriptionSet.subscriptions.contains(anotherSubscription)) - assertEquals(setOf(channelName), pubnub.pubNubCore.getSubscribedChannels().toSet()) + assertEquals(setOf(channelName), pubnub.getSubscribedChannels().toSet()) } @Test @@ -101,7 +100,7 @@ class BaseSubscriptionSetImplTest { subscriptionSet.subscribe() // then - assertEquals(setOf(channelName), pubnub.pubNubCore.getSubscribedChannels().toSet()) + assertEquals(setOf(channelName), pubnub.getSubscribedChannels().toSet()) } @Test @@ -113,7 +112,7 @@ class BaseSubscriptionSetImplTest { subscriptionSet.unsubscribe() // then - assertEquals(emptyList(), pubnub.pubNubCore.getSubscribedChannels()) + assertEquals(emptyList(), pubnub.getSubscribedChannels()) } @Test @@ -140,7 +139,7 @@ class BaseSubscriptionSetImplTest { // then // no exception from listener - assertEquals(emptyList(), pubnub.pubNubCore.getSubscribedChannels()) - assertEquals(emptyList(), pubnub.pubNubCore.getSubscribedChannelGroups()) + assertEquals(emptyList(), pubnub.getSubscribedChannels()) + assertEquals(emptyList(), pubnub.getSubscribedChannelGroups()) } } diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessorTest.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessorTest.kt index d5ac123df..0c30592b1 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessorTest.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessorTest.kt @@ -12,7 +12,6 @@ import com.pubnub.api.UserId import com.pubnub.api.crypto.CryptoModule import com.pubnub.api.models.consumer.pubsub.PNMessageResult import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult -import com.pubnub.api.v2.BasePNConfiguration import com.pubnub.internal.TestPubNub import com.pubnub.internal.crypto.encryptString import com.pubnub.internal.managers.DuplicationManager @@ -109,7 +108,7 @@ class SubscribeMessageProcessorTest( fun testProcessMessageEncryptedWithCrypto() { // given val gson = Gson() - val config: BasePNConfiguration = config { cryptoModule = CryptoModule.createAesCbcCryptoModule("enigma", false) } + val config: PNConfiguration = config { cryptoModule = CryptoModule.createAesCbcCryptoModule("enigma", false) } val subscribeMessageProcessor = messageProcessor(config) val messageEncrypted = config.cryptoModule!!.encryptString(messageJson.toString()) @@ -132,7 +131,7 @@ class SubscribeMessageProcessorTest( fun testProcessMessageUnencryptedWithCrypto() { // given val gson = Gson() - val config: BasePNConfiguration = config { cryptoModule = CryptoModule.createAesCbcCryptoModule("enigma", false) } + val config: PNConfiguration = config { cryptoModule = CryptoModule.createAesCbcCryptoModule("enigma", false) } val subscribeMessageProcessor = messageProcessor(config) @@ -154,7 +153,7 @@ class SubscribeMessageProcessorTest( fun testProcessMessageWithPnOtherEncryptedWithCrypto() { // given val gson = Gson() - val config: BasePNConfiguration = config { cryptoModule = CryptoModule.createAesCbcCryptoModule("enigma", false) } + val config: PNConfiguration = config { cryptoModule = CryptoModule.createAesCbcCryptoModule("enigma", false) } val subscribeMessageProcessor = messageProcessor(config) val message = "Hello world." @@ -185,7 +184,7 @@ class SubscribeMessageProcessorTest( }, ) = TestPNConfigurationImpl.Builder(userId = UserId("test")).apply(action).build() - private fun messageProcessor(configuration: BasePNConfiguration) = + private fun messageProcessor(configuration: PNConfiguration) = SubscribeMessageProcessor( pubnub = TestPubNub(configuration).pubNubCore, duplicationManager = DuplicationManager(configuration), diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/test/SignatureUtils.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/test/SignatureUtils.kt index af39800dd..4bab4a6a1 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/test/SignatureUtils.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/test/SignatureUtils.kt @@ -1,7 +1,6 @@ package com.pubnub.test import com.github.tomakehurst.wiremock.verification.LoggedRequest -import com.pubnub.api.v2.BasePNConfiguration import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.Request import okio.Buffer @@ -19,7 +18,7 @@ import javax.crypto.spec.SecretKeySpec object SignatureUtils { fun decomposeAndVerifySignature( - configuration: BasePNConfiguration, + configuration: PNConfiguration, request: LoggedRequest, ) { decomposeAndVerifySignature( @@ -31,7 +30,7 @@ object SignatureUtils { } fun decomposeAndVerifySignature( - configuration: BasePNConfiguration, + configuration: PNConfiguration, request: Request, ) { decomposeAndVerifySignature( @@ -43,7 +42,7 @@ object SignatureUtils { } private fun decomposeAndVerifySignature( - configuration: BasePNConfiguration, + configuration: PNConfiguration, url: String, method: String, body: String = "", diff --git a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/test/TestPNConfigurationImpl.kt b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/test/TestPNConfigurationImpl.kt index 20aa73beb..ef31d4269 100644 --- a/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/test/TestPNConfigurationImpl.kt +++ b/pubnub-core/pubnub-core-impl/src/test/kotlin/com/pubnub/test/TestPNConfigurationImpl.kt @@ -5,7 +5,7 @@ import com.pubnub.api.crypto.CryptoModule import com.pubnub.api.enums.PNHeartbeatNotificationOptions import com.pubnub.api.enums.PNLogVerbosity import com.pubnub.api.retry.RetryConfiguration -import com.pubnub.internal.v2.BasePNConfigurationImpl +import com.pubnub.internal.v2.PNConfigurationImpl import okhttp3.Authenticator import okhttp3.CertificatePinner import okhttp3.ConnectionSpec @@ -55,9 +55,9 @@ class TestPNConfigurationImpl( override val pnsdkSuffixes: Map, override val retryConfiguration: RetryConfiguration, override val managePresenceListManually: Boolean, -) : BasePNConfigurationImpl(userId) { +) : PNConfigurationImpl(userId) { class Builder internal constructor(override var userId: UserId) : - BasePNConfigurationImpl.Builder(BasePNConfigurationImpl(userId)) { + PNConfigurationImpl.Builder(PNConfigurationImpl(userId)) { /** * The subscribe key from the admin panel. */ @@ -298,11 +298,11 @@ class TestPNConfigurationImpl( * using [PubNubCore.presence]. Should be used only with ACL set on the server side. * For more information please contact PubNub support * @see PubNubCore.presence - * @see BasePNConfigurationImpl.heartbeatInterval + * @see PNConfigurationImpl.heartbeatInterval */ override var managePresenceListManually: Boolean = super.managePresenceListManually - fun build(): BasePNConfigurationImpl { + fun build(): PNConfigurationImpl { return TestPNConfigurationImpl( userId = userId, subscribeKey = subscribeKey, diff --git a/pubnub-gson/build.gradle.kts b/pubnub-gson/build.gradle.kts index 4b8dd2eee..4d40b7329 100644 --- a/pubnub-gson/build.gradle.kts +++ b/pubnub-gson/build.gradle.kts @@ -7,10 +7,10 @@ plugins { } dependencies { - api(project(":pubnub-core:pubnub-core-api")) +// api(project(":pubnub-core:pubnub-kotlin-api")) api(project(":pubnub-gson:pubnub-gson-api")) - implementation(project(":pubnub-core:pubnub-core-impl")) implementation(project(":pubnub-gson:pubnub-gson-impl")) +// implementation(project(":pubnub-core:pubnub-core-impl")) } checkstyle { diff --git a/pubnub-gson/pubnub-gson-api/build.gradle.kts b/pubnub-gson/pubnub-gson-api/build.gradle.kts index 1e2c29c06..e3e6f8554 100644 --- a/pubnub-gson/pubnub-gson-api/build.gradle.kts +++ b/pubnub-gson/pubnub-gson-api/build.gradle.kts @@ -8,8 +8,8 @@ plugins { } dependencies { - api(project(":pubnub-core:pubnub-core-api")) - implementation(project(":pubnub-core:pubnub-core-impl")) + api(project(":pubnub-kotlin:pubnub-kotlin-api")) +// implementation(project(":pubnub-core:pubnub-core-impl")) implementation(libs.slf4j) implementation(libs.jetbrains.annotations) diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/PNConfiguration.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/PNConfiguration.kt deleted file mode 100644 index 8ad0a1dc7..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/PNConfiguration.kt +++ /dev/null @@ -1,700 +0,0 @@ -package com.pubnub.api - -import com.pubnub.api.crypto.CryptoModule -import com.pubnub.api.enums.PNHeartbeatNotificationOptions -import com.pubnub.api.enums.PNLogVerbosity -import com.pubnub.api.enums.PNReconnectionPolicy -import com.pubnub.api.retry.RetryConfiguration -import com.pubnub.api.v2.BasePNConfiguration -import com.pubnub.internal.v2.BasePNConfigurationImpl -import okhttp3.Authenticator -import okhttp3.CertificatePinner -import okhttp3.ConnectionSpec -import okhttp3.logging.HttpLoggingInterceptor -import org.slf4j.LoggerFactory -import java.net.Proxy -import java.net.ProxySelector -import javax.net.ssl.HostnameVerifier -import javax.net.ssl.SSLSocketFactory -import javax.net.ssl.X509ExtendedTrustManager - -@Deprecated( - message = "Use `com.pubnub.api.v2.PNConfiguration.builder()` instead.", - replaceWith = ReplaceWith("PNConfiguration.builder(userId, subscribeKey = )", "com.pubnub.api.v2.PNConfiguration"), -) -class PNConfiguration(userId: UserId) : BasePNConfiguration { - @Deprecated( - replaceWith = ReplaceWith( - "PNConfiguration(userId = UserId(uuid))", - "com.pubnub.api.PNConfiguration", - ), - level = DeprecationLevel.WARNING, - message = "Use PNConfiguration(UserId) instead.", - ) - @Throws(PubNubException::class) - constructor(uuid: String) : this(UserId(uuid)) - - private val defaultConfiguration = BasePNConfigurationImpl(userId) - - override var userId: UserId = userId - private set - - fun setUserId(userId: UserId): PNConfiguration { - this.userId = userId - return this - } - - @Deprecated( - "Use UserId instead e.g. config.userId.value", - replaceWith = ReplaceWith("userId.value"), - level = DeprecationLevel.WARNING, - ) - override val uuid - get() = userId.value - - @Throws(PubNubException::class) - fun setUuid(uuid: String) { - userId = UserId(uuid) - } - - /** - * The subscribe key from the admin panel. - */ - fun setSubscribeKey(subscribeKey: String): PNConfiguration { - this.subscribeKey = subscribeKey - return this - } - - override var subscribeKey: String = defaultConfiguration.subscribeKey - private set - - /** - * The publish key from the admin panel (only required if publishing). - */ - fun setPublishKey(publishKey: String): PNConfiguration { - this.publishKey = publishKey - return this - } - - override var publishKey: String = defaultConfiguration.publishKey - private set - - /** - * The secret key from the admin panel (only required for modifying/revealing access permissions). - * - * Keep away from Android. - */ - fun setSecretKey(secretKey: String): PNConfiguration { - this.secretKey = secretKey - return this - } - - override var secretKey: String = defaultConfiguration.secretKey - private set - - /** - * If Access Manager is utilized, client will use this authKey in all restricted requests. - */ - fun setAuthKey(authKey: String): PNConfiguration { - this.authKey = authKey - return this - } - - override var authKey: String = defaultConfiguration.authKey - private set - - /** - * If set, all communications to and from PubNub will be encrypted. - */ - @Deprecated( - """Instead of cipherKey and useRandomInitializationVector use CryptoModule instead - e.g. config.cryptoModule = CryptoModule.createLegacyCryptoModule(cipherKey = cipherKey, randomIv = true) - or config.cryptoModule = CryptoModule.createAesCbcCryptoModule(cipherKey = cipherKey, randomIv = true)""", - level = DeprecationLevel.WARNING, - ) - var cipherKey: String? = null - private set - - /** - * If set, all communications to and from PubNub will be encrypted. - */ - fun setCipherKey(cipherKey: String?): PNConfiguration { - this.cipherKey = cipherKey - return this - } - - /** - * Should initialization vector for encrypted messages be random. - * - * Defaults to `true`. - */ - @Deprecated( - """Instead of cipherKey and useRandomInitializationVector use CryptoModule instead - e.g. config.cryptoModule = CryptoModule.createLegacyCryptoModule(cipherKey = cipherKey, randomIv = true) - or config.cryptoModule = CryptoModule.createAesCbcCryptoModule(cipherKey = cipherKey, randomIv = true)""", - level = DeprecationLevel.WARNING, - ) - var useRandomInitializationVector: Boolean = true - private set - - /** - * Should initialization vector for encrypted messages be random. - * - * Defaults to `true`. - */ - fun setUseRandomInitializationVector(useRandomInitializationVector: Boolean): PNConfiguration { - this.useRandomInitializationVector = useRandomInitializationVector - return this - } - - /** - * CryptoModule is responsible for handling encryption and decryption. - * If set, all communications to and from PubNub will be encrypted. - */ - fun setCryptoModule(cryptoModule: CryptoModule?): PNConfiguration { - this.cryptoModule = cryptoModule - return this - } - - override var cryptoModule: CryptoModule? = defaultConfiguration.cryptoModule - get() = field ?: cipherKey?.let { cipherKey -> - if (cipherKey.isNotBlank()) { - log.warn("cipherKey is deprecated. Use CryptoModule instead") - field = - CryptoModule.createLegacyCryptoModule( - cipherKey = cipherKey, - randomIv = useRandomInitializationVector, - ) - field - } else { - null - } - } - private set - - /** - * Custom origin if needed. - * - * Defaults to `ps.pndsn.com` - */ - fun setOrigin(origin: String): PNConfiguration { - this.origin = origin - return this - } - - override var origin: String = defaultConfiguration.origin - private set - - /** - * If set to `true`, requests will be made over HTTPS. - * - * Deafults to `true`. - */ - fun setSecure(secure: Boolean): PNConfiguration { - this.secure = secure - return this - } - - override var secure: Boolean = defaultConfiguration.secure - private set - - /** - * Set to [PNLogVerbosity.BODY] to enable logging of network traffic, otherwise se to [PNLogVerbosity.NONE]. - */ - fun setLogVerbosity(logVerbosity: PNLogVerbosity): PNConfiguration { - this.logVerbosity = logVerbosity - return this - } - - override var logVerbosity: PNLogVerbosity = defaultConfiguration.logVerbosity - private set - - /** - * Set Heartbeat notification options. - * - * By default, the SDK alerts on failed heartbeats (equivalent to [PNHeartbeatNotificationOptions.FAILURES]). - */ - fun setHeartbeatNotificationOptions(heartbeatNotificationOptions: PNHeartbeatNotificationOptions): PNConfiguration { - this.heartbeatNotificationOptions = heartbeatNotificationOptions - return this - } - - override var heartbeatNotificationOptions: PNHeartbeatNotificationOptions = - defaultConfiguration.heartbeatNotificationOptions - private set - - /** - * Sets the custom presence server timeout. - * - * The value is in seconds, and the minimum value is 20 seconds. - * - * Also sets the value of [heartbeatInterval] - */ - fun setPresenceTimeout(presenceTimeout: Int): PNConfiguration { - val timeout = validatePresenceTimeout(presenceTimeout) - setPresenceTimeoutWithCustomInterval(timeout, (timeout / 2) - 1) - return this - } - - /** - * Set presence configurations for timeout and announce interval. - * - * @param timeout presence timeout; how long before the server considers this client to be gone. - * @param interval presence announce interval, how often the client should announce itself. - * @return returns itself. - */ - fun setPresenceTimeoutWithCustomInterval( - timeout: Int, - interval: Int, - ): PNConfiguration { - val newTimeout = validatePresenceTimeout(timeout) - presenceTimeout = newTimeout - heartbeatInterval = interval - return this - } - - override var presenceTimeout: Int = defaultConfiguration.presenceTimeout - private set - - /** - * How often the client will announce itself to server. - * - * The value is in seconds. - */ - fun setHeartbeatInterval(heartbeatInterval: Int): PNConfiguration { - this.heartbeatInterval = heartbeatInterval - return this - } - - override var heartbeatInterval: Int = defaultConfiguration.heartbeatInterval - private set - - /** - * The subscribe request timeout. - * - * The value is in seconds. - * - * Defaults to 310. - */ - fun setSubscribeTimeout(subscribeTimeout: Int): PNConfiguration { - this.subscribeTimeout = subscribeTimeout - return this - } - - override var subscribeTimeout: Int = defaultConfiguration.subscribeTimeout - private set - - /** - * How long before the client gives up trying to connect with the server. - * - * The value is in seconds. - * - * Defaults to 5. - */ - fun setConnectTimeout(connectTimeout: Int): PNConfiguration { - this.connectTimeout = connectTimeout - return this - } - - override var connectTimeout: Int = defaultConfiguration.connectTimeout - private set - - override var nonSubscribeReadTimeout: Int = defaultConfiguration.nonSubscribeReadTimeout - private set - - /** - * For non subscribe operations (publish, herenow, etc), - * This property relates to a read timeout that is applied from the moment the connection between a client - * and the server has been successfully established. It defines a maximum time of inactivity between two - * data packets when waiting for the serverโ€™s response. - * - * The value is in seconds. - * - * Defaults to 10. - */ - fun setNonSubscribeReadTimeout(nonSubscribeReadTimeout: Int): PNConfiguration { - this.nonSubscribeReadTimeout = nonSubscribeReadTimeout - return this - } - - /** - * For non subscribe operations (publish, herenow, etc), - * This property relates to a read timeout that is applied from the moment the connection between a client - * and the server has been successfully established. It defines a maximum time of inactivity between two - * data packets when waiting for the serverโ€™s response. - * - * The value is in seconds. - * - * Defaults to 10. - */ - @Deprecated( - "This setting relates to *read* timeout and was renamed to `nonSubscribeReadTimeout`", - replaceWith = ReplaceWith("nonSubscribeReadTimeout") - ) - fun setNonSubscribeRequestTimeout(nonSubscribeRequestTimeout: Int): PNConfiguration { - return this.setNonSubscribeReadTimeout(nonSubscribeRequestTimeout) - } - - /** - * If operating behind a misbehaving proxy, allow the client to shuffle the subdomains. - * - * Defaults to `false`. - */ - fun setCacheBusting(cacheBusting: Boolean): PNConfiguration { - this.cacheBusting = cacheBusting - return this - } - - override var cacheBusting: Boolean = defaultConfiguration.cacheBusting - private set - - /** - * When `true` the SDK doesn't send out the leave requests. - * - * Defaults to `false`. - */ - fun setSuppressLeaveEvents(suppressLeaveEvents: Boolean): PNConfiguration { - this.suppressLeaveEvents = suppressLeaveEvents - return this - } - - override var suppressLeaveEvents: Boolean = defaultConfiguration.suppressLeaveEvents - private set - - /** - * When `true` the SDK will resend the last channel state that was set using [PubNub.setPresenceState] - * for the current [userId] with every automatic heartbeat (if [heartbeatInterval] is greater than 0) - * and initial subscribe connection (also after e.g. loss of network). - * - * Defaults to `true`. - * - * Please note that `maintainPresenceState` doesn't apply to state that was set on channel groups. - * It is recommended to disable this option if you set state for channel groups using [PubNub.setPresenceState] - * otherwise that state may be overwritten by individual channel states. - */ - fun setMaintainPresenceState(maintainPresenceState: Boolean): PNConfiguration { - this.maintainPresenceState = maintainPresenceState - return this - } - - override var maintainPresenceState: Boolean = defaultConfiguration.maintainPresenceState - private set - - /** - * Feature to subscribe with a custom filter expression. - */ - fun setFilterExpression(filterExpression: String): PNConfiguration { - this.filterExpression = filterExpression - return this - } - - override var filterExpression: String = defaultConfiguration.filterExpression - private set - - /** - * Whether to include a [PubNubCore.instanceId] with every request. - * - * Defaults to `false`. - */ - fun setIncludeInstanceIdentifier(includeInstanceIdentifier: Boolean): PNConfiguration { - this.includeInstanceIdentifier = includeInstanceIdentifier - return this - } - - override var includeInstanceIdentifier: Boolean = defaultConfiguration.includeInstanceIdentifier - private set - - /** - * Whether to include a [PubNubCore.requestId] with every request. - * - * Defaults to `true`. - */ - fun setIncludeRequestIdentifier(includeRequestIdentifier: Boolean): PNConfiguration { - this.includeRequestIdentifier = includeRequestIdentifier - return this - } - - override var includeRequestIdentifier: Boolean = defaultConfiguration.includeRequestIdentifier - private set - - /** - * @see [okhttp3.Dispatcher.setMaxRequestsPerHost] - */ - fun setMaximumConnections(maximumConnections: Int?): PNConfiguration { - this.maximumConnections = maximumConnections - return this - } - - override var maximumConnections: Int? = defaultConfiguration.maximumConnections - private set - - /** - * Enable Google App Engine networking. - * - * Defaults to `false`. - */ - fun setGoogleAppEngineNetworking(googleAppEngineNetworking: Boolean): PNConfiguration { - this.googleAppEngineNetworking = googleAppEngineNetworking - return this - } - - override var googleAppEngineNetworking: Boolean = defaultConfiguration.googleAppEngineNetworking - private set - - /** - * Instructs the SDK to use a proxy configuration when communicating with PubNub servers. - * - * @see [Proxy] - */ - fun setProxy(proxy: Proxy?): PNConfiguration { - this.proxy = proxy - return this - } - - override var proxy: Proxy? = defaultConfiguration.proxy - private set - - /** - * @see [ProxySelector] - */ - fun setProxySelector(proxySelector: ProxySelector?): PNConfiguration { - this.proxySelector = proxySelector - return this - } - - override var proxySelector: ProxySelector? = defaultConfiguration.proxySelector - private set - - /** - * @see [Authenticator] - */ - fun setProxyAuthenticator(proxyAuthenticator: Authenticator?): PNConfiguration { - this.proxyAuthenticator = proxyAuthenticator - return this - } - - override var proxyAuthenticator: Authenticator? = defaultConfiguration.proxyAuthenticator - private set - - /** - * @see [CertificatePinner] - */ - fun setCertificatePinner(certificatePinner: CertificatePinner?): PNConfiguration { - this.certificatePinner = certificatePinner - return this - } - - override var certificatePinner: CertificatePinner? = defaultConfiguration.certificatePinner - private set - - /** - * Sets a custom [HttpLoggingInterceptor] for logging network traffic. - * - * @see [HttpLoggingInterceptor] - */ - fun setHttpLoggingInterceptor(httpLoggingInterceptor: HttpLoggingInterceptor?): PNConfiguration { - this.httpLoggingInterceptor = httpLoggingInterceptor - return this - } - - override var httpLoggingInterceptor: HttpLoggingInterceptor? = defaultConfiguration.httpLoggingInterceptor - private set - - /** - * @see [SSLSocketFactory] - */ - fun setSslSocketFactory(sslSocketFactory: SSLSocketFactory?): PNConfiguration { - this.sslSocketFactory = sslSocketFactory - return this - } - - override var sslSocketFactory: SSLSocketFactory? = defaultConfiguration.sslSocketFactory - private set - - /** - * @see [X509ExtendedTrustManager] - */ - fun setX509ExtendedTrustManager(x509ExtendedTrustManager: X509ExtendedTrustManager?): PNConfiguration { - this.x509ExtendedTrustManager = x509ExtendedTrustManager - return this - } - - override var x509ExtendedTrustManager: X509ExtendedTrustManager? = defaultConfiguration.x509ExtendedTrustManager - private set - - /** - * @see [okhttp3.ConnectionSpec] - */ - fun setConnectionSpec(connectionSpec: ConnectionSpec?): PNConfiguration { - this.connectionSpec = connectionSpec - return this - } - - override var connectionSpec: ConnectionSpec? = defaultConfiguration.connectionSpec - private set - - /** - * @see [javax.net.ssl.HostnameVerifier] - */ - fun setHostnameVerifier(hostnameVerifier: HostnameVerifier?): PNConfiguration { - this.hostnameVerifier = hostnameVerifier - return this - } - - override var hostnameVerifier: HostnameVerifier? = defaultConfiguration.hostnameVerifier - private set - - /** - * How many times publishing file message should automatically retry before marking the action as failed - * - * Defaults to `5` - */ - fun setFileMessagePublishRetryLimit(fileMessagePublishRetryLimit: Int): PNConfiguration { - this.fileMessagePublishRetryLimit = fileMessagePublishRetryLimit - return this - } - - override var fileMessagePublishRetryLimit: Int = defaultConfiguration.fileMessagePublishRetryLimit - private set - - fun setDedupOnSubscribe(dedupOnSubscribe: Boolean): PNConfiguration { - this.dedupOnSubscribe = dedupOnSubscribe - return this - } - - override var dedupOnSubscribe: Boolean = defaultConfiguration.dedupOnSubscribe - private set - - fun setMaximumMessagesCacheSize(maximumMessagesCacheSize: Int): PNConfiguration { - this.maximumMessagesCacheSize = maximumMessagesCacheSize - return this - } - - override var maximumMessagesCacheSize: Int = defaultConfiguration.maximumMessagesCacheSize - private set - - fun setPnsdkSuffixes(pnSdkSuffixes: Map): PNConfiguration { - this.pnsdkSuffixes = pnSdkSuffixes - return this - } - - override var pnsdkSuffixes: Map = defaultConfiguration.pnsdkSuffixes - private set - - /** - * Retry configuration for requests. - * Defaults to [RetryConfiguration.None]. - * - * Use [RetryConfiguration.Linear] to set retry with linear delay interval - * Use [RetryConfiguration.Exponential] to set retry with exponential delay interval - * Delay will valy from provided value by random value. - */ - fun setRetryConfiguration(retryConfiguration: RetryConfiguration): PNConfiguration { - this.retryConfiguration = retryConfiguration - return this - } - - override var retryConfiguration: RetryConfiguration = defaultConfiguration.retryConfiguration - private set - - /** - * Enables explicit presence control. - * When set to true heartbeat calls will contain only channels and groups added explicitly - * using [PubNubCore.presence]. Should be used only with ACL set on the server side. - * For more information please contact PubNub support - * @see PubNubCore.presence - * @see BasePNConfigurationImpl.heartbeatInterval - */ - fun setManagePresenceListManually(managePresenceListManually: Boolean): PNConfiguration { - this.managePresenceListManually = managePresenceListManually - return this - } - - override var managePresenceListManually: Boolean = defaultConfiguration.managePresenceListManually - private set - - /** - * Set to [PNReconnectionPolicy.LINEAR] for automatic reconnects. - * - * Use [PNReconnectionPolicy.NONE] to disable automatic reconnects. - * - * Use [PNReconnectionPolicy.EXPONENTIAL] to set exponential retry interval. - * - * Defaults to [PNReconnectionPolicy.NONE]. - */ - @Deprecated( - """Instead of reconnectionPolicy and maximumReconnectionRetries use retryConfiguration - e.g. config.retryConfiguration = RetryConfiguration.Linear(delayInSec = 3, maxRetryNumber = 5) - or config.retryConfiguration = RetryConfiguration.Exponential(minDelayInSec = 3, maxDelayInSec = 10, maxRetryNumber = 5)""", - level = DeprecationLevel.WARNING, - ) - var reconnectionPolicy: PNReconnectionPolicy = PNReconnectionPolicy.NONE - private set - - fun setReconnectionPolicy(reconnectionPolicy: PNReconnectionPolicy): PNConfiguration { - this.reconnectionPolicy = reconnectionPolicy - calculateRetryConfiguration() - return this - } - - private fun calculateRetryConfiguration() { - retryConfiguration = when (reconnectionPolicy) { - PNReconnectionPolicy.NONE -> RetryConfiguration.None - PNReconnectionPolicy.LINEAR -> RetryConfiguration.Linear( - maxRetryNumber = getMaximumReconnectionRetriesFor(reconnectionPolicy), - ) - - PNReconnectionPolicy.EXPONENTIAL -> RetryConfiguration.Exponential( - maxRetryNumber = getMaximumReconnectionRetriesFor(reconnectionPolicy), - ) - } - } - - private fun getMaximumReconnectionRetriesFor(reconnectionPolicy: PNReconnectionPolicy): Int { - val maxRetryNumber = if (reconnectionPolicy == PNReconnectionPolicy.LINEAR) { - RetryConfiguration.Linear.MAX_RETRIES - } else { - RetryConfiguration.Exponential.MAX_RETRIES - } - return when { - maximumReconnectionRetries <= -1 -> maxRetryNumber - maximumReconnectionRetries > maxRetryNumber -> maxRetryNumber - else -> maximumReconnectionRetries - } - } - - /** - * Sets how many times to retry to reconnect before giving up. - * Must be used in combination with [reconnectionPolicy]. - * - * The default value is `-1` which means unlimited retries. - */ - @Deprecated( - """Instead of reconnectionPolicy and maximumReconnectionRetries use retryConfiguration - e.g. config.retryConfiguration = RetryConfiguration.Linear(delayInSec = 3, maxRetryNumber = 5) - or config.retryConfiguration = RetryConfiguration.Exponential(minDelayInSec = 3, maxDelayInSec = 10, maxRetryNumber = 5)""", - level = DeprecationLevel.WARNING, - ) - var maximumReconnectionRetries = -1 - private set - - fun setMaximumReconnectionRetries(maximumReconnectionRetries: Int): PNConfiguration { - this.maximumReconnectionRetries = maximumReconnectionRetries - calculateRetryConfiguration() - return this - } - - companion object { - private const val MINIMUM_PRESENCE_TIMEOUT = 20 - private val log = LoggerFactory.getLogger("PNConfiguration") - - private fun validatePresenceTimeout(timeout: Int): Int { - var validTimeout = timeout - if (timeout < MINIMUM_PRESENCE_TIMEOUT) { - validTimeout = MINIMUM_PRESENCE_TIMEOUT - log.warn("Presence timeout is too low. Defaulting to: " + MINIMUM_PRESENCE_TIMEOUT) - } - return validTimeout - } - } -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/callbacks/SubscribeCallback.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/callbacks/SubscribeCallback.java deleted file mode 100644 index 420a2d092..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/callbacks/SubscribeCallback.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.pubnub.api.callbacks; - -import com.pubnub.api.PubNub; -import com.pubnub.api.models.consumer.PNStatus; -import com.pubnub.api.v2.callbacks.EventListener; -import com.pubnub.api.v2.callbacks.StatusListener; -import org.jetbrains.annotations.NotNull; - -public abstract class SubscribeCallback implements StatusListener, EventListener { - public static class BaseSubscribeCallback extends SubscribeCallback { - @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus pnStatus) { - } - } -} \ No newline at end of file diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/Endpoint.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/Endpoint.java deleted file mode 100644 index b23af8dd6..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/Endpoint.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.pubnub.api.endpoints; - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; -import com.pubnub.api.v2.PNConfiguration; - -public interface Endpoint extends ExtendedRemoteAction { - /** - * Allows to override certain configuration options (see {@link com.pubnub.api.v2.BasePNConfigurationOverride.Builder}) for this request only. - *

- * {@link com.pubnub.api.v2.PNConfigurationOverride#from(com.pubnub.api.v2.BasePNConfiguration)} should be used to obtain a PNConfigurationOverride.Builder. - * Only options present in PNConfigurationOverride.Builder will be used for the override. - *

- * Example: - *

-     * configOverride = PNConfigurationOverride.from(pubnub.configuration)
-     * configOverride.userId(UserId("example"))
-     * endpoint.overrideConfiguration(configOverride.build()).sync()
-     * 
- * - * @return Returns the same instance for convenience, so {@link Endpoint#sync()} or {@link Endpoint#async(java.util.function.Consumer)} can be called next. - */ - Endpoint overrideConfiguration(PNConfiguration configuration); -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/Time.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/Time.java deleted file mode 100644 index 9a779b2ea..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/Time.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.pubnub.api.endpoints; - -import com.pubnub.api.models.consumer.PNTimeResult; - -public interface Time extends Endpoint { -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/GrantToken.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/GrantToken.java deleted file mode 100644 index 5e57ed551..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/GrantToken.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.pubnub.api.endpoints.access; - -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult; - -public interface GrantToken extends Endpoint { - GrantToken ttl(Integer ttl); - - GrantToken meta(Object meta); - - GrantToken authorizedUUID(String authorizedUUID); - - GrantToken channels(java.util.List channels); - - GrantToken channelGroups(java.util.List channelGroups); - - GrantToken uuids(java.util.List uuids); -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/ListAllChannelGroup.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/ListAllChannelGroup.java deleted file mode 100644 index b3da30e3c..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/ListAllChannelGroup.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.pubnub.api.endpoints.channel_groups; - -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsListAllResult; - -public interface ListAllChannelGroup extends Endpoint { -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/memberships/GetMemberships.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/memberships/GetMemberships.java deleted file mode 100644 index 14a2af650..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/memberships/GetMemberships.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.pubnub.api.endpoints.objects_api.memberships; - -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.objects_api.membership.PNGetMembershipsResult; - -public interface GetMemberships extends Endpoint { - GetMemberships uuid(String uuid); - - GetMemberships limit(Integer limit); - - GetMemberships page(com.pubnub.api.models.consumer.objects.PNPage page); - - GetMemberships filter(String filter); - - GetMemberships sort(java.util.Collection sort); - - GetMemberships includeTotalCount(boolean includeTotalCount); - - GetMemberships includeCustom(boolean includeCustom); - - GetMemberships includeChannel(com.pubnub.api.endpoints.objects_api.utils.Include.PNChannelDetailsLevel includeChannel); -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/GetUUIDMetadata.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/GetUUIDMetadata.java deleted file mode 100644 index 83751738d..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/GetUUIDMetadata.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.pubnub.api.endpoints.objects_api.uuid; - -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.objects_api.uuid.PNGetUUIDMetadataResult; - -public interface GetUUIDMetadata extends Endpoint { - GetUUIDMetadata uuid(String uuid); - - GetUUIDMetadata includeCustom(boolean includeCustom); -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/RemoveUUIDMetadata.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/RemoveUUIDMetadata.java deleted file mode 100644 index 9e382af6e..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/RemoveUUIDMetadata.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.pubnub.api.endpoints.objects_api.uuid; - -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.objects_api.uuid.PNRemoveUUIDMetadataResult; - -public interface RemoveUUIDMetadata extends Endpoint { - RemoveUUIDMetadata uuid(String uuid); -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/WhereNow.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/WhereNow.java deleted file mode 100644 index 5072fcf59..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/WhereNow.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.pubnub.api.endpoints.presence; - -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.internal.models.consumer.presence.PNWhereNowResult; - -public interface WhereNow extends Endpoint { - WhereNow uuid(String uuid); -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/PubNub.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/PubNubForJava.kt similarity index 72% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/PubNub.kt rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/PubNubForJava.kt index 242f0c00c..1d773dbe5 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/PubNub.kt +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/PubNubForJava.kt @@ -1,87 +1,86 @@ -package com.pubnub.api - -import com.pubnub.api.builder.PresenceBuilder -import com.pubnub.api.builder.SubscribeBuilder -import com.pubnub.api.builder.UnsubscribeBuilder -import com.pubnub.api.callbacks.SubscribeCallback -import com.pubnub.api.endpoints.DeleteMessages -import com.pubnub.api.endpoints.FetchMessages -import com.pubnub.api.endpoints.History -import com.pubnub.api.endpoints.MessageCounts +package com.pubnub.api.java + +import com.pubnub.api.PubNubException import com.pubnub.api.endpoints.Time -import com.pubnub.api.endpoints.access.Grant -import com.pubnub.api.endpoints.access.RevokeToken -import com.pubnub.api.endpoints.access.builder.GrantTokenBuilder -import com.pubnub.api.endpoints.channel_groups.AddChannelChannelGroup -import com.pubnub.api.endpoints.channel_groups.AllChannelsChannelGroup -import com.pubnub.api.endpoints.channel_groups.DeleteChannelGroup import com.pubnub.api.endpoints.channel_groups.ListAllChannelGroup -import com.pubnub.api.endpoints.channel_groups.RemoveChannelChannelGroup -import com.pubnub.api.endpoints.files.DeleteFile -import com.pubnub.api.endpoints.files.DownloadFile -import com.pubnub.api.endpoints.files.GetFileUrl -import com.pubnub.api.endpoints.files.ListFiles -import com.pubnub.api.endpoints.files.PublishFileMessage -import com.pubnub.api.endpoints.files.SendFile -import com.pubnub.api.endpoints.message_actions.AddMessageAction -import com.pubnub.api.endpoints.message_actions.GetMessageActions -import com.pubnub.api.endpoints.message_actions.RemoveMessageAction -import com.pubnub.api.endpoints.objects_api.channel.GetAllChannelsMetadata -import com.pubnub.api.endpoints.objects_api.channel.GetChannelMetadata -import com.pubnub.api.endpoints.objects_api.channel.RemoveChannelMetadata -import com.pubnub.api.endpoints.objects_api.channel.SetChannelMetadata -import com.pubnub.api.endpoints.objects_api.members.GetChannelMembers -import com.pubnub.api.endpoints.objects_api.members.ManageChannelMembers -import com.pubnub.api.endpoints.objects_api.members.RemoveChannelMembers -import com.pubnub.api.endpoints.objects_api.members.SetChannelMembers -import com.pubnub.api.endpoints.objects_api.memberships.GetMemberships -import com.pubnub.api.endpoints.objects_api.memberships.ManageMemberships -import com.pubnub.api.endpoints.objects_api.memberships.RemoveMemberships -import com.pubnub.api.endpoints.objects_api.memberships.SetMemberships -import com.pubnub.api.endpoints.objects_api.uuid.GetAllUUIDMetadata -import com.pubnub.api.endpoints.objects_api.uuid.GetUUIDMetadata -import com.pubnub.api.endpoints.objects_api.uuid.RemoveUUIDMetadata -import com.pubnub.api.endpoints.objects_api.uuid.SetUUIDMetadata -import com.pubnub.api.endpoints.presence.GetState -import com.pubnub.api.endpoints.presence.HereNow -import com.pubnub.api.endpoints.presence.SetState -import com.pubnub.api.endpoints.presence.WhereNow -import com.pubnub.api.endpoints.pubsub.Publish -import com.pubnub.api.endpoints.push.AddChannelsToPush -import com.pubnub.api.endpoints.push.ListPushProvisions -import com.pubnub.api.endpoints.push.RemoveAllPushChannelsForDevice -import com.pubnub.api.endpoints.push.RemoveChannelsFromPush -import com.pubnub.api.v2.BasePNConfiguration -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.callbacks.StatusListener -import com.pubnub.api.v2.endpoints.pubsub.PublishBuilder -import com.pubnub.api.v2.endpoints.pubsub.SignalBuilder -import com.pubnub.api.v2.entities.Channel -import com.pubnub.api.v2.entities.ChannelGroup -import com.pubnub.api.v2.entities.ChannelMetadata -import com.pubnub.api.v2.entities.UserMetadata -import com.pubnub.api.v2.subscriptions.Subscription -import com.pubnub.api.v2.subscriptions.SubscriptionSet -import com.pubnub.internal.BasePubNubImpl +import com.pubnub.api.java.builder.PresenceBuilder +import com.pubnub.api.java.builder.SubscribeBuilder +import com.pubnub.api.java.builder.UnsubscribeBuilder +import com.pubnub.api.java.callbacks.SubscribeCallback +import com.pubnub.api.java.endpoints.DeleteMessages +import com.pubnub.api.java.endpoints.FetchMessages +import com.pubnub.api.java.endpoints.History +import com.pubnub.api.java.endpoints.MessageCounts +import com.pubnub.api.java.endpoints.access.Grant +import com.pubnub.api.java.endpoints.access.RevokeToken +import com.pubnub.api.java.endpoints.access.builder.GrantTokenBuilder +import com.pubnub.api.java.endpoints.channel_groups.AddChannelChannelGroup +import com.pubnub.api.java.endpoints.channel_groups.AllChannelsChannelGroup +import com.pubnub.api.java.endpoints.channel_groups.DeleteChannelGroup +import com.pubnub.api.java.endpoints.channel_groups.RemoveChannelChannelGroup +import com.pubnub.api.java.endpoints.files.DeleteFile +import com.pubnub.api.java.endpoints.files.DownloadFile +import com.pubnub.api.java.endpoints.files.GetFileUrl +import com.pubnub.api.java.endpoints.files.ListFiles +import com.pubnub.api.java.endpoints.files.PublishFileMessage +import com.pubnub.api.java.endpoints.files.SendFile +import com.pubnub.api.java.endpoints.message_actions.AddMessageAction +import com.pubnub.api.java.endpoints.message_actions.GetMessageActions +import com.pubnub.api.java.endpoints.message_actions.RemoveMessageAction +import com.pubnub.api.java.endpoints.objects_api.channel.GetAllChannelsMetadata +import com.pubnub.api.java.endpoints.objects_api.channel.GetChannelMetadata +import com.pubnub.api.java.endpoints.objects_api.channel.RemoveChannelMetadata +import com.pubnub.api.java.endpoints.objects_api.channel.SetChannelMetadata +import com.pubnub.api.java.endpoints.objects_api.members.GetChannelMembers +import com.pubnub.api.java.endpoints.objects_api.members.ManageChannelMembers +import com.pubnub.api.java.endpoints.objects_api.members.RemoveChannelMembers +import com.pubnub.api.java.endpoints.objects_api.members.SetChannelMembers +import com.pubnub.api.java.endpoints.objects_api.memberships.GetMemberships +import com.pubnub.api.java.endpoints.objects_api.memberships.ManageMemberships +import com.pubnub.api.java.endpoints.objects_api.memberships.RemoveMemberships +import com.pubnub.api.java.endpoints.objects_api.memberships.SetMemberships +import com.pubnub.api.java.endpoints.objects_api.uuid.GetAllUUIDMetadata +import com.pubnub.api.java.endpoints.objects_api.uuid.GetUUIDMetadata +import com.pubnub.api.java.endpoints.objects_api.uuid.RemoveUUIDMetadata +import com.pubnub.api.java.endpoints.objects_api.uuid.SetUUIDMetadata +import com.pubnub.api.java.endpoints.presence.GetState +import com.pubnub.api.java.endpoints.presence.HereNow +import com.pubnub.api.java.endpoints.presence.SetState +import com.pubnub.api.java.endpoints.presence.WhereNow +import com.pubnub.api.java.endpoints.pubsub.Publish +import com.pubnub.api.java.endpoints.pubsub.Signal +import com.pubnub.api.java.endpoints.push.AddChannelsToPush +import com.pubnub.api.java.endpoints.push.ListPushProvisions +import com.pubnub.api.java.endpoints.push.RemoveAllPushChannelsForDevice +import com.pubnub.api.java.endpoints.push.RemoveChannelsFromPush +import com.pubnub.api.java.v2.PNConfiguration +import com.pubnub.api.java.v2.callbacks.EventEmitter +import com.pubnub.api.java.v2.callbacks.StatusEmitter +import com.pubnub.api.java.v2.endpoints.pubsub.PublishBuilder +import com.pubnub.api.java.v2.entities.Channel +import com.pubnub.api.java.v2.entities.ChannelGroup +import com.pubnub.api.java.v2.entities.ChannelMetadata +import com.pubnub.api.java.v2.entities.UserMetadata +import com.pubnub.api.java.v2.subscriptions.Subscription +import com.pubnub.api.java.v2.subscriptions.SubscriptionSet +import com.pubnub.api.models.consumer.access_manager.v3.PNToken import java.io.InputStream -interface PubNub : - BasePubNub< - EventListener, - Subscription, - Channel, - ChannelGroup, - ChannelMetadata, - UserMetadata, - SubscriptionSet, - StatusListener, - > { +interface PubNubForJava : EventEmitter, StatusEmitter { + val timestamp: Int + val baseUrl: String + + /** + * The current version of the PubNub SDK. + */ + val version: String + /** * Get the configuration that was used to initialize this PubNub instance. * Modifying the values in this configuration is not advised, as it may lead * to undefined behavior. */ - val configuration: BasePNConfiguration + val configuration: com.pubnub.api.v2.PNConfiguration /** * Causes the client to create an open TCP socket to the PubNub Real-Time Network and begin listening for messages @@ -361,7 +360,7 @@ interface PubNub : * This limit applies only to the payload, and not to the URI or headers. * If you require a larger payload size, please [contact support](mailto:support@pubnub.com). */ - fun signal(message: Any, channel: String): SignalBuilder + fun signal(message: Any, channel: String): com.pubnub.api.endpoints.pubsub.Signal /** * Send a signal to all subscribers of a channel. @@ -378,7 +377,7 @@ interface PubNub : level = DeprecationLevel.WARNING, message = "Use signal(Object, String) instead", ) - fun signal(): com.pubnub.api.endpoints.pubsub.Signal + fun signal(): Signal /** * Lists all registered channel groups for the subscribe key. @@ -534,6 +533,13 @@ interface PubNub : fun reconnect() + /** + * Force the SDK to try and reach out PubNub. Monitor the results in [SubscribeCallback.status] + * + * @param timetoken optional timetoken to use for the subscriptions on reconnection. + */ + fun reconnect(timetoken: Long = 0L) + /** * Send a message to PubNub Functions Event Handlers. * @@ -580,15 +586,15 @@ interface PubNub : */ fun getSubscribedChannelGroups(): List - override fun channel(name: String): Channel + fun channel(name: String): Channel - override fun channelGroup(name: String): ChannelGroup + fun channelGroup(name: String): ChannelGroup - override fun channelMetadata(id: String): ChannelMetadata + fun channelMetadata(id: String): ChannelMetadata - override fun userMetadata(id: String): UserMetadata + fun userMetadata(id: String): UserMetadata - override fun subscriptionSetOf(subscriptions: Set): SubscriptionSet + fun subscriptionSetOf(subscriptions: Set): SubscriptionSet /** * Add a legacy listener for both client status and events. @@ -598,6 +604,32 @@ interface PubNub : */ fun addListener(listener: SubscribeCallback) + /** + * Perform Cryptographic decryption of an input string using cipher key provided by [PNConfiguration.cipherKey]. + * + * @param inputString String to be decrypted. + * + * @return String containing the decryption of `inputString` using `cipherKey`. + * @throws PubNubException throws exception in case of failed decryption. + */ + @Throws(PubNubException::class) + fun decrypt(inputString: String): String + + /** + * Perform Cryptographic decryption of an input string using a cipher key. + * + * @param inputString String to be decrypted. + * @param cipherKey cipher key to be used for decryption. Default is [PNConfiguration.cipherKey] + * + * @return String containing the decryption of `inputString` using `cipherKey`. + * @throws PubNubException throws exception in case of failed decryption. + */ + @Throws(PubNubException::class) + fun decrypt( + inputString: String, + cipherKey: String?, + ): String + /** * Perform Cryptographic decryption of an input stream using provided cipher key. * @@ -609,6 +641,36 @@ interface PubNub : @Throws(PubNubException::class) fun decryptInputStream(inputStream: InputStream): InputStream + /** + * Perform Cryptographic decryption of an input stream using provided cipher key. + * + * @param inputStream InputStream to be encrypted. + * @param cipherKey Cipher key to be used for decryption. + * + * @return InputStream containing the encryption of `inputStream` using `cipherKey`. + * @throws PubNubException Throws exception in case of failed decryption. + */ + @Throws(PubNubException::class) + fun decryptInputStream( + inputStream: InputStream, + cipherKey: String?, + ): InputStream + + /** + * Perform Cryptographic encryption of an input string and a cipher key. + * + * @param inputString String to be encrypted. + * @param cipherKey Cipher key to be used for encryption. Default is [PNConfiguration.cipherKey] + * + * @return String containing the encryption of `inputString` using `cipherKey`. + * @throws PubNubException Throws exception in case of failed encryption. + */ + @Throws(PubNubException::class) + fun encrypt( + inputString: String, + cipherKey: String?, + ): String + /** * Perform Cryptographic encryption of an input string and a cipher key. * @@ -632,6 +694,48 @@ interface PubNub : @Throws(PubNubException::class) fun encryptInputStream(inputStream: InputStream): InputStream + /** + * Perform Cryptographic encryption of an input stream using provided cipher key. + * + * @param inputStream InputStream to be encrypted. + * @param cipherKey Cipher key to be used for encryption. + * + * @return InputStream containing the encryption of `inputStream` using `cipherKey`. + * @throws PubNubException Throws exception in case of failed encryption. + */ + @Throws(PubNubException::class) + fun encryptInputStream( + inputStream: InputStream, + cipherKey: String?, + ): InputStream + + @Throws(PubNubException::class) + fun parseToken(token: String): PNToken + + fun setToken(token: String?) + + /** + * Cancel any subscribe and heartbeat loops or ongoing re-connections. + * + * Monitor the results in [SubscribeCallback.status] + */ + fun disconnect() + + /** + * Unsubscribe from all channels and all channel groups + */ + fun unsubscribeAll() + + /** + * Frees up threads eventually and allows for a clean exit. + */ + fun destroy() + + /** + * Same as [destroy] but immediately. + */ + fun forceDestroy() + companion object { /** * Initialize and return an instance of the PubNub client. @@ -639,10 +743,10 @@ interface PubNub : * @return the PubNub client */ @JvmStatic - fun create(configuration: BasePNConfiguration): PubNub { + fun create(configuration: PNConfiguration): PubNubForJava { return Class.forName( - "com.pubnub.internal.PubNubImpl", - ).getConstructor(BasePNConfiguration::class.java).newInstance(configuration) as PubNub + "com.pubnub.internal.java.PubNubForJavaImpl", + ).getConstructor(PNConfiguration::class.java).newInstance(configuration) as PubNubForJava } /** @@ -650,6 +754,6 @@ interface PubNub : * that connects to PubNub. */ @JvmStatic - fun generateUUID(): String = BasePubNubImpl.generateUUID() + fun generateUUID(): String = com.pubnub.api.PubNub.generateUUID() } } diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/PubNubRuntimeException.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/PubNubRuntimeException.java similarity index 95% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/PubNubRuntimeException.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/PubNubRuntimeException.java index 0cefc1255..e08695497 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/PubNubRuntimeException.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/PubNubRuntimeException.java @@ -1,6 +1,7 @@ -package com.pubnub.api; +package com.pubnub.api.java; import com.google.gson.JsonElement; +import com.pubnub.api.PubNubError; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/SpaceId.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/SpaceId.java similarity index 92% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/SpaceId.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/SpaceId.java index 83551fbb2..dc3dbcde4 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/SpaceId.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/SpaceId.java @@ -1,4 +1,4 @@ -package com.pubnub.api; +package com.pubnub.api.java; import kotlin.text.StringsKt; import lombok.Getter; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/PresenceBuilder.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/PresenceBuilder.java similarity index 86% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/PresenceBuilder.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/PresenceBuilder.java index 7590e9ca4..e9bf036f8 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/PresenceBuilder.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/PresenceBuilder.java @@ -1,6 +1,6 @@ -package com.pubnub.api.builder; +package com.pubnub.api.java.builder; -import com.pubnub.internal.PubNubCore; +import com.pubnub.api.PubNub; import lombok.AccessLevel; import lombok.Setter; import lombok.experimental.Accessors; @@ -14,7 +14,7 @@ public class PresenceBuilder extends PubSubBuilder { @Setter(AccessLevel.PUBLIC) private boolean connected; - public PresenceBuilder(PubNubCore pubnub) { + public PresenceBuilder(PubNub pubnub) { super(pubnub); } diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/PubNubErrorBuilder.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/PubNubErrorBuilder.java similarity index 99% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/PubNubErrorBuilder.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/PubNubErrorBuilder.java index ca251aba8..6ecc816d0 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/PubNubErrorBuilder.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/PubNubErrorBuilder.java @@ -1,4 +1,4 @@ -package com.pubnub.api.builder; +package com.pubnub.api.java.builder; import com.pubnub.api.PubNubError; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/PubSubBuilder.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/PubSubBuilder.java similarity index 86% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/PubSubBuilder.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/PubSubBuilder.java index 59de3e476..0b91bf31a 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/PubSubBuilder.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/PubSubBuilder.java @@ -1,7 +1,7 @@ -package com.pubnub.api.builder; +package com.pubnub.api.java.builder; -import com.pubnub.internal.PubNubCore; +import com.pubnub.api.PubNub; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; @@ -21,9 +21,9 @@ public abstract class PubSubBuilder { @Getter(AccessLevel.PROTECTED) @Setter(AccessLevel.PROTECTED) - private PubNubCore pubnub; + private PubNub pubnub; - public PubSubBuilder(PubNubCore pubnub) { + public PubSubBuilder(PubNub pubnub) { this.pubnub = pubnub; this.channelSubscriptions = new ArrayList<>(); this.channelGroupSubscriptions = new ArrayList<>(); diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/SubscribeBuilder.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/SubscribeBuilder.java similarity index 92% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/SubscribeBuilder.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/SubscribeBuilder.java index 7954d2224..b69c6bd9c 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/SubscribeBuilder.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/SubscribeBuilder.java @@ -1,6 +1,6 @@ -package com.pubnub.api.builder; +package com.pubnub.api.java.builder; -import com.pubnub.internal.PubNubCore; +import com.pubnub.api.PubNub; import lombok.AccessLevel; import lombok.Setter; import lombok.experimental.Accessors; @@ -23,7 +23,7 @@ public class SubscribeBuilder extends PubSubBuilder { @Setter(AccessLevel.NONE) private Long timetoken = 0L; - public SubscribeBuilder(PubNubCore pubnub) { + public SubscribeBuilder(PubNub pubnub) { super(pubnub); } diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/UnsubscribeBuilder.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/UnsubscribeBuilder.java similarity index 69% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/UnsubscribeBuilder.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/UnsubscribeBuilder.java index 4d05f8947..6340fb11b 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/UnsubscribeBuilder.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/UnsubscribeBuilder.java @@ -1,13 +1,13 @@ -package com.pubnub.api.builder; +package com.pubnub.api.java.builder; -import com.pubnub.internal.PubNubCore; +import com.pubnub.api.PubNub; import lombok.Getter; import lombok.Setter; @Getter @Setter public class UnsubscribeBuilder extends PubSubBuilder { - public UnsubscribeBuilder(PubNubCore pubnub) { + public UnsubscribeBuilder(PubNub pubnub) { super(pubnub); } diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/ChangeTemporaryUnavailableOperation.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/ChangeTemporaryUnavailableOperation.java similarity index 91% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/ChangeTemporaryUnavailableOperation.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/ChangeTemporaryUnavailableOperation.java index 67a7045b4..80ff24695 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/ChangeTemporaryUnavailableOperation.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/ChangeTemporaryUnavailableOperation.java @@ -1,4 +1,4 @@ -package com.pubnub.api.builder.dto; +package com.pubnub.api.java.builder.dto; import lombok.Builder; import lombok.Data; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/PresenceOperation.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/PresenceOperation.java similarity index 90% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/PresenceOperation.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/PresenceOperation.java index 788c979fd..f136b66db 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/PresenceOperation.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/PresenceOperation.java @@ -1,4 +1,4 @@ -package com.pubnub.api.builder.dto; +package com.pubnub.api.java.builder.dto; import lombok.Builder; import lombok.Data; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/PubSubOperation.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/PubSubOperation.java similarity index 95% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/PubSubOperation.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/PubSubOperation.java index 19ac04f2b..78518b1f1 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/PubSubOperation.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/PubSubOperation.java @@ -1,4 +1,4 @@ -package com.pubnub.api.builder.dto; +package com.pubnub.api.java.builder.dto; import lombok.Data; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/StateOperation.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/StateOperation.java similarity index 90% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/StateOperation.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/StateOperation.java index e2e1e948c..23e5fbf39 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/StateOperation.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/StateOperation.java @@ -1,4 +1,4 @@ -package com.pubnub.api.builder.dto; +package com.pubnub.api.java.builder.dto; import lombok.Builder; import lombok.Data; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/TimetokenAndRegionOperation.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/TimetokenAndRegionOperation.java similarity index 80% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/TimetokenAndRegionOperation.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/TimetokenAndRegionOperation.java index 0693431c3..ab9da5b08 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/TimetokenAndRegionOperation.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/TimetokenAndRegionOperation.java @@ -1,4 +1,4 @@ -package com.pubnub.api.builder.dto; +package com.pubnub.api.java.builder.dto; import lombok.Data; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/UnsubscribeOperation.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/UnsubscribeOperation.java similarity index 85% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/UnsubscribeOperation.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/UnsubscribeOperation.java index f29b23261..df9ff4460 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/builder/dto/UnsubscribeOperation.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/builder/dto/UnsubscribeOperation.java @@ -1,4 +1,4 @@ -package com.pubnub.api.builder.dto; +package com.pubnub.api.java.builder.dto; import lombok.Builder; import lombok.Data; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/callbacks/PNCallback.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/callbacks/PNCallback.java similarity index 91% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/callbacks/PNCallback.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/callbacks/PNCallback.java index 088f40207..101d1eb8c 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/callbacks/PNCallback.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/callbacks/PNCallback.java @@ -1,4 +1,4 @@ -package com.pubnub.api.callbacks; +package com.pubnub.api.java.callbacks; import com.pubnub.api.PubNubException; import com.pubnub.api.v2.callbacks.Result; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/callbacks/SubscribeCallback.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/callbacks/SubscribeCallback.kt new file mode 100644 index 000000000..fd6d7a7e9 --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/callbacks/SubscribeCallback.kt @@ -0,0 +1,13 @@ +package com.pubnub.api.java.callbacks + +import com.pubnub.api.java.PubNubForJava +import com.pubnub.api.java.v2.callbacks.EventListener +import com.pubnub.api.java.v2.callbacks.StatusListener +import com.pubnub.api.models.consumer.PNStatus + +abstract class SubscribeCallback : StatusListener, EventListener { + open class BaseSubscribeCallback : SubscribeCallback(), EventListener { + override fun status(pubnub: PubNubForJava, pnStatus: PNStatus) { + } + } +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/BuilderSteps.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/BuilderSteps.java similarity index 73% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/BuilderSteps.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/BuilderSteps.java index e3110c176..9cc3e025a 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/BuilderSteps.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/BuilderSteps.java @@ -1,4 +1,4 @@ -package com.pubnub.api.endpoints; +package com.pubnub.api.java.endpoints; public interface BuilderSteps { interface ChannelStep { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/DeleteMessages.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/DeleteMessages.java similarity index 88% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/DeleteMessages.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/DeleteMessages.java index 297eec06b..bce32f4ec 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/DeleteMessages.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/DeleteMessages.java @@ -1,4 +1,4 @@ -package com.pubnub.api.endpoints; +package com.pubnub.api.java.endpoints; import com.pubnub.api.models.consumer.history.PNDeleteMessagesResult; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/Endpoint.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/Endpoint.kt new file mode 100644 index 000000000..93dc1e9bf --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/Endpoint.kt @@ -0,0 +1,25 @@ +package com.pubnub.api.java.endpoints + +import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction +import com.pubnub.api.v2.PNConfiguration + +interface Endpoint : ExtendedRemoteAction { + /** + * Allows to override certain configuration options (see [com.pubnub.api.v2.PNConfigurationOverride.Builder]) for this request only. + * + * + * [PNConfigurationOverride.from] should be used to obtain a `PNConfigurationOverride.Builder`. + * Only options present in `PNConfigurationOverride.Builder` will be used for the override. + * + * + * Example: + *
+     * configOverride = PNConfigurationOverride.from(pubnub.configuration)
+     * configOverride.userId(UserId("example"))
+     * endpoint.overrideConfiguration(configOverride.build()).sync()
+     
* + * + * @return Returns the same instance for convenience, so [Endpoint.sync] or [Endpoint.async] can be called next. + */ + fun overrideConfiguration(configuration: PNConfiguration): Endpoint +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/FetchMessages.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/FetchMessages.java similarity index 93% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/FetchMessages.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/FetchMessages.java index 00632253f..a577a20c4 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/FetchMessages.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/FetchMessages.java @@ -1,4 +1,4 @@ -package com.pubnub.api.endpoints; +package com.pubnub.api.java.endpoints; import com.pubnub.api.models.consumer.history.PNFetchMessagesResult; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/History.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/History.java similarity index 91% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/History.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/History.java index 0fde7f6fc..27ca180ce 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/History.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/History.java @@ -1,4 +1,4 @@ -package com.pubnub.api.endpoints; +package com.pubnub.api.java.endpoints; import com.pubnub.api.models.consumer.history.PNHistoryResult; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/MessageCounts.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/MessageCounts.java similarity index 87% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/MessageCounts.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/MessageCounts.java index db0d04465..d3220b59f 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/MessageCounts.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/MessageCounts.java @@ -1,4 +1,4 @@ -package com.pubnub.api.endpoints; +package com.pubnub.api.java.endpoints; import com.pubnub.api.models.consumer.history.PNMessageCountResult; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/Grant.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/Grant.java similarity index 75% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/Grant.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/Grant.java index 564de45b3..c24b1fb17 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/Grant.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/Grant.java @@ -1,7 +1,7 @@ -package com.pubnub.api.endpoints.access; +package com.pubnub.api.java.endpoints.access; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.access_manager.PNAccessManagerGrantResult; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.models.consumer.access_manager.PNAccessManagerGrantResult; public interface Grant extends Endpoint { Grant read(boolean read); diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/GrantToken.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/GrantToken.java new file mode 100644 index 000000000..03a12630d --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/GrantToken.java @@ -0,0 +1,23 @@ +package com.pubnub.api.java.endpoints.access; + +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGrant; +import com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGroupGrant; +import com.pubnub.api.java.models.consumer.access_manager.v3.UUIDGrant; +import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult; + +import java.util.List; + +public interface GrantToken extends Endpoint { + GrantToken ttl(Integer ttl); + + GrantToken meta(Object meta); + + GrantToken authorizedUUID(String authorizedUUID); + + GrantToken channels(List channels); + + GrantToken channelGroups(List channelGroups); + + GrantToken uuids(List uuids); +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/RevokeToken.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/RevokeToken.java similarity index 55% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/RevokeToken.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/RevokeToken.java index 7ff61634c..7fd729eda 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/RevokeToken.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/RevokeToken.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.access; +package com.pubnub.api.java.endpoints.access; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import kotlin.Unit; public interface RevokeToken extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/builder/AbstractGrantTokenBuilder.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/builder/AbstractGrantTokenBuilder.java similarity index 62% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/builder/AbstractGrantTokenBuilder.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/builder/AbstractGrantTokenBuilder.java index 2b1edcdd7..448eec18d 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/builder/AbstractGrantTokenBuilder.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/builder/AbstractGrantTokenBuilder.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.access.builder; +package com.pubnub.api.java.endpoints.access.builder; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult; public interface AbstractGrantTokenBuilder extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/builder/GrantTokenBuilder.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/builder/GrantTokenBuilder.java similarity index 61% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/builder/GrantTokenBuilder.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/builder/GrantTokenBuilder.java index 86cf3c5f8..91496732a 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/builder/GrantTokenBuilder.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/builder/GrantTokenBuilder.java @@ -1,13 +1,13 @@ -package com.pubnub.api.endpoints.access.builder; +package com.pubnub.api.java.endpoints.access.builder; import com.pubnub.api.UserId; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.access_manager.sum.SpacePermissions; -import com.pubnub.api.models.consumer.access_manager.sum.UserPermissions; -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant; -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.models.consumer.access_manager.sum.SpacePermissions; +import com.pubnub.api.java.models.consumer.access_manager.sum.UserPermissions; +import com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGrant; +import com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGroupGrant; import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult; -import com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant; +import com.pubnub.api.java.models.consumer.access_manager.v3.UUIDGrant; import java.util.List; @@ -15,7 +15,7 @@ public interface GrantTokenBuilder extends Endpoint { /** * @param ttl * @return instance of this builder - * @deprecated Use {@link com.pubnub.api.PubNub#grantToken(Integer)} instead. + * @deprecated Use {@link PubNubForJava#grantToken(Integer)} instead. */ @Deprecated GrantTokenBuilder ttl(Integer ttl); diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/builder/GrantTokenEntitiesBuilder.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/builder/GrantTokenEntitiesBuilder.java similarity index 66% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/builder/GrantTokenEntitiesBuilder.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/builder/GrantTokenEntitiesBuilder.java index 4d05fc5dd..d5b539df5 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/builder/GrantTokenEntitiesBuilder.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/builder/GrantTokenEntitiesBuilder.java @@ -1,9 +1,9 @@ -package com.pubnub.api.endpoints.access.builder; +package com.pubnub.api.java.endpoints.access.builder; import com.pubnub.api.UserId; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.access_manager.sum.SpacePermissions; -import com.pubnub.api.models.consumer.access_manager.sum.UserPermissions; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.models.consumer.access_manager.sum.SpacePermissions; +import com.pubnub.api.java.models.consumer.access_manager.sum.UserPermissions; import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult; import java.util.List; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/builder/GrantTokenObjectsBuilder.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/builder/GrantTokenObjectsBuilder.java similarity index 61% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/builder/GrantTokenObjectsBuilder.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/builder/GrantTokenObjectsBuilder.java index 841ce4f67..8ec5dde62 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/access/builder/GrantTokenObjectsBuilder.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/access/builder/GrantTokenObjectsBuilder.java @@ -1,10 +1,10 @@ -package com.pubnub.api.endpoints.access.builder; +package com.pubnub.api.java.endpoints.access.builder; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant; -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGrant; +import com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGroupGrant; import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult; -import com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant; +import com.pubnub.api.java.models.consumer.access_manager.v3.UUIDGrant; import java.util.List; @@ -13,7 +13,7 @@ public interface GrantTokenObjectsBuilder extends Endpoint { /** * @param ttl * @return instance of this builder - * @deprecated Use {@link com.pubnub.api.PubNub#grantToken(int)}} instead. + * @deprecated Use {@link PubNubForJava#grantToken(int)}} instead. */ GrantTokenObjectsBuilder ttl(Integer ttl); diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/AddChannelChannelGroup.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/channel_groups/AddChannelChannelGroup.java similarity index 75% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/AddChannelChannelGroup.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/channel_groups/AddChannelChannelGroup.java index 381262b9c..8cbd5619c 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/AddChannelChannelGroup.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/channel_groups/AddChannelChannelGroup.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.channel_groups; +package com.pubnub.api.java.endpoints.channel_groups; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAddChannelResult; public interface AddChannelChannelGroup extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/AllChannelsChannelGroup.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/channel_groups/AllChannelsChannelGroup.java similarity index 70% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/AllChannelsChannelGroup.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/channel_groups/AllChannelsChannelGroup.java index e9a7718fb..c5b4532db 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/AllChannelsChannelGroup.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/channel_groups/AllChannelsChannelGroup.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.channel_groups; +package com.pubnub.api.java.endpoints.channel_groups; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAllChannelsResult; public interface AllChannelsChannelGroup extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/DeleteChannelGroup.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/channel_groups/DeleteChannelGroup.java similarity index 70% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/DeleteChannelGroup.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/channel_groups/DeleteChannelGroup.java index 988a7c24b..aa457d103 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/DeleteChannelGroup.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/channel_groups/DeleteChannelGroup.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.channel_groups; +package com.pubnub.api.java.endpoints.channel_groups; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsDeleteGroupResult; public interface DeleteChannelGroup extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/RemoveChannelChannelGroup.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/channel_groups/RemoveChannelChannelGroup.java similarity index 76% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/RemoveChannelChannelGroup.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/channel_groups/RemoveChannelChannelGroup.java index bca1fbc09..9186cab4a 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/channel_groups/RemoveChannelChannelGroup.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/channel_groups/RemoveChannelChannelGroup.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.channel_groups; +package com.pubnub.api.java.endpoints.channel_groups; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsRemoveChannelResult; public interface RemoveChannelChannelGroup extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/DeleteFile.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/DeleteFile.java similarity index 54% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/DeleteFile.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/DeleteFile.java index ff7d02ed3..b54785f40 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/DeleteFile.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/DeleteFile.java @@ -1,8 +1,8 @@ -package com.pubnub.api.endpoints.files; +package com.pubnub.api.java.endpoints.files; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps; import com.pubnub.api.models.consumer.files.PNDeleteFileResult; public interface DeleteFile extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/DownloadFile.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/DownloadFile.java similarity index 59% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/DownloadFile.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/DownloadFile.java index bd871ea23..717b03e3f 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/DownloadFile.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/DownloadFile.java @@ -1,8 +1,8 @@ -package com.pubnub.api.endpoints.files; +package com.pubnub.api.java.endpoints.files; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps; import com.pubnub.api.models.consumer.files.PNDownloadFileResult; public interface DownloadFile extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/GetFileUrl.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/GetFileUrl.java similarity index 54% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/GetFileUrl.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/GetFileUrl.java index 510d3a1c4..a31933ca0 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/GetFileUrl.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/GetFileUrl.java @@ -1,8 +1,8 @@ -package com.pubnub.api.endpoints.files; +package com.pubnub.api.java.endpoints.files; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps; import com.pubnub.api.models.consumer.files.PNFileUrlResult; public interface GetFileUrl extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/ListFiles.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/ListFiles.java similarity index 69% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/ListFiles.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/ListFiles.java index c02cb1593..17f820028 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/ListFiles.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/ListFiles.java @@ -1,7 +1,7 @@ -package com.pubnub.api.endpoints.files; +package com.pubnub.api.java.endpoints.files; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.files.PNListFilesResult; public interface ListFiles extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/PublishFileMessage.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/PublishFileMessage.java similarity index 68% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/PublishFileMessage.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/PublishFileMessage.java index 49e5a5b65..2cb4e8852 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/PublishFileMessage.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/PublishFileMessage.java @@ -1,8 +1,8 @@ -package com.pubnub.api.endpoints.files; +package com.pubnub.api.java.endpoints.files; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps; import com.pubnub.api.models.consumer.files.PNPublishFileMessageResult; public interface PublishFileMessage extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/SendFile.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/SendFile.java similarity index 75% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/SendFile.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/SendFile.java index 6219fbca2..3c8cb048a 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/SendFile.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/SendFile.java @@ -1,8 +1,8 @@ -package com.pubnub.api.endpoints.files; +package com.pubnub.api.java.endpoints.files; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps; import com.pubnub.api.models.consumer.files.PNFileUploadResult; public interface SendFile extends ExtendedRemoteAction { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/requiredparambuilder/FilesBuilderSteps.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/requiredparambuilder/FilesBuilderSteps.java similarity index 76% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/requiredparambuilder/FilesBuilderSteps.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/requiredparambuilder/FilesBuilderSteps.java index f8fcb8cbd..a7406383e 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/files/requiredparambuilder/FilesBuilderSteps.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/files/requiredparambuilder/FilesBuilderSteps.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.files.requiredparambuilder; +package com.pubnub.api.java.endpoints.files.requiredparambuilder; -import com.pubnub.api.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.BuilderSteps; import java.io.IOException; import java.io.InputStream; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/message_actions/AddMessageAction.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/message_actions/AddMessageAction.java similarity index 77% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/message_actions/AddMessageAction.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/message_actions/AddMessageAction.java index e7bb83948..103675547 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/message_actions/AddMessageAction.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/message_actions/AddMessageAction.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.message_actions; +package com.pubnub.api.java.endpoints.message_actions; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.message_actions.PNAddMessageActionResult; import com.pubnub.api.models.consumer.message_actions.PNMessageAction; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/message_actions/GetMessageActions.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/message_actions/GetMessageActions.java similarity index 76% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/message_actions/GetMessageActions.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/message_actions/GetMessageActions.java index 2c603a36c..b757ea8d5 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/message_actions/GetMessageActions.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/message_actions/GetMessageActions.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.message_actions; +package com.pubnub.api.java.endpoints.message_actions; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.message_actions.PNGetMessageActionsResult; public interface GetMessageActions extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/message_actions/RemoveMessageAction.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/message_actions/RemoveMessageAction.java similarity index 77% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/message_actions/RemoveMessageAction.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/message_actions/RemoveMessageAction.java index 113d2f0e6..a2c085ba2 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/message_actions/RemoveMessageAction.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/message_actions/RemoveMessageAction.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.message_actions; +package com.pubnub.api.java.endpoints.message_actions; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.message_actions.PNRemoveMessageActionResult; public interface RemoveMessageAction extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/channel/GetAllChannelsMetadata.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/channel/GetAllChannelsMetadata.java similarity index 57% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/channel/GetAllChannelsMetadata.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/channel/GetAllChannelsMetadata.java index b4df9aebe..1622a9b59 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/channel/GetAllChannelsMetadata.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/channel/GetAllChannelsMetadata.java @@ -1,7 +1,8 @@ -package com.pubnub.api.endpoints.objects_api.channel; +package com.pubnub.api.java.endpoints.objects_api.channel; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.objects_api.channel.PNGetAllChannelsMetadataResult; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNGetAllChannelsMetadataResult; import java.util.Collection; @@ -12,7 +13,7 @@ public interface GetAllChannelsMetadata extends Endpoint sort); + GetAllChannelsMetadata sort(Collection sort); GetAllChannelsMetadata includeTotalCount(boolean includeTotalCount); diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/channel/GetChannelMetadata.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/channel/GetChannelMetadata.java similarity index 54% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/channel/GetChannelMetadata.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/channel/GetChannelMetadata.java index d492a7c0d..e0479fca4 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/channel/GetChannelMetadata.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/channel/GetChannelMetadata.java @@ -1,8 +1,8 @@ -package com.pubnub.api.endpoints.objects_api.channel; +package com.pubnub.api.java.endpoints.objects_api.channel; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.objects_api.channel.PNGetChannelMetadataResult; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNGetChannelMetadataResult; public interface GetChannelMetadata extends Endpoint { GetChannelMetadata includeCustom(boolean includeCustom); diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/channel/RemoveChannelMetadata.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/channel/RemoveChannelMetadata.java similarity index 50% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/channel/RemoveChannelMetadata.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/channel/RemoveChannelMetadata.java index b64b17b52..e13477eef 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/channel/RemoveChannelMetadata.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/channel/RemoveChannelMetadata.java @@ -1,8 +1,8 @@ -package com.pubnub.api.endpoints.objects_api.channel; +package com.pubnub.api.java.endpoints.objects_api.channel; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.objects_api.channel.PNRemoveChannelMetadataResult; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNRemoveChannelMetadataResult; public interface RemoveChannelMetadata extends Endpoint { interface Builder extends BuilderSteps.ChannelStep { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/channel/SetChannelMetadata.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/channel/SetChannelMetadata.java similarity index 69% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/channel/SetChannelMetadata.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/channel/SetChannelMetadata.java index 62c187861..22ebe63fe 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/channel/SetChannelMetadata.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/channel/SetChannelMetadata.java @@ -1,8 +1,8 @@ -package com.pubnub.api.endpoints.objects_api.channel; +package com.pubnub.api.java.endpoints.objects_api.channel; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.objects_api.channel.PNSetChannelMetadataResult; +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 java.util.Map; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/members/GetChannelMembers.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/members/GetChannelMembers.java similarity index 51% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/members/GetChannelMembers.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/members/GetChannelMembers.java index 1790546b5..d1709cdd0 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/members/GetChannelMembers.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/members/GetChannelMembers.java @@ -1,8 +1,10 @@ -package com.pubnub.api.endpoints.objects_api.members; +package com.pubnub.api.java.endpoints.objects_api.members; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.objects_api.member.PNGetChannelMembersResult; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.member.PNGetChannelMembersResult; public interface GetChannelMembers extends Endpoint { GetChannelMembers limit(Integer limit); @@ -11,13 +13,13 @@ public interface GetChannelMembers extends Endpoint { GetChannelMembers filter(String filter); - GetChannelMembers sort(java.util.Collection sort); + GetChannelMembers sort(java.util.Collection sort); GetChannelMembers includeTotalCount(boolean includeTotalCount); GetChannelMembers includeCustom(boolean includeCustom); - GetChannelMembers includeUUID(com.pubnub.api.endpoints.objects_api.utils.Include.PNUUIDDetailsLevel includeUUID); + GetChannelMembers includeUUID(Include.PNUUIDDetailsLevel includeUUID); interface Builder extends BuilderSteps.ChannelStep { @Override diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/members/ManageChannelMembers.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/members/ManageChannelMembers.java similarity index 53% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/members/ManageChannelMembers.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/members/ManageChannelMembers.java index 571100fa2..d1ba01434 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/members/ManageChannelMembers.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/members/ManageChannelMembers.java @@ -1,9 +1,11 @@ -package com.pubnub.api.endpoints.objects_api.members; +package com.pubnub.api.java.endpoints.objects_api.members; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps; -import com.pubnub.api.models.consumer.objects_api.member.PNManageChannelMembersResult; -import com.pubnub.api.models.consumer.objects_api.member.PNUUID; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.member.PNManageChannelMembersResult; +import com.pubnub.api.java.models.consumer.objects_api.member.PNUUID; import java.util.Collection; @@ -14,13 +16,13 @@ public interface ManageChannelMembers extends Endpoint sort); + ManageChannelMembers sort(Collection sort); ManageChannelMembers includeTotalCount(boolean includeTotalCount); ManageChannelMembers includeCustom(boolean includeCustom); - ManageChannelMembers includeUUID(com.pubnub.api.endpoints.objects_api.utils.Include.PNUUIDDetailsLevel includeUUID); + ManageChannelMembers includeUUID(Include.PNUUIDDetailsLevel includeUUID); interface Builder extends ObjectsBuilderSteps.ChannelStep> { @Override diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/members/RemoveChannelMembers.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/members/RemoveChannelMembers.java similarity index 52% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/members/RemoveChannelMembers.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/members/RemoveChannelMembers.java index 3169c66a6..a8b7e6eb9 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/members/RemoveChannelMembers.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/members/RemoveChannelMembers.java @@ -1,9 +1,11 @@ -package com.pubnub.api.endpoints.objects_api.members; +package com.pubnub.api.java.endpoints.objects_api.members; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps; -import com.pubnub.api.models.consumer.objects_api.member.PNRemoveChannelMembersResult; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.member.PNRemoveChannelMembersResult; import java.util.Collection; @@ -15,13 +17,13 @@ public interface RemoveChannelMembers extends Endpoint sort); + RemoveChannelMembers sort(Collection sort); RemoveChannelMembers includeTotalCount(boolean includeTotalCount); RemoveChannelMembers includeCustom(boolean includeCustom); - RemoveChannelMembers includeUUID(com.pubnub.api.endpoints.objects_api.utils.Include.PNUUIDDetailsLevel includeUUID); + RemoveChannelMembers includeUUID(Include.PNUUIDDetailsLevel includeUUID); interface Builder extends BuilderSteps.ChannelStep> { @Override diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/members/SetChannelMembers.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/members/SetChannelMembers.java similarity index 52% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/members/SetChannelMembers.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/members/SetChannelMembers.java index 06b63f099..0d6d8457a 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/members/SetChannelMembers.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/members/SetChannelMembers.java @@ -1,9 +1,11 @@ -package com.pubnub.api.endpoints.objects_api.members; +package com.pubnub.api.java.endpoints.objects_api.members; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps; -import com.pubnub.api.models.consumer.objects_api.member.PNSetChannelMembersResult; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.member.PNSetChannelMembersResult; import java.util.Collection; @@ -15,13 +17,13 @@ public interface SetChannelMembers extends Endpoint { SetChannelMembers filter(String filter); - SetChannelMembers sort(Collection sort); + SetChannelMembers sort(Collection sort); SetChannelMembers includeTotalCount(boolean includeTotalCount); SetChannelMembers includeCustom(boolean includeCustom); - SetChannelMembers includeUUID(com.pubnub.api.endpoints.objects_api.utils.Include.PNUUIDDetailsLevel includeUUID); + SetChannelMembers includeUUID(Include.PNUUIDDetailsLevel includeUUID); interface Builder extends BuilderSteps.ChannelStep> { @Override diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/memberships/GetMemberships.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/memberships/GetMemberships.java new file mode 100644 index 000000000..b6c705794 --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/memberships/GetMemberships.java @@ -0,0 +1,24 @@ +package com.pubnub.api.java.endpoints.objects_api.memberships; + +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNGetMembershipsResult; + +public interface GetMemberships extends Endpoint { + GetMemberships uuid(String uuid); + + GetMemberships limit(Integer limit); + + GetMemberships page(com.pubnub.api.models.consumer.objects.PNPage page); + + GetMemberships filter(String filter); + + GetMemberships sort(java.util.Collection sort); + + GetMemberships includeTotalCount(boolean includeTotalCount); + + GetMemberships includeCustom(boolean includeCustom); + + GetMemberships includeChannel(Include.PNChannelDetailsLevel includeChannel); +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/memberships/ManageMemberships.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/memberships/ManageMemberships.java similarity index 50% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/memberships/ManageMemberships.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/memberships/ManageMemberships.java index d4fca915b..7c934331e 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/memberships/ManageMemberships.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/memberships/ManageMemberships.java @@ -1,17 +1,19 @@ -package com.pubnub.api.endpoints.objects_api.memberships; +package com.pubnub.api.java.endpoints.objects_api.memberships; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps; -import com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership; -import com.pubnub.api.models.consumer.objects_api.membership.PNManageMembershipResult; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNManageMembershipResult; import java.util.Collection; public interface ManageMemberships extends Endpoint { - ManageMemberships set(java.util.Collection set); + ManageMemberships set(Collection set); - ManageMemberships remove(java.util.Collection remove); + ManageMemberships remove(Collection remove); ManageMemberships uuid(String uuid); @@ -21,13 +23,13 @@ public interface ManageMemberships extends Endpoint { ManageMemberships filter(String filter); - ManageMemberships sort(java.util.Collection sort); + ManageMemberships sort(Collection sort); ManageMemberships includeTotalCount(boolean includeTotalCount); ManageMemberships includeCustom(boolean includeCustom); - ManageMemberships includeChannel(com.pubnub.api.endpoints.objects_api.utils.Include.PNChannelDetailsLevel includeChannel); + ManageMemberships includeChannel(Include.PNChannelDetailsLevel includeChannel); interface Builder extends ObjectsBuilderSteps.RemoveOrSetStep { @Override diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/memberships/RemoveMemberships.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/memberships/RemoveMemberships.java similarity index 53% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/memberships/RemoveMemberships.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/memberships/RemoveMemberships.java index 3d5a0c8ca..a87341614 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/memberships/RemoveMemberships.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/memberships/RemoveMemberships.java @@ -1,9 +1,11 @@ -package com.pubnub.api.endpoints.objects_api.memberships; - -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps; -import com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership; -import com.pubnub.api.models.consumer.objects_api.membership.PNRemoveMembershipResult; +package com.pubnub.api.java.endpoints.objects_api.memberships; + +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNRemoveMembershipResult; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -18,13 +20,13 @@ public interface RemoveMemberships extends Endpoint { RemoveMemberships filter(String filter); - RemoveMemberships sort(java.util.Collection sort); + RemoveMemberships sort(Collection sort); RemoveMemberships includeTotalCount(boolean includeTotalCount); RemoveMemberships includeCustom(boolean includeCustom); - RemoveMemberships includeChannel(com.pubnub.api.endpoints.objects_api.utils.Include.PNChannelDetailsLevel includeChannel); + RemoveMemberships includeChannel(Include.PNChannelDetailsLevel includeChannel); interface Builder extends ObjectsBuilderSteps.ChannelMembershipsStep { @Override diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/memberships/SetMemberships.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/memberships/SetMemberships.java similarity index 52% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/memberships/SetMemberships.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/memberships/SetMemberships.java index 8a8f8e998..a5b9c4442 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/memberships/SetMemberships.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/memberships/SetMemberships.java @@ -1,8 +1,10 @@ -package com.pubnub.api.endpoints.objects_api.memberships; +package com.pubnub.api.java.endpoints.objects_api.memberships; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership; -import com.pubnub.api.models.consumer.objects_api.membership.PNSetMembershipResult; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNSetMembershipResult; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -16,13 +18,13 @@ public interface SetMemberships extends Endpoint { SetMemberships filter(String filter); - SetMemberships sort(java.util.Collection sort); + SetMemberships sort(Collection sort); SetMemberships includeTotalCount(boolean includeTotalCount); SetMemberships includeCustom(boolean includeCustom); - SetMemberships includeChannel(com.pubnub.api.endpoints.objects_api.utils.Include.PNChannelDetailsLevel includeChannel); + SetMemberships includeChannel(Include.PNChannelDetailsLevel includeChannel); interface Builder { SetMemberships channelMemberships(@NotNull Collection channelMemberships); diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/utils/Include.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/utils/Include.java similarity index 94% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/utils/Include.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/utils/Include.java index d5d4a3437..7711cf43a 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/utils/Include.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/utils/Include.java @@ -1,4 +1,4 @@ -package com.pubnub.api.endpoints.objects_api.utils; +package com.pubnub.api.java.endpoints.objects_api.utils; public class Include { static final String INCLUDE_CHANNEL_PARAM_VALUE = "channel"; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/utils/ObjectsBuilderSteps.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/utils/ObjectsBuilderSteps.java similarity index 73% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/utils/ObjectsBuilderSteps.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/utils/ObjectsBuilderSteps.java index 1f794f88b..eb44307fe 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/utils/ObjectsBuilderSteps.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/utils/ObjectsBuilderSteps.java @@ -1,8 +1,8 @@ -package com.pubnub.api.endpoints.objects_api.utils; +package com.pubnub.api.java.endpoints.objects_api.utils; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.models.consumer.objects_api.member.PNUUID; -import com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.models.consumer.objects_api.member.PNUUID; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership; import org.jetbrains.annotations.NotNull; import java.util.Collection; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/utils/PNSortKey.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/utils/PNSortKey.java similarity index 84% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/utils/PNSortKey.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/utils/PNSortKey.java index e2ec76daf..28d751013 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/utils/PNSortKey.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/utils/PNSortKey.java @@ -1,10 +1,10 @@ -package com.pubnub.api.endpoints.objects_api.utils; +package com.pubnub.api.java.endpoints.objects_api.utils; import lombok.AccessLevel; import lombok.Getter; -import static com.pubnub.api.endpoints.objects_api.utils.PNSortKey.Dir.ASC; -import static com.pubnub.api.endpoints.objects_api.utils.PNSortKey.Dir.DESC; +import static com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey.Dir.ASC; +import static com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey.Dir.DESC; public final class PNSortKey { public enum Dir { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/GetAllUUIDMetadata.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/GetAllUUIDMetadata.java similarity index 54% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/GetAllUUIDMetadata.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/GetAllUUIDMetadata.java index 37b246879..e9415f462 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/GetAllUUIDMetadata.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/GetAllUUIDMetadata.java @@ -1,7 +1,8 @@ -package com.pubnub.api.endpoints.objects_api.uuid; +package com.pubnub.api.java.endpoints.objects_api.uuid; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.objects_api.uuid.PNGetAllUUIDMetadataResult; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNGetAllUUIDMetadataResult; public interface GetAllUUIDMetadata extends Endpoint { GetAllUUIDMetadata includeCustom(boolean includeCustom); @@ -14,5 +15,5 @@ public interface GetAllUUIDMetadata extends Endpoint GetAllUUIDMetadata filter(String filter); - GetAllUUIDMetadata sort(java.util.Collection sort); + GetAllUUIDMetadata sort(java.util.Collection sort); } diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/GetUUIDMetadata.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/GetUUIDMetadata.java new file mode 100644 index 000000000..f607e392f --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/GetUUIDMetadata.java @@ -0,0 +1,10 @@ +package com.pubnub.api.java.endpoints.objects_api.uuid; + +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNGetUUIDMetadataResult; + +public interface GetUUIDMetadata extends Endpoint { + GetUUIDMetadata uuid(String uuid); + + GetUUIDMetadata includeCustom(boolean includeCustom); +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/RemoveUUIDMetadata.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/RemoveUUIDMetadata.java new file mode 100644 index 000000000..f9f884934 --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/RemoveUUIDMetadata.java @@ -0,0 +1,8 @@ +package com.pubnub.api.java.endpoints.objects_api.uuid; + +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNRemoveUUIDMetadataResult; + +public interface RemoveUUIDMetadata extends Endpoint { + RemoveUUIDMetadata uuid(String uuid); +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/SetUUIDMetadata.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/SetUUIDMetadata.java similarity index 73% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/SetUUIDMetadata.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/SetUUIDMetadata.java index d5b8841c1..7e4520ef1 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/objects_api/uuid/SetUUIDMetadata.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/objects_api/uuid/SetUUIDMetadata.java @@ -1,7 +1,7 @@ -package com.pubnub.api.endpoints.objects_api.uuid; +package com.pubnub.api.java.endpoints.objects_api.uuid; -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.objects_api.uuid.PNSetUUIDMetadataResult; +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNSetUUIDMetadataResult; import java.util.Map; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/GetState.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/GetState.java similarity index 75% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/GetState.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/GetState.java index c32a2b778..39679938f 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/GetState.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/GetState.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.presence; +package com.pubnub.api.java.endpoints.presence; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.presence.PNGetStateResult; public interface GetState extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/Heartbeat.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/Heartbeat.java similarity index 69% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/Heartbeat.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/Heartbeat.java index 63812d7a6..a377c96ad 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/Heartbeat.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/Heartbeat.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.presence; +package com.pubnub.api.java.endpoints.presence; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; public interface Heartbeat extends Endpoint { Heartbeat channels(java.util.List channels); diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/HereNow.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/HereNow.java similarity index 78% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/HereNow.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/HereNow.java index 385921019..b44f67387 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/HereNow.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/HereNow.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.presence; +package com.pubnub.api.java.endpoints.presence; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.presence.PNHereNowResult; public interface HereNow extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/Leave.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/Leave.java similarity index 64% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/Leave.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/Leave.java index 6b1897e0f..4c2a8538b 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/Leave.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/Leave.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.presence; +package com.pubnub.api.java.endpoints.presence; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; public interface Leave extends Endpoint { Leave channels(java.util.List channels); diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/SetState.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/SetState.java similarity index 79% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/SetState.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/SetState.java index 4019da73a..bed6bd7db 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/presence/SetState.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/SetState.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.presence; +package com.pubnub.api.java.endpoints.presence; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.presence.PNSetStateResult; public interface SetState extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/WhereNow.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/WhereNow.java new file mode 100644 index 000000000..645bb45e4 --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/presence/WhereNow.java @@ -0,0 +1,8 @@ +package com.pubnub.api.java.endpoints.presence; + +import com.pubnub.api.java.endpoints.Endpoint; +import com.pubnub.api.models.consumer.presence.PNWhereNowResult; + +public interface WhereNow extends Endpoint { + WhereNow uuid(String uuid); +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/pubsub/Publish.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/pubsub/Publish.java similarity index 80% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/pubsub/Publish.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/pubsub/Publish.java index 66148b2b3..cbd0fafec 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/pubsub/Publish.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/pubsub/Publish.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.pubsub; +package com.pubnub.api.java.endpoints.pubsub; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.PNPublishResult; public interface Publish extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/pubsub/Signal.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/pubsub/Signal.java similarity index 67% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/pubsub/Signal.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/pubsub/Signal.java index 17d210cba..3f45e3af0 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/pubsub/Signal.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/pubsub/Signal.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.pubsub; +package com.pubnub.api.java.endpoints.pubsub; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.PNPublishResult; public interface Signal extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/push/AddChannelsToPush.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/push/AddChannelsToPush.java similarity index 83% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/push/AddChannelsToPush.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/push/AddChannelsToPush.java index 032ba71f0..34348aa45 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/push/AddChannelsToPush.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/push/AddChannelsToPush.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.push; +package com.pubnub.api.java.endpoints.push; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.push.PNPushAddChannelResult; public interface AddChannelsToPush extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/push/ListPushProvisions.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/push/ListPushProvisions.java similarity index 82% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/push/ListPushProvisions.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/push/ListPushProvisions.java index ae0d43ff8..85ca3e001 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/push/ListPushProvisions.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/push/ListPushProvisions.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.push; +package com.pubnub.api.java.endpoints.push; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.push.PNPushListProvisionsResult; public interface ListPushProvisions extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/push/RemoveAllPushChannelsForDevice.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/push/RemoveAllPushChannelsForDevice.java similarity index 84% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/push/RemoveAllPushChannelsForDevice.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/push/RemoveAllPushChannelsForDevice.java index 4a6a622eb..c12135616 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/push/RemoveAllPushChannelsForDevice.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/push/RemoveAllPushChannelsForDevice.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.push; +package com.pubnub.api.java.endpoints.push; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.push.PNPushRemoveAllChannelsResult; public interface RemoveAllPushChannelsForDevice extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/push/RemoveChannelsFromPush.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/push/RemoveChannelsFromPush.java similarity index 84% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/push/RemoveChannelsFromPush.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/push/RemoveChannelsFromPush.java index 5604cd30a..3e86860de 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/endpoints/push/RemoveChannelsFromPush.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/endpoints/push/RemoveChannelsFromPush.java @@ -1,6 +1,6 @@ -package com.pubnub.api.endpoints.push; +package com.pubnub.api.java.endpoints.push; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.push.PNPushRemoveChannelResult; public interface RemoveChannelsFromPush extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerGrantResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/PNAccessManagerGrantResult.java similarity index 68% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerGrantResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/PNAccessManagerGrantResult.java index b67a021c8..a9ab93397 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerGrantResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/PNAccessManagerGrantResult.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.access_manager; +package com.pubnub.api.java.models.consumer.access_manager; import lombok.Builder; import lombok.Data; @@ -24,7 +24,7 @@ public class PNAccessManagerGrantResult { private Map> uuids; - public static PNAccessManagerGrantResult from(com.pubnub.internal.models.consumer.access_manager.PNAccessManagerGrantResult data) { + public static PNAccessManagerGrantResult from(com.pubnub.api.models.consumer.access_manager.PNAccessManagerGrantResult data) { return PNAccessManagerGrantResult.builder() .level(data.getLevel()) @@ -36,11 +36,11 @@ public static PNAccessManagerGrantResult from(com.pubnub.internal.models.consume .build(); } - private static Map> from(Map> data) { + private static Map> from(Map> data) { Map> newMap = new HashMap<>(data.size()); - for (Map.Entry> stringMapEntry : data.entrySet()) { + for (Map.Entry> stringMapEntry : data.entrySet()) { Map innerMap = new HashMap<>(stringMapEntry.getValue().size()); - for (Map.Entry innerEntry : stringMapEntry.getValue().entrySet()) { + for (Map.Entry innerEntry : stringMapEntry.getValue().entrySet()) { innerMap.put(innerEntry.getKey(), PNAccessManagerKeyData.from(innerEntry.getValue())); } newMap.put(stringMapEntry.getKey(), innerMap); diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerKeyData.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/PNAccessManagerKeyData.java similarity index 85% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerKeyData.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/PNAccessManagerKeyData.java index 9d1ff8678..8d889cbd1 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerKeyData.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/PNAccessManagerKeyData.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.access_manager; +package com.pubnub.api.java.models.consumer.access_manager; import com.google.gson.annotations.SerializedName; import lombok.Builder; @@ -29,7 +29,7 @@ public class PNAccessManagerKeyData { @SerializedName("j") private boolean joinEnabled; - static PNAccessManagerKeyData from(com.pubnub.internal.models.consumer.access_manager.PNAccessManagerKeyData data) { + static PNAccessManagerKeyData from(com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeyData data) { return PNAccessManagerKeyData.builder() .readEnabled(data.getReadEnabled()) .writeEnabled(data.getWriteEnabled()) diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerKeysData.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/PNAccessManagerKeysData.java similarity index 81% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerKeysData.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/PNAccessManagerKeysData.java index 8a2b9465e..5cea4cdfd 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerKeysData.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/PNAccessManagerKeysData.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.access_manager; +package com.pubnub.api.java.models.consumer.access_manager; import com.google.gson.annotations.SerializedName; import lombok.Getter; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/sum/SpacePermissions.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/sum/SpacePermissions.java similarity index 87% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/sum/SpacePermissions.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/sum/SpacePermissions.java index b9c16a66e..90afe626c 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/sum/SpacePermissions.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/sum/SpacePermissions.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.access_manager.sum; +package com.pubnub.api.java.models.consumer.access_manager.sum; -import com.pubnub.api.SpaceId; -import com.pubnub.api.models.consumer.access_manager.v3.PNResource; +import com.pubnub.api.java.SpaceId; +import com.pubnub.api.java.models.consumer.access_manager.v3.PNResource; public class SpacePermissions extends PNResource { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/sum/UserPermissions.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/sum/UserPermissions.java similarity index 86% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/sum/UserPermissions.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/sum/UserPermissions.java index 8b2bee413..0438448c2 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/sum/UserPermissions.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/sum/UserPermissions.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.access_manager.sum; +package com.pubnub.api.java.models.consumer.access_manager.sum; import com.pubnub.api.UserId; -import com.pubnub.api.models.consumer.access_manager.v3.PNResource; +import com.pubnub.api.java.models.consumer.access_manager.v3.PNResource; public class UserPermissions extends PNResource { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/ChannelGrant.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/ChannelGrant.java similarity index 94% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/ChannelGrant.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/ChannelGrant.java index 37f5b41fd..ada397f50 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/ChannelGrant.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/ChannelGrant.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.access_manager.v3; +package com.pubnub.api.java.models.consumer.access_manager.v3; public class ChannelGrant extends PNResource { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/ChannelGroupGrant.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/ChannelGroupGrant.java similarity index 92% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/ChannelGroupGrant.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/ChannelGroupGrant.java index edde89f27..bd8e78cfe 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/ChannelGroupGrant.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/ChannelGroupGrant.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.access_manager.v3; +package com.pubnub.api.java.models.consumer.access_manager.v3; public class ChannelGroupGrant extends PNResource { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNResource.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/PNResource.java similarity index 95% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNResource.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/PNResource.java index 9d9261722..3166a1d6d 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNResource.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/PNResource.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.access_manager.v3; +package com.pubnub.api.java.models.consumer.access_manager.v3; import lombok.AccessLevel; import lombok.Getter; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNRevokeTokenResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/PNRevokeTokenResult.java similarity index 50% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNRevokeTokenResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/PNRevokeTokenResult.java index 5eb636b39..d7f92e242 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNRevokeTokenResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/PNRevokeTokenResult.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.access_manager.v3; +package com.pubnub.api.java.models.consumer.access_manager.v3; import lombok.Data; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/UUIDGrant.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/UUIDGrant.java similarity index 91% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/UUIDGrant.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/UUIDGrant.java index c6055f1d9..04cd8d196 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/UUIDGrant.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/access_manager/v3/UUIDGrant.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.access_manager.v3; +package com.pubnub.api.java.models.consumer.access_manager.v3; public class UUIDGrant extends PNResource { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/EntityArrayEnvelope.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/EntityArrayEnvelope.java similarity index 89% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/EntityArrayEnvelope.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/EntityArrayEnvelope.java index 69ce2aadc..665f2af7c 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/EntityArrayEnvelope.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/EntityArrayEnvelope.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.objects_api; +package com.pubnub.api.java.models.consumer.objects_api; import com.pubnub.api.models.consumer.objects.PNPage; import lombok.Getter; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/EntityEnvelope.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/EntityEnvelope.java similarity index 69% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/EntityEnvelope.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/EntityEnvelope.java index fd93d6ca6..f2b459370 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/EntityEnvelope.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/EntityEnvelope.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.objects_api; +package com.pubnub.api.java.models.consumer.objects_api; import lombok.Getter; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/PNObject.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/PNObject.java similarity index 90% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/PNObject.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/PNObject.java index 9f6aa6a7f..3038a46a5 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/PNObject.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/PNObject.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.objects_api; +package com.pubnub.api.java.models.consumer.objects_api; import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNChannelMetadata.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNChannelMetadata.java similarity index 94% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNChannelMetadata.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNChannelMetadata.java index 84d7f77be..5b775dc36 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNChannelMetadata.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNChannelMetadata.java @@ -1,6 +1,6 @@ -package com.pubnub.api.models.consumer.objects_api.channel; +package com.pubnub.api.java.models.consumer.objects_api.channel; -import com.pubnub.api.models.consumer.objects_api.PNObject; +import com.pubnub.api.java.models.consumer.objects_api.PNObject; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNChannelMetadataResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNChannelMetadataResult.java similarity index 93% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNChannelMetadataResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNChannelMetadataResult.java index 3a9215be1..c4c730cef 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNChannelMetadataResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNChannelMetadataResult.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.objects_api.channel; +package com.pubnub.api.java.models.consumer.objects_api.channel; import com.google.gson.JsonElement; import com.pubnub.api.models.consumer.pubsub.PubSubResult; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNGetAllChannelsMetadataResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNGetAllChannelsMetadataResult.java similarity index 84% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNGetAllChannelsMetadataResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNGetAllChannelsMetadataResult.java index b42d9f8f6..1b2a1624a 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNGetAllChannelsMetadataResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNGetAllChannelsMetadataResult.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.objects_api.channel; +package com.pubnub.api.java.models.consumer.objects_api.channel; -import com.pubnub.api.models.consumer.objects_api.EntityArrayEnvelope; -import com.pubnub.internal.models.consumer.objects.channel.PNChannelMetadataArrayResult; +import com.pubnub.api.java.models.consumer.objects_api.EntityArrayEnvelope; +import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadataArrayResult; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNGetChannelMetadataResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNGetChannelMetadataResult.java similarity index 71% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNGetChannelMetadataResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNGetChannelMetadataResult.java index bdb589687..aec499073 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNGetChannelMetadataResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNGetChannelMetadataResult.java @@ -1,6 +1,6 @@ -package com.pubnub.api.models.consumer.objects_api.channel; +package com.pubnub.api.java.models.consumer.objects_api.channel; -import com.pubnub.api.models.consumer.objects_api.EntityEnvelope; +import com.pubnub.api.java.models.consumer.objects_api.EntityEnvelope; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNRemoveChannelMetadataResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNRemoveChannelMetadataResult.java similarity index 75% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNRemoveChannelMetadataResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNRemoveChannelMetadataResult.java index 2323855f1..7772cdd22 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNRemoveChannelMetadataResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNRemoveChannelMetadataResult.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.objects_api.channel; +package com.pubnub.api.java.models.consumer.objects_api.channel; import com.google.gson.JsonElement; -import com.pubnub.api.models.consumer.objects_api.EntityEnvelope; +import com.pubnub.api.java.models.consumer.objects_api.EntityEnvelope; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNSetChannelMetadataResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNSetChannelMetadataResult.java similarity index 71% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNSetChannelMetadataResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNSetChannelMetadataResult.java index 96debf136..cb700ced3 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/channel/PNSetChannelMetadataResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/channel/PNSetChannelMetadataResult.java @@ -1,6 +1,6 @@ -package com.pubnub.api.models.consumer.objects_api.channel; +package com.pubnub.api.java.models.consumer.objects_api.channel; -import com.pubnub.api.models.consumer.objects_api.EntityEnvelope; +import com.pubnub.api.java.models.consumer.objects_api.EntityEnvelope; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNGetChannelMembersResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNGetChannelMembersResult.java similarity index 84% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNGetChannelMembersResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNGetChannelMembersResult.java index 1efee3539..a22e73250 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNGetChannelMembersResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNGetChannelMembersResult.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.objects_api.member; +package com.pubnub.api.java.models.consumer.objects_api.member; -import com.pubnub.api.models.consumer.objects_api.EntityArrayEnvelope; -import com.pubnub.internal.models.consumer.objects.member.PNMemberArrayResult; +import com.pubnub.api.java.models.consumer.objects_api.EntityArrayEnvelope; +import com.pubnub.api.models.consumer.objects.member.PNMemberArrayResult; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNManageChannelMembersResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNManageChannelMembersResult.java similarity index 85% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNManageChannelMembersResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNManageChannelMembersResult.java index de490bbef..e4062619c 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNManageChannelMembersResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNManageChannelMembersResult.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.objects_api.member; +package com.pubnub.api.java.models.consumer.objects_api.member; -import com.pubnub.api.models.consumer.objects_api.EntityArrayEnvelope; -import com.pubnub.internal.models.consumer.objects.member.PNMemberArrayResult; +import com.pubnub.api.java.models.consumer.objects_api.EntityArrayEnvelope; +import com.pubnub.api.models.consumer.objects.member.PNMemberArrayResult; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNMembers.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNMembers.java similarity index 86% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNMembers.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNMembers.java index 23403bb6a..c4c9bc2ef 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNMembers.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNMembers.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.objects_api.member; +package com.pubnub.api.java.models.consumer.objects_api.member; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadata; -import com.pubnub.internal.models.consumer.objects.member.PNMember; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadata; +import com.pubnub.api.models.consumer.objects.member.PNMember; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNRemoveChannelMembersResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNRemoveChannelMembersResult.java similarity index 85% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNRemoveChannelMembersResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNRemoveChannelMembersResult.java index 6052cbd22..31bc80b11 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNRemoveChannelMembersResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNRemoveChannelMembersResult.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.objects_api.member; +package com.pubnub.api.java.models.consumer.objects_api.member; -import com.pubnub.api.models.consumer.objects_api.EntityArrayEnvelope; -import com.pubnub.internal.models.consumer.objects.member.PNMemberArrayResult; +import com.pubnub.api.java.models.consumer.objects_api.EntityArrayEnvelope; +import com.pubnub.api.models.consumer.objects.member.PNMemberArrayResult; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNSetChannelMembersResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNSetChannelMembersResult.java similarity index 84% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNSetChannelMembersResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNSetChannelMembersResult.java index 2dda4fa57..18a68104c 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNSetChannelMembersResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNSetChannelMembersResult.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.objects_api.member; +package com.pubnub.api.java.models.consumer.objects_api.member; -import com.pubnub.api.models.consumer.objects_api.EntityArrayEnvelope; -import com.pubnub.internal.models.consumer.objects.member.PNMemberArrayResult; +import com.pubnub.api.java.models.consumer.objects_api.EntityArrayEnvelope; +import com.pubnub.api.models.consumer.objects.member.PNMemberArrayResult; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNUUID.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNUUID.java similarity index 96% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNUUID.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNUUID.java index 680db20f1..e13bbf7ac 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/member/PNUUID.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/member/PNUUID.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.objects_api.member; +package com.pubnub.api.java.models.consumer.objects_api.member; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNChannelMembership.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNChannelMembership.java similarity index 95% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNChannelMembership.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNChannelMembership.java index 049ec5db4..14c52ae7d 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNChannelMembership.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNChannelMembership.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.objects_api.membership; +package com.pubnub.api.java.models.consumer.objects_api.membership; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNGetMembershipsResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNGetMembershipsResult.java similarity index 84% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNGetMembershipsResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNGetMembershipsResult.java index 482ad7cb4..1639c1399 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNGetMembershipsResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNGetMembershipsResult.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.objects_api.membership; +package com.pubnub.api.java.models.consumer.objects_api.membership; -import com.pubnub.api.models.consumer.objects_api.EntityArrayEnvelope; -import com.pubnub.internal.models.consumer.objects.membership.PNChannelMembershipArrayResult; +import com.pubnub.api.java.models.consumer.objects_api.EntityArrayEnvelope; +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNManageMembershipResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNManageMembershipResult.java similarity index 85% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNManageMembershipResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNManageMembershipResult.java index 8ff7af681..966f4cd6f 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNManageMembershipResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNManageMembershipResult.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.objects_api.membership; +package com.pubnub.api.java.models.consumer.objects_api.membership; -import com.pubnub.api.models.consumer.objects_api.EntityArrayEnvelope; -import com.pubnub.internal.models.consumer.objects.membership.PNChannelMembershipArrayResult; +import com.pubnub.api.java.models.consumer.objects_api.EntityArrayEnvelope; +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNMembership.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNMembership.java similarity index 79% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNMembership.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNMembership.java index 0427f1a57..eb11e37df 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNMembership.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNMembership.java @@ -1,11 +1,11 @@ -package com.pubnub.api.models.consumer.objects_api.membership; +package com.pubnub.api.java.models.consumer.objects_api.membership; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadata; -import com.pubnub.internal.models.consumer.objects.membership.PNChannelMembership; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadata; +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembership; import lombok.Data; import lombok.NonNull; -import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -14,8 +14,10 @@ @Data @Accessors(chain = true) -@RequiredArgsConstructor public class PNMembership { + public PNMembership(@NotNull PNChannelMetadata channel) { + this.channel = channel; + } @NonNull private PNChannelMetadata channel; private Object custom; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNMembershipResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNMembershipResult.java similarity index 93% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNMembershipResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNMembershipResult.java index 345b36329..357cbf67a 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNMembershipResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNMembershipResult.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.objects_api.membership; +package com.pubnub.api.java.models.consumer.objects_api.membership; import com.google.gson.JsonElement; import com.pubnub.api.models.consumer.pubsub.PubSubResult; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNRemoveMembershipResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNRemoveMembershipResult.java similarity index 82% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNRemoveMembershipResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNRemoveMembershipResult.java index 07e0ebcee..4ac51efc8 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNRemoveMembershipResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNRemoveMembershipResult.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.objects_api.membership; +package com.pubnub.api.java.models.consumer.objects_api.membership; -import com.pubnub.api.models.consumer.objects_api.EntityArrayEnvelope; -import com.pubnub.internal.models.consumer.objects.membership.PNChannelMembershipArrayResult; +import com.pubnub.api.java.models.consumer.objects_api.EntityArrayEnvelope; +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNSetMembershipResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNSetMembershipResult.java similarity index 85% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNSetMembershipResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNSetMembershipResult.java index 4bfb1c6ab..f444614aa 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNSetMembershipResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/membership/PNSetMembershipResult.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.objects_api.membership; +package com.pubnub.api.java.models.consumer.objects_api.membership; -import com.pubnub.api.models.consumer.objects_api.EntityArrayEnvelope; -import com.pubnub.internal.models.consumer.objects.membership.PNChannelMembershipArrayResult; +import com.pubnub.api.java.models.consumer.objects_api.EntityArrayEnvelope; +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNGetAllUUIDMetadataResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNGetAllUUIDMetadataResult.java similarity index 86% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNGetAllUUIDMetadataResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNGetAllUUIDMetadataResult.java index 160898a17..53a93efb5 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNGetAllUUIDMetadataResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNGetAllUUIDMetadataResult.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.objects_api.uuid; +package com.pubnub.api.java.models.consumer.objects_api.uuid; -import com.pubnub.api.models.consumer.objects_api.EntityArrayEnvelope; -import com.pubnub.internal.models.consumer.objects.uuid.PNUUIDMetadataArrayResult; +import com.pubnub.api.java.models.consumer.objects_api.EntityArrayEnvelope; +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadataArrayResult; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNGetUUIDMetadataResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNGetUUIDMetadataResult.java similarity index 81% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNGetUUIDMetadataResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNGetUUIDMetadataResult.java index 812ebcd9c..8fac5b3ae 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNGetUUIDMetadataResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNGetUUIDMetadataResult.java @@ -1,6 +1,6 @@ -package com.pubnub.api.models.consumer.objects_api.uuid; +package com.pubnub.api.java.models.consumer.objects_api.uuid; -import com.pubnub.api.models.consumer.objects_api.EntityEnvelope; +import com.pubnub.api.java.models.consumer.objects_api.EntityEnvelope; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNRemoveUUIDMetadataResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNRemoveUUIDMetadataResult.java similarity index 81% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNRemoveUUIDMetadataResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNRemoveUUIDMetadataResult.java index 43be6ce1a..262f4bdb5 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNRemoveUUIDMetadataResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNRemoveUUIDMetadataResult.java @@ -1,7 +1,7 @@ -package com.pubnub.api.models.consumer.objects_api.uuid; +package com.pubnub.api.java.models.consumer.objects_api.uuid; import com.google.gson.JsonElement; -import com.pubnub.api.models.consumer.objects_api.EntityEnvelope; +import com.pubnub.api.java.models.consumer.objects_api.EntityEnvelope; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNSetUUIDMetadataResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNSetUUIDMetadataResult.java similarity index 81% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNSetUUIDMetadataResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNSetUUIDMetadataResult.java index daa979bcb..e7185a1a2 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNSetUUIDMetadataResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNSetUUIDMetadataResult.java @@ -1,6 +1,6 @@ -package com.pubnub.api.models.consumer.objects_api.uuid; +package com.pubnub.api.java.models.consumer.objects_api.uuid; -import com.pubnub.api.models.consumer.objects_api.EntityEnvelope; +import com.pubnub.api.java.models.consumer.objects_api.EntityEnvelope; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNUUIDMetadata.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNUUIDMetadata.java similarity index 94% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNUUIDMetadata.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNUUIDMetadata.java index 409eab008..cec764261 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNUUIDMetadata.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNUUIDMetadata.java @@ -1,6 +1,6 @@ -package com.pubnub.api.models.consumer.objects_api.uuid; +package com.pubnub.api.java.models.consumer.objects_api.uuid; -import com.pubnub.api.models.consumer.objects_api.PNObject; +import com.pubnub.api.java.models.consumer.objects_api.PNObject; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNUUIDMetadataResult.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNUUIDMetadataResult.java similarity index 93% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNUUIDMetadataResult.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNUUIDMetadataResult.java index ea8d4a552..b62781583 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/models/consumer/objects_api/uuid/PNUUIDMetadataResult.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/models/consumer/objects_api/uuid/PNUUIDMetadataResult.java @@ -1,4 +1,4 @@ -package com.pubnub.api.models.consumer.objects_api.uuid; +package com.pubnub.api.java.models.consumer.objects_api.uuid; import com.google.gson.JsonElement; import com.pubnub.api.models.consumer.pubsub.PubSubResult; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/PNConfiguration.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/PNConfiguration.kt similarity index 54% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/PNConfiguration.kt rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/PNConfiguration.kt index 664b4bcfb..a6a35fd85 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/PNConfiguration.kt +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/PNConfiguration.kt @@ -1,12 +1,11 @@ -package com.pubnub.api.v2 +package com.pubnub.api.java.v2 import com.pubnub.api.UserId import com.pubnub.api.crypto.CryptoModule import com.pubnub.api.enums.PNHeartbeatNotificationOptions import com.pubnub.api.enums.PNLogVerbosity +import com.pubnub.api.java.v2.PNConfiguration.Builder import com.pubnub.api.retry.RetryConfiguration -import com.pubnub.api.v2.PNConfiguration.Builder -import com.pubnub.internal.v2.BasePNConfigurationImpl import okhttp3.Authenticator import okhttp3.CertificatePinner import okhttp3.ConnectionSpec @@ -17,22 +16,287 @@ import javax.net.ssl.HostnameVerifier import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509ExtendedTrustManager -interface PNConfiguration : BasePNConfiguration, PNConfigurationOverride { +interface PNConfiguration : com.pubnub.api.v2.PNConfiguration { companion object { @JvmStatic fun builder( userId: UserId, subscribeKey: String, ): Builder { - return Class.forName("com.pubnub.internal.v2.PNConfigurationImpl\$Builder") - .getConstructor(BasePNConfiguration::class.java) - .newInstance(object : BasePNConfigurationImpl(userId) { - override val subscribeKey: String = subscribeKey - }) as Builder + return Class.forName("com.pubnub.internal.java.v2.PNConfigurationImpl\$Builder") + .getConstructor(UserId::class.java, subscribeKey::class.java) + .newInstance(userId, subscribeKey) as Builder + } + + @JvmStatic + fun builder(initialConfiguration: com.pubnub.api.v2.PNConfiguration): Builder { + return Class.forName("com.pubnub.internal.java.v2.PNConfigurationImpl\$Builder") + .getConstructor(com.pubnub.api.v2.PNConfiguration::class.java) + .newInstance(initialConfiguration) as Builder } } - interface Builder : BasePNConfiguration.Builder, PNConfigurationOverride.Builder { + interface Builder : PNConfigurationOverride.Builder { + /** + * The user ID that the PubNub client will use. + */ + val userId: UserId + + /** + * The subscribe key from the admin panel. + */ + val subscribeKey: String + + /** + * The publish key from the admin panel (only required if publishing). + */ + val publishKey: String + + /** + * The secret key from the admin panel (only required for modifying/revealing access permissions). + * + * Keep away from Android. + */ + val secretKey: String + + /** + * If Access Manager is utilized, client will use this authKey in all restricted requests. + */ + val authKey: String + + /** + * CryptoModule is responsible for handling encryption and decryption. + * If set, all communications to and from PubNub will be encrypted. + */ + val cryptoModule: CryptoModule? + + /** + * Custom origin if needed. + * + * Defaults to `ps.pndsn.com` + */ + val origin: String + + /** + * If set to `true`, requests will be made over HTTPS. + * + * Deafults to `true`. + */ + val secure: Boolean + + /** + * Set to [PNLogVerbosity.BODY] to enable logging of network traffic, otherwise se to [PNLogVerbosity.NONE]. + */ + val logVerbosity: PNLogVerbosity + + /** + * Set Heartbeat notification options. + * + * By default, the SDK alerts on failed heartbeats (equivalent to [PNHeartbeatNotificationOptions.FAILURES]). + */ + val heartbeatNotificationOptions: PNHeartbeatNotificationOptions + + /** + * Sets the custom presence server timeout. + * + * The value is in seconds, and the minimum value is 20 seconds. + * + * Also sets the value of [heartbeatInterval] + */ + val presenceTimeout: Int + + /** + * How often the client will announce itself to server. + * + * The value is in seconds. + */ + val heartbeatInterval: Int + + /** + * The subscribe request timeout. + * + * The value is in seconds. + * + * Defaults to 310. + */ + val subscribeTimeout: Int + + /** + * How long before the client gives up trying to connect with the server. + * + * The value is in seconds. + * + * Defaults to 5. + */ + val connectTimeout: Int + + /** + * For non subscribe operations (publish, herenow, etc), + * This property relates to a read timeout that is applied from the moment the connection between a client + * and the server has been successfully established. It defines a maximum time of inactivity between two + * data packets when waiting for the serverโ€™s response. + * + * The value is in seconds. + * + * Defaults to 10. + */ + @Deprecated( + "This setting relates to *read* timeout and was renamed to `nonSubscribeReadTimeout`", + replaceWith = ReplaceWith("nonSubscribeReadTimeout") + ) + val nonSubscribeRequestTimeout: Int + get() = nonSubscribeReadTimeout + + /** + * For non subscribe operations (publish, herenow, etc), + * This property relates to a read timeout that is applied from the moment the connection between a client + * and the server has been successfully established. It defines a maximum time of inactivity between two + * data packets when waiting for the serverโ€™s response. + * + * The value is in seconds. + * + * Defaults to 10. + */ + val nonSubscribeReadTimeout: Int + + /** + * If operating behind a misbehaving proxy, allow the client to shuffle the subdomains. + * + * Defaults to `false`. + */ + val cacheBusting: Boolean + + /** + * When `true` the SDK doesn't send out the leave requests. + * + * Defaults to `false`. + */ + val suppressLeaveEvents: Boolean + + /** + * When `true` the SDK will resend the last channel state that was set using [PubNub.setPresenceState] + * for the current [userId] with every automatic heartbeat (if [heartbeatInterval] is greater than 0) + * and initial subscribe connection (also after e.g. loss of network). + * + * Defaults to `true`. + * + * Please note that `maintainPresenceState` doesn't apply to state that was set on channel groups. + * It is recommended to disable this option if you set state for channel groups using [PubNub.setPresenceState] + * otherwise that state may be overwritten by individual channel states. + */ + val maintainPresenceState: Boolean + + /** + * Feature to subscribe with a custom filter expression. + */ + val filterExpression: String + + /** + * Whether to include a [PubNubCore.instanceId] with every request. + * + * Defaults to `false`. + */ + val includeInstanceIdentifier: Boolean + + /** + * Whether to include a [PubNubCore.requestId] with every request. + * + * Defaults to `true`. + */ + val includeRequestIdentifier: Boolean + + /** + * @see [okhttp3.Dispatcher.setMaxRequestsPerHost] + */ + val maximumConnections: Int? + + /** + * Enable Google App Engine networking. + * + * Defaults to `false`. + */ + val googleAppEngineNetworking: Boolean + + /** + * Instructs the SDK to use a proxy configuration when communicating with PubNub servers. + * + * @see [Proxy] + */ + val proxy: Proxy? + + /** + * @see [ProxySelector] + */ + val proxySelector: ProxySelector? + + /** + * @see [Authenticator] + */ + val proxyAuthenticator: Authenticator? + + /** + * @see [CertificatePinner] + */ + val certificatePinner: CertificatePinner? + + /** + * Sets a custom [HttpLoggingInterceptor] for logging network traffic. + * + * @see [HttpLoggingInterceptor] + */ + val httpLoggingInterceptor: HttpLoggingInterceptor? + + /** + * @see [SSLSocketFactory] + */ + val sslSocketFactory: SSLSocketFactory? + + /** + * @see [X509ExtendedTrustManager] + */ + val x509ExtendedTrustManager: X509ExtendedTrustManager? + + /** + * @see [okhttp3.ConnectionSpec] + */ + val connectionSpec: ConnectionSpec? + + /** + * @see [javax.net.ssl.HostnameVerifier] + */ + val hostnameVerifier: HostnameVerifier? + + /** + * How many times publishing file message should automatically retry before marking the action as failed + * + * Defaults to `5` + */ + val fileMessagePublishRetryLimit: Int + + val dedupOnSubscribe: Boolean + val maximumMessagesCacheSize: Int + val pnsdkSuffixes: Map + + /** + * Retry configuration for requests. + * Defaults to [RetryConfiguration.None]. + * + * Use [RetryConfiguration.Linear] to set retry with linear delay interval + * Use [RetryConfiguration.Exponential] to set retry with exponential delay interval + * Delay will valy from provided value by random value. + */ + val retryConfiguration: RetryConfiguration + + /** + * Enables explicit presence control. + * When set to true heartbeat calls will contain only channels and groups added explicitly + * using [PubNubCore.presence]. Should be used only with ACL set on the server side. + * For more information please contact PubNub support + * @see PubNubCore.presence + * @see PNConfiguration.heartbeatInterval + */ + val managePresenceListManually: Boolean + override fun build(): PNConfiguration override fun setUserId(userId: UserId): Builder @@ -114,7 +378,10 @@ interface PNConfiguration : BasePNConfiguration, PNConfigurationOverride { "This setting relates to *read* timeout and was renamed to `nonSubscribeReadTimeout`", replaceWith = ReplaceWith("nonSubscribeReadTimeout") ) - fun nonSubscribeRequestTimeout(nonSubscribeRequestTimeout: Int): Builder + fun nonSubscribeRequestTimeout(nonSubscribeRequestTimeout: Int): Builder { + nonSubscribeReadTimeout(nonSubscribeRequestTimeout) + return this + } override fun nonSubscribeReadTimeout(nonSubscribeReadTimeout: Int): Builder @@ -254,23 +521,23 @@ interface PNConfiguration : BasePNConfiguration, PNConfigurationOverride { * using [PubNubCore.presence]. Should be used only with ACL set on the server side. * For more information please contact PubNub support * @see PubNubCore.presence - * @see BasePNConfigurationImpl.heartbeatInterval + * @see PNConfigurationImpl.heartbeatInterval */ fun managePresenceListManually(managePresenceListManually: Boolean): Builder } } -interface PNConfigurationOverride : BasePNConfigurationOverride { +interface PNConfigurationOverride : com.pubnub.api.v2.PNConfigurationOverride { companion object { @JvmStatic - fun from(configuration: BasePNConfiguration): Builder { - return Class.forName("com.pubnub.internal.v2.PNConfigurationImpl\$Builder") - .getConstructor(BasePNConfiguration::class.java) + fun from(configuration: com.pubnub.api.v2.PNConfiguration): Builder { + return Class.forName("com.pubnub.internal.java.v2.PNConfigurationImpl\$Builder") + .getConstructor(com.pubnub.api.v2.PNConfiguration::class.java) .newInstance(configuration) as Builder } } - interface Builder : BasePNConfigurationOverride.Builder { + interface Builder { fun setUserId(userId: UserId): Builder fun subscribeKey(subscribeKey: String): Builder diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/EventEmitter.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/EventEmitter.kt new file mode 100644 index 000000000..970528c16 --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/EventEmitter.kt @@ -0,0 +1,243 @@ +package com.pubnub.api.java.v2.callbacks + +import com.pubnub.api.callbacks.Listener +import com.pubnub.api.java.v2.callbacks.handlers.OnChannelMetadataHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnFileHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnMembershipHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnMessageActionHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnMessageHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnPresenceHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnSignalHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnUuidMetadataHandler + +/** + * Interface implemented by objects that are the source of real time events from the PubNub network. + */ +interface EventEmitter { + /** + * Add a listener. + * + * @param listener The listener to be added. + */ + fun addListener(listener: EventListener) + + /** + * Remove a listener. + * + * @param listener The listener to be removed, previously added with [addListener]. + */ + fun removeListener(listener: Listener) + + /** + * Removes all listeners. + */ + fun removeAllListeners() + + /** + * Sets the handler for incoming message events. + * This method allows the assignment of an [OnMessageHandler] implementation or lambda expression to handle + * incoming messages. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to message events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`setOnMessage(pnMessageResult -> System.out.println("Received: " + pnMessageResult.getMessage()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`setOnMessage(null);
+     `
* + * + * @param onMessageHandler An implementation of [OnMessageHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + fun setOnMessage(onMessageHandler: OnMessageHandler?) + + /** + * Sets the handler for incoming signal events. + * This method allows the assignment of an [OnSignalHandler] implementation or lambda expression to handle + * incoming signals. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to signal events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`setOnSignal(pnSignalResult -> System.out.println("Received: " + pnSignalResult.getMessage()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`setOnSignal(null);
+     `
* + * + * @param onSignalHandler An implementation of [OnSignalHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + fun setOnSignal(onSignalHandler: OnSignalHandler?) + + /** + * Sets the handler for incoming presence events. + * This method allows the assignment of an [OnPresenceHandler] implementation or lambda expression to handle + * incoming presence events. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to presence events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`onPresenceHandler(pnPresenceEventResult -> System.out.println("Received: " + pnPresenceEventResult.getEvent()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`onPresenceHandler(null);
+     `
* + * + * @param onPresenceHandler An implementation of [OnPresenceHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + fun setOnPresence(onPresenceHandler: OnPresenceHandler?) + + /** + * Sets the handler for incoming messageAction events. + * This method allows the assignment of an [OnMessageActionHandler] implementation or lambda expression to handle + * incoming presence events. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to messageAction events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`onMessageActionHandler(pnMessageActionResult -> System.out.println("Received: " + pnMessageActionResult.getMessageAction()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`onMessageActionHandler(null);
+     `
* + * + * @param onMessageActionHandler An implementation of [OnMessageActionHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + fun setOnMessageAction(onMessageActionHandler: OnMessageActionHandler?) + + /** + * Sets the handler for incoming uuidMetadata events. + * This method allows the assignment of an [OnUuidMetadataHandler] implementation or lambda expression to handle + * incoming presence events. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to uuidMetadata events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`onUuidMetadataHandler(pnUUIDMetadataResult -> System.out.println("Received: " + pnUUIDMetadataResult.getData()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`onUuidMetadataHandler(null);
+     `
* + * + * @param onUuidMetadataHandler An implementation of [OnUuidMetadataHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + fun setOnUuidMetadata(onUuidMetadataHandler: OnUuidMetadataHandler?) + + /** + * Sets the handler for incoming channelMetadata events. + * This method allows the assignment of an [OnChannelMetadataHandler] implementation or lambda expression to handle + * incoming presence events. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to channelMetadata events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`onChannelMetadataHandler(pnChannelMetadataResult -> System.out.println("Received: " +  pnChannelMetadataResult.getEvent()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`onChannelMetadataHandler(null);
+     `
* + * + * @param onChannelMetadataHandler An implementation of [OnChannelMetadataHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + fun setOnChannelMetadata(onChannelMetadataHandler: OnChannelMetadataHandler?) + + /** + * Sets the handler for incoming membership events. + * This method allows the assignment of an [OnMembershipHandler] implementation or lambda expression to handle + * incoming presence events. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to membership events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`onMembershipHandler(pnMembershipResult -> System.out.println("Received: " +  pnMembershipResult.getEvent()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`onMembershipHandler(null);
+     `
* + * + * @param onMembershipHandler An implementation of [OnMembershipHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + fun setOnMembership(onMembershipHandler: OnMembershipHandler?) + + /** + * Sets the handler for incoming file events. + * This method allows the assignment of an [OnFileHandler] implementation or lambda expression to handle + * incoming presence events. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to file events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`onFileHandler(pnFileEventResult -> System.out.println("Received: " +  pnFileEventResult.getMessage()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`onFileHandler(null);
+     `
* + * + * @param onFileHandler An implementation of [OnFileHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + fun setOnFile(onFileHandler: OnFileHandler?) +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/EventListener.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/EventListener.kt new file mode 100644 index 000000000..2a44e58b6 --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/EventListener.kt @@ -0,0 +1,46 @@ +package com.pubnub.api.java.v2.callbacks + +import com.pubnub.api.callbacks.Listener +import com.pubnub.api.java.PubNubForJava +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.api.models.consumer.pubsub.PNSignalResult +import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult +import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult + +/** + * Implement this interface and pass it into [EventEmitter.addListener] to listen for events from the PubNub real-time + * network. + */ +interface EventListener : Listener { + fun message(pubnub: PubNubForJava, result: PNMessageResult) {} + + fun presence( + pubnub: PubNubForJava, + result: PNPresenceEventResult, + ) {} + + fun signal(pubnub: PubNubForJava, result: PNSignalResult) {} + + fun messageAction( + pubNub: PubNubForJava, + result: PNMessageActionResult, + ) {} + + fun file( + pubnub: PubNubForJava, + result: PNFileEventResult, + ) {} + + fun uuid(pubnub: PubNubForJava, pnUUIDMetadataResult: PNUUIDMetadataResult) { + } + + fun channel(pubnub: PubNubForJava, pnChannelMetadataResult: PNChannelMetadataResult) { + } + + fun membership(pubnub: PubNubForJava, pnMembershipResult: PNMembershipResult) { + } +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/StatusEmitter.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/StatusEmitter.kt new file mode 100644 index 000000000..751e6dcab --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/StatusEmitter.kt @@ -0,0 +1,28 @@ +package com.pubnub.api.java.v2.callbacks + +import com.pubnub.api.callbacks.Listener + +/** + * Interface implemented by objects that manage the subscription connection to the PubNub network and can be monitored + * for connection state changes. + */ +interface StatusEmitter { + /** + * Add a listener. + * + * @param listener The listener to be added. + */ + fun addListener(listener: StatusListener) + + /** + * Remove a listener. + * + * @param listener The listener to be removed, previously added with [addListener]. + */ + fun removeListener(listener: Listener) + + /** + * Removes all listeners. + */ + fun removeAllListeners() +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/StatusListener.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/StatusListener.kt new file mode 100644 index 000000000..30c5b26b5 --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/StatusListener.kt @@ -0,0 +1,22 @@ +package com.pubnub.api.java.v2.callbacks + +import com.pubnub.api.callbacks.Listener +import com.pubnub.api.java.PubNubForJava +import com.pubnub.api.models.consumer.PNStatus + +interface StatusListener : Listener { + /** + * Receive status updates from the PubNub client, such as: + * * [com.pubnub.api.enums.PNStatusCategory.PNConnectedCategory], + * * [com.pubnub.api.enums.PNStatusCategory.PNDisconnectedCategory], + * * [com.pubnub.api.enums.PNStatusCategory.PNSubscriptionChanged] + * * [com.pubnub.api.enums.PNStatusCategory.PNConnectionError], + * * [com.pubnub.api.enums.PNStatusCategory.PNUnexpectedDisconnectCategory], + * + * @see [PNStatus] + * + * @param pubnub The client instance which has this listener attached. + * @param status Wrapper around the actual message content. + */ + fun status(pubnub: PubNubForJava, status: PNStatus) +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnChannelMetadataHandler.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnChannelMetadataHandler.java similarity index 86% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnChannelMetadataHandler.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnChannelMetadataHandler.java index a134324f4..67c578961 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnChannelMetadataHandler.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnChannelMetadataHandler.java @@ -1,6 +1,6 @@ -package com.pubnub.api.v2.callbacks.handlers; +package com.pubnub.api.java.v2.callbacks.handlers; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult; @FunctionalInterface public interface OnChannelMetadataHandler { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnFileHandler.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnFileHandler.java similarity index 94% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnFileHandler.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnFileHandler.java index 4abc05cf2..2df5f81b9 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnFileHandler.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnFileHandler.java @@ -1,4 +1,4 @@ -package com.pubnub.api.v2.callbacks.handlers; +package com.pubnub.api.java.v2.callbacks.handlers; import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnMembershipHandler.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnMembershipHandler.java similarity index 85% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnMembershipHandler.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnMembershipHandler.java index 10370225d..c0daa9de3 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnMembershipHandler.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnMembershipHandler.java @@ -1,6 +1,6 @@ -package com.pubnub.api.v2.callbacks.handlers; +package com.pubnub.api.java.v2.callbacks.handlers; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult; @FunctionalInterface public interface OnMembershipHandler { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnMessageActionHandler.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnMessageActionHandler.java similarity index 94% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnMessageActionHandler.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnMessageActionHandler.java index 193d94944..789d7c8fa 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnMessageActionHandler.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnMessageActionHandler.java @@ -1,4 +1,4 @@ -package com.pubnub.api.v2.callbacks.handlers; +package com.pubnub.api.java.v2.callbacks.handlers; import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnMessageHandler.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnMessageHandler.java similarity index 94% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnMessageHandler.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnMessageHandler.java index 5bc07b6f9..8bb1f2cec 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnMessageHandler.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnMessageHandler.java @@ -1,4 +1,4 @@ -package com.pubnub.api.v2.callbacks.handlers; +package com.pubnub.api.java.v2.callbacks.handlers; import com.pubnub.api.models.consumer.pubsub.PNMessageResult; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnPresenceHandler.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnPresenceHandler.java similarity index 94% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnPresenceHandler.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnPresenceHandler.java index 01e2985e7..e477b7e71 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnPresenceHandler.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnPresenceHandler.java @@ -1,4 +1,4 @@ -package com.pubnub.api.v2.callbacks.handlers; +package com.pubnub.api.java.v2.callbacks.handlers; import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnSignalHandler.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnSignalHandler.java similarity index 94% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnSignalHandler.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnSignalHandler.java index 4e4110da4..fa9c43733 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnSignalHandler.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnSignalHandler.java @@ -1,4 +1,4 @@ -package com.pubnub.api.v2.callbacks.handlers; +package com.pubnub.api.java.v2.callbacks.handlers; import com.pubnub.api.models.consumer.pubsub.PNSignalResult; diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnUuidMetadataHandler.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnUuidMetadataHandler.java similarity index 86% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnUuidMetadataHandler.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnUuidMetadataHandler.java index ac450d3eb..9a3910577 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/handlers/OnUuidMetadataHandler.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/callbacks/handlers/OnUuidMetadataHandler.java @@ -1,6 +1,6 @@ -package com.pubnub.api.v2.callbacks.handlers; +package com.pubnub.api.java.v2.callbacks.handlers; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult; @FunctionalInterface public interface OnUuidMetadataHandler { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/endpoints/pubsub/PublishBuilder.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/endpoints/pubsub/PublishBuilder.java similarity index 78% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/endpoints/pubsub/PublishBuilder.java rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/endpoints/pubsub/PublishBuilder.java index df33f8c72..92c13dac2 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/endpoints/pubsub/PublishBuilder.java +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/endpoints/pubsub/PublishBuilder.java @@ -1,6 +1,6 @@ -package com.pubnub.api.v2.endpoints.pubsub; +package com.pubnub.api.java.v2.endpoints.pubsub; -import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.java.endpoints.Endpoint; import com.pubnub.api.models.consumer.PNPublishResult; public interface PublishBuilder extends Endpoint { diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/endpoints/pubsub/SignalBuilder.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/endpoints/pubsub/SignalBuilder.java new file mode 100644 index 000000000..321ef9edd --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/endpoints/pubsub/SignalBuilder.java @@ -0,0 +1,6 @@ +package com.pubnub.api.java.v2.endpoints.pubsub; + +import com.pubnub.api.java.endpoints.pubsub.Signal; + +public interface SignalBuilder extends Signal { +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/Channel.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/Channel.kt similarity index 75% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/Channel.kt rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/Channel.kt index 5cf99a193..7ffd3676f 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/Channel.kt +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/Channel.kt @@ -1,19 +1,44 @@ -package com.pubnub.api.v2.entities +package com.pubnub.api.java.v2.entities -import com.pubnub.api.PNConfiguration -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.endpoints.pubsub.PublishBuilder -import com.pubnub.api.v2.endpoints.pubsub.SignalBuilder -import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.endpoints.pubsub.Signal +import com.pubnub.api.java.v2.endpoints.pubsub.PublishBuilder +import com.pubnub.api.java.v2.subscriptions.Subscription +import com.pubnub.api.v2.entities.Subscribable +import com.pubnub.api.v2.subscriptions.SubscriptionOptions /** * A representation of a PubNub channel identified by its [name]. * * You can get a [Subscription] to this channel through [Subscribable.subscription]. * - * Use the [com.pubnub.api.PubNub.channel] factory method to create instances of this interface. + * Use the [com.pubnub.api.java.PubNubForJava.channel] factory method to create instances of this interface. */ -interface Channel : BaseChannel { +interface Channel : com.pubnub.api.java.v2.entities.Subscribable { + /** + * The name of this channel. Supports wildcards by ending it with ".*" + * + * See more in the [documentation](https://www.pubnub.com/docs/general/channels/overview) + */ + val name: String + + /** + * Returns a [Subscription] that can be used to subscribe to this channel. + * + * Channel subscriptions support passing [com.pubnub.api.v2.subscriptions.SubscriptionOptions.receivePresenceEvents] in + * [options] to enable receiving presence events. + * + * [com.pubnub.api.v2.subscriptions.SubscriptionOptions.filter] can be used to filter events delivered to the subscription. + * + * For example, to create a subscription that only listens to presence events: + * ``` + * channel.subscription(SubscriptionOptions.receivePresenceEvents() + SubscriptionOptions.filter { it is PNPresenceEventResult } ) + * ``` + * + * @param options optional [SubscriptionOptions]. + * @return an inactive [Subscription] to this channel. You must call [Subscription.subscribe] to start receiving events. + */ + override fun subscription(options: SubscriptionOptions): Subscription + /** * Returns a [Subscription] that can be used to subscribe to this channel. * @@ -78,7 +103,7 @@ interface Channel : BaseChannel { * * @param message The payload which will be serialized and sent. */ - fun signal(message: Any): SignalBuilder + fun signal(message: Any): Signal /** * Send a message to PubNub Functions Event Handlers. diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/ChannelGroup.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/ChannelGroup.kt new file mode 100644 index 000000000..27623165a --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/ChannelGroup.kt @@ -0,0 +1,60 @@ +package com.pubnub.api.java.v2.entities + +import com.pubnub.api.java.v2.subscriptions.Subscription +import com.pubnub.api.v2.entities.Subscribable +import com.pubnub.api.v2.subscriptions.SubscriptionOptions + +/** + * A representation of a PubNub channel group identified by its [name]. + * + * You can get a [Subscription] to this channel group through [Subscribable.subscription]. + * + * Use the [com.pubnub.api.java.PubNubForJava.channelGroup] factory method to create instances of this interface. + */ +interface ChannelGroup : com.pubnub.api.java.v2.entities.Subscribable { + /** + * The name of this channel group. + * + * See more in the [documentation](https://www.pubnub.com/docs/general/channels/subscribe#channel-groups) + */ + val name: String + + /** + * Returns a [Subscription] that can be used to subscribe to this channel group. + * + * Channel group subscriptions support passing [com.pubnub.api.v2.subscriptions.SubscriptionOptions.receivePresenceEvents] + * in [options] to enable receiving presence events. + * + * [com.pubnub.api.v2.subscriptions.SubscriptionOptions.filter] can be used to filter events delivered to the subscription. + * + * For example, to create a subscription that only listens to presence events: + * ``` + * channelGroup.subscription(SubscriptionOptions.receivePresenceEvents() + SubscriptionOptions.filter { it is PNPresenceEventResult } ) + * ``` + * + * *Warning:* if a channel is part of more than one channel group, and you create subscription to both (or more) + * those groups using a single [com.pubnub.api.PubNub] instance, you will only receive events for that channel in + * one channel group subscription. + * + * For example, let's say "channel_1" is part of groups "cg_1" and "cg_2". If you only subscribe to "cg_1", + * or you only subscribe to "cg_2", you will get all events for "channel_1". However, if in your app you subscribe + * to both "cg_1" and "cg_2" at the same time, you will only receive events for "channel_1" in one of those + * subscriptions, chosen at random. + * + * This limitation is due to how the server manages channels and channel groups. + * + * @param options optional [SubscriptionOptions]. + * @return an inactive [Subscription] to this channel group. You must call [Subscription.subscribe] to start receiving events. + */ + override fun subscription(options: SubscriptionOptions): Subscription + + /** + * Returns a [Subscription] that can be used to subscribe to this channel group. + * + * The returned [Subscription] is initially inactive. You must call [Subscription.subscribe] on it + * to start receiving events. + * + * @return An inactive [Subscription] to this channel. + */ + fun subscription(): Subscription +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/ChannelMetadata.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/ChannelMetadata.kt new file mode 100644 index 000000000..2668af936 --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/ChannelMetadata.kt @@ -0,0 +1,41 @@ +package com.pubnub.api.java.v2.entities + +import com.pubnub.api.java.v2.subscriptions.Subscription +import com.pubnub.api.v2.entities.Subscribable +import com.pubnub.api.v2.subscriptions.SubscriptionOptions + +/** + * A representation of a PubNub entity for tracking channel metadata changes. + * + * You can get a [Subscription] to listen for metadata events through [Subscribable.subscription]. + * + * Use the [com.pubnub.api.java.PubNubForJava.channelMetadata] factory method to create instances of this interface. + */ +interface ChannelMetadata : com.pubnub.api.java.v2.entities.Subscribable { + /** + * The id for this channel metadata object. + * + * See more in the [documentation](https://www.pubnub.com/docs/general/metadata/channel-metadata) + */ + val id: String + + /** + * Returns a [Subscription] that can be used to subscribe to this channel metadata. + * + * [com.pubnub.api.v2.subscriptions.SubscriptionOptions.filter] can be used to filter events delivered to the subscription. + * + * @param options optional [SubscriptionOptions]. + * @return an inactive [Subscription] to this channel metadata. You must call [Subscription.subscribe] to start receiving events. + */ + override fun subscription(options: SubscriptionOptions): Subscription + + /** + * Returns a [Subscription] that can be used to subscribe to this channel. + * + * The returned [Subscription] is initially inactive. You must call [Subscription.subscribe] on it + * to start receiving events. + * + * @return An inactive [Subscription] to this channel. + */ + fun subscription(): Subscription +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/Subscribable.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/Subscribable.kt new file mode 100644 index 000000000..493c5084e --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/Subscribable.kt @@ -0,0 +1,18 @@ +package com.pubnub.api.java.v2.entities + +import com.pubnub.api.v2.subscriptions.EmptyOptions +import com.pubnub.api.v2.subscriptions.SubscriptionOptions + +/** + * This interface is implemented by entities that can be subscribed to, such as channels, channel groups, and user and + * channel metadata. + */ +interface Subscribable { + /** + * Returns a [com.pubnub.api.v2.subscriptions.Subscription] that can be used to subscribe to this `Subscribable`. + * + * @param options optional [SubscriptionOptions]. + * @return an inactive [Subscription] to this `Subscribable`. You must call [Subscription.subscribe] to start receiving events. + */ + fun subscription(options: SubscriptionOptions = EmptyOptions): com.pubnub.api.java.v2.subscriptions.Subscription +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/UserMetadata.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/UserMetadata.kt new file mode 100644 index 000000000..a8df7b081 --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/entities/UserMetadata.kt @@ -0,0 +1,41 @@ +package com.pubnub.api.java.v2.entities + +import com.pubnub.api.java.v2.subscriptions.Subscription +import com.pubnub.api.v2.entities.Subscribable +import com.pubnub.api.v2.subscriptions.SubscriptionOptions + +/** + * A representation of a PubNub entity for tracking user metadata changes. + * + * You can get a [Subscription] to listen for metadata events through [Subscribable.subscription]. + * + * Use the [com.pubnub.api.java.PubNubForJava.userMetadata] factory method to create instances of this interface. + */ +interface UserMetadata : com.pubnub.api.java.v2.entities.Subscribable { + /** + * The id for this user metadata object. + * + * See more in the [documentation](https://www.pubnub.com/docs/general/metadata/users-metadata) + */ + val id: String + + /** + * Returns a [Subscription] that can be used to subscribe to this user metadata. + * + * [com.pubnub.api.v2.subscriptions.SubscriptionOptions.filter] can be used to filter events delivered to the subscription. + * + * @param options optional [SubscriptionOptions]. + * @return an inactive [Subscription] to this user metadata. You must call [Subscription.subscribe] to start receiving events. + */ + override fun subscription(options: SubscriptionOptions): Subscription + + /** + * Returns a [Subscription] that can be used to subscribe to this channel. + * + * The returned [Subscription] is initially inactive. You must call [Subscription.subscribe] on it + * to start receiving events. + * + * @return An inactive [Subscription] to this channel. + */ + fun subscription(): Subscription +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/subscriptions/Subscription.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/subscriptions/Subscription.kt similarity index 65% rename from pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/subscriptions/Subscription.kt rename to pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/subscriptions/Subscription.kt index 22620e28a..a7d2e3f1f 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/subscriptions/Subscription.kt +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/subscriptions/Subscription.kt @@ -1,25 +1,24 @@ -package com.pubnub.api.v2.subscriptions +package com.pubnub.api.java.v2.subscriptions -import com.pubnub.api.v2.callbacks.EventEmitter -import com.pubnub.api.v2.callbacks.EventListener +import com.pubnub.api.v2.subscriptions.SubscribeCapable /** * Represents a potential subscription to the PubNub real-time network. * * Create objects of this class through the [com.pubnub.api.v2.entities.Subscribable.subscription] method of the - * respective entities, such as [com.pubnub.api.v2.entities.Channel], [com.pubnub.api.v2.entities.ChannelGroup], - * [com.pubnub.api.v2.entities.ChannelMetadata] and [com.pubnub.api.v2.entities.UserMetadata]. + * respective entities, such as [com.pubnub.api.java.v2.entities.Channel], [com.pubnub.api.java.v2.entities.ChannelGroup], + * [com.pubnub.api.java.v2.entities.ChannelMetadata] and [com.pubnub.api.java.v2.entities.UserMetadata]. * * Created subscriptions are initially inactive, which means you must call [subscribe] to start receiving events. * * This class implements the [AutoCloseable] interface to help you release resources by calling [unsubscribe] * and removing all listeners on [close]. Remember to always call [close] when you no longer need this Subscription. */ -interface Subscription : BaseSubscription, EventEmitter { +interface Subscription : com.pubnub.api.java.v2.callbacks.EventEmitter, SubscribeCapable, AutoCloseable, com.pubnub.api.v2.subscriptions.Subscription { /** * Create a [SubscriptionSet] containing this [Subscription] and the added [subscription]. */ - fun plus(subscription: Subscription): SubscriptionSet + operator fun plus(subscription: Subscription): SubscriptionSet /** * Start receiving events from the subscriptions (or subscriptions) represented by this object. diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/subscriptions/SubscriptionSet.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/subscriptions/SubscriptionSet.kt new file mode 100644 index 000000000..bc1849b4a --- /dev/null +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/subscriptions/SubscriptionSet.kt @@ -0,0 +1,65 @@ +package com.pubnub.api.java.v2.subscriptions + +import com.pubnub.api.java.v2.callbacks.EventEmitter +import com.pubnub.api.v2.subscriptions.SubscribeCapable + +/** + * A helper class that manages multiple [Subscription]s that can be added to it. + * + * Use the [com.pubnub.api.java.PubNubForJava.subscriptionSetOf] factory methods to create instances of this interface. + * + * Adding multiple `Subscription`s to the set, then calling [subscribe] or [unsubscribe] on the set is more efficient + * than calling [Subscription.subscribe] on each `Subscription` object separately, as the PubNub client can minimize + * the number of required reconnections internally. + * + * Remember to always [close] the set when you're done using it to avoid memory leaks. + * Closing the set also closes all `Subscription`s that are part of this set. + */ +interface SubscriptionSet : EventEmitter, SubscribeCapable, AutoCloseable { + /** + * Start receiving events from the subscriptions (or subscriptions) represented by this object. + * + * The PubNub client will start a network connection to the server if it doesn't have one already, + * or will alter the existing connection to add channels and groups requested by this subscriptions if needed. + */ + fun subscribe() + + /** + * Add a [Subscription] to this set. + * + * Please note that this SubscriptionSet will *not* attempt to ensure all subscriptions match their + * active/inactive state. That is, if you previously called [subscribe] or [unsubscribe] on this set, it will not be + * called on the newly added [subscription] automatically. + * + * @param subscription the [Subscription] to add. + */ + fun add(subscription: Subscription) + + /** + * Remove the [subscription] from this set. + * + * Please note that removing a subscription from the set does not automatically [unsubscribe] or [close] it. + * + * @param subscription the [Subscription] to remove. + */ + fun remove(subscription: Subscription) + + /** + * Returns an immutable copy of the set of subscriptions contained in this [BaseSubscriptionSet]. + */ + val subscriptions: Set + + /** + * Add the [subscription] to this SubscriptionSet. Equivalent to calling [add]. + * + * @see [add] + */ + operator fun plusAssign(subscription: Subscription) + + /** + * Remove the [subscription] from this set. Equivalent to calling [remove]. + * + * @see [remove] + */ + operator fun minusAssign(subscription: Subscription) +} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/EventEmitter.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/EventEmitter.java deleted file mode 100644 index 3926da4fa..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/EventEmitter.java +++ /dev/null @@ -1,223 +0,0 @@ -package com.pubnub.api.v2.callbacks; - -import com.pubnub.api.v2.callbacks.handlers.OnChannelMetadataHandler; -import com.pubnub.api.v2.callbacks.handlers.OnFileHandler; -import com.pubnub.api.v2.callbacks.handlers.OnMembershipHandler; -import com.pubnub.api.v2.callbacks.handlers.OnMessageActionHandler; -import com.pubnub.api.v2.callbacks.handlers.OnMessageHandler; -import com.pubnub.api.v2.callbacks.handlers.OnPresenceHandler; -import com.pubnub.api.v2.callbacks.handlers.OnSignalHandler; -import com.pubnub.api.v2.callbacks.handlers.OnUuidMetadataHandler; - -/** - * Interface implemented by objects that are the source of real time events from the PubNub network. - */ -public interface EventEmitter extends BaseEventEmitter { - /** - * Sets the handler for incoming message events. - * This method allows the assignment of an {@link OnMessageHandler} implementation or lambda expression to handle - * incoming messages. - * - * To deactivate the current behavior, simply set this property to `null`. - * - * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. - * For scenarios requiring multiple behaviors in response to message events, it is advisable - * to utilize {@link EventEmitter#addListener}. - * - *

Setting a Behavior Example:

- *
{@code
-     * setOnMessage(pnMessageResult -> System.out.println("Received: " + pnMessageResult.getMessage()));
-     * }
- * - *

Removing a Behavior Example:

- *
{@code
-     * setOnMessage(null);
-     * }
- * - * @param onMessageHandler An implementation of {@link OnMessageHandler} or a lambda expression to handle - * incoming messages. It can be {@code null} to remove the current handler. - */ - void setOnMessage(OnMessageHandler onMessageHandler); - - /** - * Sets the handler for incoming signal events. - * This method allows the assignment of an {@link OnSignalHandler} implementation or lambda expression to handle - * incoming signals. - * - * To deactivate the current behavior, simply set this property to `null`. - * - * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. - * For scenarios requiring multiple behaviors in response to signal events, it is advisable - * to utilize {@link EventEmitter#addListener}. - * - *

Setting a Behavior Example:

- *
{@code
-     * setOnSignal(pnSignalResult -> System.out.println("Received: " + pnSignalResult.getMessage()));
-     * }
- * - *

Removing a Behavior Example:

- *
{@code
-     * setOnSignal(null);
-     * }
- * - * @param onSignalHandler An implementation of {@link OnSignalHandler} or a lambda expression to handle - * incoming messages. It can be {@code null} to remove the current handler. - */ - void setOnSignal(OnSignalHandler onSignalHandler); - - /** - * Sets the handler for incoming presence events. - * This method allows the assignment of an {@link OnPresenceHandler} implementation or lambda expression to handle - * incoming presence events. - * - * To deactivate the current behavior, simply set this property to `null`. - * - * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. - * For scenarios requiring multiple behaviors in response to presence events, it is advisable - * to utilize {@link EventEmitter#addListener}. - * - *

Setting a Behavior Example:

- *
{@code
-     * onPresenceHandler(pnPresenceEventResult -> System.out.println("Received: " + pnPresenceEventResult.getEvent()));
-     * }
- * - *

Removing a Behavior Example:

- *
{@code
-     * onPresenceHandler(null);
-     * }
- * - * @param onPresenceHandler An implementation of {@link OnPresenceHandler} or a lambda expression to handle - * incoming messages. It can be {@code null} to remove the current handler. - */ - void setOnPresence(OnPresenceHandler onPresenceHandler); - - /** - * Sets the handler for incoming messageAction events. - * This method allows the assignment of an {@link OnMessageActionHandler} implementation or lambda expression to handle - * incoming presence events. - * - * To deactivate the current behavior, simply set this property to `null`. - * - * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. - * For scenarios requiring multiple behaviors in response to messageAction events, it is advisable - * to utilize {@link EventEmitter#addListener}. - * - *

Setting a Behavior Example:

- *
{@code
-     * onMessageActionHandler(pnMessageActionResult -> System.out.println("Received: " + pnMessageActionResult.getMessageAction()));
-     * }
- * - *

Removing a Behavior Example:

- *
{@code
-     * onMessageActionHandler(null);
-     * }
- * - * @param onMessageActionHandler An implementation of {@link OnMessageActionHandler} or a lambda expression to handle - * incoming messages. It can be {@code null} to remove the current handler. - */ - void setOnMessageAction(OnMessageActionHandler onMessageActionHandler); - - /** - * Sets the handler for incoming uuidMetadata events. - * This method allows the assignment of an {@link OnUuidMetadataHandler} implementation or lambda expression to handle - * incoming presence events. - * - * To deactivate the current behavior, simply set this property to `null`. - * - * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. - * For scenarios requiring multiple behaviors in response to uuidMetadata events, it is advisable - * to utilize {@link EventEmitter#addListener}. - * - *

Setting a Behavior Example:

- *
{@code
-     * onUuidMetadataHandler(pnUUIDMetadataResult -> System.out.println("Received: " + pnUUIDMetadataResult.getData()));
-     * }
- * - *

Removing a Behavior Example:

- *
{@code
-     * onUuidMetadataHandler(null);
-     * }
- * - * @param onUuidMetadataHandler An implementation of {@link OnUuidMetadataHandler} or a lambda expression to handle - * incoming messages. It can be {@code null} to remove the current handler. - */ - void setOnUuidMetadata(OnUuidMetadataHandler onUuidMetadataHandler); - - /** - * Sets the handler for incoming channelMetadata events. - * This method allows the assignment of an {@link OnChannelMetadataHandler} implementation or lambda expression to handle - * incoming presence events. - * - * To deactivate the current behavior, simply set this property to `null`. - * - * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. - * For scenarios requiring multiple behaviors in response to channelMetadata events, it is advisable - * to utilize {@link EventEmitter#addListener}. - * - *

Setting a Behavior Example:

- *
{@code
-     * onChannelMetadataHandler(pnChannelMetadataResult -> System.out.println("Received: " +  pnChannelMetadataResult.getEvent()));
-     * }
- * - *

Removing a Behavior Example:

- *
{@code
-     * onChannelMetadataHandler(null);
-     * }
- * - * @param onChannelMetadataHandler An implementation of {@link OnChannelMetadataHandler} or a lambda expression to handle - * incoming messages. It can be {@code null} to remove the current handler. - */ - void setOnChannelMetadata(OnChannelMetadataHandler onChannelMetadataHandler); - - /** - * Sets the handler for incoming membership events. - * This method allows the assignment of an {@link OnMembershipHandler} implementation or lambda expression to handle - * incoming presence events. - * - * To deactivate the current behavior, simply set this property to `null`. - * - * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. - * For scenarios requiring multiple behaviors in response to membership events, it is advisable - * to utilize {@link EventEmitter#addListener}. - * - *

Setting a Behavior Example:

- *
{@code
-     * onMembershipHandler(pnMembershipResult -> System.out.println("Received: " +  pnMembershipResult.getEvent()));
-     * }
- * - *

Removing a Behavior Example:

- *
{@code
-     * onMembershipHandler(null);
-     * }
- * - * @param onMembershipHandler An implementation of {@link OnMembershipHandler} or a lambda expression to handle - * incoming messages. It can be {@code null} to remove the current handler. - */ - void setOnMembership(OnMembershipHandler onMembershipHandler); - - /** - * Sets the handler for incoming file events. - * This method allows the assignment of an {@link OnFileHandler} implementation or lambda expression to handle - * incoming presence events. - * - * To deactivate the current behavior, simply set this property to `null`. - * - * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. - * For scenarios requiring multiple behaviors in response to file events, it is advisable - * to utilize {@link EventEmitter#addListener}. - * - *

Setting a Behavior Example:

- *
{@code
-     * onFileHandler(pnFileEventResult -> System.out.println("Received: " +  pnFileEventResult.getMessage()));
-     * }
- * - *

Removing a Behavior Example:

- *
{@code
-     * onFileHandler(null);
-     * }
- * - * @param onFileHandler An implementation of {@link OnFileHandler} or a lambda expression to handle - * incoming messages. It can be {@code null} to remove the current handler. - */ - void setOnFile(OnFileHandler onFileHandler); -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/EventListener.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/EventListener.java deleted file mode 100644 index 7a3a55e22..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/EventListener.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.pubnub.api.v2.callbacks; - -import com.pubnub.api.PubNub; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult; -import com.pubnub.api.models.consumer.pubsub.PNMessageResult; -import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; -import com.pubnub.api.models.consumer.pubsub.PNSignalResult; -import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult; -import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult; -import org.jetbrains.annotations.NotNull; - -/** - * Implement this interface and pass it into [EventEmitter.addListener] to listen for events from the PubNub real-time - * network. - */ -public interface EventListener extends BaseEventListener { - - default void message(@NotNull PubNub pubnub, @NotNull PNMessageResult pnMessageResult) { - } - - default void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult pnPresenceEventResult) { - } - - default void signal(@NotNull PubNub pubnub, @NotNull PNSignalResult pnSignalResult) { - } - - default void uuid(@NotNull PubNub pubnub, @NotNull PNUUIDMetadataResult pnUUIDMetadataResult) { - } - - default void channel(@NotNull PubNub pubnub, @NotNull PNChannelMetadataResult pnChannelMetadataResult) { - } - - default void membership(@NotNull PubNub pubnub, @NotNull PNMembershipResult pnMembershipResult) { - } - - default void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnMessageActionResult) { - } - - default void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - } - -} - diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/StatusEmitter.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/StatusEmitter.java deleted file mode 100644 index 39169181c..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/StatusEmitter.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.pubnub.api.v2.callbacks; - -/** - * Interface implemented by objects that manage the subscription connection to the PubNub network and can be monitored - * for connection state changes. - */ -public interface StatusEmitter extends BaseStatusEmitter { -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/StatusListener.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/StatusListener.java deleted file mode 100644 index 39fee8f5f..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/callbacks/StatusListener.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.api.v2.callbacks; - - -import com.pubnub.api.PubNub; -import com.pubnub.api.models.consumer.PNStatus; -import org.jetbrains.annotations.NotNull; - -/** - * Implement this interface and pass it into [com.pubnub.api.v2.callbacks.StatusEmitter.addListener] to listen for - * PubNub connection status changes. - */ -public interface StatusListener extends BaseStatusListener { - void status(@NotNull PubNub pubnub, @NotNull PNStatus pnStatus); -} \ No newline at end of file diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/endpoints/pubsub/SignalBuilder.java b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/endpoints/pubsub/SignalBuilder.java deleted file mode 100644 index 1a4e0b03f..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/endpoints/pubsub/SignalBuilder.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.pubnub.api.v2.endpoints.pubsub; - -import com.pubnub.api.endpoints.Endpoint; -import com.pubnub.api.models.consumer.PNPublishResult; - -public interface SignalBuilder extends Endpoint { -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/ChannelGroup.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/ChannelGroup.kt deleted file mode 100644 index cf9be43f9..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/ChannelGroup.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.pubnub.api.v2.entities - -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.subscriptions.Subscription - -/** - * A representation of a PubNub channel group identified by its [name]. - * - * You can get a [Subscription] to this channel group through [Subscribable.subscription]. - * - * Use the [com.pubnub.api.PubNub.channelGroup] factory method to create instances of this interface. - */ -interface ChannelGroup : BaseChannelGroup { - /** - * Returns a [Subscription] that can be used to subscribe to this channel group. - * - * The returned [Subscription] is initially inactive. You must call [Subscription.subscribe] on it - * to start receiving events. - * - * @return An inactive [Subscription] to this channel. - */ - fun subscription(): Subscription -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/ChannelMetadata.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/ChannelMetadata.kt deleted file mode 100644 index f8d80d661..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/ChannelMetadata.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.pubnub.api.v2.entities - -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.subscriptions.Subscription - -/** - * A representation of a PubNub entity for tracking channel metadata changes. - * - * You can get a [Subscription] to listen for metadata events through [Subscribable.subscription]. - * - * Use the [com.pubnub.api.PubNub.channelMetadata] factory method to create instances of this interface. - */ -interface ChannelMetadata : BaseChannelMetadata { - /** - * Returns a [Subscription] that can be used to subscribe to this channel. - * - * The returned [Subscription] is initially inactive. You must call [Subscription.subscribe] on it - * to start receiving events. - * - * @return An inactive [Subscription] to this channel. - */ - fun subscription(): Subscription -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/UserMetadata.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/UserMetadata.kt deleted file mode 100644 index dab2821b6..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/entities/UserMetadata.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.pubnub.api.v2.entities - -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.subscriptions.Subscription - -/** - * A representation of a PubNub entity for tracking user metadata changes. - * - * You can get a [Subscription] to listen for metadata events through [Subscribable.subscription]. - * - * Use the [com.pubnub.api.PubNub.userMetadata] factory method to create instances of this interface. - */ -interface UserMetadata : BaseUserMetadata { - /** - * Returns a [Subscription] that can be used to subscribe to this channel. - * - * The returned [Subscription] is initially inactive. You must call [Subscription.subscribe] on it - * to start receiving events. - * - * @return An inactive [Subscription] to this channel. - */ - fun subscription(): Subscription -} diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/subscriptions/SubscriptionSet.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/subscriptions/SubscriptionSet.kt deleted file mode 100644 index ba7d90f6f..000000000 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/v2/subscriptions/SubscriptionSet.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.pubnub.api.v2.subscriptions - -import com.pubnub.api.v2.callbacks.EventEmitter -import com.pubnub.api.v2.callbacks.EventListener - -/** - * A helper class that manages multiple [Subscription]s that can be added to it. - * - * Use the [com.pubnub.api.PubNub.subscriptionSetOf] factory methods to create instances of this interface. - * - * Adding multiple `Subscription`s to the set, then calling [subscribe] or [unsubscribe] on the set is more efficient - * than calling [Subscription.subscribe] on each `Subscription` object separately, as the PubNub client can minimize - * the number of required reconnections internally. - * - * Remember to always [close] the set when you're done using it to avoid memory leaks. - * Closing the set also closes all `Subscription`s that are part of this set. - */ -interface SubscriptionSet : BaseSubscriptionSet, EventEmitter { - /** - * Start receiving events from the subscriptions (or subscriptions) represented by this object. - * - * The PubNub client will start a network connection to the server if it doesn't have one already, - * or will alter the existing connection to add channels and groups requested by this subscriptions if needed. - */ - fun subscribe() -} diff --git a/pubnub-gson/pubnub-gson-impl/build.gradle.kts b/pubnub-gson/pubnub-gson-impl/build.gradle.kts index da7edd6ef..6e1942757 100644 --- a/pubnub-gson/pubnub-gson-impl/build.gradle.kts +++ b/pubnub-gson/pubnub-gson-impl/build.gradle.kts @@ -10,9 +10,9 @@ plugins { } dependencies { - api(project(":pubnub-core:pubnub-core-api")) +// api(project(":pubnub-kotlin:pubnub-kotlin-api")) api(project(":pubnub-gson:pubnub-gson-api")) - implementation(project(":pubnub-core:pubnub-core-impl")) + implementation(project(":pubnub-kotlin:pubnub-kotlin-impl")) implementation(libs.slf4j) implementation(libs.jetbrains.annotations) diff --git a/pubnub-gson/pubnub-gson-impl/config/ktlint/baseline.xml b/pubnub-gson/pubnub-gson-impl/config/ktlint/baseline.xml index 981420778..ad2a90c41 100644 --- a/pubnub-gson/pubnub-gson-impl/config/ktlint/baseline.xml +++ b/pubnub-gson/pubnub-gson-impl/config/ktlint/baseline.xml @@ -1,3 +1,9 @@ + + + + + + diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/FilesIntegrationTests.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/FilesIntegrationTests.java index d9b804121..fb4f3caea 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/FilesIntegrationTests.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/FilesIntegrationTests.java @@ -2,7 +2,8 @@ import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.callbacks.SubscribeCallback; import com.pubnub.api.crypto.CryptoModule; import com.pubnub.api.enums.PNStatusCategory; import com.pubnub.api.integration.util.BaseIntegrationTest; @@ -53,14 +54,14 @@ public void doItAllFilesTest(boolean withCipher) throws PubNubException, Interru pubNub.addListener(new SubscribeCallback() { @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus pnStatus) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus pnStatus) { if (pnStatus.getCategory() == PNStatusCategory.PNConnectedCategory) { connectedLatch.countDown(); } } @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { + public void file(@NotNull PubNubForJava pubnub, @NotNull PNFileEventResult pnFileEventResult) { if (pnFileEventResult.getFile().getName().equals(fileName)) { fileEventReceived.countDown(); } diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/HeartbeatIntegrationTest.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/HeartbeatIntegrationTest.java index 14337fd23..4d8c36479 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/HeartbeatIntegrationTest.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/HeartbeatIntegrationTest.java @@ -2,7 +2,8 @@ import com.google.gson.JsonObject; import com.pubnub.api.PubNub; -import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.callbacks.SubscribeCallback; import com.pubnub.api.enums.PNStatusCategory; import com.pubnub.api.integration.util.BaseIntegrationTest; import com.pubnub.api.models.consumer.PNStatus; @@ -40,7 +41,7 @@ protected void onAfter() { public void testStateWithHeartbeat() { final AtomicInteger hits = new AtomicInteger(); final JsonObject expectedStatePayload = generatePayload(); - final PubNub observer = getPubNub(); + final PubNubForJava observer = getPubNub(); pubNub = getPubNub(builder -> { builder.presenceTimeout(20); @@ -48,7 +49,7 @@ public void testStateWithHeartbeat() { }); observer.addListener(new SubscribeCallback.BaseSubscribeCallback() { @Override - public void status(@NotNull PubNub pn, @NotNull PNStatus status) { + public void status(@NotNull PubNubForJava pn, @NotNull PNStatus status) { if (status.getCategory() == PNStatusCategory.PNConnectedCategory) { if (status.getAffectedChannels().contains(expectedChannel)) { pubNub.subscribe() @@ -60,7 +61,7 @@ public void status(@NotNull PubNub pn, @NotNull PNStatus status) { } @Override - public void presence(@NotNull PubNub p, @NotNull PNPresenceEventResult presence) { + public void presence(@NotNull PubNubForJava p, @NotNull PNPresenceEventResult presence) { if (presence.getUuid().equals(pubNub.getConfiguration().getUserId().getValue()) && presence.getChannel().equals(expectedChannel)) { switch (presence.getEvent()) { diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/HistoryIntegrationTest.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/HistoryIntegrationTest.java index 38578f790..fbb97fcd0 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/HistoryIntegrationTest.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/HistoryIntegrationTest.java @@ -2,7 +2,8 @@ import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.builder.PubNubErrorBuilder; import com.pubnub.api.crypto.CryptoModule; import com.pubnub.api.integration.util.BaseIntegrationTest; import com.pubnub.api.integration.util.RandomGenerator; @@ -21,7 +22,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -import static com.pubnub.api.builder.PubNubErrorBuilder.PNERROBJ_HISTORY_MESSAGE_ACTIONS_MULTIPLE_CHANNELS; +import static com.pubnub.api.java.builder.PubNubErrorBuilder.PNERROBJ_HISTORY_MESSAGE_ACTIONS_MULTIPLE_CHANNELS; import static com.pubnub.api.integration.util.Utils.publishMixed; import static com.pubnub.api.integration.util.Utils.random; import static com.pubnub.api.integration.util.Utils.randomChannel; @@ -315,7 +316,7 @@ public void testHistorySingleChannel_IncludeAll_Crypto() throws PubNubException final String expectedCipherKey = random(); pubNub = getPubNub(builder -> builder.cryptoModule(CryptoModule.createLegacyCryptoModule(expectedCipherKey, true))); - final PubNub observer = getPubNub(builder -> builder.cryptoModule(CryptoModule.createLegacyCryptoModule(expectedCipherKey, true))); + final PubNubForJava observer = getPubNub(builder -> builder.cryptoModule(CryptoModule.createLegacyCryptoModule(expectedCipherKey, true))); final String expectedChannelName = random(); final int expectedMessageCount = 10; @@ -342,7 +343,7 @@ public void testHistorySingleChannel_IncludeAll_Crypto() throws PubNubException public void testReadUnencryptedMessage_FromHistory_WithCrypto() throws PubNubException { final String expectedCipherKey = random(); - final PubNub observer = getPubNub(builder -> builder.cryptoModule(CryptoModule.createLegacyCryptoModule(expectedCipherKey, true))); + final PubNubForJava observer = getPubNub(builder -> builder.cryptoModule(CryptoModule.createLegacyCryptoModule(expectedCipherKey, true))); final String expectedChannelName = random(); final int expectedMessageCount = 10; @@ -370,7 +371,7 @@ public void testReadUnencryptedMessage_FromHistory_WithCrypto() throws PubNubExc public void testReadUnencryptedMessage_FetchMessages_WithCrypto() throws PubNubException { final String expectedCipherKey = random(); - final PubNub observer = getPubNub(builder -> builder.cryptoModule(CryptoModule.createLegacyCryptoModule(expectedCipherKey, true))); + final PubNubForJava observer = getPubNub(builder -> builder.cryptoModule(CryptoModule.createLegacyCryptoModule(expectedCipherKey, true))); final String expectedChannelName = random(); final int expectedMessageCount = 10; @@ -399,7 +400,7 @@ public void testFetchSingleChannel_IncludeAll_Crypto() throws PubNubException { final String expectedCipherKey = random(); pubNub = getPubNub(builder -> builder.cryptoModule(CryptoModule.createLegacyCryptoModule(expectedCipherKey, false))); - final PubNub observer = getPubNub(builder -> builder.cryptoModule(CryptoModule.createLegacyCryptoModule(expectedCipherKey, false))); + final PubNubForJava observer = getPubNub(builder -> builder.cryptoModule(CryptoModule.createLegacyCryptoModule(expectedCipherKey, false))); final String expectedChannelName = random(); final int expectedMessageCount = 10; @@ -426,7 +427,7 @@ public void testFetchSingleChannel_IncludeAll_Crypto() throws PubNubException { public void testFetchSingleChannel_WithActions_IncludeAll_Crypto() throws PubNubException { final String expectedCipherKey = random(); pubNub = getPubNub(builder -> builder.cryptoModule(CryptoModule.createLegacyCryptoModule(expectedCipherKey, true))); - final PubNub observer = getPubNub(builder -> builder.cryptoModule(CryptoModule.createLegacyCryptoModule(expectedCipherKey, true))); + final PubNubForJava observer = getPubNub(builder -> builder.cryptoModule(CryptoModule.createLegacyCryptoModule(expectedCipherKey, true))); final String expectedChannelName = random(); final int expectedMessageCount = 10; diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/MessageActionsTest.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/MessageActionsTest.java index c30ba3295..c13797bca 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/MessageActionsTest.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/MessageActionsTest.java @@ -2,8 +2,9 @@ import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.callbacks.SubscribeCallback; -import com.pubnub.api.endpoints.message_actions.GetMessageActions; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.callbacks.SubscribeCallback; +import com.pubnub.api.java.endpoints.message_actions.GetMessageActions; import com.pubnub.api.enums.PNStatusCategory; import com.pubnub.api.integration.util.BaseIntegrationTest; import com.pubnub.api.integration.util.RandomGenerator; @@ -13,9 +14,9 @@ import com.pubnub.api.models.consumer.message_actions.PNAddMessageActionResult; import com.pubnub.api.models.consumer.message_actions.PNGetMessageActionsResult; import com.pubnub.api.models.consumer.message_actions.PNMessageAction; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult; import com.pubnub.api.models.consumer.pubsub.PNMessageResult; import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; import com.pubnub.api.models.consumer.pubsub.PNSignalResult; @@ -34,12 +35,12 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import static com.pubnub.api.builder.PubNubErrorBuilder.PNERROBJ_CHANNEL_MISSING; -import static com.pubnub.api.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_ACTION_MISSING; -import static com.pubnub.api.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_ACTION_TIMETOKEN_MISSING; -import static com.pubnub.api.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_ACTION_TYPE_MISSING; -import static com.pubnub.api.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_ACTION_VALUE_MISSING; -import static com.pubnub.api.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_TIMETOKEN_MISSING; +import static com.pubnub.api.java.builder.PubNubErrorBuilder.PNERROBJ_CHANNEL_MISSING; +import static com.pubnub.api.java.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_ACTION_MISSING; +import static com.pubnub.api.java.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_ACTION_TIMETOKEN_MISSING; +import static com.pubnub.api.java.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_ACTION_TYPE_MISSING; +import static com.pubnub.api.java.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_ACTION_VALUE_MISSING; +import static com.pubnub.api.java.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_TIMETOKEN_MISSING; import static com.pubnub.api.integration.util.Utils.isSorted; import static com.pubnub.api.integration.util.Utils.parseDate; import static com.pubnub.api.integration.util.Utils.publish; @@ -486,12 +487,7 @@ public void testActionReceive() throws PubNubException { pubNub.addListener(new SubscribeCallback() { @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - - } - - @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus pnStatus) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus pnStatus) { if (pnStatus.getCategory() == PNStatusCategory.PNConnectedCategory) { for (PNPublishResult pnPublishResult : publishResultList) { try { @@ -510,37 +506,22 @@ public void status(@NotNull PubNub pubnub, @NotNull PNStatus pnStatus) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult pnMessageResult) { - fail(); - } - - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult pnPresenceEventResult) { - - } - - @Override - public void signal(@NotNull PubNub pubnub, @NotNull PNSignalResult pnSignalResult) { - fail(); - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { + public void uuid(@NotNull final PubNubForJava pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { fail(); } @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { + public void channel(@NotNull final PubNubForJava pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { fail(); } @Override - public void membership(@NotNull final PubNub pubnub, @NotNull final PNMembershipResult pnMembershipResult) { + public void membership(@NotNull final PubNubForJava pubnub, @NotNull final PNMembershipResult pnMembershipResult) { fail(); } @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { + public void messageAction(@NotNull PubNubForJava pubnub, @NotNull PNMessageActionResult pnActionResult) { assertEquals(expectedChannelName, pnActionResult.getChannel()); actionsCount.incrementAndGet(); } diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PAMFilesIntegrationTests.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PAMFilesIntegrationTests.java index fc62091a7..96fae52a3 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PAMFilesIntegrationTests.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PAMFilesIntegrationTests.java @@ -4,6 +4,7 @@ import com.pubnub.api.PubNubException; import com.pubnub.api.integration.util.BaseIntegrationTest; import com.pubnub.api.integration.util.ITTestConfig; +import com.pubnub.api.java.PubNubForJava; import com.pubnub.api.models.consumer.files.PNDownloadFileResult; import com.pubnub.api.models.consumer.files.PNFileUploadResult; import org.aeonbits.owner.ConfigFactory; @@ -35,8 +36,8 @@ public class PAMFilesIntegrationTests extends BaseIntegrationTest { public void canSendAndDownloadFileWithPAM() throws PubNubException, IOException { assumeNotNull(getServer().getConfiguration().getSecretKey()); - final PubNub adminPubnub = getServer(); - final PubNub pubnub = getPubNub(); + final PubNubForJava adminPubnub = getServer(); + final PubNubForJava pubnub = getPubNub(); try { adminPubnub.grant() diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PNConfigurationIntegrationTests.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PNConfigurationIntegrationTests.java index 3505e286f..f3cfa8217 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PNConfigurationIntegrationTests.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PNConfigurationIntegrationTests.java @@ -5,8 +5,9 @@ import com.pubnub.api.UserId; import com.pubnub.api.integration.util.ITTestConfig; import com.pubnub.api.integration.util.Utils; -import com.pubnub.api.v2.PNConfiguration; -import com.pubnub.api.v2.PNConfigurationOverride; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.v2.PNConfiguration; +import com.pubnub.api.java.v2.PNConfigurationOverride; import org.aeonbits.owner.ConfigFactory; import org.junit.Assert; import org.junit.Test; @@ -69,7 +70,7 @@ public void useConfigurationOverrideWithPublish() throws PubNubException { PNConfiguration.Builder configBuilder = PNConfiguration.builder(new UserId(expectedUuid), itTestConfig.subscribeKey()) .publishKey("rubbishKey"); PNConfiguration config = configBuilder.build(); - PubNub pubnub = PubNub.create(config); + PubNubForJava pubnub = PubNubForJava.create(config); PNConfiguration overrideConfig = PNConfigurationOverride.from(config).publishKey(itTestConfig.publishKey()).build(); diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PresenceEventsIntegrationTests.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PresenceEventsIntegrationTests.java index 1d6af26f4..c08becb9a 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PresenceEventsIntegrationTests.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PresenceEventsIntegrationTests.java @@ -2,12 +2,13 @@ import com.google.gson.JsonObject; import com.pubnub.api.PubNub; -import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.callbacks.SubscribeCallback; import com.pubnub.api.integration.util.BaseIntegrationTest; import com.pubnub.api.models.consumer.PNStatus; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult; import com.pubnub.api.models.consumer.pubsub.PNMessageResult; import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; import com.pubnub.api.models.consumer.pubsub.PNSignalResult; @@ -33,23 +34,14 @@ public void testJoinChannel() { final AtomicBoolean success = new AtomicBoolean(false); pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - - } - - @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { - - } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { } @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { + public void presence(@NotNull PubNubForJava pubnub, @NotNull PNPresenceEventResult presence) { if (presence.getEvent().equals("join")) { assertEquals(channel, presence.getChannel()); pubNub.removeListener(this); @@ -57,30 +49,7 @@ public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult pres } } - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull final PubNub pubnub, @NotNull final PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } + }); subscribeToChannel(pubNub, channel); @@ -93,56 +62,21 @@ public void testLeaveChannel() { final AtomicBoolean success = new AtomicBoolean(false); final String channel = randomChannel(); - final PubNub guestClient = getPubNub(); + final PubNubForJava guestClient = getPubNub(); this.pubNub.addListener(new SubscribeCallback() { @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - - } - - @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { - - } - - @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { } @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { + public void presence(@NotNull PubNubForJava pubnub, @NotNull PNPresenceEventResult presence) { if (presence.getEvent().equals("leave")) { assertEquals(channel, presence.getChannel()); success.set(true); } } - - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull PubNub pubNub, @NotNull PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } }); listen(success, () -> { @@ -168,53 +102,19 @@ public void testTimeoutFromChannel() { final int waitTime = 21; pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - - } - - @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { - - } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { } @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { + public void presence(@NotNull PubNubForJava pubnub, @NotNull PNPresenceEventResult presence) { if (presence.getEvent().equals("timeout")) { assertEquals(channel, presence.getChannel()); success.set(true); } } - - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull PubNub pubNub, @NotNull PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } }); subscribeToChannel(pubNub, channel); @@ -236,23 +136,14 @@ public void testStateChangeEvent() { subscribeToChannel(pubNub, channel); pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - - } - - @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { - - } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { } @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { + public void presence(@NotNull PubNubForJava pubnub, @NotNull PNPresenceEventResult presence) { if (presence.getEvent().equals("state-change") && presence.getUuid() .equals(pubNub.getConfiguration().getUserId().getValue())) { assertEquals("state-change", presence.getEvent()); @@ -261,31 +152,6 @@ public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult pres } } - - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull PubNub pubNub, @NotNull PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } }); pubNub.setPresenceState() diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PresenceIntegrationTests.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PresenceIntegrationTests.java index 0f99b4c1a..071aba480 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PresenceIntegrationTests.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PresenceIntegrationTests.java @@ -2,7 +2,8 @@ import com.google.gson.JsonObject; import com.pubnub.api.PubNub; -import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.callbacks.SubscribeCallback; import com.pubnub.api.enums.PNHeartbeatNotificationOptions; import com.pubnub.api.enums.PNStatusCategory; import com.pubnub.api.integration.util.BaseIntegrationTest; @@ -11,7 +12,7 @@ import com.pubnub.api.models.consumer.presence.PNHereNowChannelData; import com.pubnub.api.models.consumer.presence.PNHereNowOccupantData; import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; -import com.pubnub.api.v2.callbacks.EventListener; +import com.pubnub.api.java.v2.callbacks.EventListener; import org.awaitility.Awaitility; import org.awaitility.Durations; import org.hamcrest.core.IsEqual; @@ -75,7 +76,7 @@ public void testGlobalHereNow() { expectedChannels.add(RandomGenerator.get()); } - final List clients = new ArrayList(expectedClientsCount) {{ + final List clients = new ArrayList(expectedClientsCount) {{ add(pubNub); }}; @@ -83,13 +84,13 @@ public void testGlobalHereNow() { clients.add(getPubNub()); } - for (PubNub client : clients) { + for (PubNubForJava client : clients) { subscribeToChannel(client, expectedChannels); } assertEquals(expectedClientsCount, clients.size()); - for (PubNub client : clients) { + for (PubNubForJava client : clients) { assertEquals(expectedChannelsCount, client.getSubscribedChannels().size()); } @@ -122,7 +123,7 @@ public void testGlobalHereNow() { } final List expectedUuidList = new ArrayList<>(); - for (PubNub client : clients) { + for (PubNubForJava client : clients) { expectedUuidList.add(client.getConfiguration().getUserId().getValue()); } @@ -154,14 +155,14 @@ public void testHereNow() { expectedChannels.add(RandomGenerator.get()); } - final List clients = new ArrayList() {{ + final List clients = new ArrayList() {{ add(pubNub); }}; for (int i = 1; i < expectedClientsCount; i++) { clients.add(getPubNub()); } - for (PubNub client : clients) { + for (PubNubForJava client : clients) { subscribeToChannel(client, expectedChannels); } @@ -188,7 +189,7 @@ public void testHereNow() { for (PNHereNowOccupantData occupant : entry.getValue().getOccupants()) { final String uuid = occupant.getUuid(); boolean contains = false; - for (PubNub client : clients) { + for (PubNubForJava client : clients) { if (client.getConfiguration().getUserId().getValue().equals(uuid)) { contains = true; break; @@ -220,7 +221,7 @@ public void should_setState_withHeartbeat() throws InterruptedException { pubNub.addListener(new SubscribeCallback.BaseSubscribeCallback() { @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { + public void presence(@NotNull PubNubForJava pubnub, @NotNull PNPresenceEventResult presence) { System.out.println("---" + presence.getEvent()); if (presence.getEvent().equals(STATE_CHANGE_EVENT) && presence.getChannel().equals(expectedChannel) @@ -269,7 +270,7 @@ public void testPresenceState() { pubNub.addListener(new EventListener() { @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { + public void presence(@NotNull PubNubForJava pubnub, @NotNull PNPresenceEventResult presence) { if (presence.getEvent().equals(STATE_CHANGE_EVENT) && presence.getChannel().equals(expectedChannel) && presence.getUuid().equals(pubNub.getConfiguration().getUserId().getValue())) { @@ -321,7 +322,7 @@ public void testHeartbeatsDisabled() { pubNub.addListener(new SubscribeCallback.BaseSubscribeCallback() { @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { if (!status.isError()) { if (status.getCategory() == PNStatusCategory.PNConnectedCategory) { if (status.getAffectedChannels().contains(expectedChannel)) { @@ -359,7 +360,7 @@ public void testHeartbeatsEnabled() { pubNub.addListener(new SubscribeCallback.BaseSubscribeCallback() { @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { if (!status.isError()) { if (status.getCategory() == PNStatusCategory.PNConnectedCategory) { if (status.getAffectedChannels().contains(expectedChannel)) { diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PublishIntegrationTests.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PublishIntegrationTests.java index 238da8828..e4310f21d 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PublishIntegrationTests.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/PublishIntegrationTests.java @@ -5,28 +5,31 @@ import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; import com.pubnub.api.UserId; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.callbacks.SubscribeCallback; import com.pubnub.api.crypto.CryptoModule; import com.pubnub.api.enums.PNStatusCategory; import com.pubnub.api.integration.util.BaseIntegrationTest; +import com.pubnub.api.java.v2.callbacks.handlers.OnMessageHandler; +import com.pubnub.api.java.v2.callbacks.handlers.OnSignalHandler; import com.pubnub.api.models.consumer.PNPublishResult; import com.pubnub.api.models.consumer.PNStatus; import com.pubnub.api.models.consumer.history.PNFetchMessagesResult; import com.pubnub.api.models.consumer.history.PNHistoryResult; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult; import com.pubnub.api.models.consumer.pubsub.PNMessageResult; import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; import com.pubnub.api.models.consumer.pubsub.PNSignalResult; import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult; import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult; -import com.pubnub.api.v2.PNConfiguration; -import com.pubnub.api.v2.PNConfigurationOverride; +import com.pubnub.api.java.v2.PNConfiguration; +import com.pubnub.api.java.v2.PNConfigurationOverride; import com.pubnub.api.v2.callbacks.Result; -import com.pubnub.api.v2.entities.Channel; -import com.pubnub.api.v2.subscriptions.Subscription; +import com.pubnub.api.java.v2.entities.Channel; +import com.pubnub.api.java.v2.subscriptions.Subscription; import org.awaitility.Awaitility; import org.awaitility.Durations; import org.hamcrest.Matchers; @@ -93,8 +96,8 @@ public void testPublishUsingChannelEntity() throws InterruptedException, PubNubE Channel channel = pubNub.channel(channelName); Subscription subscription = channel.subscription(); - subscription.setOnMessage(message -> messageReceived.set(true)); - subscription.setOnSignal(pnSignalResult -> signalReceived.set(true)); + subscription.setOnMessage((OnMessageHandler) message -> messageReceived.set(true)); + subscription.setOnSignal((OnSignalHandler) pnSignalResult -> signalReceived.set(true)); subscription.subscribe(); Thread.sleep(1000); @@ -264,16 +267,12 @@ public void testReceiveMessage() { final String expectedChannel = randomChannel(); final JsonObject messagePayload = generateMessage(pubNub); - final PubNub observer = getPubNub(); + final PubNubForJava observer = getPubNub(); this.pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - - } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { if (status.getCategory() == PNStatusCategory.PNConnectedCategory) { if (status.getAffectedChannels().contains(expectedChannel)) { observer.publish() @@ -285,42 +284,13 @@ public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult message) { assertEquals(expectedChannel, message.getChannel()); assertEquals(observer.getConfiguration().getUserId().getValue(), message.getPublisher()); assertEquals(messagePayload, message.getMessage()); success.set(true); } - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { - - } - - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull PubNub pubNub, @NotNull PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } }); subscribeToChannel(pubNub, expectedChannel); @@ -334,17 +304,12 @@ public void testReceiveUnencryptedMessageWithCryptoDoesntCrash() { final String expectedChannel = randomChannel(); final JsonObject messagePayload = generateMessage(pubNub); - final PubNub sender = getPubNub(); - final PubNub observer = getPubNub(builder -> builder.cryptoModule(CryptoModule.createAesCbcCryptoModule("test", false))); + final PubNubForJava sender = getPubNub(); + final PubNubForJava observer = getPubNub(builder -> builder.cryptoModule(CryptoModule.createAesCbcCryptoModule("test", false))); observer.addListener(new SubscribeCallback() { @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - - } - - @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { if (status.getCategory() == PNStatusCategory.PNConnectedCategory) { if (status.getAffectedChannels().contains(expectedChannel)) { // send an unencrypted message first to try to crash the SubscribeMessageProcessor @@ -365,7 +330,7 @@ public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult message) { if (success.get() == 0) { assertEquals(expectedChannel, message.getChannel()); assertEquals(sender.getConfiguration().getUserId().getValue(), message.getPublisher()); @@ -381,35 +346,6 @@ public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { } } - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { - - } - - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull PubNub pubNub, @NotNull PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } }); subscribeToChannel(observer, expectedChannel); @@ -477,18 +413,15 @@ public void testOrgJsonObject_Get_Receive() throws PubNubException { final AtomicBoolean success = new AtomicBoolean(); pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus pnStatus) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus pnStatus) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult pnMessageResult) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult pnMessageResult) { final JsonElement receivedMessage = pnMessageResult.getMessage(); try { final JSONObject receivedObject = new JSONObject(receivedMessage.toString()); @@ -498,37 +431,6 @@ public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult pnMessageRe e.printStackTrace(); } } - - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult pnPresenceEventResult) { - - } - - @Override - public void signal(@NotNull PubNub pubnub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - - @Override - public void membership(@NotNull PubNub pubnub, @NotNull PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnMessageActionResult) { - - } }); pubNub.subscribe() @@ -552,18 +454,15 @@ public void testOrgJsonObject_Post_Receive() throws PubNubException { final AtomicBoolean success = new AtomicBoolean(); pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus pnStatus) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus pnStatus) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult pnMessageResult) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult pnMessageResult) { final JsonElement receivedMessage = pnMessageResult.getMessage(); try { final JSONObject receivedObject = new JSONObject(receivedMessage.toString()); @@ -573,37 +472,6 @@ public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult pnMessageRe e.printStackTrace(); } } - - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult pnPresenceEventResult) { - - } - - @Override - public void signal(@NotNull PubNub pubnub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - - @Override - public void membership(@NotNull PubNub pubnub, @NotNull PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnMessageActionResult) { - - } }); pubNub.subscribe() @@ -691,18 +559,15 @@ public void testOrgJsonArray_Get_Receive() throws PubNubException { final AtomicBoolean success = new AtomicBoolean(); pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus pnStatus) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus pnStatus) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult pnMessageResult) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult pnMessageResult) { final JsonElement receivedMessage = pnMessageResult.getMessage(); try { final JSONArray receivedArray = new JSONArray(receivedMessage.toString()); @@ -712,37 +577,6 @@ public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult pnMessageRe e.printStackTrace(); } } - - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult pnPresenceEventResult) { - - } - - @Override - public void signal(@NotNull PubNub pubnub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - - @Override - public void membership(@NotNull PubNub pubnub, @NotNull PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnMessageActionResult) { - - } }); pubNub.subscribe() @@ -771,18 +605,15 @@ public void testOrgJsonArray_Post_Receive() throws PubNubException { final AtomicBoolean success = new AtomicBoolean(); pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus pnStatus) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus pnStatus) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult pnMessageResult) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult pnMessageResult) { final JsonElement receivedMessage = pnMessageResult.getMessage(); try { final JSONArray receivedArray = new JSONArray(receivedMessage.toString()); @@ -792,36 +623,6 @@ public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult pnMessageRe e.printStackTrace(); } } - - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult pnPresenceEventResult) { - - } - - @Override - public void signal(@NotNull PubNub pubnub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull final PubNub pubnub, @NotNull final PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnMessageActionResult) { - - } }); pubNub.subscribe() @@ -860,18 +661,15 @@ public void testOrgJson_Combo() throws PubNubException, JSONException { final AtomicInteger count = new AtomicInteger(); pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus pnStatus) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus pnStatus) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult pnMessageResult) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult pnMessageResult) { final JsonElement receivedMessage = pnMessageResult.getMessage(); try { final JSONObject receivedObject = new JSONObject(receivedMessage.toString()); @@ -881,36 +679,6 @@ public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult pnMessageRe e.printStackTrace(); } } - - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult pnPresenceEventResult) { - - } - - @Override - public void signal(@NotNull PubNub pubnub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull PubNub pubnub, @NotNull PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnMessageActionResult) { - - } }); pubNub.subscribe() diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/RetryConfigurationIntegrationTest.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/RetryConfigurationIntegrationTest.java index ed4fbd1e4..605f125b9 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/RetryConfigurationIntegrationTest.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/RetryConfigurationIntegrationTest.java @@ -1,13 +1,13 @@ package com.pubnub.api.integration; -import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; import com.pubnub.api.enums.PNStatusCategory; import com.pubnub.api.integration.util.BaseIntegrationTest; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.v2.callbacks.StatusListener; import com.pubnub.api.models.consumer.PNStatus; import com.pubnub.api.retry.RetryConfiguration; import com.pubnub.api.retry.RetryableEndpointGroup; -import com.pubnub.api.v2.callbacks.StatusListener; import org.jetbrains.annotations.NotNull; import org.junit.Test; @@ -32,7 +32,7 @@ public void whenRetryConfigurationIsDefinedShouldGetProperStatus() throws Interr pubNub.addListener(new StatusListener() { @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus pnStatus) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus pnStatus) { assertTrue(pnStatus.getCategory() == PNStatusCategory.PNConnectionError); success.set(true); } diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/SignalIntegrationTests.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/SignalIntegrationTests.java index 466943224..c7a1386e8 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/SignalIntegrationTests.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/SignalIntegrationTests.java @@ -1,20 +1,20 @@ package com.pubnub.api.integration; import com.google.gson.Gson; -import com.pubnub.api.PNConfiguration; import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; import com.pubnub.api.UserId; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.callbacks.SubscribeCallback; import com.pubnub.api.enums.PNStatusCategory; import com.pubnub.api.integration.util.BaseIntegrationTest; import com.pubnub.api.integration.util.RandomGenerator; import com.pubnub.api.models.consumer.PNPublishResult; import com.pubnub.api.models.consumer.PNStatus; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult; import com.pubnub.api.models.consumer.pubsub.PNMessageResult; import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; import com.pubnub.api.models.consumer.pubsub.PNSignalResult; @@ -77,16 +77,13 @@ public void testReceiveSignalMessage() { final String expectedChannel = randomChannel(); final String expectedPayload = RandomGenerator.newValue(5); - final PubNub observerClient = getPubNub(); + final PubNubForJava observerClient = getPubNub(); observerClient.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { if (status.getCategory() == PNStatusCategory.PNConnectedCategory) { if (status.getAffectedChannels().contains(expectedChannel)) { pubNub.signal() @@ -101,42 +98,12 @@ public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { - - } - - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { - - } - - @Override - public void signal(@NotNull PubNub pubnub, @NotNull PNSignalResult signal) { + public void signal(@NotNull PubNubForJava pubnub, @NotNull PNSignalResult signal) { assertEquals(pubNub.getConfiguration().getUserId().getValue(), signal.getPublisher()); assertEquals(expectedChannel, signal.getChannel()); assertEquals(expectedPayload, new Gson().fromJson(signal.getMessage(), String.class)); success.set(true); } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull final PubNub pubnub, @NotNull final PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } }); observerClient.subscribe() @@ -174,11 +141,9 @@ public void testPublishSignalMessageSyncWithoutMessage() { public void testPublishSignalMessageSyncWithoutSubKey() { try { // pubNub.getConfiguration().setSubscribeKey(null); - PubNub pubNub1 = PubNub.create(new PNConfiguration(new UserId(random()))); + PubNub pubNub1 = PubNub.create(com.pubnub.api.java.v2.PNConfiguration.builder(new UserId(random()), "").build()); - pubNub1.signal() - .channel(randomChannel()) - .message(random()) + pubNub1.signal(randomChannel(), random()) .sync(); } catch (PubNubException e) { assertEquals(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING.getMessage(), e.getPubnubError() diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/StreamFilteringIntegrationTests.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/StreamFilteringIntegrationTests.java index 69d219f29..00d8636cd 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/StreamFilteringIntegrationTests.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/StreamFilteringIntegrationTests.java @@ -1,12 +1,13 @@ package com.pubnub.api.integration; import com.pubnub.api.PubNub; -import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.callbacks.SubscribeCallback; import com.pubnub.api.integration.util.BaseIntegrationTest; import com.pubnub.api.models.consumer.PNStatus; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult; import com.pubnub.api.models.consumer.pubsub.PNMessageResult; import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; import com.pubnub.api.models.consumer.pubsub.PNSignalResult; @@ -67,50 +68,15 @@ public void testSubscribeWithLanguageFiltering() { pubNub.addListener(new SubscribeCallback() { @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { - - } - - @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult message) { assertTrue(message.getMessage().toString().contains(messageEnglish)); success.set(true); } - - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { - - } - - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull final PubNub pubnub, @NotNull final PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } }); publishMessage(pubNub, channel, messageFrench, metaFrench); @@ -129,52 +95,20 @@ public void testSubscribeWithMultipleLanguageFiltering() { subscribeToChannel(pubNub, channel); pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult message) { success.incrementAndGet(); assertFalse(message.getMessage().toString().contains("italian")); assertFalse(message.getMessage().toString().contains("spanish")); } - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { - - } - - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull PubNub pubNub, @NotNull PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } }); publishMessage(pubNub, channel, messageFrench, metaFrench); @@ -196,51 +130,18 @@ public void testSubscribeWithLanguageNegationFiltering() { subscribeToChannel(pubNub, channel); pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - - } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult message) { success.incrementAndGet(); assertFalse(message.getMessage().toString().contains("english")); } - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { - - } - - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull PubNub pubNub, @NotNull PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } }); publishMessage(pubNub, channel, messageFrench, metaFrench); @@ -262,52 +163,19 @@ public void testSubscribeWithGreaterThanFiltering() { subscribeToChannel(pubNub, channel); pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - - } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult message) { success.incrementAndGet(); assertFalse(message.getMessage().toString().contains("25")); assertFalse(message.getMessage().toString().contains("35")); } - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { - - } - - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull PubNub pubNub, @NotNull PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } }); final String messageTemp25 = "This is just message for today temperature : 25"; @@ -335,18 +203,15 @@ public void testSubscribeWithLikeFiltering() { final Map metaMessage_2_Part = getMetaLikeFilter(messageSuccess); pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult message) { boolean correctMessage = false; if (message.getMessage().toString().contains("success")) { correctMessage = true; @@ -355,35 +220,6 @@ public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { success.set(true); } - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { - - } - - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull PubNub pubNub, @NotNull PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } }); publishMessage(pubNub, channel, messageBoring, metaMessage_1_Part); diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/SubscribeIntegrationTests.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/SubscribeIntegrationTests.java index f3cdbe382..f2cfaf9e3 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/SubscribeIntegrationTests.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/SubscribeIntegrationTests.java @@ -3,28 +3,35 @@ import com.google.gson.JsonObject; import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.callbacks.SubscribeCallback; import com.pubnub.api.integration.util.BaseIntegrationTest; import com.pubnub.api.integration.util.RandomGenerator; +import com.pubnub.api.java.v2.callbacks.handlers.OnFileHandler; +import com.pubnub.api.java.v2.callbacks.handlers.OnMessageActionHandler; +import com.pubnub.api.java.v2.callbacks.handlers.OnPresenceHandler; +import com.pubnub.api.java.v2.callbacks.handlers.OnSignalHandler; import com.pubnub.api.models.consumer.PNPublishResult; import com.pubnub.api.models.consumer.PNStatus; import com.pubnub.api.models.consumer.message_actions.PNMessageAction; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult; import com.pubnub.api.models.consumer.pubsub.PNMessageResult; import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; import com.pubnub.api.models.consumer.pubsub.PNSignalResult; import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult; import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult; -import com.pubnub.api.v2.callbacks.handlers.OnMessageHandler; -import com.pubnub.api.v2.entities.Channel; -import com.pubnub.api.v2.entities.ChannelMetadata; -import com.pubnub.api.v2.entities.UserMetadata; -import com.pubnub.api.v2.subscriptions.Subscription; +import com.pubnub.api.java.v2.callbacks.handlers.OnMessageHandler; +import com.pubnub.api.java.v2.entities.Channel; +import com.pubnub.api.java.v2.entities.ChannelMetadata; +import com.pubnub.api.java.v2.entities.UserMetadata; +import com.pubnub.api.java.v2.subscriptions.Subscription; import com.pubnub.api.v2.subscriptions.SubscriptionOptions; -import com.pubnub.api.v2.subscriptions.SubscriptionSet; +import com.pubnub.api.java.v2.subscriptions.SubscriptionSet; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.NotNull; import org.junit.Test; @@ -44,7 +51,7 @@ public class SubscribeIntegrationTests extends BaseIntegrationTest { - private PubNub mGuestClient; + private PubNubForJava mGuestClient; @Override protected void onBefore() { @@ -100,50 +107,16 @@ public void testWildcardSubscribe() { pubNub.addListener(new SubscribeCallback() { @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { - - } - - @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult message) { assertTrue(message.getMessage().toString().contains("Cool message")); success.set(true); } - - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { - - } - - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull final PubNub pubnub, @NotNull final PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } + }); publishMessage(mGuestClient, "my.test", "Cool message!"); @@ -160,13 +133,10 @@ public void testUnsubscribeFromChannel() { subscribeToChannel(pubNub, expectedChannel); pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - } @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { boolean channelSubscribed = false; for (int i = 0; i < pubnub.getSubscribedChannels().size(); i++) { if (pubnub.getSubscribedChannels().get(i).contains(expectedChannel)) { @@ -180,39 +150,14 @@ public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { } @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { + public void message(@NotNull PubNubForJava pubnub, @NotNull PNMessageResult message) { success.set(true); } @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { + public void presence(@NotNull PubNubForJava pubnub, @NotNull PNPresenceEventResult presence) { success.set(true); } - - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull final PubNub pubnub, @NotNull final PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } }); unsubscribeFromChannel(pubNub, expectedChannel); @@ -227,7 +172,7 @@ public void testUnsubscribeFromAllChannels() { pubNub.addListener(new SubscribeCallback.BaseSubscribeCallback() { @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { assertEquals(0, pubNub.getSubscribedChannels().size()); success.set(true); } @@ -247,7 +192,7 @@ public void testAssignBehaviourForChannelGroup() throws InterruptedException, Pu pubNub.addChannelsToChannelGroup().channelGroup(channelGroupName).channels(Arrays.asList(channel01, channel02)).sync(); Subscription channelGroupSubscription = pubNub.channelGroup(channelGroupName).subscription(); - channelGroupSubscription.setOnMessage(pnMessageResult -> numberOfReceivedMessages.incrementAndGet()); + channelGroupSubscription.setOnMessage((OnMessageHandler) pnMessageResult -> numberOfReceivedMessages.incrementAndGet()); channelGroupSubscription.subscribe(); Thread.sleep(2000); @@ -256,7 +201,7 @@ public void testAssignBehaviourForChannelGroup() throws InterruptedException, Pu Thread.sleep(1000); assertEquals(1, numberOfReceivedMessages.get()); - channelGroupSubscription.setOnMessage(null); + channelGroupSubscription.setOnMessage((OnMessageHandler) null); pubNub.publish().message(expectedMessage).channel(channel01).sync(); Thread.sleep(1000); @@ -365,7 +310,7 @@ public void testAssigningEventBehaviourToSubscription() throws InterruptedExcept System.out.println("-=pnMessageResult: " + pnMessageResult.getMessage()); }; - subscription.setOnMessage(pnMessageResult -> System.out.println("Received message: " + pnMessageResult.getMessage())); + subscription.setOnMessage((OnMessageHandler) pnMessageResult -> System.out.println("Received message: " + pnMessageResult.getMessage())); subscription.setOnMessage(onMessageHandler); subscription.setOnPresence(pnPresenceEventResult -> { @@ -373,11 +318,11 @@ public void testAssigningEventBehaviourToSubscription() throws InterruptedExcept System.out.println("-=pnPresenceEventResult: " + pnPresenceEventResult.getEvent()); }); - subscription.setOnSignal(pnSignalResult -> numberOfReceivedSignal.incrementAndGet()); - subscription.setOnMessageAction(pnMessageActionResult -> numberOfReceivedMessageAction.incrementAndGet()); + subscription.setOnSignal((OnSignalHandler) pnSignalResult -> numberOfReceivedSignal.incrementAndGet()); + subscription.setOnMessageAction((OnMessageActionHandler) pnMessageActionResult -> numberOfReceivedMessageAction.incrementAndGet()); subscription.setOnChannelMetadata(pnChannelMetadataResult -> numberOfReceivedChannelMetadataEvents.incrementAndGet()); subscription.setOnMembership(pnMembershipResult -> numberOfReceivedMembershipEvent.incrementAndGet()); - subscription.setOnFile(pnFileEventResult -> numberOfReceivedFileMessages.incrementAndGet()); + subscription.setOnFile((OnFileHandler) pnFileEventResult -> numberOfReceivedFileMessages.incrementAndGet()); subscription.subscribe(); Thread.sleep(2000); @@ -399,13 +344,13 @@ public void testAssigningEventBehaviourToSubscription() throws InterruptedExcept assertEquals(1, numberOfReceivedMembershipEvent.get()); assertEquals(1, numberOfReceivedFileMessages.get()); - subscription.setOnMessage(null); - subscription.setOnPresence(null); - subscription.setOnSignal(null); - subscription.setOnMessageAction(null); + subscription.setOnMessage((OnMessageHandler) null); + subscription.setOnPresence((OnPresenceHandler) null); + subscription.setOnSignal((OnSignalHandler) null); + subscription.setOnMessageAction((OnMessageActionHandler) null); subscription.setOnChannelMetadata(null); subscription.setOnMembership(null); - subscription.setOnFile(null); + subscription.setOnFile((Function1) null); PNPublishResult pnPublishResult02 = pubNub.publish().message(expectedMessage).channel(chan01.getName()).sync(); pubNub.setPresenceState().state(expectedStatePayload).channels(Collections.singletonList(chan01.getName())).sync(); @@ -441,10 +386,10 @@ public void testAssigningEventBehaviourToSubscriptionSet() throws InterruptedExc Subscription subscription02 = channel02.subscription(); SubscriptionSet subscriptionSet = subscription01.plus(subscription02); - subscriptionSet.setOnMessage(pnMessageResult -> numberOfMessagesReceived.incrementAndGet()); - subscriptionSet.setOnSignal(pnSignalResult -> numberOfSignalsReceived.incrementAndGet()); - subscriptionSet.setOnPresence(pnPresenceEventResult -> numberOfPresenceEventsReceived.incrementAndGet()); - subscriptionSet.setOnMessageAction(pnMessageActionResult -> numberOfMessageActionsReceived.incrementAndGet()); + subscriptionSet.setOnMessage((OnMessageHandler) pnMessageResult -> numberOfMessagesReceived.incrementAndGet()); + subscriptionSet.setOnSignal((OnSignalHandler) pnSignalResult -> numberOfSignalsReceived.incrementAndGet()); + subscriptionSet.setOnPresence((OnPresenceHandler) pnPresenceEventResult -> numberOfPresenceEventsReceived.incrementAndGet()); + subscriptionSet.setOnMessageAction((OnMessageActionHandler) pnMessageActionResult -> numberOfMessageActionsReceived.incrementAndGet()); subscriptionSet.subscribe(); Thread.sleep(2000); @@ -464,10 +409,10 @@ public void testAssigningEventBehaviourToSubscriptionSet() throws InterruptedExc assertEquals(1, numberOfPresenceEventsReceived.get()); // first presence event is join generated automatically assertEquals(2, numberOfMessageActionsReceived.get()); - subscriptionSet.setOnMessage(null); - subscriptionSet.setOnSignal(null); - subscriptionSet.setOnPresence(null); - subscriptionSet.setOnMessageAction(null); + subscriptionSet.setOnMessage((OnMessageHandler) null); + subscriptionSet.setOnSignal((OnSignalHandler) null); + subscriptionSet.setOnPresence((OnPresenceHandler) null); + subscriptionSet.setOnMessageAction((OnMessageActionHandler) null); pubNub.publish().channel(channel01.getName()).message("anything").sync(); pubNub.publish().channel(channel02.getName()).message("anything").sync(); diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/AbstractReconnectionProblemIT.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/AbstractReconnectionProblemIT.java index ccc2f5792..9c123c009 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/AbstractReconnectionProblemIT.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/AbstractReconnectionProblemIT.java @@ -1,11 +1,13 @@ package com.pubnub.api.integration.managers.subscription; -import com.pubnub.api.PNConfiguration; import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; import com.pubnub.api.UserId; import com.pubnub.api.enums.PNStatusCategory; import com.pubnub.api.integration.util.ITTestConfig; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.callbacks.SubscribeCallback; +import com.pubnub.api.java.v2.PNConfiguration; import com.pubnub.api.models.consumer.PNStatus; import org.aeonbits.owner.ConfigFactory; import org.jetbrains.annotations.NotNull; @@ -62,7 +64,7 @@ public abstract class AbstractReconnectionProblemIT { protected static final int SUBSCRIBE_TIMEOUT = 5; protected final List collectedStatuses = Collections.synchronizedList(new ArrayList<>()); - protected PubNub pn; + protected PubNubForJava pn; protected String authKey = randomId(); @@ -71,7 +73,7 @@ private static String randomId() { } private void grantAccess(final String... protectedChannelNames) throws PubNubException { - final PubNub pnAdmin = adminPubNub(); + final PubNubForJava pnAdmin = adminPubNub(); pnAdmin.grant() .authKeys(singletonList(authKey)) .channels(asList(protectedChannelNames)) @@ -80,7 +82,7 @@ private void grantAccess(final String... protectedChannelNames) throws PubNubExc } private void grantAccessToChannelGroup(final String... protectedChannelGroupNames) throws PubNubException { - final PubNub pnAdmin = adminPubNub(); + final PubNubForJava pnAdmin = adminPubNub(); pnAdmin.grant() .authKeys(singletonList(authKey)) .channelGroups(asList(protectedChannelGroupNames)) @@ -99,22 +101,22 @@ public void disconnectClients() { pn = null; } - protected void subscribe(final PubNub pnClient, final boolean reportCallStack, final String... channels) { + protected void subscribe(final PubNubForJava pnClient, final boolean reportCallStack, final String... channels) { subscribe(pnClient, reportCallStack, null, channels); } - protected void subscribe(final PubNub pnClient, final String... channelNames) { + protected void subscribe(final PubNubForJava pnClient, final String... channelNames) { subscribe(pnClient, false, null, channelNames); } - protected void subscribe(final PubNub pnClient, final BiConsumer block, final String... channels) { + protected void subscribe(final PubNubForJava pnClient, final BiConsumer block, final String... channels) { subscribe(pnClient, false, block, channels); } - protected void subscribe(final PubNub pnClient, boolean reportCallStack, final BiConsumer block, final String... channels) { - pnClient.addListener(new SubscribeCallbackAdapter() { + protected void subscribe(final PubNubForJava pnClient, boolean reportCallStack, final BiConsumer block, final String... channels) { + pnClient.addListener(new SubscribeCallback() { @Override - public void status(final PubNub pubnub, final PNStatus pnStatus) { + public void status(final PubNubForJava pubnub, final PNStatus pnStatus) { final Exception exception = new Exception(); synchronized (collectedStatuses) { collectedStatuses.add(new CollectedStatus(pnStatus, exception)); @@ -127,7 +129,7 @@ public void status(final PubNub pubnub, final PNStatus pnStatus) { exception.printStackTrace(System.out); } if (block != null) { - block.accept(pubnub, pnStatus); + block.accept(pnClient, pnStatus); } } }); @@ -135,22 +137,22 @@ public void status(final PubNub pubnub, final PNStatus pnStatus) { pnClient.subscribe().channels(asList(channels)).execute(); } - protected void subscribeToGroup(final PubNub pnClient, final boolean reportCallStack, final String... channelGroups) { + protected void subscribeToGroup(final PubNubForJava pnClient, final boolean reportCallStack, final String... channelGroups) { subscribeToGroup(pnClient, reportCallStack, null, channelGroups); } - protected void subscribeToGroup(final PubNub pnClient, final String... channelGroups) { + protected void subscribeToGroup(final PubNubForJava pnClient, final String... channelGroups) { subscribeToGroup(pnClient, false, null, channelGroups); } - protected void subscribeToGroup(final PubNub pnClient, final BiConsumer block, final String... channelGroups) { + protected void subscribeToGroup(final PubNubForJava pnClient, final BiConsumer block, final String... channelGroups) { subscribeToGroup(pnClient, false, block, channelGroups); } - protected void subscribeToGroup(final PubNub pnClient, boolean reportCallStack, final BiConsumer block, final String... channelGroups) { - pnClient.addListener(new SubscribeCallbackAdapter() { + protected void subscribeToGroup(final PubNubForJava pnClient, boolean reportCallStack, final BiConsumer block, final String... channelGroups) { + pnClient.addListener(new SubscribeCallback() { @Override - public void status(final PubNub pubnub, final PNStatus pnStatus) { + public void status(final PubNubForJava pubnub, final PNStatus pnStatus) { final Exception exception = new Exception(); synchronized (collectedStatuses) { collectedStatuses.add(new CollectedStatus(pnStatus, exception)); @@ -164,7 +166,7 @@ public void status(final PubNub pubnub, final PNStatus pnStatus) { exception.printStackTrace(System.out); } if (block != null) { - block.accept(pubnub, pnStatus); + block.accept(pnClient, pnStatus); } } }); @@ -175,31 +177,31 @@ public void status(final PubNub pubnub, final PNStatus pnStatus) { } - protected void createChannelGroup(final PubNub pnClient, final String channelGroup, final String... channelNames) throws PubNubException { + protected void createChannelGroup(final PubNubForJava pnClient, final String channelGroup, final String... channelNames) throws PubNubException { pnClient.addChannelsToChannelGroup() .channelGroup(channelGroup) .channels(asList(channelNames)) .sync(); } - protected abstract PubNub privilegedClientPubNub(); + protected abstract @NotNull PubNubForJava privilegedClientPubNub(); - PNConfiguration getPNConfiguration() { - PNConfiguration pnConfiguration = null; + PNConfiguration.Builder getPNConfiguration() { + PNConfiguration.Builder pnConfiguration = null; try { - pnConfiguration = new PNConfiguration(new UserId("pn-" + UUID.randomUUID())); + pnConfiguration = PNConfiguration.builder(new UserId("pn-" + UUID.randomUUID()), ""); } catch (PubNubException e) { throw new RuntimeException(e); } return pnConfiguration; } - private PubNub adminPubNub() { - PNConfiguration pnConfiguration = getPNConfiguration(); - pnConfiguration.setSubscribeKey(itPamTestConfig.pamSubKey()); - pnConfiguration.setPublishKey(itPamTestConfig.pamPubKey()); - pnConfiguration.setSecretKey(itPamTestConfig.pamSecKey()); - return PubNub.create(pnConfiguration); + private PubNubForJava adminPubNub() { + PNConfiguration.Builder pnConfiguration = getPNConfiguration(); + pnConfiguration.subscribeKey(itPamTestConfig.pamSubKey()); + pnConfiguration.publishKey(itPamTestConfig.pamPubKey()); + pnConfiguration.secretKey(itPamTestConfig.pamSecKey()); + return PubNubForJava.create(pnConfiguration.build()); } @Test @@ -248,9 +250,9 @@ public void continueSubscriptionAfterUnsubscribeFromForbiddenChannel() throws In grantAccess(channel1); - subscribe(pn, true, new BiConsumer() { + subscribe(pn, true, new BiConsumer() { @Override - public void accept(final PubNub pubNub, final PNStatus status) { + public void accept(final PubNubForJava pubNub, final PNStatus status) { if (status.getCategory() == PNStatusCategory.PNConnectionError) { if (status.getException().getStatusCode() == 403) { final List channelsToUnsubscribe = status.getException().getAffectedChannels(); @@ -293,19 +295,16 @@ public void continueSubscriptionToChannelGroupAfterUnsubscribeFromForbiddenChann grantAccessToChannelGroup(channelGroup1); - subscribeToGroup(pn, true, new BiConsumer() { - @Override - public void accept(final PubNub pubNub, final PNStatus status) { - if (status.getCategory() == PNStatusCategory.PNConnectionError) { - if (status.getException().getStatusCode() == 403) { - final List channelGroupsToUnsubscribe = status.getException().getAffectedChannelGroups(); + subscribeToGroup(pn, true, (pubNub, status) -> { + if (status.getCategory() == PNStatusCategory.PNConnectionError) { + if (status.getException().getStatusCode() == 403) { + final List channelGroupsToUnsubscribe = status.getException().getAffectedChannelGroups(); - try { - System.out.println("Unsubscribing from groups: " + channelGroupsToUnsubscribe); - pubNub.unsubscribe().channelGroups(channelGroupsToUnsubscribe).execute(); - } catch (Exception e) { - e.printStackTrace(); - } + try { + System.out.println("Unsubscribing from groups: " + channelGroupsToUnsubscribe); + pn.unsubscribe().channelGroups(channelGroupsToUnsubscribe).execute(); + } catch (Exception e) { + e.printStackTrace(); } } } @@ -334,13 +333,10 @@ public void accept(final PubNub pubNub, final PNStatus status) { public void stopSubscriptionWhenRequestedToDisconnectOnAccessDenied() throws InterruptedException { final String channel = "ch-" + randomId(); - subscribe(pn, true, new BiConsumer() { - @Override - public void accept(final PubNub pubNub, final PNStatus status) { - if (status.getCategory() == PNStatusCategory.PNConnectionError) { - if (status.getException().getStatusCode() == 403) { - pn.disconnect(); - } + subscribe(pn, true, (pubNub, status) -> { + if (status.getCategory() == PNStatusCategory.PNConnectionError) { + if (status.getException().getStatusCode() == 403) { + pn.disconnect(); } } }, channel); @@ -361,13 +357,10 @@ public void accept(final PubNub pubNub, final PNStatus status) { public void stopSubscriptionToChannelGroupWhenRequestedToDisconnectOnAccessDenied() throws InterruptedException { final String channelGroup = "chg-" + randomId(); - subscribeToGroup(pn, true, new BiConsumer() { - @Override - public void accept(final PubNub pubNub, final PNStatus status) { - if (status.getCategory() == PNStatusCategory.PNConnectionError) { - if (status.getException().getStatusCode() == 403) { - pn.disconnect(); - } + subscribeToGroup(pn, true, (BiConsumer) (pubNub, status) -> { + if (status.getCategory() == PNStatusCategory.PNConnectionError) { + if (status.getException().getStatusCode() == 403) { + pn.disconnect(); } } }, channelGroup); @@ -389,9 +382,9 @@ public void accept(final PubNub pubNub, final PNStatus status) { public void stopSubscriptionWhenRequestedToForceDestroyOnAccessDenied() throws InterruptedException { final String channel = "ch-" + randomId(); - subscribe(pn, true, new BiConsumer() { + subscribe(pn, true, new BiConsumer() { @Override - public void accept(final PubNub pubNub, final PNStatus status) { + public void accept(final PubNubForJava pubNub, final PNStatus status) { if (status.getCategory() == PNStatusCategory.PNConnectionError) { if (status.getException().getStatusCode() == 403) { pn.forceDestroy(); @@ -416,9 +409,9 @@ public void accept(final PubNub pubNub, final PNStatus status) { public void stopSubscriptionToChannelGroupWhenRequestedToForceDestroyOnAccessDenied() throws InterruptedException { final String channelGroup = "chg-" + randomId(); - subscribeToGroup(pn, true, new BiConsumer() { + subscribeToGroup(pn, true, new BiConsumer() { @Override - public void accept(final PubNub pubNub, final PNStatus status) { + public void accept(final PubNubForJava pubNub, final PNStatus status) { if (status.getCategory() == PNStatusCategory.PNConnectionError) { if (status.getException().getStatusCode() == 403) { pn.forceDestroy(); diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/ReconnectionProblemWithReconnectionPolicyIT.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/ReconnectionProblemWithReconnectionPolicyIT.java index 7e00430b5..b599ee92e 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/ReconnectionProblemWithReconnectionPolicyIT.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/ReconnectionProblemWithReconnectionPolicyIT.java @@ -1,23 +1,22 @@ package com.pubnub.api.integration.managers.subscription; -import com.pubnub.api.PNConfiguration; -import com.pubnub.api.PubNub; import com.pubnub.api.enums.PNLogVerbosity; - -import static com.pubnub.api.enums.PNReconnectionPolicy.LINEAR; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.v2.PNConfiguration; +import com.pubnub.api.retry.RetryConfiguration; +import org.jetbrains.annotations.NotNull; public class ReconnectionProblemWithReconnectionPolicyIT extends AbstractReconnectionProblemIT { @Override - protected PubNub privilegedClientPubNub() { - PNConfiguration pnConfiguration = getPNConfiguration(); - pnConfiguration.setSubscribeKey(itPamTestConfig.pamSubKey()); - pnConfiguration.setPublishKey(itPamTestConfig.pamPubKey()); - pnConfiguration.setSubscribeTimeout(5); - pnConfiguration.setLogVerbosity(PNLogVerbosity.BODY); - pnConfiguration.setReconnectionPolicy(LINEAR); - pnConfiguration.setMaximumReconnectionRetries(1); - pnConfiguration.setAuthKey(authKey); - return PubNub.create(pnConfiguration); + protected @NotNull PubNubForJava privilegedClientPubNub() { + PNConfiguration.Builder pnConfiguration = getPNConfiguration(); + pnConfiguration.subscribeKey(itPamTestConfig.pamSubKey()); + pnConfiguration.publishKey(itPamTestConfig.pamPubKey()); + pnConfiguration.subscribeTimeout(5); + pnConfiguration.logVerbosity(PNLogVerbosity.BODY); + pnConfiguration.retryConfiguration(new RetryConfiguration.Linear(2, 1)); + pnConfiguration.authKey(authKey); + return PubNubForJava.create(pnConfiguration.build()); } } diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/ReconnectionProblemWithoutReconnectionPolicyIT.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/ReconnectionProblemWithoutReconnectionPolicyIT.java index 7f9e45ab5..d00895d06 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/ReconnectionProblemWithoutReconnectionPolicyIT.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/ReconnectionProblemWithoutReconnectionPolicyIT.java @@ -1,31 +1,31 @@ package com.pubnub.api.integration.managers.subscription; -import com.pubnub.api.PNConfiguration; -import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; import com.pubnub.api.UserId; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.retry.RetryConfiguration; +import org.jetbrains.annotations.NotNull; import java.util.UUID; import static com.pubnub.api.enums.PNLogVerbosity.BODY; -import static com.pubnub.api.enums.PNReconnectionPolicy.NONE; public class ReconnectionProblemWithoutReconnectionPolicyIT extends AbstractReconnectionProblemIT { @Override - protected PubNub privilegedClientPubNub() { - PNConfiguration pnConfiguration; + protected @NotNull PubNubForJava privilegedClientPubNub() { + com.pubnub.api.java.v2.PNConfiguration.Builder pnConfiguration; try { - pnConfiguration = new PNConfiguration(new UserId("pn-" + UUID.randomUUID())); + pnConfiguration = com.pubnub.api.java.v2.PNConfiguration.builder(new UserId("pn-" + UUID.randomUUID()), ""); } catch (PubNubException e) { throw new RuntimeException(e); } - pnConfiguration.setSubscribeKey(itPamTestConfig.pamSubKey()); - pnConfiguration.setPublishKey(itPamTestConfig.pamPubKey()); - pnConfiguration.setSubscribeTimeout(SUBSCRIBE_TIMEOUT); - pnConfiguration.setLogVerbosity(BODY); - pnConfiguration.setAuthKey(authKey); - pnConfiguration.setReconnectionPolicy(NONE); - return PubNub.create(pnConfiguration); + pnConfiguration.subscribeKey(itPamTestConfig.pamSubKey()); + pnConfiguration.publishKey(itPamTestConfig.pamPubKey()); + pnConfiguration.subscribeTimeout(SUBSCRIBE_TIMEOUT); + pnConfiguration.logVerbosity(BODY); + pnConfiguration.authKey(authKey); + pnConfiguration.retryConfiguration(RetryConfiguration.None.INSTANCE); + return PubNubForJava.create(pnConfiguration.build()); } } diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/SubscribeCallbackAdapter.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/SubscribeCallbackAdapter.java deleted file mode 100644 index 3a07f40f2..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/managers/subscription/SubscribeCallbackAdapter.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.pubnub.api.integration.managers.subscription; - -import com.pubnub.api.PubNub; -import com.pubnub.api.callbacks.SubscribeCallback; -import com.pubnub.api.models.consumer.PNStatus; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult; -import com.pubnub.api.models.consumer.pubsub.PNMessageResult; -import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; -import com.pubnub.api.models.consumer.pubsub.PNSignalResult; -import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult; -import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult; - -public class SubscribeCallbackAdapter extends SubscribeCallback { - - @Override - public void status(final PubNub pubnub, final PNStatus pnStatus) { - - } - - @Override - public void message(final PubNub pubnub, final PNMessageResult pnMessageResult) { - - } - - @Override - public void presence(final PubNub pubnub, final PNPresenceEventResult pnPresenceEventResult) { - - } - - @Override - public void signal(final PubNub pubnub, final PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(final PubNub pubnub, final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(final PubNub pubnub, final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(final PubNub pubnub, final PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(final PubNub pubnub, final PNMessageActionResult pnMessageActionResult) { - - } - - @Override - public void file(final PubNub pubnub, final PNFileEventResult pnFileEventResult) { - - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/ObjectsApiBaseIT.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/ObjectsApiBaseIT.java index 02db7adc4..8548cb025 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/ObjectsApiBaseIT.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/ObjectsApiBaseIT.java @@ -1,11 +1,12 @@ package com.pubnub.api.integration.objects; -import com.pubnub.api.PNConfiguration; import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; import com.pubnub.api.UserId; import com.pubnub.api.enums.PNLogVerbosity; import com.pubnub.api.integration.util.ITTestConfig; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.v2.PNConfiguration; import org.aeonbits.owner.ConfigFactory; import org.junit.Before; @@ -19,19 +20,19 @@ public abstract class ObjectsApiBaseIT { //See README.md in integrationTest directory for more info on running integration tests private ITTestConfig itTestConfig = ConfigFactory.create(ITTestConfig.class, System.getenv()); - protected final PubNub pubNubUnderTest = pubNub(); + protected final PubNubForJava pubNubUnderTest = pubNub(); - private PubNub pubNub() { - PNConfiguration pnConfiguration; + private PubNubForJava pubNub() { + PNConfiguration.Builder pnConfiguration; try { - pnConfiguration = new PNConfiguration(new UserId("pn-" + UUID.randomUUID())); + pnConfiguration = PNConfiguration.builder(new UserId("pn-" + UUID.randomUUID()), ""); } catch (PubNubException e) { throw new RuntimeException(e); } - pnConfiguration.setSubscribeKey(itTestConfig.subscribeKey()); - pnConfiguration.setLogVerbosity(PNLogVerbosity.BODY); + pnConfiguration.subscribeKey(itTestConfig.subscribeKey()); + pnConfiguration.logVerbosity(PNLogVerbosity.BODY); - return PubNub.create(pnConfiguration); + return PubNubForJava.create(pnConfiguration.build()); } @Before diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/ObjectsApiSubscriptionIT.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/ObjectsApiSubscriptionIT.java index 17a7854f0..327a608d4 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/ObjectsApiSubscriptionIT.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/ObjectsApiSubscriptionIT.java @@ -1,17 +1,12 @@ package com.pubnub.api.integration.objects; -import com.pubnub.api.PubNub; -import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.callbacks.SubscribeCallback; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult; import com.pubnub.api.models.consumer.PNStatus; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult; -import com.pubnub.api.models.consumer.pubsub.PNMessageResult; -import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; -import com.pubnub.api.models.consumer.pubsub.PNSignalResult; -import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult; -import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult; import org.jetbrains.annotations.NotNull; import org.junit.Test; @@ -34,39 +29,7 @@ public class ObjectsApiSubscriptionIT extends ObjectsApiBaseIT { class TestSubscribeCallbackAdapter extends SubscribeCallback { @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus pnStatus) { - } - - @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult pnMessageResult) { - } - - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult pnPresenceEventResult) { - } - - @Override - public void signal(@NotNull PubNub pubnub, @NotNull PNSignalResult pnSignalResult) { - } - - @Override - public void uuid(@NotNull PubNub pubnub, @NotNull PNUUIDMetadataResult pnUUIDMetadataResult) { - } - - @Override - public void channel(@NotNull PubNub pubnub, @NotNull PNChannelMetadataResult pnChannelMetadataResult) { - } - - @Override - public void membership(@NotNull PubNub pubnub, @NotNull PNMembershipResult pnMembershipResult) { - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnMessageActionResult) { - } - - @Override - public void file(@NotNull final PubNub pubnub, @NotNull final PNFileEventResult pnFileEventResult) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus pnStatus) { } } @@ -79,17 +42,17 @@ public void receivingCallbackObjectsHasBeenSet() throws Exception { pubNubUnderTest.addListener(new TestSubscribeCallbackAdapter() { @Override - public void uuid(@NotNull PubNub pubnub, @NotNull PNUUIDMetadataResult pnUUIDMetadataResult) { + public void uuid(@NotNull PubNubForJava pubnub, @NotNull PNUUIDMetadataResult pnUUIDMetadataResult) { uuidMetadataResultHolder.set(pnUUIDMetadataResult); } @Override - public void channel(@NotNull PubNub pubnub, @NotNull PNChannelMetadataResult pnChannelMetadataResult) { + public void channel(@NotNull PubNubForJava pubnub, @NotNull PNChannelMetadataResult pnChannelMetadataResult) { channelMetadataResultHolder.set(pnChannelMetadataResult); } @Override - public void membership(@NotNull PubNub pubnub, @NotNull PNMembershipResult pnMembershipResult) { + public void membership(@NotNull PubNubForJava pubnub, @NotNull PNMembershipResult pnMembershipResult) { membershipResultHolder.set(pnMembershipResult); } }); @@ -139,17 +102,17 @@ public void receivingCallbackObjectsHasBeenUnset() throws Exception { pubNubUnderTest.addListener(new TestSubscribeCallbackAdapter() { @Override - public void uuid(@NotNull PubNub pubnub, @NotNull PNUUIDMetadataResult pnUUIDMetadataResult) { + public void uuid(@NotNull PubNubForJava pubnub, @NotNull PNUUIDMetadataResult pnUUIDMetadataResult) { uuidMetadataResultHolder.set(pnUUIDMetadataResult); } @Override - public void channel(@NotNull PubNub pubnub, @NotNull PNChannelMetadataResult pnChannelMetadataResult) { + public void channel(@NotNull PubNubForJava pubnub, @NotNull PNChannelMetadataResult pnChannelMetadataResult) { channelMetadataResultHolder.set(pnChannelMetadataResult); } @Override - public void membership(@NotNull PubNub pubnub, @NotNull PNMembershipResult pnMembershipResult) { + public void membership(@NotNull PubNubForJava pubnub, @NotNull PNMembershipResult pnMembershipResult) { membershipResultHolder.set(pnMembershipResult); } }); diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/channel/ChannelMetadataIT.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/channel/ChannelMetadataIT.java index d428316c0..0ec0178cd 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/channel/ChannelMetadataIT.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/channel/ChannelMetadataIT.java @@ -2,11 +2,11 @@ import com.pubnub.api.PubNubException; import com.pubnub.api.integration.objects.ObjectsApiBaseIT; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadata; -import com.pubnub.api.models.consumer.objects_api.channel.PNGetAllChannelsMetadataResult; -import com.pubnub.api.models.consumer.objects_api.channel.PNGetChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.channel.PNRemoveChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.channel.PNSetChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadata; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNGetAllChannelsMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNGetChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNRemoveChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNSetChannelMetadataResult; import org.apache.commons.lang3.RandomStringUtils; import org.apache.http.HttpStatus; import org.jetbrains.annotations.NotNull; diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/members/ChannelMembersIT.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/members/ChannelMembersIT.java index db49c0a4f..416b8b39a 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/members/ChannelMembersIT.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/members/ChannelMembersIT.java @@ -2,13 +2,13 @@ import com.pubnub.api.PubNubException; import com.pubnub.api.integration.objects.ObjectsApiBaseIT; -import com.pubnub.api.models.consumer.objects_api.member.PNGetChannelMembersResult; -import com.pubnub.api.models.consumer.objects_api.member.PNManageChannelMembersResult; -import com.pubnub.api.models.consumer.objects_api.member.PNMembers; -import com.pubnub.api.models.consumer.objects_api.member.PNRemoveChannelMembersResult; -import com.pubnub.api.models.consumer.objects_api.member.PNSetChannelMembersResult; -import com.pubnub.api.models.consumer.objects_api.member.PNUUID; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadata; +import com.pubnub.api.java.models.consumer.objects_api.member.PNGetChannelMembersResult; +import com.pubnub.api.java.models.consumer.objects_api.member.PNManageChannelMembersResult; +import com.pubnub.api.java.models.consumer.objects_api.member.PNMembers; +import com.pubnub.api.java.models.consumer.objects_api.member.PNRemoveChannelMembersResult; +import com.pubnub.api.java.models.consumer.objects_api.member.PNSetChannelMembersResult; +import com.pubnub.api.java.models.consumer.objects_api.member.PNUUID; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadata; import org.apache.http.HttpStatus; import org.junit.After; import org.junit.Test; @@ -26,7 +26,7 @@ import java.util.UUID; import java.util.stream.Collectors; -import static com.pubnub.api.endpoints.objects_api.utils.Include.PNUUIDDetailsLevel.UUID_WITH_CUSTOM; +import static com.pubnub.api.java.endpoints.objects_api.utils.Include.PNUUIDDetailsLevel.UUID_WITH_CUSTOM; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsInAnyOrder; diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/members/CustomMetadataInMembersPropagationIT.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/members/CustomMetadataInMembersPropagationIT.java index 1a6e5d0ca..84fdaba00 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/members/CustomMetadataInMembersPropagationIT.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/members/CustomMetadataInMembersPropagationIT.java @@ -2,10 +2,10 @@ import com.pubnub.api.PubNubException; import com.pubnub.api.integration.objects.ObjectsApiBaseIT; -import com.pubnub.api.models.consumer.objects_api.member.PNGetChannelMembersResult; -import com.pubnub.api.models.consumer.objects_api.member.PNSetChannelMembersResult; -import com.pubnub.api.models.consumer.objects_api.member.PNUUID; -import com.pubnub.api.models.consumer.objects_api.uuid.PNSetUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.member.PNGetChannelMembersResult; +import com.pubnub.api.java.models.consumer.objects_api.member.PNSetChannelMembersResult; +import com.pubnub.api.java.models.consumer.objects_api.member.PNUUID; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNSetUUIDMetadataResult; import org.junit.After; import org.junit.Test; @@ -14,7 +14,7 @@ import java.util.Map; import java.util.UUID; -import static com.pubnub.api.endpoints.objects_api.utils.Include.PNUUIDDetailsLevel; +import static com.pubnub.api.java.endpoints.objects_api.utils.Include.PNUUIDDetailsLevel; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.hasItem; diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/memberships/CustomMetadataInMembershipPropagationIT.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/memberships/CustomMetadataInMembershipPropagationIT.java index 9e4ad1450..8bdf246f6 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/memberships/CustomMetadataInMembershipPropagationIT.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/memberships/CustomMetadataInMembershipPropagationIT.java @@ -2,14 +2,17 @@ import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.integration.managers.subscription.SubscribeCallbackAdapter; import com.pubnub.api.integration.objects.ObjectsApiBaseIT; -import com.pubnub.api.models.consumer.objects_api.channel.PNSetChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership; -import com.pubnub.api.models.consumer.objects_api.membership.PNGetMembershipsResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNSetMembershipResult; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.callbacks.SubscribeCallback; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNSetChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNGetMembershipsResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNSetMembershipResult; +import com.pubnub.api.models.consumer.PNStatus; import org.awaitility.core.ThrowingRunnable; +import org.jetbrains.annotations.NotNull; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -21,8 +24,8 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; -import static com.pubnub.api.endpoints.objects_api.utils.Include.PNChannelDetailsLevel.CHANNEL; -import static com.pubnub.api.endpoints.objects_api.utils.Include.PNChannelDetailsLevel.CHANNEL_WITH_CUSTOM; +import static com.pubnub.api.java.endpoints.objects_api.utils.Include.PNChannelDetailsLevel.CHANNEL; +import static com.pubnub.api.java.endpoints.objects_api.utils.Include.PNChannelDetailsLevel.CHANNEL_WITH_CUSTOM; import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; @@ -52,9 +55,14 @@ public class CustomMetadataInMembershipPropagationIT extends ObjectsApiBaseIT { @Before public void setCallbackListener() { - pubNubUnderTest.addListener(new SubscribeCallbackAdapter() { + pubNubUnderTest.addListener(new SubscribeCallback() { @Override - public void membership(final PubNub pubnub, final PNMembershipResult pnMembershipResult) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { + + } + + @Override + public void membership(final PubNubForJava pubnub, final PNMembershipResult pnMembershipResult) { pnMembershipResults.add(pnMembershipResult); } }); diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/memberships/MembershipIT.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/memberships/MembershipIT.java index 77994a17a..a2be42abd 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/memberships/MembershipIT.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/memberships/MembershipIT.java @@ -2,12 +2,12 @@ import com.pubnub.api.PubNubException; import com.pubnub.api.integration.objects.ObjectsApiBaseIT; -import com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership; -import com.pubnub.api.models.consumer.objects_api.membership.PNGetMembershipsResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNManageMembershipResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembership; -import com.pubnub.api.models.consumer.objects_api.membership.PNRemoveMembershipResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNSetMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNGetMembershipsResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNManageMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembership; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNRemoveMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNSetMembershipResult; import org.apache.http.HttpStatus; import org.junit.After; import org.junit.Test; @@ -22,7 +22,7 @@ import java.util.Map; import java.util.UUID; -import static com.pubnub.api.endpoints.objects_api.utils.Include.PNChannelDetailsLevel.CHANNEL_WITH_CUSTOM; +import static com.pubnub.api.java.endpoints.objects_api.utils.Include.PNChannelDetailsLevel.CHANNEL_WITH_CUSTOM; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.hasItem; diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/uuid/UUIDMetadataIT.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/uuid/UUIDMetadataIT.java index bb4127d5c..368914757 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/uuid/UUIDMetadataIT.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/objects/uuid/UUIDMetadataIT.java @@ -2,10 +2,10 @@ import com.pubnub.api.PubNubException; import com.pubnub.api.integration.objects.ObjectsApiBaseIT; -import com.pubnub.api.models.consumer.objects_api.uuid.PNGetAllUUIDMetadataResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNGetUUIDMetadataResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNRemoveUUIDMetadataResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNSetUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNGetAllUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNGetUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNRemoveUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNSetUUIDMetadataResult; import org.apache.commons.lang3.RandomStringUtils; import org.apache.http.HttpStatus; import org.junit.After; diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/pam/AccessManagerIntegrationTest.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/pam/AccessManagerIntegrationTest.java index 13f143bcc..58152d05d 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/pam/AccessManagerIntegrationTest.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/pam/AccessManagerIntegrationTest.java @@ -4,18 +4,19 @@ import com.google.gson.JsonObject; import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.callbacks.SubscribeCallback; -import com.pubnub.api.endpoints.access.Grant; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.callbacks.SubscribeCallback; +import com.pubnub.api.java.endpoints.access.Grant; import com.pubnub.api.integration.util.BaseIntegrationTest; import com.pubnub.api.integration.util.RandomGenerator; import com.pubnub.api.models.consumer.PNPublishResult; import com.pubnub.api.models.consumer.PNStatus; -import com.pubnub.api.models.consumer.access_manager.PNAccessManagerGrantResult; +import com.pubnub.api.java.models.consumer.access_manager.PNAccessManagerGrantResult; import com.pubnub.api.models.consumer.message_actions.PNAddMessageActionResult; import com.pubnub.api.models.consumer.message_actions.PNMessageAction; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult; import com.pubnub.api.models.consumer.pubsub.PNMessageResult; import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; import com.pubnub.api.models.consumer.pubsub.PNSignalResult; @@ -555,23 +556,15 @@ public void testPresenceWithPermission() { requestAccess(READ); pubNub.addListener(new SubscribeCallback() { - @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - } @Override - public void status(@NotNull PubNub pubNub, @NotNull PNStatus pnStatus) { + public void status(@NotNull PubNubForJava pubNub, @NotNull PNStatus pnStatus) { } @Override - public void message(@NotNull PubNub pubNub, @NotNull PNMessageResult pnMessageResult) { - - } - - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult pnPresenceEventResult) { + public void presence(@NotNull PubNubForJava pubnub, @NotNull PNPresenceEventResult pnPresenceEventResult) { if ((pnPresenceEventResult.getEvent().equals("join")) && (pnPresenceEventResult.getChannel().equals(expectedChannel))) { if (pnPresenceEventResult.getUuid().equals(server.getConfiguration().getUserId().getValue())) { @@ -580,31 +573,6 @@ public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult pnPr } } - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull final PubNub pubnub, @NotNull final PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } - }); subscribeToChannel(pubNub, expectedChannel); diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/pam/GrantIT.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/pam/GrantIT.java index d9de841d1..35f1d6554 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/pam/GrantIT.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/pam/GrantIT.java @@ -2,8 +2,8 @@ import com.pubnub.api.PubNubException; import com.pubnub.api.integration.util.BaseIntegrationTest; -import com.pubnub.api.models.consumer.access_manager.PNAccessManagerGrantResult; -import com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeyData; +import com.pubnub.api.java.models.consumer.access_manager.PNAccessManagerGrantResult; +import com.pubnub.api.java.models.consumer.access_manager.PNAccessManagerKeyData; import org.junit.Test; import java.util.Collections; diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/pam/GrantTokenIT.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/pam/GrantTokenIT.java index be747c316..f9092bec8 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/pam/GrantTokenIT.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/pam/GrantTokenIT.java @@ -1,15 +1,15 @@ package com.pubnub.api.integration.pam; -import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.SpaceId; import com.pubnub.api.UserId; import com.pubnub.api.enums.PNLogVerbosity; import com.pubnub.api.integration.util.BaseIntegrationTest; -import com.pubnub.api.models.consumer.access_manager.sum.SpacePermissions; -import com.pubnub.api.models.consumer.access_manager.sum.UserPermissions; -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant; -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.SpaceId; +import com.pubnub.api.java.models.consumer.access_manager.sum.SpacePermissions; +import com.pubnub.api.java.models.consumer.access_manager.sum.UserPermissions; +import com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGrant; +import com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGroupGrant; import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult; import com.pubnub.api.models.consumer.access_manager.v3.PNToken; import org.junit.Test; @@ -23,7 +23,7 @@ public class GrantTokenIT extends BaseIntegrationTest { @Test public void happyPath_SUM() throws PubNubException { - PubNub pubNubUnderTest = getServer(); + PubNubForJava pubNubUnderTest = getServer(); final int expectedTTL = 1337; String expectedSpaceIdValue = "space01"; String expectedUser01Value = "user01"; @@ -54,7 +54,7 @@ public void happyPath_SUM() throws PubNubException { @Test public void happyPath() throws PubNubException { //given - PubNub pubNubUnderTest = getServer(builder -> builder.logVerbosity(PNLogVerbosity.BODY)); + PubNubForJava pubNubUnderTest = getServer(builder -> builder.logVerbosity(PNLogVerbosity.BODY)); final int expectedTTL = 1337; final String expectedChannelResourceName = "channelResource"; final String expectedChannelPattern = "channel.*"; diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/util/BaseIntegrationTest.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/util/BaseIntegrationTest.java index 9f33e6b25..d4fc2bb02 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/util/BaseIntegrationTest.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/util/BaseIntegrationTest.java @@ -5,19 +5,12 @@ import com.pubnub.api.PubNubError; import com.pubnub.api.PubNubException; import com.pubnub.api.UserId; -import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.callbacks.SubscribeCallback; import com.pubnub.api.enums.PNLogVerbosity; import com.pubnub.api.enums.PNStatusCategory; import com.pubnub.api.models.consumer.PNStatus; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult; -import com.pubnub.api.models.consumer.pubsub.PNMessageResult; -import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; -import com.pubnub.api.models.consumer.pubsub.PNSignalResult; -import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult; -import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult; -import com.pubnub.api.v2.PNConfiguration; +import com.pubnub.api.java.v2.PNConfiguration; import okhttp3.logging.HttpLoggingInterceptor; import org.aeonbits.owner.ConfigFactory; import org.awaitility.Awaitility; @@ -57,13 +50,13 @@ public abstract class BaseIntegrationTest { private static String PAM_PUB_KEY; private static String PAM_SEC_KEY; - public PubNub pubNub; - public PubNub server; + public PubNubForJava pubNub; + public PubNubForJava server; public int TIMEOUT_MEDIUM = 5; public int TIMEOUT_LOW = 2; - private List mGuestClients = new ArrayList<>(); + private List mGuestClients = new ArrayList<>(); @BeforeClass public static void onlyOnce() { @@ -90,34 +83,34 @@ public void after() { onAfter(); destroyClient(pubNub); if (mGuestClients != null) { - for (PubNub guestClient : mGuestClients) { + for (PubNubForJava guestClient : mGuestClients) { destroyClient(guestClient); } } // properties.clear(); } - public PubNub getPubNub(@Nullable Consumer action) { + public PubNubForJava getPubNub(@Nullable Consumer action) { PNConfiguration pnConfiguration = provideStagingConfiguration(action); if (pnConfiguration == null) { pnConfiguration = getBasicPnConfiguration(action); } - final PubNub pubNub = PubNub.create(pnConfiguration); + final PubNubForJava pubNub = PubNubForJava.create(pnConfiguration); registerGuestClient(pubNub); return pubNub; } - public PubNub getPubNub() { + public PubNubForJava getPubNub() { return getPubNub(null); } - protected PubNub getServer(@Nullable Consumer action) { - final PubNub pubNub = PubNub.create(getServerPnConfiguration(action)); + protected PubNubForJava getServer(@Nullable Consumer action) { + final PubNubForJava pubNub = PubNubForJava.create(getServerPnConfiguration(action)); registerGuestClient(pubNub); return pubNub; } - public PubNub getServer() { + public PubNubForJava getServer() { return getServer(null); } @@ -127,26 +120,26 @@ public PubNub getServer() { // return pubNub; // } - protected void registerGuestClient(PubNub guestClient) { + protected void registerGuestClient(PubNubForJava guestClient) { if (mGuestClients == null) { mGuestClients = new ArrayList<>(); } mGuestClients.add(guestClient); } - protected void destroyClient(PubNub client) { + protected void destroyClient(PubNubForJava client) { client.unsubscribeAll(); client.forceDestroy(); } private PNConfiguration getBasicPnConfiguration(@Nullable Consumer action) { - final com.pubnub.api.v2.PNConfiguration.Builder pnConfiguration; + final com.pubnub.api.java.v2.PNConfiguration.Builder pnConfiguration; try { if (!needsServer()) { - pnConfiguration = com.pubnub.api.v2.PNConfiguration.builder(new UserId("client-".concat(UUID.randomUUID().toString())), SUB_KEY); + pnConfiguration = com.pubnub.api.java.v2.PNConfiguration.builder(new UserId("client-".concat(UUID.randomUUID().toString())), SUB_KEY); pnConfiguration.publishKey(PUB_KEY); } else { - pnConfiguration = com.pubnub.api.v2.PNConfiguration.builder(new UserId("client-".concat(UUID.randomUUID().toString())), PAM_SUB_KEY); + pnConfiguration = com.pubnub.api.java.v2.PNConfiguration.builder(new UserId("client-".concat(UUID.randomUUID().toString())), PAM_SUB_KEY); pnConfiguration.publishKey(PAM_PUB_KEY); pnConfiguration.authKey(provideAuthKey()); } @@ -184,7 +177,7 @@ private HttpLoggingInterceptor createInterceptor() { return interceptor; } - protected void subscribeToChannel(@NotNull PubNub pubnub, @NotNull String... channels) { + protected void subscribeToChannel(@NotNull PubNubForJava pubnub, @NotNull String... channels) { pubnub.subscribe() .channels(Arrays.asList(channels)) .withPresence() @@ -192,18 +185,13 @@ protected void subscribeToChannel(@NotNull PubNub pubnub, @NotNull String... cha pause(1); } - protected void subscribeToChannel(final PubNub pubnub, final List channels) { + protected void subscribeToChannel(final PubNubForJava pubnub, final List channels) { final AtomicBoolean subscribeSuccess = new AtomicBoolean(); pubnub.addListener(new SubscribeCallback() { @Override - public void file(@NotNull PubNub pubnub, @NotNull PNFileEventResult pnFileEventResult) { - - } - - @Override - public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { + public void status(@NotNull PubNubForJava pubnub, @NotNull PNStatus status) { if (status.getCategory() == PNStatusCategory.PNConnectedCategory) { if (status.getAffectedChannels().containsAll(channels)) { subscribeSuccess.set(true); @@ -214,42 +202,6 @@ public void status(@NotNull PubNub pubnub, @NotNull PNStatus status) { } } } - - @Override - public void message(@NotNull PubNub pubnub, @NotNull PNMessageResult message) { - - } - - @Override - public void presence(@NotNull PubNub pubnub, @NotNull PNPresenceEventResult presence) { - - } - - @Override - public void signal(@NotNull PubNub pubNub, @NotNull PNSignalResult pnSignalResult) { - - } - - @Override - public void uuid(@NotNull final PubNub pubnub, @NotNull final PNUUIDMetadataResult pnUUIDMetadataResult) { - - } - - @Override - public void channel(@NotNull final PubNub pubnub, @NotNull final PNChannelMetadataResult pnChannelMetadataResult) { - - } - - @Override - public void membership(@NotNull final PubNub pubnub, @NotNull final PNMembershipResult pnMembershipResult) { - - } - - @Override - public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult pnActionResult) { - - } - }); pubnub.subscribe() @@ -260,7 +212,7 @@ public void messageAction(@NotNull PubNub pubnub, @NotNull PNMessageActionResult Awaitility.await().atMost(Durations.TEN_SECONDS).untilTrue(subscribeSuccess); } - protected void subscribeToChannelGroup(@NotNull PubNub pubnub, @NotNull String group) { + protected void subscribeToChannelGroup(@NotNull PubNubForJava pubnub, @NotNull String group) { pubnub.subscribe() .channelGroups(Collections.singletonList(group)) .withPresence() @@ -268,19 +220,19 @@ protected void subscribeToChannelGroup(@NotNull PubNub pubnub, @NotNull String g pause(1); } - protected void unsubscribeFromChannel(PubNub pubNub, String channel) { + protected void unsubscribeFromChannel(PubNubForJava pubNub, String channel) { pubNub.unsubscribe() .channels(Collections.singletonList(channel)) .execute(); pause(1); } - protected void unsubscribeFromAllChannels(PubNub pubNub) { + protected void unsubscribeFromAllChannels(PubNubForJava pubNub) { pubNub.unsubscribeAll(); pause(1); } - protected Map generateMessage(PubNub pubNub, String message) { + protected Map generateMessage(PubNubForJava pubNub, String message) { final Map map = new HashMap<>(); map.put("publisher", pubNub.getConfiguration().getUserId().getValue()); map.put("text", "mymsg" + RandomGenerator.newValue(5) + "+" + RandomGenerator.newValue(5)); @@ -289,7 +241,7 @@ protected Map generateMessage(PubNub pubNub, String message) { return map; } - protected JsonObject generateMessage(PubNub pubNub) { + protected JsonObject generateMessage(PubNubForJava pubNub) { final JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("publisher", pubNub.getConfiguration().getUserId().getValue()); jsonObject.addProperty("text", RandomGenerator.newValue(8)); @@ -325,7 +277,7 @@ protected HashMap generateMap() { return map; } - protected void publishMessage(PubNub pubNub, String channel, String message) { + protected void publishMessage(PubNubForJava pubNub, String channel, String message) { pubNub.publish() .message(generateMessage(pubNub, message)) .channel(channel) @@ -335,7 +287,7 @@ protected void publishMessage(PubNub pubNub, String channel, String message) { }); } - protected void publishMessage(PubNub pubNub, String channel, String message, Map meta) { + protected void publishMessage(PubNubForJava pubNub, String channel, String message, Map meta) { pubNub.publish() .message(generateMessage(pubNub, message)) .channel(channel) diff --git a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/util/Utils.java b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/util/Utils.java index a69063e5f..e1f9ba2f6 100644 --- a/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/util/Utils.java +++ b/pubnub-gson/pubnub-gson-impl/src/integrationTest/java/com/pubnub/api/integration/util/Utils.java @@ -2,7 +2,8 @@ import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.endpoints.pubsub.Publish; +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.endpoints.pubsub.Publish; import com.pubnub.api.models.consumer.PNPublishResult; import java.text.SimpleDateFormat; @@ -36,7 +37,7 @@ public static boolean isSorted(List list) { return list.equals(sorted) || list.equals(reversed); } - public static PNPublishResult publish(PubNub pubnub, String channel, int indicator) { + public static PNPublishResult publish(PubNubForJava pubnub, String channel, int indicator) { try { return pubnub.publish() .channel(channel) @@ -62,7 +63,7 @@ public static String randomChannel() { // return request.url().queryParameter(param); // } - public static List publishMixed(PubNub pubnub, int count, String channel) { + public static List publishMixed(PubNubForJava pubnub, int count, String channel) { final List list = new ArrayList<>(); for (int i = 0; i < count; i++) { final Publish publishBuilder = pubnub.publish() diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/PubNubImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/PubNubImpl.java deleted file mode 100644 index adb9fdbd7..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/PubNubImpl.java +++ /dev/null @@ -1,673 +0,0 @@ -package com.pubnub.internal; - -import com.pubnub.api.PNConfiguration; -import com.pubnub.api.PubNub; -import com.pubnub.api.PubNubError; -import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PresenceBuilder; -import com.pubnub.api.builder.SubscribeBuilder; -import com.pubnub.api.builder.UnsubscribeBuilder; -import com.pubnub.api.callbacks.Listener; -import com.pubnub.api.callbacks.SubscribeCallback; -import com.pubnub.api.crypto.CryptoModule; -import com.pubnub.api.endpoints.DeleteMessages; -import com.pubnub.api.endpoints.FetchMessages; -import com.pubnub.api.endpoints.History; -import com.pubnub.api.endpoints.MessageCounts; -import com.pubnub.api.endpoints.Time; -import com.pubnub.api.endpoints.access.Grant; -import com.pubnub.api.endpoints.access.RevokeToken; -import com.pubnub.api.endpoints.access.builder.GrantTokenBuilder; -import com.pubnub.api.endpoints.channel_groups.AddChannelChannelGroup; -import com.pubnub.api.endpoints.channel_groups.AllChannelsChannelGroup; -import com.pubnub.api.endpoints.channel_groups.DeleteChannelGroup; -import com.pubnub.api.endpoints.channel_groups.ListAllChannelGroup; -import com.pubnub.api.endpoints.channel_groups.RemoveChannelChannelGroup; -import com.pubnub.api.endpoints.files.DeleteFile; -import com.pubnub.api.endpoints.files.DownloadFile; -import com.pubnub.api.endpoints.files.GetFileUrl; -import com.pubnub.api.endpoints.files.ListFiles; -import com.pubnub.api.endpoints.files.PublishFileMessage; -import com.pubnub.api.endpoints.files.SendFile; -import com.pubnub.api.endpoints.message_actions.AddMessageAction; -import com.pubnub.api.endpoints.message_actions.GetMessageActions; -import com.pubnub.api.endpoints.message_actions.RemoveMessageAction; -import com.pubnub.api.endpoints.objects_api.channel.GetAllChannelsMetadata; -import com.pubnub.api.endpoints.objects_api.channel.GetChannelMetadata; -import com.pubnub.api.endpoints.objects_api.channel.RemoveChannelMetadata; -import com.pubnub.api.endpoints.objects_api.channel.SetChannelMetadata; -import com.pubnub.api.endpoints.objects_api.members.GetChannelMembers; -import com.pubnub.api.endpoints.objects_api.members.ManageChannelMembers; -import com.pubnub.api.endpoints.objects_api.members.RemoveChannelMembers; -import com.pubnub.api.endpoints.objects_api.members.SetChannelMembers; -import com.pubnub.api.endpoints.objects_api.memberships.GetMemberships; -import com.pubnub.api.endpoints.objects_api.memberships.ManageMemberships; -import com.pubnub.api.endpoints.objects_api.memberships.RemoveMemberships; -import com.pubnub.api.endpoints.objects_api.memberships.SetMemberships; -import com.pubnub.api.endpoints.objects_api.uuid.GetAllUUIDMetadata; -import com.pubnub.api.endpoints.objects_api.uuid.GetUUIDMetadata; -import com.pubnub.api.endpoints.objects_api.uuid.RemoveUUIDMetadata; -import com.pubnub.api.endpoints.objects_api.uuid.SetUUIDMetadata; -import com.pubnub.api.endpoints.presence.GetState; -import com.pubnub.api.endpoints.presence.HereNow; -import com.pubnub.api.endpoints.presence.SetState; -import com.pubnub.api.endpoints.presence.WhereNow; -import com.pubnub.api.endpoints.pubsub.Publish; -import com.pubnub.api.endpoints.pubsub.Signal; -import com.pubnub.api.endpoints.push.AddChannelsToPush; -import com.pubnub.api.endpoints.push.ListPushProvisions; -import com.pubnub.api.endpoints.push.RemoveAllPushChannelsForDevice; -import com.pubnub.api.endpoints.push.RemoveChannelsFromPush; -import com.pubnub.api.v2.BasePNConfiguration; -import com.pubnub.api.v2.callbacks.EventListener; -import com.pubnub.api.v2.callbacks.StatusListener; -import com.pubnub.api.v2.endpoints.pubsub.PublishBuilder; -import com.pubnub.api.v2.endpoints.pubsub.SignalBuilder; -import com.pubnub.api.v2.entities.Channel; -import com.pubnub.api.v2.entities.ChannelGroup; -import com.pubnub.api.v2.entities.ChannelMetadata; -import com.pubnub.api.v2.entities.UserMetadata; -import com.pubnub.api.v2.subscriptions.Subscription; -import com.pubnub.api.v2.subscriptions.SubscriptionSet; -import com.pubnub.internal.callbacks.DelegatingStatusListener; -import com.pubnub.internal.callbacks.DelegatingSubscribeCallback; -import com.pubnub.internal.endpoints.DeleteMessagesImpl; -import com.pubnub.internal.endpoints.FetchMessagesImpl; -import com.pubnub.internal.endpoints.HistoryImpl; -import com.pubnub.internal.endpoints.MessageCountsImpl; -import com.pubnub.internal.endpoints.TimeImpl; -import com.pubnub.internal.endpoints.access.GrantImpl; -import com.pubnub.internal.endpoints.access.GrantTokenImpl; -import com.pubnub.internal.endpoints.access.RevokeTokenImpl; -import com.pubnub.internal.endpoints.channel_groups.AddChannelChannelGroupImpl; -import com.pubnub.internal.endpoints.channel_groups.AllChannelsChannelGroupImpl; -import com.pubnub.internal.endpoints.channel_groups.DeleteChannelGroupImpl; -import com.pubnub.internal.endpoints.channel_groups.ListAllChannelGroupImpl; -import com.pubnub.internal.endpoints.channel_groups.RemoveChannelChannelGroupImpl; -import com.pubnub.internal.endpoints.files.DeleteFileImpl; -import com.pubnub.internal.endpoints.files.DownloadFileImpl; -import com.pubnub.internal.endpoints.files.GetFileUrlImpl; -import com.pubnub.internal.endpoints.files.ListFilesImpl; -import com.pubnub.internal.endpoints.files.PublishFileMessageImpl; -import com.pubnub.internal.endpoints.files.SendFileImpl; -import com.pubnub.internal.endpoints.message_actions.AddMessageActionImpl; -import com.pubnub.internal.endpoints.message_actions.GetMessageActionsImpl; -import com.pubnub.internal.endpoints.message_actions.RemoveMessageActionImpl; -import com.pubnub.internal.endpoints.objects_api.channel.GetAllChannelsMetadataImpl; -import com.pubnub.internal.endpoints.objects_api.channel.GetChannelMetadataImpl; -import com.pubnub.internal.endpoints.objects_api.channel.RemoveChannelMetadataImpl; -import com.pubnub.internal.endpoints.objects_api.channel.SetChannelMetadataImpl; -import com.pubnub.internal.endpoints.objects_api.members.GetChannelMembersImpl; -import com.pubnub.internal.endpoints.objects_api.members.ManageChannelMembersImpl; -import com.pubnub.internal.endpoints.objects_api.members.RemoveChannelMembersImpl; -import com.pubnub.internal.endpoints.objects_api.members.SetChannelMembersImpl; -import com.pubnub.internal.endpoints.objects_api.memberships.GetMembershipsImpl; -import com.pubnub.internal.endpoints.objects_api.memberships.ManageMembershipsImpl; -import com.pubnub.internal.endpoints.objects_api.memberships.RemoveMembershipsImpl; -import com.pubnub.internal.endpoints.objects_api.memberships.SetMembershipsImpl; -import com.pubnub.internal.endpoints.objects_api.uuid.GetAllUUIDMetadataImpl; -import com.pubnub.internal.endpoints.objects_api.uuid.GetUUIDMetadataImpl; -import com.pubnub.internal.endpoints.objects_api.uuid.RemoveUUIDMetadataImpl; -import com.pubnub.internal.endpoints.objects_api.uuid.SetUUIDMetadataImpl; -import com.pubnub.internal.endpoints.presence.GetStateImpl; -import com.pubnub.internal.endpoints.presence.HereNowImpl; -import com.pubnub.internal.endpoints.presence.SetStateImpl; -import com.pubnub.internal.endpoints.presence.WhereNowImpl; -import com.pubnub.internal.endpoints.pubsub.PublishImpl; -import com.pubnub.internal.endpoints.pubsub.SignalImpl; -import com.pubnub.internal.endpoints.push.AddChannelsToPushImpl; -import com.pubnub.internal.endpoints.push.ListPushProvisionsImpl; -import com.pubnub.internal.endpoints.push.RemoveAllPushChannelsForDeviceImpl; -import com.pubnub.internal.endpoints.push.RemoveChannelsFromPushImpl; -import com.pubnub.internal.v2.callbacks.DelegatingEventListener; -import com.pubnub.internal.v2.entities.ChannelGroupImpl; -import com.pubnub.internal.v2.entities.ChannelImpl; -import com.pubnub.internal.v2.entities.ChannelMetadataImpl; -import com.pubnub.internal.v2.entities.UserMetadataImpl; -import com.pubnub.internal.v2.subscription.SubscriptionImpl; -import com.pubnub.internal.v2.subscription.SubscriptionSetImpl; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.InputStream; -import java.util.List; -import java.util.Set; - -public class PubNubImpl extends BasePubNubImpl< - EventListener, - Subscription, - Channel, - ChannelGroup, - ChannelMetadata, - UserMetadata, - SubscriptionSet, - StatusListener> implements PubNub { - - private static final String PNSDK_PUBNUB_JAVA = "PubNub-Java"; - private final @NotNull BasePNConfiguration configuration; - - public PubNubImpl(@NotNull BasePNConfiguration configuration) { - super(configuration, PNSDK_PUBNUB_JAVA); - this.configuration = configuration; - } - - @Override - @NotNull - public BasePNConfiguration getConfiguration() { - return configuration; - } - - @Override - @NotNull - public SubscribeBuilder subscribe() { - return new SubscribeBuilder(getPubNubCore()); - } - - @Override - @NotNull - public UnsubscribeBuilder unsubscribe() { - return new UnsubscribeBuilder(getPubNubCore()); - } - - @Override - @NotNull - public PresenceBuilder presence() { - return new PresenceBuilder(getPubNubCore()); - } - - // start push - - @Override - @NotNull - public AddChannelsToPush addPushNotificationsOnChannels() { - return new AddChannelsToPushImpl(getPubNubCore()); - } - - @Override - @NotNull - public RemoveChannelsFromPush removePushNotificationsFromChannels() { - return new RemoveChannelsFromPushImpl(getPubNubCore()); - } - - @Override - @NotNull - public RemoveAllPushChannelsForDevice removeAllPushNotificationsFromDeviceWithPushToken() { - return new RemoveAllPushChannelsForDeviceImpl(getPubNubCore()); - } - - @Override - @NotNull - public ListPushProvisions auditPushChannelProvisions() { - return new ListPushProvisionsImpl(getPubNubCore()); - } - - // end push - - @Override - @NotNull - public WhereNow whereNow() { - return new WhereNowImpl(getPubNubCore()); - } - - @Override - @NotNull - public HereNow hereNow() { - return new HereNowImpl(getPubNubCore()); - } - - @Override - @NotNull - public Time time() { - return new TimeImpl(getPubNubCore()); - } - - @Override - @NotNull - public History history() { - return new HistoryImpl(getPubNubCore()); - } - - @Override - @NotNull - public FetchMessages fetchMessages() { - return new FetchMessagesImpl(getPubNubCore()); - } - - @Override - @NotNull - public DeleteMessages deleteMessages() { - return new DeleteMessagesImpl(getPubNubCore()); - } - - @Override - @NotNull - public MessageCounts messageCounts() { - return new MessageCountsImpl(getPubNubCore()); - } - - @Override - @NotNull - public Grant grant() { - return new GrantImpl(getPubNubCore()); - } - - /** - * @deprecated Use {@link #grantToken(int)} instead. - */ - @Override - @NotNull - public GrantTokenBuilder grantToken() { - return new GrantTokenImpl(getPubNubCore()); - } - - @Override - @NotNull - @SuppressWarnings("deprecation") - public GrantTokenBuilder grantToken(int ttl) { - return new GrantTokenImpl(getPubNubCore()).ttl(ttl); - } - - @Override - @NotNull - public RevokeToken revokeToken() { - return new RevokeTokenImpl(getPubNubCore()); - } - - @Override - @NotNull - public GetState getPresenceState() { - return new GetStateImpl(getPubNubCore()); - } - - @Override - @NotNull - public SetState setPresenceState() { - return new SetStateImpl(getPubNubCore()); - } - - @Override - @NotNull - public Publish publish() { - return new PublishImpl(getPubNubCore()); - } - - @Override - @NotNull - public PublishBuilder publish(@NotNull Object message, @NotNull String channel) { - return new PublishImpl(getPubNubCore()).message(message).channel(channel); - } - - @Override - @NotNull - public Signal signal() { - return new SignalImpl(getPubNubCore()); - } - - @Override - @NotNull - public SignalBuilder signal(@NotNull Object message, @NotNull String channel) { - return new SignalImpl(getPubNubCore()).message(message).channel(channel); - } - - @Override - @NotNull - public ListAllChannelGroup listAllChannelGroups() { - return new ListAllChannelGroupImpl(getPubNubCore()); - } - - @Override - @NotNull - public AllChannelsChannelGroup listChannelsForChannelGroup() { - return new AllChannelsChannelGroupImpl(getPubNubCore()); - } - - @Override - @NotNull - public AddChannelChannelGroup addChannelsToChannelGroup() { - return new AddChannelChannelGroupImpl(getPubNubCore()); - } - - @Override - @NotNull - public RemoveChannelChannelGroup removeChannelsFromChannelGroup() { - return new RemoveChannelChannelGroupImpl(getPubNubCore()); - } - - @Override - @NotNull - public DeleteChannelGroup deleteChannelGroup() { - return new DeleteChannelGroupImpl(getPubNubCore()); - } - - // Start Objects API - - @Override - public SetUUIDMetadata setUUIDMetadata() { - return new SetUUIDMetadataImpl(getPubNubCore()); - } - - @Override - @NotNull - public GetAllUUIDMetadata getAllUUIDMetadata() { - return new GetAllUUIDMetadataImpl(getPubNubCore()); - } - - @Override - @NotNull - public GetUUIDMetadata getUUIDMetadata() { - return new GetUUIDMetadataImpl(getPubNubCore()); - } - - @Override - @NotNull - public RemoveUUIDMetadata removeUUIDMetadata() { - return new RemoveUUIDMetadataImpl(getPubNubCore()); - } - - @Override - public SetChannelMetadata.Builder setChannelMetadata() { - return new SetChannelMetadataImpl.Builder(getPubNubCore()); - } - - @Override - @NotNull - public GetAllChannelsMetadata getAllChannelsMetadata() { - return new GetAllChannelsMetadataImpl(getPubNubCore()); - } - - @Override - @NotNull - public GetChannelMetadata.Builder getChannelMetadata() { - return new GetChannelMetadataImpl.Builder(getPubNubCore()); - } - - @Override - public RemoveChannelMetadata.Builder removeChannelMetadata() { - return new RemoveChannelMetadataImpl.Builder(getPubNubCore()); - } - - @Override - @NotNull - public GetMemberships getMemberships() { - return new GetMembershipsImpl(getPubNubCore()); - } - - @Override - @NotNull - public SetMemberships.Builder setMemberships() { - return new SetMembershipsImpl.Builder(getPubNubCore()); - } - - @Override - public RemoveMemberships.Builder removeMemberships() { - return new RemoveMembershipsImpl.Builder(getPubNubCore()); - } - - @Override - public ManageMemberships.Builder manageMemberships() { - return new ManageMembershipsImpl.Builder(getPubNubCore()); - } - - @Override - public GetChannelMembers.Builder getChannelMembers() { - return new GetChannelMembersImpl.Builder(getPubNubCore()); - } - - @Override - public SetChannelMembers.Builder setChannelMembers() { - return new SetChannelMembersImpl.Builder(getPubNubCore()); - } - - @Override - public RemoveChannelMembers.Builder removeChannelMembers() { - return new RemoveChannelMembersImpl.Builder(getPubNubCore()); - } - - @Override - public ManageChannelMembers.Builder manageChannelMembers() { - return new ManageChannelMembersImpl.Builder(getPubNubCore()); - } - - // End Objects API - - // Start Message Actions API - - @Override - @NotNull - public AddMessageAction addMessageAction() { - return new AddMessageActionImpl(getPubNubCore()); - } - - @Override - @NotNull - public GetMessageActions getMessageActions() { - return new GetMessageActionsImpl(getPubNubCore()); - } - - @Override - @NotNull - public RemoveMessageAction removeMessageAction() { - return new RemoveMessageActionImpl(getPubNubCore()); - } - - // End Message Actions API - - @Override - public SendFile.Builder sendFile() { - return new SendFileImpl.Builder(getPubNubCore()); - } - - @Override - public ListFiles.Builder listFiles() { - return new ListFilesImpl.Builder(getPubNubCore()); - } - - @Override - public GetFileUrl.Builder getFileUrl() { - return GetFileUrlImpl.builder(getPubNubCore()); - } - - @Override - public DownloadFile.Builder downloadFile() { - return DownloadFileImpl.builder(getPubNubCore()); - } - - @Override - public DeleteFile.Builder deleteFile() { - return DeleteFileImpl.builder(getPubNubCore()); - } - - @Override - public PublishFileMessage.Builder publishFileMessage() { - return PublishFileMessageImpl.builder(getPubNubCore()); - } - - @Override - public void reconnect() { - reconnect(0); - } - - // public methods - @SuppressWarnings("deprecation") - @NotNull - private CryptoModule getCryptoModuleOrThrow(@Nullable String cipherKey) throws PubNubException { - if (cipherKey != null) { - if (configuration instanceof PNConfiguration) { - return CryptoModule.createLegacyCryptoModule(cipherKey, ((PNConfiguration) configuration).getUseRandomInitializationVector()); - } else { - return CryptoModule.createLegacyCryptoModule(cipherKey, true); - } - } else if (configuration.getCryptoModule() != null) { - return configuration.getCryptoModule(); - } else { - throw new PubNubException(PubNubError.CRYPTO_ERROR, "Crypto module is not initialized"); - } - } - - /** - * Perform Cryptographic decryption of an input string using cipher key provided by PNConfiguration - * - * @param inputString String to be encrypted - * @return String containing the encryption of inputString using cipherKey - */ - @Override - @NotNull - public String decrypt(@NotNull String inputString) throws PubNubException { - return decrypt(inputString, null); - } - - /** - * Perform Cryptographic decryption of an input string using the cipher key - * - * @param inputString String to be encrypted - * @param cipherKey cipher key to be used for encryption - * @return String containing the encryption of inputString using cipherKey - * @throws PubNubException throws exception in case of failed encryption - */ - @Override - @NotNull - public String decrypt(@NotNull String inputString, String cipherKey) throws PubNubException { - if (inputString == null) { - throw new PubNubException(PubNubError.INVALID_ARGUMENTS); - } - return getPubNubCore().decrypt(inputString, getCryptoModuleOrThrow(cipherKey)); - } - - @Override - @NotNull - public InputStream decryptInputStream(@NotNull InputStream inputStream) throws PubNubException { - return decryptInputStream(inputStream, null); - } - - @Override - @NotNull - public InputStream decryptInputStream(@NotNull InputStream inputStream, @Nullable String cipherKey) throws PubNubException { - if (inputStream == null) { - throw new PubNubException(PubNubError.INVALID_ARGUMENTS); - } - return getPubNubCore().decryptInputStream(inputStream, getCryptoModuleOrThrow(cipherKey)); - } - - /** - * Perform Cryptographic encryption of an input string and the cipher key provided by PNConfiguration - * - * @param inputString String to be encrypted - * @return String containing the encryption of inputString using cipherKey - */ - @Override - @NotNull - public String encrypt(@NotNull String inputString) throws PubNubException { - return encrypt(inputString, null); - } - - /** - * Perform Cryptographic encryption of an input string and the cipher key. - * - * @param inputString String to be encrypted - * @param cipherKey cipher key to be used for encryption - * @return String containing the encryption of inputString using cipherKey - * @throws PubNubException throws exception in case of failed encryption - */ - @Override - @NotNull - public String encrypt(@NotNull String inputString, String cipherKey) throws PubNubException { - if (inputString == null) { - throw new PubNubException(PubNubError.INVALID_ARGUMENTS); - } - return getPubNubCore().encrypt(inputString, getCryptoModuleOrThrow(cipherKey)); - } - - @Override - @NotNull - public InputStream encryptInputStream(@NotNull InputStream inputStream) throws PubNubException { - return encryptInputStream(inputStream, null); - } - - @Override - @NotNull - public InputStream encryptInputStream(@NotNull InputStream inputStream, String cipherKey) throws PubNubException { - return getPubNubCore().encryptInputStream(inputStream, getCryptoModuleOrThrow(cipherKey)); - } - - @Override - @NotNull - public Publish fire() { - return publish().shouldStore(false).replicate(false); - } - - @Override - @NotNull - public PublishBuilder fire(Object message, String channel) { - return publish(message, channel).shouldStore(false).replicate(false); - } - - @Override - @NotNull - public List getSubscribedChannels() { - return getPubNubCore().getSubscribedChannels(); - } - - @Override - @NotNull - public List getSubscribedChannelGroups() { - return getPubNubCore().getSubscribedChannelGroups(); - } - - @NotNull - @Override - public Channel channel(@NotNull String name) { - return new ChannelImpl(this, name); - } - - @NotNull - @Override - public ChannelGroup channelGroup(@NotNull String name) { - return new ChannelGroupImpl(this, name); - } - - @NotNull - @Override - public ChannelMetadata channelMetadata(@NotNull String id) { - return new ChannelMetadataImpl(this, id); - } - - @NotNull - @Override - public UserMetadata userMetadata(@NotNull String id) { - return new UserMetadataImpl(this, id); - } - - @NotNull - @Override - public SubscriptionSet subscriptionSetOf(@NotNull Set subscriptions) { - return new SubscriptionSetImpl(getPubNubCore(), (Set) subscriptions); - } - - @Override - public void addListener(@NotNull EventListener listener) { - getListenerManager().addListener(new DelegatingEventListener(listener)); - } - - @Override - public void addListener(@NotNull StatusListener listener) { - getListenerManager().addListener(new DelegatingStatusListener(listener)); - } - - @Override - public void addListener(@NotNull SubscribeCallback listener) { - getListenerManager().addListener(new DelegatingSubscribeCallback(listener)); - } - - @Override - public void removeListener(@NotNull Listener listener) { - if (listener instanceof SubscribeCallback) { - super.removeListener(new DelegatingSubscribeCallback((SubscribeCallback) listener)); - } else if (listener instanceof EventListener) { - super.removeListener(new DelegatingEventListener((EventListener) listener)); - } else if (listener instanceof StatusListener) { - super.removeListener(new DelegatingStatusListener((StatusListener) listener)); - } else { - super.removeListener(listener); - } - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/TimeImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/TimeImpl.java deleted file mode 100644 index 7f57797b8..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/TimeImpl.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.pubnub.internal.endpoints; - -import com.pubnub.api.endpoints.Time; -import com.pubnub.api.models.consumer.PNTimeResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import org.jetbrains.annotations.NotNull; - -public class TimeImpl extends IdentityMappingEndpoint implements Time { - - public TimeImpl(PubNubCore pubnub) { - super(pubnub); - } - - @Override - @NotNull - protected EndpointInterface createAction() { - return pubnub.time(); - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/ListAllChannelGroupImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/ListAllChannelGroupImpl.java deleted file mode 100644 index 9bd12a3dc..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/ListAllChannelGroupImpl.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.pubnub.internal.endpoints.channel_groups; - -import com.pubnub.api.endpoints.channel_groups.ListAllChannelGroup; -import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsListAllResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; -import lombok.experimental.Accessors; -import org.jetbrains.annotations.NotNull; - -@Accessors(chain = true, fluent = true) -public class ListAllChannelGroupImpl extends IdentityMappingEndpoint implements ListAllChannelGroup { - - public ListAllChannelGroupImpl(PubNubCore pubnub) { - super(pubnub); - } - - @Override - @NotNull - protected EndpointInterface createAction() { - return pubnub.listAllChannelGroups(); - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/PubNubForJavaImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/PubNubForJavaImpl.kt new file mode 100644 index 000000000..ffb4cd130 --- /dev/null +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/PubNubForJavaImpl.kt @@ -0,0 +1,429 @@ +package com.pubnub.internal.java + +import com.pubnub.api.callbacks.Listener +import com.pubnub.api.java.PubNubForJava +import com.pubnub.api.java.builder.PresenceBuilder +import com.pubnub.api.java.builder.SubscribeBuilder +import com.pubnub.api.java.builder.UnsubscribeBuilder +import com.pubnub.api.java.callbacks.SubscribeCallback +import com.pubnub.api.java.endpoints.DeleteMessages +import com.pubnub.api.java.endpoints.FetchMessages +import com.pubnub.api.java.endpoints.History +import com.pubnub.api.java.endpoints.MessageCounts +import com.pubnub.api.java.endpoints.access.Grant +import com.pubnub.api.java.endpoints.access.RevokeToken +import com.pubnub.api.java.endpoints.access.builder.GrantTokenBuilder +import com.pubnub.api.java.endpoints.channel_groups.AddChannelChannelGroup +import com.pubnub.api.java.endpoints.channel_groups.AllChannelsChannelGroup +import com.pubnub.api.java.endpoints.channel_groups.DeleteChannelGroup +import com.pubnub.api.java.endpoints.channel_groups.RemoveChannelChannelGroup +import com.pubnub.api.java.endpoints.files.DeleteFile +import com.pubnub.api.java.endpoints.files.DownloadFile +import com.pubnub.api.java.endpoints.files.GetFileUrl +import com.pubnub.api.java.endpoints.files.ListFiles +import com.pubnub.api.java.endpoints.files.PublishFileMessage +import com.pubnub.api.java.endpoints.files.SendFile +import com.pubnub.api.java.endpoints.message_actions.AddMessageAction +import com.pubnub.api.java.endpoints.message_actions.GetMessageActions +import com.pubnub.api.java.endpoints.message_actions.RemoveMessageAction +import com.pubnub.api.java.endpoints.objects_api.channel.GetAllChannelsMetadata +import com.pubnub.api.java.endpoints.objects_api.channel.GetChannelMetadata +import com.pubnub.api.java.endpoints.objects_api.channel.RemoveChannelMetadata +import com.pubnub.api.java.endpoints.objects_api.channel.SetChannelMetadata +import com.pubnub.api.java.endpoints.objects_api.members.GetChannelMembers +import com.pubnub.api.java.endpoints.objects_api.members.ManageChannelMembers +import com.pubnub.api.java.endpoints.objects_api.members.RemoveChannelMembers +import com.pubnub.api.java.endpoints.objects_api.members.SetChannelMembers +import com.pubnub.api.java.endpoints.objects_api.memberships.GetMemberships +import com.pubnub.api.java.endpoints.objects_api.memberships.ManageMemberships +import com.pubnub.api.java.endpoints.objects_api.memberships.RemoveMemberships +import com.pubnub.api.java.endpoints.objects_api.memberships.SetMemberships +import com.pubnub.api.java.endpoints.objects_api.uuid.GetAllUUIDMetadata +import com.pubnub.api.java.endpoints.objects_api.uuid.GetUUIDMetadata +import com.pubnub.api.java.endpoints.objects_api.uuid.RemoveUUIDMetadata +import com.pubnub.api.java.endpoints.objects_api.uuid.SetUUIDMetadata +import com.pubnub.api.java.endpoints.presence.GetState +import com.pubnub.api.java.endpoints.presence.HereNow +import com.pubnub.api.java.endpoints.presence.SetState +import com.pubnub.api.java.endpoints.presence.WhereNow +import com.pubnub.api.java.endpoints.pubsub.Publish +import com.pubnub.api.java.endpoints.pubsub.Signal +import com.pubnub.api.java.endpoints.push.AddChannelsToPush +import com.pubnub.api.java.endpoints.push.ListPushProvisions +import com.pubnub.api.java.endpoints.push.RemoveAllPushChannelsForDevice +import com.pubnub.api.java.endpoints.push.RemoveChannelsFromPush +import com.pubnub.api.java.v2.PNConfiguration +import com.pubnub.api.java.v2.callbacks.EventListener +import com.pubnub.api.java.v2.callbacks.StatusListener +import com.pubnub.api.java.v2.endpoints.pubsub.PublishBuilder +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.java.endpoints.DeleteMessagesImpl +import com.pubnub.internal.java.endpoints.FetchMessagesImpl +import com.pubnub.internal.java.endpoints.HistoryImpl +import com.pubnub.internal.java.endpoints.MessageCountsImpl +import com.pubnub.internal.java.endpoints.access.GrantImpl +import com.pubnub.internal.java.endpoints.access.GrantTokenImpl +import com.pubnub.internal.java.endpoints.access.RevokeTokenImpl +import com.pubnub.internal.java.endpoints.channel_groups.AddChannelChannelGroupImpl +import com.pubnub.internal.java.endpoints.channel_groups.AllChannelsChannelGroupImpl +import com.pubnub.internal.java.endpoints.channel_groups.DeleteChannelGroupImpl +import com.pubnub.internal.java.endpoints.channel_groups.RemoveChannelChannelGroupImpl +import com.pubnub.internal.java.endpoints.files.DeleteFileImpl +import com.pubnub.internal.java.endpoints.files.DownloadFileImpl +import com.pubnub.internal.java.endpoints.files.GetFileUrlImpl +import com.pubnub.internal.java.endpoints.files.ListFilesImpl +import com.pubnub.internal.java.endpoints.files.PublishFileMessageImpl +import com.pubnub.internal.java.endpoints.files.SendFileImpl +import com.pubnub.internal.java.endpoints.message_actions.AddMessageActionImpl +import com.pubnub.internal.java.endpoints.message_actions.GetMessageActionsImpl +import com.pubnub.internal.java.endpoints.message_actions.RemoveMessageActionImpl +import com.pubnub.internal.java.endpoints.objects_api.channel.GetAllChannelsMetadataImpl +import com.pubnub.internal.java.endpoints.objects_api.channel.GetChannelMetadataImpl +import com.pubnub.internal.java.endpoints.objects_api.channel.RemoveChannelMetadataImpl +import com.pubnub.internal.java.endpoints.objects_api.channel.SetChannelMetadataImpl +import com.pubnub.internal.java.endpoints.objects_api.members.GetChannelMembersImpl +import com.pubnub.internal.java.endpoints.objects_api.members.ManageChannelMembersImpl +import com.pubnub.internal.java.endpoints.objects_api.members.RemoveChannelMembersImpl +import com.pubnub.internal.java.endpoints.objects_api.members.SetChannelMembersImpl +import com.pubnub.internal.java.endpoints.objects_api.memberships.GetMembershipsImpl +import com.pubnub.internal.java.endpoints.objects_api.memberships.ManageMembershipsImpl +import com.pubnub.internal.java.endpoints.objects_api.memberships.RemoveMembershipsImpl +import com.pubnub.internal.java.endpoints.objects_api.memberships.SetMembershipsImpl +import com.pubnub.internal.java.endpoints.objects_api.uuid.GetAllUUIDMetadataImpl +import com.pubnub.internal.java.endpoints.objects_api.uuid.GetUUIDMetadataImpl +import com.pubnub.internal.java.endpoints.objects_api.uuid.RemoveUUIDMetadataImpl +import com.pubnub.internal.java.endpoints.objects_api.uuid.SetUUIDMetadataImpl +import com.pubnub.internal.java.endpoints.presence.GetStateImpl +import com.pubnub.internal.java.endpoints.presence.HereNowImpl +import com.pubnub.internal.java.endpoints.presence.SetStateImpl +import com.pubnub.internal.java.endpoints.presence.WhereNowImpl +import com.pubnub.internal.java.endpoints.pubsub.PublishImpl +import com.pubnub.internal.java.endpoints.pubsub.SignalImpl +import com.pubnub.internal.java.endpoints.push.AddChannelsToPushImpl +import com.pubnub.internal.java.endpoints.push.ListPushProvisionsImpl +import com.pubnub.internal.java.endpoints.push.RemoveAllPushChannelsForDeviceImpl +import com.pubnub.internal.java.endpoints.push.RemoveChannelsFromPushImpl +import com.pubnub.internal.java.v2.callbacks.DelegatingEventListener +import com.pubnub.internal.java.v2.callbacks.DelegatingStatusListener +import com.pubnub.internal.java.v2.callbacks.EventEmitterInternal +import com.pubnub.internal.java.v2.entities.ChannelGroupImpl +import com.pubnub.internal.java.v2.entities.ChannelImpl +import com.pubnub.internal.java.v2.entities.ChannelMetadataImpl +import com.pubnub.internal.java.v2.entities.UserMetadataImpl +import com.pubnub.internal.v2.entities.ChannelGroupName +import com.pubnub.internal.v2.entities.ChannelName +import com.pubnub.internal.v2.subscription.SubscriptionInternal +import java.io.InputStream + +class PubNubForJavaImpl(configuration: PNConfiguration) : + PubNubImpl( + configuration, + PNSDK_PUBNUB_JAVA + ), + PubNubForJava, + EventEmitterInternal { + override fun subscribe(): SubscribeBuilder { + return SubscribeBuilder(this) + } + + override fun unsubscribe(): UnsubscribeBuilder { + return UnsubscribeBuilder(this) + } + + override fun presence(): PresenceBuilder { + return PresenceBuilder(this) + } + + // start push + override fun addPushNotificationsOnChannels(): AddChannelsToPush { + return AddChannelsToPushImpl(this) + } + + override fun removePushNotificationsFromChannels(): RemoveChannelsFromPush { + return RemoveChannelsFromPushImpl(this) + } + + override fun removeAllPushNotificationsFromDeviceWithPushToken(): RemoveAllPushChannelsForDevice { + return RemoveAllPushChannelsForDeviceImpl(this) + } + + override fun auditPushChannelProvisions(): ListPushProvisions { + return ListPushProvisionsImpl(this) + } + + // end push + override fun whereNow(): WhereNow { + return WhereNowImpl(this) + } + + override fun hereNow(): HereNow { + return HereNowImpl(this) + } + + override fun history(): History { + return HistoryImpl(this) + } + + override fun fetchMessages(): FetchMessages { + return FetchMessagesImpl(this) + } + + override fun deleteMessages(): DeleteMessages { + return DeleteMessagesImpl(this) + } + + override fun messageCounts(): MessageCounts { + return MessageCountsImpl(this) + } + + override fun grant(): Grant { + return GrantImpl(this) + } + + @Deprecated("Use {@link #grantToken(int)} instead.") + override fun grantToken(): GrantTokenBuilder { + return GrantTokenImpl(this) + } + + override fun grantToken(ttl: Int): GrantTokenBuilder { + return GrantTokenImpl(this).ttl(ttl) as GrantTokenBuilder + } + + override fun revokeToken(): RevokeToken { + return RevokeTokenImpl(this) + } + + override fun getPresenceState(): GetState { + return GetStateImpl(this) + } + + override fun setPresenceState(): SetState { + return SetStateImpl(this as PubNubImpl) + } + + override fun publish(): Publish { + return PublishImpl(this) + } + + override fun publish(message: Any, channel: String): PublishBuilder { + return PublishImpl(this).message(message).channel(channel) as PublishBuilder + } + + override fun signal(): Signal { + return SignalImpl(this) + } + + override fun signal(message: Any, channel: String): com.pubnub.api.endpoints.pubsub.Signal { + return super.signal(channel, message) + } + + override fun listChannelsForChannelGroup(): AllChannelsChannelGroup { + return AllChannelsChannelGroupImpl(this) + } + + override fun addChannelsToChannelGroup(): AddChannelChannelGroup { + return AddChannelChannelGroupImpl(this) + } + + override fun removeChannelsFromChannelGroup(): RemoveChannelChannelGroup { + return RemoveChannelChannelGroupImpl(this) + } + + override fun deleteChannelGroup(): DeleteChannelGroup { + return DeleteChannelGroupImpl(this) + } + + // Start Objects API + override fun setUUIDMetadata(): SetUUIDMetadata { + return SetUUIDMetadataImpl(this) + } + + override fun getAllUUIDMetadata(): GetAllUUIDMetadata { + return GetAllUUIDMetadataImpl(this) + } + + override fun getUUIDMetadata(): GetUUIDMetadata { + return GetUUIDMetadataImpl(this) + } + + override fun removeUUIDMetadata(): RemoveUUIDMetadata { + return RemoveUUIDMetadataImpl(this) + } + + override fun setChannelMetadata(): SetChannelMetadata.Builder { + return SetChannelMetadataImpl.Builder(this) + } + + override fun getAllChannelsMetadata(): GetAllChannelsMetadata { + return GetAllChannelsMetadataImpl(this) + } + + override fun getChannelMetadata(): GetChannelMetadata.Builder { + return GetChannelMetadataImpl.Builder(this) + } + + override fun removeChannelMetadata(): RemoveChannelMetadata.Builder { + return RemoveChannelMetadataImpl.Builder(this) + } + + override fun getMemberships(): GetMemberships { + return GetMembershipsImpl(this) + } + + override fun setMemberships(): SetMemberships.Builder { + return SetMembershipsImpl.Builder(this) + } + + override fun removeMemberships(): RemoveMemberships.Builder { + return RemoveMembershipsImpl.Builder(this) + } + + override fun manageMemberships(): ManageMemberships.Builder { + return ManageMembershipsImpl.Builder(this) + } + + override fun getChannelMembers(): GetChannelMembers.Builder { + return GetChannelMembersImpl.Builder(this) + } + + override fun setChannelMembers(): SetChannelMembers.Builder { + return SetChannelMembersImpl.Builder(this) + } + + override fun removeChannelMembers(): RemoveChannelMembers.Builder { + return RemoveChannelMembersImpl.Builder(this) + } + + override fun manageChannelMembers(): ManageChannelMembers.Builder { + return ManageChannelMembersImpl.Builder(this) + } + + // End Objects API + // Start Message Actions API + override fun addMessageAction(): AddMessageAction { + return AddMessageActionImpl(this) + } + + override fun getMessageActions(): GetMessageActions { + return GetMessageActionsImpl(this) + } + + override fun removeMessageAction(): RemoveMessageAction { + return RemoveMessageActionImpl(this) + } + + // End Message Actions API + override fun sendFile(): SendFile.Builder { + return SendFileImpl.Builder(this) + } + + override fun listFiles(): ListFiles.Builder { + return ListFilesImpl.Builder(this) + } + + override fun getFileUrl(): GetFileUrl.Builder { + return GetFileUrlImpl.builder(this) + } + + override fun downloadFile(): DownloadFile.Builder { + return DownloadFileImpl.builder(this) + } + + override fun deleteFile(): DeleteFile.Builder { + return DeleteFileImpl.builder(this) + } + + override fun publishFileMessage(): PublishFileMessage.Builder { + return PublishFileMessageImpl.builder(this) + } + + override fun reconnect() { + reconnect(0) + } + + override fun decrypt(inputString: String): String { + return super.decrypt(inputString) + } + + @Throws(com.pubnub.api.PubNubException::class) + override fun decryptInputStream(inputStream: InputStream): InputStream { + return this.decryptInputStream(inputStream, null) + } + + /** + * Perform Cryptographic encryption of an input string and the cipher key provided by PNConfiguration + * + * @param inputString String to be encrypted + * @return String containing the encryption of inputString using cipherKey + */ + @Throws(com.pubnub.api.PubNubException::class) + override fun encrypt(inputString: String): String { + return this.encrypt(inputString, null) + } + + @Throws(com.pubnub.api.PubNubException::class) + override fun encryptInputStream(inputStream: InputStream): InputStream { + return this.encryptInputStream(inputStream, null) + } + + override fun fire(): Publish { + return publish().shouldStore(false).replicate(false) + } + + override fun fire(message: Any, channel: String): PublishBuilder { + return publish(message, channel).shouldStore(false).replicate(false) + } + + override fun channel(name: String): ChannelImpl { + return ChannelImpl(this, ChannelName(name)) + } + + override fun channelGroup(name: String): ChannelGroupImpl { + return ChannelGroupImpl(this, ChannelGroupName(name)) + } + + override fun channelMetadata(id: String): ChannelMetadataImpl { + return ChannelMetadataImpl(this, ChannelName(id)) + } + + override fun userMetadata(id: String): UserMetadataImpl { + return UserMetadataImpl(this, ChannelName(id)) + } + + override fun subscriptionSetOf(subscriptions: Set): com.pubnub.api.java.v2.subscriptions.SubscriptionSet { + return com.pubnub.internal.java.v2.subscription.SubscriptionSetImpl( + this, + subscriptions as Set + ) + } + + override fun addListener(listener: SubscribeCallback) { + addListener(listener as EventListener) + addListener(listener as StatusListener) + } + + override fun addListener(listener: EventListener) { + addListener(DelegatingEventListener(listener, this)) + } + + override fun addListener(listener: StatusListener) { + addListener(DelegatingStatusListener(listener, this)) + } + + override fun removeListener(listener: Listener) { + if (listener is EventListener) { + super.removeListener(DelegatingEventListener(listener, this)) + } // no else here to support SubscribeCallbacks which implement both interfaces + if (listener is StatusListener) { + super.removeListener(DelegatingStatusListener(listener, this)) + } + } + + override fun removeAllListeners() { + super.removeAllListeners() + } + + companion object { + private const val PNSDK_PUBNUB_JAVA = "PubNub-Java" + } +} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/DelegatingEndpoint.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/DelegatingEndpoint.kt similarity index 62% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/DelegatingEndpoint.kt rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/DelegatingEndpoint.kt index 27ecd442c..c37139bd1 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/DelegatingEndpoint.kt +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/DelegatingEndpoint.kt @@ -1,12 +1,11 @@ -package com.pubnub.internal.endpoints +package com.pubnub.internal.java.endpoints -import com.pubnub.api.endpoints.Endpoint +import com.pubnub.api.PubNub import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction +import com.pubnub.api.java.endpoints.Endpoint import com.pubnub.api.v2.PNConfiguration -import com.pubnub.internal.EndpointInterface -import com.pubnub.internal.PubNubCore -abstract class DelegatingEndpoint(pubnub: PubNubCore) : DelegatingRemoteAction(pubnub), Endpoint { +abstract class DelegatingEndpoint(pubnub: PubNub) : DelegatingRemoteAction(pubnub), Endpoint { override val remoteAction: ExtendedRemoteAction by lazy { val newAction = createAction() overrideConfiguration?.let { overrideConfigNonNull -> @@ -21,10 +20,10 @@ abstract class DelegatingEndpoint(pubnub: PubNubCore) : DelegatingRemoteAc return this } - abstract override fun createAction(): EndpointInterface + abstract override fun createAction(): com.pubnub.api.Endpoint } -abstract class IdentityMappingEndpoint(pubnub: PubNubCore) : DelegatingEndpoint(pubnub) { +abstract class IdentityMappingEndpoint(pubnub: PubNub) : DelegatingEndpoint(pubnub) { final override fun mapResult(action: ExtendedRemoteAction): ExtendedRemoteAction { return action } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/DelegatingRemoteAction.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/DelegatingRemoteAction.kt similarity index 87% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/DelegatingRemoteAction.kt rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/DelegatingRemoteAction.kt index 4eadd828d..43aee109c 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/DelegatingRemoteAction.kt +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/DelegatingRemoteAction.kt @@ -1,15 +1,15 @@ -package com.pubnub.internal.endpoints +package com.pubnub.internal.java.endpoints +import com.pubnub.api.PubNub import com.pubnub.api.PubNubException import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction import com.pubnub.api.enums.PNOperationType import com.pubnub.api.v2.callbacks.Result -import com.pubnub.internal.PubNubCore import org.jetbrains.annotations.TestOnly import java.util.function.Consumer abstract class DelegatingRemoteAction( - @JvmField protected val pubnub: PubNubCore, + @JvmField protected val pubnub: PubNub, ) : ExtendedRemoteAction { @get:TestOnly internal open val remoteAction: ExtendedRemoteAction by lazy { @@ -56,7 +56,7 @@ abstract class DelegatingRemoteAction( get() = operationType() } -abstract class IdentityMappingAction(pubnub: PubNubCore) : DelegatingRemoteAction(pubnub) { +abstract class IdentityMappingAction(pubnub: PubNub) : DelegatingRemoteAction(pubnub) { final override fun mapResult(action: ExtendedRemoteAction): ExtendedRemoteAction { return action } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/DeleteMessagesImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/DeleteMessagesImpl.java similarity index 71% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/DeleteMessagesImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/DeleteMessagesImpl.java index d00f07124..904783f4c 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/DeleteMessagesImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/DeleteMessagesImpl.java @@ -1,11 +1,11 @@ -package com.pubnub.internal.endpoints; +package com.pubnub.internal.java.endpoints; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.DeleteMessages; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.DeleteMessages; import com.pubnub.api.models.consumer.history.PNDeleteMessagesResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -20,12 +20,12 @@ public class DeleteMessagesImpl extends IdentityMappingEndpoint createAction() { + protected @NotNull Endpoint createAction() { return pubnub.deleteMessages(channels, start, end); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/FetchMessagesImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/FetchMessagesImpl.java similarity index 89% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/FetchMessagesImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/FetchMessagesImpl.java index 57ee55a13..037ddbfd1 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/FetchMessagesImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/FetchMessagesImpl.java @@ -1,11 +1,12 @@ -package com.pubnub.internal.endpoints; +package com.pubnub.internal.java.endpoints; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.FetchMessages; import com.pubnub.api.models.consumer.PNBoundedPage; import com.pubnub.api.models.consumer.history.PNFetchMessagesResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; import lombok.Setter; import lombok.experimental.Accessors; import lombok.extern.slf4j.Slf4j; @@ -17,7 +18,7 @@ @Setter @Slf4j @Accessors(chain = true, fluent = true) -public class FetchMessagesImpl extends IdentityMappingEndpoint implements com.pubnub.api.endpoints.FetchMessages { +public class FetchMessagesImpl extends IdentityMappingEndpoint implements FetchMessages { private static final int SINGLE_CHANNEL_DEFAULT_MESSAGES = 100; private static final int SINGLE_CHANNEL_MAX_MESSAGES = 100; private static final int MULTIPLE_CHANNEL_DEFAULT_MESSAGES = 25; @@ -35,13 +36,13 @@ public class FetchMessagesImpl extends IdentityMappingEndpoint createAction() { + protected Endpoint createAction() { return pubnub.fetchMessages( channels, new PNBoundedPage(start, end, maximumPerChannel), diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/HistoryImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/HistoryImpl.java similarity index 73% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/HistoryImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/HistoryImpl.java index e08854df8..54c9e08a1 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/HistoryImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/HistoryImpl.java @@ -1,10 +1,10 @@ -package com.pubnub.internal.endpoints; +package com.pubnub.internal.java.endpoints; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.java.builder.PubNubErrorBuilder; import com.pubnub.api.models.consumer.history.PNHistoryResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; import lombok.Setter; import lombok.experimental.Accessors; import lombok.extern.slf4j.Slf4j; @@ -13,7 +13,7 @@ @Setter @Slf4j @Accessors(chain = true, fluent = true) -public class HistoryImpl extends IdentityMappingEndpoint implements com.pubnub.api.endpoints.History { +public class HistoryImpl extends IdentityMappingEndpoint implements com.pubnub.api.java.endpoints.History { private String channel; private Long start; private Long end; @@ -22,13 +22,13 @@ public class HistoryImpl extends IdentityMappingEndpoint implem private boolean includeTimetoken; private boolean includeMeta; - public HistoryImpl(PubNubCore pubnub) { + public HistoryImpl(PubNub pubnub) { super(pubnub); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.history(channel, start, end, count, reverse, includeTimetoken, includeMeta); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/MessageCountsImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/MessageCountsImpl.java similarity index 79% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/MessageCountsImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/MessageCountsImpl.java index 586ae2d6b..379fe6125 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/MessageCountsImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/MessageCountsImpl.java @@ -1,11 +1,11 @@ -package com.pubnub.internal.endpoints; +package com.pubnub.internal.java.endpoints; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.MessageCounts; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.MessageCounts; import com.pubnub.api.models.consumer.history.PNMessageCountResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -28,13 +28,13 @@ public class MessageCountsImpl extends IdentityMappingEndpoint channelsTimetoken; - public MessageCountsImpl(PubNubCore pubnub) { + public MessageCountsImpl(PubNub pubnub) { super(pubnub); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.messageCounts(channels, channelsTimetoken); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/access/GrantImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/access/GrantImpl.java similarity index 59% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/access/GrantImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/access/GrantImpl.java index cf626411e..4fc78cc24 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/access/GrantImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/access/GrantImpl.java @@ -1,12 +1,12 @@ -package com.pubnub.internal.endpoints.access; +package com.pubnub.internal.java.endpoints.access; -import com.pubnub.api.endpoints.access.Grant; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; -import com.pubnub.api.models.consumer.access_manager.PNAccessManagerGrantResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; +import com.pubnub.api.java.endpoints.access.Grant; +import com.pubnub.api.java.models.consumer.access_manager.PNAccessManagerGrantResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -18,7 +18,7 @@ @Setter @Accessors(chain = true, fluent = true) -public class GrantImpl extends DelegatingEndpoint implements Grant { +public class GrantImpl extends DelegatingEndpoint implements Grant { private boolean read; private boolean write; @@ -34,13 +34,13 @@ public class GrantImpl extends DelegatingEndpoint channelGroups = new ArrayList<>(); private List uuids = Collections.emptyList(); - public GrantImpl(PubNubCore pubnub) { + public GrantImpl(PubNub pubnub) { super(pubnub); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.grant( read, write, @@ -59,7 +59,7 @@ protected EndpointInterface mapResult(@NotNull ExtendedRemoteAction action) { - return new MappingRemoteAction<>(action, PNAccessManagerGrantResult::from); + protected ExtendedRemoteAction mapResult(@NotNull ExtendedRemoteAction action) { + return new MappingRemoteAction<>(action, com.pubnub.api.java.models.consumer.access_manager.PNAccessManagerGrantResult::from); } } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/access/GrantTokenImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/access/GrantTokenImpl.java similarity index 67% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/access/GrantTokenImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/access/GrantTokenImpl.java index edd52e669..44f7054b6 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/access/GrantTokenImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/access/GrantTokenImpl.java @@ -1,21 +1,21 @@ -package com.pubnub.internal.endpoints.access; +package com.pubnub.internal.java.endpoints.access; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; import com.pubnub.api.UserId; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.access.GrantToken; -import com.pubnub.api.endpoints.access.builder.GrantTokenBuilder; -import com.pubnub.api.endpoints.access.builder.GrantTokenEntitiesBuilder; -import com.pubnub.api.endpoints.access.builder.GrantTokenObjectsBuilder; -import com.pubnub.api.models.consumer.access_manager.sum.SpacePermissions; -import com.pubnub.api.models.consumer.access_manager.sum.UserPermissions; -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant; -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.access.GrantToken; +import com.pubnub.api.java.endpoints.access.builder.GrantTokenBuilder; +import com.pubnub.api.java.endpoints.access.builder.GrantTokenEntitiesBuilder; +import com.pubnub.api.java.endpoints.access.builder.GrantTokenObjectsBuilder; +import com.pubnub.api.java.models.consumer.access_manager.sum.SpacePermissions; +import com.pubnub.api.java.models.consumer.access_manager.sum.UserPermissions; +import com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGrant; +import com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGroupGrant; +import com.pubnub.api.java.models.consumer.access_manager.v3.UUIDGrant; import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult; -import com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -35,7 +35,7 @@ public class GrantTokenImpl extends IdentityMappingEndpoint private List channelGroups = Collections.emptyList(); private List uuids = Collections.emptyList(); - public GrantTokenImpl(PubNubCore pubnub) { + public GrantTokenImpl(PubNub pubnub) { super(pubnub); } @@ -48,7 +48,7 @@ protected void validateParams() throws PubNubException { @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.grantToken( ttl, meta, @@ -129,33 +129,33 @@ public GrantTokenEntitiesBuilder authorizedUserId(UserId userId) { return this; } - private List toInternalChannels(List channels) { - ArrayList list = new ArrayList<>(channels.size()); + private List toInternalChannels(List channels) { + ArrayList list = new ArrayList<>(channels.size()); for (ChannelGrant channel : channels) { list.add(toInternal(channel)); } return list; } - private List toInternalChannelGroups(List channels) { - ArrayList list = new ArrayList<>(channels.size()); + private List toInternalChannelGroups(List channels) { + ArrayList list = new ArrayList<>(channels.size()); for (ChannelGroupGrant channel : channels) { list.add(toInternal(channel)); } return list; } - private List toInternalUuids(List uuids) { - ArrayList list = new ArrayList<>(uuids.size()); + private List toInternalUuids(List uuids) { + ArrayList list = new ArrayList<>(uuids.size()); for (UUIDGrant uuid : uuids) { list.add(toInternal(uuid)); } return list; } - static com.pubnub.internal.models.consumer.access_manager.v3.ChannelGrant toInternal(ChannelGrant grant) { + static com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant toInternal(ChannelGrant grant) { if (grant.isPatternResource()) { - return com.pubnub.internal.models.consumer.access_manager.v3.ChannelGrant.Companion.pattern( + return com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant.Companion.pattern( grant.getId(), grant.isRead(), grant.isWrite(), @@ -167,7 +167,7 @@ static com.pubnub.internal.models.consumer.access_manager.v3.ChannelGrant toInte grant.isUpdate() ); } else { - return com.pubnub.internal.models.consumer.access_manager.v3.ChannelGrant.Companion.name( + return com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant.Companion.name( grant.getId(), grant.isRead(), grant.isWrite(), @@ -181,15 +181,15 @@ static com.pubnub.internal.models.consumer.access_manager.v3.ChannelGrant toInte } } - static com.pubnub.internal.models.consumer.access_manager.v3.ChannelGroupGrant toInternal(ChannelGroupGrant grant) { + static com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant toInternal(ChannelGroupGrant grant) { if (grant.isPatternResource()) { - return com.pubnub.internal.models.consumer.access_manager.v3.ChannelGroupGrant.Companion.pattern( + return com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant.Companion.pattern( grant.getId(), grant.isRead(), grant.isManage() ); } else { - return com.pubnub.internal.models.consumer.access_manager.v3.ChannelGroupGrant.Companion.id( + return com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant.Companion.id( grant.getId(), grant.isRead(), grant.isManage() @@ -197,16 +197,16 @@ static com.pubnub.internal.models.consumer.access_manager.v3.ChannelGroupGrant t } } - static com.pubnub.internal.models.consumer.access_manager.v3.UUIDGrant toInternal(UUIDGrant grant) { + static com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant toInternal(UUIDGrant grant) { if (grant.isPatternResource()) { - return com.pubnub.internal.models.consumer.access_manager.v3.UUIDGrant.Companion.pattern( + return com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant.Companion.pattern( grant.getId(), grant.isGet(), grant.isUpdate(), grant.isDelete() ); } else { - return com.pubnub.internal.models.consumer.access_manager.v3.UUIDGrant.Companion.id( + return com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant.Companion.id( grant.getId(), grant.isGet(), grant.isUpdate(), diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/access/RevokeTokenImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/access/RevokeTokenImpl.java similarity index 61% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/access/RevokeTokenImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/access/RevokeTokenImpl.java index a02bb19f6..64b15803e 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/access/RevokeTokenImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/access/RevokeTokenImpl.java @@ -1,11 +1,11 @@ -package com.pubnub.internal.endpoints.access; +package com.pubnub.internal.java.endpoints.access; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.access.RevokeToken; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.access.RevokeToken; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import kotlin.Unit; import lombok.Setter; import lombok.experimental.Accessors; @@ -17,13 +17,13 @@ public class RevokeTokenImpl extends IdentityMappingEndpoint implements Re private String token; - public RevokeTokenImpl(PubNubCore pubnub) { + public RevokeTokenImpl(PubNub pubnub) { super(pubnub); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.revokeToken(token); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/AddChannelChannelGroupImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/channel_groups/AddChannelChannelGroupImpl.java similarity index 66% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/AddChannelChannelGroupImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/channel_groups/AddChannelChannelGroupImpl.java index 2f1dde3ff..abb6b5797 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/AddChannelChannelGroupImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/channel_groups/AddChannelChannelGroupImpl.java @@ -1,12 +1,12 @@ -package com.pubnub.internal.endpoints.channel_groups; +package com.pubnub.internal.java.endpoints.channel_groups; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.channel_groups.AddChannelChannelGroup; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.channel_groups.AddChannelChannelGroup; import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAddChannelResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -20,13 +20,13 @@ public class AddChannelChannelGroupImpl extends IdentityMappingEndpoint channels = new ArrayList<>(); - public AddChannelChannelGroupImpl(PubNubCore pubnub) { + public AddChannelChannelGroupImpl(PubNub pubnub) { super(pubnub); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.addChannelsToChannelGroup(channels, channelGroup); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/AllChannelsChannelGroupImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/channel_groups/AllChannelsChannelGroupImpl.java similarity index 63% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/AllChannelsChannelGroupImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/channel_groups/AllChannelsChannelGroupImpl.java index 43af03ffd..5431f52a8 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/AllChannelsChannelGroupImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/channel_groups/AllChannelsChannelGroupImpl.java @@ -1,12 +1,12 @@ -package com.pubnub.internal.endpoints.channel_groups; +package com.pubnub.internal.java.endpoints.channel_groups; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.channel_groups.AllChannelsChannelGroup; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.channel_groups.AllChannelsChannelGroup; import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAllChannelsResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -16,13 +16,13 @@ public class AllChannelsChannelGroupImpl extends IdentityMappingEndpoint implements AllChannelsChannelGroup { private String channelGroup; - public AllChannelsChannelGroupImpl(PubNubCore pubnub) { + public AllChannelsChannelGroupImpl(PubNub pubnub) { super(pubnub); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.listChannelsForChannelGroup(channelGroup); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/DeleteChannelGroupImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/channel_groups/DeleteChannelGroupImpl.java similarity index 61% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/DeleteChannelGroupImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/channel_groups/DeleteChannelGroupImpl.java index dc2a8c2c8..4edb14dfa 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/DeleteChannelGroupImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/channel_groups/DeleteChannelGroupImpl.java @@ -1,12 +1,12 @@ -package com.pubnub.internal.endpoints.channel_groups; +package com.pubnub.internal.java.endpoints.channel_groups; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.channel_groups.DeleteChannelGroup; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.channel_groups.DeleteChannelGroup; import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsDeleteGroupResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; @@ -15,12 +15,12 @@ public class DeleteChannelGroupImpl extends IdentityMappingEndpoint implements DeleteChannelGroup { private String channelGroup; - public DeleteChannelGroupImpl(PubNubCore pubnub) { + public DeleteChannelGroupImpl(PubNub pubnub) { super(pubnub); } @Override - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.deleteChannelGroup(channelGroup); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/RemoveChannelChannelGroupImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/channel_groups/RemoveChannelChannelGroupImpl.java similarity index 66% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/RemoveChannelChannelGroupImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/channel_groups/RemoveChannelChannelGroupImpl.java index 419135d65..a53cc9778 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/channel_groups/RemoveChannelChannelGroupImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/channel_groups/RemoveChannelChannelGroupImpl.java @@ -1,12 +1,12 @@ -package com.pubnub.internal.endpoints.channel_groups; +package com.pubnub.internal.java.endpoints.channel_groups; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.channel_groups.RemoveChannelChannelGroup; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.channel_groups.RemoveChannelChannelGroup; import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsRemoveChannelResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -21,13 +21,13 @@ public class RemoveChannelChannelGroupImpl extends IdentityMappingEndpoint channels = new ArrayList<>(); - public RemoveChannelChannelGroupImpl(PubNubCore pubnub) { + public RemoveChannelChannelGroupImpl(PubNub pubnub) { super(pubnub); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.removeChannelsFromChannelGroup(channels, channelGroup); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/DeleteFileImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/DeleteFileImpl.java similarity index 59% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/DeleteFileImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/DeleteFileImpl.java index a6293c1f4..3d2788b69 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/DeleteFileImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/DeleteFileImpl.java @@ -1,16 +1,16 @@ -package com.pubnub.internal.endpoints.files; +package com.pubnub.internal.java.endpoints.files; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.BuilderSteps.ChannelStep; -import com.pubnub.api.endpoints.files.DeleteFile; -import com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps.FileIdStep; -import com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps.FileNameStep; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.BuilderSteps.ChannelStep; +import com.pubnub.api.java.endpoints.files.DeleteFile; +import com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps.FileIdStep; +import com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps.FileNameStep; import com.pubnub.api.models.consumer.files.PNDeleteFileResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; -import com.pubnub.internal.endpoints.files.requiredparambuilder.ChannelFileNameFileIdBuilder; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.files.requiredparambuilder.ChannelFileNameFileIdBuilder; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -21,7 +21,7 @@ public class DeleteFileImpl extends IdentityMappingEndpoint private final String fileId; private final String fileName; - public DeleteFileImpl(String channel, String fileName, String fileId, PubNubCore pubnub) { + public DeleteFileImpl(String channel, String fileName, String fileId, PubNub pubnub) { super(pubnub); this.channel = channel; this.fileName = fileName; @@ -30,7 +30,7 @@ public DeleteFileImpl(String channel, String fileName, String fileId, PubNubCore @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.deleteFile( channel, fileName, @@ -38,8 +38,8 @@ protected EndpointInterface createAction() { ); } - public static DeleteFileImpl.Builder builder(PubNubCore pubnub) { - return new DeleteFileImpl.Builder(ChannelFileNameFileIdBuilder.create((channel, fileName, fileId) -> + public static Builder builder(PubNub pubnub) { + return new Builder(ChannelFileNameFileIdBuilder.create((channel, fileName, fileId) -> new DeleteFileImpl(channel, fileName, fileId, pubnub))); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/DownloadFileImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/DownloadFileImpl.java similarity index 64% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/DownloadFileImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/DownloadFileImpl.java index f84f5de70..02f4746d6 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/DownloadFileImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/DownloadFileImpl.java @@ -1,15 +1,15 @@ -package com.pubnub.internal.endpoints.files; +package com.pubnub.internal.java.endpoints.files; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.files.DownloadFile; -import com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.files.DownloadFile; +import com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps; import com.pubnub.api.models.consumer.files.PNDownloadFileResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; -import com.pubnub.internal.endpoints.files.requiredparambuilder.ChannelFileNameFileIdBuilder; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.files.requiredparambuilder.ChannelFileNameFileIdBuilder; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -24,7 +24,7 @@ public class DownloadFileImpl extends IdentityMappingEndpoint createAction() { + protected Endpoint createAction() { return pubnub.downloadFile( channel, fileName, @@ -42,8 +42,8 @@ protected EndpointInterface createAction() { ); } - public static DownloadFileImpl.Builder builder(PubNubCore pubnub) { - return new DownloadFileImpl.Builder(ChannelFileNameFileIdBuilder.create((channel, fileName, fileId) -> + public static Builder builder(PubNub pubnub) { + return new Builder(ChannelFileNameFileIdBuilder.create((channel, fileName, fileId) -> new DownloadFileImpl(channel, fileId, fileName, pubnub))); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/GetFileUrlImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/GetFileUrlImpl.java similarity index 61% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/GetFileUrlImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/GetFileUrlImpl.java index 435580c1d..68d6e4225 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/GetFileUrlImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/GetFileUrlImpl.java @@ -1,15 +1,15 @@ -package com.pubnub.internal.endpoints.files; +package com.pubnub.internal.java.endpoints.files; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.files.GetFileUrl; -import com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.files.GetFileUrl; +import com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps; import com.pubnub.api.models.consumer.files.PNFileUrlResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; -import com.pubnub.internal.endpoints.files.requiredparambuilder.ChannelFileNameFileIdBuilder; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.files.requiredparambuilder.ChannelFileNameFileIdBuilder; import org.jetbrains.annotations.NotNull; public class GetFileUrlImpl extends IdentityMappingEndpoint implements GetFileUrl { @@ -18,7 +18,7 @@ public class GetFileUrlImpl extends IdentityMappingEndpoint imp private final String fileId; private final String fileName; - public GetFileUrlImpl(String channel, String fileId, String fileName, PubNubCore pubnub) { + public GetFileUrlImpl(String channel, String fileId, String fileName, PubNub pubnub) { super(pubnub); this.channel = channel; this.fileId = fileId; @@ -27,7 +27,7 @@ public GetFileUrlImpl(String channel, String fileId, String fileName, PubNubCore @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.getFileUrl( channel, fileName, @@ -41,8 +41,8 @@ private Builder(BuilderSteps.ChannelStep + public static Builder builder(PubNub pubNub) { + return new Builder(ChannelFileNameFileIdBuilder.create((channel, fileName, fileId) -> new GetFileUrlImpl(channel, fileName, fileId, pubNub))); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/ListFilesImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/ListFilesImpl.java similarity index 75% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/ListFilesImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/ListFilesImpl.java index 0acaaf4e4..81eb5e3eb 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/ListFilesImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/ListFilesImpl.java @@ -1,13 +1,13 @@ -package com.pubnub.internal.endpoints.files; +package com.pubnub.internal.java.endpoints.files; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.files.ListFiles; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.files.ListFiles; import com.pubnub.api.models.consumer.files.PNListFilesResult; import com.pubnub.api.models.consumer.objects.PNPage; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -22,14 +22,14 @@ public class ListFilesImpl extends IdentityMappingEndpoint im @Setter private PNPage.PNNext next; - public ListFilesImpl(String channel, PubNubCore pubnub) { + public ListFilesImpl(String channel, PubNub pubnub) { super(pubnub); this.channel = channel; } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.listFiles( channel, limit, @@ -39,9 +39,9 @@ protected EndpointInterface createAction() { public static class Builder implements ListFiles.Builder { - private final PubNubCore pubnubInstance; + private final PubNub pubnubInstance; - public Builder(PubNubCore pubnubInstance) { + public Builder(PubNub pubnubInstance) { this.pubnubInstance = pubnubInstance; } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/PublishFileMessageImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/PublishFileMessageImpl.java similarity index 71% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/PublishFileMessageImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/PublishFileMessageImpl.java index aff5aadb6..b68d75baf 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/PublishFileMessageImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/PublishFileMessageImpl.java @@ -1,15 +1,15 @@ -package com.pubnub.internal.endpoints.files; +package com.pubnub.internal.java.endpoints.files; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.files.PublishFileMessage; -import com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.files.PublishFileMessage; +import com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps; import com.pubnub.api.models.consumer.files.PNPublishFileMessageResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; -import com.pubnub.internal.endpoints.files.requiredparambuilder.ChannelFileNameFileIdBuilder; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.files.requiredparambuilder.ChannelFileNameFileIdBuilder; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -31,7 +31,7 @@ public class PublishFileMessageImpl extends IdentityMappingEndpoint createAction() { + protected Endpoint createAction() { return pubnub.publishFileMessage( channel, fileName, @@ -52,7 +52,7 @@ protected EndpointInterface createAction() { ); } - public static PublishFileMessage.Builder builder(PubNubCore pubNub) { + public static PublishFileMessage.Builder builder(PubNub pubNub) { return new Builder(ChannelFileNameFileIdBuilder.create((channel, fileName, fileId) -> new PublishFileMessageImpl(channel, fileName, fileId, pubNub))); } @@ -70,4 +70,4 @@ protected void validateParams() throws PubNubException { throw new PubNubException(PubNubErrorBuilder.PNERROBJ_CHANNEL_MISSING); } } - } \ No newline at end of file +} \ No newline at end of file diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/SendFileImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/SendFileImpl.java similarity index 81% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/SendFileImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/SendFileImpl.java index d2a3b2c00..edab9705f 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/SendFileImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/SendFileImpl.java @@ -1,12 +1,12 @@ -package com.pubnub.internal.endpoints.files; +package com.pubnub.internal.java.endpoints.files; -import com.pubnub.api.endpoints.BuilderSteps; -import com.pubnub.api.endpoints.files.SendFile; -import com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; +import com.pubnub.api.java.endpoints.BuilderSteps; +import com.pubnub.api.java.endpoints.files.SendFile; +import com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps; import com.pubnub.api.models.consumer.files.PNFileUploadResult; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingAction; +import com.pubnub.internal.java.endpoints.IdentityMappingAction; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -32,7 +32,7 @@ public class SendFileImpl extends IdentityMappingAction impl @Setter private String cipherKey; - public SendFileImpl(PubNubCore pubnub, String channel, String fileName, InputStream inputStream) { + public SendFileImpl(PubNub pubnub, String channel, String fileName, InputStream inputStream) { super(pubnub); this.channel = channel; this.fileName = fileName; @@ -56,9 +56,9 @@ protected ExtendedRemoteAction createAction() { public static class Builder implements SendFile.Builder { - private final PubNubCore pubnub; + private final PubNub pubnub; - public Builder(PubNubCore pubnub) { + public Builder(PubNub pubnub) { this.pubnub = pubnub; } @@ -71,11 +71,11 @@ public static class InnerBuilder implements BuilderSteps.ChannelStep>>, FilesBuilderSteps.FileNameStep>, FilesBuilderSteps.InputStreamStep { - private final PubNubCore pubnub; + private final PubNub pubnub; private String channelValue; private String fileNameValue; - private InnerBuilder(PubNubCore pubnub) { + private InnerBuilder(PubNub pubnub) { this.pubnub = pubnub; } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/requiredparambuilder/ChannelFileNameFileIdBuilder.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/requiredparambuilder/ChannelFileNameFileIdBuilder.java similarity index 82% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/requiredparambuilder/ChannelFileNameFileIdBuilder.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/requiredparambuilder/ChannelFileNameFileIdBuilder.java index 2c4d50b12..c757678da 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/files/requiredparambuilder/ChannelFileNameFileIdBuilder.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/files/requiredparambuilder/ChannelFileNameFileIdBuilder.java @@ -1,8 +1,8 @@ -package com.pubnub.internal.endpoints.files.requiredparambuilder; +package com.pubnub.internal.java.endpoints.files.requiredparambuilder; -import com.pubnub.api.endpoints.BuilderSteps.ChannelStep; -import com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps.FileIdStep; -import com.pubnub.api.endpoints.files.requiredparambuilder.FilesBuilderSteps.FileNameStep; +import com.pubnub.api.java.endpoints.BuilderSteps.ChannelStep; +import com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps.FileIdStep; +import com.pubnub.api.java.endpoints.files.requiredparambuilder.FilesBuilderSteps.FileNameStep; import kotlin.jvm.functions.Function3; public abstract class ChannelFileNameFileIdBuilder implements diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/message_actions/AddMessageActionImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/message_actions/AddMessageActionImpl.java similarity index 63% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/message_actions/AddMessageActionImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/message_actions/AddMessageActionImpl.java index bf1ea7170..dd669f725 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/message_actions/AddMessageActionImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/message_actions/AddMessageActionImpl.java @@ -1,20 +1,20 @@ -package com.pubnub.internal.endpoints.message_actions; +package com.pubnub.internal.java.endpoints.message_actions; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.endpoints.message_actions.AddMessageAction; +import com.pubnub.api.java.endpoints.message_actions.AddMessageAction; import com.pubnub.api.models.consumer.message_actions.PNAddMessageActionResult; import com.pubnub.api.models.consumer.message_actions.PNMessageAction; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; -import static com.pubnub.api.builder.PubNubErrorBuilder.PNERROBJ_CHANNEL_MISSING; -import static com.pubnub.api.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_ACTION_MISSING; -import static com.pubnub.api.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_ACTION_VALUE_MISSING; -import static com.pubnub.api.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_TIMETOKEN_MISSING; +import static com.pubnub.api.java.builder.PubNubErrorBuilder.PNERROBJ_CHANNEL_MISSING; +import static com.pubnub.api.java.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_ACTION_MISSING; +import static com.pubnub.api.java.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_ACTION_VALUE_MISSING; +import static com.pubnub.api.java.builder.PubNubErrorBuilder.PNERROBJ_MESSAGE_TIMETOKEN_MISSING; @Setter @Accessors(chain = true, fluent = true) @@ -23,13 +23,13 @@ public class AddMessageActionImpl extends IdentityMappingEndpoint createAction() { + protected Endpoint createAction() { return pubnub.addMessageAction(channel, messageAction); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/message_actions/GetMessageActionsImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/message_actions/GetMessageActionsImpl.java similarity index 70% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/message_actions/GetMessageActionsImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/message_actions/GetMessageActionsImpl.java index dabf0d800..f036e424f 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/message_actions/GetMessageActionsImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/message_actions/GetMessageActionsImpl.java @@ -1,13 +1,13 @@ -package com.pubnub.internal.endpoints.message_actions; +package com.pubnub.internal.java.endpoints.message_actions; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubError; import com.pubnub.api.PubNubException; -import com.pubnub.api.endpoints.message_actions.GetMessageActions; +import com.pubnub.api.java.endpoints.message_actions.GetMessageActions; import com.pubnub.api.models.consumer.PNBoundedPage; import com.pubnub.api.models.consumer.message_actions.PNGetMessageActionsResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -21,7 +21,7 @@ public class GetMessageActionsImpl extends IdentityMappingEndpoint createAction() { + protected Endpoint createAction() { return pubnub.getMessageActions(channel, new PNBoundedPage(start, end, limit)); } } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/message_actions/RemoveMessageActionImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/message_actions/RemoveMessageActionImpl.java similarity index 72% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/message_actions/RemoveMessageActionImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/message_actions/RemoveMessageActionImpl.java index 8f3b4f06b..1959d005e 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/message_actions/RemoveMessageActionImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/message_actions/RemoveMessageActionImpl.java @@ -1,12 +1,12 @@ -package com.pubnub.internal.endpoints.message_actions; +package com.pubnub.internal.java.endpoints.message_actions; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubError; import com.pubnub.api.PubNubException; -import com.pubnub.api.endpoints.message_actions.RemoveMessageAction; +import com.pubnub.api.java.endpoints.message_actions.RemoveMessageAction; import com.pubnub.api.models.consumer.message_actions.PNRemoveMessageActionResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; @@ -18,12 +18,12 @@ public class RemoveMessageActionImpl extends IdentityMappingEndpoint createAction() { + protected Endpoint createAction() { return pubnub.removeMessageAction(channel, messageTimetoken, actionTimetoken); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/channel/GetAllChannelsMetadataImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/channel/GetAllChannelsMetadataImpl.java similarity index 57% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/channel/GetAllChannelsMetadataImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/channel/GetAllChannelsMetadataImpl.java index d34045b9e..47c949588 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/channel/GetAllChannelsMetadataImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/channel/GetAllChannelsMetadataImpl.java @@ -1,16 +1,16 @@ -package com.pubnub.internal.endpoints.objects_api.channel; +package com.pubnub.internal.java.endpoints.objects_api.channel; -import com.pubnub.api.endpoints.objects_api.channel.GetAllChannelsMetadata; -import com.pubnub.api.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; +import com.pubnub.api.java.endpoints.objects_api.channel.GetAllChannelsMetadata; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNGetAllChannelsMetadataResult; +import com.pubnub.api.models.consumer.objects.PNKey; import com.pubnub.api.models.consumer.objects.PNPage; -import com.pubnub.internal.models.consumer.objects.channel.PNChannelMetadataArrayResult; -import com.pubnub.api.models.consumer.objects_api.channel.PNGetAllChannelsMetadataResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.PNKey; +import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadataArrayResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -32,13 +32,13 @@ public class GetAllChannelsMetadataImpl private boolean includeTotalCount; private boolean includeCustom; - public GetAllChannelsMetadataImpl(final PubNubCore pubnubInstance) { + public GetAllChannelsMetadataImpl(final PubNub pubnubInstance) { super(pubnubInstance); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.getAllChannelMetadata( limit, page, @@ -55,9 +55,9 @@ protected ExtendedRemoteAction mapResult(@NotNul return new MappingRemoteAction<>(action, PNGetAllChannelsMetadataResult::from); } - public static Collection> toInternal(Collection sort) { - List> list = new ArrayList<>(sort.size()); - for (com.pubnub.api.endpoints.objects_api.utils.PNSortKey pnSortKey : sort) { + public static Collection> toInternal(Collection sort) { + List> list = new ArrayList<>(sort.size()); + for (PNSortKey pnSortKey : sort) { PNKey key; switch (pnSortKey.getKey()) { case ID: @@ -72,10 +72,10 @@ public static Collection(key)); + if (pnSortKey.getDir().equals(PNSortKey.Dir.ASC)) { + list.add(new com.pubnub.api.models.consumer.objects.PNSortKey.PNAsc<>(key)); } else { - list.add(new com.pubnub.internal.models.consumer.objects.PNSortKey.PNDesc<>(key)); + list.add(new com.pubnub.api.models.consumer.objects.PNSortKey.PNDesc<>(key)); } } return list; diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/channel/GetChannelMetadataImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/channel/GetChannelMetadataImpl.java similarity index 67% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/channel/GetChannelMetadataImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/channel/GetChannelMetadataImpl.java index 8d186feb5..58dcf71ce 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/channel/GetChannelMetadataImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/channel/GetChannelMetadataImpl.java @@ -1,22 +1,21 @@ -package com.pubnub.internal.endpoints.objects_api.channel; +package com.pubnub.internal.java.endpoints.objects_api.channel; -import com.pubnub.api.endpoints.objects_api.channel.GetChannelMetadata; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; +import com.pubnub.api.java.endpoints.objects_api.channel.GetChannelMetadata; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadata; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNGetChannelMetadataResult; import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadata; -import com.pubnub.api.models.consumer.objects_api.channel.PNGetChannelMetadataResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import lombok.AllArgsConstructor; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @Accessors(chain = true, fluent = true) public class GetChannelMetadataImpl extends DelegatingEndpoint implements GetChannelMetadata { - public GetChannelMetadataImpl(String channel, final PubNubCore pubnubInstance) { + public GetChannelMetadataImpl(String channel, final PubNub pubnubInstance) { super(pubnubInstance); this.channel = channel; } @@ -39,13 +38,16 @@ protected ExtendedRemoteAction mapResult(@NotNull Ex @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.getChannelMetadata(channel, includeCustom); } - @AllArgsConstructor public static class Builder implements GetChannelMetadata.Builder { - private final PubNubCore pubnubInstance; + private final PubNub pubnubInstance; + + public Builder(PubNub pubnubInstance) { + this.pubnubInstance = pubnubInstance; + } @Override public GetChannelMetadata channel(final String channel) { diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/channel/RemoveChannelMetadataImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/channel/RemoveChannelMetadataImpl.java similarity index 64% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/channel/RemoveChannelMetadataImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/channel/RemoveChannelMetadataImpl.java index 5fb00933b..2a9ea036d 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/channel/RemoveChannelMetadataImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/channel/RemoveChannelMetadataImpl.java @@ -1,18 +1,17 @@ -package com.pubnub.internal.endpoints.objects_api.channel; +package com.pubnub.internal.java.endpoints.objects_api.channel; -import com.pubnub.api.endpoints.objects_api.channel.RemoveChannelMetadata; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; -import com.pubnub.api.models.consumer.objects_api.channel.PNRemoveChannelMetadataResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.PNRemoveMetadataResult; -import lombok.AllArgsConstructor; +import com.pubnub.api.java.endpoints.objects_api.channel.RemoveChannelMetadata; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNRemoveChannelMetadataResult; +import com.pubnub.api.models.consumer.objects.PNRemoveMetadataResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import org.jetbrains.annotations.NotNull; public class RemoveChannelMetadataImpl extends DelegatingEndpoint implements RemoveChannelMetadata { - public RemoveChannelMetadataImpl(String channel, final PubNubCore pubnubInstance) { + public RemoveChannelMetadataImpl(String channel, final PubNub pubnubInstance) { super(pubnubInstance); this.channel = channel; } @@ -32,13 +31,16 @@ protected ExtendedRemoteAction mapResult(@NotNull @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.removeChannelMetadata(channel); } - @AllArgsConstructor public static class Builder implements RemoveChannelMetadata.Builder { - private final PubNubCore pubnubInstance; + private final PubNub pubnubInstance; + + public Builder(PubNub pubnubInstance) { + this.pubnubInstance = pubnubInstance; + } @Override public RemoveChannelMetadata channel(String channel) { diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/channel/SetChannelMetadataImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/channel/SetChannelMetadataImpl.java similarity index 77% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/channel/SetChannelMetadataImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/channel/SetChannelMetadataImpl.java index 508cb5cde..6171c4bb3 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/channel/SetChannelMetadataImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/channel/SetChannelMetadataImpl.java @@ -1,15 +1,14 @@ -package com.pubnub.internal.endpoints.objects_api.channel; +package com.pubnub.internal.java.endpoints.objects_api.channel; -import com.pubnub.api.endpoints.objects_api.channel.SetChannelMetadata; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; +import com.pubnub.api.java.endpoints.objects_api.channel.SetChannelMetadata; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadata; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNSetChannelMetadataResult; import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadata; -import com.pubnub.api.models.consumer.objects_api.channel.PNSetChannelMetadataResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import lombok.AllArgsConstructor; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -21,7 +20,7 @@ public class SetChannelMetadataImpl extends DelegatingEndpoint implements SetChannelMetadata { - public SetChannelMetadataImpl(final String channel, final PubNubCore pubnubInstance) { + public SetChannelMetadataImpl(final String channel, final PubNub pubnubInstance) { super(pubnubInstance); this.channel = channel; } @@ -39,7 +38,7 @@ protected ExtendedRemoteAction mapResult(@NotNull Ex @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.setChannelMetadata( channel, name, @@ -80,9 +79,12 @@ public SetChannelMetadata custom(Map custom) { return this; } - @AllArgsConstructor public static class Builder implements SetChannelMetadata.Builder { - private final PubNubCore pubnubInstance; + private final PubNub pubnubInstance; + + public Builder(PubNub pubnubInstance) { + this.pubnubInstance = pubnubInstance; + } @Override public SetChannelMetadata channel(final String channel) { diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/members/GetChannelMembersImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/members/GetChannelMembersImpl.java similarity index 68% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/members/GetChannelMembersImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/members/GetChannelMembersImpl.java index 80891a154..fe713eb81 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/members/GetChannelMembersImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/members/GetChannelMembersImpl.java @@ -1,17 +1,16 @@ -package com.pubnub.internal.endpoints.objects_api.members; +package com.pubnub.internal.java.endpoints.objects_api.members; -import com.pubnub.api.endpoints.objects_api.members.GetChannelMembers; -import com.pubnub.api.endpoints.objects_api.utils.Include; -import com.pubnub.api.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; +import com.pubnub.api.java.endpoints.objects_api.members.GetChannelMembers; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.member.PNGetChannelMembersResult; import com.pubnub.api.models.consumer.objects.PNPage; -import com.pubnub.api.models.consumer.objects_api.member.PNGetChannelMembersResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.member.PNMemberArrayResult; -import lombok.AllArgsConstructor; +import com.pubnub.api.models.consumer.objects.member.PNMemberArrayResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -33,7 +32,7 @@ public class GetChannelMembersImpl extends DelegatingEndpoint mapResult(@NotNull Ext } @Override - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.getChannelMembers( channel, limit, @@ -59,9 +58,12 @@ protected EndpointInterface createAction() { ); } - @AllArgsConstructor public static class Builder implements GetChannelMembers.Builder { - private final PubNubCore pubnubInstance; + private final PubNub pubnubInstance; + + public Builder(PubNub pubnubInstance) { + this.pubnubInstance = pubnubInstance; + } @Override public GetChannelMembers channel(final String channel) { diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/members/ManageChannelMembersImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/members/ManageChannelMembersImpl.java similarity index 80% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/members/ManageChannelMembersImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/members/ManageChannelMembersImpl.java index 31205ade6..a4c596360 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/members/ManageChannelMembersImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/members/ManageChannelMembersImpl.java @@ -1,21 +1,20 @@ -package com.pubnub.internal.endpoints.objects_api.members; +package com.pubnub.internal.java.endpoints.objects_api.members; -import com.pubnub.api.endpoints.objects_api.members.ManageChannelMembers; -import com.pubnub.api.endpoints.objects_api.utils.Include; -import com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps; -import com.pubnub.api.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; +import com.pubnub.api.java.endpoints.objects_api.members.ManageChannelMembers; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.member.PNManageChannelMembersResult; +import com.pubnub.api.java.models.consumer.objects_api.member.PNUUID; import com.pubnub.api.models.consumer.objects.PNPage; -import com.pubnub.api.models.consumer.objects_api.member.PNManageChannelMembersResult; -import com.pubnub.api.models.consumer.objects_api.member.PNUUID; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.member.MemberInput; -import com.pubnub.internal.models.consumer.objects.member.PNMember; -import com.pubnub.internal.models.consumer.objects.member.PNMemberArrayResult; -import lombok.AllArgsConstructor; +import com.pubnub.api.models.consumer.objects.member.MemberInput; +import com.pubnub.api.models.consumer.objects.member.PNMember; +import com.pubnub.api.models.consumer.objects.member.PNMemberArrayResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -41,7 +40,7 @@ public class ManageChannelMembersImpl extends DelegatingEndpoint uuidsToSet; private final Collection uuidsToRemove; - public ManageChannelMembersImpl(String channel, Collection uuidsToSet, Collection uuidsToRemove, final PubNubCore pubnubInstance) { + public ManageChannelMembersImpl(String channel, Collection uuidsToSet, Collection uuidsToRemove, final PubNub pubnubInstance) { super(pubnubInstance); this.channel = channel; this.uuidsToSet = uuidsToSet; @@ -56,7 +55,7 @@ protected ExtendedRemoteAction mapResult(@NotNull @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { List toRemove = new ArrayList(uuidsToRemove.size()); for (PNUUID pnuuid : uuidsToRemove) { toRemove.add(pnuuid.getUuid().getId()); @@ -85,9 +84,12 @@ protected EndpointInterface createAction() { ); } - @AllArgsConstructor public static class Builder implements ManageChannelMembers.Builder { - private final PubNubCore pubnubInstance; + private final PubNub pubnubInstance; + + public Builder(PubNub pubnubInstance) { + this.pubnubInstance = pubnubInstance; + } @Override public ObjectsBuilderSteps.RemoveOrSetStep channel(final String channel) { diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/members/RemoveChannelMembersImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/members/RemoveChannelMembersImpl.java similarity index 71% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/members/RemoveChannelMembersImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/members/RemoveChannelMembersImpl.java index e6afc5ea9..40dcf7ddd 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/members/RemoveChannelMembersImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/members/RemoveChannelMembersImpl.java @@ -1,19 +1,18 @@ -package com.pubnub.internal.endpoints.objects_api.members; +package com.pubnub.internal.java.endpoints.objects_api.members; -import com.pubnub.api.endpoints.objects_api.members.RemoveChannelMembers; -import com.pubnub.api.endpoints.objects_api.utils.Include; -import com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps; -import com.pubnub.api.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; +import com.pubnub.api.java.endpoints.objects_api.members.RemoveChannelMembers; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.member.PNRemoveChannelMembersResult; +import com.pubnub.api.java.models.consumer.objects_api.member.PNUUID; import com.pubnub.api.models.consumer.objects.PNPage; -import com.pubnub.api.models.consumer.objects_api.member.PNRemoveChannelMembersResult; -import com.pubnub.api.models.consumer.objects_api.member.PNUUID; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.member.PNMemberArrayResult; -import lombok.AllArgsConstructor; +import com.pubnub.api.models.consumer.objects.member.PNMemberArrayResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -38,7 +37,7 @@ public class RemoveChannelMembersImpl extends DelegatingEndpoint uuids; private Include.PNUUIDDetailsLevel includeUUID; - public RemoveChannelMembersImpl(String channel, Collection uuids, final PubNubCore pubnubInstance) { + public RemoveChannelMembersImpl(String channel, Collection uuids, final PubNub pubnubInstance) { super(pubnubInstance); this.channel = channel; this.uuids = new ArrayList<>(uuids.size()); @@ -55,7 +54,7 @@ protected ExtendedRemoteAction mapResult(@NotNull @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.removeChannelMembers( channel, uuids, @@ -70,9 +69,12 @@ protected EndpointInterface createAction() { ); } - @AllArgsConstructor public static class Builder implements RemoveChannelMembers.Builder { - private final PubNubCore pubnubInstance; + private final PubNub pubnubInstance; + + public Builder(PubNub pubnubInstance) { + this.pubnubInstance = pubnubInstance; + } @Override public ObjectsBuilderSteps.UUIDsStep channel(final String channel) { diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/members/SetChannelMembersImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/members/SetChannelMembersImpl.java similarity index 67% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/members/SetChannelMembersImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/members/SetChannelMembersImpl.java index 5893e6c62..74e5adf37 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/members/SetChannelMembersImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/members/SetChannelMembersImpl.java @@ -1,23 +1,22 @@ -package com.pubnub.internal.endpoints.objects_api.members; +package com.pubnub.internal.java.endpoints.objects_api.members; -import com.pubnub.api.endpoints.objects_api.members.SetChannelMembers; -import com.pubnub.api.endpoints.objects_api.utils.Include; -import com.pubnub.api.endpoints.objects_api.utils.ObjectsBuilderSteps; -import com.pubnub.api.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; +import com.pubnub.api.java.endpoints.objects_api.members.SetChannelMembers; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.ObjectsBuilderSteps; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.member.PNSetChannelMembersResult; +import com.pubnub.api.java.models.consumer.objects_api.member.PNUUID; +import com.pubnub.api.models.consumer.objects.PNMemberKey; import com.pubnub.api.models.consumer.objects.PNPage; -import com.pubnub.api.models.consumer.objects_api.member.PNSetChannelMembersResult; -import com.pubnub.api.models.consumer.objects_api.member.PNUUID; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.PNMemberKey; -import com.pubnub.internal.models.consumer.objects.member.MemberInput; -import com.pubnub.internal.models.consumer.objects.member.PNMember; -import com.pubnub.internal.models.consumer.objects.member.PNMemberArrayResult; -import com.pubnub.internal.models.consumer.objects.member.PNUUIDDetailsLevel; -import lombok.AllArgsConstructor; +import com.pubnub.api.models.consumer.objects.member.MemberInput; +import com.pubnub.api.models.consumer.objects.member.PNMember; +import com.pubnub.api.models.consumer.objects.member.PNMemberArrayResult; +import com.pubnub.api.models.consumer.objects.member.PNUUIDDetailsLevel; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -43,7 +42,7 @@ public class SetChannelMembersImpl extends DelegatingEndpoint uuids; - public SetChannelMembersImpl(final PubNubCore pubnubInstance, String channel, Collection uuids) { + public SetChannelMembersImpl(final PubNub pubnubInstance, String channel, Collection uuids) { super(pubnubInstance); this.channel = channel; this.uuids = uuids; @@ -57,7 +56,7 @@ protected ExtendedRemoteAction mapResult(@NotNull Ext @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { List memberInputs = new ArrayList<>(uuids.size()); for (PNUUID uuid : uuids) { memberInputs.add(new PNMember.Partial(uuid.getUuid().getId(), (uuid instanceof PNUUID.UUIDWithCustom) ? ((PNUUID.UUIDWithCustom) uuid).getCustom() : null, uuid.getStatus())); @@ -65,8 +64,8 @@ protected EndpointInterface createAction() { return pubnub.setChannelMembers(channel, memberInputs, limit, page, filter, toInternal(sort), includeTotalCount, includeCustom, toInternal(includeUUID), includeType); } - static Collection> toInternal(Collection sort) { - List> list = new ArrayList<>(sort.size()); + static Collection> toInternal(Collection sort) { + List> list = new ArrayList<>(sort.size()); for (PNSortKey pnSortKey : sort) { PNMemberKey key = null; switch (pnSortKey.getKey()) { @@ -83,9 +82,9 @@ static Collection(key)); + list.add(new com.pubnub.api.models.consumer.objects.PNSortKey.PNAsc<>(key)); } else { - list.add(new com.pubnub.internal.models.consumer.objects.PNSortKey.PNDesc<>(key)); + list.add(new com.pubnub.api.models.consumer.objects.PNSortKey.PNDesc<>(key)); } } return list; @@ -106,9 +105,12 @@ static PNUUIDDetailsLevel toInternal(Include.PNUUIDDetailsLevel detailLevel) { } } - @AllArgsConstructor public static class Builder implements SetChannelMembers.Builder { - private final PubNubCore pubnubInstance; + private final PubNub pubnubInstance; + + public Builder(PubNub pubnubInstance) { + this.pubnubInstance = pubnubInstance; + } @Override public ObjectsBuilderSteps.UUIDsStep channel(final String channel) { diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/memberships/GetMembershipsImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/memberships/GetMembershipsImpl.java similarity index 68% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/memberships/GetMembershipsImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/memberships/GetMembershipsImpl.java index be76749dc..608de41a5 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/memberships/GetMembershipsImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/memberships/GetMembershipsImpl.java @@ -1,16 +1,16 @@ -package com.pubnub.internal.endpoints.objects_api.memberships; +package com.pubnub.internal.java.endpoints.objects_api.memberships; -import com.pubnub.api.endpoints.objects_api.memberships.GetMemberships; -import com.pubnub.api.endpoints.objects_api.utils.Include; -import com.pubnub.api.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; +import com.pubnub.api.java.endpoints.objects_api.memberships.GetMemberships; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNGetMembershipsResult; import com.pubnub.api.models.consumer.objects.PNPage; -import com.pubnub.api.models.consumer.objects_api.membership.PNGetMembershipsResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.membership.PNChannelMembershipArrayResult; +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -32,7 +32,7 @@ public class GetMembershipsImpl extends DelegatingEndpoint mapResult(@NotNull Extend @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.getMemberships(uuid, limit, page, diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/memberships/ManageMembershipsImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/memberships/ManageMembershipsImpl.java similarity index 77% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/memberships/ManageMembershipsImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/memberships/ManageMembershipsImpl.java index a3c24e499..585f44620 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/memberships/ManageMembershipsImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/memberships/ManageMembershipsImpl.java @@ -1,19 +1,18 @@ -package com.pubnub.internal.endpoints.objects_api.memberships; +package com.pubnub.internal.java.endpoints.objects_api.memberships; -import com.pubnub.api.endpoints.objects_api.memberships.ManageMemberships; -import com.pubnub.api.endpoints.objects_api.utils.Include; -import com.pubnub.api.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; +import com.pubnub.api.java.endpoints.objects_api.memberships.ManageMemberships; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNManageMembershipResult; import com.pubnub.api.models.consumer.objects.PNPage; -import com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership; -import com.pubnub.api.models.consumer.objects_api.membership.PNManageMembershipResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.membership.ChannelMembershipInput; -import com.pubnub.internal.models.consumer.objects.membership.PNChannelMembershipArrayResult; -import lombok.AllArgsConstructor; +import com.pubnub.api.models.consumer.objects.membership.ChannelMembershipInput; +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -37,7 +36,7 @@ public class ManageMembershipsImpl extends DelegatingEndpoint channelsToSet, Collection channelsToRemove, final PubNubCore pubnubInstance) { + public ManageMembershipsImpl(Collection channelsToSet, Collection channelsToRemove, final PubNub pubnubInstance) { super(pubnubInstance); set = channelsToSet; remove = channelsToRemove; @@ -51,10 +50,10 @@ protected ExtendedRemoteAction mapResult(@NotNull Exte @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { ArrayList toSet = new ArrayList<>(set.size()); for (PNChannelMembership channel : set) { - toSet.add(new com.pubnub.internal.models.consumer.objects.membership.PNChannelMembership.Partial( + toSet.add(new com.pubnub.api.models.consumer.objects.membership.PNChannelMembership.Partial( channel.getChannel().getId(), (channel instanceof PNChannelMembership.ChannelWithCustom) ? ((PNChannelMembership.ChannelWithCustom) channel).getCustom() @@ -83,9 +82,12 @@ protected EndpointInterface createAction() { ); } - @AllArgsConstructor public static class Builder implements ManageMemberships.Builder { - private final PubNubCore pubnubInstance; + private final PubNub pubnubInstance; + + public Builder(PubNub pubnubInstance) { + this.pubnubInstance = pubnubInstance; + } @Override public RemoveStep set(final Collection channelsToSet) { diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/memberships/RemoveMembershipsImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/memberships/RemoveMembershipsImpl.java similarity index 71% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/memberships/RemoveMembershipsImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/memberships/RemoveMembershipsImpl.java index b6e4d18cb..150a5cccb 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/memberships/RemoveMembershipsImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/memberships/RemoveMembershipsImpl.java @@ -1,18 +1,17 @@ -package com.pubnub.internal.endpoints.objects_api.memberships; +package com.pubnub.internal.java.endpoints.objects_api.memberships; -import com.pubnub.api.endpoints.objects_api.memberships.RemoveMemberships; -import com.pubnub.api.endpoints.objects_api.utils.Include; -import com.pubnub.api.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; +import com.pubnub.api.java.endpoints.objects_api.memberships.RemoveMemberships; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNRemoveMembershipResult; import com.pubnub.api.models.consumer.objects.PNPage; -import com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership; -import com.pubnub.api.models.consumer.objects_api.membership.PNRemoveMembershipResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.membership.PNChannelMembershipArrayResult; -import lombok.AllArgsConstructor; +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -35,7 +34,7 @@ public class RemoveMembershipsImpl extends DelegatingEndpoint channelMemberships, final PubNubCore pubnubInstance) { + public RemoveMembershipsImpl(@NotNull Collection channelMemberships, final PubNub pubnubInstance) { super(pubnubInstance); this.channelMemberships = channelMemberships; } @@ -48,7 +47,7 @@ protected ExtendedRemoteAction mapResult(@NotNull Exte @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { ArrayList channelList = new ArrayList<>(channelMemberships.size()); for (PNChannelMembership channel : channelMemberships) { channelList.add(channel.getChannel().getId()); @@ -67,9 +66,12 @@ protected EndpointInterface createAction() { ); } - @AllArgsConstructor public static class Builder implements RemoveMemberships.Builder { - private final PubNubCore pubnubInstance; + private final PubNub pubnubInstance; + + public Builder(PubNub pubnubInstance) { + this.pubnubInstance = pubnubInstance; + } @Override public RemoveMemberships channelMemberships(@NotNull final Collection channelMemberships) { diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/memberships/SetMembershipsImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/memberships/SetMembershipsImpl.java similarity index 64% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/memberships/SetMembershipsImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/memberships/SetMembershipsImpl.java index bdcea9834..74501341b 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/memberships/SetMembershipsImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/memberships/SetMembershipsImpl.java @@ -1,21 +1,20 @@ -package com.pubnub.internal.endpoints.objects_api.memberships; +package com.pubnub.internal.java.endpoints.objects_api.memberships; -import com.pubnub.api.endpoints.objects_api.memberships.SetMemberships; -import com.pubnub.api.endpoints.objects_api.utils.Include; -import com.pubnub.api.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; +import com.pubnub.api.java.endpoints.objects_api.memberships.SetMemberships; +import com.pubnub.api.java.endpoints.objects_api.utils.Include; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNChannelMembership; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNSetMembershipResult; +import com.pubnub.api.models.consumer.objects.PNMembershipKey; import com.pubnub.api.models.consumer.objects.PNPage; -import com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership; -import com.pubnub.api.models.consumer.objects_api.membership.PNSetMembershipResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.PNMembershipKey; -import com.pubnub.internal.models.consumer.objects.membership.ChannelMembershipInput; -import com.pubnub.internal.models.consumer.objects.membership.PNChannelMembership.Partial; -import com.pubnub.internal.models.consumer.objects.membership.PNChannelMembershipArrayResult; -import lombok.AllArgsConstructor; +import com.pubnub.api.models.consumer.objects.membership.ChannelMembershipInput; +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembership.Partial; +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -40,14 +39,14 @@ public class SetMembershipsImpl extends DelegatingEndpoint channelMemberships, final PubNubCore pubnubInstance) { + public SetMembershipsImpl(@NotNull Collection channelMemberships, final PubNub pubnubInstance) { super(pubnubInstance); this.channels = channelMemberships; } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { ArrayList channelList = new ArrayList<>(channels.size()); for (PNChannelMembership channel : channels) { channelList.add(new Partial( @@ -78,9 +77,12 @@ protected ExtendedRemoteAction mapResult(@NotNull Extende return new MappingRemoteAction<>(action, PNSetMembershipResult::from); } - @AllArgsConstructor public static class Builder implements SetMemberships.Builder { - private final PubNubCore pubnubInstance; + private final PubNub pubnubInstance; + + public Builder(PubNub pubnubInstance) { + this.pubnubInstance = pubnubInstance; + } public SetMemberships channelMemberships(@NotNull final Collection channelMemberships) { return new SetMembershipsImpl(channelMemberships, pubnubInstance); @@ -88,22 +90,22 @@ public SetMemberships channelMemberships(@NotNull final Collection> toInternal(Collection sort) { - List> list = new ArrayList<>(sort.size()); + static Collection> toInternal(Collection sort) { + List> list = new ArrayList<>(sort.size()); for (PNSortKey pnSortKey : sort) { PNMembershipKey key = null; switch (pnSortKey.getKey()) { @@ -120,9 +122,9 @@ static Collection(key)); + list.add(new com.pubnub.api.models.consumer.objects.PNSortKey.PNAsc<>(key)); } else { - list.add(new com.pubnub.internal.models.consumer.objects.PNSortKey.PNDesc<>(key)); + list.add(new com.pubnub.api.models.consumer.objects.PNSortKey.PNDesc<>(key)); } } return list; diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/uuid/GetAllUUIDMetadataImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/uuid/GetAllUUIDMetadataImpl.java similarity index 64% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/uuid/GetAllUUIDMetadataImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/uuid/GetAllUUIDMetadataImpl.java index 3b3e37715..dc07eb0ea 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/uuid/GetAllUUIDMetadataImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/uuid/GetAllUUIDMetadataImpl.java @@ -1,15 +1,15 @@ -package com.pubnub.internal.endpoints.objects_api.uuid; +package com.pubnub.internal.java.endpoints.objects_api.uuid; -import com.pubnub.api.endpoints.objects_api.utils.PNSortKey; -import com.pubnub.api.endpoints.objects_api.uuid.GetAllUUIDMetadata; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; +import com.pubnub.api.java.endpoints.objects_api.utils.PNSortKey; +import com.pubnub.api.java.endpoints.objects_api.uuid.GetAllUUIDMetadata; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNGetAllUUIDMetadataResult; import com.pubnub.api.models.consumer.objects.PNPage; -import com.pubnub.api.models.consumer.objects_api.uuid.PNGetAllUUIDMetadataResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.uuid.PNUUIDMetadataArrayResult; +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadataArrayResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -17,7 +17,7 @@ import java.util.Collection; import java.util.Collections; -import static com.pubnub.internal.endpoints.objects_api.channel.GetAllChannelsMetadataImpl.toInternal; +import static com.pubnub.internal.java.endpoints.objects_api.channel.GetAllChannelsMetadataImpl.toInternal; @Setter @Accessors(chain = true, fluent = true) @@ -31,13 +31,13 @@ public class GetAllUUIDMetadataImpl private String filter; private Collection sort = Collections.emptyList(); - public GetAllUUIDMetadataImpl(final PubNubCore pubnub) { + public GetAllUUIDMetadataImpl(final PubNub pubnub) { super(pubnub); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.getAllUUIDMetadata( limit, page, diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/uuid/GetUUIDMetadataImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/uuid/GetUUIDMetadataImpl.java similarity index 62% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/uuid/GetUUIDMetadataImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/uuid/GetUUIDMetadataImpl.java index c0cbc5039..91927689a 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/uuid/GetUUIDMetadataImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/uuid/GetUUIDMetadataImpl.java @@ -1,14 +1,14 @@ -package com.pubnub.internal.endpoints.objects_api.uuid; +package com.pubnub.internal.java.endpoints.objects_api.uuid; -import com.pubnub.api.endpoints.objects_api.uuid.GetUUIDMetadata; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; -import com.pubnub.api.models.consumer.objects_api.uuid.PNGetUUIDMetadataResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadata; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.uuid.PNUUIDMetadataResult; +import com.pubnub.api.java.endpoints.objects_api.uuid.GetUUIDMetadata; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNGetUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadata; +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadataResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -20,13 +20,13 @@ public class GetUUIDMetadataImpl extends DelegatingEndpoint createAction() { + protected Endpoint createAction() { return pubnub.getUUIDMetadata( uuid, includeCustom diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/uuid/RemoveUUIDMetadataImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/uuid/RemoveUUIDMetadataImpl.java similarity index 63% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/uuid/RemoveUUIDMetadataImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/uuid/RemoveUUIDMetadataImpl.java index 9f76b5eda..c7d78301d 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/uuid/RemoveUUIDMetadataImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/uuid/RemoveUUIDMetadataImpl.java @@ -1,13 +1,13 @@ -package com.pubnub.internal.endpoints.objects_api.uuid; +package com.pubnub.internal.java.endpoints.objects_api.uuid; -import com.pubnub.api.endpoints.objects_api.uuid.RemoveUUIDMetadata; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; -import com.pubnub.api.models.consumer.objects_api.uuid.PNRemoveUUIDMetadataResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.PNRemoveMetadataResult; +import com.pubnub.api.java.endpoints.objects_api.uuid.RemoveUUIDMetadata; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNRemoveUUIDMetadataResult; +import com.pubnub.api.models.consumer.objects.PNRemoveMetadataResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -18,13 +18,13 @@ public class RemoveUUIDMetadataImpl extends DelegatingEndpoint createAction() { + protected Endpoint createAction() { return pubnub.removeUUIDMetadata(uuid); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/uuid/SetUUIDMetadataImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/uuid/SetUUIDMetadataImpl.java similarity index 72% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/uuid/SetUUIDMetadataImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/uuid/SetUUIDMetadataImpl.java index 229f5c3a3..7605dd016 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/objects_api/uuid/SetUUIDMetadataImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/objects_api/uuid/SetUUIDMetadataImpl.java @@ -1,14 +1,14 @@ -package com.pubnub.internal.endpoints.objects_api.uuid; +package com.pubnub.internal.java.endpoints.objects_api.uuid; -import com.pubnub.api.endpoints.objects_api.uuid.SetUUIDMetadata; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; -import com.pubnub.api.models.consumer.objects_api.uuid.PNSetUUIDMetadataResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadata; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; -import com.pubnub.internal.models.consumer.objects.uuid.PNUUIDMetadataResult; +import com.pubnub.api.java.endpoints.objects_api.uuid.SetUUIDMetadata; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNSetUUIDMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadata; +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadataResult; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -37,11 +37,12 @@ public class SetUUIDMetadataImpl extends DelegatingEndpoint custom) { + @Override + public SetUUIDMetadata custom(Map custom) { final HashMap customHashMap = new HashMap<>(); if (custom != null) { customHashMap.putAll(custom); @@ -52,7 +53,7 @@ public SetUUIDMetadataImpl(final PubNubCore pubnub) { @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.setUUIDMetadata( uuid, name, diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/GetStateImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/GetStateImpl.java similarity index 67% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/GetStateImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/GetStateImpl.java index e8a8e1d5c..c7242a869 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/GetStateImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/GetStateImpl.java @@ -1,10 +1,10 @@ -package com.pubnub.internal.endpoints.presence; +package com.pubnub.internal.java.endpoints.presence; -import com.pubnub.api.endpoints.presence.GetState; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; +import com.pubnub.api.java.endpoints.presence.GetState; import com.pubnub.api.models.consumer.presence.PNGetStateResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -20,14 +20,14 @@ public class GetStateImpl extends IdentityMappingEndpoint impl private List channelGroups = new ArrayList<>(); private String uuid; - public GetStateImpl(PubNubCore pubnub) { + public GetStateImpl(PubNub pubnub) { super(pubnub); uuid = pubnub.getConfiguration().getUuid(); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.getPresenceState( channels, channelGroups, diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/HeartbeatImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/HeartbeatImpl.java similarity index 52% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/HeartbeatImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/HeartbeatImpl.java index d26511cff..2d9eb3e9a 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/HeartbeatImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/HeartbeatImpl.java @@ -1,9 +1,10 @@ -package com.pubnub.internal.endpoints.presence; +package com.pubnub.internal.java.endpoints.presence; -import com.pubnub.api.endpoints.presence.Heartbeat; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.api.Endpoint; +import com.pubnub.api.java.endpoints.presence.Heartbeat; +import com.pubnub.internal.PubNubImpl; +import com.pubnub.internal.endpoints.presence.HeartbeatEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -19,7 +20,7 @@ public class HeartbeatImpl extends IdentityMappingEndpoint implements H private List channelGroups; private Object state; - public HeartbeatImpl(PubNubCore pubnub) { + public HeartbeatImpl(PubNubImpl pubnub) { super(pubnub); channels = new ArrayList<>(); channelGroups = new ArrayList<>(); @@ -27,7 +28,7 @@ public HeartbeatImpl(PubNubCore pubnub) { @Override @NotNull - protected EndpointInterface createAction() { - return new HeartbeatEndpoint(pubnub, channels, channelGroups, state); + protected Endpoint createAction() { + return new HeartbeatEndpoint((PubNubImpl) pubnub, channels, channelGroups, state); } } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/HereNowImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/HereNowImpl.java similarity index 68% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/HereNowImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/HereNowImpl.java index b81bfbc78..fb0fc9635 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/HereNowImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/HereNowImpl.java @@ -1,11 +1,11 @@ -package com.pubnub.internal.endpoints.presence; +package com.pubnub.internal.java.endpoints.presence; -import com.pubnub.api.endpoints.presence.HereNow; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; +import com.pubnub.api.java.endpoints.presence.HereNow; import com.pubnub.api.models.consumer.presence.PNHereNowResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -21,13 +21,13 @@ public class HereNowImpl extends IdentityMappingEndpoint implem private boolean includeState = false; private boolean includeUUIDs = true; - public HereNowImpl(PubNubCore pubnub) { + public HereNowImpl(PubNub pubnub) { super(pubnub); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.hereNow( channels, channelGroups, diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/LeaveImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/LeaveImpl.java similarity index 53% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/LeaveImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/LeaveImpl.java index 16b82fb02..8409fed11 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/LeaveImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/LeaveImpl.java @@ -1,9 +1,11 @@ -package com.pubnub.internal.endpoints.presence; +package com.pubnub.internal.java.endpoints.presence; -import com.pubnub.api.endpoints.presence.Leave; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; +import com.pubnub.api.java.endpoints.presence.Leave; +import com.pubnub.internal.PubNubImpl; +import com.pubnub.internal.endpoints.presence.LeaveEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -17,14 +19,14 @@ public class LeaveImpl extends IdentityMappingEndpoint implements Leave private List channels = new ArrayList<>(); private List channelGroups = new ArrayList<>(); - public LeaveImpl(PubNubCore pubnub) { + public LeaveImpl(PubNub pubnub) { super(pubnub); } @Override @NotNull - protected EndpointInterface createAction() { - LeaveEndpoint leave = new LeaveEndpoint(pubnub); + protected Endpoint createAction() { + LeaveEndpoint leave = new LeaveEndpoint((PubNubImpl) pubnub); leave.setChannels(channels); leave.setChannelGroups(channelGroups); return leave; diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/SetStateImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/SetStateImpl.java similarity index 77% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/SetStateImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/SetStateImpl.java index 12bae7346..4748bf9f2 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/SetStateImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/SetStateImpl.java @@ -1,14 +1,15 @@ -package com.pubnub.internal.endpoints.presence; +package com.pubnub.internal.java.endpoints.presence; +import com.pubnub.api.Endpoint; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.presence.SetState; import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction; import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.presence.SetState; import com.pubnub.api.models.consumer.presence.PNSetStateResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.DelegatingEndpoint; +import com.pubnub.internal.PubNubImpl; +import com.pubnub.internal.endpoints.presence.HeartbeatEndpoint; +import com.pubnub.internal.java.endpoints.DelegatingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -34,7 +35,7 @@ public class SetStateImpl extends DelegatingEndpoint i private boolean withHeartbeat; - public SetStateImpl(PubNubCore pubnub) { + public SetStateImpl(PubNubImpl pubnub) { super(pubnub); } @@ -44,7 +45,7 @@ protected void validateParams() throws PubNubException { throw new PubNubException(PubNubErrorBuilder.PNERROBJ_STATE_MISSING); } - String stringifiedState = pubnub.getMapper().toJson(state); + String stringifiedState = ((PubNubImpl) pubnub).getMapper().toJson(state); if (!isJsonObject(stringifiedState)) { throw new PubNubException(PubNubErrorBuilder.PNERROBJ_STATE_MUST_BE_JSON_OBJECT); } @@ -68,7 +69,7 @@ private boolean isJsonObject(String json) { @Override protected ExtendedRemoteAction mapResult(@NotNull ExtendedRemoteAction action) { if (withHeartbeat) { - return new MappingRemoteAction<>(action, result -> new PNSetStateResult(pubnub.getMapper().toJsonTree(state))); + return new MappingRemoteAction<>(action, result -> new PNSetStateResult(((PubNubImpl) pubnub).getMapper().toJsonTree(state))); } else { return new MappingRemoteAction<>(action, result -> (PNSetStateResult) result); } @@ -76,10 +77,10 @@ protected ExtendedRemoteAction mapResult(@NotNull ExtendedRemo @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { if (!withHeartbeat) { // Regular way of setting presence explicitly is through SetPresenceState: - return (EndpointInterface) (Object) pubnub.setPresenceState( + return (Endpoint) (Object) pubnub.setPresenceState( channels, channelGroups, state, @@ -88,7 +89,7 @@ protected EndpointInterface createAction() { } else { // Some clients require alternative way of setting it through Heartbeat // Which is a feature brought over from the legacy Java SDK, and we need to be compatible: - return (EndpointInterface) (Object) new HeartbeatEndpoint(pubnub, channels, channelGroups, composeStateParamValue()); + return (Endpoint) (Object) new HeartbeatEndpoint((PubNubImpl) pubnub, channels, channelGroups, composeStateParamValue()); } } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/WhereNowImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/WhereNowImpl.java similarity index 50% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/WhereNowImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/WhereNowImpl.java index 51f405276..26176f59b 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/presence/WhereNowImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/presence/WhereNowImpl.java @@ -1,10 +1,10 @@ -package com.pubnub.internal.endpoints.presence; +package com.pubnub.internal.java.endpoints.presence; -import com.pubnub.api.endpoints.presence.WhereNow; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; -import com.pubnub.internal.models.consumer.presence.PNWhereNowResult; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; +import com.pubnub.api.java.endpoints.presence.WhereNow; +import com.pubnub.api.models.consumer.presence.PNWhereNowResult; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -15,14 +15,14 @@ public class WhereNowImpl extends IdentityMappingEndpoint impl private String uuid; - public WhereNowImpl(PubNubCore pubnub) { + public WhereNowImpl(PubNub pubnub) { super(pubnub); uuid = pubnub.getConfiguration().getUuid(); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.whereNow(uuid); } } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/pubsub/PublishImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/pubsub/PublishImpl.java similarity index 72% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/pubsub/PublishImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/pubsub/PublishImpl.java index 135766715..d1709a082 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/pubsub/PublishImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/pubsub/PublishImpl.java @@ -1,13 +1,13 @@ -package com.pubnub.internal.endpoints.pubsub; +package com.pubnub.internal.java.endpoints.pubsub; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.pubsub.Publish; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.pubsub.Publish; +import com.pubnub.api.java.v2.endpoints.pubsub.PublishBuilder; import com.pubnub.api.models.consumer.PNPublishResult; -import com.pubnub.api.v2.endpoints.pubsub.PublishBuilder; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -24,14 +24,14 @@ public class PublishImpl extends IdentityMappingEndpoint implem private boolean replicate; private Integer ttl; - public PublishImpl(PubNubCore pubnub) { + public PublishImpl(PubNub pubnub) { super(pubnub); this.replicate = true; } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.publish( channel, message, diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/pubsub/SignalImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/pubsub/SignalImpl.java similarity index 65% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/pubsub/SignalImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/pubsub/SignalImpl.java index 6ab440c6b..be284efde 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/pubsub/SignalImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/pubsub/SignalImpl.java @@ -1,13 +1,13 @@ -package com.pubnub.internal.endpoints.pubsub; +package com.pubnub.internal.java.endpoints.pubsub; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; -import com.pubnub.api.endpoints.pubsub.Signal; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.pubsub.Signal; +import com.pubnub.api.java.v2.endpoints.pubsub.SignalBuilder; import com.pubnub.api.models.consumer.PNPublishResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.api.v2.endpoints.pubsub.SignalBuilder; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -19,13 +19,13 @@ public class SignalImpl extends IdentityMappingEndpoint impleme private Object message; private String channel; - public SignalImpl(PubNubCore pubnub) { + public SignalImpl(PubNub pubnub) { super(pubnub); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.signal(channel, message); } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/push/AddChannelsToPushImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/push/AddChannelsToPushImpl.java similarity index 75% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/push/AddChannelsToPushImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/push/AddChannelsToPushImpl.java index e4a98d922..e73004f72 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/push/AddChannelsToPushImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/push/AddChannelsToPushImpl.java @@ -1,13 +1,14 @@ -package com.pubnub.internal.endpoints.push; +package com.pubnub.internal.java.endpoints.push; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; import com.pubnub.api.enums.PNPushEnvironment; import com.pubnub.api.enums.PNPushType; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.push.AddChannelsToPush; import com.pubnub.api.models.consumer.push.PNPushAddChannelResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -16,7 +17,7 @@ @Setter @Accessors(chain = true, fluent = true) -public class AddChannelsToPushImpl extends IdentityMappingEndpoint implements com.pubnub.api.endpoints.push.AddChannelsToPush { +public class AddChannelsToPushImpl extends IdentityMappingEndpoint implements AddChannelsToPush { private PNPushType pushType; private List channels; @@ -24,13 +25,13 @@ public class AddChannelsToPushImpl extends IdentityMappingEndpoint createAction() { + protected Endpoint createAction() { return pubnub.addPushNotificationsOnChannels( pushType, channels, diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/push/ListPushProvisionsImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/push/ListPushProvisionsImpl.java similarity index 70% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/push/ListPushProvisionsImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/push/ListPushProvisionsImpl.java index ce4e5c9a9..47dd67bc6 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/push/ListPushProvisionsImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/push/ListPushProvisionsImpl.java @@ -1,33 +1,34 @@ -package com.pubnub.internal.endpoints.push; +package com.pubnub.internal.java.endpoints.push; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; import com.pubnub.api.enums.PNPushEnvironment; import com.pubnub.api.enums.PNPushType; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.push.ListPushProvisions; import com.pubnub.api.models.consumer.push.PNPushListProvisionsResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @Setter @Accessors(chain = true, fluent = true) -public class ListPushProvisionsImpl extends IdentityMappingEndpoint implements com.pubnub.api.endpoints.push.ListPushProvisions { +public class ListPushProvisionsImpl extends IdentityMappingEndpoint implements ListPushProvisions { private PNPushType pushType; private String deviceId; private PNPushEnvironment environment = PNPushEnvironment.DEVELOPMENT; private String topic; - public ListPushProvisionsImpl(PubNubCore pubnub) { + public ListPushProvisionsImpl(PubNub pubnub) { super(pubnub); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.auditPushChannelProvisions( pushType, deviceId, diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/push/RemoveAllPushChannelsForDeviceImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/push/RemoveAllPushChannelsForDeviceImpl.java similarity index 69% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/push/RemoveAllPushChannelsForDeviceImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/push/RemoveAllPushChannelsForDeviceImpl.java index 1f927f387..e867ba5a8 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/push/RemoveAllPushChannelsForDeviceImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/push/RemoveAllPushChannelsForDeviceImpl.java @@ -1,32 +1,33 @@ -package com.pubnub.internal.endpoints.push; +package com.pubnub.internal.java.endpoints.push; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; import com.pubnub.api.enums.PNPushEnvironment; import com.pubnub.api.enums.PNPushType; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.push.RemoveAllPushChannelsForDevice; import com.pubnub.api.models.consumer.push.PNPushRemoveAllChannelsResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @Setter @Accessors(chain = true, fluent = true) -public class RemoveAllPushChannelsForDeviceImpl extends IdentityMappingEndpoint implements com.pubnub.api.endpoints.push.RemoveAllPushChannelsForDevice { +public class RemoveAllPushChannelsForDeviceImpl extends IdentityMappingEndpoint implements RemoveAllPushChannelsForDevice { private PNPushType pushType; private String deviceId; private PNPushEnvironment environment = PNPushEnvironment.DEVELOPMENT; private String topic; - public RemoveAllPushChannelsForDeviceImpl(PubNubCore pubnub) { + public RemoveAllPushChannelsForDeviceImpl(PubNub pubnub) { super(pubnub); } @Override @NotNull - protected EndpointInterface createAction() { + protected Endpoint createAction() { return pubnub.removeAllPushNotificationsFromDeviceWithPushToken( pushType, deviceId, diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/push/RemoveChannelsFromPushImpl.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/push/RemoveChannelsFromPushImpl.java similarity index 73% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/push/RemoveChannelsFromPushImpl.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/push/RemoveChannelsFromPushImpl.java index 7a169abba..68360dd4c 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/endpoints/push/RemoveChannelsFromPushImpl.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/endpoints/push/RemoveChannelsFromPushImpl.java @@ -1,13 +1,14 @@ -package com.pubnub.internal.endpoints.push; +package com.pubnub.internal.java.endpoints.push; +import com.pubnub.api.Endpoint; +import com.pubnub.api.PubNub; import com.pubnub.api.PubNubException; -import com.pubnub.api.builder.PubNubErrorBuilder; import com.pubnub.api.enums.PNPushEnvironment; import com.pubnub.api.enums.PNPushType; +import com.pubnub.api.java.builder.PubNubErrorBuilder; +import com.pubnub.api.java.endpoints.push.RemoveChannelsFromPush; import com.pubnub.api.models.consumer.push.PNPushRemoveChannelResult; -import com.pubnub.internal.EndpointInterface; -import com.pubnub.internal.PubNubCore; -import com.pubnub.internal.endpoints.IdentityMappingEndpoint; +import com.pubnub.internal.java.endpoints.IdentityMappingEndpoint; import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.NotNull; @@ -16,7 +17,7 @@ @Setter @Accessors(chain = true, fluent = true) -public class RemoveChannelsFromPushImpl extends IdentityMappingEndpoint implements com.pubnub.api.endpoints.push.RemoveChannelsFromPush { +public class RemoveChannelsFromPushImpl extends IdentityMappingEndpoint implements RemoveChannelsFromPush { private PNPushType pushType; private List channels; @@ -24,13 +25,13 @@ public class RemoveChannelsFromPushImpl extends IdentityMappingEndpoint createAction() { + protected Endpoint createAction() { return pubnub.removePushNotificationsFromChannels( pushType, channels, diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/PNConfigurationImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/PNConfigurationImpl.kt similarity index 65% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/PNConfigurationImpl.kt rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/PNConfigurationImpl.kt index 25eed4871..c8de6d401 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/PNConfigurationImpl.kt +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/PNConfigurationImpl.kt @@ -1,12 +1,12 @@ -package com.pubnub.internal.v2 +package com.pubnub.internal.java.v2 import com.pubnub.api.UserId import com.pubnub.api.crypto.CryptoModule import com.pubnub.api.enums.PNHeartbeatNotificationOptions import com.pubnub.api.enums.PNLogVerbosity +import com.pubnub.api.java.v2.PNConfiguration +import com.pubnub.api.java.v2.PNConfigurationOverride import com.pubnub.api.retry.RetryConfiguration -import com.pubnub.api.v2.BasePNConfiguration -import com.pubnub.api.v2.PNConfiguration import okhttp3.Authenticator import okhttp3.CertificatePinner import okhttp3.ConnectionSpec @@ -20,59 +20,67 @@ import javax.net.ssl.X509ExtendedTrustManager class PNConfigurationImpl( override val userId: UserId, - override val subscribeKey: String, - override val publishKey: String, - override val secretKey: String, - override val authKey: String, - override val cryptoModule: CryptoModule?, - override val origin: String, - override val secure: Boolean, - override val logVerbosity: PNLogVerbosity, - override val heartbeatNotificationOptions: PNHeartbeatNotificationOptions, - override val presenceTimeout: Int, - override val heartbeatInterval: Int, - override val subscribeTimeout: Int, - override val connectTimeout: Int, - override val nonSubscribeReadTimeout: Int, - override val cacheBusting: Boolean, - override val suppressLeaveEvents: Boolean, - override val maintainPresenceState: Boolean, - override val filterExpression: String, - override val includeInstanceIdentifier: Boolean, - override val includeRequestIdentifier: Boolean, - override val maximumConnections: Int?, - override val googleAppEngineNetworking: Boolean, - override val proxy: Proxy?, - override val proxySelector: ProxySelector?, - override val proxyAuthenticator: Authenticator?, - override val certificatePinner: CertificatePinner?, - override val httpLoggingInterceptor: HttpLoggingInterceptor?, - override val sslSocketFactory: SSLSocketFactory?, - override val x509ExtendedTrustManager: X509ExtendedTrustManager?, - override val connectionSpec: ConnectionSpec?, - override val hostnameVerifier: HostnameVerifier?, - override val fileMessagePublishRetryLimit: Int, - override val dedupOnSubscribe: Boolean, - override val maximumMessagesCacheSize: Int, - override val pnsdkSuffixes: Map, - override val retryConfiguration: RetryConfiguration, - override val managePresenceListManually: Boolean, -) : BasePNConfigurationImpl(userId), PNConfiguration { - class Builder internal constructor(defaultConfiguration: BasePNConfiguration) : - BasePNConfigurationImpl.Builder(defaultConfiguration), - PNConfiguration.Builder { + override val subscribeKey: String = "", + override val publishKey: String = "", + override val secretKey: String = "", + override val authKey: String = "", + override val cryptoModule: CryptoModule? = null, + override val origin: String = "", + override val secure: Boolean = true, + override val logVerbosity: PNLogVerbosity = PNLogVerbosity.NONE, + override val heartbeatNotificationOptions: PNHeartbeatNotificationOptions = PNHeartbeatNotificationOptions.FAILURES, + override val presenceTimeout: Int = PRESENCE_TIMEOUT, + override val heartbeatInterval: Int = 0, + override val subscribeTimeout: Int = SUBSCRIBE_TIMEOUT, + override val connectTimeout: Int = CONNECT_TIMEOUT, + override val nonSubscribeReadTimeout: Int = NON_SUBSCRIBE_REQUEST_TIMEOUT, + override val cacheBusting: Boolean = false, + override val suppressLeaveEvents: Boolean = false, + override val maintainPresenceState: Boolean = true, + override val filterExpression: String = "", + override val includeInstanceIdentifier: Boolean = false, + override val includeRequestIdentifier: Boolean = true, + override val maximumConnections: Int? = null, + override val googleAppEngineNetworking: Boolean = false, + override val proxy: Proxy? = null, + override val proxySelector: ProxySelector? = null, + override val proxyAuthenticator: Authenticator? = null, + override val certificatePinner: CertificatePinner? = null, + override val httpLoggingInterceptor: HttpLoggingInterceptor? = null, + override val sslSocketFactory: SSLSocketFactory? = null, + override val x509ExtendedTrustManager: X509ExtendedTrustManager? = null, + override val connectionSpec: ConnectionSpec? = null, + override val hostnameVerifier: HostnameVerifier? = null, + override val fileMessagePublishRetryLimit: Int = 5, + override val dedupOnSubscribe: Boolean = false, + override val maximumMessagesCacheSize: Int = DEFAULT_DEDUPE_SIZE, + override val pnsdkSuffixes: Map = emptyMap(), + override val retryConfiguration: RetryConfiguration = RetryConfiguration.None, + override val managePresenceListManually: Boolean = false, +) : PNConfiguration { + companion object { + const val DEFAULT_DEDUPE_SIZE = 100 + const val PRESENCE_TIMEOUT = 300 + const val MINIMUM_PRESENCE_TIMEOUT = 20 + const val NON_SUBSCRIBE_REQUEST_TIMEOUT = 10 + const val SUBSCRIBE_TIMEOUT = 310 + const val CONNECT_TIMEOUT = 5 + } + + class Builder internal constructor(defaultConfiguration: com.pubnub.api.v2.PNConfiguration) : + PNConfiguration.Builder, PNConfigurationOverride.Builder { + constructor(userId: UserId, subscribeKey: String) : this(PNConfigurationImpl(userId, subscribeKey)) + private val log = LoggerFactory.getLogger(this::class.simpleName) - override var userId: UserId = super.userId - private set + override var userId: UserId = defaultConfiguration.userId override fun setUserId(userId: UserId): PNConfiguration.Builder { this.userId = userId return this } - override var subscribeKey: String = super.subscribeKey - private set + override var subscribeKey: String = defaultConfiguration.subscribeKey override fun subscribeKey(subscribeKey: String): PNConfiguration.Builder { this.subscribeKey = subscribeKey @@ -84,64 +92,57 @@ class PNConfigurationImpl( return this } - override var publishKey: String = super.publishKey - private set + override var publishKey: String = defaultConfiguration.publishKey override fun secretKey(secretKey: String): Builder { this.secretKey = secretKey return this } - override var secretKey: String = super.secretKey - private set + override var secretKey: String = defaultConfiguration.secretKey override fun authKey(authKey: String): Builder { this.authKey = authKey return this } - override var authKey: String = super.authKey - private set + override var authKey: String = defaultConfiguration.authKey override fun cryptoModule(cryptoModule: CryptoModule?): Builder { this.cryptoModule = cryptoModule return this } - override var cryptoModule: CryptoModule? = super.cryptoModule - private set + override var cryptoModule: CryptoModule? = defaultConfiguration.cryptoModule override fun origin(origin: String): Builder { this.origin = origin return this } - override var origin: String = super.origin - private set + override var origin: String = defaultConfiguration.origin override fun secure(secure: Boolean): Builder { this.secure = secure return this } - override var secure: Boolean = super.secure - private set + override var secure: Boolean = defaultConfiguration.secure override fun logVerbosity(logVerbosity: PNLogVerbosity): Builder { this.logVerbosity = logVerbosity return this } - override var logVerbosity: PNLogVerbosity = super.logVerbosity - private set + override var logVerbosity: PNLogVerbosity = defaultConfiguration.logVerbosity override fun heartbeatNotificationOptions(heartbeatNotificationOptions: PNHeartbeatNotificationOptions): Builder { this.heartbeatNotificationOptions = heartbeatNotificationOptions return this } - override var heartbeatNotificationOptions: PNHeartbeatNotificationOptions = super.heartbeatNotificationOptions - private set + override var heartbeatNotificationOptions: PNHeartbeatNotificationOptions = + defaultConfiguration.heartbeatNotificationOptions override fun presenceTimeout(presenceTimeout: Int): Builder { this.presenceTimeout = if (presenceTimeout < MINIMUM_PRESENCE_TIMEOUT) { @@ -154,32 +155,28 @@ class PNConfigurationImpl( return this } - override var presenceTimeout: Int = super.presenceTimeout - private set + override var presenceTimeout: Int = defaultConfiguration.presenceTimeout override fun heartbeatInterval(heartbeatInterval: Int): Builder { this.heartbeatInterval = heartbeatInterval return this } - override var heartbeatInterval: Int = super.heartbeatInterval - private set + override var heartbeatInterval: Int = defaultConfiguration.heartbeatInterval override fun subscribeTimeout(subscribeTimeout: Int): Builder { this.subscribeTimeout = subscribeTimeout return this } - override var subscribeTimeout: Int = super.subscribeTimeout - private set + override var subscribeTimeout: Int = defaultConfiguration.subscribeTimeout override fun connectTimeout(connectTimeout: Int): Builder { this.connectTimeout = connectTimeout return this } - override var connectTimeout: Int = super.connectTimeout - private set + override var connectTimeout: Int = defaultConfiguration.connectTimeout @Deprecated( "This setting relates to *read* timeout and was renamed to `nonSubscribeReadTimeout`", @@ -194,192 +191,168 @@ class PNConfigurationImpl( return this } - override var nonSubscribeReadTimeout: Int = super.nonSubscribeReadTimeout - private set + override var nonSubscribeReadTimeout: Int = defaultConfiguration.nonSubscribeReadTimeout override fun cacheBusting(cacheBusting: Boolean): Builder { this.cacheBusting = cacheBusting return this } - override var cacheBusting: Boolean = super.cacheBusting - private set + override var cacheBusting: Boolean = defaultConfiguration.cacheBusting override fun suppressLeaveEvents(suppressLeaveEvents: Boolean): Builder { this.suppressLeaveEvents = suppressLeaveEvents return this } - override var suppressLeaveEvents: Boolean = super.suppressLeaveEvents - private set + override var suppressLeaveEvents: Boolean = defaultConfiguration.suppressLeaveEvents override fun maintainPresenceState(maintainPresenceState: Boolean): Builder { this.maintainPresenceState = maintainPresenceState return this } - override var maintainPresenceState: Boolean = super.maintainPresenceState - private set + override var maintainPresenceState: Boolean = defaultConfiguration.maintainPresenceState override fun filterExpression(filterExpression: String): Builder { this.filterExpression = filterExpression return this } - override var filterExpression: String = super.filterExpression - private set + override var filterExpression: String = defaultConfiguration.filterExpression override fun includeInstanceIdentifier(includeInstanceIdentifier: Boolean): Builder { this.includeInstanceIdentifier = includeInstanceIdentifier return this } - override var includeInstanceIdentifier: Boolean = super.includeInstanceIdentifier - private set + override var includeInstanceIdentifier: Boolean = defaultConfiguration.includeInstanceIdentifier override fun includeRequestIdentifier(includeRequestIdentifier: Boolean): Builder { this.includeRequestIdentifier = includeRequestIdentifier return this } - override var includeRequestIdentifier: Boolean = super.includeRequestIdentifier - private set + override var includeRequestIdentifier: Boolean = defaultConfiguration.includeRequestIdentifier override fun maximumConnections(maximumConnections: Int?): Builder { this.maximumConnections = maximumConnections return this } - override var maximumConnections: Int? = super.maximumConnections - private set + override var maximumConnections: Int? = defaultConfiguration.maximumConnections override fun googleAppEngineNetworking(googleAppEngineNetworking: Boolean): Builder { this.googleAppEngineNetworking = googleAppEngineNetworking return this } - override var googleAppEngineNetworking: Boolean = super.googleAppEngineNetworking - private set + override var googleAppEngineNetworking: Boolean = defaultConfiguration.googleAppEngineNetworking override fun proxy(proxy: Proxy?): Builder { this.proxy = proxy return this } - override var proxy: Proxy? = super.proxy - private set + override var proxy: Proxy? = defaultConfiguration.proxy override fun proxySelector(proxySelector: ProxySelector?): Builder { this.proxySelector = proxySelector return this } - override var proxySelector: ProxySelector? = super.proxySelector - private set + override var proxySelector: ProxySelector? = defaultConfiguration.proxySelector override fun proxyAuthenticator(proxyAuthenticator: Authenticator?): Builder { this.proxyAuthenticator = proxyAuthenticator return this } - override var proxyAuthenticator: Authenticator? = super.proxyAuthenticator - private set + override var proxyAuthenticator: Authenticator? = defaultConfiguration.proxyAuthenticator override fun certificatePinner(certificatePinner: CertificatePinner?): Builder { this.certificatePinner = certificatePinner return this } - override var certificatePinner: CertificatePinner? = super.certificatePinner - private set + override var certificatePinner: CertificatePinner? = defaultConfiguration.certificatePinner override fun httpLoggingInterceptor(httpLoggingInterceptor: HttpLoggingInterceptor?): Builder { this.httpLoggingInterceptor = httpLoggingInterceptor return this } - override var httpLoggingInterceptor: HttpLoggingInterceptor? = super.httpLoggingInterceptor - private set + override var httpLoggingInterceptor: HttpLoggingInterceptor? = defaultConfiguration.httpLoggingInterceptor override fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?): Builder { this.sslSocketFactory = sslSocketFactory return this } - override var sslSocketFactory: SSLSocketFactory? = super.sslSocketFactory - private set + override var sslSocketFactory: SSLSocketFactory? = defaultConfiguration.sslSocketFactory override fun x509ExtendedTrustManager(x509ExtendedTrustManager: X509ExtendedTrustManager?): Builder { this.x509ExtendedTrustManager = x509ExtendedTrustManager return this } - override var x509ExtendedTrustManager: X509ExtendedTrustManager? = super.x509ExtendedTrustManager - private set + override var x509ExtendedTrustManager: X509ExtendedTrustManager? = defaultConfiguration.x509ExtendedTrustManager override fun connectionSpec(connectionSpec: ConnectionSpec?): Builder { this.connectionSpec = connectionSpec return this } - override var connectionSpec: ConnectionSpec? = super.connectionSpec - private set + override var connectionSpec: ConnectionSpec? = defaultConfiguration.connectionSpec override fun hostnameVerifier(hostnameVerifier: HostnameVerifier?): Builder { this.hostnameVerifier = hostnameVerifier return this } - override var hostnameVerifier: HostnameVerifier? = super.hostnameVerifier - private set + override var hostnameVerifier: HostnameVerifier? = defaultConfiguration.hostnameVerifier override fun fileMessagePublishRetryLimit(fileMessagePublishRetryLimit: Int): Builder { this.fileMessagePublishRetryLimit = fileMessagePublishRetryLimit return this } - override var fileMessagePublishRetryLimit: Int = super.fileMessagePublishRetryLimit - private set + override var fileMessagePublishRetryLimit: Int = defaultConfiguration.fileMessagePublishRetryLimit override fun dedupOnSubscribe(dedupOnSubscribe: Boolean): Builder { this.dedupOnSubscribe = dedupOnSubscribe return this } - override var dedupOnSubscribe: Boolean = super.dedupOnSubscribe - private set + override var dedupOnSubscribe: Boolean = defaultConfiguration.dedupOnSubscribe override fun maximumMessagesCacheSize(maximumMessagesCacheSize: Int): Builder { this.maximumMessagesCacheSize = maximumMessagesCacheSize return this } - override var maximumMessagesCacheSize: Int = super.maximumMessagesCacheSize - private set + override var maximumMessagesCacheSize: Int = defaultConfiguration.maximumMessagesCacheSize override fun pnsdkSuffixes(pnsdkSuffixes: Map): Builder { this.pnsdkSuffixes = pnsdkSuffixes return this } - override var pnsdkSuffixes: Map = super.pnsdkSuffixes - private set + override var pnsdkSuffixes: Map = defaultConfiguration.pnsdkSuffixes override fun retryConfiguration(retryConfiguration: RetryConfiguration): Builder { this.retryConfiguration = retryConfiguration return this } - override var retryConfiguration: RetryConfiguration = super.retryConfiguration - private set + override var retryConfiguration: RetryConfiguration = defaultConfiguration.retryConfiguration override fun managePresenceListManually(managePresenceListManually: Boolean): Builder { this.managePresenceListManually = managePresenceListManually return this } - override var managePresenceListManually: Boolean = super.managePresenceListManually - private set + override var managePresenceListManually: Boolean = defaultConfiguration.managePresenceListManually override fun build(): PNConfiguration { return PNConfigurationImpl( diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/callbacks/DelegatingEventListener.java b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/Converters.java similarity index 58% rename from pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/callbacks/DelegatingEventListener.java rename to pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/Converters.java index 106615008..615d32b02 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/callbacks/DelegatingEventListener.java +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/Converters.java @@ -1,81 +1,67 @@ -package com.pubnub.internal.v2.callbacks; - -import com.pubnub.api.BasePubNub; -import com.pubnub.api.PubNub; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadata; -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembership; -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadata; -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult; -import com.pubnub.api.models.consumer.pubsub.PNMessageResult; -import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; -import com.pubnub.api.models.consumer.pubsub.PNSignalResult; -import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult; -import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult; -import com.pubnub.api.v2.callbacks.EventListener; -import com.pubnub.internal.models.consumer.pubsub.objects.PNDeleteChannelMetadataEventMessage; -import com.pubnub.internal.models.consumer.pubsub.objects.PNDeleteMembershipEventMessage; -import com.pubnub.internal.models.consumer.pubsub.objects.PNDeleteUUIDMetadataEventMessage; -import com.pubnub.internal.models.consumer.pubsub.objects.PNObjectEventMessage; -import com.pubnub.internal.models.consumer.pubsub.objects.PNObjectEventResult; -import com.pubnub.internal.models.consumer.pubsub.objects.PNSetChannelMetadataEventMessage; -import com.pubnub.internal.models.consumer.pubsub.objects.PNSetMembershipEvent; -import com.pubnub.internal.models.consumer.pubsub.objects.PNSetMembershipEventMessage; -import com.pubnub.internal.models.consumer.pubsub.objects.PNSetUUIDMetadataEventMessage; +package com.pubnub.internal.java.v2.callbacks; + +import com.pubnub.api.java.PubNubForJava; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadata; +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembership; +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadata; +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult; +import com.pubnub.api.java.v2.callbacks.EventListener; +import com.pubnub.api.models.consumer.pubsub.objects.ObjectResult; +import com.pubnub.api.models.consumer.pubsub.objects.PNDeleteChannelMetadataEventMessage; +import com.pubnub.api.models.consumer.pubsub.objects.PNDeleteMembershipEventMessage; +import com.pubnub.api.models.consumer.pubsub.objects.PNDeleteUUIDMetadataEventMessage; +import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventMessage; +import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult; +import com.pubnub.api.models.consumer.pubsub.objects.PNSetChannelMetadataEventMessage; +import com.pubnub.api.models.consumer.pubsub.objects.PNSetMembershipEvent; +import com.pubnub.api.models.consumer.pubsub.objects.PNSetMembershipEventMessage; +import com.pubnub.api.models.consumer.pubsub.objects.PNSetUUIDMetadataEventMessage; import org.jetbrains.annotations.NotNull; -import java.util.Objects; - -public class DelegatingEventListener implements EventListenerCore { - private final EventListener listener; - - public DelegatingEventListener(EventListener listener) { - this.listener = listener; +class Converters { + private Converters() { } - public EventListener getListener() { - return listener; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof DelegatingEventListener)) { - return false; + static ObjectResult objects(@NotNull PNObjectEventResult objectEvent) { + PNObjectEventMessage message = objectEvent.getExtractedMessage(); + if (message instanceof PNDeleteMembershipEventMessage) { + return getDeleteMembershipResult(objectEvent, (PNDeleteMembershipEventMessage) message); + } else if (message instanceof PNSetMembershipEventMessage) { + return getSetMembershipResult(objectEvent, (PNSetMembershipEventMessage) message); + } else if (message instanceof PNDeleteChannelMetadataEventMessage) { + return getDeleteChannelMetadataResult(objectEvent, (PNDeleteChannelMetadataEventMessage) message); + } else if (message instanceof PNSetChannelMetadataEventMessage) { + return getSetChannelMetadataResult(objectEvent, (PNSetChannelMetadataEventMessage) message); + } else if (message instanceof PNDeleteUUIDMetadataEventMessage) { + return getDeleteUuidMetadataResult(objectEvent, (PNDeleteUUIDMetadataEventMessage) message); + } else if (message instanceof PNSetUUIDMetadataEventMessage) { + return getSetUuidMetadataResult(objectEvent, (PNSetUUIDMetadataEventMessage) message); } - DelegatingEventListener that = (DelegatingEventListener) o; - return Objects.equals(listener, that.listener); + return null; } - @Override - public int hashCode() { - return Objects.hash(listener); - } - - @Override - public final void objects(@NotNull BasePubNub pubnub, @NotNull PNObjectEventResult objectEvent) { + static void objects(@NotNull PNObjectEventResult objectEvent, @NotNull EventListener emitter, @NotNull PubNubForJava pubnub) { PNObjectEventMessage message = objectEvent.getExtractedMessage(); if (message instanceof PNDeleteMembershipEventMessage) { PNMembershipResult result = getDeleteMembershipResult(objectEvent, (PNDeleteMembershipEventMessage) message); - listener.membership((PubNub) pubnub, result); + emitter.membership(pubnub, result); } else if (message instanceof PNSetMembershipEventMessage) { PNMembershipResult result = getSetMembershipResult(objectEvent, (PNSetMembershipEventMessage) message); - listener.membership((PubNub) pubnub, result); + emitter.membership(pubnub, result); } else if (message instanceof PNDeleteChannelMetadataEventMessage) { PNChannelMetadataResult result = getDeleteChannelMetadataResult(objectEvent, (PNDeleteChannelMetadataEventMessage) message); - listener.channel((PubNub) pubnub, result); + emitter.channel(pubnub, result); } else if (message instanceof PNSetChannelMetadataEventMessage) { PNChannelMetadataResult result = getSetChannelMetadataResult(objectEvent, (PNSetChannelMetadataEventMessage) message); - listener.channel((PubNub) pubnub, result); + emitter.channel(pubnub, result); } else if (message instanceof PNDeleteUUIDMetadataEventMessage) { PNUUIDMetadataResult result = getDeleteUuidMetadataResult(objectEvent, (PNDeleteUUIDMetadataEventMessage) message); - listener.uuid((PubNub) pubnub, result); + emitter.uuid(pubnub, result); } else if (message instanceof PNSetUUIDMetadataEventMessage) { PNUUIDMetadataResult result = getSetUuidMetadataResult(objectEvent, (PNSetUUIDMetadataEventMessage) message); - listener.uuid((PubNub) pubnub, result); + emitter.uuid(pubnub, result); } } @@ -178,30 +164,4 @@ static PNMembershipResult getSetMembershipResult(@NotNull PNObjectEventResult ob objectEvent.getPublisher() ); } - - - @Override - public void message(@NotNull BasePubNub pubnub, @NotNull PNMessageResult message) { - listener.message((PubNub) pubnub, message); - } - - @Override - public void presence(@NotNull BasePubNub pubnub, @NotNull PNPresenceEventResult presenceEvent) { - listener.presence((PubNub) pubnub, presenceEvent); - } - - @Override - public void signal(@NotNull BasePubNub pubnub, @NotNull PNSignalResult signal) { - listener.signal((PubNub) pubnub, signal); - } - - @Override - public void messageAction(@NotNull BasePubNub pubnub, @NotNull PNMessageActionResult messageAction) { - listener.messageAction((PubNub) pubnub, messageAction); - } - - @Override - public void file(@NotNull BasePubNub pubnub, @NotNull PNFileEventResult fileEvent) { - listener.file((PubNub) pubnub, fileEvent); - } } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/DelegatingEventListener.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/DelegatingEventListener.kt new file mode 100644 index 000000000..b3b557399 --- /dev/null +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/DelegatingEventListener.kt @@ -0,0 +1,53 @@ +package com.pubnub.internal.java.v2.callbacks + +import com.pubnub.api.PubNub +import com.pubnub.api.java.PubNubForJava +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.api.models.consumer.pubsub.PNSignalResult +import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult +import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult +import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult +import com.pubnub.api.v2.callbacks.EventListener + +/** + * Implement this interface and pass it into [EventEmitter.addListener] to listen for events from the PubNub real-time + * network. + */ +data class DelegatingEventListener( + private val listener: com.pubnub.api.java.v2.callbacks.EventListener, + private val pubnubJava: PubNubForJava, +) : EventListener { + override fun message(pubnub: PubNub, result: PNMessageResult) { + listener.message(pubnubJava, result) + } + + override fun presence( + pubnub: PubNub, + result: PNPresenceEventResult, + ) { + listener.presence(pubnubJava, result) + } + + override fun signal(pubnub: PubNub, result: PNSignalResult) { + listener.signal(pubnubJava, result) + } + + override fun messageAction( + pubnub: PubNub, + result: PNMessageActionResult, + ) { + listener.messageAction(pubnubJava, result) + } + + override fun file( + pubnub: PubNub, + result: PNFileEventResult, + ) { + listener.file(pubnubJava, result) + } + + override fun objects(pubnub: PubNub, result: PNObjectEventResult) { + Converters.objects(result, listener, pubnubJava) + } +} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/DelegatingStatusListener.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/DelegatingStatusListener.kt new file mode 100644 index 000000000..af4fb2939 --- /dev/null +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/DelegatingStatusListener.kt @@ -0,0 +1,12 @@ +package com.pubnub.internal.java.v2.callbacks + +import com.pubnub.api.PubNub +import com.pubnub.api.java.PubNubForJava +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.api.v2.callbacks.StatusListener + +data class DelegatingStatusListener(private val listener: com.pubnub.api.java.v2.callbacks.StatusListener, private val pubnubJava: PubNubForJava) : StatusListener { + override fun status(pubnub: PubNub, status: PNStatus) { + listener.status(pubnubJava, status) + } +} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/EventEmitterInternal.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/EventEmitterInternal.kt new file mode 100644 index 000000000..9a74afe3a --- /dev/null +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/callbacks/EventEmitterInternal.kt @@ -0,0 +1,269 @@ +package com.pubnub.internal.java.v2.callbacks + +import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult +import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult +import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult +import com.pubnub.api.java.v2.callbacks.handlers.OnChannelMetadataHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnFileHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnMembershipHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnMessageActionHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnMessageHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnPresenceHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnSignalHandler +import com.pubnub.api.java.v2.callbacks.handlers.OnUuidMetadataHandler +import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult +import com.pubnub.api.v2.callbacks.EventEmitter + +/** + * Interface implemented by objects that are the source of real time events from the PubNub network. + */ +interface EventEmitterInternal : EventEmitter, com.pubnub.api.java.v2.callbacks.EventEmitter { + /** + * Sets the handler for incoming message events. + * This method allows the assignment of an [OnMessageHandler] implementation or lambda expression to handle + * incoming messages. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to message events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`setOnMessage(pnMessageResult -> System.out.println("Received: " + pnMessageResult.getMessage()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`setOnMessage(null);
+     `
* + * + * @param onMessageHandler An implementation of [OnMessageHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + override fun setOnMessage(onMessageHandler: OnMessageHandler?) { + onMessage = onMessageHandler?.let { handler -> + handler::handle + } + } + + /** + * Sets the handler for incoming signal events. + * This method allows the assignment of an [OnSignalHandler] implementation or lambda expression to handle + * incoming signals. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to signal events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`setOnSignal(pnSignalResult -> System.out.println("Received: " + pnSignalResult.getMessage()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`setOnSignal(null);
+     `
* + * + * @param onSignalHandler An implementation of [OnSignalHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + override fun setOnSignal(onSignalHandler: OnSignalHandler?) { + onSignal = onSignalHandler?.let { handler -> + handler::handle + } + } + + /** + * Sets the handler for incoming presence events. + * This method allows the assignment of an [OnPresenceHandler] implementation or lambda expression to handle + * incoming presence events. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to presence events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`onPresenceHandler(pnPresenceEventResult -> System.out.println("Received: " + pnPresenceEventResult.getEvent()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`onPresenceHandler(null);
+     `
* + * + * @param onPresenceHandler An implementation of [OnPresenceHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + override fun setOnPresence(onPresenceHandler: OnPresenceHandler?) { + onPresence = onPresenceHandler?.let { handler -> + handler::handle + } + } + + /** + * Sets the handler for incoming messageAction events. + * This method allows the assignment of an [OnMessageActionHandler] implementation or lambda expression to handle + * incoming presence events. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to messageAction events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`onMessageActionHandler(pnMessageActionResult -> System.out.println("Received: " + pnMessageActionResult.getMessageAction()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`onMessageActionHandler(null);
+     `
* + * + * @param onMessageActionHandler An implementation of [OnMessageActionHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + override fun setOnMessageAction(onMessageActionHandler: OnMessageActionHandler?) { + onMessageAction = onMessageActionHandler?.let { handler -> + handler::handle + } + } + + /** + * Sets the handler for incoming uuidMetadata events. + * This method allows the assignment of an [OnUuidMetadataHandler] implementation or lambda expression to handle + * incoming presence events. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to uuidMetadata events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`onUuidMetadataHandler(pnUUIDMetadataResult -> System.out.println("Received: " + pnUUIDMetadataResult.getData()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`onUuidMetadataHandler(null);
+     `
* + * + * @param onUuidMetadataHandler An implementation of [OnUuidMetadataHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + override fun setOnUuidMetadata(onUuidMetadataHandler: OnUuidMetadataHandler?) { + onObjects = (onObjects as? CombinedObjectHandler)?.copy(uuidHandler = onUuidMetadataHandler) ?: CombinedObjectHandler(uuidHandler = onUuidMetadataHandler) + } + + /** + * Sets the handler for incoming channelMetadata events. + * This method allows the assignment of an [OnChannelMetadataHandler] implementation or lambda expression to handle + * incoming presence events. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to channelMetadata events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`onChannelMetadataHandler(pnChannelMetadataResult -> System.out.println("Received: " +  pnChannelMetadataResult.getEvent()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`onChannelMetadataHandler(null);
+     `
* + * + * @param onChannelMetadataHandler An implementation of [OnChannelMetadataHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + override fun setOnChannelMetadata(onChannelMetadataHandler: OnChannelMetadataHandler?) { + onObjects = (onObjects as? CombinedObjectHandler)?.copy(channelHandler = onChannelMetadataHandler) ?: CombinedObjectHandler(channelHandler = onChannelMetadataHandler) + } + + /** + * Sets the handler for incoming membership events. + * This method allows the assignment of an [OnMembershipHandler] implementation or lambda expression to handle + * incoming presence events. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to membership events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`onMembershipHandler(pnMembershipResult -> System.out.println("Received: " +  pnMembershipResult.getEvent()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`onMembershipHandler(null);
+     `
* + * + * @param onMembershipHandler An implementation of [OnMembershipHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + override fun setOnMembership(onMembershipHandler: OnMembershipHandler?) { + onObjects = (onObjects as? CombinedObjectHandler)?.copy(membershipHandler = onMembershipHandler) ?: CombinedObjectHandler(membershipHandler = onMembershipHandler) + } + + /** + * Sets the handler for incoming file events. + * This method allows the assignment of an [OnFileHandler] implementation or lambda expression to handle + * incoming presence events. + * + * To deactivate the current behavior, simply set this property to `null`. + * + * Note that this property allows for the assignment of a singular behavior at a time, as any new assignment will override the previous one. + * For scenarios requiring multiple behaviors in response to file events, it is advisable + * to utilize [EventEmitter.addListener]. + * + * + * **Setting a Behavior Example:** + *
`onFileHandler(pnFileEventResult -> System.out.println("Received: " +  pnFileEventResult.getMessage()));
+     `
* + * + * + * **Removing a Behavior Example:** + *
`onFileHandler(null);
+     `
* + * + * @param onFileHandler An implementation of [OnFileHandler] or a lambda expression to handle + * incoming messages. It can be `null` to remove the current handler. + */ + override fun setOnFile(onFileHandler: OnFileHandler?) { + onFile = onFileHandler?.let { handler -> + handler::handle + } + } +} + +private data class CombinedObjectHandler( + val membershipHandler: OnMembershipHandler? = null, + val uuidHandler: OnUuidMetadataHandler? = null, + val channelHandler: OnChannelMetadataHandler? = null, +) : ((PNObjectEventResult) -> Unit) { + override fun invoke(objectEvent: PNObjectEventResult) { + val objectResult = Converters.objects(objectEvent) + when (objectResult) { + is PNMembershipResult -> membershipHandler?.handle(objectResult) + is PNChannelMetadataResult -> channelHandler?.handle(objectResult) + is PNUUIDMetadataResult -> uuidHandler?.handle(objectResult) + } + } +} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/ChannelGroupImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/ChannelGroupImpl.kt new file mode 100644 index 000000000..df3a0562d --- /dev/null +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/ChannelGroupImpl.kt @@ -0,0 +1,20 @@ +package com.pubnub.internal.java.v2.entities + +import com.pubnub.api.v2.subscriptions.EmptyOptions +import com.pubnub.api.v2.subscriptions.SubscriptionOptions +import com.pubnub.internal.java.PubNubForJavaImpl +import com.pubnub.internal.java.v2.subscription.SubscriptionImpl +import com.pubnub.internal.v2.entities.ChannelGroupImpl +import com.pubnub.internal.v2.entities.ChannelGroupName + +class ChannelGroupImpl(pubnub: PubNubForJavaImpl, channelGroupName: ChannelGroupName) : + ChannelGroupImpl(pubnub, channelGroupName), + com.pubnub.api.java.v2.entities.ChannelGroup { + override fun subscription(options: SubscriptionOptions): SubscriptionImpl { + return SubscriptionImpl.from(super.subscription(options)) + } + + override fun subscription(): com.pubnub.api.java.v2.subscriptions.Subscription { + return subscription(EmptyOptions) + } +} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/ChannelImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/ChannelImpl.kt new file mode 100644 index 000000000..e16b2138b --- /dev/null +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/ChannelImpl.kt @@ -0,0 +1,33 @@ +package com.pubnub.internal.java.v2.entities + +import com.pubnub.api.endpoints.pubsub.Signal +import com.pubnub.api.java.v2.endpoints.pubsub.PublishBuilder +import com.pubnub.api.v2.subscriptions.EmptyOptions +import com.pubnub.api.v2.subscriptions.SubscriptionOptions +import com.pubnub.internal.java.PubNubForJavaImpl +import com.pubnub.internal.java.v2.subscription.SubscriptionImpl +import com.pubnub.internal.v2.entities.ChannelImpl +import com.pubnub.internal.v2.entities.ChannelName + +class ChannelImpl(private val pubnub: PubNubForJavaImpl, channelName: ChannelName) : + com.pubnub.api.java.v2.entities.Channel, ChannelImpl(pubnub, channelName) { + override fun subscription(): com.pubnub.api.java.v2.subscriptions.Subscription { + return subscription(EmptyOptions) + } + + override fun subscription(options: SubscriptionOptions): SubscriptionImpl { + return SubscriptionImpl.from(super.subscription(options)) + } + + override fun publish(message: Any): PublishBuilder { + return pubnub.publish(message, name) + } + + override fun signal(message: Any): Signal { + return pubnub.signal(message, name) + } + + override fun fire(message: Any): PublishBuilder { + return pubnub.publish(message, name).replicate(false).shouldStore(false) + } +} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/ChannelMetadataImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/ChannelMetadataImpl.kt new file mode 100644 index 000000000..e0e9c5235 --- /dev/null +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/ChannelMetadataImpl.kt @@ -0,0 +1,20 @@ +package com.pubnub.internal.java.v2.entities + +import com.pubnub.api.java.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.EmptyOptions +import com.pubnub.api.v2.subscriptions.SubscriptionOptions +import com.pubnub.internal.java.PubNubForJavaImpl +import com.pubnub.internal.java.v2.subscription.SubscriptionImpl +import com.pubnub.internal.v2.entities.ChannelName + +class ChannelMetadataImpl(pubnub: PubNubForJavaImpl, channelName: ChannelName) : + com.pubnub.internal.v2.entities.ChannelMetadataImpl(pubnub, channelName), + com.pubnub.api.java.v2.entities.ChannelMetadata { + override fun subscription(options: SubscriptionOptions): SubscriptionImpl { + return SubscriptionImpl.from(super.subscription(options)) + } + + override fun subscription(): Subscription { + return subscription(EmptyOptions) + } +} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/UserMetadataImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/UserMetadataImpl.kt new file mode 100644 index 000000000..5edd9cd84 --- /dev/null +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/entities/UserMetadataImpl.kt @@ -0,0 +1,20 @@ +package com.pubnub.internal.java.v2.entities + +import com.pubnub.api.v2.subscriptions.EmptyOptions +import com.pubnub.api.v2.subscriptions.SubscriptionOptions +import com.pubnub.internal.java.PubNubForJavaImpl +import com.pubnub.internal.java.v2.subscription.SubscriptionImpl +import com.pubnub.internal.v2.entities.ChannelName +import com.pubnub.internal.v2.entities.UserMetadataImpl + +class UserMetadataImpl(pubnub: PubNubForJavaImpl, channelName: ChannelName) : + UserMetadataImpl(pubnub, channelName), + com.pubnub.api.java.v2.entities.UserMetadata { + override fun subscription(options: SubscriptionOptions): SubscriptionImpl { + return SubscriptionImpl.from(super.subscription(options)) + } + + override fun subscription(): com.pubnub.api.java.v2.subscriptions.Subscription { + return subscription(EmptyOptions) + } +} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/subscription/EmitterHelper.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/subscription/EmitterHelper.kt new file mode 100644 index 000000000..dbf098662 --- /dev/null +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/subscription/EmitterHelper.kt @@ -0,0 +1,96 @@ +// package com.pubnub.internal.java.v2.subscription +// +// import com.pubnub.api.PubNub +// import com.pubnub.api.java.models.consumer.objects_api.channel.PNChannelMetadataResult +// import com.pubnub.api.java.models.consumer.objects_api.membership.PNMembershipResult +// import com.pubnub.api.java.models.consumer.objects_api.uuid.PNUUIDMetadataResult +// import com.pubnub.api.models.consumer.pubsub.PNMessageResult +// import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +// import com.pubnub.api.models.consumer.pubsub.PNSignalResult +// import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult +// import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult +// import com.pubnub.api.v2.callbacks.BaseEventEmitter +// import com.pubnub.api.v2.callbacks.EventListener +// import com.pubnub.api.v2.callbacks.handlers.OnChannelMetadataHandler +// import com.pubnub.api.v2.callbacks.handlers.OnFileHandler +// import com.pubnub.api.v2.callbacks.handlers.OnMembershipHandler +// import com.pubnub.api.v2.callbacks.handlers.OnMessageActionHandler +// import com.pubnub.api.v2.callbacks.handlers.OnMessageHandler +// import com.pubnub.api.v2.callbacks.handlers.OnPresenceHandler +// import com.pubnub.api.v2.callbacks.handlers.OnSignalHandler +// import com.pubnub.api.v2.callbacks.handlers.OnUuidMetadataHandler +// import com.pubnub.internal.v2.callbacks.DelegatingEventListener +// import com.pubnub.internal.v2.callbacks.EventListenerCore +// +// class EmitterHelper { +// private val listener = object : EventListener { +// override fun message( +// pubnub: PubNub, +// result: PNMessageResult, +// ) { +// onMessage?.handle(result) +// } +// +// override fun presence( +// pubnub: PubNub, +// result: PNPresenceEventResult, +// ) { +// onPresence?.handle(result) +// } +// +// override fun signal( +// pubnub: PubNub, +// result: PNSignalResult, +// ) { +// onSignal?.handle(result) +// } +// +// override fun messageAction( +// pubnub: PubNub, +// result: PNMessageActionResult, +// ) { +// onMessageAction?.handle(result) +// } +// +// override fun uuid( +// pubnub: PubNub, +// result: PNUUIDMetadataResult, +// ) { +// onUuid?.handle(result) +// } +// +// override fun channel( +// pubnub: PubNub, +// result: PNChannelMetadataResult, +// ) { +// onChannel?.handle(result) +// } +// +// override fun membership( +// pubnub: PubNub, +// result: PNMembershipResult, +// ) { +// onMembership?.handle(result) +// } +// +// override fun file( +// pubnub: PubNub, +// result: PNFileEventResult, +// ) { +// onFile?.handle(result) +// } +// } +// +// fun initialize(eventEmitter: BaseEventEmitter) { +// eventEmitter.addListener(DelegatingEventListener(listener)) +// } +// +// var onMessage: OnMessageHandler? = null +// var onPresence: OnPresenceHandler? = null +// var onSignal: OnSignalHandler? = null +// var onMessageAction: OnMessageActionHandler? = null +// var onUuid: OnUuidMetadataHandler? = null +// var onChannel: OnChannelMetadataHandler? = null +// var onMembership: OnMembershipHandler? = null +// var onFile: OnFileHandler? = null +// } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/subscription/SubscriptionImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/subscription/SubscriptionImpl.kt new file mode 100644 index 000000000..81b5e28f3 --- /dev/null +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/subscription/SubscriptionImpl.kt @@ -0,0 +1,60 @@ +package com.pubnub.internal.java.v2.subscription + +import com.pubnub.api.callbacks.Listener +import com.pubnub.api.java.v2.callbacks.EventListener +import com.pubnub.api.java.v2.callbacks.StatusListener +import com.pubnub.api.v2.subscriptions.EmptyOptions +import com.pubnub.api.v2.subscriptions.SubscriptionCursor +import com.pubnub.api.v2.subscriptions.SubscriptionOptions +import com.pubnub.internal.java.PubNubForJavaImpl +import com.pubnub.internal.java.v2.callbacks.DelegatingEventListener +import com.pubnub.internal.java.v2.callbacks.DelegatingStatusListener +import com.pubnub.internal.java.v2.callbacks.EventEmitterInternal +import com.pubnub.internal.v2.entities.ChannelGroupName +import com.pubnub.internal.v2.entities.ChannelName +import com.pubnub.internal.v2.subscription.SubscriptionImpl +import com.pubnub.internal.v2.subscription.SubscriptionInternal + +class SubscriptionImpl( + override val pubnub: PubNubForJavaImpl, + channels: Set = emptySet(), + channelGroups: Set = emptySet(), + options: SubscriptionOptions = EmptyOptions +) : + SubscriptionImpl(pubnub, channels, channelGroups, options), + com.pubnub.api.java.v2.subscriptions.Subscription, + EventEmitterInternal { + override fun plus( + subscription: com.pubnub.api.java.v2.subscriptions.Subscription, + ): com.pubnub.api.java.v2.subscriptions.SubscriptionSet { + return SubscriptionSetImpl(pubnub, setOf(this, subscription) as Set) + } + + override fun subscribe() { + subscribe(SubscriptionCursor(0)) + } + + override fun addListener(listener: EventListener) { + addListener(DelegatingEventListener(listener, pubnub)) + } + + override fun removeListener(listener: Listener) { + if (listener is EventListener) { + super.removeListener(DelegatingEventListener(listener, pubnub)) + } // no else here to support SubscribeCallbacks which implement both interfaces + if (listener is StatusListener) { + super.removeListener(DelegatingStatusListener(listener, pubnub)) + } + } + + companion object { + fun from(subscription: SubscriptionImpl): com.pubnub.internal.java.v2.subscription.SubscriptionImpl { + return com.pubnub.internal.java.v2.subscription.SubscriptionImpl( + subscription.pubnub as PubNubForJavaImpl, + subscription.channels, + subscription.channelGroups, + subscription.options + ) + } + } +} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/subscription/SubscriptionSetImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/subscription/SubscriptionSetImpl.kt new file mode 100644 index 000000000..782d8232f --- /dev/null +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/subscription/SubscriptionSetImpl.kt @@ -0,0 +1,63 @@ +package com.pubnub.internal.java.v2.subscription + +import com.pubnub.api.callbacks.Listener +import com.pubnub.api.java.v2.callbacks.EventListener +import com.pubnub.api.java.v2.callbacks.StatusListener +import com.pubnub.api.java.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.SubscriptionCursor +import com.pubnub.internal.java.PubNubForJavaImpl +import com.pubnub.internal.java.v2.callbacks.DelegatingEventListener +import com.pubnub.internal.java.v2.callbacks.DelegatingStatusListener +import com.pubnub.internal.java.v2.callbacks.EventEmitterInternal +import com.pubnub.internal.v2.subscription.SubscriptionInternal +import com.pubnub.internal.v2.subscription.SubscriptionSetImpl + +class SubscriptionSetImpl( + private val pubnubJava: PubNubForJavaImpl, + initialSubscriptions: Set +) : + SubscriptionSetImpl(pubnubJava, initialSubscriptions), + com.pubnub.api.java.v2.subscriptions.SubscriptionSet, + EventEmitterInternal { + override val subscriptions: Set + get() = super.subscriptions.map { it as Subscription }.toSet() + + override fun plusAssign(subscription: Subscription) { + super.addInternal(subscription as SubscriptionInternal) + } + + override fun minusAssign(subscription: Subscription) { + super.removeInternal(subscription as SubscriptionInternal) + } + + /** + * Start receiving events from the subscriptions (or subscriptions) represented by this object. + * + * The PubNub client will start a network connection to the server if it doesn't have one already, + * or will alter the existing connection to add channels and groups requested by this subscriptions if needed. + */ + override fun subscribe() { + subscribe(SubscriptionCursor(0)) + } + + override fun add(subscription: Subscription) { + plusAssign(subscription) + } + + override fun remove(subscription: Subscription) { + minusAssign(subscription) + } + + override fun addListener(listener: EventListener) { + addListener(DelegatingEventListener(listener, pubnubJava)) + } + + override fun removeListener(listener: Listener) { + if (listener is EventListener) { + super.removeListener(DelegatingEventListener(listener, pubnubJava)) + } // no else here to support SubscribeCallbacks which implement both interfaces + if (listener is StatusListener) { + super.removeListener(DelegatingStatusListener(listener, pubnubJava)) + } + } +} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/callbacks/DelegatingStatusListener.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/callbacks/DelegatingStatusListener.kt deleted file mode 100644 index da293c991..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/callbacks/DelegatingStatusListener.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.pubnub.internal.callbacks - -import com.pubnub.api.BasePubNub -import com.pubnub.api.PubNub -import com.pubnub.api.models.consumer.PNStatus -import com.pubnub.api.v2.callbacks.StatusListener - -data class DelegatingStatusListener(private val listener: StatusListener) : - com.pubnub.internal.v2.callbacks.StatusListenerCore { - override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, - status: PNStatus, - ) { - listener.status(pubnub as PubNub, status) - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallback.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallback.kt deleted file mode 100644 index 0d5ff5880..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallback.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.pubnub.internal.callbacks - -import com.pubnub.api.BasePubNub -import com.pubnub.api.PubNub -import com.pubnub.api.callbacks.SubscribeCallback -import com.pubnub.api.models.consumer.PNStatus -import com.pubnub.internal.v2.callbacks.DelegatingEventListener -import com.pubnub.internal.v2.callbacks.StatusListenerCore - -data class DelegatingSubscribeCallback(private val listener: SubscribeCallback) : - com.pubnub.internal.callbacks.SubscribeCallback, - DelegatingEventListener( - listener, - ), - StatusListenerCore { - override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, - status: PNStatus, - ) { - listener.status(pubnub as PubNub, status) - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/ChannelGroupImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/ChannelGroupImpl.kt deleted file mode 100644 index 883b7672a..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/ChannelGroupImpl.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.pubnub.internal.v2.entities - -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.entities.ChannelGroup -import com.pubnub.api.v2.subscriptions.EmptyOptions -import com.pubnub.api.v2.subscriptions.Subscription -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.v2.subscription.SubscriptionImpl - -class ChannelGroupImpl(pubnub: PubNubImpl, channelGroupName: String) : - BaseChannelGroupImpl( - pubnub.pubNubCore, - ChannelGroupName(channelGroupName), - { channels, channelGroups, options -> SubscriptionImpl(pubnub, channels, channelGroups, options) }, - ), - ChannelGroup { - override fun subscription(): Subscription { - return subscription(EmptyOptions) - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/ChannelImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/ChannelImpl.kt deleted file mode 100644 index 00e7e847b..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/ChannelImpl.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.pubnub.internal.v2.entities - -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.endpoints.pubsub.PublishBuilder -import com.pubnub.api.v2.endpoints.pubsub.SignalBuilder -import com.pubnub.api.v2.entities.Channel -import com.pubnub.api.v2.subscriptions.EmptyOptions -import com.pubnub.api.v2.subscriptions.Subscription -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.v2.subscription.SubscriptionImpl - -class ChannelImpl(pubnub: PubNubImpl, channelName: String) : - BaseChannelImpl( - pubnub.pubNubCore, - ChannelName(channelName), - { channels, channelGroups, options -> SubscriptionImpl(pubnub, channels, channelGroups, options) }, - ), - Channel { - private val pubNubImpl: PubNubImpl = pubnub - - override fun subscription(): Subscription { - return subscription(EmptyOptions) - } - - override fun publish(message: Any): PublishBuilder { - return pubNubImpl.publish(message, channelName.id) - } - - override fun signal(message: Any): SignalBuilder { - return pubNubImpl.signal(message, channelName.id) - } - - override fun fire(message: Any): PublishBuilder { - return pubNubImpl.publish(message, channelName.id).replicate(false).shouldStore(false) - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/ChannelMetadataImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/ChannelMetadataImpl.kt deleted file mode 100644 index 8e9376a32..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/ChannelMetadataImpl.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.pubnub.internal.v2.entities - -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.entities.ChannelMetadata -import com.pubnub.api.v2.subscriptions.EmptyOptions -import com.pubnub.api.v2.subscriptions.Subscription -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.v2.subscription.SubscriptionImpl - -class ChannelMetadataImpl(pubnub: PubNubImpl, id: String) : - BaseChannelMetadataImpl( - pubnub.pubNubCore, - ChannelName(id), - { channels, channelGroups, options -> SubscriptionImpl(pubnub, channels, channelGroups, options) }, - ), - ChannelMetadata { - override fun subscription(): Subscription { - return subscription(EmptyOptions) - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/UserMetadataImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/UserMetadataImpl.kt deleted file mode 100644 index 2feac95a8..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/entities/UserMetadataImpl.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.pubnub.internal.v2.entities - -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.entities.UserMetadata -import com.pubnub.api.v2.subscriptions.EmptyOptions -import com.pubnub.api.v2.subscriptions.Subscription -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.v2.subscription.SubscriptionImpl - -class UserMetadataImpl(pubnub: PubNubImpl, id: String) : - BaseUserMetadataImpl( - pubnub.pubNubCore, - ChannelName(id), - { channels, channelGroups, options -> SubscriptionImpl(pubnub, channels, channelGroups, options) }, - ), - UserMetadata { - override fun subscription(): Subscription { - return subscription(EmptyOptions) - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/subscription/EmitterHelper.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/subscription/EmitterHelper.kt deleted file mode 100644 index cc77df994..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/subscription/EmitterHelper.kt +++ /dev/null @@ -1,96 +0,0 @@ -package com.pubnub.internal.v2.subscription - -import com.pubnub.api.PubNub -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult -import com.pubnub.api.models.consumer.pubsub.PNMessageResult -import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult -import com.pubnub.api.models.consumer.pubsub.PNSignalResult -import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult -import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult -import com.pubnub.api.v2.callbacks.BaseEventEmitter -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.callbacks.handlers.OnChannelMetadataHandler -import com.pubnub.api.v2.callbacks.handlers.OnFileHandler -import com.pubnub.api.v2.callbacks.handlers.OnMembershipHandler -import com.pubnub.api.v2.callbacks.handlers.OnMessageActionHandler -import com.pubnub.api.v2.callbacks.handlers.OnMessageHandler -import com.pubnub.api.v2.callbacks.handlers.OnPresenceHandler -import com.pubnub.api.v2.callbacks.handlers.OnSignalHandler -import com.pubnub.api.v2.callbacks.handlers.OnUuidMetadataHandler -import com.pubnub.internal.v2.callbacks.DelegatingEventListener -import com.pubnub.internal.v2.callbacks.EventListenerCore - -class EmitterHelper { - private val listener = object : EventListener { - override fun message( - pubnub: PubNub, - result: PNMessageResult, - ) { - onMessage?.handle(result) - } - - override fun presence( - pubnub: PubNub, - result: PNPresenceEventResult, - ) { - onPresence?.handle(result) - } - - override fun signal( - pubnub: PubNub, - result: PNSignalResult, - ) { - onSignal?.handle(result) - } - - override fun messageAction( - pubnub: PubNub, - result: PNMessageActionResult, - ) { - onMessageAction?.handle(result) - } - - override fun uuid( - pubnub: PubNub, - result: PNUUIDMetadataResult, - ) { - onUuid?.handle(result) - } - - override fun channel( - pubnub: PubNub, - result: PNChannelMetadataResult, - ) { - onChannel?.handle(result) - } - - override fun membership( - pubnub: PubNub, - result: PNMembershipResult, - ) { - onMembership?.handle(result) - } - - override fun file( - pubnub: PubNub, - result: PNFileEventResult, - ) { - onFile?.handle(result) - } - } - - fun initialize(eventEmitter: BaseEventEmitter) { - eventEmitter.addListener(DelegatingEventListener(listener)) - } - - var onMessage: OnMessageHandler? = null - var onPresence: OnPresenceHandler? = null - var onSignal: OnSignalHandler? = null - var onMessageAction: OnMessageActionHandler? = null - var onUuid: OnUuidMetadataHandler? = null - var onChannel: OnChannelMetadataHandler? = null - var onMembership: OnMembershipHandler? = null - var onFile: OnFileHandler? = null -} diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/subscription/SubscriptionImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/subscription/SubscriptionImpl.kt deleted file mode 100644 index 73c4a9ba0..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/subscription/SubscriptionImpl.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.pubnub.internal.v2.subscription - -import com.pubnub.api.callbacks.Listener -import com.pubnub.api.callbacks.SubscribeCallback -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.callbacks.handlers.OnChannelMetadataHandler -import com.pubnub.api.v2.callbacks.handlers.OnFileHandler -import com.pubnub.api.v2.callbacks.handlers.OnMembershipHandler -import com.pubnub.api.v2.callbacks.handlers.OnMessageActionHandler -import com.pubnub.api.v2.callbacks.handlers.OnMessageHandler -import com.pubnub.api.v2.callbacks.handlers.OnPresenceHandler -import com.pubnub.api.v2.callbacks.handlers.OnSignalHandler -import com.pubnub.api.v2.callbacks.handlers.OnUuidMetadataHandler -import com.pubnub.api.v2.subscriptions.Subscription -import com.pubnub.api.v2.subscriptions.SubscriptionCursor -import com.pubnub.api.v2.subscriptions.SubscriptionOptions -import com.pubnub.api.v2.subscriptions.SubscriptionSet -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.callbacks.DelegatingSubscribeCallback -import com.pubnub.internal.managers.AnnouncementCallback -import com.pubnub.internal.v2.callbacks.DelegatingEventListener -import com.pubnub.internal.v2.callbacks.EventEmitterImpl -import com.pubnub.internal.v2.entities.ChannelGroupName -import com.pubnub.internal.v2.entities.ChannelName - -class SubscriptionImpl - @JvmOverloads - constructor( - val pubnub: PubNubImpl, - channels: Set, - channelGroups: Set, - options: SubscriptionOptions, - private val emitterHelper: EmitterHelper = EmitterHelper(), - eventEmitterFactory: (BaseSubscriptionImpl) -> EventEmitterImpl = { baseSubscriptionImpl -> - EventEmitterImpl(AnnouncementCallback.Phase.SUBSCRIPTION, baseSubscriptionImpl::accepts) - }, - ) : Subscription, - BaseSubscriptionImpl(pubnub.pubNubCore, channels, channelGroups, options, eventEmitterFactory) { - init { - emitterHelper.initialize(eventEmitter) - } - - /** - * Add a listener. - * - * @param listener The listener to be added. - */ - override fun addListener(listener: EventListener) { - addListener(DelegatingEventListener(listener)) - } - - /** - * Create a [SubscriptionSet] that contains both subscriptions. - * - * @param subscription the other [Subscription] to add to the [SubscriptionSet] - */ - override fun plus(subscription: Subscription): SubscriptionSet { - return pubnub.subscriptionSetOf(setOf(this, subscription)) - } - - override fun subscribe() { - subscribe(SubscriptionCursor(0)) - } - - override fun removeListener(listener: Listener) { - when (listener) { - is SubscribeCallback -> { - super.removeListener(DelegatingSubscribeCallback(listener)) - } - - is EventListener -> { - super.removeListener(DelegatingEventListener(listener)) - } - - else -> { - super.removeListener(listener) - } - } - } - - override fun setOnMessage(onMessageHandler: OnMessageHandler?) { - emitterHelper.onMessage = onMessageHandler - } - - override fun setOnSignal(onSignalHandler: OnSignalHandler?) { - emitterHelper.onSignal = onSignalHandler - } - - override fun setOnPresence(onPresenceHandler: OnPresenceHandler?) { - emitterHelper.onPresence = onPresenceHandler - } - - override fun setOnMessageAction(onMessageActionHandler: OnMessageActionHandler?) { - emitterHelper.onMessageAction = onMessageActionHandler - } - - override fun setOnUuidMetadata(onUuidMetadataHandler: OnUuidMetadataHandler?) { - emitterHelper.onUuid = onUuidMetadataHandler - } - - override fun setOnChannelMetadata(onChannelMetadataHandler: OnChannelMetadataHandler?) { - emitterHelper.onChannel = onChannelMetadataHandler - } - - override fun setOnMembership(onMembershipHandler: OnMembershipHandler?) { - emitterHelper.onMembership = onMembershipHandler - } - - override fun setOnFile(onFileHandler: OnFileHandler?) { - emitterHelper.onFile = onFileHandler - } - } diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/subscription/SubscriptionSetImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/subscription/SubscriptionSetImpl.kt deleted file mode 100644 index c0e8916b0..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/v2/subscription/SubscriptionSetImpl.kt +++ /dev/null @@ -1,92 +0,0 @@ -package com.pubnub.internal.v2.subscription - -import com.pubnub.api.callbacks.Listener -import com.pubnub.api.callbacks.SubscribeCallback -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.callbacks.handlers.OnChannelMetadataHandler -import com.pubnub.api.v2.callbacks.handlers.OnFileHandler -import com.pubnub.api.v2.callbacks.handlers.OnMembershipHandler -import com.pubnub.api.v2.callbacks.handlers.OnMessageActionHandler -import com.pubnub.api.v2.callbacks.handlers.OnMessageHandler -import com.pubnub.api.v2.callbacks.handlers.OnPresenceHandler -import com.pubnub.api.v2.callbacks.handlers.OnSignalHandler -import com.pubnub.api.v2.callbacks.handlers.OnUuidMetadataHandler -import com.pubnub.api.v2.subscriptions.Subscription -import com.pubnub.api.v2.subscriptions.SubscriptionCursor -import com.pubnub.api.v2.subscriptions.SubscriptionSet -import com.pubnub.internal.PubNubCore -import com.pubnub.internal.callbacks.DelegatingSubscribeCallback -import com.pubnub.internal.v2.callbacks.DelegatingEventListener - -class SubscriptionSetImpl - @JvmOverloads - constructor( - pubnub: PubNubCore, - initialSubscriptions: Set, - private val emitterHelper: EmitterHelper = EmitterHelper(), - ) : SubscriptionSet, BaseSubscriptionSetImpl(pubnub, initialSubscriptions) { - init { - emitterHelper.initialize(eventEmitter) - } - - override fun subscribe() { - subscribe(SubscriptionCursor(0)) - } - - /** - * Add a listener. - * - * @param listener The listener to be added. - */ - override fun addListener(listener: EventListener) { - addListener(DelegatingEventListener(listener)) - } - - override fun removeListener(listener: Listener) { - when (listener) { - is SubscribeCallback -> { - super.removeListener(DelegatingSubscribeCallback(listener)) - } - - is EventListener -> { - super.removeListener(DelegatingEventListener(listener)) - } - - else -> { - super.removeListener(listener) - } - } - } - - override fun setOnMessage(onMessageHandler: OnMessageHandler?) { - emitterHelper.onMessage = onMessageHandler - } - - override fun setOnSignal(onSignalHandler: OnSignalHandler?) { - emitterHelper.onSignal = onSignalHandler - } - - override fun setOnPresence(onPresenceHandler: OnPresenceHandler?) { - emitterHelper.onPresence = onPresenceHandler - } - - override fun setOnMessageAction(onMessageActionHandler: OnMessageActionHandler?) { - emitterHelper.onMessageAction = onMessageActionHandler - } - - override fun setOnUuidMetadata(onUuidHandler: OnUuidMetadataHandler?) { - emitterHelper.onUuid = onUuidHandler - } - - override fun setOnChannelMetadata(onChannelMetadataHandler: OnChannelMetadataHandler?) { - emitterHelper.onChannel = onChannelMetadataHandler - } - - override fun setOnMembership(onMembershipHandler: OnMembershipHandler?) { - emitterHelper.onMembership = onMembershipHandler - } - - override fun setOnFile(onFileHandler: OnFileHandler?) { - emitterHelper.onFile = onFileHandler - } - } diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/api/PNConfigurationTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/api/PNConfigurationTest.kt index 6c9ad7f37..88bd0bd49 100644 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/api/PNConfigurationTest.kt +++ b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/api/PNConfigurationTest.kt @@ -1,121 +1,120 @@ package com.pubnub.api -import com.pubnub.api.crypto.CryptoModule -import com.pubnub.api.enums.PNReconnectionPolicy -import com.pubnub.api.retry.RetryConfiguration -import com.pubnub.api.v2.PNConfigurationOverride -import com.pubnub.internal.BasePubNubImpl +import com.pubnub.api.java.v2.PNConfiguration +import com.pubnub.api.java.v2.PNConfigurationOverride import org.junit.Assert import org.junit.Test class PNConfigurationTest { - @Suppress("DEPRECATION") - @Test(expected = PubNubException::class) - fun setUUIDToEmptyString() { - PNConfiguration("") - } - - @Suppress("DEPRECATION") - @Test(expected = PubNubException::class) - fun resetUUIDToEmptyString() { - val config = PNConfiguration(BasePubNubImpl.generateUUID()) - config.setUuid("") - } - - @Suppress("DEPRECATION") - @Test - fun resetUUIDToNonEmptyString() { - val config = PNConfiguration(BasePubNubImpl.generateUUID()) - val newUUID = BasePubNubImpl.generateUUID() - config.setUuid(newUUID) - - Assert.assertEquals(newUUID, config.userId.value) - } - - @Test - fun testDefaultTimeoutValues() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - Assert.assertEquals(300, config.presenceTimeout) - Assert.assertEquals(0, config.heartbeatInterval) - } - - @Test - fun testCustomTimeoutValues1() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - config.setPresenceTimeout(100) - Assert.assertEquals(100, config.presenceTimeout) - Assert.assertEquals(49, config.heartbeatInterval) - } - - @Test - fun testCustomTimeoutValues2() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - config.setHeartbeatInterval(100) - Assert.assertEquals(300, config.presenceTimeout) - Assert.assertEquals(100, config.heartbeatInterval) - } - - @Test - fun testCustomTimeoutValues3() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - config.setHeartbeatInterval(40) - config.setPresenceTimeout(50) - Assert.assertEquals(50, config.presenceTimeout) - Assert.assertEquals(24, config.heartbeatInterval) - } - - @Test - fun `reconnection policy should set retry configuration`() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - config.setReconnectionPolicy(PNReconnectionPolicy.NONE) - Assert.assertTrue(config.retryConfiguration is RetryConfiguration.None) - - config.setReconnectionPolicy(PNReconnectionPolicy.LINEAR) - Assert.assertTrue(config.retryConfiguration is RetryConfiguration.Linear) - - config.setReconnectionPolicy(PNReconnectionPolicy.EXPONENTIAL) - Assert.assertTrue(config.retryConfiguration is RetryConfiguration.Exponential) - } - - @Test - fun `maximumReconnectionRetries policy should reset retry configuration`() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - - config.setReconnectionPolicy(PNReconnectionPolicy.LINEAR) - config.setMaximumReconnectionRetries(5) - Assert.assertTrue(config.retryConfiguration is RetryConfiguration.Linear) - Assert.assertEquals(5, (config.retryConfiguration as RetryConfiguration.Linear).maxRetryNumber) - - config.setMaximumReconnectionRetries(10) - Assert.assertTrue(config.retryConfiguration is RetryConfiguration.Linear) - Assert.assertEquals(10, (config.retryConfiguration as RetryConfiguration.Linear).maxRetryNumber) - } - - @Test - fun `cryptomodule uses cipherKey when cryptomodule is not set`() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - - config.setCryptoModule(null) - config.setCipherKey("enigma") - Assert.assertNotNull(config.cryptoModule) - } - - @Test - fun `cryptomodule uses cryptomodule when cryptomodule is set`() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - val expectedCryptoModule = CryptoModule.createAesCbcCryptoModule("cipher") - config.setCryptoModule(expectedCryptoModule) - config.setCipherKey("enigma") - - Assert.assertEquals(expectedCryptoModule, config.cryptoModule) - } +// @Suppress("DEPRECATION") +// @Test(expected = PubNubException::class) +// fun setUUIDToEmptyString() { +// PNConfiguration("") +// } +// +// @Suppress("DEPRECATION") +// @Test(expected = PubNubException::class) +// fun resetUUIDToEmptyString() { +// val config = PNConfiguration(PubNub.generateUUID()) +// config.setUuid("") +// } +// +// @Suppress("DEPRECATION") +// @Test +// fun resetUUIDToNonEmptyString() { +// val config = PNConfiguration(PubNub.generateUUID()) +// val newUUID = PubNub.generateUUID() +// config.setUuid(newUUID) +// +// Assert.assertEquals(newUUID, config.userId.value) +// } +// +// @Test +// fun testDefaultTimeoutValues() { +// val config = PNConfiguration(UserId(PubNub.generateUUID())) +// Assert.assertEquals(300, config.presenceTimeout) +// Assert.assertEquals(0, config.heartbeatInterval) +// } +// +// @Test +// fun testCustomTimeoutValues1() { +// val config = PNConfiguration(UserId(PubNub.generateUUID())) +// config.setPresenceTimeout(100) +// Assert.assertEquals(100, config.presenceTimeout) +// Assert.assertEquals(49, config.heartbeatInterval) +// } +// +// @Test +// fun testCustomTimeoutValues2() { +// val config = PNConfiguration(UserId(PubNub.generateUUID())) +// config.setHeartbeatInterval(100) +// Assert.assertEquals(300, config.presenceTimeout) +// Assert.assertEquals(100, config.heartbeatInterval) +// } +// +// @Test +// fun testCustomTimeoutValues3() { +// val config = PNConfiguration(UserId(PubNub.generateUUID())) +// config.setHeartbeatInterval(40) +// config.setPresenceTimeout(50) +// Assert.assertEquals(50, config.presenceTimeout) +// Assert.assertEquals(24, config.heartbeatInterval) +// } +// +// @Test +// fun `reconnection policy should set retry configuration`() { +// val config = PNConfiguration(UserId(PubNub.generateUUID())) +// config.setReconnectionPolicy(PNReconnectionPolicy.NONE) +// Assert.assertTrue(config.retryConfiguration is RetryConfiguration.None) +// +// config.setReconnectionPolicy(PNReconnectionPolicy.LINEAR) +// Assert.assertTrue(config.retryConfiguration is RetryConfiguration.Linear) +// +// config.setReconnectionPolicy(PNReconnectionPolicy.EXPONENTIAL) +// Assert.assertTrue(config.retryConfiguration is RetryConfiguration.Exponential) +// } +// +// @Test +// fun `maximumReconnectionRetries policy should reset retry configuration`() { +// val config = PNConfiguration(UserId(PubNub.generateUUID())) +// +// config.setReconnectionPolicy(PNReconnectionPolicy.LINEAR) +// config.setMaximumReconnectionRetries(5) +// Assert.assertTrue(config.retryConfiguration is RetryConfiguration.Linear) +// Assert.assertEquals(5, (config.retryConfiguration as RetryConfiguration.Linear).maxRetryNumber) +// +// config.setMaximumReconnectionRetries(10) +// Assert.assertTrue(config.retryConfiguration is RetryConfiguration.Linear) +// Assert.assertEquals(10, (config.retryConfiguration as RetryConfiguration.Linear).maxRetryNumber) +// } +// +// @Test +// fun `cryptomodule uses cipherKey when cryptomodule is not set`() { +// val config = PNConfiguration(UserId(PubNub.generateUUID())) +// +// config.setCryptoModule(null) +// config.setCipherKey("enigma") +// Assert.assertNotNull(config.cryptoModule) +// } +// +// @Test +// fun `cryptomodule uses cryptomodule when cryptomodule is set`() { +// val config = PNConfiguration(UserId(PubNub.generateUUID())) +// val expectedCryptoModule = CryptoModule.createAesCbcCryptoModule("cipher") +// config.setCryptoModule(expectedCryptoModule) +// config.setCipherKey("enigma") +// +// Assert.assertEquals(expectedCryptoModule, config.cryptoModule) +// } @Test fun `create config override from existing config`() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) + val config = PNConfiguration(UserId(PubNub.generateUUID())) val override = PNConfigurationOverride.from(config).apply { setUserId(UserId("override")) }.build() Assert.assertEquals("override", override.userId.value) } } + +private fun PNConfiguration(userId: UserId) = PNConfiguration.builder(userId, "").build() diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/DelegatingEndpointTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/DelegatingEndpointTest.kt similarity index 68% rename from pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/DelegatingEndpointTest.kt rename to pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/DelegatingEndpointTest.kt index 55699ff0f..f251d3a7d 100644 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/DelegatingEndpointTest.kt +++ b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/DelegatingEndpointTest.kt @@ -1,14 +1,14 @@ -package com.pubnub.internal.endpoints +package com.pubnub.internal.java.endpoints +import com.pubnub.api.Endpoint import com.pubnub.api.UserId import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction import com.pubnub.api.enums.PNOperationType -import com.pubnub.api.v2.BasePNConfiguration -import com.pubnub.api.v2.PNConfiguration +import com.pubnub.api.java.v2.PNConfiguration +import com.pubnub.api.v2.PNConfigurationOverride import com.pubnub.api.v2.callbacks.Result import com.pubnub.api.v2.callbacks.getOrThrow -import com.pubnub.internal.EndpointInterface import io.mockk.mockk import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue @@ -18,53 +18,16 @@ import java.util.function.Consumer internal class DelegatingEndpointTest { private lateinit var delegatingEndpoint: DelegatingEndpoint - var validateParamsCalled = false - var silentCancelCalled = false - var retryCalled = false - var mapping: (ExtendedRemoteAction) -> ExtendedRemoteAction = { it } - - val action = - object : EndpointInterface { - private lateinit var overridenConfiguration: BasePNConfiguration - - override fun operationType(): PNOperationType { - return PNOperationType.FileOperation - } - - override fun retry() { - retryCalled = true - } - - override fun sync(): Boolean { - return true - } - override fun silentCancel() { - silentCancelCalled = true - } - - override fun overrideConfiguration(configuration: BasePNConfiguration) { - overridenConfiguration = configuration - } - - override val configuration: BasePNConfiguration - get() = overridenConfiguration - - override fun async(callback: Consumer>) { - callback.accept(Result.success(true)) - } - } + var mapping: (ExtendedRemoteAction) -> ExtendedRemoteAction = { it } + val action = TestAction() @BeforeEach fun setUp() { - validateParamsCalled = false - silentCancelCalled = false - retryCalled = false - delegatingEndpoint = object : DelegatingEndpoint(mockk()) { - override fun createAction(): EndpointInterface { + override fun createAction(): Endpoint { return action } @@ -108,7 +71,7 @@ internal class DelegatingEndpointTest { delegatingEndpoint.overrideConfiguration(overridingConfig) delegatingEndpoint.remoteAction - assertEquals(overridingConfig, action.configuration) + assertEquals(overridingConfig, action.overridenConfiguration) } @Test @@ -123,13 +86,13 @@ internal class DelegatingEndpointTest { @Test fun `when retry is called calls retry on delegate`() { delegatingEndpoint.retry() - assertTrue(retryCalled) + assertTrue(action.retryCalled) } @Test fun `when silentCancel is called calls silentCancel on delegate`() { delegatingEndpoint.silentCancel() - assertTrue(silentCancelCalled) + assertTrue(action.silentCancelCalled) } @Test @@ -141,8 +104,43 @@ internal class DelegatingEndpointTest { @Test fun `IdentityMappingEndpoint returns the same remote action`() { delegatingEndpoint = object : IdentityMappingEndpoint(mockk()) { - override fun createAction(): EndpointInterface = action + override fun createAction(): Endpoint = action } assertEquals(action, delegatingEndpoint.remoteAction) } } + +class TestAction() : Endpoint { + lateinit var overridenConfiguration: com.pubnub.api.v2.PNConfiguration + var retryCalled: Boolean = false + var silentCancelCalled: Boolean = false + + override fun operationType(): PNOperationType { + return PNOperationType.FileOperation + } + + override fun retry() { + retryCalled = true + } + + override fun sync(): Boolean { + return true + } + + override fun silentCancel() { + silentCancelCalled = true + } + + override fun async(callback: Consumer>) { + callback.accept(Result.success(true)) + } + + override fun overrideConfiguration(action: PNConfigurationOverride.Builder.() -> Unit): Endpoint { + TODO("Not yet implemented") + } + + override fun overrideConfiguration(configuration: com.pubnub.api.v2.PNConfiguration): Endpoint { + overridenConfiguration = configuration + return this + } +} diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/DelegatingRemoteActionTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/DelegatingRemoteActionTest.kt similarity index 98% rename from pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/DelegatingRemoteActionTest.kt rename to pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/DelegatingRemoteActionTest.kt index 516d8304b..270e9027c 100644 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/DelegatingRemoteActionTest.kt +++ b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/DelegatingRemoteActionTest.kt @@ -1,4 +1,4 @@ -package com.pubnub.internal.endpoints +package com.pubnub.internal.java.endpoints import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction import com.pubnub.api.enums.PNOperationType diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/DeleteMessagesImplTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/DeleteMessagesImplTest.kt similarity index 87% rename from pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/DeleteMessagesImplTest.kt rename to pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/DeleteMessagesImplTest.kt index cdba4c352..fbc357b4a 100644 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/DeleteMessagesImplTest.kt +++ b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/DeleteMessagesImplTest.kt @@ -1,8 +1,8 @@ -package com.pubnub.internal.endpoints +package com.pubnub.internal.java.endpoints +import com.pubnub.api.PubNub import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction import com.pubnub.api.models.consumer.history.PNDeleteMessagesResult -import com.pubnub.internal.PubNubCore import io.mockk.mockk import io.mockk.verify import org.junit.jupiter.api.Assertions.assertTrue @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test class DeleteMessagesImplTest { private lateinit var objectUnderTest: DeleteMessagesImpl - private val pubNubCore: PubNubCore = mockk(relaxed = true) + private val pubNubCore: PubNub = mockk(relaxed = true) private val channels: List = listOf("Channel01") private val start: Long = 123456789L private val end: Long = 987654321L diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/FetchMessagesImplTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/FetchMessagesImplTest.kt similarity index 92% rename from pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/FetchMessagesImplTest.kt rename to pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/FetchMessagesImplTest.kt index 53c016439..781cb589f 100644 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/FetchMessagesImplTest.kt +++ b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/FetchMessagesImplTest.kt @@ -1,9 +1,9 @@ -package com.pubnub.internal.endpoints +package com.pubnub.internal.java.endpoints +import com.pubnub.api.PubNub import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction import com.pubnub.api.models.consumer.PNBoundedPage import com.pubnub.api.models.consumer.history.PNFetchMessagesResult -import com.pubnub.internal.PubNubCore import io.mockk.mockk import io.mockk.verify import org.junit.jupiter.api.Assertions.assertTrue @@ -12,7 +12,7 @@ import org.junit.jupiter.api.Test class FetchMessagesImplTest { private lateinit var objectUnderTest: FetchMessagesImpl - private val pubNubCore: PubNubCore = mockk(relaxed = true) + private val pubNubCore: PubNub = mockk(relaxed = true) private val channels: List = listOf("Channel01") private val maximumPerChannel: Int = 2 private val start: Long = 123 diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/access/GrantTokenImplTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/access/GrantTokenImplTest.kt similarity index 65% rename from pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/access/GrantTokenImplTest.kt rename to pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/access/GrantTokenImplTest.kt index bc6fabb14..eeeaad137 100644 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/endpoints/access/GrantTokenImplTest.kt +++ b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/endpoints/access/GrantTokenImplTest.kt @@ -1,9 +1,10 @@ -package com.pubnub.internal.endpoints.access +package com.pubnub.internal.java.endpoints.access -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant -import com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant -import com.pubnub.internal.PubNubCore +import com.pubnub.api.PubNub +import com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGrant +import com.pubnub.api.java.models.consumer.access_manager.v3.ChannelGroupGrant +import com.pubnub.api.java.models.consumer.access_manager.v3.UUIDGrant +import com.pubnub.internal.endpoints.access.GrantTokenEndpoint import io.mockk.CapturingSlot import io.mockk.every import io.mockk.mockk @@ -15,7 +16,7 @@ import org.junit.jupiter.api.Test class GrantTokenImplTest { private lateinit var objectUnderTest: GrantTokenImpl - private val pubNubCore: PubNubCore = mockk() + private val pubNubCore: PubNub = mockk() private val grantTokenEndpoint: GrantTokenEndpoint = mockk() private val ttl: Int = 123 private val meta: Any? = null @@ -23,11 +24,11 @@ class GrantTokenImplTest { private val channels = listOf(ChannelGrant.name("myChannel01").delete(), ChannelGrant.name("myChannel02").manage()) private val channelGroups = listOf(ChannelGroupGrant.pattern("myChannelGroup01").manage()) private val uuids = listOf(UUIDGrant.id("myUUID").update()) - private val channelsCapture: CapturingSlot> = + private val channelsCapture: CapturingSlot> = slot() - private val channelGroupsCapture: CapturingSlot> = + private val channelGroupsCapture: CapturingSlot> = slot() - private val uuidsCapture: CapturingSlot> = + private val uuidsCapture: CapturingSlot> = slot() @Test @@ -56,11 +57,11 @@ class GrantTokenImplTest { // then verify { pubNubCore.grantToken(ttl, meta, authorizedUUID, any(), any(), any()) } - val capturedChannels: List = + val capturedChannels: List = channelsCapture.captured - val capturedChannelGroups: List = + val capturedChannelGroups: List = channelGroupsCapture.captured - val capturedUUIDs: List = uuidsCapture.captured + val capturedUUIDs: List = uuidsCapture.captured assertEquals(2, capturedChannels.size) assertEquals(1, capturedChannelGroups.size) assertEquals(1, capturedUUIDs.size) diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/PNConfigurationImplTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/PNConfigurationImplTest.kt similarity index 91% rename from pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/PNConfigurationImplTest.kt rename to pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/PNConfigurationImplTest.kt index b2f8af26e..24bfd703f 100644 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/PNConfigurationImplTest.kt +++ b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/PNConfigurationImplTest.kt @@ -1,12 +1,13 @@ -package com.pubnub.internal.v2 +package com.PubNubForJava.internal.java.v2 import com.pubnub.api.UserId import com.pubnub.api.crypto.CryptoModule import com.pubnub.api.enums.PNHeartbeatNotificationOptions import com.pubnub.api.enums.PNLogVerbosity +import com.pubnub.api.java.PubNubForJava +import com.pubnub.api.java.v2.PNConfiguration import com.pubnub.api.retry.RetryConfiguration -import com.pubnub.api.v2.PNConfiguration -import com.pubnub.internal.BasePubNubImpl +import com.pubnub.internal.java.v2.PNConfigurationImpl import io.mockk.mockk import okhttp3.Authenticator import okhttp3.CertificatePinner @@ -24,14 +25,14 @@ import javax.net.ssl.X509ExtendedTrustManager class PNConfigurationImplTest { @Test fun testDefaultTimeoutValues() { - val config = PNConfiguration.builder(UserId(BasePubNubImpl.generateUUID()), "demo") + val config = PNConfiguration.builder(UserId(PubNubForJava.generateUUID()), "demo") assertEquals(300, config.presenceTimeout) assertEquals(0, config.heartbeatInterval) } @Test fun testCustomTimeoutValues1() { - val config = PNConfiguration.builder(UserId(BasePubNubImpl.generateUUID()), "demo") + val config = PNConfiguration.builder(UserId(PubNubForJava.generateUUID()), "demo") config.presenceTimeout(100) assertEquals(100, config.presenceTimeout) assertEquals(49, config.heartbeatInterval) @@ -39,7 +40,7 @@ class PNConfigurationImplTest { @Test fun testCustomTimeoutValues2() { - val config = PNConfiguration.builder(UserId(BasePubNubImpl.generateUUID()), "demo") + val config = PNConfiguration.builder(UserId(PubNubForJava.generateUUID()), "demo") config.heartbeatInterval(100) assertEquals(300, config.presenceTimeout) assertEquals(100, config.heartbeatInterval) @@ -47,7 +48,7 @@ class PNConfigurationImplTest { @Test fun testCustomTimeoutValues3() { - val config = PNConfiguration.builder(UserId(BasePubNubImpl.generateUUID()), "demo") + val config = PNConfiguration.builder(UserId(PubNubForJava.generateUUID()), "demo") config.heartbeatInterval(40) config.presenceTimeout(50) assertEquals(50, config.presenceTimeout) @@ -56,7 +57,7 @@ class PNConfigurationImplTest { @Test fun `build uses all values from Builder`() { - val expectedUserId = UserId(BasePubNubImpl.generateUUID()) + val expectedUserId = UserId(PubNubForJava.generateUUID()) val expectedCryptoModule = CryptoModule.createAesCbcCryptoModule("cipher") val expectedProxy = Proxy(Proxy.Type.HTTP, InetSocketAddress(80)) val expectedProxySelector = DefaultProxySelector() @@ -149,10 +150,10 @@ class PNConfigurationImplTest { } @Test - fun `builder has all default values from BasePNConfiguration`() { - val expectedUserId = UserId(BasePubNubImpl.generateUUID()) + fun `builder has all default values from PNConfiguration`() { + val expectedUserId = UserId(PubNubForJava.generateUUID()) val builder = PNConfiguration.builder(expectedUserId, "subKey") - val expectedDefaults = BasePNConfigurationImpl(expectedUserId) + val expectedDefaults = PNConfigurationImpl(expectedUserId) assertEquals(expectedUserId, builder.userId) assertEquals("subKey", builder.subscribeKey) @@ -196,7 +197,7 @@ class PNConfigurationImplTest { @Test fun `can reset userId and subscribeKey`() { - val expectedUserId = UserId(BasePubNubImpl.generateUUID()) + val expectedUserId = UserId(PubNubForJava.generateUUID()) val expectedSubKey = "expectedSubKey" val config = PNConfiguration.builder(UserId("aaa"), "subKey") diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/entities/ChannelGroupImplTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/entities/ChannelGroupImplTest.kt similarity index 51% rename from pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/entities/ChannelGroupImplTest.kt rename to pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/entities/ChannelGroupImplTest.kt index 1b36f3389..4b2c04273 100644 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/entities/ChannelGroupImplTest.kt +++ b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/entities/ChannelGroupImplTest.kt @@ -1,23 +1,23 @@ -package com.pubnub.internal.v2.entities +package com.pubnub.internal.java.v2.entities import com.pubnub.api.v2.subscriptions.EmptyOptions -import com.pubnub.api.v2.subscriptions.Subscription -import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.java.PubNubForJavaImpl +import com.pubnub.internal.v2.entities.ChannelGroupName import io.mockk.mockk +import io.mockk.spyk import io.mockk.verify import org.junit.jupiter.api.Test class ChannelGroupImplTest { private lateinit var objectUnderTest: ChannelGroupImpl - private val pubnub: PubNubImpl = mockk(relaxed = true) + private val pubnub: PubNubForJavaImpl = mockk(relaxed = true) @Test fun shouldCallSubscriptionWithEmptyOptionObjectWhenCallingSubscriptionWithoutOptions() { - val channelGroupName = "myChannelGroupName" - objectUnderTest = ChannelGroupImpl(pubnub, channelGroupName) + objectUnderTest = spyk(ChannelGroupImpl(pubnub, ChannelGroupName("abc"))) - val subscription: Subscription = objectUnderTest.subscription() + objectUnderTest.subscription() verify { objectUnderTest.subscription(EmptyOptions) } } diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/entities/ChannelImplTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/entities/ChannelImplTest.kt similarity index 58% rename from pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/entities/ChannelImplTest.kt rename to pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/entities/ChannelImplTest.kt index 4faa76425..f8ace8cfc 100644 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/entities/ChannelImplTest.kt +++ b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/entities/ChannelImplTest.kt @@ -1,20 +1,21 @@ -package com.pubnub.internal.v2.entities +package com.pubnub.internal.java.v2.entities import com.pubnub.api.v2.subscriptions.EmptyOptions -import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.java.PubNubForJavaImpl +import com.pubnub.internal.v2.entities.ChannelName import io.mockk.mockk +import io.mockk.spyk import io.mockk.verify import org.junit.jupiter.api.Test class ChannelImplTest { private lateinit var objectUnderTest: ChannelImpl - private val pubnub: PubNubImpl = mockk(relaxed = true) + private val pubnub: PubNubForJavaImpl = mockk(relaxed = true) @Test fun shouldCallSubscriptionWithEmptyOptionObjectWhenCallingSubscriptionWithoutOpitons() { - val channelName = "myChannelName" - objectUnderTest = ChannelImpl(pubnub, channelName) + objectUnderTest = spyk(ChannelImpl(pubnub, ChannelName("abc"))) objectUnderTest.subscription() diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/subscription/SubscriptionImplTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/subscription/SubscriptionImplTest.kt new file mode 100644 index 000000000..90a1bfdec --- /dev/null +++ b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/subscription/SubscriptionImplTest.kt @@ -0,0 +1,187 @@ + +import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.SubscriptionOptions +import com.pubnub.internal.java.PubNubForJavaImpl +import com.pubnub.internal.java.v2.subscription.SubscriptionImpl +import com.pubnub.internal.java.v2.subscription.SubscriptionSetImpl +import com.pubnub.internal.v2.entities.ChannelGroupName +import com.pubnub.internal.v2.entities.ChannelName +import io.mockk.every +import io.mockk.mockk +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +// package com.pubnub.internal.java.v2.subscription +// +// import com.pubnub.api.java.callbacks.SubscribeCallback +// import com.pubnub.api.java.v2.callbacks.EventListener +// import com.pubnub.api.java.v2.callbacks.handlers.OnChannelMetadataHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnFileHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnMembershipHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnMessageActionHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnMessageHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnPresenceHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnSignalHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnUuidMetadataHandler +// import com.pubnub.api.v2.subscriptions.Subscription +// import io.mockk.mockk +// import io.mockk.slot +// import io.mockk.verify +// import org.junit.jupiter.api.Assertions.assertEquals +// import org.junit.jupiter.api.Assertions.assertTrue +// import org.junit.jupiter.api.BeforeEach +// import org.junit.jupiter.api.Test +// +// class SubscriptionImplTest { +// private lateinit var objectUnderTest: SubscriptionImpl +// val underlyingSubscription: Subscription = mockk() +// private val eventListener: EventListener = mockk() +// private val listenerSubscribeCallback: SubscribeCallback = mockk() +// +// @BeforeEach +// fun setUp() { +// objectUnderTest = SubscriptionImpl(underlyingSubscription) +// } +// +// @Test +// fun `addListener should call addListener on eventEmitter`() { +// val capturedListeners = mutableListOf() +// objectUnderTest.addListener(eventListener) +// +// verify { underlyingSubscription.addListener(capture(capturedListeners)) } +// assertTrue(capturedListeners.contains(eventListener)) +// } +// +// @Test +// fun `removeListener which is SubscribeCallback should call proper removeListener on eventEmitter`() { +// val delegatingSubscribeCallbackSlot = slot() +// objectUnderTest.removeListener(listenerSubscribeCallback) +// +// verify(exactly = 1) { underlyingSubscription.removeListener(capture(delegatingSubscribeCallbackSlot)) } +// assertEquals(delegatingSubscribeCallbackSlot.captured, listenerSubscribeCallback) +// } +// +// @Test +// fun `removeListener which is EventListener should call proper removeListener on eventEmitter`() { +// val delegatingEventListenerSlot = slot() +// objectUnderTest.removeListener(eventListener) +// +// verify(exactly = 1) { underlyingSubscription.removeListener(capture(delegatingEventListenerSlot)) } +// assertEquals(delegatingEventListenerSlot.captured, eventListener) +// } +// +// @Test +// fun `removeAllListeners should call removeAllListeners on eventEmitter`() { +// objectUnderTest.removeAllListeners() +// +// verify(exactly = 1) { underlyingSubscription.removeAllListeners() } +// } +// +// @Test +// fun `setOnMessage should set property on emitterHelper`() { +// val onMessageHandler: OnMessageHandler = mockk() +// +// objectUnderTest.setOnMessage(onMessageHandler) +// +// verify { underlyingSubscription.onMessage = any() } +// } +// +// @Test +// fun `setOnSignal should set property on emitterHelper`() { +// val onSignalHandler: OnSignalHandler = mockk() +// +// objectUnderTest.setOnSignal(onSignalHandler) +// +// verify { underlyingSubscription.onSignal = any() } +// } +// +// @Test +// fun `setOnPresence should set property on emitterHelper`() { +// val onPresenceHandler: OnPresenceHandler = mockk() +// +// objectUnderTest.setOnPresence(onPresenceHandler) +// +// verify { underlyingSubscription.onPresence = any() } +// } +// +// @Test +// fun `setOnMessageAction should set property on emitterHelper`() { +// val onMessageActionHandler: OnMessageActionHandler = mockk() +// +// objectUnderTest.setOnMessageAction(onMessageActionHandler) +// +// verify { underlyingSubscription.onMessageAction = any() } +// } +// +// @Test +// fun `setOnUuidMetadata should set property on emitterHelper`() { +// val onUuidMetadataHandler: OnUuidMetadataHandler = mockk() +// +// objectUnderTest.setOnUuidMetadata(onUuidMetadataHandler) +// +// verify { underlyingSubscription.onObjects = any() } +// } +// +// @Test +// fun `setOnChannelMetadata should set property on emitterHelper`() { +// val onChannelMetadataHandler: OnChannelMetadataHandler = mockk() +// +// objectUnderTest.setOnChannelMetadata(onChannelMetadataHandler) +// +// verify { underlyingSubscription.onObjects = any() } +// } +// +// @Test +// fun `setOnMembership should set property on emitterHelper`() { +// val onMembershipHandler: OnMembershipHandler = mockk() +// +// objectUnderTest.setOnMembership(onMembershipHandler) +// +// verify { underlyingSubscription.onObjects = any() } +// } +// +// @Test +// fun `setOnFile should set property on emitterHelper`() { +// val onFileHandler: OnFileHandler = mockk() +// +// objectUnderTest.setOnFile(onFileHandler) +// +// verify { underlyingSubscription.onFile = any() } +// } +// } + +class SubscriptionImplTest { + private lateinit var objectUnderTest: com.pubnub.api.java.v2.subscriptions.Subscription + + private val pubNubImpl: PubNubForJavaImpl = mockk(relaxed = true) + private val subscriptionSetImpl: SubscriptionSetImpl = mockk() + + private val channels = setOf(ChannelName("Channel1")) + private val channels02 = setOf(ChannelName("Channel2")) + private val channelGroups = setOf(ChannelGroupName("ChannelGroup1")) + private val channelGroups02 = setOf(ChannelGroupName("ChannelGroup2")) + + @BeforeEach + fun setUp() { + objectUnderTest = SubscriptionImpl(pubNubImpl, channels, channelGroups, SubscriptionOptions.receivePresenceEvents()) + } + + @Test + fun `should add subscription to subscription creating subscriptionSet when using plus method`() { + // given + val subscriptionToBeAdded: com.pubnub.api.java.v2.subscriptions.Subscription = + SubscriptionImpl(pubNubImpl, channels02, channelGroups02, SubscriptionOptions.receivePresenceEvents()) + every { pubNubImpl.subscriptionSetOf(any>()) } returns subscriptionSetImpl + every { subscriptionSetImpl.subscriptions } returns setOf(objectUnderTest, subscriptionToBeAdded) + + // when + val subscriptionSet: com.pubnub.api.java.v2.subscriptions.SubscriptionSet = objectUnderTest + subscriptionToBeAdded + + // then + assertEquals(2, subscriptionSet.subscriptions.size) + assertTrue(subscriptionSet.subscriptions.contains(objectUnderTest)) + assertTrue(subscriptionSet.subscriptions.contains(subscriptionToBeAdded)) + } +} diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/subscription/SubscriptionSetImplTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/subscription/SubscriptionSetImplTest.kt new file mode 100644 index 000000000..7a556b06f --- /dev/null +++ b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/java/v2/subscription/SubscriptionSetImplTest.kt @@ -0,0 +1,157 @@ +package com.pubnub.internal.java.v2.subscription + +import com.pubnub.api.v2.subscriptions.EmptyOptions +import com.pubnub.api.v2.subscriptions.SubscriptionOptions +import com.pubnub.internal.java.PubNubForJavaImpl +import com.pubnub.internal.managers.ListenerManager +import com.pubnub.internal.v2.entities.ChannelGroupName +import com.pubnub.internal.v2.entities.ChannelName +import io.mockk.every +import io.mockk.mockk +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class SubscriptionSetImplTest { + private lateinit var objectUnderTest: SubscriptionSetImpl + + private val pubNubImpl: PubNubForJavaImpl = mockk() + + private val channel = setOf(ChannelName("Channel2")) + private val channelGroup = setOf(ChannelGroupName("ChannelGroup2")) + + @BeforeEach + fun setUp() { + val listenerManager = ListenerManager(pubNubImpl) + every { pubNubImpl.listenerManager } returns listenerManager + + objectUnderTest = SubscriptionSetImpl(pubNubImpl, emptySet()) + } + + @Test + fun `should add subscription to subscription set when using plusAssign method`() { + // given + val subscriptionToBeAdded = SubscriptionImpl(pubNubImpl, channel, channelGroup, SubscriptionOptions.receivePresenceEvents()) + + // when + objectUnderTest += subscriptionToBeAdded + + // then + assertEquals(1, objectUnderTest.subscriptions.size) + assertTrue(objectUnderTest.subscriptions.contains(subscriptionToBeAdded)) + } + + @Test + fun `should remove subscription from subscription set when using minusAssign method`() { + // given + val subscriptionToBeRemoved = SubscriptionImpl(pubNubImpl, channel, channelGroup, EmptyOptions) + objectUnderTest += subscriptionToBeRemoved + assertTrue(objectUnderTest.subscriptions.contains(subscriptionToBeRemoved)) + + // when + objectUnderTest -= subscriptionToBeRemoved + + // then + assertEquals(0, objectUnderTest.subscriptions.size) + } +} + +// +// import com.pubnub.api.PubNub +// import com.pubnub.api.java.v2.callbacks.handlers.OnChannelMetadataHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnFileHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnMembershipHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnMessageActionHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnMessageHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnPresenceHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnSignalHandler +// import com.pubnub.api.java.v2.callbacks.handlers.OnUuidMetadataHandler +// import com.pubnub.api.v2.subscriptions.SubscriptionSet +// import io.mockk.mockk +// import io.mockk.verify +// import org.junit.jupiter.api.BeforeEach +// import org.junit.jupiter.api.Test +// +// class SubscriptionSetImplTest { +// private lateinit var objectUnderTest: SubscriptionSetImpl +// private val underlyingSet: SubscriptionSet = mockk() +// private val pubNub: PubNub = mockk(relaxed = true) +// +// @BeforeEach +// fun setUp() { +// objectUnderTest = SubscriptionSetImpl(underlyingSet) +// } +// +// @Test +// fun `setOnMessage sets onMessageHandler in EmitterHelper`() { +// val onMessageHandler: OnMessageHandler = mockk() +// +// objectUnderTest.setOnMessage(onMessageHandler) +// +// verify { underlyingSet.onMessage = any() } +// } +// +// @Test +// fun `setOnSignal should set property on underlyingSet`() { +// val onSignalHandler: OnSignalHandler = mockk() +// +// objectUnderTest.setOnSignal(onSignalHandler) +// +// verify { underlyingSet.onSignal = any() } +// } +// +// @Test +// fun `setOnPresence should set property on underlyingSet`() { +// val onPresenceHandler: OnPresenceHandler = mockk() +// +// objectUnderTest.setOnPresence(onPresenceHandler) +// +// verify { underlyingSet.onPresence = any() } +// } +// +// @Test +// fun `setOnMessageAction should set property on underlyingSet`() { +// val onMessageActionHandler: OnMessageActionHandler = mockk() +// +// objectUnderTest.setOnMessageAction(onMessageActionHandler) +// +// verify { underlyingSet.onMessageAction = any() } +// } +// +// @Test +// fun `setOnUuidMetadata should set property on underlyingSet`() { +// val onUuidMetadataHandler: OnUuidMetadataHandler = mockk() +// +// objectUnderTest.setOnUuidMetadata(onUuidMetadataHandler) +// +// verify { underlyingSet.onObjects = any() } +// } +// +// @Test +// fun `setOnChannelMetadata should set property on underlyingSet`() { +// val onChannelMetadataHandler: OnChannelMetadataHandler = mockk() +// +// objectUnderTest.setOnChannelMetadata(onChannelMetadataHandler) +// +// verify { underlyingSet.onObjects = any() } +// } +// +// @Test +// fun `setOnMembership should set property on underlyingSet`() { +// val onMembershipHandler: OnMembershipHandler = mockk() +// +// objectUnderTest.setOnMembership(onMembershipHandler) +// +// verify { underlyingSet.onObjects = any() } +// } +// +// @Test +// fun `setOnFile should set property on underlyingSet`() { +// val onFileHandler: OnFileHandler = mockk() +// +// objectUnderTest.setOnFile(onFileHandler) +// +// verify { underlyingSet.onFile = any() } +// } +// } diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingEventListenerTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingEventListenerTest.kt deleted file mode 100644 index 36caea8b0..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingEventListenerTest.kt +++ /dev/null @@ -1,318 +0,0 @@ -package com.pubnub.internal.v2.callbacks - -import com.google.gson.JsonPrimitive -import com.pubnub.api.PNConfiguration -import com.pubnub.api.PubNub -import com.pubnub.api.UserId -import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata -import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadataResult -import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult -import com.pubnub.api.models.consumer.objects_api.uuid.PNUUIDMetadataResult -import com.pubnub.api.models.consumer.pubsub.BasePubSubResult -import com.pubnub.api.utils.PatchValue -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.internal.models.consumer.pubsub.objects.PNDeleteChannelMetadataEventMessage -import com.pubnub.internal.models.consumer.pubsub.objects.PNDeleteMembershipEvent -import com.pubnub.internal.models.consumer.pubsub.objects.PNDeleteMembershipEventMessage -import com.pubnub.internal.models.consumer.pubsub.objects.PNDeleteUUIDMetadataEventMessage -import com.pubnub.internal.models.consumer.pubsub.objects.PNObjectEventResult -import com.pubnub.internal.models.consumer.pubsub.objects.PNSetChannelMetadataEventMessage -import com.pubnub.internal.models.consumer.pubsub.objects.PNSetMembershipEvent -import com.pubnub.internal.models.consumer.pubsub.objects.PNSetMembershipEventMessage -import com.pubnub.internal.models.consumer.pubsub.objects.PNSetUUIDMetadataEventMessage -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test - -internal class DelegatingEventListenerTest { - @Test - fun testEquals() { - val eventListener = object : EventListener {} - val otherEventListener = object : EventListener {} - val delegating1 = DelegatingEventListener(eventListener) - val delegating2 = DelegatingEventListener(eventListener) - val otherDelegating = DelegatingEventListener(otherEventListener) - - Assertions.assertEquals(delegating1, delegating2) - Assertions.assertEquals(delegating2, delegating1) - Assertions.assertNotEquals(delegating1, otherDelegating) - Assertions.assertNotEquals(delegating2, otherDelegating) - Assertions.assertNotEquals(otherDelegating, delegating1) - Assertions.assertNotEquals(otherDelegating, delegating2) - } - - @Test - fun objects() { - var uuidCalled = false - var channelCalled = false - var membershipCalled = false - - val eventListener = - object : EventListener { - override fun uuid( - pubnub: PubNub, - pnUUIDMetadataResult: PNUUIDMetadataResult, - ) { - uuidCalled = true - } - - override fun channel( - pubnub: PubNub, - pnChannelMetadataResult: PNChannelMetadataResult, - ) { - channelCalled = true - } - - override fun membership( - pubnub: PubNub, - pnMembershipResult: PNMembershipResult, - ) { - membershipCalled = true - } - } - val delegating = DelegatingEventListener(eventListener) - val pn = PubNub.create(PNConfiguration(UserId("a"))) - delegating.objects( - pn, - PNObjectEventResult( - BasePubSubResult("a", "b", 0L, null, null), - PNSetMembershipEventMessage("a", "b", "c", "d", PNSetMembershipEvent("a", "b", null, "c", "d", null)), - ), - ) - delegating.objects( - pn, - PNObjectEventResult( - BasePubSubResult("a", "b", 0L, null, null), - PNSetUUIDMetadataEventMessage( - "a", - "b", - "c", - "d", - PNUUIDMetadata("a", PatchValue.of("b"), null, PatchValue.of("c"), PatchValue.of("d"), null, null, null, null, null) - ), - ), - ) - delegating.objects( - pn, - PNObjectEventResult( - BasePubSubResult("a", "b", 0L, null, null), - PNSetChannelMetadataEventMessage( - "a", - "b", - "c", - "d", - com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata( - "a", - PatchValue.of("b"), - null, - PatchValue.of(mapOf("c" to "c")), - PatchValue.of("d"), - null, - null, - null - ) - ), - ), - ) - - Assertions.assertTrue(uuidCalled) - Assertions.assertTrue(channelCalled) - Assertions.assertTrue(membershipCalled) - } - - val channel = "myChannel" - val subscription = "mySub" - val timetoken = 100L - val userMetadata = JsonPrimitive(100) - val publisher = "myPublish" - - val source = "mySource" - val version = "myVersion" - val event = "myEvent" - val type = "myType" - - val id = "myId" - val name = "myName" - val externalId = "myExternalId" - val profileUrl = "myProfileUrl" - val email = "myEmail" - val custom = mapOf("a" to "b") - val updated = "myUpdated" - val eTag = "myEtag" - val status = "myStatus" - val uuid = "myUuid" - val description = "myDescription" - - @Test - fun getSetUuidMetadataResult() { - val metadata = PNUUIDMetadata( - id, - PatchValue.of(name), - PatchValue.of(externalId), - PatchValue.of(profileUrl), - PatchValue.of(email), - PatchValue.of(custom), - PatchValue.of(updated), - PatchValue.of(eTag), - PatchValue.of(type), - PatchValue.of(status) - ) - val message = PNSetUUIDMetadataEventMessage(source, version, event, type, metadata) - val objectEvent = PNObjectEventResult(BasePubSubResult(channel, subscription, timetoken, userMetadata, publisher), message) - - val result: PNUUIDMetadataResult = DelegatingEventListener.getSetUuidMetadataResult(objectEvent, message) - - Assertions.assertEquals(event, result.event) - result.data.let { data -> - Assertions.assertEquals(id, data.id) - Assertions.assertEquals(name, data.name) - Assertions.assertEquals(externalId, data.externalId) - Assertions.assertEquals(profileUrl, data.profileUrl) - Assertions.assertEquals(email, data.email) - Assertions.assertEquals(custom, data.custom) - Assertions.assertEquals(updated, data.updated) - Assertions.assertEquals(eTag, data.eTag) - Assertions.assertEquals(type, data.type) - Assertions.assertEquals(status, data.status) - } - Assertions.assertEquals(channel, result.channel) - Assertions.assertEquals(subscription, result.subscription) - Assertions.assertEquals(timetoken, result.timetoken) - Assertions.assertEquals(userMetadata, result.userMetadata) - Assertions.assertEquals(publisher, result.publisher) - } - - @Test - fun getDeleteUuidMetadataResult() { - val message = PNDeleteUUIDMetadataEventMessage(source, version, event, type, uuid) - val objectEvent = PNObjectEventResult(BasePubSubResult(channel, subscription, timetoken, userMetadata, publisher), message) - val result: PNUUIDMetadataResult = DelegatingEventListener.getDeleteUuidMetadataResult(objectEvent, message) - - Assertions.assertEquals(event, result.event) - result.data.let { data -> - Assertions.assertEquals(uuid, data.id) - Assertions.assertEquals(null, data.name) - Assertions.assertEquals(null, data.externalId) - Assertions.assertEquals(null, data.profileUrl) - Assertions.assertEquals(null, data.email) - Assertions.assertEquals(null, data.custom) - Assertions.assertEquals(null, data.updated) - Assertions.assertEquals(null, data.eTag) - Assertions.assertEquals(null, data.type) - Assertions.assertEquals(null, data.status) - } - Assertions.assertEquals(channel, result.channel) - Assertions.assertEquals(subscription, result.subscription) - Assertions.assertEquals(timetoken, result.timetoken) - Assertions.assertEquals(userMetadata, result.userMetadata) - Assertions.assertEquals(publisher, result.publisher) - } - - @Test - fun getSetChannelMetadataResult() { - val metadata = - com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata( - id, - PatchValue.of(name), - PatchValue.of(description), - PatchValue.of(custom), - PatchValue.of(updated), - PatchValue.of(eTag), - PatchValue.of(type), - PatchValue.of(status) - ) - val message = PNSetChannelMetadataEventMessage(source, version, event, type, metadata) - val objectEvent = PNObjectEventResult(BasePubSubResult(channel, subscription, timetoken, userMetadata, publisher), message) - - val result: PNChannelMetadataResult = DelegatingEventListener.getSetChannelMetadataResult(objectEvent, message) - - Assertions.assertEquals(event, result.event) - result.data.let { data -> - Assertions.assertEquals(id, data.id) - Assertions.assertEquals(name, data.name) - Assertions.assertEquals(description, data.description) - Assertions.assertEquals(custom, data.custom) - Assertions.assertEquals(updated, data.updated) - Assertions.assertEquals(eTag, data.eTag) - Assertions.assertEquals(type, data.type) - Assertions.assertEquals(status, data.status) - } - Assertions.assertEquals(channel, result.channel) - Assertions.assertEquals(subscription, result.subscription) - Assertions.assertEquals(timetoken, result.timetoken) - Assertions.assertEquals(userMetadata, result.userMetadata) - Assertions.assertEquals(publisher, result.publisher) - } - - @Test - fun getDeleteChannelMetadataResult() { - val message = PNDeleteChannelMetadataEventMessage(source, version, event, type, channel) - val objectEvent = PNObjectEventResult(BasePubSubResult(channel, subscription, timetoken, userMetadata, publisher), message) - - val result: PNChannelMetadataResult = DelegatingEventListener.getDeleteChannelMetadataResult(objectEvent, message) - - Assertions.assertEquals(event, result.event) - result.data.let { data -> - Assertions.assertEquals(channel, data.id) - Assertions.assertEquals(null, data.name) - Assertions.assertEquals(null, data.description) - Assertions.assertEquals(null, data.custom) - Assertions.assertEquals(null, data.updated) - Assertions.assertEquals(null, data.eTag) - Assertions.assertEquals(null, data.type) - Assertions.assertEquals(null, data.status) - } - Assertions.assertEquals(channel, result.channel) - Assertions.assertEquals(subscription, result.subscription) - Assertions.assertEquals(timetoken, result.timetoken) - Assertions.assertEquals(userMetadata, result.userMetadata) - Assertions.assertEquals(publisher, result.publisher) - } - - @Test - fun getDeleteMembershipResult() { - val membershipEvent = PNDeleteMembershipEvent(channel, uuid) - val message = PNDeleteMembershipEventMessage(source, version, event, type, membershipEvent) - val objectEvent = PNObjectEventResult(BasePubSubResult(channel, subscription, timetoken, userMetadata, publisher), message) - - val result: PNMembershipResult = DelegatingEventListener.getDeleteMembershipResult(objectEvent, message) - - Assertions.assertEquals(event, result.event) - result.data.let { data -> - Assertions.assertEquals(channel, data.channel.id) - Assertions.assertEquals(uuid, data.uuid) - Assertions.assertEquals(null, data.custom) - Assertions.assertEquals(null, data.updated) - Assertions.assertEquals(null, data.eTag) - Assertions.assertEquals(null, data.status) - } - Assertions.assertEquals(channel, result.channel) - Assertions.assertEquals(subscription, result.subscription) - Assertions.assertEquals(timetoken, result.timetoken) - Assertions.assertEquals(userMetadata, result.userMetadata) - Assertions.assertEquals(publisher, result.publisher) - } - - @Test - fun getSetMembershipResult() { - val metadata = PNSetMembershipEvent(channel, uuid, PatchValue.of(custom), eTag, updated, PatchValue.of(status)) - val message = PNSetMembershipEventMessage(source, version, event, type, metadata) - val objectEvent = PNObjectEventResult(BasePubSubResult(channel, subscription, timetoken, userMetadata, publisher), message) - - val result: PNMembershipResult = DelegatingEventListener.getSetMembershipResult(objectEvent, message) - - Assertions.assertEquals(event, result.event) - result.data.let { data -> - Assertions.assertEquals(channel, data.channel.id) - Assertions.assertEquals(uuid, data.uuid) - Assertions.assertEquals(custom, data.custom) - Assertions.assertEquals(updated, data.updated) - Assertions.assertEquals(eTag, data.eTag) - Assertions.assertEquals(status, data.status) - } - Assertions.assertEquals(channel, result.channel) - Assertions.assertEquals(subscription, result.subscription) - Assertions.assertEquals(timetoken, result.timetoken) - Assertions.assertEquals(userMetadata, result.userMetadata) - Assertions.assertEquals(publisher, result.publisher) - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingStatusListenerTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingStatusListenerTest.kt deleted file mode 100644 index 9595360d3..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingStatusListenerTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.pubnub.internal.v2.callbacks - -import com.pubnub.api.v2.callbacks.StatusListener -import com.pubnub.internal.callbacks.DelegatingStatusListener -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test - -internal class DelegatingStatusListenerTest { - @Test - fun testEquals() { - val statusListener = StatusListener { pubnub, pnStatus -> TODO("Not yet implemented") } - val otherStatusListener = StatusListener { pubnub, pnStatus -> TODO("Not yet implemented") } - val delegating1 = DelegatingStatusListener(statusListener) - val delegating2 = DelegatingStatusListener(statusListener) - val otherDelegating = DelegatingStatusListener(otherStatusListener) - - Assertions.assertEquals(delegating1, delegating2) - Assertions.assertEquals(delegating2, delegating1) - Assertions.assertNotEquals(delegating1, otherDelegating) - Assertions.assertNotEquals(delegating2, otherDelegating) - Assertions.assertNotEquals(otherDelegating, delegating1) - Assertions.assertNotEquals(otherDelegating, delegating2) - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallbackTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallbackTest.kt deleted file mode 100644 index 82d47bfe4..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallbackTest.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.pubnub.internal.v2.callbacks - -import com.pubnub.api.PubNub -import com.pubnub.api.models.consumer.PNStatus -import com.pubnub.internal.callbacks.DelegatingSubscribeCallback -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test - -internal class DelegatingSubscribeCallbackTest { - @Test - fun testEquals() { - val statusListener = - object : com.pubnub.api.callbacks.SubscribeCallback() { - override fun status( - pubnub: PubNub, - pnStatus: PNStatus, - ) { } - } - val otherStatusListener = - object : com.pubnub.api.callbacks.SubscribeCallback() { - override fun status( - pubnub: PubNub, - pnStatus: PNStatus, - ) {} - } - - val delegating1 = DelegatingSubscribeCallback(statusListener) - val delegating2 = DelegatingSubscribeCallback(statusListener) - val otherDelegating = DelegatingSubscribeCallback(otherStatusListener) - - Assertions.assertEquals(delegating1, delegating2) - Assertions.assertEquals(delegating2, delegating1) - Assertions.assertNotEquals(delegating1, otherDelegating) - Assertions.assertNotEquals(delegating2, otherDelegating) - Assertions.assertNotEquals(otherDelegating, delegating1) - Assertions.assertNotEquals(otherDelegating, delegating2) - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/SubscriptionImplTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/SubscriptionImplTest.kt deleted file mode 100644 index dee2e806c..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/SubscriptionImplTest.kt +++ /dev/null @@ -1,160 +0,0 @@ -package com.pubnub.internal.v2.subscription - -import com.pubnub.api.callbacks.SubscribeCallback -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.callbacks.handlers.OnChannelMetadataHandler -import com.pubnub.api.v2.callbacks.handlers.OnFileHandler -import com.pubnub.api.v2.callbacks.handlers.OnMembershipHandler -import com.pubnub.api.v2.callbacks.handlers.OnMessageActionHandler -import com.pubnub.api.v2.callbacks.handlers.OnMessageHandler -import com.pubnub.api.v2.callbacks.handlers.OnPresenceHandler -import com.pubnub.api.v2.callbacks.handlers.OnSignalHandler -import com.pubnub.api.v2.callbacks.handlers.OnUuidMetadataHandler -import com.pubnub.api.v2.subscriptions.EmptyOptions -import com.pubnub.api.v2.subscriptions.SubscriptionOptions -import com.pubnub.internal.PubNubCore -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.callbacks.DelegatingSubscribeCallback -import com.pubnub.internal.v2.callbacks.DelegatingEventListener -import com.pubnub.internal.v2.callbacks.EventEmitterImpl -import com.pubnub.internal.v2.callbacks.EventListenerCore -import com.pubnub.internal.v2.entities.ChannelGroupName -import com.pubnub.internal.v2.entities.ChannelName -import io.mockk.every -import io.mockk.mockk -import io.mockk.slot -import io.mockk.verify -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -class SubscriptionImplTest { - private lateinit var objectUnderTest: SubscriptionImpl - - private val pubnub: PubNubImpl = mockk(relaxed = true) - private val pubnubCore: PubNubCore = mockk() - private val channels: Set = emptySet() - private val channelGroups: Set = emptySet() - private val options: SubscriptionOptions = EmptyOptions - private val eventEmitter: EventEmitterImpl = mockk(relaxed = true) - private val eventListener: EventListener = mockk() - private val listenerSubscribeCallback: SubscribeCallback = mockk() - private val emitterHelper: EmitterHelper = mockk(relaxed = true) - - @BeforeEach - fun setUp() { - every { pubnub.pubNubCore } returns pubnubCore - every { eventEmitter.addListener(any()) } returns Unit - every { emitterHelper.initialize(eventEmitter) } returns Unit - objectUnderTest = SubscriptionImpl(pubnub, channels, channelGroups, options, emitterHelper) { eventEmitter } - } - - @Test - fun `addListener should call addListener on eventEmitter`() { - val capturedListeners = mutableListOf() - objectUnderTest.addListener(eventListener) - - verify { eventEmitter.addListener(capture(capturedListeners)) } - assertTrue(capturedListeners.all { it is DelegatingEventListener }) - } - - @Test - fun `removeListener which is SubscribeCallback should call proper removeListener on eventEmitter`() { - val delegatingSubscribeCallbackSlot = slot() - objectUnderTest.removeListener(listenerSubscribeCallback) - - verify(exactly = 1) { eventEmitter.removeListener(capture(delegatingSubscribeCallbackSlot)) } - assertTrue(delegatingSubscribeCallbackSlot.captured is DelegatingSubscribeCallback) - assertEquals(listenerSubscribeCallback, delegatingSubscribeCallbackSlot.captured.listener) - } - - @Test - fun `removeListener which is EventListener should call proper removeListener on eventEmitter`() { - val delegatingEventListenerSlot = slot() - objectUnderTest.removeListener(eventListener) - - verify(exactly = 1) { eventEmitter.removeListener(capture(delegatingEventListenerSlot)) } - assertTrue(delegatingEventListenerSlot.captured is DelegatingEventListener) - assertEquals(eventListener, delegatingEventListenerSlot.captured.listener) - } - - @Test - fun `removeAllListeners should call removeAllListeners on eventEmitter`() { - objectUnderTest.removeAllListeners() - - verify(exactly = 1) { eventEmitter.removeAllListeners() } - } - - @Test - fun `setOnMessage should set property on emitterHelper`() { - val onMessageHandler: OnMessageHandler = mockk() - - objectUnderTest.setOnMessage(onMessageHandler) - - verify { emitterHelper.onMessage = onMessageHandler } - } - - @Test - fun `setOnSignal should set property on emitterHelper`() { - val onSignalHandler: OnSignalHandler = mockk() - - objectUnderTest.setOnSignal(onSignalHandler) - - verify { emitterHelper.onSignal = onSignalHandler } - } - - @Test - fun `setOnPresence should set property on emitterHelper`() { - val onPresenceHandler: OnPresenceHandler = mockk() - - objectUnderTest.setOnPresence(onPresenceHandler) - - verify { emitterHelper.onPresence = onPresenceHandler } - } - - @Test - fun `setOnMessageAction should set property on emitterHelper`() { - val onMessageActionHandler: OnMessageActionHandler = mockk() - - objectUnderTest.setOnMessageAction(onMessageActionHandler) - - verify { emitterHelper.onMessageAction = onMessageActionHandler } - } - - @Test - fun `setOnUuidMetadata should set property on emitterHelper`() { - val onUuidMetadataHandler: OnUuidMetadataHandler = mockk() - - objectUnderTest.setOnUuidMetadata(onUuidMetadataHandler) - - verify { emitterHelper.onUuid = onUuidMetadataHandler } - } - - @Test - fun `setOnChannelMetadata should set property on emitterHelper`() { - val onChannelMetadataHandler: OnChannelMetadataHandler = mockk() - - objectUnderTest.setOnChannelMetadata(onChannelMetadataHandler) - - verify { emitterHelper.onChannel = onChannelMetadataHandler } - } - - @Test - fun `setOnMembership should set property on emitterHelper`() { - val onMembershipHandler: OnMembershipHandler = mockk() - - objectUnderTest.setOnMembership(onMembershipHandler) - - verify { emitterHelper.onMembership = onMembershipHandler } - } - - @Test - fun `setOnFile should set property on emitterHelper`() { - val onFileHandler: OnFileHandler = mockk() - - objectUnderTest.setOnFile(onFileHandler) - - verify { emitterHelper.onFile = onFileHandler } - } -} diff --git a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/SubscriptionSetImplTest.kt b/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/SubscriptionSetImplTest.kt deleted file mode 100644 index 95359889f..000000000 --- a/pubnub-gson/pubnub-gson-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/SubscriptionSetImplTest.kt +++ /dev/null @@ -1,106 +0,0 @@ -package com.pubnub.internal.v2.subscription - -import com.pubnub.api.v2.callbacks.handlers.OnChannelMetadataHandler -import com.pubnub.api.v2.callbacks.handlers.OnFileHandler -import com.pubnub.api.v2.callbacks.handlers.OnMembershipHandler -import com.pubnub.api.v2.callbacks.handlers.OnMessageActionHandler -import com.pubnub.api.v2.callbacks.handlers.OnMessageHandler -import com.pubnub.api.v2.callbacks.handlers.OnPresenceHandler -import com.pubnub.api.v2.callbacks.handlers.OnSignalHandler -import com.pubnub.api.v2.callbacks.handlers.OnUuidMetadataHandler -import com.pubnub.internal.PubNubCore -import com.pubnub.internal.managers.ListenerManager -import io.mockk.Runs -import io.mockk.every -import io.mockk.just -import io.mockk.mockk -import io.mockk.verify -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -class SubscriptionSetImplTest { - private lateinit var objectUnderTest: SubscriptionSetImpl - - private val pubNubCore: PubNubCore = mockk(relaxed = true) - private val emitterHelper: EmitterHelper = mockk(relaxed = true) - private val listenerManager: ListenerManager = mockk(relaxed = true) - - @BeforeEach - fun setUp() { - every { pubNubCore.listenerManager } returns listenerManager - every { emitterHelper.initialize(any()) } just Runs - objectUnderTest = SubscriptionSetImpl(pubNubCore, emptySet(), emitterHelper) - } - - @Test - fun `setOnMessage sets onMessageHandler in EmitterHelper`() { - val onMessageHandler: OnMessageHandler = mockk() - - objectUnderTest.setOnMessage(onMessageHandler) - - verify { emitterHelper.onMessage = onMessageHandler } - } - - @Test - fun `setOnSignal should set property on emitterHelper`() { - val onSignalHandler: OnSignalHandler = mockk() - - objectUnderTest.setOnSignal(onSignalHandler) - - verify { emitterHelper.onSignal = onSignalHandler } - } - - @Test - fun `setOnPresence should set property on emitterHelper`() { - val onPresenceHandler: OnPresenceHandler = mockk() - - objectUnderTest.setOnPresence(onPresenceHandler) - - verify { emitterHelper.onPresence = onPresenceHandler } - } - - @Test - fun `setOnMessageAction should set property on emitterHelper`() { - val onMessageActionHandler: OnMessageActionHandler = mockk() - - objectUnderTest.setOnMessageAction(onMessageActionHandler) - - verify { emitterHelper.onMessageAction = onMessageActionHandler } - } - - @Test - fun `setOnUuidMetadata should set property on emitterHelper`() { - val onUuidMetadataHandler: OnUuidMetadataHandler = mockk() - - objectUnderTest.setOnUuidMetadata(onUuidMetadataHandler) - - verify { emitterHelper.onUuid = onUuidMetadataHandler } - } - - @Test - fun `setOnChannelMetadata should set property on emitterHelper`() { - val onChannelMetadataHandler: OnChannelMetadataHandler = mockk() - - objectUnderTest.setOnChannelMetadata(onChannelMetadataHandler) - - verify { emitterHelper.onChannel = onChannelMetadataHandler } - } - - @Test - fun `setOnMembership should set property on emitterHelper`() { - val onMembershipHandler: OnMembershipHandler = mockk() - - objectUnderTest.setOnMembership(onMembershipHandler) - - verify { emitterHelper.onMembership = onMembershipHandler } - } - - @Test - fun `setOnFile should set property on emitterHelper`() { - val onFileHandler: OnFileHandler = mockk() - - objectUnderTest.setOnFile(onFileHandler) - - verify { emitterHelper.onFile = onFileHandler } - } -} diff --git a/pubnub-kotlin/build.gradle.kts b/pubnub-kotlin/build.gradle.kts index c6059f2d6..1224b4548 100644 --- a/pubnub-kotlin/build.gradle.kts +++ b/pubnub-kotlin/build.gradle.kts @@ -5,8 +5,8 @@ plugins { } dependencies { - api(project(":pubnub-core:pubnub-core-api")) +// api(project(":pubnub-core:pubnub-core-api")) api(project(":pubnub-kotlin:pubnub-kotlin-api")) - implementation(project(":pubnub-core:pubnub-core-impl")) +// implementation(project(":pubnub-core:pubnub-core-impl")) implementation(project(":pubnub-kotlin:pubnub-kotlin-impl")) } diff --git a/pubnub-kotlin/pubnub-kotlin-api/build.gradle.kts b/pubnub-kotlin/pubnub-kotlin-api/build.gradle.kts index 96e10880c..c9f60d159 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/build.gradle.kts +++ b/pubnub-kotlin/pubnub-kotlin-api/build.gradle.kts @@ -14,13 +14,20 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - api(project(":pubnub-core:pubnub-core-api")) +// api(project(":pubnub-core:pubnub-core-api")) + implementation(libs.kotlinx.atomicfu) } } val jvmMain by getting { dependencies { - implementation(project(":pubnub-core:pubnub-core-impl")) +// implementation(project(":pubnub-core:pubnub-core-impl")) + api(libs.retrofit2) + api(libs.okhttp) + api(libs.okhttp.logging) + api(libs.json) + api(libs.gson) + implementation(libs.slf4j) implementation(libs.slf4j) } } @@ -50,7 +57,7 @@ kotlin { if (enableAnyIosTarget) { (this as ExtensionAware).extensions.configure { framework { - export(project(":pubnub-core:pubnub-core-api")) +// export(project(":pubnub-core:pubnub-core-api")) } } } diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/JsonElement.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/JsonElement.kt new file mode 100644 index 000000000..c01cf621b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/JsonElement.kt @@ -0,0 +1,32 @@ +package com.pubnub.api + +expect abstract class JsonElement + +expect fun JsonElement.isNull(): Boolean + +expect fun JsonElement.asList(): List? + +expect fun JsonElement.asLong(): Long? + +expect fun JsonElement.asDouble(): Double? + +expect fun JsonElement.asNumber(): Number? + +expect fun JsonElement.asBoolean(): Boolean? + +expect fun JsonElement.asString(): String? + +expect fun JsonElement.asMap(): Map? + +expect fun createJsonElement(any: Any?): JsonElement + +fun JsonElement.decode(): Any? { + if (isNull()) { + return null + } + return asMap()?.mapValues { it.value.decode() } + ?: asList()?.map { it.decode() } + ?: asNumber() + ?: asBoolean() + ?: asString() +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/PubNub.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/PubNub.kt index f410310e8..5a6ba7a25 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/PubNub.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/PubNub.kt @@ -89,7 +89,7 @@ expect interface PubNub { channel: String, message: Any, meta: Any? = null, - shouldStore: Boolean = true, + shouldStore: Boolean? = null, usePost: Boolean = false, replicate: Boolean = true, ttl: Int? = null, diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/PubNubError.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/PubNubError.kt new file mode 100644 index 000000000..1a5e052b6 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/PubNubError.kt @@ -0,0 +1,244 @@ +package com.pubnub.api + +import com.pubnub.api.models.consumer.PNStatus + +/** + * List of known PubNub errors. Observe them in [PubNubException.pubnubError] in [PNStatus.exception]. + * + * @property code The error code. + * @property message The error message. + */ +enum class PubNubError(private val code: Int, val message: String) { + TIMEOUT( + 100, + "Timeout Occurred", + ), + + CONNECT_EXCEPTION( + 102, + "Connect Exception. Please verify if network is reachable", + ), + + SECRET_KEY_MISSING( + 114, + "ULS configuration failed. Secret Key not configured", + ), + + JSON_ERROR( + 121, + "JSON Error while processing API response", + ), + INTERNAL_ERROR( + 125, + "Internal Error", + ), + PARSING_ERROR( + 126, + "Parsing Error", + ), + INVALID_ARGUMENTS( + 131, + "Invalid arguments", + ), + GROUP_MISSING( + 136, + "Group Missing", + ), + + SUBSCRIBE_KEY_MISSING( + 138, + "ULS configuration failed. Subscribe Key not configured.", + ), + + PUBLISH_KEY_MISSING( + 139, + "ULS configuration failed. Publish Key not configured.", + ), + + SUBSCRIBE_TIMEOUT( + 130, + "Subscribe Timeout", + ), + + HTTP_ERROR( + 103, + "HTTP Error. Please check network connectivity.", + ), + + MESSAGE_MISSING( + 142, + "Message Missing", + ), + + CHANNEL_MISSING( + 132, + "Channel Missing", + ), + + CRYPTO_ERROR( + 135, + "Error while encrypting/decrypting message. Please contact support with error details.", + ), + + STATE_MISSING( + 140, + "State Missing.", + ), + + CHANNEL_AND_GROUP_MISSING( + 141, + "Channel and Group Missing.", + ), + + PUSH_TYPE_MISSING( + 143, + "Push Type Missing.", + ), + + DEVICE_ID_MISSING( + 144, + "Device ID Missing", + ), + + TIMETOKEN_MISSING( + 145, + "Timetoken Missing.", + ), + + CHANNELS_TIMETOKEN_MISMATCH( + 146, + "Channels and timetokens are not equal in size.", + ), + + USER_MISSING( + 147, + "User is missing", + ), + + USER_ID_MISSING( + 148, + "User ID is missing", + ), + + USER_NAME_MISSING( + 149, + "User name is missing", + ), + + RESOURCES_MISSING( + 153, + "Resources missing", + ), + + PERMISSION_MISSING( + 156, + "Permission missing", + ), + + INVALID_ACCESS_TOKEN( + 157, + "Invalid access token", + ), + + MESSAGE_ACTION_MISSING( + 158, + "Message action is missing.", + ), + + MESSAGE_ACTION_TYPE_MISSING( + 159, + "Message action type is missing.", + ), + + MESSAGE_ACTION_VALUE_MISSING( + 160, + "Message action value is missing.", + ), + + MESSAGE_TIMETOKEN_MISSING( + 161, + "Message timetoken is missing.", + ), + + MESSAGE_ACTION_TIMETOKEN_MISSING( + 162, + "Message action timetoken is missing.", + ), + + HISTORY_MESSAGE_ACTIONS_MULTIPLE_CHANNELS( + 163, + "History can return message action data for a single channel only. Either pass a single channel or disable the includeMessageActions flag.", + ), + + PUSH_TOPIC_MISSING( + 164, + "Push notification topic is missing. Required only if push type is APNS2.", + ), + + TOKEN_MISSING( + 168, + "Token missing", + ), + + UUID_NULL_OR_EMPTY( + 169, + "Uuid can't be null nor empty", + ), + + USERID_NULL_OR_EMPTY( + 170, + "UserId can't have empty value", + ), + + CHANNEL_OR_CHANNEL_GROUP_MISSING( + 171, + "Please, provide channel or channelGroup", + ), + + UNKNOWN_CRYPTOR( + 172, + "Cryptor not found.", + ), + + CRYPTOR_DATA_HEADER_SIZE_TO_SMALL( + 173, + "Cryptor data size is to small.", + ), + + CRYPTOR_HEADER_VERSION_UNKNOWN( + 174, + "Cryptor header version unknown. Please, update SDK.", + ), + + CRYPTOR_HEADER_PARSE_ERROR( + 175, + "Cryptor header parse error.", + ), + + ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED( + 176, + "Encryption of empty data not allowed.", + ), + + CRYPTO_IS_CONFIGURED_BUT_MESSAGE_IS_NOT_ENCRYPTED( + 177, + "Message decryption failed using the current crypto configuration.", + ), + TTL_MISSING( + 178, + "TTL missing", + ), + STATE_MUST_BE_JSON_OBJECT( + 179, + "State must be a JSON object.", + ), + USERID_CAN_NOT_BE_DIFFERENT_FROM_IN_CONFIGURATION_WHEN_WITHHEARTBEAT_TRUE( + 180, + "UserId can't be different from UserId in configuration when flag withHeartbeat is set to true", + ), + ; + + override fun toString(): String { + return "PubNubError(name=$name, code=$code, message='$message')" + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/PubNubException.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/PubNubException.kt new file mode 100644 index 000000000..67a693011 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/PubNubException.kt @@ -0,0 +1,23 @@ +package com.pubnub.api + +/** + * Custom exception wrapper for errors occurred during execution or processing of a PubNub API operation. + * + * @property errorMessage The error message received from the server, if any. + * @property pubnubError The appropriate matching PubNub error. + * @property jso The error json received from the server, if any. + * @property statusCode HTTP status code. + * @property affectedCall A reference to the affected call. Useful for calling [retry][Endpoint.retry]. + */ +expect class PubNubException(errorMessage: String?, cause: Throwable? = null) : Exception { + constructor(pubnubError: PubNubError, cause: Throwable? = null) + + // test only + constructor(errorMessage: String?, statusCode: Int, cause: Throwable? = null) + + val statusCode: Int + + companion object { + fun from(e: Throwable): PubNubException + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/UserId.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/UserId.kt new file mode 100644 index 000000000..060b4face --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/UserId.kt @@ -0,0 +1,14 @@ +package com.pubnub.api + +import kotlin.js.JsExport + +@JsExport +data class UserId + @Throws(PubNubException::class) + constructor(val value: String) { + init { + if (value.isBlank()) { + throw PubNubException(PubNubError.USERID_NULL_OR_EMPTY) + } + } + } diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/callbacks/Listener.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/callbacks/Listener.kt new file mode 100644 index 000000000..5d57141f1 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/callbacks/Listener.kt @@ -0,0 +1,6 @@ +package com.pubnub.api.callbacks + +/** + * Base superclass for all listeners. Not intended for subclassing directly. + */ +interface Listener diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/endpoints/remoteaction/RemoteAction.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/endpoints/remoteaction/RemoteAction.kt new file mode 100644 index 000000000..63cd199ca --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/endpoints/remoteaction/RemoteAction.kt @@ -0,0 +1,56 @@ +package com.pubnub.api.endpoints.remoteaction + +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.v2.callbacks.Consumer +import com.pubnub.api.v2.callbacks.Result +import com.pubnub.kmp.PNFuture + +interface ExtendedRemoteAction : RemoteAction { + /** + * Return the type of this operation from the values defined in [PNOperationType]. + */ + fun operationType(): PNOperationType +} + +interface RemoteAction : PNFuture, Cancelable { + /** + * Run the action synchronously, potentially blocking the calling thread. + * @return returns the result of the action + * @throws PubNubException in case of an error + */ + @Throws(PubNubException::class) + fun sync(): Output + + /** + * Run the action asynchronously, without blocking the calling thread and delivering the result through + * the [callback]. + * + * The delivered result can be either a success (including a result value if any) or + * a failure (including a [PubNubException]). + * + * The recommended pattern to use is: + * ```kotlin + * action.async { result -> + * result.onSuccess { value -> + * // do something with value + * }.onFailure { exception -> + * // do something with exception + * } + * } + * ``` + */ + override fun async(callback: Consumer>) + + /** + * Attempt to retry the action and deliver the result to a callback registered with a previous call to [async]. + */ + fun retry() +} + +interface Cancelable { + /** + * Cancel the action without reporting any further results. + */ + fun silentCancel() +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNHeartbeatNotificationOptions.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNHeartbeatNotificationOptions.kt new file mode 100644 index 000000000..9d5bd350d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNHeartbeatNotificationOptions.kt @@ -0,0 +1,18 @@ +package com.pubnub.api.enums + +enum class PNHeartbeatNotificationOptions { + /** + * Do not report heartbeat events through [com.pubnub.api.v2.callbacks.StatusListener] + */ + NONE, + + /** + * Receive failed heartbeat events in [com.pubnub.api.v2.callbacks.StatusListener] + */ + FAILURES, + + /** + * Receive all heartbeat events in [com.pubnub.api.v2.callbacks.StatusListener] + */ + ALL, +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNLogVerbosity.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNLogVerbosity.kt new file mode 100644 index 000000000..5ddce72b4 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNLogVerbosity.kt @@ -0,0 +1,17 @@ +package com.pubnub.api.enums + +enum class PNLogVerbosity { + /** + * No logs. + * + * @see [okhttp3.logging.HttpLoggingInterceptor.Level.NONE] + */ + NONE, + + /** + * Logs request and response lines and their respective headers and bodies (if present). + * + * @see [okhttp3.logging.HttpLoggingInterceptor.Level.BODY] + */ + BODY, +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNOperationType.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNOperationType.kt new file mode 100644 index 000000000..17b4e3fb8 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNOperationType.kt @@ -0,0 +1,121 @@ +package com.pubnub.api.enums + +sealed class PNOperationType(open val queryParam: String? = null) { + open class PublishOperation : PNOperationType("pub") + + open class HistoryOperation : PNOperationType("hist") + + open class PresenceOperation : PNOperationType("pres") + + open class ChannelGroupOperation : PNOperationType("cg") + + open class PushNotificationsOperation : PNOperationType("push") + + open class PAMOperation : PNOperationType("pam") + + open class MessageCountsOperation : PNOperationType("mc") + + open class SignalsOperation : PNOperationType("sig") + + open class ObjectsOperation : PNOperationType("obj") + + open class PAMV3Operation : PNOperationType("pamv3") + + open class MessageActionsOperation : PNOperationType("msga") + + open class TimeOperation : PNOperationType("time") + + object FileOperation : PNOperationType("file") + + object SpaceOperation : PNOperationType("obj") + + object UserOperation : PNOperationType("obj") + + object MembershipOperation : PNOperationType("obj") + + object PNSubscribeOperation : PNOperationType() + + object PNDisconnectOperation : PNOperationType() + + object PNPublishOperation : PublishOperation() + + object PNHistoryOperation : HistoryOperation() + + object PNFetchMessagesOperation : HistoryOperation() + + object PNDeleteMessagesOperation : HistoryOperation() + + object PNUnsubscribeOperation : PresenceOperation() + + object PNWhereNowOperation : PresenceOperation() + + object PNHereNowOperation : PresenceOperation() + + object PNHeartbeatOperation : PresenceOperation() + + object PNSetStateOperation : PresenceOperation() + + object PNGetState : PresenceOperation() + + object PNAddChannelsToGroupOperation : ChannelGroupOperation() + + object PNRemoveChannelsFromGroupOperation : ChannelGroupOperation() + + object PNChannelGroupsOperation : ChannelGroupOperation() + + object PNRemoveGroupOperation : ChannelGroupOperation() + + object PNChannelsForGroupOperation : ChannelGroupOperation() + + object PNPushNotificationEnabledChannelsOperation : PushNotificationsOperation() + + object PNAddPushNotificationsOnChannelsOperation : PushNotificationsOperation() + + object PNRemovePushNotificationsFromChannelsOperation : PushNotificationsOperation() + + object PNRemoveAllPushNotificationsOperation : PushNotificationsOperation() + + object PNAccessManagerAudit : PAMOperation() + + object PNAccessManagerGrant : PAMOperation() + + object PNMessageCountOperation : MessageCountsOperation() + + object PNSignalOperation : SignalsOperation() + + object PNSetUUIDMetadataOperation : ObjectsOperation() + + object PNGetUUIDMetadataOperation : ObjectsOperation() + + object PNGetAllUUIDMetadataOperation : ObjectsOperation() + + object PNRemoveUUIDMetadataOperation : ObjectsOperation() + + object PNSetChannelMetadataOperation : ObjectsOperation() + + object PNGetChannelMetadataOperation : ObjectsOperation() + + object PNGetAllChannelsMetadataOperation : ObjectsOperation() + + object PNRemoveChannelMetadataOperation : ObjectsOperation() + + object PNGetMembershipsOperation : ObjectsOperation() + + object PNSetMembershipsOperation : ObjectsOperation() + + object PNUpdateMembershipsOperation : ObjectsOperation() + + object PNManageMemberships : ObjectsOperation() + + object PNAccessManagerGrantToken : PAMV3Operation() + + object PNAccessManagerRevokeToken : PAMV3Operation() + + object PNAddMessageAction : MessageActionsOperation() + + object PNGetMessageActions : MessageActionsOperation() + + object PNDeleteMessageAction : MessageActionsOperation() + + object PNTimeOperation : TimeOperation() +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNPushEnvironment.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNPushEnvironment.kt new file mode 100644 index 000000000..e145db68b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNPushEnvironment.kt @@ -0,0 +1,11 @@ +package com.pubnub.api.enums + +enum class PNPushEnvironment { + DEVELOPMENT, + PRODUCTION, + ; + + fun toParamString(): String { + return name.lowercase() + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNPushType.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNPushType.kt new file mode 100644 index 000000000..644f5d214 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNPushType.kt @@ -0,0 +1,18 @@ +package com.pubnub.api.enums + +enum class PNPushType(private val value: String) { + APNS("apns"), + MPNS("mpns"), + GCM("gcm"), + FCM("gcm"), + APNS2("apns2"), + ; + + fun toParamString(): String { + return value.lowercase() + } + + override fun toString(): String { + return value + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNReconnectionPolicy.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNReconnectionPolicy.kt new file mode 100644 index 000000000..5556fea74 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNReconnectionPolicy.kt @@ -0,0 +1,25 @@ +package com.pubnub.api.enums + +@Deprecated(message = "Use [com.pubnub.api.retry.RetryConfiguration] instead.") +enum class PNReconnectionPolicy { + /** + * No reconnection policy. If the subscribe loop gets cancelled due to network or other issues, + * the SDK will not attempt to try to restore it. + */ + NONE, + + /** + * The SDK will try to reconnect to the network by doing so every 3 seconds. + * + * @see [PNConfiguration.maximumReconnectionRetries] + */ + LINEAR, + + /** + * The SDK will try to reconnect to the network by doing so in non-fixed intervals. + * ie. the interval between retries is another power of 2, starting from 0 to 5. + * + * @see [PNConfiguration.maximumReconnectionRetries] + */ + EXPONENTIAL, +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNStatusCategory.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNStatusCategory.kt new file mode 100644 index 000000000..7054bf774 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/enums/PNStatusCategory.kt @@ -0,0 +1,54 @@ +package com.pubnub.api.enums + +import com.pubnub.api.models.consumer.PNStatus + +/** + * Check the status category via [PNStatus.category] in the [com.pubnub.api.v2.callbacks.StatusListener] added to a + * [com.pubnub.api.PubNub] object. + */ +enum class PNStatusCategory { + /** + * SDK successfully connected the Subscribe loop. + */ + PNConnectedCategory, + + /** + * SDK subscribed with a new mix of channels (fired every time the channel / channel group mix changed) since the + * initial connection. + */ + PNSubscriptionChanged, + + /** + * Previously started subscribe loop failed and at this moment client is disconnected from real-time data channels. + */ + PNUnexpectedDisconnectCategory, + + /** + * The subscription has been stopped as requested (when all channels and channel groups have been unsubscribed). + */ + PNDisconnectedCategory, + + /** + * The subscription loop was not able to connect, and at this moment the client is disconnected from real-time + * data channels. + */ + PNConnectionError, + + /** + * A background implicit Heartbeat request attempt failed. + */ + PNHeartbeatFailed, + + /** + * A background implicit Heartbeat request was successful. + */ + PNHeartbeatSuccess, + + /** + * PubNub sent a malformed response. + * This may happen when you connect to a public WiFi Hotspot that requires you to auth via your web browser first, + * or if there is a proxy somewhere returning an HTML access denied error, + * or if there was an intermittent server issue. + */ + PNMalformedResponseCategory, +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/TokenBitmask.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/TokenBitmask.kt new file mode 100644 index 000000000..bc18f9a62 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/TokenBitmask.kt @@ -0,0 +1,12 @@ +package com.pubnub.api.models + +object TokenBitmask { + const val READ = 1 + const val WRITE = 2 + const val MANAGE = 4 + const val DELETE = 8 + const val CREATE = 16 + const val GET = 32 + const val UPDATE = 64 + const val JOIN = 128 +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNBoundedPage.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNBoundedPage.kt new file mode 100644 index 000000000..f5277cc86 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNBoundedPage.kt @@ -0,0 +1,16 @@ +package com.pubnub.api.models.consumer + +/** + * The paging object used for pagination + * + * @param start Timetoken denoting the start of the range requested + * (return values will be less than start). + * @param end Timetoken denoting the end of the range requested + * (return values will be greater than or equal to end). + * @param limit Specifies the number of items to return in response. + */ +data class PNBoundedPage( + val start: Long? = null, + val end: Long? = null, + val limit: Int? = null, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNPublishResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNPublishResult.kt new file mode 100644 index 000000000..68bf21c50 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNPublishResult.kt @@ -0,0 +1,14 @@ +package com.pubnub.api.models.consumer + +/** + * Result of the Publish operation + * + * @property timetoken The time token when the message or signal was published. + */ +class PNPublishResult( + val timetoken: Long, +) { + override fun toString(): String { + return "PNPublishResult(timetoken=$timetoken)" + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNStatus.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNStatus.kt new file mode 100644 index 000000000..f5e7b32dc --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNStatus.kt @@ -0,0 +1,56 @@ +package com.pubnub.api.models.consumer + +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNStatusCategory +import kotlin.jvm.JvmName + +class PNStatus( + val category: PNStatusCategory, + val exception: PubNubException? = null, + val currentTimetoken: Long? = null, + val affectedChannels: Collection = emptySet(), + val affectedChannelGroups: Collection = emptySet(), +) { + @get:JvmName("isError") + val error: Boolean = exception != null + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other !is PNStatus) { + return false + } + + if (category != other.category) { + return false + } + if (exception != other.exception) { + return false + } + if (currentTimetoken != other.currentTimetoken) { + return false + } + if (affectedChannels != other.affectedChannels) { + return false + } + if (affectedChannelGroups != other.affectedChannelGroups) { + return false + } + + return true + } + + override fun hashCode(): Int { + var result = category.hashCode() + result = 31 * result + (exception?.hashCode() ?: 0) + result = 31 * result + (currentTimetoken?.hashCode() ?: 0) + result = 31 * result + affectedChannels.hashCode() + result = 31 * result + affectedChannelGroups.hashCode() + return result + } + + override fun toString(): String { + return "PNStatus(error=$error, affectedChannelGroups=$affectedChannelGroups, affectedChannels=$affectedChannels, currentTimetoken=$currentTimetoken, exception=$exception, category=$category)" + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNTimeResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNTimeResult.kt new file mode 100644 index 000000000..4fbd21e8a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/PNTimeResult.kt @@ -0,0 +1,10 @@ +package com.pubnub.api.models.consumer + +/** + * Result of the Time operation. + * + * @property timetoken Current time token. + */ +class PNTimeResult( + val timetoken: Long, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/PNAccessManagerGrantResults.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/PNAccessManagerGrantResults.kt index c985a7ff6..0489df329 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/PNAccessManagerGrantResults.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/PNAccessManagerGrantResults.kt @@ -1,5 +1,7 @@ package com.pubnub.api.models.consumer.access_manager +import com.pubnub.api.utils.SerializedName + /** * Result of the [PubNubCore.grant] operation * @@ -16,33 +18,45 @@ class PNAccessManagerGrantResult( val subscribeKey: String, val channels: Map?>, val channelGroups: Map?>, + val uuids: Map?>, ) open class PNAccessManagerKeyData { /** * Is `true` if *read* rights are granted. */ + @field:SerializedName("r") var readEnabled: Boolean = false /** * Is `true` if *write* rights are granted. */ + @field:SerializedName("w") var writeEnabled: Boolean = false /** * Is `true` if *manage* rights are granted. */ + @field:SerializedName("m") var manageEnabled: Boolean = false /** * Is `true` if *delete* rights are granted. */ + @field:SerializedName("d") var deleteEnabled: Boolean = false + + @field:SerializedName("g") var getEnabled: Boolean = false + + @field:SerializedName("u") var updateEnabled: Boolean = false + + @field:SerializedName("j") var joinEnabled: Boolean = false } class PNAccessManagerKeysData { + @field:SerializedName("auths") val authKeys: Map? = null } diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/v3/Grants.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/v3/Grants.kt index bd707e462..35debc05a 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/v3/Grants.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/v3/Grants.kt @@ -15,7 +15,7 @@ interface PNGrant { val id: String } -internal sealed class PNAbstractGrant( +sealed class PNAbstractGrant protected constructor( override val read: Boolean = false, override val write: Boolean = false, override val manage: Boolean = false, @@ -26,9 +26,9 @@ internal sealed class PNAbstractGrant( override val update: Boolean = false, ) : PNGrant -internal sealed class PNResourceGrant : PNAbstractGrant() +sealed class PNResourceGrant : PNAbstractGrant() -internal sealed class PNPatternGrant : PNAbstractGrant() +sealed class PNPatternGrant : PNAbstractGrant() internal data class PNChannelResourceGrant( override val id: String, diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/v3/PNGrantTokenResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/v3/PNGrantTokenResult.kt new file mode 100644 index 000000000..87ca63aa3 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/v3/PNGrantTokenResult.kt @@ -0,0 +1,3 @@ +package com.pubnub.api.models.consumer.access_manager.v3 + +data class PNGrantTokenResult(val token: String) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/v3/PNToken.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/v3/PNToken.kt new file mode 100644 index 000000000..913f74c64 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/access_manager/v3/PNToken.kt @@ -0,0 +1,39 @@ +package com.pubnub.api.models.consumer.access_manager.v3 + +import com.pubnub.api.models.TokenBitmask + +data class PNToken( + 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( + val channels: Map = emptyMap(), + val channelGroups: Map = emptyMap(), + val uuids: Map = emptyMap(), + ) + + data class PNResourcePermissions( + val read: Boolean = false, + val write: Boolean = false, + val manage: Boolean = false, + val delete: Boolean = false, + val get: Boolean = false, + val update: Boolean = false, + val join: Boolean = false, + ) { + constructor(grant: Int) : this( + grant and TokenBitmask.READ != 0, + grant and TokenBitmask.WRITE != 0, + grant and TokenBitmask.MANAGE != 0, + grant and TokenBitmask.DELETE != 0, + grant and TokenBitmask.GET != 0, + grant and TokenBitmask.UPDATE != 0, + grant and TokenBitmask.JOIN != 0, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsResults.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsResults.kt new file mode 100644 index 000000000..d472bbad5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsResults.kt @@ -0,0 +1,34 @@ +package com.pubnub.api.models.consumer.channel_group + +/** + * Result of the AddChannelsToChannelGroup operation. + */ +class PNChannelGroupsAddChannelResult + +/** + * Result of the DeleteChannelGroup operation. + */ +class PNChannelGroupsDeleteGroupResult + +/** + * Result of the RemoveChannelsFromChannelGroup operation. + */ +class PNChannelGroupsRemoveChannelResult + +/** + * Result of the ListChannelsForChannelGroup operation. + * + * @property channels List of channels belonging to a channel group. + */ +class PNChannelGroupsAllChannelsResult( + val channels: List, +) + +/** + * Result of the ListAllChannelGroups operation. + * + * @property groups List of all channel groups + */ +class PNChannelGroupsListAllResult( + val groups: List, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNDeleteFileResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNDeleteFileResult.kt new file mode 100644 index 000000000..b7bdc9c6b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNDeleteFileResult.kt @@ -0,0 +1,3 @@ +package com.pubnub.api.models.consumer.files + +data class PNDeleteFileResult(val status: Int) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNDownloadFileResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNDownloadFileResult.kt new file mode 100644 index 000000000..ce136f96e --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNDownloadFileResult.kt @@ -0,0 +1,8 @@ +package com.pubnub.api.models.consumer.files + +import com.pubnub.kmp.Downloadable + +data class PNDownloadFileResult( + val fileName: String, + val byteStream: Downloadable?, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNDownloadableFile.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNDownloadableFile.kt new file mode 100644 index 000000000..d5714f793 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNDownloadableFile.kt @@ -0,0 +1,7 @@ +package com.pubnub.api.models.consumer.files + +data class PNDownloadableFile( + override val id: String, + override val name: String, + val url: String, +) : PNFile diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNFile.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNFile.kt new file mode 100644 index 000000000..e419a0c99 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNFile.kt @@ -0,0 +1,18 @@ +package com.pubnub.api.models.consumer.files + +interface PNFile { + val id: String + val name: String +} + +data class PNUploadedFile( + override val id: String, + override val name: String, + val size: Int, + val created: String, +) : PNFile + +data class PNBaseFile( + override val id: String, + override val name: String, +) : PNFile diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNFileUploadResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNFileUploadResult.kt new file mode 100644 index 000000000..fcd57e92c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNFileUploadResult.kt @@ -0,0 +1,7 @@ +package com.pubnub.api.models.consumer.files + +data class PNFileUploadResult( + val timetoken: Long, + val status: Int, + val file: PNBaseFile, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNFileUrlResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNFileUrlResult.kt new file mode 100644 index 000000000..97a004859 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNFileUrlResult.kt @@ -0,0 +1,3 @@ +package com.pubnub.api.models.consumer.files + +data class PNFileUrlResult(val url: String) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNListFilesResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNListFilesResult.kt new file mode 100644 index 000000000..5ae750910 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNListFilesResult.kt @@ -0,0 +1,10 @@ +package com.pubnub.api.models.consumer.files + +import com.pubnub.api.models.consumer.objects.PNPage + +data class PNListFilesResult( + val count: Int, + val next: PNPage.PNNext?, + val status: Int, + val data: Collection, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNPublishFileMessageResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNPublishFileMessageResult.kt new file mode 100644 index 000000000..c5df79190 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/files/PNPublishFileMessageResult.kt @@ -0,0 +1,3 @@ +package com.pubnub.api.models.consumer.files + +data class PNPublishFileMessageResult(val timetoken: Long) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/HistoryMessageType.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/HistoryMessageType.kt new file mode 100644 index 000000000..b17073be1 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/HistoryMessageType.kt @@ -0,0 +1,18 @@ +package com.pubnub.api.models.consumer.history + +import com.pubnub.api.PubNubException + +enum class HistoryMessageType(val value: Int) { + Message(0), + File(4), + ; + + companion object { + fun of(value: Int?): HistoryMessageType = + when (value) { + null, 0, -1, 999 -> Message + 4 -> File + else -> throw PubNubException("Unknown message type value $value") + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNDeleteMessagesResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNDeleteMessagesResult.kt new file mode 100644 index 000000000..a6ed3be82 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNDeleteMessagesResult.kt @@ -0,0 +1,6 @@ +package com.pubnub.api.models.consumer.history + +/** + * Result of the [PubNubImpl.deleteMessages] operation. + */ +class PNDeleteMessagesResult diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNFetchMessage.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNFetchMessage.kt new file mode 100644 index 000000000..2874ba2f0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNFetchMessage.kt @@ -0,0 +1,84 @@ +package com.pubnub.api.models.consumer.history + +import com.pubnub.api.JsonElement +import com.pubnub.api.PubNubError +import com.pubnub.api.models.consumer.PNBoundedPage +import com.pubnub.api.models.consumer.history.PNFetchMessageItem.Action + +/** + * Result of the FetchMessages operation. + * + * @property channels Map of channels and their respective lists of [PNFetchMessageItem]. + */ +data class PNFetchMessagesResult( + val channels: Map>, + val page: PNBoundedPage?, +) + +/** + * Encapsulates a message in terms of a batch history entry. + * + * @property uuid Publisher uuid. Is `null` if not requested. + * @property message The actual message content. + * @property timetoken Publish timetoken of the message. + * @property meta Metadata of the message, if requested via `includeMeta`. + * Is `null` if not requested, otherwise an empty string if requested but no associated metadata. + * @property actions The message actions associated with the message. + * Is `null` if not requested via `includeMessageActions`. + * The key of the map is the action type. The value is another map, + * which key is the actual value of the message action, + * and the key being a list of actions, i.e. a list of UUIDs which have posted such a message action. + * @see [Action] + * @property messageType The message type associated with the item. + * @property error The error associated with message retrieval, if any. + * e.g. a message is unencrypted but PubNub instance is configured with the Crypto + * so PubNub can't decrypt the unencrypted message and return the message. + */ +data class PNFetchMessageItem( + val uuid: String?, + val message: JsonElement, + val meta: JsonElement?, + val timetoken: Long?, + val actions: Map>>? = null, + val messageType: HistoryMessageType?, + val error: PubNubError? = null, +) { + // for compatibility with legacy Java SDK + class Action(uuid: String, actionTimetoken: Long) : + com.pubnub.api.models.consumer.history.Action(uuid, actionTimetoken) +} + +/** + * Encapsulates a message action in terms of batch history. + * + * @property uuid The UUID of the publisher. + * @property actionTimetoken The publish timetoken of the message action. + */ +open class Action( + val uuid: String, + val actionTimetoken: Long, +) { + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other !is Action) { + return false + } + + if (uuid != other.uuid) { + return false + } + if (actionTimetoken != other.actionTimetoken) { + return false + } + + return true + } + + override fun hashCode(): Int { + var result = uuid.hashCode() + result = 31 * result + actionTimetoken.hashCode() + return result + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNHistoryResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNHistoryResult.kt new file mode 100644 index 000000000..aa0f1b512 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNHistoryResult.kt @@ -0,0 +1,39 @@ +package com.pubnub.api.models.consumer.history + +import com.pubnub.api.JsonElement +import com.pubnub.api.PubNubError + +/** + * Result of a History operation. + * + * @property messages List of messages as instances of [PNHistoryItemResult]. + * @property startTimetoken Start timetoken of the returned list of messages. + * @property endTimetoken End timetoken of the returned list of messages. + */ +class PNHistoryResult( + val messages: List, + val startTimetoken: Long, + val endTimetoken: Long, +) { + companion object { + const val MAX_COUNT = 100 + } +} + +/** + * Encapsulates a message in terms of a history entry. + * + * @property entry The actual message content. + * @property timetoken Publish timetoken of the message, if requested via [History.includeTimetoken] + * @property meta Metadata of the message, if requested via [History.includeMeta]. + * Is `null` if not requested, otherwise an empty string if requested but no associated metadata. + * @property error The error associated with message retrieval, if any. + * e.g. a message is unencrypted but PubNub instance is configured with the Crypto + * so PubNub can't decrypt the unencrypted message and return the message. + */ +data class PNHistoryItemResult( + val entry: JsonElement, + val timetoken: Long? = null, + val meta: JsonElement? = null, + val error: PubNubError? = null, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNMessageCountResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNMessageCountResult.kt new file mode 100644 index 000000000..97a20270f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/history/PNMessageCountResult.kt @@ -0,0 +1,11 @@ +package com.pubnub.api.models.consumer.history + +/** + * Result of the MessageCounts operation. + * + * @property channels A map with values of Long for each channel. Channels without messages have a count of 0. + * Channels with 10,000 messages or more have a count of `10000`. + */ +class PNMessageCountResult( + val channels: Map, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNAddMessageActionResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNAddMessageActionResult.kt new file mode 100644 index 000000000..c52a89ac0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNAddMessageActionResult.kt @@ -0,0 +1,9 @@ +package com.pubnub.api.models.consumer.message_actions + +/** + * Result for the AddMessageAction API operation. + * + * Essentially a wrapper around [PNMessageAction]. + */ +class PNAddMessageActionResult(action: PNMessageAction) : + PNMessageAction(action) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNGetMessageActionsResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNGetMessageActionsResult.kt new file mode 100644 index 000000000..32eaa4bd9 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNGetMessageActionsResult.kt @@ -0,0 +1,18 @@ +package com.pubnub.api.models.consumer.message_actions + +import com.pubnub.api.models.consumer.PNBoundedPage +import com.pubnub.api.utils.SerializedName + +/** + * Result for the GetMessageActions API operation. + * + * @property actions List of message actions for a certain message in a certain channel. + * @property page Information about next page. When null there's no next page. + */ + +class PNGetMessageActionsResult( + @field:SerializedName("data") + val actions: List, + @field:SerializedName("more") + val page: PNBoundedPage?, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNMessageAction.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNMessageAction.kt new file mode 100644 index 000000000..cf20c9914 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNMessageAction.kt @@ -0,0 +1,91 @@ +package com.pubnub.api.models.consumer.message_actions + +import kotlin.jvm.JvmField + +/** + * Concrete implementation of a message action. + * + * Add or remove actions on published messages to build features like receipts, + * reactions or to associate custom metadata to messages. + * + * Clients can subscribe to a channel to receive message action events on that channel. + * They can also fetch past message actions from PubNub Storage independently or when they fetch original messages. + * + * @property type Message action's type. + * @property value Message action's value. + * @property messageTimetoken Timestamp when the actual message was created the message action belongs to. + */ + +open class PNMessageAction( + @JvmField var type: String, + @JvmField var value: String, + @JvmField var messageTimetoken: Long, +) { + constructor() : this("", "", 0L) + + internal constructor(pnMessageAction: PNMessageAction) : this( + pnMessageAction.type, + pnMessageAction.value, + pnMessageAction.messageTimetoken, + ) { + this.uuid = pnMessageAction.uuid + this.actionTimetoken = pnMessageAction.actionTimetoken + } + + /** + * Message action's author. + */ + @JvmField + var uuid: String? = null + + /** + * Timestamp when the message action was created. + */ + @JvmField + var actionTimetoken: Long? = null + + fun setType(type: String): PNMessageAction { + this.type = type + return this + } + + fun setValue(value: String): PNMessageAction { + this.value = value + return this + } + + fun setMessageTimetoken(messageTimetoken: Long): PNMessageAction { + this.messageTimetoken = messageTimetoken + return this + } + + fun setUuid(uuid: String): PNMessageAction { + this.uuid = uuid + return this + } + + fun setActionTimetoken(actionTimetoken: Long): PNMessageAction { + this.actionTimetoken = actionTimetoken + return this + } + + fun getType(): String { + return this.type + } + + fun getValue(): String { + return this.value + } + + fun getMessageTimetoken(): Long? { + return this.messageTimetoken + } + + fun getUuid(): String? { + return this.uuid + } + + fun getActionTimetoken(): Long? { + return this.actionTimetoken + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNRemoveMessageActionResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNRemoveMessageActionResult.kt new file mode 100644 index 000000000..cfac20433 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNRemoveMessageActionResult.kt @@ -0,0 +1,6 @@ +package com.pubnub.api.models.consumer.message_actions + +/** + * Result for the RemoveMessageAction API operation. + */ +class PNRemoveMessageActionResult diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/PNPage.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/PNPage.kt new file mode 100644 index 000000000..6fee0c911 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/PNPage.kt @@ -0,0 +1,12 @@ +package com.pubnub.api.models.consumer.objects + +sealed class PNPage { + abstract val pageHash: String + + @Deprecated(message = "Use `pageHash` instead.", replaceWith = ReplaceWith("pageHash")) + val hash: String get() = pageHash + + data class PNNext(override val pageHash: String) : PNPage() + + data class PNPrev(override val pageHash: String) : PNPage() +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/channel/PNChannelMetadata.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/channel/PNChannelMetadata.kt new file mode 100644 index 000000000..2f2d1980d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/channel/PNChannelMetadata.kt @@ -0,0 +1,29 @@ +package com.pubnub.api.models.consumer.objects.channel + +import com.pubnub.api.utils.PatchValue + +data class PNChannelMetadata( + val id: String, + val name: PatchValue? = null, + val description: PatchValue? = null, + val custom: PatchValue?>? = null, + val updated: PatchValue? = null, + val eTag: PatchValue? = null, + val type: PatchValue? = null, + val status: PatchValue? = 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, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/channel/PNChannelMetadataArrayResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/channel/PNChannelMetadataArrayResult.kt new file mode 100644 index 000000000..843381d92 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/channel/PNChannelMetadataArrayResult.kt @@ -0,0 +1,11 @@ +package com.pubnub.api.models.consumer.objects.channel + +import com.pubnub.api.models.consumer.objects.PNPage + +data class PNChannelMetadataArrayResult( + val status: Int, + val data: Collection, + val totalCount: Int?, + val next: PNPage.PNNext?, + val prev: PNPage.PNPrev?, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/channel/PNChannelMetadataResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/channel/PNChannelMetadataResult.kt new file mode 100644 index 000000000..771bec6f1 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/channel/PNChannelMetadataResult.kt @@ -0,0 +1,6 @@ +package com.pubnub.api.models.consumer.objects.channel + +data class PNChannelMetadataResult( + val status: Int, + val data: PNChannelMetadata, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/uuid/PNUUIDMetadata.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/uuid/PNUUIDMetadata.kt new file mode 100644 index 000000000..a1379b325 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/objects/uuid/PNUUIDMetadata.kt @@ -0,0 +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: PatchValue? = null, + val externalId: PatchValue? = null, + val profileUrl: PatchValue? = null, + val email: PatchValue? = null, + val custom: PatchValue?>? = null, + val updated: PatchValue? = null, + val eTag: PatchValue? = null, + val type: PatchValue? = null, + val status: PatchValue? = 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, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/presence/PNGetStateResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/presence/PNGetStateResult.kt new file mode 100644 index 000000000..55b51d06b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/presence/PNGetStateResult.kt @@ -0,0 +1,12 @@ +package com.pubnub.api.models.consumer.presence + +import com.pubnub.api.JsonElement + +/** + * Result of the GetPresenceState operation. + * + * @property stateByUUID Map of UUIDs and the user states. + */ +class PNGetStateResult( + val stateByUUID: Map, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/presence/PNHereNow.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/presence/PNHereNow.kt new file mode 100644 index 000000000..30e6a07cb --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/presence/PNHereNow.kt @@ -0,0 +1,41 @@ +package com.pubnub.api.models.consumer.presence + +import com.pubnub.api.JsonElement + +/** + * Result of the HereNow operation. + * + * @property totalChannels Total number channels matching the associated HereNow call. + * @property totalOccupancy Total occupancy matching the associated HereNow call. + * @property channels A map with values of [PNHereNowChannelData] for each channel. + */ +class PNHereNowResult( + val totalChannels: Int, + val totalOccupancy: Int, + // TODO this should be immutable + val channels: MutableMap = mutableMapOf(), +) + +/** + * Wrapper class representing 'here now' data for a given channel. + * + * @property channelName The channel name. + * @property occupancy Total number of UUIDs currently in the channel. + * @property occupants List of [PNHereNowOccupantData] (users) currently in the channel. + */ +class PNHereNowChannelData( + val channelName: String, + val occupancy: Int, + var occupants: List = emptyList(), +) + +/** + * Wrapper class representing a UUID (user) within the means of HereNow calls. + * + * @property uuid UUID of the user if requested via HereNow.includeUUIDs. + * @property state Presence State of the user if requested via HereNow.includeState. + */ +class PNHereNowOccupantData( + val uuid: String, + val state: JsonElement? = null, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/presence/PNSetStateResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/presence/PNSetStateResult.kt new file mode 100644 index 000000000..b4ca42e8b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/presence/PNSetStateResult.kt @@ -0,0 +1,12 @@ +package com.pubnub.api.models.consumer.presence + +import com.pubnub.api.JsonElement + +/** + * Result of the [PubNubImpl.setPresenceState] operation. + * + * @property state The actual state object. + */ +class PNSetStateResult( + val state: JsonElement, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/BasePubSubResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/BasePubSubResult.kt new file mode 100644 index 000000000..543e56c0f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/BasePubSubResult.kt @@ -0,0 +1,26 @@ +package com.pubnub.api.models.consumer.pubsub + +import com.pubnub.api.JsonElement + +interface PubSubResult : PNEvent { + override val channel: String + override val subscription: String? + override val timetoken: Long? + val userMetadata: JsonElement? + val publisher: String? +} + +/** + * @property channel The channel a PubNub API operation is related to. + * @property subscription The subscriptions a PubNub API operation is related to. + * @property timetoken Timetoken of the PubNub API operation in question. + * @property userMetadata User metadata if any. + * @property publisher The publisher of the PubNub API operation in question. + */ +data class BasePubSubResult( + override val channel: String, + override val subscription: String?, + override val timetoken: Long?, + override val userMetadata: JsonElement?, + override val publisher: String?, +) : PubSubResult diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/MessageResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/MessageResult.kt new file mode 100644 index 000000000..1bb614d2b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/MessageResult.kt @@ -0,0 +1,10 @@ +package com.pubnub.api.models.consumer.pubsub + +import com.pubnub.api.JsonElement + +/** + * @property message The actual message content + */ +interface MessageResult : PubSubResult { + val message: JsonElement +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNEvent.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNEvent.kt new file mode 100644 index 000000000..ee09db5fc --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNEvent.kt @@ -0,0 +1,7 @@ +package com.pubnub.api.models.consumer.pubsub + +interface PNEvent { + val channel: String + val subscription: String? + val timetoken: Long? +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNMessageResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNMessageResult.kt new file mode 100644 index 000000000..3a42c0c2f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNMessageResult.kt @@ -0,0 +1,46 @@ +package com.pubnub.api.models.consumer.pubsub + +import com.pubnub.api.JsonElement +import com.pubnub.api.PubNubError + +/** + * Wrapper around an actual message. + */ +class PNMessageResult( + private val basePubSubResult: PubSubResult, + override val message: JsonElement, + val error: PubNubError? = null, +) : MessageResult, PubSubResult by basePubSubResult { + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other == null) { + return false + } + if (this::class != other::class) { + return false + } + + other as PNMessageResult + + if (basePubSubResult != other.basePubSubResult) { + return false + } + if (message != other.message) { + return false + } + if (error != other.error) { + return false + } + + return true + } + + override fun hashCode(): Int { + var result = basePubSubResult.hashCode() + result = 31 * result + message.hashCode() + result = 31 * result + (error?.hashCode() ?: 0) + return result + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNPresenceEventResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNPresenceEventResult.kt new file mode 100644 index 000000000..fd0d7ea63 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNPresenceEventResult.kt @@ -0,0 +1,113 @@ +package com.pubnub.api.models.consumer.pubsub + +import com.pubnub.api.JsonElement + +/** + * Wrapper around a presence event. + * + * @property event The presence event. Could be `join`, `leave`, `state-change` or `interval`. + * @property uuid The UUID which the presence event is related to. + * @property timestamp The timestamp of the event. + * @property occupancy Total number of users currently present in the `channel` in question. + * @property state Presence state of the related UUID, if any. + * @property channel The channel which the `event` is performed on. + * @property subscription The related subscriptions. + * @property timetoken The timetoken of the event. + * @property join List of users that have *joined* the `channel` if the `event` is an `interval`. + * This needs to be enabled under **presence_deltas** at the Admin Dashboard. + * @property leave List of users that have *left* the `channel` if the `event` is an `interval`. + * This needs to be enabled under **presence_deltas** at the Admin Dashboard. + * @property timeout List of users that have *timed out* of the `channel` if the `event` is an `interval`. + * This needs to be enabled under **presence_deltas** at the Admin Dashboard. + * @property hereNowRefresh Indicates to the user that a manual HereNow should be called to get + * the complete list of users present in the channel. + * @property userMetadata User metadata if any. + */ +class PNPresenceEventResult( + val event: String? = null, + val uuid: String? = null, + val timestamp: Long? = null, + val occupancy: Int? = null, + val state: JsonElement? = null, + override val channel: String, + override val subscription: String? = null, + override val timetoken: Long? = null, + val join: List? = null, + val leave: List? = null, + val timeout: List? = null, + val hereNowRefresh: Boolean? = null, + val userMetadata: Any? = null, +) : PNEvent { + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other == null) { + return false + } + if (this::class != other::class) { + return false + } + + other as PNPresenceEventResult + + if (event != other.event) { + return false + } + if (uuid != other.uuid) { + return false + } + if (timestamp != other.timestamp) { + return false + } + if (occupancy != other.occupancy) { + return false + } + if (state != other.state) { + return false + } + if (channel != other.channel) { + return false + } + if (subscription != other.subscription) { + return false + } + if (timetoken != other.timetoken) { + return false + } + if (join != other.join) { + return false + } + if (leave != other.leave) { + return false + } + if (timeout != other.timeout) { + return false + } + if (hereNowRefresh != other.hereNowRefresh) { + return false + } + if (userMetadata != other.userMetadata) { + return false + } + + return true + } + + override fun hashCode(): Int { + var result = event?.hashCode() ?: 0 + result = 31 * result + (uuid?.hashCode() ?: 0) + result = 31 * result + (timestamp?.hashCode() ?: 0) + result = 31 * result + (occupancy ?: 0) + result = 31 * result + (state?.hashCode() ?: 0) + result = 31 * result + channel.hashCode() + result = 31 * result + (subscription?.hashCode() ?: 0) + result = 31 * result + (timetoken?.hashCode() ?: 0) + result = 31 * result + (join?.hashCode() ?: 0) + result = 31 * result + (leave?.hashCode() ?: 0) + result = 31 * result + (timeout?.hashCode() ?: 0) + result = 31 * result + (hereNowRefresh?.hashCode() ?: 0) + result = 31 * result + (userMetadata?.hashCode() ?: 0) + return result + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNSignalResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNSignalResult.kt new file mode 100644 index 000000000..90362ad42 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/PNSignalResult.kt @@ -0,0 +1,11 @@ +package com.pubnub.api.models.consumer.pubsub + +import com.pubnub.api.JsonElement + +/** + * Wrapper around a received signal. + */ +data class PNSignalResult( + private val basePubSubResult: PubSubResult, + override val message: JsonElement, +) : MessageResult, PubSubResult by basePubSubResult diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/files/PNFileEventResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/files/PNFileEventResult.kt new file mode 100644 index 000000000..0af2c12a7 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/files/PNFileEventResult.kt @@ -0,0 +1,71 @@ +package com.pubnub.api.models.consumer.pubsub.files + +import com.pubnub.api.JsonElement +import com.pubnub.api.PubNubError +import com.pubnub.api.models.consumer.files.PNDownloadableFile +import com.pubnub.api.models.consumer.pubsub.PNEvent + +class PNFileEventResult( + override val channel: String, + // timetoken in every other event model is nullable + override val timetoken: Long?, + val publisher: String?, + val message: Any?, + val file: PNDownloadableFile, + val jsonMessage: JsonElement, + override val subscription: String? = null, + val error: PubNubError? = null, +) : PNEvent { + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other == null) { + return false + } + if (this::class != other::class) { + return false + } + + other as PNFileEventResult + + if (channel != other.channel) { + return false + } + if (timetoken != other.timetoken) { + return false + } + if (publisher != other.publisher) { + return false + } + if (message != other.message) { + return false + } + if (file != other.file) { + return false + } + if (jsonMessage != other.jsonMessage) { + return false + } + if (subscription != other.subscription) { + return false + } + if (error != other.error) { + return false + } + + return true + } + + override fun hashCode(): Int { + var result = channel.hashCode() + result = 31 * result + (timetoken?.hashCode() ?: 0) + result = 31 * result + (publisher?.hashCode() ?: 0) + result = 31 * result + (message?.hashCode() ?: 0) + result = 31 * result + file.hashCode() + result = 31 * result + jsonMessage.hashCode() + result = 31 * result + (subscription?.hashCode() ?: 0) + result = 31 * result + (error?.hashCode() ?: 0) + return result + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/message_actions/PNMessageActionResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/message_actions/PNMessageActionResult.kt new file mode 100644 index 000000000..bc3c7618d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/message_actions/PNMessageActionResult.kt @@ -0,0 +1,57 @@ +package com.pubnub.api.models.consumer.pubsub.message_actions + +import com.pubnub.api.models.consumer.message_actions.PNMessageAction +import com.pubnub.api.models.consumer.pubsub.BasePubSubResult +import com.pubnub.api.models.consumer.pubsub.PubSubResult +import com.pubnub.api.models.consumer.pubsub.objects.ObjectResult + +/** + * Wrapper around message actions received in [SubscribeCallback.messageAction]. + * + * @property event The message action event. Could be `added` or `removed`. + * @property data The actual message action. + */ +class PNMessageActionResult( + private val result: BasePubSubResult, + override val event: String, + override val data: PNMessageAction, +) : ObjectResult, PubSubResult by result { + val messageAction: PNMessageAction = data + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other == null) { + return false + } + if (this::class != other::class) { + return false + } + + other as PNMessageActionResult + + if (result != other.result) { + return false + } + if (event != other.event) { + return false + } + if (data != other.data) { + return false + } + if (messageAction != other.messageAction) { + return false + } + + return true + } + + override fun hashCode(): Int { + var result1 = result.hashCode() + result1 = 31 * result1 + event.hashCode() + result1 = 31 * result1 + data.hashCode() + result1 = 31 * result1 + messageAction.hashCode() + return result1 + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/objects/ObjectPayload.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/objects/ObjectPayload.kt new file mode 100644 index 000000000..d5dd426fb --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/objects/ObjectPayload.kt @@ -0,0 +1,11 @@ +package com.pubnub.api.models.consumer.pubsub.objects + +import com.pubnub.api.JsonElement + +data class ObjectPayload( + val source: String, + val version: String, + val event: String, + val type: String, + val data: JsonElement, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/objects/ObjectResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/objects/ObjectResult.kt new file mode 100644 index 000000000..31e9a6d57 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/pubsub/objects/ObjectResult.kt @@ -0,0 +1,6 @@ +package com.pubnub.api.models.consumer.pubsub.objects + +interface ObjectResult { + val event: String + val data: T +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushAddChannelResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushAddChannelResult.kt new file mode 100644 index 000000000..2f38d62e8 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushAddChannelResult.kt @@ -0,0 +1,6 @@ +package com.pubnub.api.models.consumer.push + +/** + * Result of [PubNubImpl.addPushNotificationsOnChannels] operation. + */ +class PNPushAddChannelResult diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushListProvisionsResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushListProvisionsResult.kt new file mode 100644 index 000000000..c55eed826 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushListProvisionsResult.kt @@ -0,0 +1,5 @@ +package com.pubnub.api.models.consumer.push + +class PNPushListProvisionsResult( + val channels: List, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushRemoveAllChannelsResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushRemoveAllChannelsResult.kt new file mode 100644 index 000000000..ec973954d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushRemoveAllChannelsResult.kt @@ -0,0 +1,3 @@ +package com.pubnub.api.models.consumer.push + +class PNPushRemoveAllChannelsResult diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushRemoveChannelResult.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushRemoveChannelResult.kt new file mode 100644 index 000000000..9eae9ab2e --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/PNPushRemoveChannelResult.kt @@ -0,0 +1,3 @@ +package com.pubnub.api.models.consumer.push + +class PNPushRemoveChannelResult diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/payload/PushPayloadHelper.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/payload/PushPayloadHelper.kt new file mode 100644 index 000000000..21a7d9a64 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/payload/PushPayloadHelper.kt @@ -0,0 +1,465 @@ +package com.pubnub.api.models.consumer.push.payload + +import com.pubnub.api.enums.PNPushEnvironment + +class PushPayloadHelper { + var commonPayload: Map? = null + + @Deprecated( + replaceWith = ReplaceWith("fcmPayloadV2"), + message = "The legacy GCM/FCM payload is deprecated and will" + + "be removed in the next major release. Use `fcmPayloadV2` with the `FCMPayloadV2` message body instead." + ) + var fcmPayload: FCMPayload? = null + var fcmPayloadV2: FCMPayloadV2? = null + var mpnsPayload: MPNSPayload? = null + var apnsPayload: APNSPayload? = null + + fun build(): Map { + return mutableMapOf().apply { + apnsPayload?.let { + it.toMap().run { + if (isNotEmpty()) { + put("pn_apns", this) + } + } + } + fcmPayload?.let { + it.toMap().run { + if (isNotEmpty()) { + put("pn_gcm", this) + } + } + } + fcmPayloadV2?.let { + it.toMap().run { + if (isNotEmpty()) { + put("pn_fcm", this) + } + } + } + mpnsPayload?.let { + it.toMap().run { + if (isNotEmpty()) { + put("pn_mpns", this) + } + } + } + commonPayload?.let { putAll(it) } + } + } + + class APNSPayload : PushPayloadSerializer { + var aps: APS? = null + var apns2Configurations: List? = null + var custom: Map? = null + + override fun toMap(): Map { + return mutableMapOf().apply { + aps?.let { + it.toMap().run { + if (this.isNotEmpty()) { + put("aps", this) + } + } + } + apns2Configurations?.let { put("pn_push", it.map { it.toMap() }.filter { it.isNotEmpty() }.toList()) } + custom?.let { putAll(it) } + } + } + + class APS : PushPayloadSerializer { + var alert: Any? = null + var badge: Int? = null + var sound: String? = null + + override fun toMap(): Map { + return mutableMapOf().apply { + alert?.let { put("alert", it) } + badge?.let { put("badge", it) } + sound?.let { put("sound", it) } + } + } + } + + class APNS2Configuration : PushPayloadSerializer { + var collapseId: String? = null + var expiration: String? = null + var targets: List? = null + var version: String? = null + var authMethod: APNS2AuthMethod? = null + + enum class APNS2AuthMethod { + TOKEN, + CERT, + CERTIFICATE; + + override fun toString(): String { + return name.lowercase() + } + } + + override fun toMap(): Map { + return mutableMapOf().apply { + collapseId?.let { put("collapse_id", it) } + expiration?.let { put("expiration", it) } + targets?.let { + it.map { it.toMap() }.filter { it.isNotEmpty() }.toList().run { + if (this.isNotEmpty()) { + put("targets", this) + } + } + } + version?.let { put("version", it) } + authMethod?.let { put("auth_method", it.toString()) } + } + } + + class Target : PushPayloadSerializer { + var topic: String? = null + var excludeDevices: List? = null + var environment: PNPushEnvironment? = null + + override fun toMap(): Map { + return mutableMapOf().apply { + topic?.let { put("topic", it) } + excludeDevices?.let { put("excluded_devices", it) } + environment?.let { put("environment", it.name.lowercase()) } + } + } + } + } + } + + class MPNSPayload : PushPayloadSerializer { + var count: Int? = null + var backTitle: String? = null + var title: String? = null + var backContent: String? = null + var type: String? = null + var custom: Map? = null + + override fun toMap(): Map { + return mutableMapOf().apply { + count?.let { put("count", it) } + backTitle?.let { put("back_title", it) } + title?.let { put("title", it) } + backContent?.let { put("back_content", it) } + type?.let { put("type", it) } + custom?.let { putAll(it) } + } + } + } + + class FCMPayload : PushPayloadSerializer { + var custom: Map? = null + var data: Map? = null + var notification: Notification? = null + + override fun toMap(): Map { + return mutableMapOf().apply { + custom?.let { putAll(it) } + data?.let { + if (it.isNotEmpty()) { + put("data", it) + } + } + notification?.let { + it.toMap().run { + if (this.isNotEmpty()) { + put("notification", this) + } + } + } + } + } + + class Notification : PushPayloadSerializer { + var title: String? = null + var body: String? = null + var image: String? = null + + override fun toMap(): Map { + return mutableMapOf().apply { + title?.let { put("title", it) } + body?.let { put("body", it) } + image?.let { put("image", it) } + } + } + } + } + + class FCMPayloadV2 : PushPayloadSerializer { + var data: Map? = null + var notification: Notification? = null + var android: AndroidConfig? = null + var webpush: WebpushConfig? = null + var apns: ApnsConfig? = null + var fcmOptions: FcmOptions? = null + + override fun toMap(): Map { + return mutableMapOf().apply { + data?.let { + if (it.isNotEmpty()) { + put("data", it) + } + } + notification?.let { + it.toMap().let { map -> + if (map.isNotEmpty()) { + put("notification", map) + } + } + } + android?.let { + it.toMap().let { map -> + if (map.isNotEmpty()) { + put("android", map) + } + } + } + webpush?.let { + it.toMap().let { map -> + if (map.isNotEmpty()) { + put("webpush", map) + } + } + } + apns?.let { + it.toMap().let { map -> + if (map.isNotEmpty()) { + put("apns", map) + } + } + } + fcmOptions?.let { + it.toMap().let { map -> + if (map.isNotEmpty()) { + put("fcm_options", map) + } + } + } + } + } + + class WebpushConfig : PushPayloadSerializer { + var headers: Map? = null + var data: Map? = null + var notification: Map? = null + var fcmOptions: WebpushFcmOptions? = null + + class WebpushFcmOptions : PushPayloadSerializer { + var link: String? = null + var analyticsLabel: String? = null + + override fun toMap(): Map { + return buildMap { + link?.let { put("link", it) } + analyticsLabel?.let { put("analytics_label", it) } + } + } + } + + override fun toMap(): Map { + return buildMap { + headers?.let { put("headers", it) } + data?.let { put("data", it) } + notification?.let { put("notification", it) } + fcmOptions?.let { put("fcm_options", it.toMap()) } + } + } + } + + class ApnsConfig : PushPayloadSerializer { + var headers: Map? = null + var payload: Map? = null + var fcmOptions: ApnsFcmOptions? = null + + class ApnsFcmOptions : PushPayloadSerializer { + var analyticsLabel: String? = null + var image: String? = null + + override fun toMap(): Map { + return buildMap { + analyticsLabel?.let { put("analytics_label", it) } + image?.let { put("image", it) } + } + } + } + + override fun toMap(): Map { + return buildMap { + headers?.let { put("headers", it) } + payload?.let { put("payload", it) } + fcmOptions?.let { put("fcm_options", it.toMap()) } + } + } + } + + class FcmOptions : PushPayloadSerializer { + var analyticsLabel: String? = null + + override fun toMap(): Map { + return buildMap { + analyticsLabel?.let { put("analytics_label", it) } + } + } + } + + class AndroidConfig : PushPayloadSerializer { + var collapseKey: String? = null + var priority: AndroidMessagePriority = AndroidMessagePriority.NORMAL + var ttl: String? = null + var restrictedPackageName: String? = null + var data: Map? = null + var notification: AndroidNotification? = null + var fcmOptions: AndroidFcmOptions? = null + var directBootOk: Boolean = false + + class AndroidFcmOptions : PushPayloadSerializer { + var analyticsLabel: String? = null + + override fun toMap(): Map { + return buildMap { + analyticsLabel?.let { put("analytics_label", it) } + } + } + } + + class AndroidNotification : PushPayloadSerializer { + var title: String? = null + var body: String? = null + var icon: String? = null + var color: String? = null + var sound: String? = null + var tag: String? = null + var clickAction: String? = null + var bodyLocKey: String? = null + var bodyLocArgs: List? = null + var titleLocKey: String? = null + var titleLocArgs: List? = null + var channelId: String? = null + var ticker: String? = null + var sticky: Boolean = false + var eventTime: String? = null + var localOnly: Boolean? = null + var notificationPriority: NotificationPriority = NotificationPriority.PRIORITY_DEFAULT + var defaultSound: Boolean? = null + var defaultVibrateTimings: Boolean? = null + var defaultLightSettings: Boolean? = null + var vibrateTimings: List? = null + var visibility: Visibility? = null + var notificationCount: Int? = null + var lightSettings: LightSettings? = null + var image: String? = null + + class LightSettings : PushPayloadSerializer { + var color: Color? = null + var lightOnDuration: String? = null + var lightOffDuration: String? = null + + class Color(val red: Float, val green: Float, val blue: Float, val alpha: Float) : PushPayloadSerializer { + override fun toMap(): Map { + return buildMap { + put("red", red) + put("green", green) + put("blue", blue) + put("alpha", alpha) + } + } + } + + override fun toMap(): Map { + return buildMap { + color?.let { put("color", it.toMap()) } + lightOnDuration?.let { put("light_on_duration", it) } + lightOffDuration?.let { put("light_off_duration", it) } + } + } + } + + enum class Visibility { + PRIVATE, + PUBLIC, + SECRET; + + override fun toString(): String { + return name.lowercase() + } + } + + enum class NotificationPriority { + PRIORITY_MIN, + PRIORITY_LOW, + PRIORITY_DEFAULT, + PRIORITY_HIGH, + PRIORITY_MAX + } + + override fun toMap(): Map = buildMap { + title?.let { put("title", it) } + body?.let { put("body", it) } + icon?.let { put("icon", it) } + color?.let { put("color", it) } + sound?.let { put("sound", it) } + tag?.let { put("tag", it) } + clickAction?.let { put("click_action", it) } + bodyLocKey?.let { put("body_loc_key", it) } + bodyLocArgs?.let { put("body_loc_args", it) } + titleLocKey?.let { put("title_loc_key", it) } + titleLocArgs?.let { put("title_loc_args", it) } + channelId?.let { put("channel_id", it) } + ticker?.let { put("ticker", it) } + put("sticky", sticky) + eventTime?.let { put("event_time", it) } + localOnly?.let { put("local_only", it) } + put("notification_priority", notificationPriority.name) + defaultSound?.let { put("default_sound", it) } + defaultVibrateTimings?.let { put("default_vibrate_timings", it) } + defaultLightSettings?.let { put("default_light_settings", it) } + vibrateTimings?.let { put("vibrate_timings", it) } + visibility?.let { put("visibility", it.name) } + notificationCount?.let { put("notification_count", it) } + lightSettings?.let { put("light_settings", it.toMap()) } + image?.let { put("image", it) } + } + } + + enum class AndroidMessagePriority { + NORMAL, + HIGH; + + override fun toString(): String { + return name.lowercase() + } + } + + override fun toMap(): Map { + return buildMap { + collapseKey?.let { put("collapse_key", it) } + put("priority", priority.name) + ttl?.let { put("ttl", it) } + restrictedPackageName?.let { put("restricted_package_name", it) } + data?.let { put("data", it) } + notification?.let { put("notification", it.toMap()) } + fcmOptions?.let { put("fcm_options", it.toMap()) } + put("direct_boot_ok", directBootOk) + } + } + } + + class Notification : PushPayloadSerializer { + var title: String? = null + var body: String? = null + var image: String? = null + + override fun toMap(): Map { + return mutableMapOf().apply { + title?.let { put("title", it) } + body?.let { put("body", it) } + image?.let { put("image", it) } + } + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/payload/PushPayloadSerializer.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/payload/PushPayloadSerializer.kt new file mode 100644 index 000000000..213198d31 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/models/consumer/push/payload/PushPayloadSerializer.kt @@ -0,0 +1,5 @@ +package com.pubnub.api.models.consumer.push.payload + +interface PushPayloadSerializer { + fun toMap(): Map +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/retry/RetryConfiguration.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/retry/RetryConfiguration.kt new file mode 100644 index 000000000..a1d1c3621 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/retry/RetryConfiguration.kt @@ -0,0 +1,147 @@ +package com.pubnub.api.retry + +// import org.jetbrains.annotations.TestOnly TODO +// import org.slf4j.LoggerFactory TODO +import kotlin.jvm.JvmSynthetic +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds + +private const val MIN_DELAY = 2 +private const val MAX_DELAY = 150 + +/** + * This sealed class represents the various retry policies for a request. + */ +sealed class RetryConfiguration { + /** + * None represents no retry policy in a network request + */ + object None : RetryConfiguration() + + /** + * This data class represents a linear retry policy for network requests with a delay between retries, + * a maximum number of retries and a list of operations to exclude from retries. + * + * @property delayInSec The delay in seconds between retries. Minimum value is 3 seconds. + * @property maxRetryNumber The maximum number of retries allowed. Maximum value is 10. + * @property excludedOperations List of [RetryableEndpointGroup] to be excluded from retry. + */ + class Linear private constructor( + var delayInSec: Duration = MIN_DELAY.seconds, + var maxRetryNumber: Int = MAX_RETRIES, + val excludedOperations: List = emptyList(), + isInternal: Boolean = false, + ) : RetryConfiguration() { +// private val log = LoggerFactory.getLogger(this.javaClass.simpleName + "-" + "RetryConfiguration") + + constructor( + delayInSec: Int = MIN_DELAY, + maxRetryNumber: Int = MAX_RETRIES, + excludedOperations: List = emptyList(), + ) : this(delayInSec.seconds, maxRetryNumber, excludedOperations, false) + + // additional constructors for java + constructor() : this(MIN_DELAY, MAX_RETRIES, emptyList()) + constructor(delayInSec: Int) : this(delayInSec, MAX_RETRIES, emptyList()) + constructor(delayInSec: Int, maxRetryNumber: Int) : this(delayInSec, maxRetryNumber, emptyList()) + + init { + if (!isInternal) { + if (delayInSec < MIN_DELAY.seconds) { +// log.trace("Provided delay is less than $MIN_DELAY, setting it to $MIN_DELAY") + delayInSec = MIN_DELAY.seconds + } + if (maxRetryNumber > MAX_RETRIES) { +// log.trace("Provided maxRetryNumber is greater than $MAX_RETRIES, setting it to $MAX_RETRIES") + maxRetryNumber = MAX_RETRIES + } + } + } + + companion object { + @JvmSynthetic +// @TestOnly + internal fun createForTest( + delayInSec: Duration = MIN_DELAY.seconds, + maxRetryNumber: Int = MAX_RETRIES, + excludedOperations: List = emptyList(), + isInternal: Boolean = false, + ): Linear = Linear(delayInSec, maxRetryNumber, excludedOperations, isInternal) + + const val MAX_RETRIES = 10 + } + } + + /** + * This class represents an exponential retry policy with a minimum and + * maximum delay between retries, a maximum number of retries, and a list of + * operations to exclude from retry attempts. + * + * @property minDelayInSec The minimum delay in seconds between retries. Minimum value is 3 seconds. + * @property maxDelayInSec The maximum delay in seconds between retries. + * @property maxRetryNumber The maximum number of retries allowed. Maximum value is 10. + * @property excludedOperations List of [RetryableEndpointGroup] to be excluded from retry. + */ + class Exponential private constructor( + var minDelayInSec: Duration = MIN_DELAY.seconds, + var maxDelayInSec: Duration = MAX_DELAY.seconds, + var maxRetryNumber: Int = MAX_RETRIES, + val excludedOperations: List = emptyList(), + isInternal: Boolean = false, + ) : RetryConfiguration() { +// private val log = LoggerFactory.getLogger(this.javaClass.simpleName + "-" + "RetryConfiguration") + + constructor( + minDelayInSec: Int = MIN_DELAY, + maxDelayInSec: Int = MAX_DELAY, + maxRetryNumber: Int = MAX_RETRIES, + excludedOperations: List = emptyList(), + ) : this(minDelayInSec.seconds, maxDelayInSec.seconds, maxRetryNumber, excludedOperations, false) + + // additional constructors for java + constructor() : this(MIN_DELAY, MAX_DELAY, MAX_RETRIES, emptyList()) + constructor(minDelayInSec: Int, maxDelayInSec: Int) : this( + minDelayInSec, + maxDelayInSec, + MAX_RETRIES, + emptyList(), + ) + + constructor(minDelayInSec: Int, maxDelayInSec: Int, maxRetryNumber: Int) : this( + minDelayInSec, + maxDelayInSec, + maxRetryNumber, + emptyList(), + ) + + init { + if (!isInternal) { + val originalMinDelayInSec = minDelayInSec + val originalMaxDelayInSec = maxDelayInSec + val originalMaxRetryNumber = maxRetryNumber + + minDelayInSec = minDelayInSec.coerceIn(MIN_DELAY.seconds, MAX_DELAY.seconds) + maxDelayInSec = maxDelayInSec.coerceAtLeast(minDelayInSec).coerceAtMost(MAX_DELAY.seconds) + maxRetryNumber = maxRetryNumber.coerceAtMost(MAX_RETRIES) + + if (minDelayInSec != originalMinDelayInSec || maxDelayInSec != originalMaxDelayInSec || maxRetryNumber != originalMaxRetryNumber) { +// log.trace("Adjusted values: minDelayInSec=$minDelayInSec, maxDelayInSec=$maxDelayInSec, maxRetryNumber=$maxRetryNumber") + } + } + } + + companion object { + @JvmSynthetic +// @TestOnly + internal fun createForTest( + minDelayInSec: Duration = MIN_DELAY.seconds, + maxDelayInSec: Duration = MAX_DELAY.seconds, + maxRetryNumber: Int = MAX_RETRIES, + excludedOperations: List = emptyList(), + isInternal: Boolean = false, + ): Exponential = Exponential(minDelayInSec, maxDelayInSec, maxRetryNumber, excludedOperations, isInternal) + + const val MAX_RETRIES = 6 + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/retry/RetryableEndpointGroup.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/retry/RetryableEndpointGroup.kt new file mode 100644 index 000000000..178197c04 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/retry/RetryableEndpointGroup.kt @@ -0,0 +1,67 @@ +package com.pubnub.api.retry + +/** + * Enum representing various retryable endpoint groups. + * Each enum constant denotes a specific category of operations + * that can be retried under certain conditions. + */ +enum class RetryableEndpointGroup { + /** + * Represents operation related to subscribing like [PubNub.subscribe] + */ + SUBSCRIBE, + + /** + * Represents the group of operations related to publishing like [PubNub.publish], [PubNub.publishFileMessage], [PubNub.signal], [PubNub.fire] + */ + PUBLISH, + + /** + * Represents the group of operations related to presence like [PubNub.getPresenceState], [PubNub.setPresenceState], + * [PubNub.hereNow], [PubNub.whereNow], [PubNub.presence] (Heartbeat), and Leave. + */ + PRESENCE, + + /** + * Represents the group of operations related to file persistence like [PubNub.getFileUrl], [PubNub.deleteFile], + * [PubNub.listFiles], [PubNub.downloadFile], and [PubNub.sendFile]. + */ + FILE_PERSISTENCE, + + /** + * Represents the group of operations related to message persistence like [PubNub.fetchMessages], + * [PubNub.deleteMessages], [PubNub.messageCounts], and [PubNub.time]. + */ + MESSAGE_PERSISTENCE, + + /** + * Represents the group of operations on channel group like [PubNub.listAllChannelGroups], [PubNub.deleteChannelGroup], + * [PubNub.removeChannelsFromChannelGroup], [PubNub.listChannelsForChannelGroup], and [PubNub.addChannelsToChannelGroup] + */ + CHANNEL_GROUP, + + /** + * Represents the group of operations related to push notification like [PubNub.removeAllPushNotificationsFromDeviceWithPushToken], + * [PubNub.addPushNotificationsOnChannels], [PubNub.auditPushChannelProvisions] and [PubNub.removePushNotificationsFromChannels] + */ + PUSH_NOTIFICATION, + + /** + * Represents the group of operations related to application context like [PubNub.getAllUUIDMetadata], [PubNub.getUUIDMetadata], + * [PubNub.setUUIDMetadata], [PubNub.removeUUIDMetadata], [PubNub.getAllChannelMetadata], [PubNub.getChannelMetadata], + * [PubNub.removeChannelMetadata], [PubNub.setChannelMetadata], [PubNub.getChannelMembers], [PubNub.manageChannelMembers], + * [PubNub.getMemberships], and [PubNub.manageMemberships] + */ + APP_CONTEXT, + + /** + * Represents the group of operations related to message reaction like [PubNub.addMessageAction], + * [PubNub.getMessageActions] and [PubNub.removeMessageAction] + */ + MESSAGE_REACTION, + + /** + * Represents the group of operations related to access management like [PubNub.grant], [PubNub.grantToken], [PubNub.revokeToken] + */ + ACCESS_MANAGER, +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/utils/PatchValue.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/utils/PatchValue.kt new file mode 100644 index 000000000..c60d7b801 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/utils/PatchValue.kt @@ -0,0 +1,17 @@ +package com.pubnub.api.utils + +/** + * An optional that accepts nullable values. Thus, it can represent two (`PatchValue`) or three (`PatchValue?`) 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 internal constructor(val value: T) { + companion object { + /** + * Create an optional with the specified value (which can be null). + */ + @JvmStatic + fun of(value: T): PatchValue = PatchValue(value) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/utils/SerializedName.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/utils/SerializedName.kt new file mode 100644 index 000000000..9b2657daa --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/utils/SerializedName.kt @@ -0,0 +1,7 @@ +@file:OptIn(ExperimentalMultiplatform::class) + +package com.pubnub.api.utils + +@OptionalExpectation +@Target(AnnotationTarget.FIELD) +expect annotation class SerializedName(val value: String, val alternate: Array = []) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.kt new file mode 100644 index 000000000..1a5a9ef89 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.kt @@ -0,0 +1,5 @@ +package com.pubnub.api.v2.callbacks + +expect fun interface Consumer { + fun accept(p: T) +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/EventEmitter.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/EventEmitter.kt index e77ebdff5..c698a62b9 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/EventEmitter.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/EventEmitter.kt @@ -1,5 +1,6 @@ package com.pubnub.api.v2.callbacks +import com.pubnub.api.callbacks.Listener import com.pubnub.api.models.consumer.pubsub.PNMessageResult import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult import com.pubnub.api.models.consumer.pubsub.PNSignalResult @@ -10,7 +11,26 @@ import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult /** * Interface implemented by objects that are the source of real time events from the PubNub network. */ -interface EventEmitter : BaseEventEmitter { +interface EventEmitter { + /** + * Add a listener. + * + * @param listener The listener to be added. + */ + fun addListener(listener: EventListener) + + /** + * Remove a listener. + * + * @param listener The listener to be removed, previously added with [addListener]. + */ + fun removeListener(listener: Listener) + + /** + * Removes all listeners. + */ + fun removeAllListeners() + /** * A nullable property that can be set to a function (or lambda expression) to handle incoming message events. * This function is invoked whenever a new message is received, providing a convenient way to process or react to messages. @@ -33,6 +53,8 @@ interface EventEmitter : BaseEventEmitter { * ``` */ var onMessage: ((PNMessageResult) -> Unit)? + get() = error("Not supported") + set(value) = error("Not supported") /** * A nullable property designed to set a function or lambda expression for handling incoming presence events. @@ -56,6 +78,8 @@ interface EventEmitter : BaseEventEmitter { * ``` */ var onPresence: ((PNPresenceEventResult) -> Unit)? + get() = error("Not supported") + set(value) = error("Not supported") /** * A nullable property for assigning a function or lambda expression to handle incoming signal events. @@ -79,6 +103,8 @@ interface EventEmitter : BaseEventEmitter { * ``` */ var onSignal: ((PNSignalResult) -> Unit)? + get() = error("Not supported") + set(value) = error("Not supported") /** * A nullable property that allows setting a function or lambda to react to message action events. @@ -102,6 +128,8 @@ interface EventEmitter : BaseEventEmitter { * ``` */ var onMessageAction: ((PNMessageActionResult) -> Unit)? + get() = error("Not supported") + set(value) = error("Not supported") /** * A nullable property for assigning a function or lambda to handle object events. @@ -125,6 +153,8 @@ interface EventEmitter : BaseEventEmitter { * ``` */ var onObjects: ((PNObjectEventResult) -> Unit)? + get() = error("Not supported") + set(value) = error("Not supported") /** * A nullable property to set a function or lambda for responding to file events. @@ -148,4 +178,6 @@ interface EventEmitter : BaseEventEmitter { * ``` */ var onFile: ((PNFileEventResult) -> Unit)? + get() = error("Not supported") + set(value) = error("Not supported") } diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/EventListener.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/EventListener.kt index d751791b5..9f419ffa9 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/EventListener.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/EventListener.kt @@ -1,7 +1,9 @@ package com.pubnub.api.v2.callbacks +import com.pubnub.api.callbacks.Listener + /** * Implement this interface and pass it into [EventEmitter.addListener] to listen for events from the PubNub real-time * network. */ -expect interface EventListener : BaseEventListener +expect interface EventListener : Listener diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/Result.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/Result.kt new file mode 100644 index 000000000..dda0799c0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/callbacks/Result.kt @@ -0,0 +1,252 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license. + */ +@file:JvmName("Results") + +package com.pubnub.api.v2.callbacks + +import com.pubnub.api.PubNubException +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlin.jvm.JvmField +import kotlin.jvm.JvmName +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic + +/** + * A discriminated union that encapsulates a successful outcome with a value of type [T] + * or a failure with a [PubNubException]. + */ +class Result + @PublishedApi + internal constructor( + @PublishedApi + @get:JvmSynthetic + internal val value: Any?, + ) { + // discovery + + /** + * Returns `true` if this instance represents a successful outcome. + * In this case [isFailure] returns `false`. + */ + public val isSuccess: Boolean get() = value !is Failure + + /** + * Returns `true` if this instance represents a failed outcome. + * In this case [isSuccess] returns `false`. + */ + public val isFailure: Boolean get() = value is Failure + + // value & exception retrieval + + /** + * Returns the encapsulated value if this instance represents [success][Result.isSuccess] or `null` + * if it is [failure][Result.isFailure]. + * + * This function is a shorthand for `getOrElse { null }` (see [getOrElse]) or + * `fold(onSuccess = { it }, onFailure = { null })` (see [fold]). + */ + @Suppress("UNCHECKED_CAST") + public inline fun getOrNull(): T? = + when { + isFailure -> null + else -> value as T + } + + /** + * Returns the encapsulated [PubNubException] exception if this instance represents [failure][isFailure] or `null` + * if it is [success][isSuccess]. + * + * This function is a shorthand for `fold(onSuccess = { null }, onFailure = { it })` (see [fold]). + */ + public fun exceptionOrNull(): PubNubException? = + when (value) { + is Failure -> value.exception + else -> null + } + + /** + * Returns a string `Success(v)` if this instance represents [success][Result.isSuccess] + * where `v` is a string representation of the value or a string `Failure(x)` if + * it is [failure][isFailure] where `x` is a string representation of the exception. + */ + public override fun toString(): String = + when (value) { + is Failure -> value.toString() // "Failure($exception)" + else -> "Success($value)" + } + + public inline fun onFailure(action: Consumer): Result { + exceptionOrNull()?.let { action.accept(it) } + return this + } + + @Suppress("UNCHECKED_CAST") + public inline fun onSuccess(action: Consumer): Result { + if (isSuccess) { + action.accept(value as T) + } + return this + } + + // companion with constructors + + /** + * Companion object for [Result] class that contains its constructor functions + * [success] and [failure]. + */ + public companion object { + /** + * Returns an instance that encapsulates the given [value] as successful value. + */ + @JvmStatic + public fun success(value: T): Result = Result(value) + + /** + * Returns an instance that encapsulates the given [PubNubException] [exception] as failure. + */ + @JvmStatic + public fun failure(exception: PubNubException): Result = Result(createFailure(exception)) + + @JvmStatic + public fun failure(exception: Throwable): Result = Result(createFailure(PubNubException.from(exception))) + } + + internal class Failure( + @JvmField + val exception: PubNubException, + ) { + override fun equals(other: Any?): Boolean = other is Failure && exception == other.exception + + override fun hashCode(): Int = exception.hashCode() + + override fun toString(): String = "Failure($exception)" + } + } + +/** + * Creates an instance of internal marker [Result.Failure] class to + * make sure that this class is not exposed in ABI. + */ +private fun createFailure(exception: PubNubException): Any = Result.Failure(exception) + +/** + * Throws exception if the result is failure. This internal function minimizes + * inlined bytecode for [getOrThrow] and makes sure that in the future we can + * add some exception-augmenting logic here (if needed). + */ +private fun Result<*>.throwOnFailure() { + if (value is Result.Failure) { + throw value.exception + } +} + +// -- extensions --- + +/** + * Returns the encapsulated value if this instance represents [success][Result.isSuccess] or throws the encapsulated [PubNubException] + * if it is [failure][Result.isFailure]. + * + * This function is a shorthand for `getOrElse { throw it }` (see [getOrElse]). + */ +public fun Result.getOrThrow(): T { + throwOnFailure() + return value as T +} + +/** + * Returns the encapsulated value if this instance represents [success][Result.isSuccess] or the + * result of [onFailure] function for the encapsulated [PubNubException] exception if it is [failure][Result.isFailure]. + * + * Note, that this function rethrows any [Throwable] exception thrown by [onFailure] function. + * + * This function is a shorthand for `fold(onSuccess = { it }, onFailure = onFailure)` (see [fold]). + */ +@OptIn(ExperimentalContracts::class) +public inline fun Result.getOrElse(onFailure: (exception: PubNubException) -> R): R { + contract { + callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE) + } + return when (val exception = exceptionOrNull()) { + null -> value as T + else -> onFailure(exception) + } +} + +/** + * Returns the encapsulated value if this instance represents [success][Result.isSuccess] or the + * [defaultValue] if it is [failure][Result.isFailure]. + * + * This function is a shorthand for `getOrElse { defaultValue }` (see [getOrElse]). + */ +public inline fun Result.getOrDefault(defaultValue: R): R { + if (isFailure) { + return defaultValue + } + return value as T +} + +/** + * Returns the result of [onSuccess] for the encapsulated value if this instance represents [success][Result.isSuccess] + * or the result of [onFailure] function for the encapsulated [Throwable] exception if it is [failure][Result.isFailure]. + * + * Note, that this function rethrows any [Throwable] exception thrown by [onSuccess] or by [onFailure] function. + */ +public inline fun Result.fold( + onSuccess: (value: T) -> R, + onFailure: (exception: Throwable) -> R +): R { +// contract { +// callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE) +// callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE) +// } + return when (val exception = exceptionOrNull()) { + null -> onSuccess(value as T) + else -> onFailure(exception) + } +} + +// transformation + +/** + * Returns the encapsulated result of the given [transform] function applied to the encapsulated value + * if this instance represents [success][Result.isSuccess] or the + * original encapsulated [Throwable] exception if it is [failure][Result.isFailure]. + * + * Note, that this function rethrows any [Throwable] exception thrown by [transform] function. + * See [mapCatching] for an alternative that encapsulates exceptions. + */ +public inline fun Result.map(transform: (value: T) -> R): Result { +// contract { +// callsInPlace(transform, InvocationKind.AT_MOST_ONCE) +// } + return when { + isSuccess -> Result.success(transform(value as T)) + else -> Result(value) + } +} + +public inline fun Result.mapCatching(transform: (value: T) -> R): Result { + return when { + isSuccess -> runCatching { transform(value as T) } + else -> Result(value) + } +} + +public inline fun T.runCatching(block: T.() -> R): Result { + return try { + Result.success(block()) + } catch (e: Throwable) { + Result.failure(e) + } +} + +public inline fun Result.wrapException(block: (PubNubException) -> PubNubException): Result { + return when { + isSuccess -> this + else -> Result.failure(block(exceptionOrNull()!!)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/Channel.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/Channel.kt index 5b5ad4df1..82762f387 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/Channel.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/Channel.kt @@ -4,8 +4,8 @@ import com.pubnub.api.endpoints.files.DeleteFile import com.pubnub.api.endpoints.files.SendFile import com.pubnub.api.endpoints.pubsub.Publish import com.pubnub.api.endpoints.pubsub.Signal -import com.pubnub.api.v2.callbacks.EventListener import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.SubscriptionOptions import com.pubnub.kmp.Uploadable /** @@ -15,7 +15,32 @@ import com.pubnub.kmp.Uploadable * * Use the [com.pubnub.api.PubNub.channel] factory method to create instances of this interface. */ -interface Channel : BaseChannel { +interface Channel : Subscribable { + /** + * The name of this channel. Supports wildcards by ending it with ".*" + * + * See more in the [documentation](https://www.pubnub.com/docs/general/channels/overview) + */ + val name: String + + /** + * Returns a [Subscription] that can be used to subscribe to this channel. + * + * Channel subscriptions support passing [com.pubnub.api.v2.subscriptions.SubscriptionOptions.receivePresenceEvents] in + * [options] to enable receiving presence events. + * + * [com.pubnub.api.v2.subscriptions.SubscriptionOptions.filter] can be used to filter events delivered to the subscription. + * + * For example, to create a subscription that only listens to presence events: + * ``` + * channel.subscription(SubscriptionOptions.receivePresenceEvents() + SubscriptionOptions.filter { it is PNPresenceEventResult } ) + * ``` + * + * @param options optional [SubscriptionOptions]. + * @return an inactive [Subscription] to this channel. You must call [Subscription.subscribe] to start receiving events. + */ + override fun subscription(options: SubscriptionOptions): Subscription + /** * Send a message to all subscribers of the channel. * diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/ChannelGroup.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/ChannelGroup.kt index 2bd90ac29..e0c355039 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/ChannelGroup.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/ChannelGroup.kt @@ -1,7 +1,7 @@ package com.pubnub.api.v2.entities -import com.pubnub.api.v2.callbacks.EventListener import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.SubscriptionOptions /** * A representation of a PubNub channel group identified by its [name]. @@ -10,4 +10,40 @@ import com.pubnub.api.v2.subscriptions.Subscription * * Use the [com.pubnub.api.PubNub.channelGroup] factory method to create instances of this interface. */ -interface ChannelGroup : BaseChannelGroup +interface ChannelGroup : Subscribable { + /** + * The name of this channel group. + * + * See more in the [documentation](https://www.pubnub.com/docs/general/channels/subscribe#channel-groups) + */ + val name: String + + /** + * Returns a [Subscription] that can be used to subscribe to this channel group. + * + * Channel group subscriptions support passing [com.pubnub.api.v2.subscriptions.SubscriptionOptions.receivePresenceEvents] + * in [options] to enable receiving presence events. + * + * [com.pubnub.api.v2.subscriptions.SubscriptionOptions.filter] can be used to filter events delivered to the subscription. + * + * For example, to create a subscription that only listens to presence events: + * ``` + * channelGroup.subscription(SubscriptionOptions.receivePresenceEvents() + SubscriptionOptions.filter { it is PNPresenceEventResult } ) + * ``` + * + * *Warning:* if a channel is part of more than one channel group, and you create subscription to both (or more) + * those groups using a single [com.pubnub.api.PubNub] instance, you will only receive events for that channel in + * one channel group subscription. + * + * For example, let's say "channel_1" is part of groups "cg_1" and "cg_2". If you only subscribe to "cg_1", + * or you only subscribe to "cg_2", you will get all events for "channel_1". However, if in your app you subscribe + * to both "cg_1" and "cg_2" at the same time, you will only receive events for "channel_1" in one of those + * subscriptions, chosen at random. + * + * This limitation is due to how the server manages channels and channel groups. + * + * @param options optional [SubscriptionOptions]. + * @return an inactive [Subscription] to this channel group. You must call [Subscription.subscribe] to start receiving events. + */ + override fun subscription(options: SubscriptionOptions): Subscription +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/ChannelMetadata.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/ChannelMetadata.kt index 5bb2b819e..cc89bb3e3 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/ChannelMetadata.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/ChannelMetadata.kt @@ -1,7 +1,7 @@ package com.pubnub.api.v2.entities -import com.pubnub.api.v2.callbacks.EventListener import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.SubscriptionOptions /** * A representation of a PubNub entity for tracking channel metadata changes. @@ -10,4 +10,21 @@ import com.pubnub.api.v2.subscriptions.Subscription * * Use the [com.pubnub.api.PubNub.channelMetadata] factory method to create instances of this interface. */ -interface ChannelMetadata : BaseChannelMetadata +interface ChannelMetadata : Subscribable { + /** + * The id for this channel metadata object. + * + * See more in the [documentation](https://www.pubnub.com/docs/general/metadata/channel-metadata) + */ + val id: String + + /** + * Returns a [Subscription] that can be used to subscribe to this channel metadata. + * + * [com.pubnub.api.v2.subscriptions.SubscriptionOptions.filter] can be used to filter events delivered to the subscription. + * + * @param options optional [SubscriptionOptions]. + * @return an inactive [Subscription] to this channel metadata. You must call [Subscription.subscribe] to start receiving events. + */ + override fun subscription(options: SubscriptionOptions): Subscription +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/Subscribable.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/Subscribable.kt new file mode 100644 index 000000000..a869d8b82 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/Subscribable.kt @@ -0,0 +1,19 @@ +package com.pubnub.api.v2.entities + +import com.pubnub.api.v2.subscriptions.EmptyOptions +import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.SubscriptionOptions + +/** + * This interface is implemented by entities that can be subscribed to, such as channels, channel groups, and user and + * channel metadata. + */ +interface Subscribable { + /** + * Returns a [com.pubnub.api.v2.subscriptions.Subscription] that can be used to subscribe to this `Subscribable`. + * + * @param options optional [SubscriptionOptions]. + * @return an inactive [Subscription] to this `Subscribable`. You must call [Subscription.subscribe] to start receiving events. + */ + fun subscription(options: SubscriptionOptions = EmptyOptions): Subscription +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/UserMetadata.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/UserMetadata.kt index 6f68eefc8..bd71c498c 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/UserMetadata.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/entities/UserMetadata.kt @@ -1,7 +1,7 @@ package com.pubnub.api.v2.entities -import com.pubnub.api.v2.callbacks.EventListener import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.SubscriptionOptions /** * A representation of a PubNub entity for tracking user metadata changes. @@ -10,4 +10,21 @@ import com.pubnub.api.v2.subscriptions.Subscription * * Use the [com.pubnub.api.PubNub.userMetadata] factory method to create instances of this interface. */ -interface UserMetadata : BaseUserMetadata +interface UserMetadata : Subscribable { + /** + * The id for this user metadata object. + * + * See more in the [documentation](https://www.pubnub.com/docs/general/metadata/users-metadata) + */ + val id: String + + /** + * Returns a [Subscription] that can be used to subscribe to this user metadata. + * + * [com.pubnub.api.v2.subscriptions.SubscriptionOptions.filter] can be used to filter events delivered to the subscription. + * + * @param options optional [SubscriptionOptions]. + * @return an inactive [Subscription] to this user metadata. You must call [Subscription.subscribe] to start receiving events. + */ + override fun subscription(options: SubscriptionOptions): Subscription +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscribeCapable.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscribeCapable.kt new file mode 100644 index 000000000..e785b53c5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscribeCapable.kt @@ -0,0 +1,26 @@ +package com.pubnub.api.v2.subscriptions + +interface SubscribeCapable { + /** + * Start receiving events from the subscription (or subscriptions) represented by this object. + * + * The PubNub client will start a network connection to the server if it doesn't have one already, + * or will alter the existing connection to add channels and groups requested by this subscription if needed. + * + * Please note that passing a [cursor] to [subscribe] affects *all* subscriptions that are currently active in the + * [com.pubnub.api.PubNub] client, as it will reset the global timetoken for the server connection. If an active + * subscription had previously delivered *any* events to its listeners, it will only deliver events *newer* that the + * last timetoken it recorded. + * + */ + fun subscribe(cursor: SubscriptionCursor = SubscriptionCursor(0)) + + /** + * Stop receiving events from this subscription. + * + * Please note that if there are any other subscriptions to the same channel or channel group, they will continue to + * receive events until they are also unsubscribed. Only once there are no longer any active subscriptions to a + * given channel/group, the [com.pubnub.api.PubNub] client will remove that channel/group from the connection. + */ + fun unsubscribe() +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/Subscription.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/Subscription.kt index 6b6eb7a6c..bf17cc02b 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/Subscription.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/Subscription.kt @@ -1,7 +1,6 @@ package com.pubnub.api.v2.subscriptions import com.pubnub.api.v2.callbacks.EventEmitter -import com.pubnub.api.v2.callbacks.EventListener /** * Represents a potential subscription to the PubNub real-time network. @@ -15,7 +14,7 @@ import com.pubnub.api.v2.callbacks.EventListener * This class implements the [AutoCloseable] interface to help you release resources by calling [unsubscribe] * and removing all listeners on [close]. Remember to always call [close] when you no longer need this Subscription. */ -interface Subscription : BaseSubscription, EventEmitter { +interface Subscription : EventEmitter, SubscribeCapable, AutoCloseable { /** * Create a [SubscriptionSet] containing this [Subscription] and the added [subscription]. */ diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscriptionCursor.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscriptionCursor.kt new file mode 100644 index 000000000..83ceb0be5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscriptionCursor.kt @@ -0,0 +1,10 @@ +package com.pubnub.api.v2.subscriptions + +/** + * A holder for a timetoken value. + * + * Used with [BaseSubscription.subscribe] to start listening for events newer or equal to the requested timetoken. + */ +class SubscriptionCursor( + val timetoken: Long, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscriptionOptions.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscriptionOptions.kt new file mode 100644 index 000000000..d50f205ee --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscriptionOptions.kt @@ -0,0 +1,61 @@ +package com.pubnub.api.v2.subscriptions + +import com.pubnub.api.models.consumer.pubsub.PNEvent +import kotlin.jvm.JvmStatic + +/** + * SubscriptionOptions is a mechanism used for supplying optional modifiers for subscriptions. + * + * The options currently available in the PubNub client are: + * * [SubscriptionOptions.filter] + * * [SubscriptionOptions.receivePresenceEvents] + */ +open class SubscriptionOptions internal constructor( + optionsSet: Set = emptySet(), +) { + open val allOptions = optionsSet.toSet() + get() = + field.ifEmpty { + setOf(this) + } + + /** + * Combine multiple options, for example: + * + * val options = `SubscriptionOptions.filter { /* some expression*/ } + SubscriptionOptions.receivePresenceEvents()` + */ + open operator fun plus(options: SubscriptionOptions): SubscriptionOptions { + val newOptions = + buildSet { + addAll(allOptions) + addAll(options.allOptions) + } + return SubscriptionOptions(newOptions) + } + + companion object { + /** + * Enable receiving presence events for a given subscriptions to a channel or channel group. + */ + @JvmStatic + fun receivePresenceEvents(): SubscriptionOptions = ReceivePresenceEventsImpl + + /** + * Create a filter for messages delivered to [com.pubnub.api.v2.callbacks.BaseEventListener]. + * Please see [com.pubnub.api.v2.callbacks.BaseEventListener] for available events. + */ + @JvmStatic + fun filter(predicate: (PNEvent) -> Boolean): SubscriptionOptions = FilterImpl(predicate) + } +} + +/** + * A no-op options object. It doesn't modify subscription behavior in any way. + */ +object EmptyOptions : SubscriptionOptions() { + override val allOptions = emptySet() +} + +class FilterImpl internal constructor(val predicate: (PNEvent) -> Boolean) : SubscriptionOptions() + +object ReceivePresenceEventsImpl : SubscriptionOptions() diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscriptionSet.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscriptionSet.kt index 7b2f0c09d..4e75907be 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscriptionSet.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/api/v2/subscriptions/SubscriptionSet.kt @@ -1,7 +1,6 @@ package com.pubnub.api.v2.subscriptions import com.pubnub.api.v2.callbacks.EventEmitter -import com.pubnub.api.v2.callbacks.EventListener /** * A helper class that manages multiple [Subscription]s that can be added to it. @@ -15,7 +14,32 @@ import com.pubnub.api.v2.callbacks.EventListener * Remember to always [close] the set when you're done using it to avoid memory leaks. * Closing the set also closes all `Subscription`s that are part of this set. */ -interface SubscriptionSet : BaseSubscriptionSet, EventEmitter { +interface SubscriptionSet : EventEmitter, SubscribeCapable, AutoCloseable { + /** + * Add a [Subscription] to this set. + * + * Please note that this SubscriptionSet will *not* attempt to ensure all subscriptions match their + * active/inactive state. That is, if you previously called [subscribe] or [unsubscribe] on this set, it will not be + * called on the newly added [subscription] automatically. + * + * @param subscription the [Subscription] to add. + */ + fun add(subscription: Subscription) + + /** + * Remove the [subscription] from this set. + * + * Please note that removing a subscription from the set does not automatically [unsubscribe] or [close] it. + * + * @param subscription the [Subscription] to remove. + */ + fun remove(subscription: Subscription) + + /** + * Returns an immutable copy of the set of subscriptions contained in this [BaseSubscriptionSet]. + */ + val subscriptions: Set + /** * Add the [subscription] to this SubscriptionSet. Equivalent to calling [add]. * diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/kmp/Downloadable.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/kmp/Downloadable.kt new file mode 100644 index 000000000..04cada04a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/kmp/Downloadable.kt @@ -0,0 +1,3 @@ +package com.pubnub.kmp + +expect abstract class Downloadable diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/kmp/PNFuture.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/kmp/PNFuture.kt new file mode 100644 index 000000000..60dd9824c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/kmp/PNFuture.kt @@ -0,0 +1,8 @@ +package com.pubnub.kmp + +import com.pubnub.api.v2.callbacks.Consumer +import com.pubnub.api.v2.callbacks.Result + +fun interface PNFuture { + fun async(callback: Consumer>) +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/kmp/futures.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/kmp/futures.kt new file mode 100644 index 000000000..e0eb0ba1b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonMain/kotlin/com/pubnub/kmp/futures.kt @@ -0,0 +1,149 @@ +@file:JvmName("PNFutures") + +package com.pubnub.kmp + +import com.pubnub.api.PubNubException +import com.pubnub.api.v2.callbacks.Consumer +import com.pubnub.api.v2.callbacks.Result +import com.pubnub.api.v2.callbacks.mapCatching +import kotlinx.atomicfu.atomic +import kotlinx.atomicfu.locks.ReentrantLock +import kotlinx.atomicfu.locks.reentrantLock +import kotlinx.atomicfu.locks.withLock +import kotlin.jvm.JvmName + +private class CompletablePNFuture : PNFuture { + private val reentrantLock: ReentrantLock = reentrantLock() + private lateinit var result: Result + private var callback: Consumer>? = null + + fun complete(result: Result) { + reentrantLock.withLock { + if (!this::result.isInitialized) { + this.result = result + callback?.accept(result) + } else { + error("This CompletablePNFuture has already completed with result $result") + } + } + } + + override fun async(callback: Consumer>) { + reentrantLock.withLock { + if (this.callback != null) { + error("Only one callback is supported for CompletablePNFuture.") + } + if (!this::result.isInitialized) { + this.callback = callback + } else { + callback.accept(result) + } + } + } +} + +fun PubNubException.asFuture(): PNFuture = PNFuture { callback -> + callback.accept(Result.failure(this@asFuture)) +} + +fun T.asFuture(): PNFuture = PNFuture { callback -> + callback.accept(Result.success(this@asFuture)) +} + +fun Result.asFuture(): PNFuture = PNFuture { callback -> + callback.accept(this@asFuture) +} + +fun PNFuture.then(action: (T) -> U): PNFuture = PNFuture { callback -> + this@then.async { it: Result -> + callback.accept(it.mapCatching(action)) + } +} + +fun PNFuture.thenAsync(action: (T) -> PNFuture): PNFuture = PNFuture { callback -> + this@thenAsync.async { firstFutureResult: Result -> + val intermediateResult: Result> = firstFutureResult.mapCatching(action) + intermediateResult.onFailure { + callback.accept(Result.failure(it)) + }.onSuccess { secondFuture: PNFuture -> + secondFuture.async(callback) + } + } +} + +fun PNFuture.remember(): PNFuture = CompletablePNFuture().also { completableFuture -> + this@remember.async { + completableFuture.complete(it) + } +} + +/** + * Execute a second PNFuture after this PNFuture completes successfully, + * and return the *original* value of this PNFuture after the second PNFuture completes successfully. + * + * Failures are propagated to the resulting PNFuture. + */ +fun PNFuture.alsoAsync(action: (T) -> PNFuture<*>): PNFuture = + this@alsoAsync.thenAsync { outerResult: T -> + action(outerResult).then { _ -> + outerResult + } + } + +fun PNFuture.catch(action: (Exception) -> Result): PNFuture = PNFuture { callback -> + this@catch.async { result: Result -> + result.onSuccess { + callback.accept(result) + }.onFailure { + try { + callback.accept(action(it)) + } catch (e: Exception) { + callback.accept(Result.failure(e)) + } + } + } +} + +fun Collection>.awaitAll(): PNFuture> = PNFuture { callback -> + if (isEmpty()) { + callback.accept(Result.success(emptyList())) + return@PNFuture + } + val counter = atomic(0) + val failed = atomic(false) + val array = Array(size) { null } + forEachIndexed { index, future -> + future.async { res -> + res.onSuccess { value -> + array[index] = value + val counterIncremented = counter.incrementAndGet() + if (counterIncremented == size) { + callback.accept(Result.success(array.toList() as List)) + } + }.onFailure { exception -> + val failedWasSetPreviously = failed.getAndSet(true) + if (!failedWasSetPreviously) { + callback.accept(Result.failure(exception)) + } + } + } + } +} + +@Suppress("UNCHECKED_CAST") +fun awaitAll( + future1: PNFuture, + future2: PNFuture +): PNFuture> = listOf(future1 as PNFuture, future2 as PNFuture).awaitAll().then { it: List -> + Pair(it[0] as T, it[1] as U) +} + +@Suppress("UNCHECKED_CAST") +fun awaitAll( + future1: PNFuture, + future2: PNFuture, + future3: PNFuture +): PNFuture> = listOf(future1 as PNFuture, future2 as PNFuture, future3 as PNFuture).awaitAll().then { + it: List -> + Triple(it[0] as T, it[1] as U, it[2] as X) +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonTest/kotlin/com/pubnub/kmp/JsonElementTest.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonTest/kotlin/com/pubnub/kmp/JsonElementTest.kt new file mode 100644 index 000000000..e63308b60 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonTest/kotlin/com/pubnub/kmp/JsonElementTest.kt @@ -0,0 +1,17 @@ +package com.pubnub.kmp + +import com.pubnub.api.asList +import com.pubnub.api.asString +import com.pubnub.api.createJsonElement +import kotlin.test.Test +import kotlin.test.assertEquals + +class JsonElementTest { + @Test + fun asList() { + val list = listOf("abc", "def") + val jsonList = createJsonElement(list) + + assertEquals(list, jsonList.asList()?.map { it.asString() }) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/commonTest/kotlin/com/pubnub/kmp/PNFutureTest.kt b/pubnub-kotlin/pubnub-kotlin-api/src/commonTest/kotlin/com/pubnub/kmp/PNFutureTest.kt new file mode 100644 index 000000000..881743c82 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/commonTest/kotlin/com/pubnub/kmp/PNFutureTest.kt @@ -0,0 +1,63 @@ +package com.pubnub.kmp + +import com.pubnub.api.v2.callbacks.Result +import kotlin.test.Test +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class PNFutureTest { + @Test + fun asFuture1() { + val value = true + var set = false + value.asFuture().async { result -> + assertTrue { result.isSuccess } + result.onSuccess { value -> + set = value + } + } + assertEquals(value, set) + } + + @Test + fun then() { + val value = true + var set: String? = null + value.asFuture().then { it.toString() }.async { result -> + assertTrue { result.isSuccess } + result.onSuccess { value -> + set = value + } + } + assertEquals(value.toString(), set) + } + + @Test + fun thenAsync() { + val value = true + var set: String? = null + value.asFuture().thenAsync { it.toString().asFuture() }.async { result -> + assertTrue { result.isSuccess } + result.onSuccess { value -> + set = value + } + } + assertEquals(value.toString(), set) + } + + @Test + fun awaitAll() { + val value1 = true + val value2 = 1 + val value3: Any? = null + var set: List? = null + listOf>(value1.asFuture(), value2.asFuture(), value3.asFuture()).awaitAll().async { result: Result> -> + assertTrue { result.isSuccess } + result.onSuccess { + set = it + } + } + assertContentEquals(listOf(value1, value2, value3), set) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/JsonElement.ios.kt b/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/JsonElement.ios.kt new file mode 100644 index 000000000..c4695a845 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/JsonElement.ios.kt @@ -0,0 +1,126 @@ +@file:OptIn(ExperimentalForeignApi::class) + +package com.pubnub.api + +import cocoapods.PubNubSwift.AnyJSONObjC +import kotlinx.cinterop.ExperimentalForeignApi + +actual abstract class JsonElement(val value: Any?) { + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other !is JsonElement) { + return false + } + + if (asMap() != null && other.asMap() != null) { + return asMap() == other.asMap() + } else if (asList() != null && other.asList() != null) { + return asList() == other.asList() + } else if (asString() != null && other.asString() != null) { + return asString() == other.asString() + } else if (asNumber() != null && other.asNumber() != null) { + return asNumber() == other.asNumber() + } + if (value != other.value) { + return false + } + + return true + } + + override fun hashCode(): Int { + return value?.hashCode() ?: 0 + } + + override fun toString(): String { + return "JSONElement($value)" + } +} + +class JsonElementImpl(value: Any?) : JsonElement(value) + +actual fun JsonElement.asString(): String? { + return when (value) { + is AnyJSONObjC -> value.asString() + is String -> value + else -> null + } +} + +actual fun JsonElement.asMap(): Map? { + return when (value) { + is AnyJSONObjC -> (value.asMap() as? Map)?.mapValues { + JsonElementImpl(it.value) + } + is Map<*, *> -> (value as Map)?.mapValues { JsonElementImpl(it.value) } + else -> null + } +} + +actual fun JsonElement.isNull(): Boolean { + return value == null || (value as? AnyJSONObjC)?.isNull() == true +} + +actual fun JsonElement.asList(): List? { + return when (value) { + is AnyJSONObjC -> value.asList()?.map { + JsonElementImpl(it) + } + is List<*> -> value.map { JsonElementImpl(it) } + else -> null + } +} + +actual fun JsonElement.asLong(): Long? { + return when (value) { + is AnyJSONObjC -> value.asInt()?.longValue + is Long -> value + is Int -> value.toLong() + is Boolean -> if (value) { + 1 + } else { + 0 + } + else -> null + } +} + +actual fun JsonElement.asBoolean(): Boolean? { + return when (value) { + is AnyJSONObjC -> value.asBool()?.boolValue + is Boolean -> value + else -> null + } +} + +actual fun JsonElement.asDouble(): Double? { + return when (value) { + is AnyJSONObjC -> value.asDouble()?.doubleValue + is Number -> value.toDouble() + is Boolean -> if (value) { + 1.0 + } else { + 0.0 + } + else -> null + } +} + +actual fun JsonElement.asNumber(): Number? { + return when (value) { + is AnyJSONObjC -> value.asNumber() as? Number + is Number -> value + is Boolean -> if (value) { + 1 + } else { + 0 + } + else -> null + } +} + +actual fun createJsonElement(any: Any?): JsonElement { + return JsonElementImpl(AnyJSONObjC(any)) +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/PubNubException.ios.kt b/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/PubNubException.ios.kt new file mode 100644 index 000000000..36751a415 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/PubNubException.ios.kt @@ -0,0 +1,32 @@ +package com.pubnub.api + +/** + * Custom exception wrapper for errors occurred during execution or processing of a PubNub API operation. + * + * @property errorMessage The error message received from the server, if any. + * @property pubnubError The appropriate matching PubNub error. + * @property jso The error json received from the server, if any. + * @property statusCode HTTP status code. + * @property affectedCall A reference to the affected call. Useful for calling [retry][Endpoint.retry]. + */ +actual class PubNubException( + actual val statusCode: Int = 0, + errorMessage: String?, + cause: Throwable? +) : Exception(errorMessage, cause) { + actual constructor(errorMessage: String?, cause: Throwable?) : this(statusCode = 0, errorMessage, cause) + actual constructor(pubnubError: PubNubError, cause: Throwable?) : this(statusCode = 0, pubnubError.message, cause) + + // test only + actual constructor(errorMessage: String?, statusCode: Int, cause: Throwable?) : this(statusCode, errorMessage, cause) + + actual companion object { + actual fun from(e: Throwable): PubNubException { + return if (e is PubNubException) { + e + } else { + PubNubException(e.message, e) + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNGetMessageActionsResult.ios.kt b/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNGetMessageActionsResult.ios.kt new file mode 100644 index 000000000..f039ced59 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNGetMessageActionsResult.ios.kt @@ -0,0 +1,8 @@ +package com.pubnub.api.models.consumer.message_actions + +import com.pubnub.api.models.consumer.PNBoundedPage + +actual class PNGetMessageActionsResult actual constructor( + actual val actions: List, + actual val page: PNBoundedPage?, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.ios.kt b/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.ios.kt new file mode 100644 index 000000000..69490ac96 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.ios.kt @@ -0,0 +1,5 @@ +package com.pubnub.api.v2.callbacks + +actual fun interface Consumer { + actual fun accept(p: T) +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/kmp/Downloadable.ios.kt b/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/kmp/Downloadable.ios.kt new file mode 100644 index 000000000..20df3d448 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/iosMain/kotlin/com/pubnub/kmp/Downloadable.ios.kt @@ -0,0 +1,9 @@ +package com.pubnub.kmp + +import platform.Foundation.NSInputStream + +actual abstract class Downloadable + +class DownloadableImpl( + private val inputStream: NSInputStream +) : Downloadable() diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/JsonElement.js.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/JsonElement.js.kt new file mode 100644 index 000000000..1b7627d8f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/JsonElement.js.kt @@ -0,0 +1,125 @@ +@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") + +package com.pubnub.api + +import com.pubnub.kmp.JsMap +import com.pubnub.kmp.toMap +import kotlin.js.json + +actual abstract class JsonElement(val value: Any?) { + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other !is JsonElement) { + return false + } + + if (asMap() != null && other.asMap() != null) { + return asMap() == other.asMap() + } else if (asList() != null && other.asList() != null) { + return asList() == other.asList() + } + if (value != other.value) { + return false + } + + return true + } + + override fun hashCode(): Int { + return value?.hashCode() ?: 0 + } +} + +class JsonElementImpl(value: Any?) : JsonElement(value) { + override fun toString(): String { + return "JsonElementImpl($value : ${value?.let { it::class }})" + } +} + +actual fun JsonElement.asString(): String? { + return value as? String +} + +actual fun JsonElement.asMap(): Map? { + if (value?.isJsObject() != true) { + return null + } + return value.unsafeCast>().toMap().mapValues { JsonElementImpl(it.value) } +} + +actual fun JsonElement.isNull(): Boolean { + return value == null +} + +actual fun JsonElement.asList(): List? { + return (value as? Array<*>)?.map { JsonElementImpl(it) } +} + +actual fun JsonElement.asLong(): Long? { + return (value as? Number)?.toLong() ?: (value as? String)?.toLongOrNull() +} + +actual fun JsonElement.asBoolean(): Boolean? { + return value as? Boolean +} + +actual fun JsonElement.asDouble(): Double? { + return (value as? Number)?.toDouble() +} + +actual fun JsonElement.asNumber(): Number? { + return value as? Number +} + +actual fun createJsonElement(any: Any?): JsonElement { + return JsonElementImpl(any.adjustCollectionTypes()) +} + +private fun Any.isJsObject(): Boolean { + return this !is Array<*> && jsTypeOf(this) == "object" +} + +internal fun Any?.adjustCollectionTypes(): Any? { + return when (this) { + is Map<*, *> -> { + val json = json() + entries.forEach { + val value = it.value.adjustCollectionTypes() + if (value is JsonElementImpl) { + json[it.key.toString()] = value.value + } else { + json[it.key.toString()] = value + } + } + json + } + + is Collection<*> -> { + this.map { it.adjustCollectionTypes() }.map { + if (it is JsonElementImpl) { + it.value + } else { + it + } + }.toTypedArray() + } + + is Array<*> -> { + this.map { it.adjustCollectionTypes() }.map { + if (it is JsonElementImpl) { + it.value + } else { + it + } + }.toTypedArray() + } + + is Long -> { + return toString() + } + + else -> this + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/PubNubException.js.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/PubNubException.js.kt new file mode 100644 index 000000000..771f88aa5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/PubNubException.js.kt @@ -0,0 +1,33 @@ +package com.pubnub.api + +/** + * Custom exception wrapper for errors occurred during execution or processing of a PubNub API operation. + * + * @property errorMessage The error message received from the server, if any. + * @property pubnubError The appropriate matching PubNub error. + * @property jso The error json received from the server, if any. + * @property statusCode HTTP status code. + * @property affectedCall A reference to the affected call. Useful for calling [retry][Endpoint.retry]. + */ +actual class PubNubException( + actual val statusCode: Int = 0, + errorMessage: String?, + cause: Throwable? +) : Exception(errorMessage, cause) { + actual constructor(errorMessage: String?, cause: Throwable?) : this(statusCode = 0, errorMessage, cause) + actual constructor(pubnubError: PubNubError, cause: Throwable?) : this(statusCode = 0, pubnubError.message, cause) + + // test only + actual constructor(errorMessage: String?, statusCode: Int, cause: Throwable?) : this(statusCode, errorMessage, cause) + + actual companion object { + actual fun from(e: Throwable): PubNubException { + return if (e is PubNubException) { + e + } else { + val statusCode = (e.asDynamic().status?.statusCode as? Int) ?: 0 + PubNubException(statusCode, e.message, e) + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNGetMessageActionsResult.js.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNGetMessageActionsResult.js.kt new file mode 100644 index 000000000..f039ced59 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/models/consumer/message_actions/PNGetMessageActionsResult.js.kt @@ -0,0 +1,8 @@ +package com.pubnub.api.models.consumer.message_actions + +import com.pubnub.api.models.consumer.PNBoundedPage + +actual class PNGetMessageActionsResult actual constructor( + actual val actions: List, + actual val page: PNBoundedPage?, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.js.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.js.kt new file mode 100644 index 000000000..69490ac96 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.js.kt @@ -0,0 +1,5 @@ +package com.pubnub.api.v2.callbacks + +actual fun interface Consumer { + actual fun accept(p: T) +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/kmp/Downloadable.js.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/kmp/Downloadable.js.kt new file mode 100644 index 000000000..b532a4fc9 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/kmp/Downloadable.js.kt @@ -0,0 +1,5 @@ +package com.pubnub.kmp + +actual abstract class Downloadable(val pubnubFile: Any) + +class DownloadableImpl(pubnubFile: Any) : Downloadable(pubnubFile) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/kmp/JsMap.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/kmp/JsMap.kt new file mode 100644 index 000000000..543f792d8 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jsMain/kotlin/com/pubnub/kmp/JsMap.kt @@ -0,0 +1,19 @@ +package com.pubnub.kmp + +external interface JsMap + +fun entriesOf(jsObject: JsMap): List> = + (js("Object.entries") as (dynamic) -> Array>) + .invoke(jsObject) + .map { entry -> entry[0] as String to entry[1] } + +fun JsMap.toMap(): Map = + entriesOf(this).toMap() + +fun Map.toJsMap(): JsMap = createJsObject { + entries.forEach { + this[it.key] = it.value + } +}.unsafeCast>() + +fun createJsObject(configure: T.() -> Unit = {}): T = (js("({})") as T).apply(configure) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/JsonElement.jvm.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/JsonElement.jvm.kt new file mode 100644 index 000000000..8c75534e6 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/JsonElement.jvm.kt @@ -0,0 +1,69 @@ +package com.pubnub.api + +import com.google.gson.GsonBuilder + +actual typealias JsonElement = com.google.gson.JsonElement + +actual fun JsonElement.asString(): String? { + return if (this.isJsonPrimitive && this.asJsonPrimitive.isString) { + this.asString + } else { + null + } +} + +actual fun JsonElement.asMap(): Map? { + return if (this.isJsonObject) { + this.asJsonObject.asMap() + } else { + null + } +} + +actual fun JsonElement.isNull(): Boolean { + return this.isJsonNull +} + +actual fun JsonElement.asList(): List? { + return if (this.isJsonArray) { + this.asJsonArray.asList() + } else { + null + } +} + +actual fun JsonElement.asLong(): Long? { + return if (this.isJsonPrimitive && this.asJsonPrimitive.isNumber) { + this.asLong + } else { + null + } +} + +actual fun JsonElement.asBoolean(): Boolean? { + return if (this.isJsonPrimitive && this.asJsonPrimitive.isBoolean) { + this.asBoolean + } else { + null + } +} + +actual fun JsonElement.asDouble(): Double? { + return if (this.isJsonPrimitive && this.asJsonPrimitive.isNumber) { + this.asDouble + } else { + null + } +} + +actual fun JsonElement.asNumber(): Number? { + return if (this.isJsonPrimitive && this.asJsonPrimitive.isNumber) { + this.asNumber + } else { + null + } +} + +actual fun createJsonElement(any: Any?): JsonElement { + return GsonBuilder().serializeNulls().create().toJsonTree(any) +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/PNConfiguration.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/PNConfiguration.kt deleted file mode 100644 index 4802be24f..000000000 --- a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/PNConfiguration.kt +++ /dev/null @@ -1,394 +0,0 @@ -package com.pubnub.api - -import com.pubnub.api.crypto.CryptoModule -import com.pubnub.api.enums.PNHeartbeatNotificationOptions -import com.pubnub.api.enums.PNLogVerbosity -import com.pubnub.api.enums.PNReconnectionPolicy -import com.pubnub.api.retry.RetryConfiguration -import com.pubnub.api.v2.PNConfiguration -import com.pubnub.internal.v2.BasePNConfigurationImpl -import com.pubnub.internal.v2.BasePNConfigurationImpl.Companion.MINIMUM_PRESENCE_TIMEOUT -import okhttp3.Authenticator -import okhttp3.CertificatePinner -import okhttp3.logging.HttpLoggingInterceptor -import org.slf4j.LoggerFactory -import java.net.Proxy -import java.net.ProxySelector -import javax.net.ssl.SSLSocketFactory -import javax.net.ssl.X509ExtendedTrustManager - -@Deprecated( - message = "Use `com.pubnub.api.v2.PNConfiguration.builder` instead.", - replaceWith = ReplaceWith("com.pubnub.api.v2.PNConfiguration.builder(userId, subscribeKey)"), -) -class PNConfiguration(override var userId: UserId) : PNConfiguration { - private val configuration = BasePNConfigurationImpl(userId) - private val log = LoggerFactory.getLogger("PNConfiguration") - - @Deprecated( - replaceWith = ReplaceWith( - "PNConfiguration(userId = UserId(uuid))", - "com.pubnub.api.PNConfiguration", - ), - level = DeprecationLevel.WARNING, - message = "Use PNConfiguration(UserId) instead.", - ) - @Throws(PubNubException::class) - constructor(uuid: String) : this(UserId(uuid)) - - @Deprecated( - "Use UserId instead e.g. config.userId.value", - replaceWith = ReplaceWith("userId.value"), - level = DeprecationLevel.WARNING, - ) - override var uuid - get() = userId.value - set(value) { - userId = UserId(value) - } - - /** - * The subscribe key from the admin panel. - */ - override var subscribeKey = "" - - /** - * The publish key from the admin panel (only required if publishing). - */ - override var publishKey = configuration.publishKey - - /** - * The secret key from the admin panel (only required for modifying/revealing access permissions). - * - * Keep away from Android. - */ - override var secretKey = configuration.secretKey - - /** - * If Access Manager is utilized, client will use this authKey in all restricted requests. - */ - override var authKey = configuration.authKey - - /** - * If set, all communications to and from PubNub will be encrypted. - */ - @Deprecated( - """Instead of cipherKey and useRandomInitializationVector use CryptoModule instead - e.g. config.cryptoModule = CryptoModule.createLegacyCryptoModule(cipherKey = cipherKey, randomIv = true) - or config.cryptoModule = CryptoModule.createAesCbcCryptoModule(cipherKey = cipherKey, randomIv = true)""", - level = DeprecationLevel.WARNING, - ) - var cipherKey: String? = null - - /** - * Should initialization vector for encrypted messages be random. - * - * Defaults to `true`. - */ - @Deprecated( - """Instead of cipherKey and useRandomInitializationVector use CryptoModule instead - e.g. config.cryptoModule = CryptoModule.createLegacyCryptoModule(cipherKey = cipherKey, randomIv = true) - or config.cryptoModule = CryptoModule.createAesCbcCryptoModule(cipherKey = cipherKey, randomIv = true)""", - level = DeprecationLevel.WARNING, - ) - var useRandomInitializationVector = true - - /** - * CryptoModule is responsible for handling encryption and decryption. - * If set, all communications to and from PubNub will be encrypted. - */ - override var cryptoModule: CryptoModule? = configuration.cryptoModule - get() = field ?: cipherKey?.let { cipherKey -> - if (cipherKey.isNotBlank()) { - log.warn("cipherKey is deprecated. Use CryptoModule instead") - field = - CryptoModule.createLegacyCryptoModule(cipherKey = cipherKey, randomIv = useRandomInitializationVector) - field - } else { - null - } - } - - /** - * Custom origin if needed. - * - * Defaults to `ps.pndsn.com` - */ - override var origin = configuration.origin - - /** - * If set to `true`, requests will be made over HTTPS. - * - * Deafults to `true`. - */ - override var secure = configuration.secure - - /** - * Set to [PNLogVerbosity.BODY] to enable logging of network traffic, otherwise se to [PNLogVerbosity.NONE]. - */ - override var logVerbosity: PNLogVerbosity = configuration.logVerbosity - - /** - * Set Heartbeat notification options. - * - * By default, the SDK alerts on failed heartbeats (equivalent to [PNHeartbeatNotificationOptions.FAILURES]). - */ - override var heartbeatNotificationOptions = configuration.heartbeatNotificationOptions - - /** - * Set to [PNReconnectionPolicy.LINEAR] for automatic reconnects. - * - * Use [PNReconnectionPolicy.NONE] to disable automatic reconnects. - * - * Use [PNReconnectionPolicy.EXPONENTIAL] to set exponential retry interval. - * - * Defaults to [PNReconnectionPolicy.NONE]. - */ - @Deprecated( - """Instead of reconnectionPolicy and maximumReconnectionRetries use retryConfiguration - e.g. config.retryConfiguration = RetryConfiguration.Linear(delayInSec = 3, maxRetryNumber = 5) - or config.retryConfiguration = RetryConfiguration.Exponential(minDelayInSec = 3, maxDelayInSec = 10, maxRetryNumber = 5)""", - level = DeprecationLevel.WARNING, - ) - var reconnectionPolicy: PNReconnectionPolicy = PNReconnectionPolicy.NONE - set(value) { - field = value - calculateRetryConfiguration() - } - - private fun calculateRetryConfiguration() { - retryConfiguration = when (reconnectionPolicy) { - PNReconnectionPolicy.NONE -> RetryConfiguration.None - PNReconnectionPolicy.LINEAR -> RetryConfiguration.Linear( - maxRetryNumber = getMaximumReconnectionRetriesFor(reconnectionPolicy), - ) - - PNReconnectionPolicy.EXPONENTIAL -> RetryConfiguration.Exponential( - maxRetryNumber = getMaximumReconnectionRetriesFor(reconnectionPolicy), - ) - } - } - - private fun getMaximumReconnectionRetriesFor(reconnectionPolicy: PNReconnectionPolicy): Int { - val maxRetryNumber = if (reconnectionPolicy == PNReconnectionPolicy.LINEAR) { - RetryConfiguration.Linear.MAX_RETRIES - } else { - RetryConfiguration.Exponential.MAX_RETRIES - } - return when { - maximumReconnectionRetries <= -1 -> maxRetryNumber - maximumReconnectionRetries > maxRetryNumber -> maxRetryNumber - else -> maximumReconnectionRetries - } - } - - /** - * Sets the custom presence server timeout. - * - * The value is in seconds, and the minimum value is 20 seconds. - * - * Also sets the value of [heartbeatInterval] - */ - override var presenceTimeout = configuration.presenceTimeout - set(value) { - field = - if (value < MINIMUM_PRESENCE_TIMEOUT) { - log.warn("Presence timeout is too low. Defaulting to: $MINIMUM_PRESENCE_TIMEOUT") - MINIMUM_PRESENCE_TIMEOUT - } else { - value - } - heartbeatInterval = (presenceTimeout / 2) - 1 - } - - /** - * How often the client will announce itself to server. - * - * The value is in seconds. - * - * Defaults to 0 (disabled). - */ - override var heartbeatInterval = configuration.heartbeatInterval - - /** - * The subscribe request timeout. - * - * The value is in seconds. - * - * Defaults to 310. - */ - override var subscribeTimeout = configuration.subscribeTimeout - - /** - * How long before the client gives up trying to connect with the server. - * - * The value is in seconds. - * - * Defaults to 5. - */ - override var connectTimeout = configuration.connectTimeout - - @Deprecated( - "This setting relates to *read* timeout and was renamed to `nonSubscribeReadTimeout`", - replaceWith = ReplaceWith("nonSubscribeReadTimeout") - ) - override var nonSubscribeRequestTimeout: Int - get() = nonSubscribeReadTimeout - set(value) { - nonSubscribeReadTimeout = value - } - - override var nonSubscribeReadTimeout: Int = configuration.nonSubscribeReadTimeout - - /** - * If operating behind a misbehaving proxy, allow the client to shuffle the subdomains. - * - * Defaults to `false`. - */ - override var cacheBusting = configuration.cacheBusting - - /** - * When `true` the SDK doesn't send out the leave requests. - * - * Defaults to `false`. - */ - override var suppressLeaveEvents = configuration.suppressLeaveEvents - - /** - * When `true` the SDK will resend the last channel state that was set using [PubNub.setPresenceState] - * for the current [userId] with every automatic heartbeat (if [heartbeatInterval] is greater than 0) - * and initial subscribe connection (also after e.g. loss of network). - * - * Defaults to `true`. - * - * Please note that `maintainPresenceState` doesn't apply to state that was set on channel groups. - * It is recommended to disable this option if you set state for channel groups using [PubNub.setPresenceState], - * otherwise that state may be overwritten by individual channel states. - */ - override var maintainPresenceState = configuration.maintainPresenceState - - /** - * Feature to subscribe with a custom filter expression. - */ - override var filterExpression = configuration.filterExpression - - /** - * Whether to include a instanceId with every request. - * - * Defaults to `false`. - */ - override var includeInstanceIdentifier = configuration.includeInstanceIdentifier - - /** - * Whether to include a requestId with every request. - * - * Defaults to `true`. - */ - override var includeRequestIdentifier = configuration.includeRequestIdentifier - - /** - * Sets how many times to retry to reconnect before giving up. - * Must be used in combination with [reconnectionPolicy]. - * - * The default value is `-1` which means unlimited retries. - */ - @Deprecated( - """Instead of reconnectionPolicy and maximumReconnectionRetries use retryConfiguration - e.g. config.retryConfiguration = RetryConfiguration.Linear(delayInSec = 3, maxRetryNumber = 5) - or config.retryConfiguration = RetryConfiguration.Exponential(minDelayInSec = 3, maxDelayInSec = 10, maxRetryNumber = 5)""", - level = DeprecationLevel.WARNING, - ) - var maximumReconnectionRetries = -1 - set(value) { - field = value - calculateRetryConfiguration() - } - - /** - * @see [okhttp3.Dispatcher.setMaxRequestsPerHost] - */ - override var maximumConnections = configuration.maximumConnections - - /** - * Enable Google App Engine networking. - * - * Defaults to `false`. - */ - override var googleAppEngineNetworking = configuration.googleAppEngineNetworking - - /** - * Instructs the SDK to use a proxy configuration when communicating with PubNub servers. - * - * @see [Proxy] - */ - override var proxy = configuration.proxy - - /** - * @see [ProxySelector] - */ - override var proxySelector = configuration.proxySelector - - /** - * @see [Authenticator] - */ - override var proxyAuthenticator = configuration.proxyAuthenticator - - /** - * @see [CertificatePinner] - */ - override var certificatePinner = configuration.certificatePinner - - /** - * Sets a custom [HttpLoggingInterceptor] for logging network traffic. - * - * @see [HttpLoggingInterceptor] - */ - override var httpLoggingInterceptor = configuration.httpLoggingInterceptor - - /** - * @see [SSLSocketFactory] - */ - override var sslSocketFactory = configuration.sslSocketFactory - - /** - * @see [X509ExtendedTrustManager] - */ - override var x509ExtendedTrustManager = configuration.x509ExtendedTrustManager - - /** - * @see [okhttp3.ConnectionSpec] - */ - override var connectionSpec = configuration.connectionSpec - - /** - * @see [javax.net.ssl.HostnameVerifier] - */ - override var hostnameVerifier = configuration.hostnameVerifier - - /** - * How many times publishing file message should automatically retry before marking the action as failed - * - * Defaults to `5` - */ - override var fileMessagePublishRetryLimit = configuration.fileMessagePublishRetryLimit - - override var dedupOnSubscribe = configuration.dedupOnSubscribe - - override var maximumMessagesCacheSize = configuration.maximumMessagesCacheSize - - override val pnsdkSuffixes: MutableMap = configuration.pnsdkSuffixes.toMutableMap() - - @Suppress("DeprecatedCallableAddReplaceWith") - @Deprecated("To be used by components", level = DeprecationLevel.WARNING) - fun addPnsdkSuffix(vararg nameToSuffixes: Pair) { - @Suppress("DEPRECATION") - addPnsdkSuffix(nameToSuffixes.toMap()) - } - - @Deprecated("To be used by components", level = DeprecationLevel.WARNING) - fun addPnsdkSuffix(nameToSuffixes: Map) = pnsdkSuffixes.putAll(nameToSuffixes) - - override var retryConfiguration = configuration.retryConfiguration - - override var managePresenceListManually: Boolean = configuration.managePresenceListManually -} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/PubNub.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/PubNub.kt index e9a4336bc..673ed8f0d 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/PubNub.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/PubNub.kt @@ -52,6 +52,7 @@ import com.pubnub.api.models.consumer.access_manager.sum.SpacePermissions import com.pubnub.api.models.consumer.access_manager.sum.UserPermissions import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant +import com.pubnub.api.models.consumer.access_manager.v3.PNToken import com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant import com.pubnub.api.models.consumer.history.PNHistoryResult import com.pubnub.api.models.consumer.message_actions.PNMessageAction @@ -64,9 +65,9 @@ import com.pubnub.api.models.consumer.objects.member.MemberInput import com.pubnub.api.models.consumer.objects.member.PNUUIDDetailsLevel import com.pubnub.api.models.consumer.objects.membership.ChannelMembershipInput import com.pubnub.api.models.consumer.objects.membership.PNChannelDetailsLevel +import com.pubnub.api.v2.PNConfiguration import com.pubnub.api.v2.callbacks.EventEmitter -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.callbacks.StatusListener +import com.pubnub.api.v2.callbacks.StatusEmitter import com.pubnub.api.v2.entities.Channel import com.pubnub.api.v2.entities.ChannelGroup import com.pubnub.api.v2.entities.ChannelMetadata @@ -74,48 +75,238 @@ import com.pubnub.api.v2.entities.UserMetadata import com.pubnub.api.v2.subscriptions.Subscription import com.pubnub.api.v2.subscriptions.SubscriptionOptions import com.pubnub.api.v2.subscriptions.SubscriptionSet -import com.pubnub.internal.BasePubNubImpl import com.pubnub.kmp.CustomObject import java.io.InputStream +import java.util.UUID + +actual interface PubNub : StatusEmitter, EventEmitter { + val timestamp: Int + val baseUrl: String + + /** + * The current version of the PubNub SDK. + */ + val version: String + + /** + * Create a handle to a [Channel] that can be used to obtain a [Subscription]. + * + * The function is cheap to call, and the returned object is lightweight, as it doesn't change any client or server + * state. It is therefore permitted to use this method whenever a representation of a channel is required. + * + * The returned [Channel] holds a reference to this [PubNub] instance internally. + * + * @param name the name of the channel to return. Supports wildcards by ending it with ".*". See more in the + * [documentation](https://www.pubnub.com/docs/general/channels/overview) + * + * @return a [Channel] instance representing the channel with the given [name] + */ + actual fun channel(name: String): Channel + + /** + * Create a handle to a [ChannelGroup] that can be used to obtain a [Subscription]. + * + * The function is cheap to call, and the returned object is lightweight, as it doesn't change any client or server + * state. It is therefore permitted to use this method whenever a representation of a channel group is required. + * + * The returned [ChannelGroup] holds a reference to this [PubNub] instance internally. + * + * @param name the name of the channel group to return. See more in the + * [documentation](https://www.pubnub.com/docs/general/channels/subscribe#channel-groups) + * + * @return a [ChannelGroup] instance representing the channel group with the given [name] + */ + actual fun channelGroup(name: String): ChannelGroup + + /** + * Create a handle to a [ChannelMetadata] object that can be used to obtain a [Subscription] to metadata events. + * + * The function is cheap to call, and the returned object is lightweight, as it doesn't change any client or server + * state. It is therefore permitted to use this method whenever a representation of a metadata channel is required. + * + * The returned [ChannelMetadata] holds a reference to this [PubNub] instance internally. + * + * @param id the id of the channel metadata to return. See more in the + * [documentation](https://www.pubnub.com/docs/general/metadata/channel-metadata) + * + * @return a [ChannelMetadata] instance representing the channel metadata with the given [id] + */ + actual fun channelMetadata(id: String): ChannelMetadata + + /** + * Create a handle to a [UserMetadata] object that can be used to obtain a [Subscription] to user metadata events. + * + * The function is cheap to call, and the returned object is lightweight, as it doesn't change any client or server + * state. It is therefore permitted to use this method whenever a representation of a user metadata is required. + * + * The returned [UserMetadata] holds a reference to this [PubNub] instance internally. + * + * @param id the id of the user. See more in the + * [documentation](https://www.pubnub.com/docs/general/metadata/users-metadata) + * + * @return a [UserMetadata] instance representing the channel metadata with the given [id] + */ + actual fun userMetadata(id: String): UserMetadata + + /** + * Create a [SubscriptionSet] from the given [subscriptions]. + * + * @param subscriptions the subscriptions that will be added to the returned [SubscriptionSet] + * @return a [SubscriptionSet] containing all [subscriptions] + */ + actual fun subscriptionSetOf(subscriptions: Set): SubscriptionSet + + /** + * Create a [SubscriptionSet] containing [Subscription] objects for the given sets of [channels] and + * [channelGroups]. + * + * Please note that the subscriptions are not active until you call [SubscriptionSet.subscribe]. + * + * This is a convenience method, and it is equal to calling [PubNub.channel] followed by [Channel.subscription] for + * each channel, then creating a [subscriptionSetOf] using the returned [Subscription] objects (and similarly for + * channel groups). + * + * @param channels the channels to create subscriptions for + * @param channelGroups the channel groups to create subscriptions for + * @param options the [SubscriptionOptions] to pass for each subscription. Refer to supported options in [Channel] and + * [ChannelGroup] documentation. + * @return a [SubscriptionSet] containing subscriptions for the given [channels] and [channelGroups] + */ + actual fun subscriptionSetOf( + channels: Set, + channelGroups: Set, + options: SubscriptionOptions, + ): SubscriptionSet + + /** + * Perform Cryptographic decryption of an input string using cipher key provided by [PNConfiguration.cipherKey]. + * + * @param inputString String to be decrypted. + * + * @return String containing the decryption of `inputString` using `cipherKey`. + * @throws PubNubException throws exception in case of failed decryption. + */ + @Throws(PubNubException::class) + fun decrypt(inputString: String): String + + /** + * Perform Cryptographic decryption of an input string using a cipher key. + * + * @param inputString String to be decrypted. + * @param cipherKey cipher key to be used for decryption. Default is [PNConfiguration.cipherKey] + * + * @return String containing the decryption of `inputString` using `cipherKey`. + * @throws PubNubException throws exception in case of failed decryption. + */ + @Throws(PubNubException::class) + fun decrypt( + inputString: String, + cipherKey: String? = null, + ): String + + /** + * Perform Cryptographic decryption of an input stream using provided cipher key. + * + * @param inputStream InputStream to be encrypted. + * @param cipherKey Cipher key to be used for decryption. + * + * @return InputStream containing the encryption of `inputStream` using `cipherKey`. + * @throws PubNubException Throws exception in case of failed decryption. + */ + @Throws(PubNubException::class) + fun decryptInputStream( + inputStream: InputStream, + cipherKey: String? = null, + ): InputStream + + /** + * Perform Cryptographic encryption of an input string and a cipher key. + * + * @param inputString String to be encrypted. + * @param cipherKey Cipher key to be used for encryption. Default is [PNConfiguration.cipherKey] + * + * @return String containing the encryption of `inputString` using `cipherKey`. + * @throws PubNubException Throws exception in case of failed encryption. + */ + @Throws(PubNubException::class) + fun encrypt( + inputString: String, + cipherKey: String? = null, + ): String + + /** + * Perform Cryptographic encryption of an input stream using provided cipher key. + * + * @param inputStream InputStream to be encrypted. + * @param cipherKey Cipher key to be used for encryption. + * + * @return InputStream containing the encryption of `inputStream` using `cipherKey`. + * @throws PubNubException Throws exception in case of failed encryption. + */ + @Throws(PubNubException::class) + fun encryptInputStream( + inputStream: InputStream, + cipherKey: String? = null, + ): InputStream + + @Throws(PubNubException::class) + actual fun parseToken(token: String): PNToken + + actual fun setToken(token: String?) + + /** + * Force the SDK to try and reach out PubNub. Monitor the results in [SubscribeCallback.status] + * + * @param timetoken optional timetoken to use for the subscriptions on reconnection. + */ + fun reconnect(timetoken: Long = 0L) + + /** + * Cancel any subscribe and heartbeat loops or ongoing re-connections. + * + * Monitor the results in [SubscribeCallback.status] + */ + fun disconnect() + + /** + * Unsubscribe from all channels and all channel groups + */ + actual fun unsubscribeAll() + + /** + * Frees up threads eventually and allows for a clean exit. + */ + actual fun destroy() + + /** + * Same as [destroy] but immediately. + */ + fun forceDestroy() -actual interface PubNub : - BasePubNub, - EventEmitter { companion object { /** * Initialize and return an instance of the PubNub client. * @param configuration the configuration to use * @return the PubNub client */ - fun create(configuration: com.pubnub.api.v2.PNConfiguration): PubNub { - return Class.forName( - "com.pubnub.internal.PubNubImpl", - ).getConstructor(com.pubnub.api.v2.PNConfiguration::class.java).newInstance(configuration) as PubNub - } - - @Deprecated( - message = "Use `create` with the new PNConfiguration.Builder instead", - replaceWith = ReplaceWith("create(userId, subscribeKey, builder)") - ) - fun create( - userId: UserId, - builder: PNConfiguration.() -> Unit, - ): PubNub { + @JvmStatic + fun create(configuration: PNConfiguration): PubNub { return Class.forName( "com.pubnub.internal.PubNubImpl", - ).getConstructor(com.pubnub.api.v2.PNConfiguration::class.java).newInstance(PNConfiguration(userId).apply(builder)) as PubNub + ).getConstructor(PNConfiguration::class.java).newInstance(configuration) as PubNub } + @JvmStatic fun create( userId: UserId, subscribeKey: String, - builder: com.pubnub.api.v2.PNConfiguration.Builder.() -> Unit, + builder: PNConfiguration.Builder.() -> Unit, ): PubNub { return Class.forName( "com.pubnub.internal.PubNubImpl", - ).getConstructor(com.pubnub.api.v2.PNConfiguration::class.java) + ).getConstructor(PNConfiguration::class.java) .newInstance( - com.pubnub.api.v2.PNConfiguration.builder(userId, subscribeKey, builder).build() + PNConfiguration.builder(userId, subscribeKey, builder).build() ) as PubNub } @@ -123,7 +314,8 @@ actual interface PubNub : * Generates random UUID to use. You should set a unique UUID to identify the user or the device * that connects to PubNub. */ - fun generateUUID(): String = BasePubNubImpl.generateUUID() + @JvmStatic + fun generateUUID(): String = "pn-${UUID.randomUUID()}" } /** @@ -131,7 +323,7 @@ actual interface PubNub : * Modifying the values in this configuration is not advised, as it may lead * to undefined behavior. */ - actual val configuration: com.pubnub.api.v2.PNConfiguration + actual val configuration: PNConfiguration /** * Add a legacy listener for both client status and events. @@ -191,7 +383,7 @@ actual interface PubNub : channel: String, message: Any, meta: Any?, - shouldStore: Boolean, + shouldStore: Boolean?, usePost: Boolean, replicate: Boolean, ttl: Int?, @@ -796,33 +988,33 @@ actual interface PubNub : * It's possible to grant permissions to multiple [channelGroups] simultaneously. */ fun grant( - read: Boolean, - write: Boolean, - manage: Boolean, - delete: Boolean, - ttl: Int, - authKeys: List, - channels: List, - channelGroups: List, - uuids: List, + read: Boolean = false, + write: Boolean = false, + manage: Boolean = false, + delete: Boolean = false, + ttl: Int = -1, + authKeys: List = emptyList(), + channels: List = emptyList(), + channelGroups: List = emptyList(), + uuids: List = emptyList(), ): Grant /** * See [grant] */ fun grant( - read: Boolean, - write: Boolean, - manage: Boolean, - delete: Boolean, - get: Boolean, - update: Boolean, - join: Boolean, - ttl: Int, - authKeys: List, - channels: List, - channelGroups: List, - uuids: List, + read: Boolean = false, + write: Boolean = false, + manage: Boolean = false, + delete: Boolean = false, + get: Boolean = false, + update: Boolean = false, + join: Boolean = false, + ttl: Int = -1, + authKeys: List = emptyList(), + channels: List = emptyList(), + channelGroups: List = emptyList(), + uuids: List = emptyList(), ): Grant /** @@ -1380,6 +1572,7 @@ actual interface PubNub : * Default is `false`. * @param includeCustom Include respective additional fields in the response. * @param includeUUIDDetails Include custom fields for UUIDs metadata. + * @param includeType Include the type field for UUID metadata */ fun manageChannelMembers( channel: String, @@ -1392,6 +1585,7 @@ actual interface PubNub : includeCount: Boolean = false, includeCustom: Boolean = false, includeUUIDDetails: PNUUIDDetailsLevel? = null, + includeUUIDType: Boolean = false, ): ManageChannelMembers /** @@ -1566,10 +1760,4 @@ actual interface PubNub : channels: List, channelGroups: List, ) - - actual override fun subscriptionSetOf( - channels: Set, - channelGroups: Set, - options: SubscriptionOptions - ): SubscriptionSet } diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/PubNubException.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/PubNubException.kt new file mode 100644 index 000000000..f082f907f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/PubNubException.kt @@ -0,0 +1,68 @@ +package com.pubnub.api + +import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction +import okhttp3.Request +import retrofit2.Call + +/** + * Custom exception wrapper for errors occurred during execution or processing of a PubNub API operation. + * + * @property errorMessage The error message received from the server, if any. + * @property pubnubError The appropriate matching PubNub error. + * @property jso The error json received from the server, if any. + * @property statusCode HTTP status code. + * @property affectedCall A reference to the affected call. Useful for calling [retry][Endpoint.retry]. + */ +actual data class PubNubException( + val errorMessage: String? = null, + val pubnubError: PubNubError? = null, + val jso: String? = null, + actual val statusCode: Int = 0, + val affectedCall: Call<*>? = null, + val retryAfterHeaderValue: Int? = null, + val affectedChannels: List = emptyList(), + val affectedChannelGroups: List = emptyList(), + override val cause: Throwable? = null, + val requestInfo: RequestInfo? = null, + val remoteAction: ExtendedRemoteAction<*>? = null, +) : Exception(errorMessage, cause) { + data class RequestInfo( + val tlsEnabled: Boolean, + val origin: String, + val uuid: String?, + val authKey: String?, + val clientRequest: Request, + ) + + @JvmOverloads + actual constructor(errorMessage: String?, cause: Throwable?) : this(errorMessage, pubnubError = null, cause = cause) + + @JvmOverloads + actual constructor(pubnubError: PubNubError, cause: Throwable?) : this( + errorMessage = pubnubError.message, + pubnubError = pubnubError, + cause = cause + ) + + constructor(pubnubError: PubNubError, message: String) : this( + errorMessage = message, + pubnubError = pubnubError, + ) + + // test only + actual constructor(errorMessage: String?, statusCode: Int, cause: Throwable?) : this( + statusCode = statusCode, + errorMessage = errorMessage, + cause = cause, + pubnubError = null + ) + + actual companion object { + actual fun from(e: Throwable): PubNubException = + if (e is PubNubException) { + e + } else { + PubNubException(e.message, cause = e) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/CryptoModule.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/CryptoModule.kt new file mode 100644 index 000000000..6fe8a26ff --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/CryptoModule.kt @@ -0,0 +1,77 @@ +package com.pubnub.api.crypto + +import com.pubnub.api.crypto.cryptor.Cryptor +import java.io.InputStream + +interface CryptoModule { + fun encrypt(data: ByteArray): ByteArray + + fun decrypt(encryptedData: ByteArray): ByteArray + + companion object { + @JvmStatic + fun createLegacyCryptoModule( + cipherKey: String, + randomIv: Boolean = true, + ): CryptoModule { + return instantiateCryptoModuleImpl( + primaryCryptor = instantiateLegacyCryptor(cipherKey, randomIv), + cryptorsForDecryptionOnly = listOf(instantiateLegacyCryptor(cipherKey, randomIv), instantiateAesCbcCryptor(cipherKey)), + ) + } + + @JvmStatic + fun createAesCbcCryptoModule( + cipherKey: String, + randomIv: Boolean = true, + ): CryptoModule { + return instantiateCryptoModuleImpl( + primaryCryptor = instantiateAesCbcCryptor(cipherKey), + cryptorsForDecryptionOnly = listOf(instantiateAesCbcCryptor(cipherKey), instantiateLegacyCryptor(cipherKey, randomIv)), + ) + } + + @JvmStatic + fun createNewCryptoModule( + defaultCryptor: Cryptor, + cryptorsForDecryptionOnly: List = listOf(), + ): CryptoModule { + return instantiateCryptoModuleImpl( + primaryCryptor = defaultCryptor, + cryptorsForDecryptionOnly = listOf(defaultCryptor) + cryptorsForDecryptionOnly, + ) + } + } + + fun encryptStream(stream: InputStream): InputStream + + fun decryptStream(encryptedData: InputStream): InputStream +} + +private fun instantiateCryptoModuleImpl( + primaryCryptor: Cryptor, + cryptorsForDecryptionOnly: List, +): CryptoModule { + return Class.forName("com.pubnub.internal.crypto.CryptoModuleImpl").getConstructor(Cryptor::class.java, List::class.java).newInstance( + primaryCryptor, + cryptorsForDecryptionOnly, + ) as CryptoModule +} + +private fun instantiateLegacyCryptor( + cipherKey: String, + randomIv: Boolean = true, +): Cryptor { + return Class.forName( + "com.pubnub.internal.crypto.cryptor.LegacyCryptor", + ).getConstructor(String::class.java, Boolean::class.java).newInstance( + cipherKey, + randomIv, + ) as Cryptor +} + +private fun instantiateAesCbcCryptor(cipherKey: String): Cryptor { + return Class.forName("com.pubnub.internal.crypto.cryptor.AesCbcCryptor").getConstructor(String::class.java).newInstance( + cipherKey, + ) as Cryptor +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/cryptor/Cryptor.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/cryptor/Cryptor.kt new file mode 100644 index 000000000..117417a40 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/cryptor/Cryptor.kt @@ -0,0 +1,17 @@ +package com.pubnub.api.crypto.cryptor + +import com.pubnub.api.crypto.data.EncryptedData +import com.pubnub.api.crypto.data.EncryptedStreamData +import java.io.InputStream + +interface Cryptor { + fun id(): ByteArray // Should return a ByteArray of exactly 4 bytes. + + fun encrypt(data: ByteArray): EncryptedData + + fun decrypt(encryptedData: EncryptedData): ByteArray + + fun encryptStream(stream: InputStream): EncryptedStreamData + + fun decryptStream(encryptedData: EncryptedStreamData): InputStream +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/data/EncryptedData.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/data/EncryptedData.kt new file mode 100644 index 000000000..c4c73871d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/data/EncryptedData.kt @@ -0,0 +1,6 @@ +package com.pubnub.api.crypto.data + +data class EncryptedData( + val metadata: ByteArray? = null, + val data: ByteArray, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/data/EncryptedStreamData.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/data/EncryptedStreamData.kt new file mode 100644 index 000000000..0266d572a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/crypto/data/EncryptedStreamData.kt @@ -0,0 +1,8 @@ +package com.pubnub.api.crypto.data + +import java.io.InputStream + +data class EncryptedStreamData( + val metadata: ByteArray? = null, + val stream: InputStream, +) diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/AddChannelsToPush.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/AddChannelsToPush.kt index dc967b265..23f7393b8 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/AddChannelsToPush.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/AddChannelsToPush.kt @@ -1,9 +1,17 @@ package com.pubnub.api.endpoints.push import com.pubnub.api.Endpoint +import com.pubnub.api.enums.PNPushEnvironment +import com.pubnub.api.enums.PNPushType import com.pubnub.api.models.consumer.push.PNPushAddChannelResult /** * @see [PubNub.addPushNotificationsOnChannels] */ -actual interface AddChannelsToPush : Endpoint +actual interface AddChannelsToPush : Endpoint { + val pushType: PNPushType + val channels: List + val deviceId: String + val topic: String? + val environment: PNPushEnvironment +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/ListPushProvisions.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/ListPushProvisions.kt index 2b02a22ca..e51b38c8d 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/ListPushProvisions.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/ListPushProvisions.kt @@ -1,9 +1,16 @@ package com.pubnub.api.endpoints.push import com.pubnub.api.Endpoint +import com.pubnub.api.enums.PNPushEnvironment +import com.pubnub.api.enums.PNPushType import com.pubnub.api.models.consumer.push.PNPushListProvisionsResult /** * @see [PubNub.auditPushChannelProvisions] */ -actual interface ListPushProvisions : Endpoint +actual interface ListPushProvisions : Endpoint { + val pushType: PNPushType + val deviceId: String + val topic: String? + val environment: PNPushEnvironment +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/RemoveAllPushChannelsForDevice.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/RemoveAllPushChannelsForDevice.kt index 7137ab2d5..1a708c6a6 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/RemoveAllPushChannelsForDevice.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/RemoveAllPushChannelsForDevice.kt @@ -1,9 +1,16 @@ package com.pubnub.api.endpoints.push import com.pubnub.api.Endpoint +import com.pubnub.api.enums.PNPushEnvironment +import com.pubnub.api.enums.PNPushType import com.pubnub.api.models.consumer.push.PNPushRemoveAllChannelsResult /** * @see [PubNub.removeAllPushNotificationsFromDeviceWithPushToken] */ -actual interface RemoveAllPushChannelsForDevice : Endpoint +actual interface RemoveAllPushChannelsForDevice : Endpoint { + val pushType: PNPushType + val deviceId: String + val environment: PNPushEnvironment + val topic: String? +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/RemoveChannelsFromPush.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/RemoveChannelsFromPush.kt index dc50d0cc7..9c8f30a9c 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/RemoveChannelsFromPush.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/push/RemoveChannelsFromPush.kt @@ -1,9 +1,17 @@ package com.pubnub.api.endpoints.push import com.pubnub.api.Endpoint +import com.pubnub.api.enums.PNPushEnvironment +import com.pubnub.api.enums.PNPushType import com.pubnub.api.models.consumer.push.PNPushRemoveChannelResult /** * @see [PubNub.removePushNotificationsFromChannels] */ -actual interface RemoveChannelsFromPush : Endpoint +actual interface RemoveChannelsFromPush : Endpoint { + val pushType: PNPushType + val channels: List + val deviceId: String + val topic: String? + val environment: PNPushEnvironment +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/remoteaction/ComposableRemoteAction.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/remoteaction/ComposableRemoteAction.kt new file mode 100644 index 000000000..563ff1a24 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/remoteaction/ComposableRemoteAction.kt @@ -0,0 +1,100 @@ +package com.pubnub.api.endpoints.remoteaction + +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.v2.callbacks.Result +import java.util.function.Consumer + +class ComposableRemoteAction( + private val remoteAction: ExtendedRemoteAction, + private val createNextRemoteAction: (T) -> ExtendedRemoteAction, + private var checkpoint: Boolean, +) : ExtendedRemoteAction { + private var nextRemoteAction: ExtendedRemoteAction? = null + private var isCancelled = false + + fun then(factory: (U) -> ExtendedRemoteAction): ComposableRemoteAction { + return ComposableRemoteAction(this, factory, false) + } + + @Synchronized + fun checkpoint(): ComposableRemoteAction { + checkpoint = true + return this + } + + @Throws(PubNubException::class) + override fun sync(): U { + return remoteAction.sync().let { result -> + createNextRemoteAction(result).sync() + } + } + + override fun async(callback: Consumer>) { + remoteAction.async { r: Result -> + r.onFailure { + callback.accept(Result.failure(it.copy(remoteAction = this))) + }.onSuccess { + try { + synchronized(this) { + if (!isCancelled) { + val newNextRemoteAction = + createNextRemoteAction(it) // if s is not error r shouldn't be null + nextRemoteAction = newNextRemoteAction + newNextRemoteAction.async { r2: Result -> + r2.onFailure { + callback.accept(Result.failure(it.copy(remoteAction = this))) + }.onSuccess { + callback.accept(Result.success(it)) + } + } + } + } + } catch (ex: PubNubException) { + callback.accept(Result.failure(ex.copy(remoteAction = this))) + } + } + } + } + + @Synchronized + override fun retry() { + if (checkpoint && nextRemoteAction != null) { + nextRemoteAction!!.retry() + } else { + remoteAction.retry() + } + } + + @Synchronized + override fun silentCancel() { + isCancelled = true + remoteAction.silentCancel() + if (nextRemoteAction != null) { + nextRemoteAction!!.silentCancel() + } + } + + override fun operationType(): PNOperationType { + return nextRemoteAction?.operationType() ?: remoteAction.operationType() + } + + class ComposableBuilder(private val remoteAction: ExtendedRemoteAction) { + private var checkpoint = false + + fun then(factory: (T) -> ExtendedRemoteAction): ComposableRemoteAction { + return ComposableRemoteAction(remoteAction, factory, checkpoint) + } + + fun checkpoint(): ComposableBuilder { + checkpoint = true + return this + } + } + + companion object { + fun firstDo(remoteAction: ExtendedRemoteAction): ComposableBuilder { + return ComposableBuilder(remoteAction) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/remoteaction/MappingRemoteAction.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/remoteaction/MappingRemoteAction.kt new file mode 100644 index 000000000..180dd9d25 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/endpoints/remoteaction/MappingRemoteAction.kt @@ -0,0 +1,44 @@ +package com.pubnub.api.endpoints.remoteaction + +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.v2.callbacks.Result +import java.util.function.Consumer + +class MappingRemoteAction(private val remoteAction: ExtendedRemoteAction, private val function: (T) -> U) : + ExtendedRemoteAction { + override fun operationType(): PNOperationType { + return remoteAction.operationType() + } + + override fun retry() { + remoteAction.retry() + } + + override fun sync(): U = function(remoteAction.sync()) + + override fun silentCancel() { + remoteAction.silentCancel() + } + + override fun async(callback: Consumer>) { + remoteAction.async { r -> + r.onSuccess { + val newValue = + try { + function(it) + } catch (e: Throwable) { + callback.accept(Result.failure(PubNubException.from(e))) + return@onSuccess + } + callback.accept(Result.success(newValue)) + }.onFailure { + callback.accept(Result.failure(it.copy(remoteAction = this))) + } + } + } +} + +fun ExtendedRemoteAction.map(function: (T) -> U): ExtendedRemoteAction { + return MappingRemoteAction(this, function) +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/utils/SerializedName.jvm.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/utils/SerializedName.jvm.kt new file mode 100644 index 000000000..35f3301b9 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/utils/SerializedName.jvm.kt @@ -0,0 +1,5 @@ +package com.pubnub.api.utils + +import com.google.gson.annotations.SerializedName + +actual typealias SerializedName = SerializedName diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/PNConfiguration.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/PNConfiguration.kt index 7964a87a0..57cf56087 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/PNConfiguration.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/PNConfiguration.kt @@ -5,7 +5,6 @@ import com.pubnub.api.crypto.CryptoModule import com.pubnub.api.enums.PNHeartbeatNotificationOptions import com.pubnub.api.enums.PNLogVerbosity import com.pubnub.api.retry.RetryConfiguration -import com.pubnub.internal.v2.BasePNConfigurationImpl import okhttp3.Authenticator import okhttp3.CertificatePinner import okhttp3.ConnectionSpec @@ -16,8 +15,278 @@ import javax.net.ssl.HostnameVerifier import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509ExtendedTrustManager -actual interface PNConfiguration : BasePNConfiguration { +actual interface PNConfiguration { + /** + * The user ID that the PubNub client will use. + */ + actual val userId: UserId + + /** + * The subscribe key from the admin panel. + */ + actual val subscribeKey: String + + /** + * The publish key from the admin panel (only required if publishing). + */ + actual val publishKey: String + + /** + * The secret key from the admin panel (only required for modifying/revealing access permissions). + * + * Keep away from Android. + */ + actual val secretKey: String + + /** + * If Access Manager is utilized, client will use this authKey in all restricted requests. + */ + actual val authKey: String + + /** + * CryptoModule is responsible for handling encryption and decryption. + * If set, all communications to and from PubNub will be encrypted. + */ + val cryptoModule: CryptoModule? + + /** + * Custom origin if needed. + * + * Defaults to `ps.pndsn.com` + */ + val origin: String + + /** + * If set to `true`, requests will be made over HTTPS. + * + * Deafults to `true`. + */ + val secure: Boolean + + /** + * Set to [PNLogVerbosity.BODY] to enable logging of network traffic, otherwise se to [PNLogVerbosity.NONE]. + */ + actual val logVerbosity: PNLogVerbosity + + /** + * Set Heartbeat notification options. + * + * By default, the SDK alerts on failed heartbeats (equivalent to [PNHeartbeatNotificationOptions.FAILURES]). + */ + val heartbeatNotificationOptions: PNHeartbeatNotificationOptions + + /** + * Sets the custom presence server timeout. + * + * The value is in seconds, and the minimum value is 20 seconds. + * + * Also sets the value of [heartbeatInterval] + */ + val presenceTimeout: Int + + /** + * How often the client will announce itself to server. + * + * The value is in seconds. + */ + val heartbeatInterval: Int + + /** + * The subscribe request timeout. + * + * The value is in seconds. + * + * Defaults to 310. + */ + val subscribeTimeout: Int + + /** + * How long before the client gives up trying to connect with the server. + * + * The value is in seconds. + * + * Defaults to 5. + */ + val connectTimeout: Int + + /** + * For non subscribe operations (publish, herenow, etc), + * This property relates to a read timeout that is applied from the moment the connection between a client + * and the server has been successfully established. It defines a maximum time of inactivity between two + * data packets when waiting for the serverโ€™s response. + * + * The value is in seconds. + * + * Defaults to 10. + */ + @Deprecated( + "This setting relates to *read* timeout and was renamed to `nonSubscribeReadTimeout`", + replaceWith = ReplaceWith("nonSubscribeReadTimeout") + ) + val nonSubscribeRequestTimeout: Int + get() = nonSubscribeReadTimeout + + /** + * For non subscribe operations (publish, herenow, etc), + * This property relates to a read timeout that is applied from the moment the connection between a client + * and the server has been successfully established. It defines a maximum time of inactivity between two + * data packets when waiting for the serverโ€™s response. + * + * The value is in seconds. + * + * Defaults to 10. + */ + val nonSubscribeReadTimeout: Int + + /** + * If operating behind a misbehaving proxy, allow the client to shuffle the subdomains. + * + * Defaults to `false`. + */ + val cacheBusting: Boolean + + /** + * When `true` the SDK doesn't send out the leave requests. + * + * Defaults to `false`. + */ + val suppressLeaveEvents: Boolean + + /** + * When `true` the SDK will resend the last channel state that was set using [PubNub.setPresenceState] + * for the current [userId] with every automatic heartbeat (if [heartbeatInterval] is greater than 0) + * and initial subscribe connection (also after e.g. loss of network). + * + * Defaults to `true`. + * + * Please note that `maintainPresenceState` doesn't apply to state that was set on channel groups. + * It is recommended to disable this option if you set state for channel groups using [PubNub.setPresenceState] + * otherwise that state may be overwritten by individual channel states. + */ + val maintainPresenceState: Boolean + + /** + * Feature to subscribe with a custom filter expression. + */ + val filterExpression: String + + /** + * Whether to include a [PubNubCore.instanceId] with every request. + * + * Defaults to `false`. + */ + val includeInstanceIdentifier: Boolean + + /** + * Whether to include a [PubNubCore.requestId] with every request. + * + * Defaults to `true`. + */ + val includeRequestIdentifier: Boolean + + /** + * @see [okhttp3.Dispatcher.setMaxRequestsPerHost] + */ + val maximumConnections: Int? + + /** + * Enable Google App Engine networking. + * + * Defaults to `false`. + */ + val googleAppEngineNetworking: Boolean + + /** + * Instructs the SDK to use a proxy configuration when communicating with PubNub servers. + * + * @see [Proxy] + */ + val proxy: Proxy? + + /** + * @see [ProxySelector] + */ + val proxySelector: ProxySelector? + + /** + * @see [Authenticator] + */ + val proxyAuthenticator: Authenticator? + + /** + * @see [CertificatePinner] + */ + val certificatePinner: CertificatePinner? + + /** + * Sets a custom [HttpLoggingInterceptor] for logging network traffic. + * + * @see [HttpLoggingInterceptor] + */ + val httpLoggingInterceptor: HttpLoggingInterceptor? + + /** + * @see [SSLSocketFactory] + */ + val sslSocketFactory: SSLSocketFactory? + + /** + * @see [X509ExtendedTrustManager] + */ + val x509ExtendedTrustManager: X509ExtendedTrustManager? + + /** + * @see [okhttp3.ConnectionSpec] + */ + val connectionSpec: ConnectionSpec? + + /** + * @see [javax.net.ssl.HostnameVerifier] + */ + val hostnameVerifier: HostnameVerifier? + + /** + * How many times publishing file message should automatically retry before marking the action as failed + * + * Defaults to `5` + */ + val fileMessagePublishRetryLimit: Int + + val dedupOnSubscribe: Boolean + val maximumMessagesCacheSize: Int + val pnsdkSuffixes: Map + + /** + * Retry configuration for requests. + * Defaults to [RetryConfiguration.None]. + * + * Use [RetryConfiguration.Linear] to set retry with linear delay interval + * Use [RetryConfiguration.Exponential] to set retry with exponential delay interval + * Delay will valy from provided value by random value. + */ + val retryConfiguration: RetryConfiguration + + /** + * Enables explicit presence control. + * When set to true heartbeat calls will contain only channels and groups added explicitly + * using [PubNubCore.presence]. Should be used only with ACL set on the server side. + * For more information please contact PubNub support + * @see PubNubCore.presence + * @see PNConfiguration.heartbeatInterval + */ + val managePresenceListManually: Boolean + + @Deprecated( + level = DeprecationLevel.WARNING, + message = """Use UserId instead e.g. config.userId.value""", + replaceWith = ReplaceWith("userId.value"), + ) + val uuid: String + get() = userId.value + companion object { + fun String.isValid() = isNotBlank() + @JvmStatic fun builder( userId: UserId, @@ -26,63 +295,282 @@ actual interface PNConfiguration : BasePNConfiguration { ): Builder { return ( Class.forName("com.pubnub.internal.v2.PNConfigurationImpl\$Builder") - .getConstructor(BasePNConfiguration::class.java) - .newInstance(object : BasePNConfigurationImpl(userId) { - override val subscribeKey: String = subscribeKey - }) as Builder + .getConstructor(UserId::class.java, subscribeKey::class.java) + .newInstance(userId, subscribeKey) as Builder ).apply(action) } + + @JvmStatic + fun builder(initialConfiguration: PNConfiguration): Builder { + return Class.forName("com.pubnub.internal.v2.PNConfigurationImpl\$Builder") + .getConstructor(PNConfiguration::class.java) + .newInstance(initialConfiguration) as Builder + } } - interface Builder : BasePNConfiguration.Builder { - override var userId: UserId - override var subscribeKey: String - override var publishKey: String - override var secretKey: String - override var authKey: String - override var cryptoModule: CryptoModule? - override var origin: String - override var secure: Boolean - override var logVerbosity: PNLogVerbosity - override var heartbeatNotificationOptions: PNHeartbeatNotificationOptions - override var presenceTimeout: Int - override var heartbeatInterval: Int - override var subscribeTimeout: Int - override var connectTimeout: Int + interface Builder { + /** + * The user ID that the PubNub client will use. + */ + var userId: UserId + /** + * The subscribe key from the admin panel. + */ + var subscribeKey: String + + /** + * The publish key from the admin panel (only required if publishing). + */ + var publishKey: String + + /** + * The secret key from the admin panel (only required for modifying/revealing access permissions). + * + * Keep away from Android. + */ + var secretKey: String + + /** + * If Access Manager is utilized, client will use this authKey in all restricted requests. + */ + var authKey: String + + /** + * CryptoModule is responsible for handling encryption and decryption. + * If set, all communications to and from PubNub will be encrypted. + */ + var cryptoModule: CryptoModule? + + /** + * Custom origin if needed. + * + * Defaults to `ps.pndsn.com` + */ + var origin: String + + /** + * If set to `true`, requests will be made over HTTPS. + * + * Deafults to `true`. + */ + var secure: Boolean + + /** + * Set to [PNLogVerbosity.BODY] to enable logging of network traffic, otherwise se to [PNLogVerbosity.NONE]. + */ + var logVerbosity: PNLogVerbosity + + /** + * Set Heartbeat notification options. + * + * By default, the SDK alerts on failed heartbeats (equivalent to [PNHeartbeatNotificationOptions.FAILURES]). + */ + var heartbeatNotificationOptions: PNHeartbeatNotificationOptions + + /** + * Sets the custom presence server timeout. + * + * The value is in seconds, and the minimum value is 20 seconds. + * + * Also sets the value of [heartbeatInterval] + */ + var presenceTimeout: Int + + /** + * How often the client will announce itself to server. + * + * The value is in seconds. + */ + var heartbeatInterval: Int + + /** + * The subscribe request timeout. + * + * The value is in seconds. + * + * Defaults to 310. + */ + var subscribeTimeout: Int + + /** + * How long before the client gives up trying to connect with the server. + * + * The value is in seconds. + * + * Defaults to 5. + */ + var connectTimeout: Int + + /** + * For non subscribe operations (publish, herenow, etc), + * This property relates to a read timeout that is applied from the moment the connection between a client + * and the server has been successfully established. It defines a maximum time of inactivity between two + * data packets when waiting for the serverโ€™s response. + * + * The value is in seconds. + * + * Defaults to 10. + */ @Deprecated( "This setting relates to *read* timeout and was renamed to `nonSubscribeReadTimeout`", replaceWith = ReplaceWith("nonSubscribeReadTimeout") ) - override var nonSubscribeRequestTimeout: Int + var nonSubscribeRequestTimeout: Int get() = nonSubscribeReadTimeout set(value) { nonSubscribeReadTimeout = value } - override var nonSubscribeReadTimeout: Int - override var cacheBusting: Boolean - override var suppressLeaveEvents: Boolean - override var maintainPresenceState: Boolean - override var filterExpression: String - override var includeInstanceIdentifier: Boolean - override var includeRequestIdentifier: Boolean - override var maximumConnections: Int? - override var googleAppEngineNetworking: Boolean - override var proxy: Proxy? - override var proxySelector: ProxySelector? - override var proxyAuthenticator: Authenticator? - override var certificatePinner: CertificatePinner? - override var httpLoggingInterceptor: HttpLoggingInterceptor? - override var sslSocketFactory: SSLSocketFactory? - override var x509ExtendedTrustManager: X509ExtendedTrustManager? - override var connectionSpec: ConnectionSpec? - override var hostnameVerifier: HostnameVerifier? - override var fileMessagePublishRetryLimit: Int - override var dedupOnSubscribe: Boolean - override var maximumMessagesCacheSize: Int - override var pnsdkSuffixes: Map - override var retryConfiguration: RetryConfiguration - override var managePresenceListManually: Boolean + + /** + * For non subscribe operations (publish, herenow, etc), + * This property relates to a read timeout that is applied from the moment the connection between a client + * and the server has been successfully established. It defines a maximum time of inactivity between two + * data packets when waiting for the serverโ€™s response. + * + * The value is in seconds. + * + * Defaults to 10. + */ + var nonSubscribeReadTimeout: Int + + /** + * If operating behind a misbehaving proxy, allow the client to shuffle the subdomains. + * + * Defaults to `false`. + */ + var cacheBusting: Boolean + + /** + * When `true` the SDK doesn't send out the leave requests. + * + * Defaults to `false`. + */ + var suppressLeaveEvents: Boolean + + /** + * When `true` the SDK will resend the last channel state that was set using [PubNub.setPresenceState] + * for the current [userId] with every automatic heartbeat (if [heartbeatInterval] is greater than 0) + * and initial subscribe connection (also after e.g. loss of network). + * + * Defaults to `true`. + * + * Please note that `maintainPresenceState` doesn't apply to state that was set on channel groups. + * It is recommended to disable this option if you set state for channel groups using [PubNub.setPresenceState] + * otherwise that state may be overwritten by individual channel states. + */ + var maintainPresenceState: Boolean + + /** + * Feature to subscribe with a custom filter expression. + */ + var filterExpression: String + + /** + * Whether to include a [PubNubCore.instanceId] with every request. + * + * Defaults to `false`. + */ + var includeInstanceIdentifier: Boolean + + /** + * Whether to include a [PubNubCore.requestId] with every request. + * + * Defaults to `true`. + */ + var includeRequestIdentifier: Boolean + + /** + * @see [okhttp3.Dispatcher.setMaxRequestsPerHost] + */ + var maximumConnections: Int? + + /** + * Enable Google App Engine networking. + * + * Defaults to `false`. + */ + var googleAppEngineNetworking: Boolean + + /** + * Instructs the SDK to use a proxy configuration when communicating with PubNub servers. + * + * @see [Proxy] + */ + var proxy: Proxy? + + /** + * @see [ProxySelector] + */ + var proxySelector: ProxySelector? + + /** + * @see [Authenticator] + */ + var proxyAuthenticator: Authenticator? + + /** + * @see [CertificatePinner] + */ + var certificatePinner: CertificatePinner? + + /** + * Sets a custom [HttpLoggingInterceptor] for logging network traffic. + * + * @see [HttpLoggingInterceptor] + */ + var httpLoggingInterceptor: HttpLoggingInterceptor? + + /** + * @see [SSLSocketFactory] + */ + var sslSocketFactory: SSLSocketFactory? + + /** + * @see [X509ExtendedTrustManager] + */ + var x509ExtendedTrustManager: X509ExtendedTrustManager? + + /** + * @see [okhttp3.ConnectionSpec] + */ + var connectionSpec: ConnectionSpec? + + /** + * @see [javax.net.ssl.HostnameVerifier] + */ + var hostnameVerifier: HostnameVerifier? + + /** + * How many times publishing file message should automatically retry before marking the action as failed + * + * Defaults to `5` + */ + var fileMessagePublishRetryLimit: Int + + var dedupOnSubscribe: Boolean + var maximumMessagesCacheSize: Int + var pnsdkSuffixes: Map + + /** + * Retry configuration for requests. + * Defaults to [RetryConfiguration.None]. + * + * Use [RetryConfiguration.Linear] to set retry with linear delay intervar + * Use [RetryConfiguration.Exponential] to set retry with exponential delay interval + * Delay will vary from provided value by random value. + */ + var retryConfiguration: RetryConfiguration + + /** + * Enables explicit presence control. + * When set to true heartbeat calls will contain only channels and groups added explicitly + * using [PubNubCore.presence]. Should be used only with ACL set on the server side. + * For more information please contact PubNub support + * @see PubNubCore.presence + * @see PNConfiguration.heartbeatInterval + */ + var managePresenceListManually: Boolean /** * Create a [PNConfiguration] object with values from this builder. @@ -91,28 +579,94 @@ actual interface PNConfiguration : BasePNConfiguration { } } -interface PNConfigurationOverride : BasePNConfigurationOverride { +interface PNConfigurationOverride { companion object { @JvmStatic - fun from(configuration: BasePNConfiguration): Builder { + fun from(configuration: PNConfiguration): Builder { return Class.forName("com.pubnub.internal.v2.PNConfigurationImpl\$Builder") - .getConstructor(BasePNConfiguration::class.java) + .getConstructor(PNConfiguration::class.java) .newInstance(configuration) as Builder } } - interface Builder : BasePNConfigurationOverride.Builder { - override var subscribeKey: String - override var publishKey: String - override var secretKey: String - override var retryConfiguration: RetryConfiguration - override var userId: UserId - override var includeInstanceIdentifier: Boolean - override var includeRequestIdentifier: Boolean - override var authKey: String - override var cryptoModule: CryptoModule? - override var connectTimeout: Int - override var nonSubscribeReadTimeout: Int + interface Builder { + /** + * The subscribe key from the admin panel. + */ + var subscribeKey: String + + /** + * The publish key from the admin panel (only required if publishing). + */ + var publishKey: String + + /** + * The secret key from the admin panel (only required for modifying/revealing access permissions). + * + * Keep away from Android. + */ + var secretKey: String + + /** + * Retry configuration for requests. + * Defaults to [RetryConfiguration.None]. + * + * Use [RetryConfiguration.Linear] to set retry with linear delay interval + * Use [RetryConfiguration.Exponential] to set retry with exponential delay interval + * Delay will valy from provided value by random value. + */ + var retryConfiguration: RetryConfiguration + + /** + * The user ID that the PubNub client will use. + */ + var userId: UserId + + /** + * Whether to include a [PubNub.instanceId] with every request. + * + * Defaults to `false`. + */ + var includeInstanceIdentifier: Boolean + + /** + * Whether to include a [PubNub.requestId] with every request. + * + * Defaults to `true`. + */ + var includeRequestIdentifier: Boolean + + /** + * If Access Manager is utilized, client will use this authKey in all restricted requests. + */ + var authKey: String + + /** + * CryptoModule is responsible for handling encryption and decryption. + * If set, all communications to and from PubNub will be encrypted. + */ + var cryptoModule: CryptoModule? + + /** + * How long before the client gives up trying to connect with the server. + * + * The value is in seconds. + * + * Defaults to 5. + */ + var connectTimeout: Int + + /** + * For non subscribe operations (publish, herenow, etc), + * This property relates to a read timeout that is applied from the moment the connection between a client + * and the server has been successfully established. It defines a maximum time of inactivity between two + * data packets when waiting for the serverโ€™s response. + * + * The value is in seconds. + * + * Defaults to 10. + */ + var nonSubscribeReadTimeout: Int /** * Create a [PNConfiguration] object with values from this builder. diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.jvm.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.jvm.kt new file mode 100644 index 000000000..00cf05076 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/Consumer.jvm.kt @@ -0,0 +1,5 @@ +package com.pubnub.api.v2.callbacks + +import java.util.function.Consumer + +actual typealias Consumer = Consumer diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/EventListener.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/EventListener.kt index edcf25483..5edbe644a 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/EventListener.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/EventListener.kt @@ -13,7 +13,7 @@ import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult * Implement this interface and pass it into [EventEmitter.addListener] to listen for events from the PubNub real-time * network. */ -actual interface EventListener : BaseEventListener, Listener { +actual interface EventListener : Listener { /** * Receive messages at subscribed channels. * diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/StatusEmitter.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/StatusEmitter.kt index b3f0e34fa..9282e4f5f 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/StatusEmitter.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/StatusEmitter.kt @@ -1,7 +1,28 @@ package com.pubnub.api.v2.callbacks +import com.pubnub.api.callbacks.Listener + /** * Interface implemented by objects that manage the subscription connection to the PubNub network and can be monitored * for connection state changes. */ -interface StatusEmitter : BaseStatusEmitter +interface StatusEmitter { + /** + * Add a listener. + * + * @param listener The listener to be added. + */ + fun addListener(listener: StatusListener) + + /** + * Remove a listener. + * + * @param listener The listener to be removed, previously added with [addListener]. + */ + fun removeListener(listener: Listener) + + /** + * Removes all listeners. + */ + fun removeAllListeners() +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/StatusListener.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/StatusListener.kt index 519f561f0..86c84e3ee 100644 --- a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/StatusListener.kt +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/api/v2/callbacks/StatusListener.kt @@ -8,7 +8,7 @@ import com.pubnub.api.models.consumer.PNStatus * Implement this interface and pass it into [com.pubnub.api.v2.callbacks.StatusEmitter.addListener] to listen for * PubNub connection status changes. */ -actual interface StatusListener : BaseStatusListener, Listener { +actual interface StatusListener : Listener { /** * Receive status updates from the PubNub client, such as: * * [PNStatusCategory.PNConnectedCategory], @@ -16,7 +16,6 @@ actual interface StatusListener : BaseStatusListener, Listener { * * [PNStatusCategory.PNSubscriptionChanged] * * [PNStatusCategory.PNConnectionError], * * [PNStatusCategory.PNUnexpectedDisconnectCategory], - * * [PNStatusCategory.PNAcknowledgmentCategory] * * @see [PNStatus] * diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/internal/endpoints/HasOverridableConfig.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/internal/endpoints/HasOverridableConfig.kt new file mode 100644 index 000000000..d9967da67 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/internal/endpoints/HasOverridableConfig.kt @@ -0,0 +1,9 @@ +package com.pubnub.internal.endpoints + +import com.pubnub.api.v2.PNConfiguration + +interface HasOverridableConfig { + fun overrideConfigurationInternal(configuration: PNConfiguration) + + val configuration: PNConfiguration +} diff --git a/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/kmp/Downloadable.jvm.kt b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/kmp/Downloadable.jvm.kt new file mode 100644 index 000000000..a87836fdf --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-api/src/jvmMain/kotlin/com/pubnub/kmp/Downloadable.jvm.kt @@ -0,0 +1,5 @@ +package com.pubnub.kmp + +import java.io.InputStream + +actual typealias Downloadable = InputStream diff --git a/pubnub-kotlin/pubnub-kotlin-impl/build.gradle.kts b/pubnub-kotlin/pubnub-kotlin-impl/build.gradle.kts index 17a77976a..102349b24 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/build.gradle.kts +++ b/pubnub-kotlin/pubnub-kotlin-impl/build.gradle.kts @@ -1,14 +1,29 @@ +import com.pubnub.gradle.tasks.GenerateVersionTask + plugins { alias(libs.plugins.benmanes.versions) + id("pubnub.java-library") id("pubnub.kotlin-library") id("pubnub.test") id("pubnub.integration-test") } +val generateVersion = + tasks.register("generateVersion") { + version.set(providers.gradleProperty("VERSION_NAME")) + outputDirectory.set( + layout.buildDirectory.map { + it.dir("generated/sources/generateVersion") + }, + ) + } + +kotlin.sourceSets.getByName("main").kotlin.srcDir(generateVersion) + dependencies { - api(project(":pubnub-core:pubnub-core-api")) +// api(project(":pubnub-core:pubnub-core-api")) api(project(":pubnub-kotlin:pubnub-kotlin-api")) - implementation(project(":pubnub-core:pubnub-core-impl")) +// implementation(project(":pubnub-core:pubnub-core-impl")) implementation(libs.slf4j) testImplementation(libs.wiremock) @@ -24,4 +39,38 @@ dependencies { testImplementation(libs.junit.jupiter.engine) testImplementation(libs.mockk) testImplementation(libs.owner) + implementation(libs.retrofit2) + implementation(libs.retrofit2.converter.gson) + implementation(libs.okhttp) + implementation(libs.okhttp.logging) + + implementation(libs.json) + implementation(libs.gson) + + implementation(libs.slf4j) + implementation(libs.cbor) + + testImplementation(libs.wiremock) + testImplementation(libs.logback.classic) + testImplementation(libs.logback.core) + testImplementation(libs.cucumber.java) + testImplementation(libs.cucumber.junit) + testImplementation(libs.cucumber.picocontainer) + testImplementation(libs.awaitility) + testImplementation(libs.junit4) + testImplementation(libs.junit.jupiter.engine) + testImplementation(libs.junit.vintage.engine) + testImplementation(libs.junit.jupiter) + testImplementation(libs.mockk) + testImplementation(libs.owner) +} + +task("cucumber") { + filter { + // include all tests from package + includeTestsMatching("com.pubnub.contract.*") + } + systemProperty("cucumber.filter.tags", System.getProperty("cucumber.filter.tags")) + systemProperty("cucumber.features", System.getProperty("cucumber.features")) + systemProperty("cucumber.plugins", System.getProperty("cucumber.plugins")) } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/config/ktlint/baseline.xml b/pubnub-kotlin/pubnub-kotlin-impl/config/ktlint/baseline.xml index 981420778..0490138f9 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/config/ktlint/baseline.xml +++ b/pubnub-kotlin/pubnub-kotlin-impl/config/ktlint/baseline.xml @@ -1,3 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/AppTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/AppTest.kt index fe3ea5b78..60c92a24c 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/AppTest.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/AppTest.kt @@ -1,9 +1,9 @@ package com.pubnub.api.integration -import com.pubnub.api.PNConfiguration import com.pubnub.api.PubNub import com.pubnub.api.UserId import com.pubnub.api.enums.PNLogVerbosity +import com.pubnub.api.v2.PNConfiguration import com.pubnub.test.Keys import com.pubnub.test.listen import org.awaitility.Awaitility @@ -22,11 +22,10 @@ class AppTest { fun initPubnub() { pubnub = PubNub.create( - PNConfiguration(userId = UserId(PubNub.generateUUID())).apply { - subscribeKey = Keys.subKey + PNConfiguration.builder(userId = UserId(PubNub.generateUUID()), subscribeKey = Keys.subKey) { publishKey = Keys.pubKey logVerbosity = PNLogVerbosity.BODY - }, + }.build(), ) } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/test/SignatureUtils.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/test/SignatureUtils.kt index 24da57178..9545c0ffc 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/test/SignatureUtils.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/test/SignatureUtils.kt @@ -1,7 +1,7 @@ package com.pubnub.test import com.github.tomakehurst.wiremock.verification.LoggedRequest -import com.pubnub.api.PNConfiguration +import com.pubnub.api.v2.PNConfiguration import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.Request import okio.Buffer diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/java/com/pubnub/internal/vendor/AppEngineFactory.java b/pubnub-kotlin/pubnub-kotlin-impl/src/main/java/com/pubnub/internal/vendor/AppEngineFactory.java new file mode 100644 index 000000000..60d71294e --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/java/com/pubnub/internal/vendor/AppEngineFactory.java @@ -0,0 +1,152 @@ +package com.pubnub.internal.vendor; + +import com.pubnub.api.v2.PNConfiguration; +import com.pubnub.internal.PubNubImpl; +import com.pubnub.internal.PubNubUtil; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Headers; +import okhttp3.MediaType; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.BufferedSink; +import okio.BufferedSource; +import okio.Okio; +import okio.Timeout; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +public class AppEngineFactory implements Call { + private Request request; + private final PNConfiguration configuration; + + AppEngineFactory(Request request, PNConfiguration configuration) { + this.request = request; + this.configuration = configuration; + } + + @NotNull + @Override + public Request request() { + return request; + } + + @NotNull + @Override + public Response execute() throws IOException { + request = PubNubUtil.INSTANCE.signRequest(request, configuration, PubNubImpl.timestamp()); + + URL url = request.url().url(); + final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setUseCaches(false); + connection.setDoOutput(true); + connection.setRequestMethod(request.method()); + + Headers headers = request.headers(); + if (headers != null) { + for (int i = 0; i < headers.size(); i++) { + String name = headers.name(i); + connection.setRequestProperty(name, headers.get(name)); + } + } + + if (request.body() != null) { + BufferedSink outbuf; + outbuf = Okio.buffer(Okio.sink(connection.getOutputStream())); + request.body().writeTo(outbuf); + outbuf.close(); + } + + connection.connect(); + + final BufferedSource source = Okio.buffer(Okio.source(connection.getInputStream())); + if (connection.getResponseCode() != 200) { + throw new IOException("Fail to call " + " :: " + source.readUtf8()); + } + Response response = new Response.Builder() + .code(connection.getResponseCode()) + .message(connection.getResponseMessage()) + .request(request) + .protocol(Protocol.HTTP_1_1) + .body(new ResponseBody() { + @Override + public MediaType contentType() { + return MediaType.parse(connection.getContentType()); + } + + @Override + public long contentLength() { + String contentLengthField = connection.getHeaderField("content-length"); + long contentLength; + try { + contentLength = Long.parseLong(contentLengthField); + } catch (NumberFormatException ignored) { + contentLength = -1; + } + return contentLength; + } + + @Override + public BufferedSource source() { + return source; + } + }) + .build(); + return response; + } + + @Override + public void enqueue(Callback responseCallback) { + + } + + @Override + public void cancel() { + + } + + @Override + public boolean isExecuted() { + return false; + } + + @Override + public boolean isCanceled() { + return false; + } + + @NotNull + @Override + public Timeout timeout() { + return Timeout.NONE; + } + + @NotNull + @Override + public Call clone() { + try { + return (Call) super.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } + + public static class Factory implements Call.Factory { + private final PNConfiguration configuration; + + public Factory(PNConfiguration configuration) { + this.configuration = configuration; + } + + @NotNull + @Override + public Call newCall(Request request) { + return new AppEngineFactory(request, configuration); + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/java/com/pubnub/internal/vendor/Base64.java b/pubnub-kotlin/pubnub-kotlin-impl/src/main/java/com/pubnub/internal/vendor/Base64.java new file mode 100644 index 000000000..d2bd0bda4 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/java/com/pubnub/internal/vendor/Base64.java @@ -0,0 +1,746 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.pubnub.internal.vendor; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +/** + * Utilities for encoding and decoding the Base64 representation of + * binary data. See RFCs 2045 and 3548. + */ +public class Base64 { + /** + * Default values for encoder/decoder flags. + */ + public static final int DEFAULT = 0; + + /** + * Encoder flag bit to omit the padding '=' characters at the end + * of the output (if any). + */ + public static final int NO_PADDING = 1; + + /** + * Encoder flag bit to omit all line terminators (i.e., the output + * will be on one long line). + */ + public static final int NO_WRAP = 2; + + /** + * Encoder flag bit to indicate lines should be terminated with a + * CRLF pair instead of just an LF. Has no effect if {@code + * NO_WRAP} is specified as well. + */ + public static final int CRLF = 4; + + /** + * Encoder/decoder flag bit to indicate using the "URL and + * filename safe" variant of Base64 (see RFC 3548 section 4) where + * {@code -} and {@code _} are used in place of {@code +} and + * {@code /}. + */ + public static final int URL_SAFE = 8; + + /** + * Flag to pass to indicate that it + * should not close the output stream it is wrapping when it + * itself is closed. + */ + public static final int NO_CLOSE = 16; + + // -------------------------------------------------------- + // shared code + // -------------------------------------------------------- + + /* package */ static abstract class Coder { + public byte[] output; + public int op; + + /** + * Encode/decode another block of input data. this.output is + * provided by the caller, and must be big enough to hold all + * the coded data. On exit, this.opwill be set to the length + * of the coded data. + * + * @param finish true if this is the final call to process for + * this object. Will finalize the coder state and + * include any final bytes in the output. + * @return true if the input so far is good; false if some + * error has been detected in the input stream.. + */ + public abstract boolean process(byte[] input, int offset, int len, boolean finish); + + /** + * @return the maximum number of bytes a call to process() + * could produce for the given number of input bytes. This may + * be an overestimate. + */ + public abstract int maxOutputSize(int len); + } + + // -------------------------------------------------------- + // decoding + // -------------------------------------------------------- + + /** + * Decode the Base64-encoded data in input and return the data in + * a new byte array. + * + *

The padding '=' characters at the end are considered optional, but + * if any are present, there must be the correct number of them. + * + * @param str the input String to decode, which is converted to + * bytes using the default charset + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. + * @throws IllegalArgumentException if the input contains + * incorrect padding + */ + public static byte[] decode(String str, int flags) { + return decode(str.getBytes(Charset.forName("UTF-8")), flags); + } + + /** + * Decode the Base64-encoded data in input and return the data in + * a new byte array. + * + *

The padding '=' characters at the end are considered optional, but + * if any are present, there must be the correct number of them. + * + * @param input the input array to decode + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. + * @throws IllegalArgumentException if the input contains + * incorrect padding + */ + public static byte[] decode(byte[] input, int flags) { + return decode(input, 0, input.length, flags); + } + + /** + * Decode the Base64-encoded data in input and return the data in + * a new byte array. + * + *

The padding '=' characters at the end are considered optional, but + * if any are present, there must be the correct number of them. + * + * @param input the data to decode + * @param offset the position within the input array at which to start + * @param len the number of bytes of input to decode + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. + * @throws IllegalArgumentException if the input contains + * incorrect padding + */ + public static byte[] decode(byte[] input, int offset, int len, int flags) { + // Allocate space for the most data the input could represent. + // (It could contain less if it contains whitespace, etc.) + Decoder decoder = new Decoder(flags, new byte[len * 3 / 4]); + + if (!decoder.process(input, offset, len, true)) { + throw new IllegalArgumentException("bad base-64"); + } + + // Maybe we got lucky and allocated exactly enough output space. + if (decoder.op == decoder.output.length) { + return decoder.output; + } + + // Need to shorten the array, so allocate a new one of the + // right size and copy. + byte[] temp = new byte[decoder.op]; + System.arraycopy(decoder.output, 0, temp, 0, decoder.op); + return temp; + } + + /* package */ static class Decoder extends Coder { + /** + * Lookup table for turning bytes into their position in the + * Base64 alphabet. + */ + private static final int DECODE[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + /** + * Decode lookup table for the "web safe" variant (RFC 3548 + * sec. 4) where - and _ replace + and /. + */ + private static final int DECODE_WEBSAFE[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + /** + * Non-data values in the DECODE arrays. + */ + private static final int SKIP = -1; + private static final int EQUALS = -2; + + /** + * States 0-3 are reading through the next input tuple. + * State 4 is having read one '=' and expecting exactly + * one more. + * State 5 is expecting no more data or padding characters + * in the input. + * State 6 is the error state; an error has been detected + * in the input and no future input can "fix" it. + */ + private int state; // state number (0 to 6) + private int value; + + final private int[] alphabet; + + public Decoder(int flags, byte[] output) { + this.output = output; + + alphabet = ((flags & URL_SAFE) == 0) ? DECODE : DECODE_WEBSAFE; + state = 0; + value = 0; + } + + /** + * @return an overestimate for the number of bytes {@code + * len} bytes could decode to. + */ + public int maxOutputSize(int len) { + return len * 3 / 4 + 10; + } + + /** + * Decode another block of input data. + * + * @return true if the state machine is still healthy. false if + * bad base-64 data has been detected in the input stream. + */ + public boolean process(byte[] input, int offset, int len, boolean finish) { + if (this.state == 6) return false; + + int p = offset; + len += offset; + + // Using local variables makes the decoder about 12% + // faster than if we manipulate the member variables in + // the loop. (Even alphabet makes a measurable + // difference, which is somewhat surprising to me since + // the member variable is final.) + int state = this.state; + int value = this.value; + int op = 0; + final byte[] output = this.output; + final int[] alphabet = this.alphabet; + + while (p < len) { + // Try the fast path: we're starting a new tuple and the + // next four bytes of the input stream are all data + // bytes. This corresponds to going through states + // 0-1-2-3-0. We expect to use this method for most of + // the data. + // + // If any of the next four bytes of input are non-data + // (whitespace, etc.), value will end up negative. (All + // the non-data values in decode are small negative + // numbers, so shifting any of them up and or'ing them + // together will result in a value with its top bit set.) + // + // You can remove this whole block and the output should + // be the same, just slower. + if (state == 0) { + while (p + 4 <= len && + (value = ((alphabet[input[p] & 0xff] << 18) | + (alphabet[input[p + 1] & 0xff] << 12) | + (alphabet[input[p + 2] & 0xff] << 6) | + (alphabet[input[p + 3] & 0xff]))) >= 0) { + output[op + 2] = (byte) value; + output[op + 1] = (byte) (value >> 8); + output[op] = (byte) (value >> 16); + op += 3; + p += 4; + } + if (p >= len) break; + } + + // The fast path isn't available -- either we've read a + // partial tuple, or the next four input bytes aren't all + // data, or whatever. Fall back to the slower state + // machine implementation. + + int d = alphabet[input[p++] & 0xff]; + + switch (state) { + case 0: + if (d >= 0) { + value = d; + ++state; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 1: + if (d >= 0) { + value = (value << 6) | d; + ++state; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 2: + if (d >= 0) { + value = (value << 6) | d; + ++state; + } else if (d == EQUALS) { + // Emit the last (partial) output tuple; + // expect exactly one more padding character. + output[op++] = (byte) (value >> 4); + state = 4; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 3: + if (d >= 0) { + // Emit the output triple and return to state 0. + value = (value << 6) | d; + output[op + 2] = (byte) value; + output[op + 1] = (byte) (value >> 8); + output[op] = (byte) (value >> 16); + op += 3; + state = 0; + } else if (d == EQUALS) { + // Emit the last (partial) output tuple; + // expect no further data or padding characters. + output[op + 1] = (byte) (value >> 2); + output[op] = (byte) (value >> 10); + op += 2; + state = 5; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 4: + if (d == EQUALS) { + ++state; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 5: + if (d != SKIP) { + this.state = 6; + return false; + } + break; + } + } + + if (!finish) { + // We're out of input, but a future call could provide + // more. + this.state = state; + this.value = value; + this.op = op; + return true; + } + + // Done reading input. Now figure out where we are left in + // the state machine and finish up. + + switch (state) { + case 0: + // Output length is a multiple of three. Fine. + break; + case 1: + // Read one extra input byte, which isn't enough to + // make another output byte. Illegal. + this.state = 6; + return false; + case 2: + // Read two extra input bytes, enough to emit 1 more + // output byte. Fine. + output[op++] = (byte) (value >> 4); + break; + case 3: + // Read three extra input bytes, enough to emit 2 more + // output bytes. Fine. + output[op++] = (byte) (value >> 10); + output[op++] = (byte) (value >> 2); + break; + case 4: + // Read one padding '=' when we expected 2. Illegal. + this.state = 6; + return false; + case 5: + // Read all the padding '='s we expected and no more. + // Fine. + break; + } + + this.state = state; + this.op = op; + return true; + } + } + + // -------------------------------------------------------- + // encoding + // -------------------------------------------------------- + + /** + * Base64-encode the given data and return a newly allocated + * String with the result. + * + * @param input the data to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static String encodeToString(byte[] input, int flags) { + try { + return new String(encode(input, flags), "US-ASCII"); + } catch (UnsupportedEncodingException e) { + // US-ASCII is guaranteed to be available. + throw new AssertionError(e); + } + } + + /** + * Base64-encode the given data and return a newly allocated + * String with the result. + * + * @param input the data to encode + * @param offset the position within the input array at which to + * start + * @param len the number of bytes of input to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static String encodeToString(byte[] input, int offset, int len, int flags) { + try { + return new String(encode(input, offset, len, flags), "US-ASCII"); + } catch (UnsupportedEncodingException e) { + // US-ASCII is guaranteed to be available. + throw new AssertionError(e); + } + } + + /** + * Base64-encode the given data and return a newly allocated + * byte[] with the result. + * + * @param input the data to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static byte[] encode(byte[] input, int flags) { + return encode(input, 0, input.length, flags); + } + + /** + * Base64-encode the given data and return a newly allocated + * byte[] with the result. + * + * @param input the data to encode + * @param offset the position within the input array at which to + * start + * @param len the number of bytes of input to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static byte[] encode(byte[] input, int offset, int len, int flags) { + Encoder encoder = new Encoder(flags, null); + + // Compute the exact length of the array we will produce. + int output_len = len / 3 * 4; + + // Account for the tail of the data and the padding bytes, if any. + if (encoder.do_padding) { + if (len % 3 > 0) { + output_len += 4; + } + } else { + switch (len % 3) { + case 0: + break; + case 1: + output_len += 2; + break; + case 2: + output_len += 3; + break; + default: + break; + } + } + + // Account for the newlines, if any. + if (encoder.do_newline && len > 0) { + output_len += (((len - 1) / (3 * Encoder.LINE_GROUPS)) + 1) * + (encoder.do_cr ? 2 : 1); + } + + encoder.output = new byte[output_len]; + encoder.process(input, offset, len, true); + + assert encoder.op == output_len; + + return encoder.output; + } + + /* package */ static class Encoder extends Coder { + /** + * Emit a new line every this many output tuples. Corresponds to + * a 76-character line length (the maximum allowable according to + * RFC 2045). + */ + public static final int LINE_GROUPS = 19; + + /** + * Lookup table for turning Base64 alphabet positions (6 bits) + * into output bytes. + */ + private static final byte ENCODE[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', + }; + + /** + * Lookup table for turning Base64 alphabet positions (6 bits) + * into output bytes. + */ + private static final byte ENCODE_WEBSAFE[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', + }; + + final private byte[] tail; + /* package */ int tailLen; + private int count; + + final public boolean do_padding; + final public boolean do_newline; + final public boolean do_cr; + final private byte[] alphabet; + + public Encoder(int flags, byte[] output) { + this.output = output; + + do_padding = (flags & NO_PADDING) == 0; + do_newline = (flags & NO_WRAP) == 0; + do_cr = (flags & CRLF) != 0; + alphabet = ((flags & URL_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE; + + tail = new byte[2]; + tailLen = 0; + + count = do_newline ? LINE_GROUPS : -1; + } + + /** + * @return an overestimate for the number of bytes {@code + * len} bytes could encode to. + */ + public int maxOutputSize(int len) { + return len * 8 / 5 + 10; + } + + public boolean process(byte[] input, int offset, int len, boolean finish) { + // Using local variables makes the encoder about 9% faster. + final byte[] alphabet = this.alphabet; + final byte[] output = this.output; + int op = 0; + int count = this.count; + + int p = offset; + len += offset; + int v = -1; + + // First we need to concatenate the tail of the previous call + // with any input bytes available now and see if we can empty + // the tail. + + switch (tailLen) { + case 0: + // There was no tail. + break; + case 1: + if (p + 2 <= len) { + // A 1-byte tail with at least 2 bytes of + // input available now. + v = ((tail[0] & 0xff) << 16) | + ((input[p++] & 0xff) << 8) | + (input[p++] & 0xff); + tailLen = 0; + } + break; + case 2: + if (p + 1 <= len) { + // A 2-byte tail with at least 1 byte of input. + v = ((tail[0] & 0xff) << 16) | + ((tail[1] & 0xff) << 8) | + (input[p++] & 0xff); + tailLen = 0; + } + break; + } + + if (v != -1) { + output[op++] = alphabet[(v >> 18) & 0x3f]; + output[op++] = alphabet[(v >> 12) & 0x3f]; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (--count == 0) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + count = LINE_GROUPS; + } + } + + // At this point either there is no tail, or there are fewer + // than 3 bytes of input available. + + // The main loop, turning 3 input bytes into 4 output bytes on + // each iteration. + while (p + 3 <= len) { + v = ((input[p] & 0xff) << 16) | + ((input[p + 1] & 0xff) << 8) | + (input[p + 2] & 0xff); + output[op] = alphabet[(v >> 18) & 0x3f]; + output[op + 1] = alphabet[(v >> 12) & 0x3f]; + output[op + 2] = alphabet[(v >> 6) & 0x3f]; + output[op + 3] = alphabet[v & 0x3f]; + p += 3; + op += 4; + if (--count == 0) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + count = LINE_GROUPS; + } + } + + if (finish) { + // Finish up the tail of the input. Note that we need to + // consume any bytes in tail before any bytes + // remaining in input; there should be at most two bytes + // total. + + if (p - tailLen == len - 1) { + int t = 0; + v = ((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 4; + tailLen -= t; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (do_padding) { + output[op++] = '='; + output[op++] = '='; + } + if (do_newline) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + } else if (p - tailLen == len - 2) { + int t = 0; + v = (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) | + (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2); + tailLen -= t; + output[op++] = alphabet[(v >> 12) & 0x3f]; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (do_padding) { + output[op++] = '='; + } + if (do_newline) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + } else if (do_newline && op > 0 && count != LINE_GROUPS) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + + assert tailLen == 0; + assert p == len; + } else { + // Save the leftovers in tail to be consumed on the next + // call to encodeInternal. + + if (p == len - 1) { + tail[tailLen++] = input[p]; + } else if (p == len - 2) { + tail[tailLen++] = input[p]; + tail[tailLen++] = input[p + 1]; + } + } + + this.op = op; + this.count = count; + + return true; + } + } + + private Base64() { + } // don't instantiate +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/java/com/pubnub/internal/vendor/Crypto.java b/pubnub-kotlin/pubnub-kotlin-impl/src/main/java/com/pubnub/internal/vendor/Crypto.java new file mode 100644 index 000000000..cc18ac8bf --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/java/com/pubnub/internal/vendor/Crypto.java @@ -0,0 +1,162 @@ +package com.pubnub.internal.vendor; + +import com.pubnub.api.PubNubError; +import com.pubnub.api.PubNubException; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Collections; + +public class Crypto { + + private static final String ENCODING_UTF_8 = "UTF-8"; + private static final String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding"; + + private final boolean dynamicIV; + byte[] keyBytes = null; + byte[] ivBytes = null; + String initializationVector = "0123456789012345"; + String cipherKey; + boolean INIT = false; + private SecureRandom secureRandom = new SecureRandom(); + + public Crypto(String cipherKey) { + this(cipherKey, false); + } + + public Crypto(String cipherKey, String customInitializationVector) { + this(cipherKey, false); + if (customInitializationVector != null) { + this.initializationVector = customInitializationVector; + } + } + + public Crypto(String cipherKey, boolean dynamicIV) { + this.cipherKey = cipherKey; + this.dynamicIV = dynamicIV; + } + + public void initCiphers() throws PubNubException { + if (INIT && !dynamicIV) + return; + try { + + keyBytes = new String(hexEncode(sha256(this.cipherKey.getBytes(ENCODING_UTF_8))), ENCODING_UTF_8) + .substring(0, 32) + .toLowerCase().getBytes(ENCODING_UTF_8); + if (dynamicIV) { + ivBytes = new byte[16]; + secureRandom.nextBytes(ivBytes); + } else { + ivBytes = initializationVector.getBytes(ENCODING_UTF_8); + INIT = true; + } + } catch (UnsupportedEncodingException e) { + throw newCryptoError(11, e); + } + } + + public static byte[] hexEncode(byte[] input) throws PubNubException { + StringBuffer result = new StringBuffer(); + for (byte byt : input) + result.append(Integer.toString((byt & 0xff) + 0x100, 16).substring(1)); + try { + return result.toString().getBytes(ENCODING_UTF_8); + } catch (UnsupportedEncodingException e) { + throw newCryptoError(12, e); + } + } + + public static PubNubException newCryptoError(int code, Exception exception) { + return new PubNubException( + exception.getClass().getSimpleName() + " " + exception.getMessage() + " " + code, + PubNubError.CRYPTO_ERROR, + null, + 0, + null, + null, + Collections.emptyList(), + Collections.emptyList(), + exception, + null, + null + ); + } + + public String encrypt(String input) throws PubNubException { + return encrypt(input, Base64.NO_WRAP); + } + + public String encrypt(String input, Integer flags) throws PubNubException { + try { + initCiphers(); + AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes); + SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES"); + Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION); + cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec); + if (dynamicIV) { + byte[] encrypted = cipher.doFinal(input.getBytes(ENCODING_UTF_8)); + byte[] encryptedWithIV = new byte[ivBytes.length + encrypted.length]; + System.arraycopy(ivBytes, 0, encryptedWithIV, 0, ivBytes.length); + System.arraycopy(encrypted, 0, encryptedWithIV, ivBytes.length, encrypted.length); + return new String(Base64.encode(encryptedWithIV, flags), ENCODING_UTF_8); + } else { + return new String(Base64.encode(cipher.doFinal(input.getBytes(ENCODING_UTF_8)), flags), ENCODING_UTF_8); + } + } catch (Exception e) { + throw newCryptoError(0, e); + } + + } + + public String decrypt(String cipher_text) throws PubNubException { + return decrypt(cipher_text, Base64.NO_WRAP); + } + + public String decrypt(String cipher_text, Integer flags) throws PubNubException { + try { + byte[] dataBytes; + initCiphers(); + if (dynamicIV) { + dataBytes = Base64.decode(cipher_text, flags); + System.arraycopy(dataBytes, 0, ivBytes, 0, 16); + byte[] receivedCipherBytes = new byte[dataBytes.length - 16]; + System.arraycopy(dataBytes, 16, receivedCipherBytes, 0, dataBytes.length - 16); + dataBytes = receivedCipherBytes; + } else { + dataBytes = Base64.decode(cipher_text, flags); + } + AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes); + SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES"); + Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION); + cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec); + return new String(cipher.doFinal(dataBytes), ENCODING_UTF_8); + } catch (Exception e) { + throw newCryptoError(0, e); + } + } + + /** + * Get SHA256 + * + * @param input + * @return byte[] + * @throws PubNubException + */ + public static byte[] sha256(byte[] input) throws PubNubException { + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-256"); + byte[] hashedBytes = digest.digest(input); + return hashedBytes; + } catch (Exception e) { + throw newCryptoError(0, e); + } + } + +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/DelegatingEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/DelegatingEndpoint.kt deleted file mode 100644 index 36eabd8a5..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/DelegatingEndpoint.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.pubnub.internal - -import com.pubnub.api.PubNubException -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.enums.PNOperationType -import com.pubnub.api.v2.callbacks.Result -import java.util.function.Consumer - -abstract class DelegatingEndpoint(originalRemoteAction: EndpointInterface) : - EndpointImpl(originalRemoteAction) { - private val remoteAction: ExtendedRemoteAction = convertAction(originalRemoteAction) - - protected abstract fun convertAction(remoteAction: ExtendedRemoteAction): ExtendedRemoteAction - - @Throws(PubNubException::class) - override fun sync(): OUTPUT { - return remoteAction.sync() - } - - override fun async(callback: Consumer>) { - remoteAction.async(callback) - } - - override fun retry() { - remoteAction.retry() - } - - override fun silentCancel() { - remoteAction.silentCancel() - } - - override fun operationType(): PNOperationType { - return remoteAction.operationType() - } -} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/EndpointCore.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/EndpointCore.kt new file mode 100644 index 000000000..cf4e9141d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/EndpointCore.kt @@ -0,0 +1,458 @@ +package com.pubnub.internal + +import com.google.gson.JsonElement +import com.pubnub.api.Endpoint +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.api.v2.PNConfiguration +import com.pubnub.api.v2.PNConfiguration.Companion.isValid +import com.pubnub.api.v2.PNConfigurationOverride +import com.pubnub.api.v2.callbacks.Consumer +import com.pubnub.api.v2.callbacks.Result +import com.pubnub.internal.managers.RetrofitManager +import com.pubnub.internal.retry.RetryableBase +import com.pubnub.internal.retry.RetryableCallback +import com.pubnub.internal.retry.RetryableRestCaller +import com.pubnub.internal.v2.PNConfigurationImpl +import org.slf4j.LoggerFactory +import retrofit2.Call +import retrofit2.Response +import java.io.IOException +import java.net.ConnectException +import java.net.SocketTimeoutException +import java.net.UnknownHostException + +/** + * Base class for all PubNub API operation implementations. + * + * @param Input Server's response. + * @param Output Parsed and encapsulated response for endusers. + * @property pubnub The client instance. + */ +abstract class EndpointCore protected constructor(protected val pubnub: PubNubImpl) : Endpoint { + private var configOverride: PNConfiguration? = null + val configuration: PNConfiguration + get() = configOverride ?: pubnub.configuration + + protected val retrofitManager: RetrofitManager + get() = configOverride?.let { configOverrideNonNull -> + RetrofitManager(pubnub.retrofitManager, configOverrideNonNull) + } ?: pubnub.retrofitManager + + private val log = LoggerFactory.getLogger(this.javaClass.simpleName) + + private lateinit var cachedCallback: Consumer> + private lateinit var call: Call + private var silenceFailures = false + private val retryableRestCaller by lazy { + RetryableRestCaller( + configuration.retryConfiguration, + getEndpointGroupName(), + isEndpointRetryable(), + ) + } + + override fun overrideConfiguration(action: PNConfigurationOverride.Builder.() -> Unit): Endpoint { + overrideConfigurationInternal(PNConfigurationImpl.Builder(configuration).apply(action).build()) + return this + } + + override fun overrideConfiguration(configuration: PNConfiguration): Endpoint { + overrideConfigurationInternal(configuration) + return this + } + + /** + * Key-value object to pass with every PubNub API operation. Used for debugging purposes. + * todo: it should be removed! + */ + val queryParam: MutableMap = mutableMapOf() + + /** + * Executes the call synchronously. This function blocks the thread. + * + * @return A parsed and encapsulated response if the request has been successful, `null` otherwise. + * + * @throws [PubNubException] if anything goes wrong with the request. + */ + override fun sync(): Output { + validateParams() + call = doWork(createBaseParams()) + val response = retryableRestCaller.execute(call) + return handleResponse(response) + } + + private fun handleResponse(response: Response): Output { + when { + response.isSuccessful -> { + return checkAndCreateResponse(response) + } + + else -> { + val (errorString, errorJson) = extractErrorBody(response) + throw createException(response, errorString, errorJson) + } + } + } + + /** + * Executes the call asynchronously. This function does not block the thread. + * + * @param callback The callback to receive the response in. + */ + override fun async(callback: Consumer>) { + cachedCallback = callback + + try { + validateParams() + call = doWork(createBaseParams()) + } catch (pubnubException: PubNubException) { + callback.accept(Result.failure(pubnubException)) + return + } + + call.enqueue( + object : RetryableCallback( + call = call, + retryConfiguration = configuration.retryConfiguration, + endpointGroupName = getEndpointGroupName(), + isEndpointRetryable = isEndpointRetryable(), + executorService = pubnub.executorService, + ) { + override fun onFinalResponse( + call: Call, + response: Response, + ) { + when { + response.isSuccessful -> { + // query params + try { + Result.success(checkAndCreateResponse(response)) + } catch (e: PubNubException) { + Result.failure(e) + }.let { result -> + callback.accept(result) + } + } + + else -> { + val (errorString, errorJson) = extractErrorBody(response) + + callback.accept( + Result.failure( + createException( + response, + errorString, + errorJson, + ), + ), + ) + } + } + } + + override fun onFinalFailure( + call: Call, + t: Throwable, + ) { + if (silenceFailures) { + return + } + + val error: PubNubError = + when (t) { + is UnknownHostException, is ConnectException -> { + PubNubError.CONNECT_EXCEPTION + } + + is SocketTimeoutException -> { + PubNubError.SUBSCRIBE_TIMEOUT + } + + is IOException -> { + PubNubError.PARSING_ERROR + } + + is IllegalStateException -> { + PubNubError.PARSING_ERROR + } + + else -> { + PubNubError.HTTP_ERROR + } + } + + val pubnubException = + PubNubException( + errorMessage = t.toString(), + pubnubError = error, + cause = t, + remoteAction = this@EndpointCore, + ) + callback.accept(Result.failure(pubnubException)) + } + }, + ) + } + + protected fun createBaseParams(): HashMap { + val map = hashMapOf() + + map += queryParam + + map["pnsdk"] = pubnub.generatePnsdk() + map["uuid"] = configuration.userId.value + + if (configuration.includeInstanceIdentifier) { + map["instanceid"] = pubnub.instanceId + } + + if (configuration.includeRequestIdentifier) { + map["requestid"] = pubnub.requestId() + } + + if (isAuthRequired()) { + val token = pubnub.tokenManager.getToken() + if (token != null) { + map["auth"] = token + } else if (configuration.authKey.isValid()) { + map["auth"] = configuration.authKey + } + } + return map + } + + /** + * Cancel the operation but do not alert anybody, useful for restarting the heartbeats and subscribe loops. + */ + override fun silentCancel() { + if (::call.isInitialized) { + if (!call.isCanceled) { + silenceFailures = true + call.cancel() + } + } + } + + private fun createException( + response: Response, + errorString: String? = null, + errorBody: JsonElement? = null, + ): PubNubException { + val errorChannels = mutableListOf() + val errorGroups = mutableListOf() + + if (errorBody != null) { + if (pubnub.mapper.isJsonObject(errorBody) && + pubnub.mapper.hasField( + errorBody, + "payload", + ) + ) { + val payloadBody = pubnub.mapper.getField(errorBody, "payload")!! + + if (pubnub.mapper.hasField(payloadBody, "channels")) { + val iterator = pubnub.mapper.getArrayIterator(payloadBody, "channels") + while (iterator.hasNext()) { + errorChannels.add(pubnub.mapper.elementToString(iterator.next())!!) + } + } + + if (pubnub.mapper.hasField(payloadBody, "channel-groups")) { + val iterator = pubnub.mapper.getArrayIterator(payloadBody, "channel-groups") + while (iterator.hasNext()) { + val node = iterator.next() + + val channelGroupName = + pubnub.mapper.elementToString(node)!!.let { + if (it.first().toString() == ":") { + it.substring(1) + } else { + it + } + } + + errorGroups.add(channelGroupName) + } + } + } + } + + val affectedChannels = + errorChannels.ifEmpty { + try { + getAffectedChannels() + } catch (e: UninitializedPropertyAccessException) { + emptyList() + } + } + + val affectedChannelGroups = + errorGroups.ifEmpty { + try { + getAffectedChannelGroups() + } catch (e: UninitializedPropertyAccessException) { + emptyList() + } + } + + return PubNubException( + pubnubError = PubNubError.HTTP_ERROR, + errorMessage = errorString, + jso = errorBody?.toString(), + statusCode = response.code(), + affectedCall = call, + retryAfterHeaderValue = response.headers()[RetryableBase.RETRY_AFTER_HEADER_NAME]?.toIntOrNull(), + affectedChannels = affectedChannels, + affectedChannelGroups = affectedChannelGroups, + requestInfo = + PubNubException.RequestInfo( + tlsEnabled = response.raw().request.url.isHttps, + origin = response.raw().request.url.host, + uuid = response.raw().request.url.queryParameter("uuid"), + authKey = response.raw().request.url.queryParameter("auth"), + clientRequest = response.raw().request, + ), + remoteAction = this, + ) + } + + override fun retry() { + silenceFailures = false + async(cachedCallback) + } + + private fun extractErrorBody(response: Response): Pair { + val errorBodyString = + try { + response.errorBody()?.string() + } catch (e: IOException) { + "N/A" + } + + val errorBodyJson = + try { + pubnub.mapper.fromJson(errorBodyString, JsonElement::class.java) + } catch (e: PubNubException) { + null + } + + return errorBodyString to errorBodyJson + } + + private fun checkAndCreateResponse(input: Response): Output { + try { + return createResponse(input) + } catch (pubnubException: PubNubException) { + throw pubnubException.copy( + statusCode = input.code(), + jso = pubnub.mapper.toJson(input.body()), + affectedCall = call, + ) + } catch (e: KotlinNullPointerException) { + throw PubNubException( + pubnubError = PubNubError.PARSING_ERROR, + errorMessage = e.toString(), + affectedCall = call, + statusCode = input.code(), + jso = pubnub.mapper.toJson(input.body()), + cause = e, + ) + } catch (e: IllegalStateException) { + throw PubNubException( + pubnubError = PubNubError.PARSING_ERROR, + errorMessage = e.toString(), + affectedCall = call, + statusCode = input.code(), + jso = pubnub.mapper.toJson(input.body()), + cause = e, + ) + } catch (e: IndexOutOfBoundsException) { + throw PubNubException( + pubnubError = PubNubError.PARSING_ERROR, + errorMessage = e.toString(), + affectedCall = call, + statusCode = input.code(), + jso = pubnub.mapper.toJson(input.body()), + cause = e, + ) + } catch (e: NullPointerException) { + throw PubNubException( + pubnubError = PubNubError.PARSING_ERROR, + errorMessage = e.toString(), + affectedCall = call, + statusCode = input.code(), + jso = pubnub.mapper.toJson(input.body()), + cause = e, + ) + } catch (e: IllegalArgumentException) { + throw PubNubException( + pubnubError = PubNubError.PARSING_ERROR, + errorMessage = e.toString(), + affectedCall = call, + statusCode = input.code(), + jso = pubnub.mapper.toJson(input.body()), + cause = e, + ) + } catch (e: TypeCastException) { + throw PubNubException( + pubnubError = PubNubError.PARSING_ERROR, + errorMessage = e.toString(), + affectedCall = call, + statusCode = input.code(), + jso = pubnub.mapper.toJson(input.body()), + cause = e, + ) + } catch (e: ClassCastException) { + throw PubNubException( + pubnubError = PubNubError.PARSING_ERROR, + errorMessage = e.toString(), + affectedCall = call, + statusCode = input.code(), + jso = pubnub.mapper.toJson(input.body()), + cause = e, + ) + } catch (e: UninitializedPropertyAccessException) { + throw PubNubException( + pubnubError = PubNubError.PARSING_ERROR, + errorMessage = e.toString(), + affectedCall = call, + statusCode = input.code(), + jso = pubnub.mapper.toJson(input.body()), + cause = e, + ) + } + } + + protected open fun getAffectedChannels() = emptyList() + + protected open fun getAffectedChannelGroups(): List = emptyList() + + protected open fun validateParams() { + if (isSubKeyRequired() && !configuration.subscribeKey.isValid()) { + throw PubNubException(PubNubError.SUBSCRIBE_KEY_MISSING) + } + if (isPubKeyRequired() && !configuration.publishKey.isValid()) { + throw PubNubException(PubNubError.PUBLISH_KEY_MISSING) + } + } + + fun overrideConfigurationInternal(configuration: PNConfiguration) { + this.configOverride = configuration + } + + protected abstract fun doWork(queryParams: HashMap): Call + + protected abstract fun createResponse(input: Response): Output + + protected open fun isSubKeyRequired() = true + + protected open fun isPubKeyRequired() = false + + protected open fun isAuthRequired() = true + + protected abstract fun getEndpointGroupName(): RetryableEndpointGroup + + protected open fun isEndpointRetryable(): Boolean = true +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/EndpointImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/EndpointImpl.kt deleted file mode 100644 index 45b3c6998..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/EndpointImpl.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.pubnub.internal - -import com.pubnub.api.Endpoint -import com.pubnub.api.endpoints.HasOverridableConfig -import com.pubnub.api.v2.PNConfiguration -import com.pubnub.api.v2.PNConfigurationOverride -import com.pubnub.internal.v2.PNConfigurationImpl - -abstract class EndpointImpl(private val endpoint: HasOverridableConfig) : Endpoint { - override fun overrideConfiguration(action: PNConfigurationOverride.Builder.() -> Unit): Endpoint { - endpoint.overrideConfiguration(PNConfigurationImpl.Builder(endpoint.configuration).apply(action).build()) - return this - } - - override fun overrideConfiguration(configuration: PNConfiguration): Endpoint { - endpoint.overrideConfiguration(configuration) - return this - } -} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubCore.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubCore.kt new file mode 100644 index 000000000..1b7112fe8 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubCore.kt @@ -0,0 +1,672 @@ +// package com.pubnub.internal +// +// import com.pubnub.api.PubNubException +// import com.pubnub.api.UserId +// import com.pubnub.api.crypto.CryptoModule +// import com.pubnub.api.enums.PNPushEnvironment +// import com.pubnub.api.enums.PNPushType +// import com.pubnub.api.models.consumer.PNBoundedPage +// import com.pubnub.api.models.consumer.access_manager.v3.PNToken +// import com.pubnub.api.models.consumer.history.PNHistoryResult +// import com.pubnub.api.models.consumer.message_actions.PNMessageAction +// import com.pubnub.api.models.consumer.objects.PNPage +// import com.pubnub.api.v2.PNConfiguration +// import com.pubnub.api.v2.subscriptions.EmptyOptions +// import com.pubnub.api.v2.subscriptions.Subscription +// import com.pubnub.api.v2.subscriptions.SubscriptionCursor +// import com.pubnub.api.v2.subscriptions.SubscriptionOptions +// import com.pubnub.internal.crypto.decryptString +// import com.pubnub.internal.crypto.encryptString +// import com.pubnub.internal.endpoints.DeleteMessagesEndpoint +// import com.pubnub.internal.endpoints.FetchMessagesEndpoint +// import com.pubnub.internal.endpoints.HistoryEndpoint +// import com.pubnub.internal.endpoints.MessageCountsEndpoint +// import com.pubnub.internal.endpoints.TimeEndpoint +// import com.pubnub.internal.endpoints.access.GrantEndpoint +// import com.pubnub.internal.endpoints.access.GrantTokenEndpoint +// import com.pubnub.internal.endpoints.access.RevokeTokenEndpoint +// import com.pubnub.internal.endpoints.channel_groups.AddChannelChannelGroupEndpoint +// import com.pubnub.internal.endpoints.channel_groups.AllChannelsChannelGroupEndpoint +// import com.pubnub.internal.endpoints.channel_groups.DeleteChannelGroupEndpoint +// import com.pubnub.internal.endpoints.channel_groups.ListAllChannelGroupEndpoint +// import com.pubnub.internal.endpoints.channel_groups.RemoveChannelChannelGroupEndpoint +// import com.pubnub.internal.endpoints.files.DeleteFileEndpoint +// import com.pubnub.internal.endpoints.files.DownloadFileEndpoint +// import com.pubnub.internal.endpoints.files.GenerateUploadUrlEndpoint +// import com.pubnub.internal.endpoints.files.GetFileUrlEndpoint +// import com.pubnub.internal.endpoints.files.ListFilesEndpoint +// import com.pubnub.internal.endpoints.files.PublishFileMessageEndpoint +// import com.pubnub.internal.endpoints.files.SendFileEndpoint +// import com.pubnub.internal.endpoints.files.UploadFileEndpoint +// import com.pubnub.internal.endpoints.message_actions.AddMessageActionEndpoint +// import com.pubnub.internal.endpoints.message_actions.GetMessageActionsEndpoint +// import com.pubnub.internal.endpoints.message_actions.RemoveMessageActionEndpoint +// import com.pubnub.internal.endpoints.objects.channel.GetAllChannelMetadataEndpoint +// import com.pubnub.internal.endpoints.objects.channel.GetChannelMetadataEndpoint +// import com.pubnub.internal.endpoints.objects.channel.RemoveChannelMetadataEndpoint +// import com.pubnub.internal.endpoints.objects.channel.SetChannelMetadataEndpoint +// import com.pubnub.internal.endpoints.objects.internal.CollectionQueryParameters +// import com.pubnub.internal.endpoints.objects.internal.IncludeQueryParam +// import com.pubnub.internal.endpoints.objects.member.GetChannelMembersEndpoint +// import com.pubnub.internal.endpoints.objects.member.ManageChannelMembersEndpoint +// import com.pubnub.internal.endpoints.objects.membership.GetMembershipsEndpoint +// import com.pubnub.internal.endpoints.objects.membership.ManageMembershipsEndpoint +// import com.pubnub.internal.endpoints.objects.uuid.GetAllUUIDMetadataEndpoint +// import com.pubnub.internal.endpoints.objects.uuid.GetUUIDMetadataEndpoint +// import com.pubnub.internal.endpoints.objects.uuid.RemoveUUIDMetadataEndpoint +// import com.pubnub.internal.endpoints.objects.uuid.SetUUIDMetadataEndpoint +// import com.pubnub.internal.endpoints.presence.GetStateEndpoint +// import com.pubnub.internal.endpoints.presence.HereNowEndpoint +// import com.pubnub.internal.endpoints.presence.SetStateEndpoint +// import com.pubnub.internal.endpoints.presence.WhereNowEndpoint +// import com.pubnub.internal.endpoints.pubsub.PublishEndpoint +// import com.pubnub.internal.endpoints.pubsub.SignalEndpoint +// import com.pubnub.internal.endpoints.push.AddChannelsToPushEndpoint +// import com.pubnub.internal.endpoints.push.ListPushProvisionsEndpoint +// import com.pubnub.internal.endpoints.push.RemoveAllPushChannelsForDeviceEndpoint +// import com.pubnub.internal.endpoints.push.RemoveChannelsFromPushEndpoint +// import com.pubnub.internal.managers.BasePathManager +// import com.pubnub.internal.managers.DuplicationManager +// import com.pubnub.internal.managers.ListenerManager +// import com.pubnub.internal.managers.MapperManager +// import com.pubnub.internal.managers.PublishSequenceManager +// import com.pubnub.internal.managers.RetrofitManager +// import com.pubnub.internal.managers.TokenManager +// import com.pubnub.internal.managers.TokenParser +// import com.pubnub.api.models.consumer.access_manager.sum.SpacePermissions +// import com.pubnub.api.models.consumer.access_manager.sum.UserPermissions +// import com.pubnub.api.models.consumer.access_manager.sum.toChannelGrant +// import com.pubnub.api.models.consumer.access_manager.sum.toUuidGrant +// import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant +// import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant +// import com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant +// import com.pubnub.api.models.consumer.objects.PNKey +// import com.pubnub.api.models.consumer.objects.PNMemberKey +// import com.pubnub.api.models.consumer.objects.PNMembershipKey +// import com.pubnub.api.models.consumer.objects.PNSortKey +// import com.pubnub.api.models.consumer.objects.member.MemberInput +// import com.pubnub.api.models.consumer.objects.member.PNUUIDDetailsLevel +// import com.pubnub.api.models.consumer.objects.membership.ChannelMembershipInput +// import com.pubnub.api.models.consumer.objects.membership.PNChannelDetailsLevel +// import com.pubnub.internal.presence.Presence +// import com.pubnub.internal.presence.eventengine.data.PresenceData +// import com.pubnub.internal.presence.eventengine.effect.effectprovider.HeartbeatProviderImpl +// import com.pubnub.internal.presence.eventengine.effect.effectprovider.LeaveProviderImpl +// import com.pubnub.internal.subscribe.PRESENCE_CHANNEL_SUFFIX +// import com.pubnub.internal.subscribe.Subscribe +// import com.pubnub.internal.subscribe.eventengine.configuration.EventEnginesConf +// import com.pubnub.internal.v2.entities.ChannelGroupImpl +// import com.pubnub.internal.v2.entities.ChannelGroupName +// import com.pubnub.internal.v2.entities.ChannelImpl +// import com.pubnub.internal.v2.entities.ChannelName +// import com.pubnub.internal.v2.subscription.SubscriptionImpl +// import com.pubnub.internal.workers.SubscribeMessageProcessor +// import java.io.InputStream +// import java.util.Date +// import java.util.UUID +// import java.util.concurrent.Executors +// import java.util.concurrent.ScheduledExecutorService +// import kotlin.time.Duration.Companion.seconds +// +// class PubNubCore internal constructor( +// val configuration: PNConfiguration, +// val listenerManager: ListenerManager, +// eventEnginesConf: EventEnginesConf = EventEnginesConf(), +// private val pnsdkName: String, +// ) { +// +// +// //region Presence +// +// //endregion +// +// //region MessageActions +// +// //region PAM +// +// +// +// //endregion +// +// //region Miscellaneous +// +// //endregion +// +// //region ObjectsAPI +// +// +// +// /** +// * @see [PubNub.getChannelMembers] +// */ +// @Deprecated( +// replaceWith = +// ReplaceWith( +// "getChannelMembers(channel = channel, limit = limit, " + +// "page = page, filter = filter, sort = sort, includeCount = includeCount, includeCustom = includeCustom," + +// "includeUUIDDetails = includeUUIDDetails)", +// ), +// level = DeprecationLevel.WARNING, +// message = "Use getChannelMembers instead", +// ) +// fun getMembers( +// channel: String, +// limit: Int? = null, +// page: PNPage? = null, +// filter: String? = null, +// sort: Collection> = listOf(), +// includeCount: Boolean = false, +// includeCustom: Boolean = false, +// includeUUIDDetails: PNUUIDDetailsLevel? = null, +// ) = getChannelMembers( +// channel = channel, +// limit = limit, +// page = page, +// filter = filter, +// sort = sort, +// includeCount = includeCount, +// includeCustom = includeCustom, +// includeUUIDDetails = includeUUIDDetails, +// ) +// +// /** +// * @see [PubNub.setChannelMembers] +// */ +// @Deprecated( +// replaceWith = +// ReplaceWith( +// "setChannelMembers(channel = channel, uuids = uuids, limit = limit, " + +// "page = page, filter = filter, sort = sort, includeCount = includeCount, includeCustom = includeCustom," + +// "includeUUIDDetails = includeUUIDDetails)", +// ), +// level = DeprecationLevel.WARNING, +// message = "Use setChannelMembers instead", +// ) +// fun addMembers( +// channel: String, +// uuids: List, +// limit: Int? = null, +// page: PNPage? = null, +// filter: String? = null, +// sort: Collection> = listOf(), +// includeCount: Boolean = false, +// includeCustom: Boolean = false, +// includeUUIDDetails: PNUUIDDetailsLevel? = null, +// ) = setChannelMembers( +// channel = channel, +// uuids = uuids, +// limit = limit, +// page = page, +// filter = filter, +// sort = sort, +// includeCount = includeCount, +// includeCustom = includeCustom, +// includeUUIDDetails = includeUUIDDetails, +// ) +// +// /** +// * This method sets members in a channel. +// * +// * @param channel Channel name +// * @param uuids List of members to add to the channel. List can contain strings (uuid only) +// * or objects (which can include custom data). @see [PNMember.Partial] +// * @param limit Number of objects to return in the response. +// * Default is 100, which is also the maximum value. +// * Set limit to 0 (zero) and includeCount to true if you want to retrieve only a result count. +// * @param page Use for pagination. +// * - [PNNext] : Previously-returned cursor bookmark for fetching the next page. +// * - [PNPrev] : Previously-returned cursor bookmark for fetching the previous page. +// * Ignored if you also supply the start parameter. +// * @param filter Expression used to filter the results. Only objects whose properties satisfy the given +// * expression are returned. +// * @param sort List of properties to sort by. Available options are id, name, and updated. +// * @see [PNAsc], [PNDesc] +// * @param includeCount Request totalCount to be included in paginated response. By default, totalCount is omitted. +// * Default is `false`. +// * @param includeCustom Include respective additional fields in the response. +// * @param includeUUIDDetails Include custom fields for UUIDs metadata. +// */ +// fun setChannelMembers( +// channel: String, +// uuids: List, +// limit: Int? = null, +// page: PNPage? = null, +// filter: String? = null, +// sort: Collection> = listOf(), +// includeCount: Boolean = false, +// includeCustom: Boolean = false, +// includeUUIDDetails: PNUUIDDetailsLevel? = null, +// includeType: Boolean = false, +// ) = manageChannelMembers( +// channel = channel, +// uuidsToSet = uuids, +// uuidsToRemove = listOf(), +// limit = limit, +// page = page, +// filter = filter, +// sort = sort, +// includeCount = includeCount, +// includeCustom = includeCustom, +// includeUUIDDetails = includeUUIDDetails, +// includeType = includeType +// ) +// +// /** +// * @see [PubNub.removeChannelMembers] +// */ +// @Deprecated( +// replaceWith = +// ReplaceWith( +// "removeChannelMembers(channel = channel, uuids = uuids, limit = limit, " + +// "page = page, filter = filter, sort = sort, includeCount = includeCount, includeCustom = includeCustom," + +// "includeUUIDDetails = includeUUIDDetails)", +// ), +// level = DeprecationLevel.WARNING, +// message = "Use removeChannelMembers instead", +// ) +// fun removeMembers( +// channel: String, +// uuids: List, +// limit: Int? = null, +// page: PNPage? = null, +// filter: String? = null, +// sort: Collection> = listOf(), +// includeCount: Boolean = false, +// includeCustom: Boolean = false, +// includeUUIDDetails: PNUUIDDetailsLevel? = null, +// ) = removeChannelMembers( +// channel = channel, +// uuids = uuids, +// limit = limit, +// page = page, +// filter = filter, +// sort = sort, +// includeCount = includeCount, +// includeCustom = includeCustom, +// includeUUIDDetails = includeUUIDDetails, +// ) +// +// /** +// * Remove members from a Channel. +// * +// * @param channel Channel name +// * @param uuids Members to remove from channel. +// * @param limit Number of objects to return in the response. +// * Default is 100, which is also the maximum value. +// * Set limit to 0 (zero) and includeCount to true if you want to retrieve only a result count. +// * @param page Use for pagination. +// * - [PNNext] : Previously-returned cursor bookmark for fetching the next page. +// * - [PNPrev] : Previously-returned cursor bookmark for fetching the previous page. +// * Ignored if you also supply the start parameter. +// * @param filter Expression used to filter the results. Only objects whose properties satisfy the given +// * expression are returned. +// * @param sort List of properties to sort by. Available options are id, name, and updated. +// * @see [PNAsc], [PNDesc] +// * @param includeCount Request totalCount to be included in paginated response. By default, totalCount is omitted. +// * Default is `false`. +// * @param includeCustom Include respective additional fields in the response. +// * @param includeUUIDDetails Include custom fields for UUIDs metadata. +// */ +// fun removeChannelMembers( +// channel: String, +// uuids: List, +// limit: Int? = null, +// page: PNPage? = null, +// filter: String? = null, +// sort: Collection> = listOf(), +// includeCount: Boolean = false, +// includeCustom: Boolean = false, +// includeUUIDDetails: PNUUIDDetailsLevel? = null, +// includeType: Boolean = false, +// ) = manageChannelMembers( +// channel = channel, +// uuidsToSet = listOf(), +// uuidsToRemove = uuids, +// limit = limit, +// page = page, +// filter = filter, +// sort = sort, +// includeCount = includeCount, +// includeCustom = includeCustom, +// includeUUIDDetails = includeUUIDDetails, +// includeType = includeType, +// ) +// +// /** +// * Set or remove members in a channel. +// * +// * @param channel Channel name +// * @param uuidsToSet Collection of members to add to the channel. @see [com.pubnub.api.models.consumer.objects.member.PNMember.Partial] +// * @param uuidsToRemove Members to remove from channel. +// * @param limit Number of objects to return in the response. +// * Default is 100, which is also the maximum value. +// * Set limit to 0 (zero) and includeCount to true if you want to retrieve only a result count. +// * @param page Use for pagination. +// * - [PNNext] : Previously-returned cursor bookmark for fetching the next page. +// * - [PNPrev] : Previously-returned cursor bookmark for fetching the previous page. +// * Ignored if you also supply the start parameter. +// * @param filter Expression used to filter the results. Only objects whose properties satisfy the given +// * expression are returned. +// * @param sort List of properties to sort by. Available options are id, name, and updated. +// * @see [PNAsc], [PNDesc] +// * @param includeCount Request totalCount to be included in paginated response. By default, totalCount is omitted. +// * Default is `false`. +// * @param includeCustom Include respective additional fields in the response. +// * @param includeUUIDDetails Include custom fields for UUIDs metadata. +// */ +// fun manageChannelMembers( +// channel: String, +// uuidsToSet: Collection, +// uuidsToRemove: Collection, +// limit: Int? = null, +// page: PNPage? = null, +// filter: String? = null, +// sort: Collection> = listOf(), +// includeCount: Boolean = false, +// includeCustom: Boolean = false, +// includeUUIDDetails: PNUUIDDetailsLevel? = null, +// includeType: Boolean = false, +// ) = ManageChannelMembersEndpoint( +// pubnub = this, +// channel = channel, +// uuidsToSet = uuidsToSet, +// uuidsToRemove = uuidsToRemove, +// collectionQueryParameters = +// CollectionQueryParameters( +// limit = limit, +// page = page, +// filter = filter, +// sort = sort, +// includeCount = includeCount, +// ), +// includeQueryParam = +// IncludeQueryParam( +// includeCustom = includeCustom, +// includeUUIDDetails = includeUUIDDetails, +// includeUuidType = includeType, +// ), +// ) +// +// //endregion ObjectsAPI +// +// //region files +// +// /** +// * Upload file / data to specified Channel. +// * +// * @param channel Channel name +// * @param fileName Name of the file to send. +// * @param inputStream Input stream with file content. The inputStream will be depleted after the call. +// * @param message The payload. +// * **Warning:** It is important to note that you should not serialize JSON +// * when sending signals/messages via PubNub. +// * Why? Because the serialization is done for you automatically. +// * Instead just pass the full object as the message payload. +// * PubNub takes care of everything for you. +// * @param meta Metadata object which can be used with the filtering ability. +// * @param ttl Set a per message time to live in storage. +// * - If `shouldStore = true`, and `ttl = 0`, the message is stored +// * with no expiry time. +// * - If `shouldStore = true` and `ttl = X` (`X` is an Integer value), +// * the message is stored with an expiry time of `X` hours. +// * - If `shouldStore = false`, the `ttl` parameter is ignored. +// * - If ttl isn't specified, then expiration of the message defaults +// * back to the expiry value for the key. +// * @param shouldStore Store in history. +// * If not specified, then the history configuration of the key is used. +// * @param cipherKey Key to be used to encrypt uploaded data. +// */ +// fun sendFile( +// channel: String, +// fileName: String, +// inputStream: InputStream, +// message: Any? = null, +// meta: Any? = null, +// ttl: Int? = null, +// shouldStore: Boolean? = null, +// cipherKey: String? = null, +// ): SendFileEndpoint { +// val cryptoModule = +// if (cipherKey != null) { +// CryptoModule.createLegacyCryptoModule(cipherKey) +// } else { +// configuration.cryptoModule +// } +// +// return SendFileEndpoint( +// channel = channel, +// fileName = fileName, +// inputStream = inputStream, +// message = message, +// meta = meta, +// ttl = ttl, +// shouldStore = shouldStore, +// executorService = +// retrofitManager.getTransactionClientExecutorService() +// ?: Executors.newSingleThreadExecutor(), +// fileMessagePublishRetryLimit = configuration.fileMessagePublishRetryLimit, +// generateUploadUrlFactory = GenerateUploadUrlEndpoint.Factory(this), +// publishFileMessageFactory = PublishFileMessageEndpoint.Factory(this), +// sendFileToS3Factory = UploadFileEndpoint.Factory(this), +// cryptoModule = cryptoModule, +// ) +// } +// +// /** +// * Retrieve list of files uploaded to Channel. +// * +// * @param channel Channel name +// * @param limit Number of files to return. Minimum value is 1, and maximum is 100. Default value is 100. +// * @param next Previously-returned cursor bookmark for fetching the next page. @see [PNPage.PNNext] +// */ +// fun listFiles( +// channel: String, +// limit: Int? = null, +// next: PNPage.PNNext? = null, +// ): ListFilesEndpoint { +// return ListFilesEndpoint( +// pubNub = this, +// channel = channel, +// limit = limit, +// next = next, +// ) +// } +// +// /** +// * Generate URL which can be used to download file from target Channel. +// * +// * @param channel Name of channel to which the file has been uploaded. +// * @param fileName Name under which the uploaded file is stored. +// * @param fileId Unique identifier for the file, assigned during upload. +// */ +// fun getFileUrl( +// channel: String, +// fileName: String, +// fileId: String, +// ): GetFileUrlEndpoint { +// return GetFileUrlEndpoint( +// pubNub = this, +// channel = channel, +// fileName = fileName, +// fileId = fileId, +// ) +// } +// +// /** +// * Download file from specified Channel. +// * +// * @param channel Name of channel to which the file has been uploaded. +// * @param fileName Name under which the uploaded file is stored. +// * @param fileId Unique identifier for the file, assigned during upload. +// * @param cipherKey Key to be used to decrypt downloaded data. If a key is not provided, +// * the SDK uses the cipherKey from the @see [PNConfiguration]. +// */ +// fun downloadFile( +// channel: String, +// fileName: String, +// fileId: String, +// cipherKey: String? = null, +// ): DownloadFileEndpoint { +// val cryptoModule = +// if (cipherKey != null) { +// CryptoModule.createLegacyCryptoModule(cipherKey) +// } else { +// configuration.cryptoModule +// } +// +// return DownloadFileEndpoint( +// pubNub = this, +// channel = channel, +// fileName = fileName, +// fileId = fileId, +// cryptoModule = cryptoModule, +// ) +// } +// +// /** +// * Delete file from specified Channel. +// * +// * @param channel Name of channel to which the file has been uploaded. +// * @param fileName Name under which the uploaded file is stored. +// * @param fileId Unique identifier for the file, assigned during upload. +// */ +// fun deleteFile( +// channel: String, +// fileName: String, +// fileId: String, +// ): DeleteFileEndpoint { +// return DeleteFileEndpoint( +// pubNub = this, +// channel = channel, +// fileName = fileName, +// fileId = fileId, +// ) +// } +// +// /** +// * Publish file message from specified Channel. +// * @param channel Name of channel to which the file has been uploaded. +// * @param fileName Name under which the uploaded file is stored. +// * @param fileId Unique identifier for the file, assigned during upload. +// * @param message The payload. +// * **Warning:** It is important to note that you should not serialize JSON +// * when sending signals/messages via PubNub. +// * Why? Because the serialization is done for you automatically. +// * Instead just pass the full object as the message payload. +// * PubNub takes care of everything for you. +// * @param meta Metadata object which can be used with the filtering ability. +// * @param ttl Set a per message time to live in storage. +// * - If `shouldStore = true`, and `ttl = 0`, the message is stored +// * with no expiry time. +// * - If `shouldStore = true` and `ttl = X` (`X` is an Integer value), +// * the message is stored with an expiry time of `X` hours. +// * - If `shouldStore = false`, the `ttl` parameter is ignored. +// * - If ttl isn't specified, then expiration of the message defaults +// * back to the expiry value for the key. +// * @param shouldStore Store in history. +// * If not specified, then the history configuration of the key is used. +// * +// */ +// fun publishFileMessage( +// channel: String, +// fileName: String, +// fileId: String, +// message: Any? = null, +// meta: Any? = null, +// ttl: Int? = null, +// shouldStore: Boolean? = null, +// ): PublishFileMessageEndpoint { +// return PublishFileMessageEndpoint( +// pubNub = this, +// channel = channel, +// fileName = fileName, +// fileId = fileId, +// message = message, +// meta = meta, +// ttl = ttl, +// shouldStore = shouldStore, +// ) +// } +// //endregion +// +// //region Encryption +// +// /** +// * Perform Cryptographic decryption of an input string using cipher key provided by [PNConfiguration.cipherKey]. +// * +// * @param inputString String to be decrypted. +// * +// * @return String containing the decryption of `inputString` using `cipherKey`. +// * @throws PubNubException throws exception in case of failed decryption. +// */ +// @Throws(PubNubException::class) +// fun decrypt(inputString: String): String = decrypt(inputString, null) +// +// /** +// * Perform Cryptographic decryption of an input string using a cipher key. +// * +// * @param inputString String to be decrypted. +// * @param cipherKey cipher key to be used for decryption. Default is [PNConfiguration.cipherKey] +// * +// * @return String containing the decryption of `inputString` using `cipherKey`. +// * @throws PubNubException throws exception in case of failed decryption. +// */ +// @Throws(PubNubException::class) +// fun decrypt( +// inputString: String, +// cryptoModule: CryptoModule? = null, +// ): String = getCryptoModuleOrThrow(cryptoModule).decryptString(inputString) +// +// /** +// * Perform Cryptographic decryption of an input stream using provided cipher key. +// * +// * @param inputStream InputStream to be encrypted. +// * @param cipherKey Cipher key to be used for decryption. +// * +// * @return InputStream containing the encryption of `inputStream` using `cipherKey`. +// * @throws PubNubException Throws exception in case of failed decryption. +// */ +// @Throws(PubNubException::class) +// fun decryptInputStream( +// inputStream: InputStream, +// cryptoModule: CryptoModule? = null, +// ): InputStream = getCryptoModuleOrThrow(cryptoModule).decryptStream(inputStream) +// +// /** +// * Perform Cryptographic encryption of an input string and a cipher key. +// * +// * @param inputString String to be encrypted. +// * @param cipherKey Cipher key to be used for encryption. Default is [PNConfiguration.cipherKey] +// * +// * @return String containing the encryption of `inputString` using `cipherKey`. +// * @throws PubNubException Throws exception in case of failed encryption. +// */ +// @Throws(PubNubException::class) +// fun encrypt( +// inputString: String, +// cryptoModule: CryptoModule? = null, +// ): String = getCryptoModuleOrThrow(cryptoModule).encryptString(inputString) +// +// /** +// * Perform Cryptographic encryption of an input stream using provided cipher key. +// * +// * @param inputStream InputStream to be encrypted. +// * @param cipherKey Cipher key to be used for encryption. +// * +// * @return InputStream containing the encryption of `inputStream` using `cipherKey`. +// * @throws PubNubException Throws exception in case of failed encryption. +// */ +// @Throws(PubNubException::class) +// fun encryptInputStream( +// inputStream: InputStream, +// cryptoModule: CryptoModule? = null, +// ): InputStream = getCryptoModuleOrThrow(cryptoModule).encryptStream(inputStream) +// +// @Throws(PubNubException::class) +// private fun getCryptoModuleOrThrow(cryptoModule: CryptoModule? = null): CryptoModule { +// return cryptoModule ?: configuration.cryptoModule ?: throw PubNubException("Crypto module is not initialized") +// } +// //endregion +// +// +// } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubImpl.kt index 9203fbad7..221514ae9 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubImpl.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubImpl.kt @@ -1,6 +1,5 @@ package com.pubnub.internal -import com.pubnub.api.PNConfiguration import com.pubnub.api.PubNub import com.pubnub.api.PubNubException import com.pubnub.api.UserId @@ -56,8 +55,11 @@ import com.pubnub.api.enums.PNPushType import com.pubnub.api.models.consumer.PNBoundedPage import com.pubnub.api.models.consumer.access_manager.sum.SpacePermissions import com.pubnub.api.models.consumer.access_manager.sum.UserPermissions +import com.pubnub.api.models.consumer.access_manager.sum.toChannelGrant +import com.pubnub.api.models.consumer.access_manager.sum.toUuidGrant import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant +import com.pubnub.api.models.consumer.access_manager.v3.PNToken import com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant import com.pubnub.api.models.consumer.message_actions.PNMessageAction import com.pubnub.api.models.consumer.objects.PNKey @@ -75,33 +77,82 @@ import com.pubnub.api.models.consumer.pubsub.PNSignalResult import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult +import com.pubnub.api.v2.PNConfiguration import com.pubnub.api.v2.callbacks.EventListener import com.pubnub.api.v2.callbacks.StatusListener -import com.pubnub.api.v2.entities.BaseChannel -import com.pubnub.api.v2.entities.BaseChannelGroup -import com.pubnub.api.v2.entities.BaseChannelMetadata -import com.pubnub.api.v2.entities.BaseUserMetadata -import com.pubnub.api.v2.entities.Channel import com.pubnub.api.v2.entities.ChannelGroup import com.pubnub.api.v2.entities.ChannelMetadata import com.pubnub.api.v2.entities.UserMetadata -import com.pubnub.api.v2.subscriptions.BaseSubscription -import com.pubnub.api.v2.subscriptions.BaseSubscriptionSet +import com.pubnub.api.v2.subscriptions.EmptyOptions import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.SubscriptionCursor import com.pubnub.api.v2.subscriptions.SubscriptionOptions import com.pubnub.api.v2.subscriptions.SubscriptionSet -import com.pubnub.internal.models.toInternal -import com.pubnub.internal.models.toInternalChannelGrants -import com.pubnub.internal.models.toInternalChannelGroupGrants -import com.pubnub.internal.models.toInternalChannelMemberships -import com.pubnub.internal.models.toInternalMemberInputs -import com.pubnub.internal.models.toInternalSortKeys -import com.pubnub.internal.models.toInternalSpacePermissions -import com.pubnub.internal.models.toInternalUserPermissions -import com.pubnub.internal.models.toInternalUuidGrants -import com.pubnub.internal.v2.callbacks.DelegatingEventListener -import com.pubnub.internal.v2.callbacks.DelegatingStatusListener -import com.pubnub.internal.v2.callbacks.DelegatingSubscribeCallback +import com.pubnub.internal.crypto.decryptString +import com.pubnub.internal.crypto.encryptString +import com.pubnub.internal.endpoints.DeleteMessagesEndpoint +import com.pubnub.internal.endpoints.FetchMessagesEndpoint +import com.pubnub.internal.endpoints.HistoryEndpoint +import com.pubnub.internal.endpoints.MessageCountsEndpoint +import com.pubnub.internal.endpoints.TimeEndpoint +import com.pubnub.internal.endpoints.access.GrantEndpoint +import com.pubnub.internal.endpoints.access.GrantTokenEndpoint +import com.pubnub.internal.endpoints.access.RevokeTokenEndpoint +import com.pubnub.internal.endpoints.channel_groups.AddChannelChannelGroupEndpoint +import com.pubnub.internal.endpoints.channel_groups.AllChannelsChannelGroupEndpoint +import com.pubnub.internal.endpoints.channel_groups.DeleteChannelGroupEndpoint +import com.pubnub.internal.endpoints.channel_groups.ListAllChannelGroupEndpoint +import com.pubnub.internal.endpoints.channel_groups.RemoveChannelChannelGroupEndpoint +import com.pubnub.internal.endpoints.files.DeleteFileEndpoint +import com.pubnub.internal.endpoints.files.DownloadFileEndpoint +import com.pubnub.internal.endpoints.files.GenerateUploadUrlEndpoint +import com.pubnub.internal.endpoints.files.GetFileUrlEndpoint +import com.pubnub.internal.endpoints.files.ListFilesEndpoint +import com.pubnub.internal.endpoints.files.PublishFileMessageEndpoint +import com.pubnub.internal.endpoints.files.SendFileEndpoint +import com.pubnub.internal.endpoints.files.UploadFileEndpoint +import com.pubnub.internal.endpoints.message_actions.AddMessageActionEndpoint +import com.pubnub.internal.endpoints.message_actions.GetMessageActionsEndpoint +import com.pubnub.internal.endpoints.message_actions.RemoveMessageActionEndpoint +import com.pubnub.internal.endpoints.objects.channel.GetAllChannelMetadataEndpoint +import com.pubnub.internal.endpoints.objects.channel.GetChannelMetadataEndpoint +import com.pubnub.internal.endpoints.objects.channel.RemoveChannelMetadataEndpoint +import com.pubnub.internal.endpoints.objects.channel.SetChannelMetadataEndpoint +import com.pubnub.internal.endpoints.objects.internal.CollectionQueryParameters +import com.pubnub.internal.endpoints.objects.internal.IncludeQueryParam +import com.pubnub.internal.endpoints.objects.member.GetChannelMembersEndpoint +import com.pubnub.internal.endpoints.objects.member.ManageChannelMembersEndpoint +import com.pubnub.internal.endpoints.objects.membership.GetMembershipsEndpoint +import com.pubnub.internal.endpoints.objects.membership.ManageMembershipsEndpoint +import com.pubnub.internal.endpoints.objects.uuid.GetAllUUIDMetadataEndpoint +import com.pubnub.internal.endpoints.objects.uuid.GetUUIDMetadataEndpoint +import com.pubnub.internal.endpoints.objects.uuid.RemoveUUIDMetadataEndpoint +import com.pubnub.internal.endpoints.objects.uuid.SetUUIDMetadataEndpoint +import com.pubnub.internal.endpoints.presence.GetStateEndpoint +import com.pubnub.internal.endpoints.presence.HereNowEndpoint +import com.pubnub.internal.endpoints.presence.SetStateEndpoint +import com.pubnub.internal.endpoints.presence.WhereNowEndpoint +import com.pubnub.internal.endpoints.pubsub.PublishEndpoint +import com.pubnub.internal.endpoints.pubsub.SignalEndpoint +import com.pubnub.internal.endpoints.push.AddChannelsToPushEndpoint +import com.pubnub.internal.endpoints.push.ListPushProvisionsEndpoint +import com.pubnub.internal.endpoints.push.RemoveAllPushChannelsForDeviceEndpoint +import com.pubnub.internal.endpoints.push.RemoveChannelsFromPushEndpoint +import com.pubnub.internal.managers.BasePathManager +import com.pubnub.internal.managers.DuplicationManager +import com.pubnub.internal.managers.ListenerManager +import com.pubnub.internal.managers.MapperManager +import com.pubnub.internal.managers.PublishSequenceManager +import com.pubnub.internal.managers.RetrofitManager +import com.pubnub.internal.managers.TokenManager +import com.pubnub.internal.managers.TokenParser +import com.pubnub.internal.presence.Presence +import com.pubnub.internal.presence.eventengine.data.PresenceData +import com.pubnub.internal.presence.eventengine.effect.effectprovider.HeartbeatProviderImpl +import com.pubnub.internal.presence.eventengine.effect.effectprovider.LeaveProviderImpl +import com.pubnub.internal.subscribe.PRESENCE_CHANNEL_SUFFIX +import com.pubnub.internal.subscribe.Subscribe +import com.pubnub.internal.subscribe.eventengine.configuration.EventEnginesConf import com.pubnub.internal.v2.entities.ChannelGroupImpl import com.pubnub.internal.v2.entities.ChannelGroupName import com.pubnub.internal.v2.entities.ChannelImpl @@ -110,19 +161,84 @@ import com.pubnub.internal.v2.entities.ChannelName import com.pubnub.internal.v2.entities.UserMetadataImpl import com.pubnub.internal.v2.subscription.EmitterHelper import com.pubnub.internal.v2.subscription.SubscriptionImpl +import com.pubnub.internal.v2.subscription.SubscriptionInternal import com.pubnub.internal.v2.subscription.SubscriptionSetImpl +import com.pubnub.internal.workers.SubscribeMessageProcessor import com.pubnub.kmp.CustomObject import java.io.InputStream +import java.util.Date +import java.util.UUID +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService +import kotlin.time.Duration.Companion.seconds private const val PNSDK_PUBNUB_KOTLIN = "PubNub-Kotlin" -class PubNubImpl( - override val configuration: com.pubnub.api.v2.PNConfiguration, -) : BasePubNubImpl( - configuration, - PNSDK_PUBNUB_KOTLIN, - ), - PubNub { +open class PubNubImpl( + override val configuration: PNConfiguration, + val pnsdkName: String = PNSDK_PUBNUB_KOTLIN, + eventEnginesConf: EventEnginesConf = EventEnginesConf() +) : PubNub { + constructor(configuration: PNConfiguration) : this(configuration, PNSDK_PUBNUB_KOTLIN) + + val mapper = MapperManager() + + private val numberOfThreadsInPool = Integer.min(Runtime.getRuntime().availableProcessors(), 8) + internal val executorService: ScheduledExecutorService = Executors.newScheduledThreadPool(numberOfThreadsInPool) + val listenerManager: ListenerManager = ListenerManager(this) + private val basePathManager = BasePathManager(configuration) + internal val retrofitManager = RetrofitManager(this, configuration) + internal val publishSequenceManager = PublishSequenceManager(MAX_SEQUENCE) + internal val tokenManager: TokenManager = TokenManager() + private val tokenParser: TokenParser = TokenParser() + private val presenceData = PresenceData() + private val subscribe = + Subscribe.create( + this, + listenerManager, + eventEnginesConf, + SubscribeMessageProcessor(this, DuplicationManager(configuration)), + presenceData, + configuration.maintainPresenceState, + ) + + private val presence = + Presence.create( + heartbeatProvider = HeartbeatProviderImpl(this), + leaveProvider = LeaveProviderImpl(this), + heartbeatInterval = configuration.heartbeatInterval.seconds, + suppressLeaveEvents = configuration.suppressLeaveEvents, + heartbeatNotificationOptions = configuration.heartbeatNotificationOptions, + listenerManager = listenerManager, + eventEngineConf = eventEnginesConf.presence, + presenceData = presenceData, + sendStateWithHeartbeat = configuration.maintainPresenceState, + executorService = executorService, + ) + + /** + * Unique id of this PubNub instance. + * + * @see [PNConfiguration.includeInstanceIdentifier] + */ + val instanceId = UUID.randomUUID().toString() + + //region Internal + internal fun baseUrl() = basePathManager.basePath() + + internal fun requestId() = UUID.randomUUID().toString() + //endregion + + fun generatePnsdk(): String { + val joinedSuffixes = configuration.pnsdkSuffixes.toSortedMap().values.joinToString(" ") + return "$pnsdkName/$SDK_VERSION" + + if (joinedSuffixes.isNotBlank()) { + " $joinedSuffixes" + } else { + "" + } + } + private val emitterHelper = EmitterHelper(listenerManager) override var onMessage: ((PNMessageResult) -> Unit)? by emitterHelper::onMessage override var onPresence: ((PNPresenceEventResult) -> Unit)? by emitterHelper::onPresence @@ -131,32 +247,69 @@ class PubNubImpl( override var onObjects: ((PNObjectEventResult) -> Unit)? by emitterHelper::onObjects override var onFile: ((PNFileEventResult) -> Unit)? by emitterHelper::onFile + /** + * The current version of the Kotlin SDK. + */ + override val version: String + get() = SDK_VERSION + + override val timestamp: Int + get() = timestamp() + + override val baseUrl: String + get() = baseUrl() + + companion object { + internal const val TIMESTAMP_DIVIDER = 1000 + internal const val SDK_VERSION = PUBNUB_VERSION + internal const val MAX_SEQUENCE = 65535 + + @JvmStatic + fun timestamp() = (Date().time / TIMESTAMP_DIVIDER).toInt() + + /** + * Generates random UUID to use. You should set a unique UUID to identify the user or the device that connects to PubNub. + */ + @JvmStatic + fun generateUUID() = "pn-${UUID.randomUUID()}" + } + + override fun subscriptionSetOf( + channels: Set, + channelGroups: Set, + options: SubscriptionOptions, + ): SubscriptionSet { + val subscriptionSet = subscriptionSetOf(subscriptions = emptySet()) + channels.forEach { + subscriptionSet.add(channel(it).subscription(options)) + } + channelGroups.forEach { + subscriptionSet.add(channelGroup(it).subscription(options)) + } + return subscriptionSet + } + + /** + * Removes all status and event listeners. + */ + override fun removeAllListeners() { + listenerManager.removeAllListeners() + } + override fun addListener(listener: SubscribeCallback) { - listenerManager.addListener(DelegatingSubscribeCallback(listener)) + listenerManager.addListener(listener) } override fun addListener(listener: StatusListener) { - listenerManager.addListener(DelegatingStatusListener(listener)) + listenerManager.addListener(listener) } override fun addListener(listener: EventListener) { - listenerManager.addListener(DelegatingEventListener(listener)) + listenerManager.addListener(listener) } override fun removeListener(listener: Listener) { - when (listener) { - is SubscribeCallback -> { - super.removeListener(DelegatingSubscribeCallback(listener)) - } - - is EventListener -> { - super.removeListener(DelegatingEventListener(listener)) - } - - else -> { - super.removeListener(listener) - } - } + listenerManager.removeListener(listener) } /** @@ -172,7 +325,7 @@ class PubNubImpl( * * @return a [BaseChannel] instance representing the channel with the given [name] */ - override fun channel(name: String): Channel { + override fun channel(name: String): ChannelImpl { return ChannelImpl(this, ChannelName(name)) } @@ -234,65 +387,37 @@ class PubNubImpl( * @return a [BaseSubscriptionSet] containing all [subscriptions] */ override fun subscriptionSetOf(subscriptions: Set): SubscriptionSet { - return SubscriptionSetImpl(pubNubCore, subscriptions as Set) - } - - /** - * Create a [BaseSubscriptionSet] containing [BaseSubscription] objects for the given sets of [channels] and - * [channelGroups]. - * - * Please note that the subscriptions are not active until you call [BaseSubscriptionSet.subscribe]. - * - * This is a convenience method, and it is equal to calling [PubNubImpl.channel] followed by [BaseChannel.subscription] for - * each channel, then creating a [subscriptionSetOf] using the returned [BaseSubscription] objects (and similarly for - * channel groups). - * - * @param channels the channels to create subscriptions for - * @param channelGroups the channel groups to create subscriptions for - * @param options the [SubscriptionOptions] to pass for each subscriptions. Refer to supported options in [BaseChannel] and - * [BaseChannelGroup] documentation. - * @return a [BaseSubscriptionSet] containing subscriptions for the given [channels] and [channelGroups] - */ - override fun subscriptionSetOf( - channels: Set, - channelGroups: Set, - options: SubscriptionOptions, - ): SubscriptionSet { - return super.subscriptionSetOf(channels, channelGroups, options) as SubscriptionSet + return SubscriptionSetImpl(this, subscriptions as Set) } override fun publish( channel: String, message: Any, meta: Any?, - shouldStore: Boolean, + shouldStore: Boolean?, usePost: Boolean, replicate: Boolean, ttl: Int?, - ): Publish { - return com.pubnub.internal.endpoints.pubsub.PublishImpl( - pubNubCore.publish( - channel, - message, - meta, - shouldStore, - usePost, - replicate, - ttl, - ), + ): Publish = + PublishEndpoint( + pubnub = this, + channel = channel, + message = message, + meta = meta, + shouldStore = shouldStore, + usePost = usePost, + replicate = replicate, + ttl = ttl, ) - } - override fun fire(channel: String, message: Any, meta: Any?, usePost: Boolean): Publish { - return com.pubnub.internal.endpoints.pubsub.PublishImpl( - pubNubCore.fire( - channel, - message, - meta, - usePost, - ), - ) - } + override fun fire(channel: String, message: Any, meta: Any?, usePost: Boolean): Publish = publish( + channel = channel, + message = message, + meta = meta, + shouldStore = false, + usePost = usePost, + replicate = false, + ) @Deprecated( "`fire()` never used the `ttl` parameter, please use the version without `ttl`.", @@ -309,49 +434,7 @@ class PubNubImpl( override fun signal( channel: String, message: Any, - ): Signal { - return com.pubnub.internal.endpoints.pubsub.SignalImpl(pubNubCore.signal(channel, message)) - } - - /** - * Unsubscribe from all channels and all channel groups - */ - override fun unsubscribeAll() { - pubNubCore.unsubscribeAll() - } - - override fun subscribe( - channels: List, - channelGroups: List, - withPresence: Boolean, - withTimetoken: Long, - ) { - pubNubCore.subscribe(channels, channelGroups, withPresence, withTimetoken) - } - - /** - * When subscribed to a single channel, this function causes the client to issue a leave from the channel - * and close any open socket to the PubNub Network. - * - * For multiplexed channels, the specified channel(s) will be removed and the socket remains open - * until there are no more channels remaining in the list. - * - * * **WARNING** - * Unsubscribing from all the channel(s) and then subscribing to a new channel Y isn't the same as - * Subscribing to channel Y and then unsubscribing from the previously subscribed channel(s). - * - * Unsubscribing from all the channels resets the timetoken and thus, - * there could be some gaps in the subscriptions that may lead to a message loss. - * - * @param channels Channels to subscribe/unsubscribe. Either `channel` or [channelGroups] are required. - * @param channelGroups Channel groups to subscribe/unsubscribe. Either `channelGroups` or [channels] are required. - */ - override fun unsubscribe( - channels: List, - channelGroups: List, - ) { - pubNubCore.unsubscribe(channels, channelGroups) - } + ): Signal = SignalEndpoint(pubnub = this, channel = channel, message = message) override fun addPushNotificationsOnChannels( pushType: PNPushType, @@ -360,14 +443,13 @@ class PubNubImpl( topic: String?, environment: PNPushEnvironment, ): AddChannelsToPush { - return com.pubnub.internal.endpoints.push.AddChannelsToPushImpl( - pubNubCore.addPushNotificationsOnChannels( - pushType, - channels, - deviceId, - topic, - environment, - ), + return AddChannelsToPushEndpoint( + pubnub = this, + pushType = pushType, + channels = channels, + deviceId = deviceId, + topic = topic, + environment = environment, ) } @@ -377,13 +459,12 @@ class PubNubImpl( topic: String?, environment: PNPushEnvironment, ): ListPushProvisions { - return com.pubnub.internal.endpoints.push.ListPushProvisionsImpl( - pubNubCore.auditPushChannelProvisions( - pushType, - deviceId, - topic, - environment, - ), + return ListPushProvisionsEndpoint( + pubnub = this, + pushType = pushType, + deviceId = deviceId, + topic = topic, + environment = environment, ) } @@ -394,14 +475,13 @@ class PubNubImpl( topic: String?, environment: PNPushEnvironment, ): RemoveChannelsFromPush { - return com.pubnub.internal.endpoints.push.RemoveChannelsFromPushImpl( - pubNubCore.removePushNotificationsFromChannels( - pushType, - channels, - deviceId, - topic, - environment, - ), + return RemoveChannelsFromPushEndpoint( + pubnub = this, + pushType = pushType, + channels = channels, + deviceId = deviceId, + topic = topic, + environment = environment, ) } @@ -411,13 +491,12 @@ class PubNubImpl( topic: String?, environment: PNPushEnvironment, ): RemoveAllPushChannelsForDevice { - return com.pubnub.internal.endpoints.push.RemoveAllPushChannelsForDeviceImpl( - pubNubCore.removeAllPushNotificationsFromDeviceWithPushToken( - pushType, - deviceId, - topic, - environment, - ), + return RemoveAllPushChannelsForDeviceEndpoint( + pubnub = this, + pushType = pushType, + deviceId = deviceId, + topic = topic, + environment = environment, ) } @@ -430,16 +509,15 @@ class PubNubImpl( includeTimetoken: Boolean, includeMeta: Boolean, ): History { - return com.pubnub.internal.endpoints.HistoryImpl( - pubNubCore.history( - channel, - start, - end, - count, - reverse, - includeTimetoken, - includeMeta, - ), + return HistoryEndpoint( + pubnub = this, + channel = channel, + start = start, + end = end, + count = count, + reverse = reverse, + includeTimetoken = includeTimetoken, + includeMeta = includeMeta, ) } @@ -451,15 +529,14 @@ class PubNubImpl( includeMessageActions: Boolean, includeMessageType: Boolean, ): FetchMessages { - return com.pubnub.internal.endpoints.FetchMessagesImpl( - pubNubCore.fetchMessages( - channels, - page, - includeUUID, - includeMeta, - includeMessageActions, - includeMessageType, - ), + return FetchMessagesEndpoint( + pubnub = this, + channels = channels, + page = page, + includeUUID = includeUUID, + includeMeta = includeMeta, + includeMessageActions = includeMessageActions, + includeMessageType = includeMessageType, ) } @@ -468,25 +545,14 @@ class PubNubImpl( start: Long?, end: Long?, ): DeleteMessages { - return com.pubnub.internal.endpoints.DeleteMessagesImpl( - pubNubCore.deleteMessages( - channels, - start, - end, - ), - ) + return DeleteMessagesEndpoint(pubnub = this, channels = channels, start = start, end = end) } override fun messageCounts( channels: List, channelsTimetoken: List, ): MessageCounts { - return com.pubnub.internal.endpoints.MessageCountsImpl( - pubNubCore.messageCounts( - channels, - channelsTimetoken, - ), - ) + return MessageCountsEndpoint(pubnub = this, channels = channels, channelsTimetoken = channelsTimetoken) } override fun hereNow( @@ -495,18 +561,17 @@ class PubNubImpl( includeState: Boolean, includeUUIDs: Boolean, ): HereNow { - return com.pubnub.internal.endpoints.presence.HereNowImpl( - pubNubCore.hereNow( - channels, - channelGroups, - includeState, - includeUUIDs, - ), + return HereNowEndpoint( + pubnub = this, + channels = channels, + channelGroups = channelGroups, + includeState = includeState, + includeUUIDs = includeUUIDs, ) } override fun whereNow(uuid: String): WhereNow { - return com.pubnub.internal.endpoints.presence.WhereNowImpl(pubNubCore.whereNow(uuid)) + return WhereNowEndpoint(pubnub = this, uuid = uuid) } override fun setPresenceState( @@ -515,13 +580,13 @@ class PubNubImpl( state: Any, uuid: String, ): SetState { - return com.pubnub.internal.endpoints.presence.SetStateImpl( - pubNubCore.setPresenceState( - channels, - channelGroups, - state, - uuid, - ), + return SetStateEndpoint( + pubnub = this, + channels = channels, + channelGroups = channelGroups, + state = state, + uuid = uuid, + presenceData = presenceData, ) } @@ -530,13 +595,13 @@ class PubNubImpl( channelGroups: List, state: Any ): SetState { - return com.pubnub.internal.endpoints.presence.SetStateImpl( - pubNubCore.setPresenceState( - channels, - channelGroups, - state, - configuration.userId.value, - ), + return SetStateEndpoint( + pubnub = this, + channels = channels, + channelGroups = channelGroups, + state = state, + uuid = configuration.userId.value, + presenceData = presenceData, ) } @@ -545,25 +610,14 @@ class PubNubImpl( channelGroups: List, uuid: String, ): GetState { - return com.pubnub.internal.endpoints.presence.GetStateImpl( - pubNubCore.getPresenceState( - channels, - channelGroups, - uuid, - ), - ) + return GetStateEndpoint(pubnub = this, channels = channels, channelGroups = channelGroups, uuid = uuid) } override fun addMessageAction( channel: String, messageAction: PNMessageAction, ): AddMessageAction { - return com.pubnub.internal.endpoints.message_actions.AddMessageActionImpl( - pubNubCore.addMessageAction( - channel, - messageAction, - ), - ) + return AddMessageActionEndpoint(pubnub = this, channel = channel, messageAction = messageAction) } override fun removeMessageAction( @@ -571,12 +625,11 @@ class PubNubImpl( messageTimetoken: Long, actionTimetoken: Long, ): RemoveMessageAction { - return com.pubnub.internal.endpoints.message_actions.RemoveMessageActionImpl( - pubNubCore.removeMessageAction( - channel, - messageTimetoken, - actionTimetoken, - ), + return RemoveMessageActionEndpoint( + pubnub = this, + channel = channel, + messageTimetoken = messageTimetoken, + actionTimetoken = actionTimetoken, ) } @@ -584,48 +637,33 @@ class PubNubImpl( channel: String, page: PNBoundedPage, ): GetMessageActions { - return com.pubnub.internal.endpoints.message_actions.GetMessageActionsImpl( - pubNubCore.getMessageActions( - channel, - page, - ), - ) + return GetMessageActionsEndpoint(pubnub = this, channel = channel, page = page) } override fun addChannelsToChannelGroup( channels: List, channelGroup: String, ): AddChannelChannelGroup { - return com.pubnub.internal.endpoints.channel_groups.AddChannelChannelGroupImpl( - pubNubCore.addChannelsToChannelGroup(channels, channelGroup), - ) + return AddChannelChannelGroupEndpoint(pubnub = this, channels = channels, channelGroup = channelGroup) } override fun listChannelsForChannelGroup(channelGroup: String): AllChannelsChannelGroup { - return com.pubnub.internal.endpoints.channel_groups.AllChannelsChannelGroupImpl( - pubNubCore.listChannelsForChannelGroup(channelGroup), - ) + return AllChannelsChannelGroupEndpoint(pubnub = this, channelGroup = channelGroup) } override fun removeChannelsFromChannelGroup( channels: List, channelGroup: String, ): RemoveChannelChannelGroup { - return com.pubnub.internal.endpoints.channel_groups.RemoveChannelChannelGroupImpl( - pubNubCore.removeChannelsFromChannelGroup(channels, channelGroup), - ) + return RemoveChannelChannelGroupEndpoint(pubnub = this, channels = channels, channelGroup = channelGroup) } override fun listAllChannelGroups(): ListAllChannelGroup { - return com.pubnub.internal.endpoints.channel_groups.ListAllChannelGroupImpl(pubNubCore.listAllChannelGroups()) + return ListAllChannelGroupEndpoint(this) } override fun deleteChannelGroup(channelGroup: String): DeleteChannelGroup { - return com.pubnub.internal.endpoints.channel_groups.DeleteChannelGroupImpl( - pubNubCore.deleteChannelGroup( - channelGroup, - ), - ) + return DeleteChannelGroupEndpoint(pubnub = this, channelGroup = channelGroup) } override fun grant( @@ -642,21 +680,20 @@ class PubNubImpl( channelGroups: List, uuids: List, ): Grant = - com.pubnub.internal.endpoints.access.GrantImpl( - pubNubCore.grant( - read, - write, - manage, - delete, - get, - update, - join, - ttl, - authKeys, - channels, - channelGroups, - uuids, - ), + GrantEndpoint( + pubnub = this, + read = read, + write = write, + manage = manage, + delete = delete, + get = get, + update = update, + join = join, + ttl = ttl, + authKeys = authKeys, + channels = channels, + channelGroups = channelGroups, + uuids = uuids, ) override fun grant( @@ -670,18 +707,17 @@ class PubNubImpl( channelGroups: List, uuids: List, ): Grant = - com.pubnub.internal.endpoints.access.GrantImpl( - pubNubCore.grant( - read, - write, - manage, - delete, - ttl, - authKeys, - channels, - channelGroups, - uuids, - ), + GrantEndpoint( + pubnub = this, + read = read, + write = write, + manage = manage, + delete = delete, + ttl = ttl, + authKeys = authKeys, + channels = channels, + channelGroups = channelGroups, + uuids = uuids, ) override fun grantToken( @@ -692,15 +728,14 @@ class PubNubImpl( channelGroups: List, uuids: List, ): GrantToken { - return com.pubnub.internal.endpoints.access.GrantTokenImpl( - pubNubCore.grantToken( - ttl, - meta, - authorizedUUID, - channels.toInternalChannelGrants(), - channelGroups.toInternalChannelGroupGrants(), - uuids.toInternalUuidGrants(), - ), + return GrantTokenEndpoint( + pubnub = this, + ttl = ttl, + meta = meta, + authorizedUUID = authorizedUUID, + channels = channels, + channelGroups = channelGroups, + uuids = uuids, ) } @@ -711,25 +746,26 @@ class PubNubImpl( spacesPermissions: List, usersPermissions: List, ): GrantToken { - return com.pubnub.internal.endpoints.access.GrantTokenImpl( - pubNubCore.grantToken( - ttl, - meta, - authorizedUserId, - spacesPermissions.toInternalSpacePermissions(), - usersPermissions.toInternalUserPermissions(), - ), + return GrantTokenEndpoint( + pubnub = this, + ttl = ttl, + meta = meta, + authorizedUUID = authorizedUserId?.value, + channels = spacesPermissions.map { spacePermissions -> spacePermissions.toChannelGrant() }, + channelGroups = emptyList(), + uuids = usersPermissions.map { userPermissions -> userPermissions.toUuidGrant() }, ) } override fun revokeToken(token: String): RevokeToken { - return com.pubnub.internal.endpoints.access.RevokeTokenImpl( - pubNubCore.revokeToken(token), + return RevokeTokenEndpoint( + pubnub = this, + token = token, ) } override fun time(): Time { - return com.pubnub.internal.endpoints.TimeImpl(pubNubCore.time()) + return TimeEndpoint(this) } override fun getAllChannelMetadata( @@ -740,15 +776,17 @@ class PubNubImpl( includeCount: Boolean, includeCustom: Boolean, ): GetAllChannelMetadata { - return com.pubnub.internal.endpoints.objects.channel.GetAllChannelMetadataImpl( - pubNubCore.getAllChannelMetadata( - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - ), + return GetAllChannelMetadataEndpoint( + pubnub = this, + collectionQueryParameters = + CollectionQueryParameters( + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + ), + includeQueryParam = IncludeQueryParam(includeCustom = includeCustom), ) } @@ -756,8 +794,10 @@ class PubNubImpl( channel: String, includeCustom: Boolean, ): GetChannelMetadata { - return com.pubnub.internal.endpoints.objects.channel.GetChannelMetadataImpl( - pubNubCore.getChannelMetadata(channel, includeCustom), + return GetChannelMetadataEndpoint( + pubnub = this, + channel = channel, + includeQueryParam = IncludeQueryParam(includeCustom = includeCustom), ) } @@ -770,15 +810,20 @@ class PubNubImpl( type: String?, status: String?, ): SetChannelMetadata { - return com.pubnub.internal.endpoints.objects.channel.SetChannelMetadataImpl( - pubNubCore.setChannelMetadata(channel, name, description, custom, includeCustom, type, status), + return SetChannelMetadataEndpoint( + pubnub = this, + channel = channel, + name = name, + description = description, + custom = custom, + includeQueryParam = IncludeQueryParam(includeCustom = includeCustom), + type = type, + status = status, ) } override fun removeChannelMetadata(channel: String): RemoveChannelMetadata { - return com.pubnub.internal.endpoints.objects.channel.RemoveChannelMetadataImpl( - pubNubCore.removeChannelMetadata(channel), - ) + return RemoveChannelMetadataEndpoint(this, channel = channel) } override fun getAllUUIDMetadata( @@ -789,15 +834,17 @@ class PubNubImpl( includeCount: Boolean, includeCustom: Boolean, ): GetAllUUIDMetadata { - return com.pubnub.internal.endpoints.objects.uuid.GetAllUUIDMetadataImpl( - pubNubCore.getAllUUIDMetadata( - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - ), + return GetAllUUIDMetadataEndpoint( + pubnub = this, + collectionQueryParameters = + CollectionQueryParameters( + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + ), + withInclude = IncludeQueryParam(includeCustom = includeCustom), ) } @@ -805,8 +852,10 @@ class PubNubImpl( uuid: String?, includeCustom: Boolean, ): GetUUIDMetadata { - return com.pubnub.internal.endpoints.objects.uuid.GetUUIDMetadataImpl( - pubNubCore.getUUIDMetadata(uuid, includeCustom), + return GetUUIDMetadataEndpoint( + pubnub = this, + uuid = uuid ?: configuration.userId.value, + includeQueryParam = IncludeQueryParam(includeCustom = includeCustom), ) } @@ -821,25 +870,22 @@ class PubNubImpl( type: String?, status: String?, ): SetUUIDMetadata { - return com.pubnub.internal.endpoints.objects.uuid.SetUUIDMetadataImpl( - pubNubCore.setUUIDMetadata( - uuid, - name, - externalId, - profileUrl, - email, - custom, - includeCustom, - type, - status, - ), + return SetUUIDMetadataEndpoint( + pubnub = this, + uuid = uuid, + name = name, + externalId = externalId, + profileUrl = profileUrl, + email = email, + custom = custom, + withInclude = IncludeQueryParam(includeCustom = includeCustom), + type = type, + status = status, ) } override fun removeUUIDMetadata(uuid: String?): RemoveUUIDMetadata { - return com.pubnub.internal.endpoints.objects.uuid.RemoveUUIDMetadataImpl( - pubNubCore.removeUUIDMetadata(uuid), - ) + return RemoveUUIDMetadataEndpoint(pubnub = this, uuid = uuid) } override fun getMemberships( @@ -853,18 +899,23 @@ class PubNubImpl( includeChannelDetails: PNChannelDetailsLevel?, includeType: Boolean, ): GetMemberships { - return com.pubnub.internal.endpoints.objects.membership.GetMembershipsImpl( - pubNubCore.getMemberships( - uuid, - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - includeChannelDetails.toInternal(), - includeType - ), + return GetMembershipsEndpoint( + pubnub = this, + uuid = uuid ?: configuration.userId.value, + collectionQueryParameters = + CollectionQueryParameters( + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + ), + includeQueryParam = + IncludeQueryParam( + includeCustom = includeCustom, + includeChannelDetails = includeChannelDetails, + includeChannelType = includeType, + ), ) } @@ -879,22 +930,19 @@ class PubNubImpl( includeCustom: Boolean, includeChannelDetails: PNChannelDetailsLevel?, includeType: Boolean, - ): ManageMemberships { - return com.pubnub.internal.endpoints.objects.membership.ManageMembershipsImpl( - pubNubCore.setMemberships( - channels.toInternalChannelMemberships(), - uuid, - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - includeChannelDetails.toInternal(), - includeType - ), - ) - } + ): ManageMemberships = manageMemberships( + channelsToSet = channels, + channelsToRemove = listOf(), + uuid = uuid, + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + includeCustom = includeCustom, + includeChannelDetails = includeChannelDetails, + includeType = includeType + ) override fun removeMemberships( channels: List, @@ -907,22 +955,19 @@ class PubNubImpl( includeCustom: Boolean, includeChannelDetails: PNChannelDetailsLevel?, includeType: Boolean, - ): ManageMemberships { - return com.pubnub.internal.endpoints.objects.membership.ManageMembershipsImpl( - pubNubCore.removeMemberships( - channels, - uuid, - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - includeChannelDetails.toInternal(), - includeType - ), - ) - } + ): ManageMemberships = manageMemberships( + channelsToSet = listOf(), + channelsToRemove = channels, + uuid = uuid, + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + includeCustom = includeCustom, + includeChannelDetails = includeChannelDetails, + includeType = includeType, + ) override fun manageMemberships( channelsToSet: List, @@ -937,20 +982,25 @@ class PubNubImpl( includeChannelDetails: PNChannelDetailsLevel?, includeType: Boolean, ): ManageMemberships { - return com.pubnub.internal.endpoints.objects.membership.ManageMembershipsImpl( - pubNubCore.manageMemberships( - channelsToSet.toInternalChannelMemberships(), - channelsToRemove, - uuid, - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - includeChannelDetails.toInternal(), - includeType - ), + return ManageMembershipsEndpoint( + pubnub = this, + channelsToSet = channelsToSet, + channelsToRemove = channelsToRemove, + uuid = uuid ?: configuration.userId.value, + collectionQueryParameters = + CollectionQueryParameters( + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + ), + includeQueryParam = + IncludeQueryParam( + includeCustom = includeCustom, + includeChannelDetails = includeChannelDetails, + includeChannelType = includeType, + ), ) } @@ -965,18 +1015,23 @@ class PubNubImpl( includeUUIDDetails: PNUUIDDetailsLevel?, includeType: Boolean, ): GetChannelMembers { - return com.pubnub.internal.endpoints.objects.member.GetChannelMembersImpl( - pubNubCore.getChannelMembers( - channel, - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - includeUUIDDetails.toInternal(), - includeType - ), + return GetChannelMembersEndpoint( + pubnub = this, + channel = channel, + collectionQueryParameters = + CollectionQueryParameters( + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + ), + includeQueryParam = + IncludeQueryParam( + includeCustom = includeCustom, + includeUUIDDetails = includeUUIDDetails, + includeUuidType = includeType, + ), ) } @@ -999,19 +1054,14 @@ class PubNubImpl( includeMeta: Boolean, includeMessageActions: Boolean, includeMessageType: Boolean, - ): FetchMessages { - return com.pubnub.internal.endpoints.FetchMessagesImpl( - pubNubCore.fetchMessages( - channels, - maximumPerChannel, - start, - end, - includeMeta, - includeMessageActions, - includeMessageType, - ), - ) - } + ): FetchMessages = fetchMessages( + channels = channels, + page = PNBoundedPage(start = start, end = end, limit = maximumPerChannel), + includeUUID = true, + includeMeta = includeMeta, + includeMessageActions = includeMessageActions, + includeMessageType = includeMessageType + ) @Deprecated( replaceWith = @@ -1028,9 +1078,7 @@ class PubNubImpl( end: Long?, limit: Int?, ): GetMessageActions { - return com.pubnub.internal.endpoints.message_actions.GetMessageActionsImpl( - pubNubCore.getMessageActions(channel, start, end, limit), - ) + return getMessageActions(channel = channel, page = PNBoundedPage(start = start, end = end, limit = limit)) } @Deprecated( @@ -1053,21 +1101,17 @@ class PubNubImpl( includeCount: Boolean, includeCustom: Boolean, includeChannelDetails: PNChannelDetailsLevel?, - ): ManageMemberships { - return com.pubnub.internal.endpoints.objects.membership.ManageMembershipsImpl( - pubNubCore.addMemberships( - channels.toInternalChannelMemberships(), - uuid, - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - includeChannelDetails.toInternal(), - ), - ) - } + ): ManageMemberships = setMemberships( + channels = channels, + uuid = uuid ?: configuration.userId.value, + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + includeCustom = includeCustom, + includeChannelDetails = includeChannelDetails, + ) @Deprecated( "Use getChannelMembers instead", @@ -1088,20 +1132,16 @@ class PubNubImpl( includeCount: Boolean, includeCustom: Boolean, includeUUIDDetails: PNUUIDDetailsLevel?, - ): GetChannelMembers { - return com.pubnub.internal.endpoints.objects.member.GetChannelMembersImpl( - pubNubCore.getMembers( - channel, - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - includeUUIDDetails.toInternal(), - ), - ) - } + ): GetChannelMembers = getChannelMembers( + channel = channel, + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + includeCustom = includeCustom, + includeUUIDDetails = includeUUIDDetails, + ) @Deprecated( "Use setChannelMembers instead", @@ -1123,21 +1163,17 @@ class PubNubImpl( includeCount: Boolean, includeCustom: Boolean, includeUUIDDetails: PNUUIDDetailsLevel?, - ): ManageChannelMembers { - return com.pubnub.internal.endpoints.objects.member.ManageChannelMembersImpl( - pubNubCore.addMembers( - channel, - uuids.toInternalMemberInputs(), - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - includeUUIDDetails.toInternal(), - ), - ) - } + ): ManageChannelMembers = setChannelMembers( + channel = channel, + uuids = uuids, + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + includeCustom = includeCustom, + includeUUIDDetails = includeUUIDDetails, + ) override fun setChannelMembers( channel: String, @@ -1150,22 +1186,19 @@ class PubNubImpl( includeCustom: Boolean, includeUUIDDetails: PNUUIDDetailsLevel?, includeType: Boolean, - ): ManageChannelMembers { - return com.pubnub.internal.endpoints.objects.member.ManageChannelMembersImpl( - pubNubCore.setChannelMembers( - channel, - uuids.toInternalMemberInputs(), - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - includeUUIDDetails.toInternal(), - includeType - ), - ) - } + ): ManageChannelMembers = manageChannelMembers( + channel = channel, + uuidsToSet = uuids, + uuidsToRemove = listOf(), + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + includeCustom = includeCustom, + includeUUIDDetails = includeUUIDDetails, + includeUUIDType = includeType + ) @Deprecated( "Use removeChannelMembers instead", @@ -1187,21 +1220,17 @@ class PubNubImpl( includeCount: Boolean, includeCustom: Boolean, includeUUIDDetails: PNUUIDDetailsLevel?, - ): ManageChannelMembers { - return com.pubnub.internal.endpoints.objects.member.ManageChannelMembersImpl( - pubNubCore.removeMembers( - channel, - uuids, - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - includeUUIDDetails.toInternal(), - ), - ) - } + ): ManageChannelMembers = removeChannelMembers( + channel = channel, + uuids = uuids, + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + includeCustom = includeCustom, + includeUUIDDetails = includeUUIDDetails, + ) override fun removeChannelMembers( channel: String, @@ -1214,21 +1243,19 @@ class PubNubImpl( includeCustom: Boolean, includeUUIDDetails: PNUUIDDetailsLevel?, includeType: Boolean, - ): ManageChannelMembers { - return com.pubnub.internal.endpoints.objects.member.ManageChannelMembersImpl( - pubNubCore.removeChannelMembers( - channel, - uuids, - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - includeUUIDDetails.toInternal(), - ), - ) - } + ): ManageChannelMembers = manageChannelMembers( + channel = channel, + uuidsToSet = listOf(), + uuidsToRemove = uuids, + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + includeCustom = includeCustom, + includeUUIDDetails = includeUUIDDetails, + includeUUIDType = includeType, + ) override fun manageChannelMembers( channel: String, @@ -1241,20 +1268,27 @@ class PubNubImpl( includeCount: Boolean, includeCustom: Boolean, includeUUIDDetails: PNUUIDDetailsLevel?, + includeUUIDType: Boolean, ): ManageChannelMembers { - return com.pubnub.internal.endpoints.objects.member.ManageChannelMembersImpl( - pubNubCore.manageChannelMembers( - channel, - uuidsToSet.toInternalMemberInputs(), - uuidsToRemove, - limit, - page, - filter, - sort.toInternalSortKeys(), - includeCount, - includeCustom, - includeUUIDDetails.toInternal(), - ), + return ManageChannelMembersEndpoint( + pubnub = this, + channel = channel, + uuidsToSet = uuidsToSet, + uuidsToRemove = uuidsToRemove, + collectionQueryParameters = + CollectionQueryParameters( + limit = limit, + page = page, + filter = filter, + sort = sort, + includeCount = includeCount, + ), + includeQueryParam = + IncludeQueryParam( + includeCustom = includeCustom, + includeUUIDDetails = includeUUIDDetails, + includeUuidType = includeUUIDType, + ), ) } @@ -1268,17 +1302,28 @@ class PubNubImpl( shouldStore: Boolean?, cipherKey: String?, ): SendFile { - return com.pubnub.internal.endpoints.files.SendFileImpl( - pubNubCore.sendFile( - channel, - fileName, - inputStream, - message, - meta, - ttl, - shouldStore, - cipherKey, - ), + val cryptoModule = + if (cipherKey != null) { + CryptoModule.createLegacyCryptoModule(cipherKey) + } else { + configuration.cryptoModule + } + return SendFileEndpoint( + channel = channel, + fileName = fileName, + inputStream = inputStream, + message = message, + meta = meta, + ttl = ttl, + shouldStore = shouldStore, + executorService = + retrofitManager.getTransactionClientExecutorService() + ?: Executors.newSingleThreadExecutor(), + fileMessagePublishRetryLimit = configuration.fileMessagePublishRetryLimit, + generateUploadUrlFactory = GenerateUploadUrlEndpoint.Factory(this), + publishFileMessageFactory = PublishFileMessageEndpoint.Factory(this), + sendFileToS3Factory = UploadFileEndpoint.Factory(this), + cryptoModule = cryptoModule, ) } @@ -1287,8 +1332,11 @@ class PubNubImpl( limit: Int?, next: PNPage.PNNext?, ): ListFiles { - return com.pubnub.internal.endpoints.files.ListFilesImpl( - pubNubCore.listFiles(channel, limit, next), + return ListFilesEndpoint( + pubNub = this, + channel = channel, + limit = limit, + next = next, ) } @@ -1297,8 +1345,11 @@ class PubNubImpl( fileName: String, fileId: String, ): GetFileUrl { - return com.pubnub.internal.endpoints.files.GetFileUrlImpl( - pubNubCore.getFileUrl(channel, fileName, fileId), + return GetFileUrlEndpoint( + pubNub = this, + channel = channel, + fileName = fileName, + fileId = fileId, ) } @@ -1308,8 +1359,18 @@ class PubNubImpl( fileId: String, cipherKey: String?, ): DownloadFile { - return com.pubnub.internal.endpoints.files.DownloadFileImpl( - pubNubCore.downloadFile(channel, fileName, fileId, cipherKey), + val cryptoModule = + if (cipherKey != null) { + CryptoModule.createLegacyCryptoModule(cipherKey) + } else { + configuration.cryptoModule + } + return DownloadFileEndpoint( + pubNub = this, + channel = channel, + fileName = fileName, + fileId = fileId, + cryptoModule = cryptoModule, ) } @@ -1318,8 +1379,11 @@ class PubNubImpl( fileName: String, fileId: String, ): DeleteFile { - return com.pubnub.internal.endpoints.files.DeleteFileImpl( - pubNubCore.deleteFile(channel, fileName, fileId), + return DeleteFileEndpoint( + pubNub = this, + channel = channel, + fileName = fileName, + fileId = fileId, ) } @@ -1332,111 +1396,392 @@ class PubNubImpl( ttl: Int?, shouldStore: Boolean?, ): PublishFileMessage { - return com.pubnub.internal.endpoints.files.PublishFileMessageImpl( - pubNubCore.publishFileMessage(channel, fileName, fileId, message, meta, ttl, shouldStore), + return PublishFileMessageEndpoint( + pubNub = this, + channel = channel, + fileName = fileName, + fileId = fileId, + message = message, + meta = meta, + ttl = ttl, + shouldStore = shouldStore, ) } - /** - * Queries the local subscribe loop for channels currently in the mix. - * - * @return A list of channels the client is currently subscribed to. - */ - override fun getSubscribedChannels(): List = pubNubCore.getSubscribedChannels() + override fun getSubscribedChannels() = subscribe.getSubscribedChannels() - /** - * Queries the local subscribe loop for channel groups currently in the mix. - * - * @return A list of channel groups the client is currently subscribed to. - */ - override fun getSubscribedChannelGroups(): List = pubNubCore.getSubscribedChannelGroups() + override fun getSubscribedChannelGroups() = subscribe.getSubscribedChannelGroups() - /** - * Track the online and offline status of users and devices in real time and store custom state information. - * When you have Presence enabled, PubNub automatically creates a presence channel for each channel. - * - * Subscribing to a presence channel or presence channel group will only return presence events - * - * @param channels Channels to subscribe/unsubscribe. Either `channel` or [channelGroups] are required. - * @param channelGroups Channel groups to subscribe/unsubscribe. Either `channelGroups` or [channels] are required. - */ override fun presence( channels: List, channelGroups: List, connected: Boolean, - ) = pubNubCore.presence(channels, channelGroups, connected) + ) = presence.presence( + channels = channels.toSet(), + channelGroups = channelGroups.toSet(), + connected = connected, + ) private fun getCryptoModuleOrThrow(cipherKey: String? = null): CryptoModule { - return cipherKey?.let { cipherKeyNotNull -> - (configuration as? PNConfiguration)?.let { - CryptoModule.createLegacyCryptoModule(cipherKeyNotNull, it.useRandomInitializationVector) - } ?: CryptoModule.createLegacyCryptoModule(cipherKeyNotNull) - } ?: configuration.cryptoModule ?: throw PubNubException("Crypto module is not initialized") + return cipherKey?.let { cipherKeyNotNull -> CryptoModule.createLegacyCryptoModule(cipherKeyNotNull) } + ?: configuration.cryptoModule ?: throw PubNubException("Crypto module is not initialized") } - /** - * Perform Cryptographic decryption of an input string using cipher key provided by [PNConfiguration.cipherKey]. - * - * @param inputString String to be decrypted. - * - * @return String containing the decryption of `inputString` using `cipherKey`. - * @throws PubNubException throws exception in case of failed decryption. - */ - override fun decrypt(inputString: String): String = pubNubCore.decrypt(inputString) + @Throws(PubNubException::class) + fun decrypt( + inputString: String, + cryptoModule: CryptoModule? = null, + ): String = getCryptoModuleOrThrow(cryptoModule).decryptString(inputString) + + @Throws(PubNubException::class) + override fun decrypt(inputString: String): String = decrypt(inputString, cipherKey = null) - /** - * Perform Cryptographic decryption of an input string using a cipher key. - * - * @param inputString String to be decrypted. - * @param cipherKey cipher key to be used for decryption. Default is [PNConfiguration.cipherKey] - * - * @return String containing the decryption of `inputString` using `cipherKey`. - * @throws PubNubException throws exception in case of failed decryption. - */ override fun decrypt( inputString: String, cipherKey: String?, - ): String = pubNubCore.decrypt(inputString, getCryptoModuleOrThrow(cipherKey)) + ): String = decrypt(inputString, getCryptoModuleOrThrow(cipherKey)) - /** - * Perform Cryptographic decryption of an input stream using provided cipher key. - * - * @param inputStream InputStream to be encrypted. - * @param cipherKey Cipher key to be used for decryption. - * - * @return InputStream containing the encryption of `inputStream` using `cipherKey`. - * @throws PubNubException Throws exception in case of failed decryption. - */ override fun decryptInputStream( inputStream: InputStream, cipherKey: String?, - ): InputStream = pubNubCore.decryptInputStream(inputStream, getCryptoModuleOrThrow(cipherKey)) + ): InputStream = decryptInputStream(inputStream, getCryptoModuleOrThrow(cipherKey)) + + private fun decryptInputStream( + inputStream: InputStream, + cryptoModule: CryptoModule? = null, + ): InputStream = getCryptoModuleOrThrow(cryptoModule).decryptStream(inputStream) + + override fun encrypt( + inputString: String, + cipherKey: String?, + ): String = encrypt(inputString, getCryptoModuleOrThrow(cipherKey)) + + @Throws(PubNubException::class) + private fun encrypt( + inputString: String, + cryptoModule: CryptoModule? = null, + ): String = getCryptoModuleOrThrow(cryptoModule).encryptString(inputString) + + override fun encryptInputStream( + inputStream: InputStream, + cipherKey: String?, + ): InputStream = encryptInputStream(inputStream, getCryptoModuleOrThrow(cipherKey)) + + @Throws(PubNubException::class) + private fun encryptInputStream( + inputStream: InputStream, + cryptoModule: CryptoModule? = null, + ): InputStream = getCryptoModuleOrThrow(cryptoModule).encryptStream(inputStream) + + private fun subscribeInternal( + channels: List = emptyList(), + channelGroups: List = emptyList(), + withPresence: Boolean = false, + withTimetoken: Long = 0L, + ) { + subscribe.subscribe(channels.toSet(), channelGroups.toSet(), withPresence, withTimetoken) + if (!configuration.managePresenceListManually) { + presence.joined( + channels.filterNot { it.endsWith(PRESENCE_CHANNEL_SUFFIX) }.toSet(), + channelGroups.filterNot { it.endsWith(PRESENCE_CHANNEL_SUFFIX) }.toSet(), + ) + } + } + + private fun unsubscribeInternal( + channels: List = emptyList(), + channelGroups: List = emptyList(), + ) { + val channelSetWithoutPresence = channels.filter { !it.endsWith(PRESENCE_CHANNEL_SUFFIX) }.toSet() + val groupSetWithoutPresence = channelGroups.filter { !it.endsWith(PRESENCE_CHANNEL_SUFFIX) }.toSet() + subscribe.unsubscribe(channelSetWithoutPresence, groupSetWithoutPresence) + if (!configuration.managePresenceListManually) { + presence.left(channelSetWithoutPresence, groupSetWithoutPresence) + } + } + + override fun reconnect(timetoken: Long) { + subscribe.reconnect(timetoken) + presence.reconnect() + } + + override fun disconnect() { + subscribe.disconnect() + presence.disconnect() + } + + override fun destroy() { + subscribe.destroy() + presence.destroy() + + retrofitManager.destroy() + executorService.shutdown() + } + + /** + * Same as [destroy] but immediately. + */ + override fun forceDestroy() { + subscribe.destroy() + presence.destroy() + + retrofitManager.destroy(true) + executorService.shutdownNow() + } + + override fun parseToken(token: String): PNToken { + return tokenParser.unwrapToken(token) + } + + override fun setToken(token: String?) { + return tokenManager.setToken(token) + } + + // internal + private val lockChannelsAndGroups = Any() + private val channelSubscriptions = mutableMapOf>() + private val channelGroupSubscriptions = mutableMapOf>() + + internal fun subscribe( + vararg subscriptions: SubscriptionInternal, + cursor: SubscriptionCursor, + ) { + synchronized(lockChannelsAndGroups) { + val channelsToSubscribe = mutableSetOf() + subscriptions.forEach { subscription -> + subscription.channels.forEach { channelName -> + channelSubscriptions.computeIfAbsent(channelName) { mutableSetOf() } + .also { set -> set.add(subscription) } + channelsToSubscribe.add(channelName) + } + } + val groupsToSubscribe = mutableSetOf() + subscriptions.forEach { subscription -> + subscription.channelGroups.forEach { channelGroupName -> + channelGroupSubscriptions.computeIfAbsent(channelGroupName) { mutableSetOf() } + .also { set -> set.add(subscription) } + groupsToSubscribe.add(channelGroupName) + } + } + + val (channelsWithPresence, channelsNoPresence) = + channelsToSubscribe.filter { !it.isPresence } + .partition { + channelsToSubscribe.contains(it.withPresence) + } + val (groupsWithPresence, groupsNoPresence) = + groupsToSubscribe.filter { !it.isPresence }.partition { + groupsToSubscribe.contains(it.withPresence) + } + if (channelsWithPresence.isNotEmpty() || groupsWithPresence.isNotEmpty()) { + subscribeInternal( + channels = channelsWithPresence.map(ChannelName::id), + channelGroups = groupsWithPresence.map(ChannelGroupName::id), + withPresence = true, + withTimetoken = cursor.timetoken, + ) + } + if (channelsNoPresence.isNotEmpty() || groupsNoPresence.isNotEmpty()) { + subscribeInternal( + channels = channelsNoPresence.map(ChannelName::id), + channelGroups = groupsNoPresence.map(ChannelGroupName::id), + withPresence = false, + withTimetoken = cursor.timetoken, + ) + } + } + } + + internal fun unsubscribe(vararg subscriptions: SubscriptionInternal) { + synchronized(lockChannelsAndGroups) { + val channelsToUnsubscribe = mutableSetOf() + subscriptions.forEach { subscription -> + subscription.channels.forEach { channelName -> + val set = channelSubscriptions[channelName] + set?.remove(subscription) + if (set != null && set.isEmpty()) { // there were mappings but there none now + channelsToUnsubscribe += channelName + channelSubscriptions.remove(channelName) + } + } + } + + val groupsToUnsubscribe = mutableSetOf() + subscriptions.forEach { subscription -> + subscription.channelGroups.forEach { channelGroupName -> + val set = channelGroupSubscriptions[channelGroupName] + set?.remove(subscription) + if (set != null && set.isEmpty()) { + groupsToUnsubscribe += channelGroupName + channelGroupSubscriptions.remove(channelGroupName) + } + } + } + if (channelsToUnsubscribe.isNotEmpty() || groupsToUnsubscribe.isNotEmpty()) { + unsubscribeInternal( + channels = channelsToUnsubscribe.map(ChannelName::id), + channelGroups = groupsToUnsubscribe.map(ChannelGroupName::id), + ) + } + } + } + + private val channelSubscriptionMap = mutableMapOf() + private val channelGroupSubscriptionMap = mutableMapOf() + + //region Subscribe /** - * Perform Cryptographic encryption of an input string and a cipher key. + * Causes the client to create an open TCP socket to the PubNub Real-Time Network and begin listening for messages + * on a specified channel. + * + * To subscribe to a channel the client must send the appropriate [PNConfiguration.subscribeKey] at initialization. + * + * By default, a newly subscribed client will only receive messages published to the channel + * after the `subscribe()` call completes. * - * @param inputString String to be encrypted. - * @param cipherKey Cipher key to be used for encryption. Default is [PNConfiguration.cipherKey] + * If a client gets disconnected from a channel, it can automatically attempt to reconnect to that channel + * and retrieve any available messages that were missed during that period. + * This can be achieved by setting [PNConfiguration.retryConfiguration] when + * initializing the client. * - * @return String containing the encryption of `inputString` using `cipherKey`. - * @throws PubNubException Throws exception in case of failed encryption. + * @param channels Channels to subscribe/unsubscribe. Either `channel` or [channelGroups] are required. + * @param channelGroups Channel groups to subscribe/unsubscribe. Either `channelGroups` or [channels] are required. + * @param withPresence Also subscribe to related presence channel. + * @param withTimetoken A timetoken to start the subscribe loop from. */ - override fun encrypt( - inputString: String, - cipherKey: String?, - ): String = pubNubCore.encrypt(inputString, getCryptoModuleOrThrow(cipherKey)) + @Synchronized + override fun subscribe( + channels: List, + channelGroups: List, + withPresence: Boolean, + withTimetoken: Long, + ) { + val toSubscribe = mutableSetOf() + channels.filter { it.isNotEmpty() }.map { ChannelName(it) }.forEach { channelName -> + // if we are adding a NEW subscriptions in this step, this var will contain it: + var subscription: SubscriptionImpl? = null + channelSubscriptionMap.computeIfAbsent(channelName) { newChannelName -> + val channel = + ChannelImpl( + this, + newChannelName + ) + val options = + if (withPresence) { + SubscriptionOptions.receivePresenceEvents() + } else { + EmptyOptions + } + channel.subscription(options).also { sub -> + toSubscribe.add(sub) + subscription = sub + } + } + // make sure we are also subscribed and tracking the -pnpres channel if withPresence==true + if (withPresence) { + channelSubscriptionMap.computeIfAbsent(channelName.withPresence) { presenceChannelName -> + // this will either be the subscriptions we just created in the previous step, + // or if we were already subscribed to the channel WITHOUT presence, we need to create a new one + subscription ?: ChannelImpl( + this, + presenceChannelName + ).subscription().also { sub -> + toSubscribe.add(sub) + } + } + } + } + channelGroups.filter { it.isNotEmpty() }.map { ChannelGroupName(it) }.forEach { channelGroupName -> + var subscription: SubscriptionImpl? = null + + channelGroupSubscriptionMap.computeIfAbsent(channelGroupName) { newChannelGroupName -> + val channelGroup = ChannelGroupImpl(this, newChannelGroupName) + val options = + if (withPresence) { + SubscriptionOptions.receivePresenceEvents() + } else { + EmptyOptions + } + channelGroup.subscription(options).also { sub -> + toSubscribe.add(sub) + subscription = sub + } + } + // make sure we are also subscribed and tracking the -pnpres channel if withPresence==true + if (withPresence) { + channelGroupSubscriptionMap.computeIfAbsent(channelGroupName.withPresence) { presenceGroupName -> + // this will either be the subscriptions we just created in the previous step, + // or if we were already subscribed to the channel WITHOUT presence, we need to create a new one + subscription ?: ChannelGroupImpl(this, presenceGroupName) + .subscription().also { sub -> + toSubscribe.add(sub) + } + } + } + } + + // actually subscribe to all subscriptions created in this function and added to the set + subscribe(*toSubscribe.toTypedArray(), cursor = SubscriptionCursor(withTimetoken)) + } /** - * Perform Cryptographic encryption of an input stream using provided cipher key. + * When subscribed to a single channel, this function causes the client to issue a leave from the channel + * and close any open socket to the PubNub Network. + * + * For multiplexed channels, the specified channel(s) will be removed and the socket remains open + * until there are no more channels remaining in the list. + * + * * **WARNING** + * Unsubscribing from all the channel(s) and then subscribing to a new channel Y isn't the same as + * Subscribing to channel Y and then unsubscribing from the previously subscribed channel(s). * - * @param inputStream InputStream to be encrypted. - * @param cipherKey Cipher key to be used for encryption. + * Unsubscribing from all the channels resets the timetoken and thus, + * there could be some gaps in the subscriptions that may lead to a message loss. * - * @return InputStream containing the encryption of `inputStream` using `cipherKey`. - * @throws PubNubException Throws exception in case of failed encryption. + * @param channels Channels to subscribe/unsubscribe. Either `channel` or [channelGroups] are required. + * @param channelGroups Channel groups to subscribe/unsubscribe. Either `channelGroups` or [channels] are required. */ - override fun encryptInputStream( - inputStream: InputStream, - cipherKey: String?, - ): InputStream = pubNubCore.encryptInputStream(inputStream, getCryptoModuleOrThrow(cipherKey)) + @Synchronized + override fun unsubscribe( + channels: List, + channelGroups: List, + ) { + val toUnsubscribe: MutableSet = mutableSetOf() + channels.filter { it.isNotEmpty() }.map { ChannelName(it) }.forEach { channelName -> + channelSubscriptionMap.remove(channelName)?.let { sub -> + toUnsubscribe.add(sub) + } + channelSubscriptionMap.remove(channelName.withPresence)?.let { sub -> + toUnsubscribe.add(sub) + } + } + channelGroups.filter { it.isNotEmpty() }.map { ChannelGroupName(it) }.forEach { groupName -> + channelGroupSubscriptionMap.remove(groupName)?.let { sub -> + toUnsubscribe.add(sub) + } + channelGroupSubscriptionMap.remove(groupName.withPresence)?.let { sub -> + toUnsubscribe.add(sub) + } + } + unsubscribe(*toUnsubscribe.toTypedArray()) + } + + /** + * Unsubscribe from all channels and all channel groups + */ + @Synchronized + override fun unsubscribeAll() { + synchronized(lockChannelsAndGroups) { + channelSubscriptions.clear() + channelGroupSubscriptions.clear() + subscribe.unsubscribeAll() + presence.leftAll() + } + } + + @Throws(PubNubException::class) + private fun getCryptoModuleOrThrow(cryptoModule: CryptoModule? = null): CryptoModule { + return cryptoModule ?: configuration.cryptoModule ?: throw PubNubException("Crypto module is not initialized") + } } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubRetryableException.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubRetryableException.kt new file mode 100644 index 000000000..ac5d1294a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubRetryableException.kt @@ -0,0 +1,9 @@ +package com.pubnub.internal + +import com.pubnub.api.PubNubError + +internal data class PubNubRetryableException( + val errorMessage: String? = null, + val pubnubError: PubNubError? = null, + val statusCode: Int = 0, +) : Exception(errorMessage) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubUtil.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubUtil.kt new file mode 100644 index 000000000..cf128d2bc --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/PubNubUtil.kt @@ -0,0 +1,269 @@ +package com.pubnub.internal + +import com.pubnub.api.PubNubException +import com.pubnub.api.v2.PNConfiguration +import com.pubnub.api.v2.PNConfiguration.Companion.isValid +import okhttp3.Request +import okio.Buffer +import org.slf4j.LoggerFactory +import java.io.IOException +import java.io.UnsupportedEncodingException +import java.net.URLDecoder +import java.net.URLEncoder +import java.nio.charset.Charset +import java.security.InvalidKeyException +import java.security.NoSuchAlgorithmException +import java.util.Locale +import java.util.TreeSet +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec + +internal object PubNubUtil { + private val log = LoggerFactory.getLogger("PubNubUtil") + + private const val CHARSET = "UTF-8" + const val SIGNATURE_QUERY_PARAM_NAME = "signature" + const val TIMESTAMP_QUERY_PARAM_NAME = "timestamp" + const val AUTH_QUERY_PARAM_NAME = "auth" + + fun replaceLast( + string: String, + toReplace: String, + replacement: String, + ): String { + val pos = string.lastIndexOf(toReplace) + return if (pos > -1) { + string.substring(0, pos) + replacement + + string.substring( + pos + toReplace.length, + string.length, + ) + } else { + string + } + } + + /** + * Returns decoded String + * + * @param stringToEncode , input string + * @return , decoded string + */ + fun urlDecode(stringToEncode: String?): String? { + return try { + URLDecoder.decode(stringToEncode, CHARSET) + } catch (e: UnsupportedEncodingException) { + null + } + } + + fun signRequest( + originalRequest: Request, + pnConfiguration: PNConfiguration, + timestamp: Int, + ): Request { + // only sign if we have a secret key in place. + if (!pnConfiguration.secretKey.isValid()) { + return originalRequest + } + val signature = generateSignature( + originalRequest, + timestamp, + pnConfiguration.subscribeKey, + pnConfiguration.publishKey, + pnConfiguration.secretKey, + ) + val rebuiltUrl = + originalRequest.url.newBuilder() + .addQueryParameter("timestamp", timestamp.toString()) + .addQueryParameter("signature", signature) + .build() + return originalRequest.newBuilder().url(rebuiltUrl).build() + } + + fun shouldSignRequest(configuration: PNConfiguration): Boolean { + return configuration.secretKey.isValid() + } + + fun generateSignature( + requestURL: String, + queryParams: MutableMap, + method: String, + requestBody: String?, + timestamp: Int, + subscribeKey: String, + publishKey: String, + secretKey: String, + ): String { + val signatureBuilder = StringBuilder() + queryParams["timestamp"] = timestamp.toString() + + val classic = true + val encodedQueryString = + if (classic) { + preparePamArguments(queryParams) + } else { + preparePamArguments("$requestURL×tamp=$timestamp") + } + + val isV2Signature: Boolean = !(requestURL.startsWith("/publish") && method.equals("post", ignoreCase = true)) + if (!isV2Signature) { + signatureBuilder.append(subscribeKey).append("\n") + signatureBuilder.append(publishKey).append("\n") + signatureBuilder.append(requestURL).append("\n") + signatureBuilder.append(encodedQueryString) + } else { + signatureBuilder.append(method.uppercase(Locale.getDefault())).append("\n") + signatureBuilder.append(publishKey).append("\n") + signatureBuilder.append(requestURL).append("\n") + signatureBuilder.append(encodedQueryString).append("\n") + signatureBuilder.append(requestBody) + } + + var signature = "" + try { + signature = signSHA256(secretKey, signatureBuilder.toString()) + if (isV2Signature) { + signature = removeTrailingEqualSigns(signature) + signature = "v2.$signature" + } + } catch (e: PubNubException) { + log.warn("signature failed on SignatureInterceptor: $e") + } catch (e: UnsupportedEncodingException) { + log.warn("signature failed on SignatureInterceptor: $e") + } + return signature + } + + internal fun signSHA256( + key: String, + data: String, + ): String { + val sha256HMAC: Mac + val hmacData: ByteArray + val secretKey = SecretKeySpec(key.toByteArray(charset(CHARSET)), "HmacSHA256") + sha256HMAC = + try { + Mac.getInstance("HmacSHA256") + } catch (e: NoSuchAlgorithmException) { + throw com.pubnub.internal.vendor.Crypto.newCryptoError(0, e) + } + try { + sha256HMAC.init(secretKey) + } catch (e: InvalidKeyException) { + throw com.pubnub.internal.vendor.Crypto.newCryptoError(0, e) + } + hmacData = sha256HMAC.doFinal(data.toByteArray(charset(CHARSET))) + + val signed = + String( + com.pubnub.internal.vendor.Base64.encode(hmacData, com.pubnub.internal.vendor.Base64.NO_WRAP), + Charset.forName(CHARSET), + ) + .replace('+', '-') + .replace('/', '_') + return signed + } + + private fun generateSignature( + request: Request, + timestamp: Int, + subscribeKey: String, + publishKey: String, + secretKey: String, + ): String { + val queryParams: MutableMap = mutableMapOf() + for (queryKey: String in request.url.queryParameterNames) { + val value = request.url.queryParameter(queryKey) + if (value != null) { + queryParams[queryKey] = value + } + } + return generateSignature( + request.url.encodedPath, + queryParams, + request.method, + requestBodyToString(request), + timestamp, + subscribeKey, + publishKey, + secretKey, + ) + } + + fun removeTrailingEqualSigns(signature: String): String { + var cleanSignature = signature + while (cleanSignature[cleanSignature.length - 1] == '=') { + cleanSignature = cleanSignature.substring(0, cleanSignature.length - 1) + } + return cleanSignature + } + + internal fun requestBodyToString(request: Request): String? { + if (request.body == null) { + return "" + } + try { + val buffer = Buffer() + request.body!!.writeTo(buffer) + return buffer.readUtf8() + } catch (e: IOException) { + e.printStackTrace() + } + return "" + } + + internal fun preparePamArguments(pamArgs: Map): String { + val pamKeys: Set = TreeSet(pamArgs.keys) + var stringifiedArguments = "" + var i = 0 + for (pamKey in pamKeys) { + if (i != 0) { + stringifiedArguments = "$stringifiedArguments&" + } + stringifiedArguments = stringifiedArguments + pamKey + "=" + pamEncode(pamArgs[pamKey]!!) + i += 1 + } + return stringifiedArguments + } + + private fun preparePamArguments(encodedQueryString: String): String { + return encodedQueryString.split("&") + .toSortedSet() + .map { pamEncode(it, true) } + .joinToString("&") + } + + /** + * Returns encoded String + * + * @param stringToEncode , input string + * @return , encoded string + */ + internal fun pamEncode( + stringToEncode: String, + alreadyPercentEncoded: Boolean = false, + ): String { + // !'()*~ + + return if (alreadyPercentEncoded) { + stringToEncode + } else { + URLEncoder.encode(stringToEncode, "UTF-8") + .replace("+", "%20") + }.run { + replace("*", "%2A") + } + } + + internal fun maybeAddEeQueryParam(queryParams: MutableMap) { + queryParams["ee"] = "" + } +} + +internal fun List.toCsv(): String { + if (this.isNotEmpty()) { + return this.joinToString(",") + } + return "," +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/SubscriptionFactory.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/SubscriptionFactory.kt new file mode 100644 index 000000000..a14068837 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/SubscriptionFactory.kt @@ -0,0 +1,11 @@ +package com.pubnub.internal + +import com.pubnub.api.v2.subscriptions.SubscriptionOptions +import com.pubnub.internal.v2.entities.ChannelGroupName +import com.pubnub.internal.v2.entities.ChannelName + +typealias SubscriptionFactory = ( + channels: Set, + channelGroups: Set, + options: SubscriptionOptions, +) -> T diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/callbacks/ReconnectionCallback.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/callbacks/ReconnectionCallback.kt new file mode 100644 index 000000000..c978a810d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/callbacks/ReconnectionCallback.kt @@ -0,0 +1,7 @@ +package com.pubnub.internal.callbacks + +internal abstract class ReconnectionCallback { + abstract fun onReconnection() + + abstract fun onMaxReconnectionExhaustion() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/CryptoModuleImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/CryptoModuleImpl.kt new file mode 100644 index 000000000..04390fd15 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/CryptoModuleImpl.kt @@ -0,0 +1,196 @@ +package com.pubnub.internal.crypto + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.api.crypto.cryptor.Cryptor +import com.pubnub.api.crypto.data.EncryptedData +import com.pubnub.api.crypto.data.EncryptedStreamData +import com.pubnub.internal.crypto.cryptor.HeaderParser +import com.pubnub.internal.crypto.cryptor.LEGACY_CRYPTOR_ID +import com.pubnub.internal.crypto.cryptor.ParseResult +import java.io.BufferedInputStream +import java.io.InputStream +import java.io.SequenceInputStream + +private const val SIZE_OF_CRYPTOR_ID = 4 + +class CryptoModuleImpl internal constructor( + @get:JvmSynthetic + internal val primaryCryptor: Cryptor, + @get:JvmSynthetic + internal val cryptorsForDecryptionOnly: List = listOf(), +) : CryptoModule { + private val headerParser: HeaderParser = HeaderParser() + + override fun encrypt(data: ByteArray): ByteArray { + val cryptorId = primaryCryptor.id() + validateData(data) + validateCryptorIdSize(cryptorId) + val (metadata, encryptedData) = primaryCryptor.encrypt(data) + + return if (cryptorId.contentEquals(LEGACY_CRYPTOR_ID)) { + encryptedData + } else { + val cryptorHeader = headerParser.createCryptorHeader(cryptorId, metadata) + cryptorHeader + encryptedData + } + } + + override fun decrypt(encryptedData: ByteArray): ByteArray { + validateData(encryptedData) + val parsedData: ParseResult = headerParser.parseDataWithHeader(encryptedData) + val decryptedData: ByteArray = + when (parsedData) { + is ParseResult.NoHeader -> { + getDecryptedDataForLegacyCryptor(encryptedData) + } + + is ParseResult.Success -> { + getDecryptedDataForCryptorWithHeader(parsedData) + } + } + return decryptedData + } + + override fun encryptStream(stream: InputStream): InputStream { + val bufferedInputStream = validateStreamAndReturnBuffered(stream) + val (metadata, encryptedData) = primaryCryptor.encryptStream(bufferedInputStream) + return if (primaryCryptor.id().contentEquals(LEGACY_CRYPTOR_ID)) { + encryptedData + } else { + val cryptorHeader: ByteArray = headerParser.createCryptorHeader(primaryCryptor.id(), metadata) + SequenceInputStream(cryptorHeader.inputStream(), encryptedData) + } + } + + override fun decryptStream(encryptedData: InputStream): InputStream { + val bufferedInputStream = validateStreamAndReturnBuffered(encryptedData) + return when (val parsedHeader = headerParser.parseDataWithHeader(bufferedInputStream)) { + ParseResult.NoHeader -> { + val decryptor = cryptorsForDecryptionOnly.firstOrNull { it.id().contentEquals(LEGACY_CRYPTOR_ID) } + decryptor?.decryptStream(EncryptedStreamData(stream = bufferedInputStream)) ?: throw PubNubException( + errorMessage = "LegacyCryptor not registered", + pubnubError = PubNubError.UNKNOWN_CRYPTOR, + ) + } + + is ParseResult.Success -> { + val decryptor = + cryptorsForDecryptionOnly.first { + it.id().contentEquals(parsedHeader.cryptoId) + } + decryptor.decryptStream( + EncryptedStreamData( + metadata = parsedHeader.cryptorData, + stream = parsedHeader.encryptedData, + ), + ) + } + } + } + + private fun validateCryptorIdSize(cryptorId: ByteArray) { + if (cryptorId.size != SIZE_OF_CRYPTOR_ID) { + throw PubNubException( + errorMessage = "CryptorId should be exactly 4 bytes long", + pubnubError = PubNubError.UNKNOWN_CRYPTOR, + ) + } + } + + private fun getDecryptedDataForLegacyCryptor(encryptedData: ByteArray): ByteArray { + return getCryptorById(LEGACY_CRYPTOR_ID)?.decrypt(EncryptedData(data = encryptedData)) ?: throw PubNubException( + errorMessage = "LegacyCryptor not available", + pubnubError = PubNubError.UNKNOWN_CRYPTOR, + ) + } + + private fun getDecryptedDataForCryptorWithHeader(parsedHeader: ParseResult.Success): ByteArray { + val decryptedData: ByteArray + val cryptorId = parsedHeader.cryptoId + val cryptorData = parsedHeader.cryptorData + val pureEncryptedData = parsedHeader.encryptedData + val cryptor = getCryptorById(cryptorId) + decryptedData = + cryptor?.decrypt(EncryptedData(cryptorData, pureEncryptedData)) + ?: throw PubNubException(errorMessage = "No cryptor found", pubnubError = PubNubError.UNKNOWN_CRYPTOR) + return decryptedData + } + + private fun getCryptorById(cryptorId: ByteArray): Cryptor? { + validateCryptorIdSize(cryptorId) + return cryptorsForDecryptionOnly.firstOrNull { it.id().contentEquals(cryptorId) } + } + + private fun validateData(data: ByteArray) { + if (data.isEmpty()) { + throw PubNubException( + errorMessage = "Encryption/Decryption of empty data not allowed.", + pubnubError = PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, + ) + } + } + + private fun validateStreamAndReturnBuffered(stream: InputStream): BufferedInputStream { + val bufferedInputStream = stream.buffered() + bufferedInputStream.checkMinSize(1) { + throw PubNubException( + errorMessage = "Encryption/Decryption of empty data not allowed.", + pubnubError = PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, + ) + } + return bufferedInputStream + } +} + +internal fun CryptoModule.encryptString(inputString: String): String = + String( + com.pubnub.internal.vendor.Base64.encode( + encrypt(inputString.toByteArray()), + com.pubnub.internal.vendor.Base64.NO_WRAP, + ), + ) + +internal fun CryptoModule.decryptString(inputString: String): String = + decrypt(com.pubnub.internal.vendor.Base64.decode(inputString, com.pubnub.internal.vendor.Base64.NO_WRAP)).toString( + Charsets.UTF_8, + ) + +// this method read data from stream and allows to read them again in subsequent reads without manual reset or repositioning +internal fun BufferedInputStream.checkMinSize( + size: Int, + exceptionBlock: (Int) -> Unit, +) { + mark(size + 1) + + val readBytes = readNBytez(size) + reset() + if (readBytes.size < size) { + exceptionBlock(size) + } +} + +internal fun BufferedInputStream.readExactlyNBytez( + size: Int, + exceptionBlock: (Int) -> Unit, +): ByteArray { + val readBytes = readNBytez(size) + if (readBytes.size < size) { + exceptionBlock(size) + } + return readBytes +} + +internal fun InputStream.readNBytez(len: Int): ByteArray { + var remaining: Int = len + var n: Int + val originalArray = ByteArray(remaining) + var nread = 0 + + while (read(originalArray, nread, Integer.min(originalArray.size - nread, remaining)).also { n = it } > 0) { + nread += n + remaining -= n + } + return originalArray.copyOf(nread) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/AesCbcCryptor.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/AesCbcCryptor.kt new file mode 100644 index 000000000..24341f1d6 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/AesCbcCryptor.kt @@ -0,0 +1,132 @@ +package com.pubnub.internal.crypto.cryptor + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.crypto.cryptor.Cryptor +import com.pubnub.api.crypto.data.EncryptedData +import com.pubnub.api.crypto.data.EncryptedStreamData +import com.pubnub.internal.crypto.checkMinSize +import java.io.BufferedInputStream +import java.io.InputStream +import java.security.MessageDigest +import java.security.SecureRandom +import javax.crypto.Cipher +import javax.crypto.CipherInputStream +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +private const val CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding" +private const val RANDOM_IV_SIZE = 16 + +class AesCbcCryptor(val cipherKey: String) : Cryptor { + private val newKey: SecretKeySpec = createNewKey() + + override fun id(): ByteArray { + return byteArrayOf('A'.code.toByte(), 'C'.code.toByte(), 'R'.code.toByte(), 'H'.code.toByte()) + } + + override fun encrypt(data: ByteArray): EncryptedData { + validateData(data) + return try { + val ivBytes: ByteArray = createRandomIv() + val cipher = createInitializedCipher(ivBytes, Cipher.ENCRYPT_MODE) + val encryptedData: ByteArray = cipher.doFinal(data) + EncryptedData(metadata = ivBytes, data = encryptedData) + } catch (e: Exception) { + throw PubNubException(errorMessage = e.message, pubnubError = PubNubError.CRYPTO_ERROR) + } + } + + override fun decrypt(encryptedData: EncryptedData): ByteArray { + validateData(encryptedData.data) + return try { + val ivBytes: ByteArray = + encryptedData.metadata?.takeIf { it.size == RANDOM_IV_SIZE } + ?: throw PubNubException(errorMessage = "Invalid random IV", pubnubError = PubNubError.CRYPTO_ERROR) + val cipher = createInitializedCipher(ivBytes, Cipher.DECRYPT_MODE) + val decryptedData = cipher.doFinal(encryptedData.data) + decryptedData + } catch (e: Exception) { + throw PubNubException(errorMessage = e.message, pubnubError = PubNubError.CRYPTO_ERROR) + } + } + + override fun encryptStream(stream: InputStream): EncryptedStreamData { + val bufferedInputStream = validateInputStreamAndReturnBuffered(stream) + try { + val ivBytes: ByteArray = createRandomIv() + val cipher = createInitializedCipher(ivBytes, Cipher.ENCRYPT_MODE) + val cipheredStream = CipherInputStream(bufferedInputStream, cipher) + + return EncryptedStreamData( + metadata = ivBytes, + stream = cipheredStream, + ) + } catch (e: Exception) { + throw PubNubException(e.message, PubNubError.CRYPTO_ERROR) + } + } + + override fun decryptStream(encryptedData: EncryptedStreamData): InputStream { + val bufferedInputStream = validateInputStreamAndReturnBuffered(encryptedData.stream) + try { + val ivBytes: ByteArray = + encryptedData.metadata?.takeIf { it.size == RANDOM_IV_SIZE } + ?: throw PubNubException(errorMessage = "Invalid random IV", pubnubError = PubNubError.CRYPTO_ERROR) + val cipher = createInitializedCipher(ivBytes, Cipher.DECRYPT_MODE) + return CipherInputStream(bufferedInputStream, cipher) + } catch (e: Exception) { + throw PubNubException(errorMessage = e.message, pubnubError = PubNubError.CRYPTO_ERROR) + } + } + + private fun validateData(data: ByteArray) { + if (data.isEmpty()) { + throw PubNubException( + errorMessage = "Encryption/Decryption of empty data not allowed.", + pubnubError = PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, + ) + } + } + + private fun createInitializedCipher( + iv: ByteArray, + mode: Int, + ): Cipher { + return Cipher.getInstance(CIPHER_TRANSFORMATION).also { + it.init(mode, newKey, IvParameterSpec(iv)) + } + } + + private fun createNewKey(): SecretKeySpec { + val keyBytes = sha256(cipherKey.toByteArray(Charsets.UTF_8)) + return SecretKeySpec(keyBytes, "AES") + } + + private fun createRandomIv(): ByteArray { + val ivBytes = ByteArray(RANDOM_IV_SIZE) + SecureRandom().nextBytes(ivBytes) + return ivBytes + } + + private fun sha256(input: ByteArray): ByteArray { + val digest: MessageDigest + return try { + digest = MessageDigest.getInstance("SHA-256") + digest.digest(input) + } catch (e: java.lang.Exception) { + throw PubNubException(errorMessage = e.message, pubnubError = PubNubError.CRYPTO_ERROR) + } + } + + private fun validateInputStreamAndReturnBuffered(stream: InputStream): BufferedInputStream { + val bufferedInputStream = stream.buffered() + bufferedInputStream.checkMinSize(1) { + throw PubNubException( + errorMessage = "Encryption/Decryption of empty data not allowed.", + pubnubError = PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, + ) + } + return bufferedInputStream + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/CryptorHeader.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/CryptorHeader.kt new file mode 100644 index 000000000..75875823a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/CryptorHeader.kt @@ -0,0 +1,51 @@ +package com.pubnub.internal.crypto.cryptor + +internal class CryptorHeader( + val sentinel: ByteArray, // 4 bytes + val version: Byte, // 1 byte + val cryptorId: ByteArray, // 4 bytes + val cryptorDataSize: ByteArray, // 1 or 3 bytes + val cryptorData: ByteArray, // 0-65535 bytes +) { + fun toByteArray(): ByteArray { + return sentinel + version + cryptorId + cryptorDataSize + cryptorData + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (javaClass != other?.javaClass) { + return false + } + + other as CryptorHeader + + if (!sentinel.contentEquals(other.sentinel)) { + return false + } + if (version != other.version) { + return false + } + if (!cryptorId.contentEquals(other.cryptorId)) { + return false + } + if (!cryptorDataSize.contentEquals(other.cryptorDataSize)) { + return false + } + if (!cryptorData.contentEquals(other.cryptorData)) { + return false + } + + return true + } + + override fun hashCode(): Int { + var result = sentinel.contentHashCode() + result = 31 * result + version + result = 31 * result + cryptorId.contentHashCode() + result = 31 * result + cryptorDataSize.contentHashCode() + result = 31 * result + cryptorData.contentHashCode() + return result + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/CryptorHeaderVersion.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/CryptorHeaderVersion.kt new file mode 100644 index 000000000..9bb4a9323 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/CryptorHeaderVersion.kt @@ -0,0 +1,12 @@ +package com.pubnub.internal.crypto.cryptor + +internal enum class CryptorHeaderVersion(val value: Int) { + One(1), + ; + + companion object { + fun fromValue(value: Int): CryptorHeaderVersion? { + return values().find { it.value == value } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/HeaderParser.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/HeaderParser.kt new file mode 100644 index 000000000..032c59c3b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/HeaderParser.kt @@ -0,0 +1,203 @@ +package com.pubnub.internal.crypto.cryptor + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.internal.crypto.readExactlyNBytez +import org.slf4j.LoggerFactory +import java.io.BufferedInputStream +import java.io.InputStream + +private val SENTINEL = "PNED".toByteArray() +private const val STARTING_INDEX_OF_ONE_BYTE_CRYPTOR_DATA_SIZE = 10 +private const val STARTING_INDEX_OF_THREE_BYTES_CRYPTOR_DATA_SIZE = 12 +private const val MINIMAL_SIZE_OF_DATA_HAVING_CRYPTOR_HEADER = 10 +private const val THREE_BYTES_SIZE_CRYPTOR_DATA_INDICATOR: UByte = 255U + +private const val SENTINEL_STARTING_INDEX = 0 +private const val SENTINEL_ENDING_INDEX = 3 +private const val VERSION_INDEX = 4 +private const val CRYPTOR_ID_STARTING_INDEX = 5 +private const val CRYPTOR_ID_ENDING_INDEX = 8 +private const val CRYPTOR_DATA_SIZE_STARTING_INDEX = 9 +private const val THREE_BYTES_CRYPTOR_DATA_SIZE_STARTING_INDEX = 10 +private const val THREE_BYTES_CRYPTOR_DATA_SIZE_ENDING_INDEX = 11 +private const val MAX_VALUE_THAT_CAN_BE_STORED_ON_TWO_BYTES = 65535 +private const val MINIMAL_SIZE_OF_CRYPTO_HEADER = 10 + +internal class HeaderParser { + private val log = LoggerFactory.getLogger(HeaderParser::class.java) + + fun parseDataWithHeader(stream: BufferedInputStream): ParseResult { + val bufferedInputStream = stream.buffered() + bufferedInputStream.mark(Int.MAX_VALUE) // TODO Can be calculated from spec + val possibleInitialHeader = ByteArray(MINIMAL_SIZE_OF_CRYPTO_HEADER) + val initiallyRead = bufferedInputStream.read(possibleInitialHeader) + if (!possibleInitialHeader.sliceArray(SENTINEL_STARTING_INDEX..SENTINEL_ENDING_INDEX).contentEquals(SENTINEL)) { + bufferedInputStream.reset() + return ParseResult.NoHeader + } + + if (initiallyRead < MINIMAL_SIZE_OF_DATA_HAVING_CRYPTOR_HEADER) { + throw PubNubException( + errorMessage = "Minimal size of Cryptor Data Header is: $MINIMAL_SIZE_OF_DATA_HAVING_CRYPTOR_HEADER", + pubnubError = PubNubError.CRYPTOR_HEADER_PARSE_ERROR, + ) + } + + validateCryptorHeaderVersion(possibleInitialHeader) + val cryptorId = possibleInitialHeader.sliceArray(CRYPTOR_ID_STARTING_INDEX..CRYPTOR_ID_ENDING_INDEX) + val cryptorDataSizeFirstByte = possibleInitialHeader[CRYPTOR_DATA_SIZE_STARTING_INDEX].toUByte() + + val cryptorData: ByteArray = + if (cryptorDataSizeFirstByte == THREE_BYTES_SIZE_CRYPTOR_DATA_INDICATOR) { + val cryptorDataSizeBytes = readExactlyNBytez(bufferedInputStream, 2) + val cryptorDataSize = convertTwoBytesToIntBigEndian(cryptorDataSizeBytes[0], cryptorDataSizeBytes[1]) + readExactlyNBytez(bufferedInputStream, cryptorDataSize) + } else { + if (cryptorDataSizeFirstByte == UByte.MIN_VALUE) { + byteArrayOf() + } else { + readExactlyNBytez(bufferedInputStream, cryptorDataSizeFirstByte.toInt()) + } + } + return ParseResult.Success(cryptorId, cryptorData, bufferedInputStream) + } + + private fun readExactlyNBytez( + bufferedInputStream: BufferedInputStream, + numberOfBytesToRead: Int, + ) = bufferedInputStream.readExactlyNBytez(numberOfBytesToRead) { n -> + throw PubNubException(errorMessage = "Couldn't read $n bytes") + } + + fun parseDataWithHeader(data: ByteArray): ParseResult { + if (data.size < SENTINEL.size) { + return ParseResult.NoHeader + } + val sentinel = data.sliceArray(SENTINEL_STARTING_INDEX..SENTINEL_ENDING_INDEX) + if (!SENTINEL.contentEquals(sentinel)) { + return ParseResult.NoHeader + } + + if (data.size < MINIMAL_SIZE_OF_DATA_HAVING_CRYPTOR_HEADER) { + throw PubNubException( + errorMessage = + "Minimal size of encrypted data having Cryptor Data Header is: $MINIMAL_SIZE_OF_DATA_HAVING_CRYPTOR_HEADER", + pubnubError = PubNubError.CRYPTOR_DATA_HEADER_SIZE_TO_SMALL, + ) + } + + validateCryptorHeaderVersion(data) + + val cryptorId = data.sliceArray(CRYPTOR_ID_STARTING_INDEX..CRYPTOR_ID_ENDING_INDEX) + log.trace("CryptoId: ${String(cryptorId, Charsets.UTF_8)}") + + val cryptorDataSizeFirstByte: Byte = data[CRYPTOR_DATA_SIZE_STARTING_INDEX] + val (startingIndexOfCryptorData, cryptorDataSize) = + getCryptorDataSizeAndStartingIndex( + data, + cryptorDataSizeFirstByte, + ) + + if (startingIndexOfCryptorData + cryptorDataSize > data.size) { + throw PubNubException( + errorMessage = "Input data size: ${data.size} is to small to fit header of size $startingIndexOfCryptorData and cryptorData of size: $cryptorDataSize", + pubnubError = PubNubError.CRYPTOR_HEADER_PARSE_ERROR, + ) + } + val cryptorData = + data.sliceArray(startingIndexOfCryptorData until (startingIndexOfCryptorData + cryptorDataSize)) + val sizeOfCryptorHeader = startingIndexOfCryptorData + cryptorDataSize + val encryptedData = data.sliceArray(sizeOfCryptorHeader until data.size) + + return ParseResult.Success(cryptorId, cryptorData, encryptedData) + } + + fun createCryptorHeader( + cryptorId: ByteArray, + cryptorData: ByteArray?, + ): ByteArray { + val sentinel: ByteArray = SENTINEL + val cryptorHeaderVersion: Byte = getCurrentCryptoHeaderVersion() + val cryptorDataSize: Int = cryptorData?.size ?: 0 + val finalCryptorDataSize: ByteArray = + if (cryptorDataSize < THREE_BYTES_SIZE_CRYPTOR_DATA_INDICATOR.toInt()) { + byteArrayOf(cryptorDataSize.toByte()) // cryptorDataSize will be stored on 1 byte + } else if (cryptorDataSize < MAX_VALUE_THAT_CAN_BE_STORED_ON_TWO_BYTES) { + // cryptorDataSize will be stored on 3 byte + byteArrayOf(cryptorDataSize.toByte()) + writeNumberOnTwoBytes(cryptorDataSize) + } else { + throw PubNubException( + errorMessage = + "Cryptor Data Size is: $cryptorDataSize whereas " + + "max cryptor data size is: $MAX_VALUE_THAT_CAN_BE_STORED_ON_TWO_BYTES", + pubnubError = PubNubError.CRYPTOR_HEADER_PARSE_ERROR, + ) + } + + val cryptorHeader = + CryptorHeader(sentinel, cryptorHeaderVersion, cryptorId, finalCryptorDataSize, cryptorData ?: byteArrayOf()) + return cryptorHeader.toByteArray() + } + + private fun getCurrentCryptoHeaderVersion(): Byte { + return CryptorHeaderVersion.One.value.toByte() + } + + private fun getCryptorDataSizeAndStartingIndex( + data: ByteArray, + cryptorDataSizeFirstByte: Byte, + ): Pair { + val startingIndexOfCryptorData: Int + val cryptorDataSize: Int + val cryptoDataFirstByteAsUByte: UByte = cryptorDataSizeFirstByte.toUByte() + + if (cryptoDataFirstByteAsUByte == THREE_BYTES_SIZE_CRYPTOR_DATA_INDICATOR) { + startingIndexOfCryptorData = STARTING_INDEX_OF_THREE_BYTES_CRYPTOR_DATA_SIZE + log.trace("\"Cryptor data size\" first byte's value is 255 that mean that size is stored on two next bytes") + val cryptorDataSizeSecondByte = data[THREE_BYTES_CRYPTOR_DATA_SIZE_STARTING_INDEX] + val cryptorDataSizeThirdByte = data[THREE_BYTES_CRYPTOR_DATA_SIZE_ENDING_INDEX] + cryptorDataSize = convertTwoBytesToIntBigEndian(cryptorDataSizeSecondByte, cryptorDataSizeThirdByte) + } else { + startingIndexOfCryptorData = STARTING_INDEX_OF_ONE_BYTE_CRYPTOR_DATA_SIZE + cryptorDataSize = cryptoDataFirstByteAsUByte.toInt() + log.trace("\"Cryptor data size\" is 1 byte long and its value is: $cryptorDataSize") + } + return Pair(startingIndexOfCryptorData, cryptorDataSize) + } + + private fun validateCryptorHeaderVersion(data: ByteArray) { + val version: UByte = data[VERSION_INDEX].toUByte() // 5th byte + val versionAsInt = version.toInt() + log.trace("Cryptor header version is: $versionAsInt") + // check if version exist in this SDK version + CryptorHeaderVersion.fromValue(versionAsInt) + ?: throw PubNubException( + errorMessage = "Cryptor header version unknown. Please, update SDK", + pubnubError = PubNubError.CRYPTOR_HEADER_VERSION_UNKNOWN, + ) + } + + private fun convertTwoBytesToIntBigEndian( + byte1: Byte, + byte2: Byte, + ): Int { + return ((byte1.toInt() and 0xFF) shl 8) or (byte2.toInt() and 0xFF) + } + + private fun writeNumberOnTwoBytes(number: Int): ByteArray { + val result = ByteArray(2) + + result[0] = (number shr 8).toByte() + result[1] = number.toByte() + + return result + } +} + +internal sealed class ParseResult { + data class Success(val cryptoId: ByteArray, val cryptorData: ByteArray, val encryptedData: T) : + ParseResult() + + object NoHeader : ParseResult() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/InputStreamSeparator.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/InputStreamSeparator.kt new file mode 100644 index 000000000..50f946fbe --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/InputStreamSeparator.kt @@ -0,0 +1,45 @@ +package com.pubnub.internal.crypto.cryptor + +import java.io.InputStream + +/** This class is used to separate the inputStream from the CipherInputStream. + * We might want to separate the inputStream from the CipherInputStream because we want to be able to close the + * CipherInputStream without closing the inputStream. + * */ +internal class InputStreamSeparator(private val inputStream: InputStream) : InputStream() { + override fun read(): Int { + return inputStream.read() + } + + override fun read(b: ByteArray): Int { + return inputStream.read(b) + } + + override fun read( + b: ByteArray, + off: Int, + len: Int, + ): Int { + return inputStream.read(b, off, len) + } + + override fun skip(n: Long): Long { + return inputStream.skip(n) + } + + override fun available(): Int { + return inputStream.available() + } + + override fun mark(readlimit: Int) { + inputStream.mark(readlimit) + } + + override fun reset() { + inputStream.reset() + } + + override fun markSupported(): Boolean { + return inputStream.markSupported() + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/LegacyCryptor.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/LegacyCryptor.kt new file mode 100644 index 000000000..1accad056 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/crypto/cryptor/LegacyCryptor.kt @@ -0,0 +1,224 @@ +package com.pubnub.internal.crypto.cryptor + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.crypto.cryptor.Cryptor +import com.pubnub.api.crypto.data.EncryptedData +import com.pubnub.api.crypto.data.EncryptedStreamData +import com.pubnub.internal.crypto.checkMinSize +import java.io.BufferedInputStream +import java.io.InputStream +import java.io.SequenceInputStream +import java.io.UnsupportedEncodingException +import java.security.MessageDigest +import java.security.SecureRandom +import java.util.Locale +import javax.crypto.Cipher +import javax.crypto.CipherInputStream +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +private const val STATIC_IV = "0123456789012345" +private const val CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding" + +@get:JvmSynthetic +internal val LEGACY_CRYPTOR_ID = ByteArray(4) { 0.toByte() } + +private const val IV_SIZE = 16 +private const val SIZE_OF_ONE_BLOCK_OF_ENCRYPTED_DATA = 16 +private const val RANDOM_IV_STARTING_INDEX = 0 +private const val RANDOM_IV_ENDING_INDEX = 15 +private const val ENCRYPTED_DATA_STARTING_INDEX = 16 // this is when useRandomIv = true + +internal class LegacyCryptor(val cipherKey: String, val useRandomIv: Boolean = true) : Cryptor { + private val newKey: SecretKeySpec = createNewKey() + + override fun id(): ByteArray { + return LEGACY_CRYPTOR_ID // it was agreed that legacy PN Cryptor will have 0 as ID + } + + override fun encrypt(data: ByteArray): EncryptedData { + validateData(data) + return try { + val ivBytes: ByteArray = getIvBytesForEncryption() + val cipher = createInitializedCipher(ivBytes, Cipher.ENCRYPT_MODE) + val encrypted: ByteArray = cipher.doFinal(data) + if (useRandomIv) { + EncryptedData( + data = ivBytes + encrypted, + ) + } else { + EncryptedData( + data = encrypted, + ) + } + } catch (e: Exception) { + throw PubNubException(errorMessage = e.message, pubnubError = PubNubError.CRYPTO_ERROR) + } + } + + override fun decrypt(encryptedData: EncryptedData): ByteArray { + validateData(encryptedData) + return try { + val ivBytes: ByteArray = getIvBytesForDecryption(encryptedData) + val cipher = createInitializedCipher(ivBytes, Cipher.DECRYPT_MODE) + val encryptedDataForProcessing = getEncryptedDataForProcessing(encryptedData) + val decryptedData = cipher.doFinal(encryptedDataForProcessing) + decryptedData + } catch (e: Exception) { + throw PubNubException(errorMessage = e.message, pubnubError = PubNubError.CRYPTO_ERROR) + } + } + + override fun encryptStream(stream: InputStream): EncryptedStreamData { + val bufferedInputStream = validateStreamAndReturnBuffered(stream) + try { + val ivBytes: ByteArray = createRandomIv() + val cipher = createInitializedCipher(ivBytes, Cipher.ENCRYPT_MODE) + val cipheredStream = CipherInputStream(bufferedInputStream, cipher) + return EncryptedStreamData(stream = SequenceInputStream(ivBytes.inputStream(), cipheredStream)) + } catch (e: Exception) { + throw PubNubException(errorMessage = e.message, pubnubError = PubNubError.CRYPTO_ERROR) + } + } + + override fun decryptStream(encryptedData: EncryptedStreamData): InputStream { + val bufferedInputStream = validateEncryptedInputStreamAndReturnBuffered(encryptedData.stream) + try { + val ivBytes = ByteArray(IV_SIZE) + val numberOfReadBytes = bufferedInputStream.read(ivBytes) + if (numberOfReadBytes != IV_SIZE) { + throw PubNubException( + errorMessage = "Could not read IV from encrypted stream", + pubnubError = PubNubError.CRYPTO_ERROR, + ) + } + val cipher = createInitializedCipher(ivBytes, Cipher.DECRYPT_MODE) + return CipherInputStream(bufferedInputStream, cipher) + } catch (e: Exception) { + throw PubNubException(errorMessage = e.message, pubnubError = PubNubError.CRYPTO_ERROR) + } + } + + private fun validateEncryptedInputStreamAndReturnBuffered(stream: InputStream): BufferedInputStream { + val bufferedInputStream = stream.buffered() + bufferedInputStream.checkMinSize(IV_SIZE + SIZE_OF_ONE_BLOCK_OF_ENCRYPTED_DATA) { + throw PubNubException( + errorMessage = "Encryption/Decryption of empty data not allowed.", + pubnubError = PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, + ) + } + return bufferedInputStream + } + + private fun validateStreamAndReturnBuffered(stream: InputStream): BufferedInputStream { + val bufferedInputStream = stream.buffered() + bufferedInputStream.checkMinSize(1) { + throw PubNubException( + errorMessage = "Encryption/Decryption of empty data not allowed.", + pubnubError = PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, + ) + } + return bufferedInputStream + } + + private fun createNewKey(): SecretKeySpec { + val keyBytes = + String(hexEncode(sha256(cipherKey.toByteArray())), Charsets.UTF_8) + .substring(0, 32) + .lowercase(Locale.getDefault()).toByteArray() + return SecretKeySpec(keyBytes, "AES") + } + + private fun sha256(input: ByteArray): ByteArray { + val digest: MessageDigest + return try { + digest = MessageDigest.getInstance("SHA-256") + digest.digest(input) + } catch (e: java.lang.Exception) { + throw PubNubException(errorMessage = e.message, pubnubError = PubNubError.CRYPTO_ERROR) + } + } + + private fun hexEncode(input: ByteArray): ByteArray { + val result = StringBuilder() + for (byt in input) { + result.append(Integer.toString((byt.toInt() and 0xff) + 0x100, 16).substring(1)) + } + try { + return result.toString().toByteArray() + } catch (e: UnsupportedEncodingException) { + throw PubNubException(errorMessage = e.message, pubnubError = PubNubError.CRYPTO_ERROR) + } + } + + private fun validateData(data: ByteArray) { + if (data.isEmpty()) { + throw PubNubException( + errorMessage = "Encryption/Decryption of empty data not allowed.", + pubnubError = PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, + ) + } + } + + private fun getIvBytesForEncryption(): ByteArray { + return if (useRandomIv) { + createRandomIv() + } else { + STATIC_IV.toByteArray() + } + } + + private fun createRandomIv(): ByteArray { + val ivBytes = ByteArray(IV_SIZE) + SecureRandom().nextBytes(ivBytes) + return ivBytes + } + + private fun validateData(encryptedData: EncryptedData) { + val encryptedDatSize = encryptedData.data.size + if (useRandomIv) { + if (encryptedDatSize <= IV_SIZE) { + throw PubNubException( + errorMessage = "Encryption/Decryption of empty data not allowed.", + pubnubError = PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, + ) + } + } else { + if (encryptedDatSize == 0) { + throw PubNubException( + errorMessage = "Encryption/Decryption of empty data not allowed.", + pubnubError = PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, + ) + } + } + } + + private fun getIvBytesForDecryption(encryptedData: EncryptedData): ByteArray { + return if (useRandomIv) { + encryptedData.data.sliceArray(RANDOM_IV_STARTING_INDEX..RANDOM_IV_ENDING_INDEX) + } else { + STATIC_IV.toByteArray() + } + } + + private fun createInitializedCipher( + iv: ByteArray, + mode: Int, + ): Cipher { + return Cipher.getInstance(CIPHER_TRANSFORMATION).also { + it.init(mode, newKey, IvParameterSpec(iv)) + } + } + + private fun getEncryptedDataForProcessing(encryptedData: EncryptedData): ByteArray { + val encryptedDataForProcessing: ByteArray = + if (useRandomIv) { + encryptedData.data.sliceArray(ENCRYPTED_DATA_STARTING_INDEX until encryptedData.data.size) + } else { + // when there is useRandomIv = false then there is no IV in message + encryptedData.data + } + return encryptedDataForProcessing + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/DeleteMessagesEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/DeleteMessagesEndpoint.kt new file mode 100644 index 000000000..b9132d07d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/DeleteMessagesEndpoint.kt @@ -0,0 +1,52 @@ +package com.pubnub.internal.endpoints + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.DeleteMessages +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.history.PNDeleteMessagesResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.toCsv +import retrofit2.Call +import retrofit2.Response +import java.util.Locale + +/** + * @see [PubNubImpl.deleteMessages] + */ +class DeleteMessagesEndpoint internal constructor( + pubnub: PubNubImpl, + override val channels: List, + override val start: Long? = null, + override val end: Long? = null, +) : EndpointCore(pubnub), DeleteMessages { + override fun validateParams() { + super.validateParams() + if (channels.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + } + + override fun doWork(queryParams: HashMap): Call { + addQueryParams(queryParams) + + return retrofitManager.historyService.deleteMessages( + configuration.subscribeKey, + channels.toCsv(), + queryParams, + ) + } + + override fun createResponse(input: Response): PNDeleteMessagesResult = PNDeleteMessagesResult() + + override fun operationType() = PNOperationType.PNDeleteMessagesOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.MESSAGE_PERSISTENCE + + private fun addQueryParams(queryParams: MutableMap) { + start?.run { queryParams["start"] = this.toString().lowercase(Locale.US) } + end?.run { queryParams["end"] = this.toString().lowercase(Locale.US) } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/DeleteMessagesImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/DeleteMessagesImpl.kt deleted file mode 100644 index 8e06ae815..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/DeleteMessagesImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints - -import com.pubnub.api.endpoints.DeleteMessages -import com.pubnub.api.models.consumer.history.PNDeleteMessagesResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.deleteMessages] - */ -class DeleteMessagesImpl internal constructor(deleteMessages: DeleteMessagesInterface) : - DeleteMessagesInterface by deleteMessages, - DeleteMessages, - EndpointImpl(deleteMessages) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/FetchMessagesEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/FetchMessagesEndpoint.kt new file mode 100644 index 000000000..9681b7371 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/FetchMessagesEndpoint.kt @@ -0,0 +1,151 @@ +package com.pubnub.internal.endpoints + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.FetchMessages +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.PNBoundedPage +import com.pubnub.api.models.consumer.history.HistoryMessageType +import com.pubnub.api.models.consumer.history.PNFetchMessageItem +import com.pubnub.api.models.consumer.history.PNFetchMessagesResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.extension.limit +import com.pubnub.internal.extension.nonPositiveToNull +import com.pubnub.internal.extension.tryDecryptMessage +import com.pubnub.internal.models.server.FetchMessagesEnvelope +import com.pubnub.internal.toCsv +import retrofit2.Call +import retrofit2.Response +import java.util.Locale + +/** + * @see [PubNubImpl.fetchMessages] + */ +class FetchMessagesEndpoint internal constructor( + pubnub: PubNubImpl, + override val channels: List, + override val page: PNBoundedPage, + override val includeUUID: Boolean, + override val includeMeta: Boolean, + override val includeMessageActions: Boolean, + override val includeMessageType: Boolean, +) : EndpointCore(pubnub), FetchMessages { + internal companion object { + private const val SINGLE_CHANNEL_DEFAULT_MESSAGES = 100 + private const val SINGLE_CHANNEL_MAX_MESSAGES = 100 + private const val MULTIPLE_CHANNEL_DEFAULT_MESSAGES = 25 + private const val MULTIPLE_CHANNEL_MAX_MESSAGES = 25 + private const val DEFAULT_MESSAGES_WITH_ACTIONS = 25 + private const val MAX_MESSAGES_WITH_ACTIONS = 25 + internal const val INCLUDE_MESSAGE_TYPE_QUERY_PARAM = "include_message_type" + + internal fun effectiveMax( + maximumPerChannel: Int?, + includeMessageActions: Boolean, + numberOfChannels: Int, + ): Int = + when { + includeMessageActions -> + maximumPerChannel?.limit(MAX_MESSAGES_WITH_ACTIONS)?.nonPositiveToNull() + ?: DEFAULT_MESSAGES_WITH_ACTIONS + + numberOfChannels == 1 -> + maximumPerChannel?.limit(SINGLE_CHANNEL_MAX_MESSAGES)?.nonPositiveToNull() + ?: SINGLE_CHANNEL_DEFAULT_MESSAGES + + else -> + maximumPerChannel?.limit(MULTIPLE_CHANNEL_MAX_MESSAGES)?.nonPositiveToNull() + ?: MULTIPLE_CHANNEL_DEFAULT_MESSAGES + } + } + + override fun validateParams() { + super.validateParams() + if (channels.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + if (includeMessageActions && channels.size > 1) { + throw PubNubException(PubNubError.HISTORY_MESSAGE_ACTIONS_MULTIPLE_CHANNELS) + } + } + + override fun getAffectedChannels() = channels + + override fun doWork(queryParams: HashMap): Call { + addQueryParams(queryParams) + + return if (!includeMessageActions) { + retrofitManager.historyService.fetchMessages( + subKey = configuration.subscribeKey, + channels = channels.toCsv(), + options = queryParams, + ) + } else { + retrofitManager.historyService.fetchMessagesWithActions( + subKey = configuration.subscribeKey, + channel = channels.first(), + options = queryParams, + ) + } + } + + override fun createResponse(input: Response): PNFetchMessagesResult { + val body = input.body()!! + val channelsMap = + body.channels.mapValues { (_, value) -> + value.map { serverMessageItem -> + val (newMessage, error) = + serverMessageItem.message.tryDecryptMessage( + configuration.cryptoModule, + pubnub.mapper, + ) + val newActions = + if (includeMessageActions) { + serverMessageItem.actions ?: mapOf() + } else { + serverMessageItem.actions + } + PNFetchMessageItem( + uuid = serverMessageItem.uuid, + message = newMessage, + meta = serverMessageItem.meta, + timetoken = serverMessageItem.timetoken, + actions = newActions, + messageType = + if (includeMessageType) { + HistoryMessageType.of(serverMessageItem.messageType) + } else { + null + }, + error = error, + ) + } + }.toMap() + + val page = + body.more?.let { + PNBoundedPage(start = it.start, end = it.end, limit = it.max) + } + return PNFetchMessagesResult(channels = channelsMap, page = page) + } + + override fun operationType() = PNOperationType.PNFetchMessagesOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.MESSAGE_PERSISTENCE + + private fun addQueryParams(queryParams: MutableMap) { + queryParams["max"] = effectiveMax(page.limit, includeMessageActions, channels.size).toString() + queryParams["include_uuid"] = includeUUID.toString() + + page.start?.run { queryParams["start"] = this.toString().lowercase(Locale.US) } + page.end?.run { queryParams["end"] = this.toString().lowercase(Locale.US) } + + queryParams[INCLUDE_MESSAGE_TYPE_QUERY_PARAM] = includeMessageType.toString() + + if (includeMeta) { + queryParams["include_meta"] = "true" + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/FetchMessagesImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/FetchMessagesImpl.kt deleted file mode 100644 index 0d06be4b0..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/FetchMessagesImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints - -import com.pubnub.api.endpoints.FetchMessages -import com.pubnub.api.models.consumer.history.PNFetchMessagesResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.fetchMessages] - */ -class FetchMessagesImpl internal constructor(fetchMessages: FetchMessagesInterface) : - FetchMessagesInterface by fetchMessages, - FetchMessages, - EndpointImpl(fetchMessages) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/HistoryEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/HistoryEndpoint.kt new file mode 100644 index 000000000..c869a3760 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/HistoryEndpoint.kt @@ -0,0 +1,131 @@ +package com.pubnub.internal.endpoints + +import com.google.gson.JsonElement +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.History +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.history.PNHistoryItemResult +import com.pubnub.api.models.consumer.history.PNHistoryResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.extension.tryDecryptMessage +import retrofit2.Call +import retrofit2.Response +import java.util.Locale + +/** + * @see [PubNubImpl.history] + */ +class HistoryEndpoint internal constructor( + pubnub: PubNubImpl, + override val channel: String, + override val start: Long? = null, + override val end: Long? = null, + override val count: Int, + override val reverse: Boolean, + override val includeTimetoken: Boolean, + override val includeMeta: Boolean, +) : EndpointCore(pubnub), History { + private val countParam: Int = + if (count in 1..PNHistoryResult.MAX_COUNT) { + count + } else { + PNHistoryResult.MAX_COUNT + } + + override fun validateParams() { + super.validateParams() + if (channel.isBlank()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + } + + override fun getAffectedChannels() = listOf(channel) + + override fun doWork(queryParams: HashMap): Call { + addQueryParams(queryParams) + + return retrofitManager.historyService.fetchHistory( + configuration.subscribeKey, + channel, + queryParams, + ) + } + + override fun createResponse(input: Response): PNHistoryResult { + val startTimeToken = + pubnub.mapper.elementToLong(pubnub.mapper.getArrayElement(input.body()!!, 1)) + val endTimeToken = + pubnub.mapper.elementToLong(pubnub.mapper.getArrayElement(input.body()!!, 2)) + + val messages = mutableListOf() + + val historyData = + PNHistoryResult( + messages = messages, + startTimetoken = startTimeToken, + endTimetoken = endTimeToken, + ) + + if (pubnub.mapper.getArrayElement(input.body()!!, 0).isJsonArray) { + val iterator = pubnub.mapper.getArrayIterator(pubnub.mapper.getArrayElement(input.body()!!, 0))!! + while (iterator.hasNext()) { + val historyEntry = iterator.next() + + var timetoken: Long? = null + var meta: JsonElement? = null + val historyMessageWithError: Pair + + if (includeTimetoken || includeMeta) { + historyMessageWithError = + pubnub.mapper.getField(historyEntry, "message")!! + .tryDecryptMessage(configuration.cryptoModule, pubnub.mapper) + if (includeTimetoken) { + timetoken = pubnub.mapper.elementToLong(historyEntry, "timetoken") + } + if (includeMeta) { + meta = pubnub.mapper.getField(historyEntry, "meta") + } + } else { + historyMessageWithError = historyEntry.tryDecryptMessage(configuration.cryptoModule, pubnub.mapper) + } + + val message: JsonElement = historyMessageWithError.first + val error: PubNubError? = historyMessageWithError.second + + val historyItem = + PNHistoryItemResult( + entry = message, + timetoken = timetoken, + meta = meta, + error = error, + ) + + messages.add(historyItem) + } + } else { + throw PubNubException( + pubnubError = PubNubError.HTTP_ERROR, + errorMessage = "History is disabled", + ) + } + + return historyData + } + + override fun operationType() = PNOperationType.PNHistoryOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.MESSAGE_PERSISTENCE + + private fun addQueryParams(queryParams: MutableMap) { + queryParams["reverse"] = reverse.toString() + queryParams["include_token"] = includeTimetoken.toString() + queryParams["include_meta"] = includeMeta.toString() + queryParams["count"] = countParam.toString() + + start?.run { queryParams["start"] = this.toString().lowercase(Locale.US) } + end?.run { queryParams["end"] = this.toString().lowercase(Locale.US) } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/HistoryImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/HistoryImpl.kt deleted file mode 100644 index 23e3d30ed..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/HistoryImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints - -import com.pubnub.api.endpoints.History -import com.pubnub.api.models.consumer.history.PNHistoryResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.history] - */ -class HistoryImpl internal constructor(history: HistoryInterface) : - HistoryInterface by history, - History, - EndpointImpl(history) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/MessageCountsEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/MessageCountsEndpoint.kt new file mode 100644 index 000000000..ab8a7b8e0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/MessageCountsEndpoint.kt @@ -0,0 +1,71 @@ +package com.pubnub.internal.endpoints + +import com.google.gson.JsonElement +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.MessageCounts +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.history.PNMessageCountResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.toCsv +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.messageCounts] + */ +class MessageCountsEndpoint internal constructor( + pubnub: PubNubImpl, + override val channels: List, + override val channelsTimetoken: List, +) : EndpointCore(pubnub), MessageCounts { + override fun validateParams() { + super.validateParams() + if (channels.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + if (channelsTimetoken.isEmpty()) { + throw PubNubException(PubNubError.TIMETOKEN_MISSING) + } + if (channelsTimetoken.size != channels.size && channelsTimetoken.size > 1) { + throw PubNubException(PubNubError.CHANNELS_TIMETOKEN_MISMATCH) + } + } + + override fun getAffectedChannels() = channels + + override fun doWork(queryParams: HashMap): Call { + addQueryParams(queryParams) + + return retrofitManager.historyService.fetchCount( + subKey = configuration.subscribeKey, + channels = channels.toCsv(), + options = queryParams, + ) + } + + override fun createResponse(input: Response): PNMessageCountResult { + val channelsMap = HashMap() + + val it = pubnub.mapper.getObjectIterator(input.body()!!, "channels") + while (it.hasNext()) { + val entry = it.next() + channelsMap[entry.key] = entry.value.asLong + } + return PNMessageCountResult(channelsMap) + } + + override fun operationType() = PNOperationType.PNMessageCountOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.MESSAGE_PERSISTENCE + + private fun addQueryParams(queryParams: MutableMap) { + if (channelsTimetoken.size == 1) { + queryParams["timetoken"] = channelsTimetoken.toCsv() + } else { + queryParams["channelsTimetoken"] = channelsTimetoken.toCsv() + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/MessageCountsImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/MessageCountsImpl.kt deleted file mode 100644 index 46cee4c6e..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/MessageCountsImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints - -import com.pubnub.api.endpoints.MessageCounts -import com.pubnub.api.models.consumer.history.PNMessageCountResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.messageCounts] - */ -class MessageCountsImpl internal constructor(messageCounts: MessageCountsInterface) : - MessageCountsInterface by messageCounts, - MessageCounts, - EndpointImpl(messageCounts) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/TimeEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/TimeEndpoint.kt new file mode 100644 index 000000000..edfee085c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/TimeEndpoint.kt @@ -0,0 +1,37 @@ +package com.pubnub.internal.endpoints + +import com.pubnub.api.endpoints.Time +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.PNTimeResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import retrofit2.Response + +/** + * @see [PubNubImpl.time] + */ +class TimeEndpoint(pubnub: PubNubImpl, private val excludeFromRetry: Boolean = false) : + EndpointCore, PNTimeResult>(pubnub), + Time { + override fun getAffectedChannels() = emptyList() + + override fun getAffectedChannelGroups() = emptyList() + + override fun doWork(queryParams: HashMap) = retrofitManager.timeService.fetchTime(queryParams) + + override fun createResponse(input: Response>): PNTimeResult { + return PNTimeResult(input.body()!![0]) + } + + override fun operationType() = PNOperationType.PNTimeOperation + + override fun isAuthRequired() = false + + override fun isSubKeyRequired() = false + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.MESSAGE_PERSISTENCE + + // it is excluded here because retry in old subscribeLoop uses it to check connectivity + override fun isEndpointRetryable(): Boolean = !excludeFromRetry +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/TimeImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/TimeImpl.kt deleted file mode 100644 index 8f694ac10..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/TimeImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints - -import com.pubnub.api.endpoints.Time -import com.pubnub.api.models.consumer.PNTimeResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.time] - */ -class TimeImpl internal constructor(time: TimeInterface) : - TimeInterface by time, - Time, - EndpointImpl(time) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantEndpoint.kt new file mode 100644 index 000000000..8210e8451 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantEndpoint.kt @@ -0,0 +1,194 @@ +package com.pubnub.internal.endpoints.access + +import com.google.gson.JsonElement +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.access.Grant +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerGrantResult +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeyData +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.api.v2.PNConfiguration.Companion.isValid +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.Envelope +import com.pubnub.internal.models.server.access_manager.AccessManagerGrantPayload +import com.pubnub.internal.toCsv +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.grant] + */ +open class GrantEndpoint( + pubnub: PubNubImpl, + override val read: Boolean = false, + override val write: Boolean = false, + override val manage: Boolean = false, + override val delete: Boolean = false, + override val get: Boolean = false, + override val update: Boolean = false, + override val join: Boolean = false, + override val ttl: Int = -1, + override val authKeys: List = emptyList(), + override val channels: List = emptyList(), + override val channelGroups: List = emptyList(), + override val uuids: List = emptyList(), +) : EndpointCore, PNAccessManagerGrantResult>(pubnub), Grant { + override fun validateParams() { + super.validateParams() + if (!pubnub.configuration.secretKey.isValid()) { + throw PubNubException(PubNubError.SECRET_KEY_MISSING) + } + } + + override fun getAffectedChannels() = channels + + override fun getAffectedChannelGroups() = channelGroups + + override fun doWork(queryParams: HashMap): Call> { + addQueryParams(queryParams) + + return retrofitManager.accessManagerService + .grant( + subKey = configuration.subscribeKey, + options = queryParams, + ) + } + + override fun createResponse(input: Response>): PNAccessManagerGrantResult { + val data = input.body()!!.payload!! + + val constructedChannels = mutableMapOf?>() + val constructedGroups = mutableMapOf?>() + + // we have a case of a singular channel. + data.channel?.let { + constructedChannels[it] = data.authKeys!! + } + + if (channelGroups.size == 1) { + constructedGroups[pubnub.mapper.elementToString(data.channelGroups)!!] = data.authKeys!! + } else if (channelGroups.size > 1) { + val it = pubnub.mapper.getObjectIterator(data.channelGroups!!) + while (it.hasNext()) { + val (k, v) = it.next() + constructedGroups[k] = createKeyMap(v) + } + } + + data.channels?.forEach { + constructedChannels[it.key] = data.channels[it.key]!!.authKeys + } + + val constructedUuids = mutableMapOf?>() + data.uuids?.forEach { + constructedUuids[it.key] = data.uuids[it.key]!!.authKeys + } + + return PNAccessManagerGrantResult( + level = data.level!!, + ttl = data.ttl, + subscribeKey = data.subscribeKey!!, + channels = constructedChannels, + channelGroups = constructedGroups, + uuids = constructedUuids, + ) + } + + override fun operationType() = PNOperationType.PNAccessManagerGrant + + override fun isAuthRequired() = false + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.ACCESS_MANAGER + + private fun createKeyMap(input: JsonElement): Map { + val result: MutableMap = + HashMap() + val it: Iterator> = + pubnub.mapper.getObjectIterator(input, "auths") + while (it.hasNext()) { + val keyMap = it.next() + val pnAccessManagerKeyData = PNAccessManagerKeyData() + pnAccessManagerKeyData.manageEnabled = (pubnub.mapper.getAsBoolean(keyMap.value, "m")) + pnAccessManagerKeyData.writeEnabled = (pubnub.mapper.getAsBoolean(keyMap.value, "w")) + pnAccessManagerKeyData.readEnabled = (pubnub.mapper.getAsBoolean(keyMap.value, "r")) + pnAccessManagerKeyData.deleteEnabled = (pubnub.mapper.getAsBoolean(keyMap.value, "d")) + pnAccessManagerKeyData.getEnabled = (pubnub.mapper.getAsBoolean(keyMap.value, "g")) + pnAccessManagerKeyData.updateEnabled = (pubnub.mapper.getAsBoolean(keyMap.value, "u")) + pnAccessManagerKeyData.joinEnabled = (pubnub.mapper.getAsBoolean(keyMap.value, "j")) + result[keyMap.key] = pnAccessManagerKeyData + } + return result + } + + private fun addQueryParams(queryParams: MutableMap) { + channels.run { + if (isNotEmpty()) { + queryParams["channel"] = toCsv() + } + } + channelGroups.run { + if (isNotEmpty()) { + queryParams["channel-group"] = toCsv() + } + } + authKeys.run { + if (isNotEmpty()) { + queryParams["auth"] = toCsv() + } + } + uuids.run { + if (isNotEmpty()) { + queryParams["target-uuid"] = toCsv() + } + } + + if (ttl >= -1) { + queryParams["ttl"] = ttl.toString() + } + + queryParams["r"] = + if (read) { + "1" + } else { + "0" + } + queryParams["w"] = + if (write) { + "1" + } else { + "0" + } + queryParams["m"] = + if (manage) { + "1" + } else { + "0" + } + queryParams["d"] = + if (delete) { + "1" + } else { + "0" + } + queryParams["g"] = + if (get) { + "1" + } else { + "0" + } + queryParams["u"] = + if (update) { + "1" + } else { + "0" + } + queryParams["j"] = + if (join) { + "1" + } else { + "0" + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantImpl.kt deleted file mode 100644 index a564e474a..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantImpl.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.pubnub.internal.endpoints.access - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction -import com.pubnub.api.models.consumer.access_manager.PNAccessManagerGrantResult -import com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeyData -import com.pubnub.internal.DelegatingEndpoint -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.grant] - */ -class GrantImpl internal constructor(grant: GrantEndpoint) : - DelegatingEndpoint( - grant, - ), - GrantInterface by grant, - com.pubnub.api.endpoints.access.Grant { - override fun convertAction( - remoteAction: ExtendedRemoteAction, - ): ExtendedRemoteAction { - return MappingRemoteAction(remoteAction, ::from) - } - } - -fun from(result: com.pubnub.internal.models.consumer.access_manager.PNAccessManagerGrantResult): PNAccessManagerGrantResult { - with(result) { - return PNAccessManagerGrantResult( - level, - ttl, - subscribeKey, - channels.toApi(), - channelGroups.toApi(), - ) - } -} - -private fun Map?>.toApi(): Map?> { - return mapValues { - it.value?.mapValues { from(it.value) } - } -} - -fun from(value: com.pubnub.internal.models.consumer.access_manager.PNAccessManagerKeyData): PNAccessManagerKeyData { - return PNAccessManagerKeyData().apply { - this.readEnabled = value.readEnabled - this.manageEnabled = value.manageEnabled - this.writeEnabled = value.writeEnabled - this.deleteEnabled = value.deleteEnabled - this.getEnabled = value.getEnabled - this.updateEnabled = value.updateEnabled - this.joinEnabled = value.joinEnabled - } -} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantTokenEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantTokenEndpoint.kt new file mode 100644 index 000000000..19dc000e7 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantTokenEndpoint.kt @@ -0,0 +1,72 @@ +package com.pubnub.internal.endpoints.access + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.access.GrantToken +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant +import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant +import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult +import com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.api.v2.PNConfiguration.Companion.isValid +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.access_manager.v3.GrantTokenRequestBody +import com.pubnub.internal.models.server.access_manager.v3.GrantTokenResponse +import retrofit2.Call +import retrofit2.Response + +class GrantTokenEndpoint( + pubnub: PubNubImpl, + override val ttl: Int, + private val meta: Any?, + private val authorizedUUID: String?, + private val channels: List, + private val channelGroups: List, + private val uuids: List, +) : EndpointCore(pubnub), GrantToken { + override fun getAffectedChannels(): List = channels.map { it.id } + + override fun getAffectedChannelGroups(): List = channelGroups.map { it.id } + + override fun validateParams() { + if (!pubnub.configuration.secretKey.isValid()) { + throw PubNubException(PubNubError.SECRET_KEY_MISSING) + } + if (!configuration.subscribeKey.isValid()) { + throw PubNubException(PubNubError.SUBSCRIBE_KEY_MISSING) + } + if ((channels + channelGroups + uuids).isEmpty()) { + throw PubNubException( + pubnubError = PubNubError.RESOURCES_MISSING, + errorMessage = "At least one grant required", + ) + } + } + + override fun doWork(queryParams: HashMap): Call { + val requestBody: GrantTokenRequestBody = + GrantTokenRequestBody.of( + ttl = ttl, + channels = channels, + groups = channelGroups, + uuids = uuids, + meta = meta, + uuid = authorizedUUID, + ) + return retrofitManager + .accessManagerService + .grantToken(configuration.subscribeKey, requestBody, queryParams) + } + + override fun createResponse(input: Response): PNGrantTokenResult { + return input.body()!!.data.token.let { PNGrantTokenResult(it) } + } + + override fun operationType(): PNOperationType = PNOperationType.PNAccessManagerGrantToken + + override fun isAuthRequired(): Boolean = false + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.ACCESS_MANAGER +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantTokenImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantTokenImpl.kt deleted file mode 100644 index b573aab7f..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/GrantTokenImpl.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.pubnub.internal.endpoints.access - -import com.pubnub.api.endpoints.access.GrantToken -import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult -import com.pubnub.internal.EndpointImpl - -class GrantTokenImpl internal constructor(grantToken: GrantTokenInterface) : - EndpointImpl(grantToken), - GrantTokenInterface by grantToken, - GrantToken diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/RevokeTokenEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/RevokeTokenEndpoint.kt new file mode 100644 index 000000000..789e8f017 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/RevokeTokenEndpoint.kt @@ -0,0 +1,47 @@ +package com.pubnub.internal.endpoints.access + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.access.RevokeToken +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.api.v2.PNConfiguration.Companion.isValid +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.access_manager.v3.RevokeTokenResponse +import retrofit2.Call +import retrofit2.Response +import java.net.URLEncoder + +class RevokeTokenEndpoint( + pubnub: PubNubImpl, + private val token: String, +) : EndpointCore(pubnub), RevokeToken { + override fun validateParams() { + super.validateParams() + if (!pubnub.configuration.secretKey.isValid()) { + throw PubNubException(PubNubError.SECRET_KEY_MISSING) + } + if (token.isBlank()) { + throw PubNubException(PubNubError.TOKEN_MISSING) + } + } + + override fun doWork(queryParams: HashMap): Call { + return retrofitManager + .accessManagerService + .revokeToken(configuration.subscribeKey, repairEncoding(token), queryParams) + } + + override fun createResponse(input: Response): Unit = Unit + + override fun operationType(): PNOperationType = PNOperationType.PNAccessManagerRevokeToken + + override fun isAuthRequired(): Boolean = false + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.ACCESS_MANAGER + + private fun repairEncoding(token: String): String { + return URLEncoder.encode(token, "utf-8").replace("+", "%20") + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/RevokeTokenImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/RevokeTokenImpl.kt deleted file mode 100644 index f9a20efcf..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/access/RevokeTokenImpl.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.pubnub.internal.endpoints.access - -import com.pubnub.api.endpoints.access.RevokeToken -import com.pubnub.internal.EndpointImpl - -class RevokeTokenImpl internal constructor(token: RevokeTokenInterface) : - RevokeTokenInterface by token, - RevokeToken, - EndpointImpl(token) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AddChannelChannelGroupEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AddChannelChannelGroupEndpoint.kt new file mode 100644 index 000000000..18550b48d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AddChannelChannelGroupEndpoint.kt @@ -0,0 +1,59 @@ +package com.pubnub.internal.endpoints.channel_groups + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.channel_groups.AddChannelChannelGroup +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAddChannelResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.toCsv +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.addChannelsToChannelGroup] + */ +class AddChannelChannelGroupEndpoint internal constructor( + pubnub: PubNubImpl, + override val channelGroup: String, + override val channels: List, +) : EndpointCore(pubnub), AddChannelChannelGroup { + override fun getAffectedChannels() = channels + + override fun getAffectedChannelGroups() = listOf(channelGroup) + + override fun validateParams() { + super.validateParams() + if (channelGroup.isBlank()) { + throw PubNubException(PubNubError.GROUP_MISSING) + } + if (channels.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + } + + override fun doWork(queryParams: HashMap): Call { + addQueryParams(queryParams) + + return retrofitManager.channelGroupService + .addChannelChannelGroup( + configuration.subscribeKey, + channelGroup, + queryParams, + ) + } + + override fun createResponse(input: Response): PNChannelGroupsAddChannelResult = PNChannelGroupsAddChannelResult() + + override fun operationType() = PNOperationType.PNAddChannelsToGroupOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.CHANNEL_GROUP + + private fun addQueryParams(queryParams: MutableMap) { + if (channels.isNotEmpty()) { + queryParams["add"] = channels.toCsv() + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AddChannelChannelGroupImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AddChannelChannelGroupImpl.kt deleted file mode 100644 index 1ec2017e6..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AddChannelChannelGroupImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.channel_groups - -import com.pubnub.api.endpoints.channel_groups.AddChannelChannelGroup -import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAddChannelResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.addChannelsToChannelGroup] - */ -class AddChannelChannelGroupImpl internal constructor(addChannelChannelGroup: AddChannelChannelGroupInterface) : - AddChannelChannelGroupInterface by addChannelChannelGroup, - AddChannelChannelGroup, - EndpointImpl(addChannelChannelGroup) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AllChannelsChannelGroupEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AllChannelsChannelGroupEndpoint.kt new file mode 100644 index 000000000..7c6c074cf --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AllChannelsChannelGroupEndpoint.kt @@ -0,0 +1,50 @@ +package com.pubnub.internal.endpoints.channel_groups + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.channel_groups.AllChannelsChannelGroup +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAllChannelsResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.Envelope +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.listChannelsForChannelGroup] + */ +class AllChannelsChannelGroupEndpoint internal constructor( + pubnub: PubNubImpl, + override val channelGroup: String, +) : EndpointCore>, PNChannelGroupsAllChannelsResult>(pubnub), + AllChannelsChannelGroup { + override fun getAffectedChannelGroups() = listOf(channelGroup) + + override fun validateParams() { + super.validateParams() + if (channelGroup.isBlank()) { + throw PubNubException(PubNubError.GROUP_MISSING) + } + } + + override fun doWork(queryParams: HashMap): Call>> { + return retrofitManager.channelGroupService + .allChannelsChannelGroup( + configuration.subscribeKey, + channelGroup, + queryParams, + ) + } + + @Suppress("UNCHECKED_CAST") + override fun createResponse(input: Response>>): PNChannelGroupsAllChannelsResult = + PNChannelGroupsAllChannelsResult( + channels = input.body()!!.payload!!["channels"] as List, + ) + + override fun operationType() = PNOperationType.PNChannelsForGroupOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.CHANNEL_GROUP +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AllChannelsChannelGroupImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AllChannelsChannelGroupImpl.kt deleted file mode 100644 index ba95de1d6..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/AllChannelsChannelGroupImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.channel_groups - -import com.pubnub.api.endpoints.channel_groups.AllChannelsChannelGroup -import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAllChannelsResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.listChannelsForChannelGroup] - */ -class AllChannelsChannelGroupImpl internal constructor(allChannelsChannelGroup: AllChannelsChannelGroupInterface) : - AllChannelsChannelGroupInterface by allChannelsChannelGroup, - AllChannelsChannelGroup, - EndpointImpl(allChannelsChannelGroup) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/DeleteChannelGroupEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/DeleteChannelGroupEndpoint.kt new file mode 100644 index 000000000..f06ba172d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/DeleteChannelGroupEndpoint.kt @@ -0,0 +1,44 @@ +package com.pubnub.internal.endpoints.channel_groups + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.channel_groups.DeleteChannelGroup +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsDeleteGroupResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.deleteChannelGroup] + */ +class DeleteChannelGroupEndpoint internal constructor( + pubnub: PubNubImpl, + override val channelGroup: String, +) : EndpointCore(pubnub), DeleteChannelGroup { + override fun validateParams() { + super.validateParams() + if (channelGroup.isBlank()) { + throw PubNubException(PubNubError.GROUP_MISSING) + } + } + + override fun getAffectedChannelGroups() = listOf(channelGroup) + + override fun doWork(queryParams: HashMap): Call { + return retrofitManager.channelGroupService + .deleteChannelGroup( + configuration.subscribeKey, + channelGroup, + queryParams, + ) + } + + override fun createResponse(input: Response): PNChannelGroupsDeleteGroupResult = PNChannelGroupsDeleteGroupResult() + + override fun operationType() = PNOperationType.PNRemoveGroupOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.CHANNEL_GROUP +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/DeleteChannelGroupImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/DeleteChannelGroupImpl.kt deleted file mode 100644 index 31f64fc06..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/DeleteChannelGroupImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.channel_groups - -import com.pubnub.api.endpoints.channel_groups.DeleteChannelGroup -import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsDeleteGroupResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.deleteChannelGroup] - */ -class DeleteChannelGroupImpl internal constructor(deleteChannelGroup: DeleteChannelGroupInterface) : - DeleteChannelGroupInterface by deleteChannelGroup, - DeleteChannelGroup, - EndpointImpl(deleteChannelGroup) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/ListAllChannelGroupEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/ListAllChannelGroupEndpoint.kt new file mode 100644 index 000000000..d9bf4ec70 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/ListAllChannelGroupEndpoint.kt @@ -0,0 +1,36 @@ +package com.pubnub.internal.endpoints.channel_groups + +import com.pubnub.api.endpoints.channel_groups.ListAllChannelGroup +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsListAllResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.Envelope +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.listAllChannelGroups] + */ +class ListAllChannelGroupEndpoint internal constructor(pubnub: PubNubImpl) : + EndpointCore>, PNChannelGroupsListAllResult>(pubnub), + ListAllChannelGroup { + override fun doWork(queryParams: HashMap): Call>> { + return retrofitManager.channelGroupService + .listAllChannelGroup( + configuration.subscribeKey, + queryParams, + ) + } + + @Suppress("UNCHECKED_CAST") + override fun createResponse(input: Response>>): PNChannelGroupsListAllResult = + PNChannelGroupsListAllResult( + groups = input.body()!!.payload!!["groups"] as List, + ) + + override fun operationType() = PNOperationType.PNChannelGroupsOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.CHANNEL_GROUP + } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/ListAllChannelGroupImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/ListAllChannelGroupImpl.kt deleted file mode 100644 index 19d87b2af..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/ListAllChannelGroupImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.channel_groups - -import com.pubnub.api.endpoints.channel_groups.ListAllChannelGroup -import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsListAllResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.listAllChannelGroups] - */ -class ListAllChannelGroupImpl internal constructor(listAllChannelGroup: ListAllChannelGroupInterface) : - ListAllChannelGroupInterface by listAllChannelGroup, - ListAllChannelGroup, - EndpointImpl(listAllChannelGroup) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/RemoveChannelChannelGroupEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/RemoveChannelChannelGroupEndpoint.kt new file mode 100644 index 000000000..2e9bda8b0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/RemoveChannelChannelGroupEndpoint.kt @@ -0,0 +1,59 @@ +package com.pubnub.internal.endpoints.channel_groups + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.channel_groups.RemoveChannelChannelGroup +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsRemoveChannelResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.toCsv +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.removeChannelsFromChannelGroup] + */ +class RemoveChannelChannelGroupEndpoint internal constructor( + pubnub: PubNubImpl, + override val channelGroup: String, + override val channels: List, +) : EndpointCore(pubnub), RemoveChannelChannelGroup { + override fun getAffectedChannels() = channels + + override fun getAffectedChannelGroups() = listOf(channelGroup) + + override fun validateParams() { + super.validateParams() + if (channelGroup.isBlank()) { + throw PubNubException(PubNubError.GROUP_MISSING) + } + if (channels.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + } + + override fun doWork(queryParams: HashMap): Call { + addQueryParams(queryParams) + + return retrofitManager.channelGroupService + .removeChannel( + configuration.subscribeKey, + channelGroup, + queryParams, + ) + } + + override fun createResponse(input: Response): PNChannelGroupsRemoveChannelResult = PNChannelGroupsRemoveChannelResult() + + override fun operationType() = PNOperationType.PNRemoveChannelsFromGroupOperation + + private fun addQueryParams(queryParams: MutableMap) { + if (channels.isNotEmpty()) { + queryParams["remove"] = channels.toCsv() + } + } + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.CHANNEL_GROUP +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/RemoveChannelChannelGroupImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/RemoveChannelChannelGroupImpl.kt deleted file mode 100644 index 897cadc0a..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/channel_groups/RemoveChannelChannelGroupImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.channel_groups - -import com.pubnub.api.endpoints.channel_groups.RemoveChannelChannelGroup -import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsRemoveChannelResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.removeChannelsFromChannelGroup] - */ -class RemoveChannelChannelGroupImpl internal constructor(removeChannelChannelGroup: RemoveChannelChannelGroupInterface) : - RemoveChannelChannelGroupInterface by removeChannelChannelGroup, - RemoveChannelChannelGroup, - EndpointImpl(removeChannelChannelGroup) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DeleteFileEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DeleteFileEndpoint.kt new file mode 100644 index 000000000..282f3d5c6 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DeleteFileEndpoint.kt @@ -0,0 +1,61 @@ +package com.pubnub.internal.endpoints.files + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.files.DeleteFile +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.files.PNDeleteFileResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.deleteFile] + */ +class DeleteFileEndpoint( + private val channel: String, + private val fileName: String, + private val fileId: String, + pubNub: PubNubImpl, +) : EndpointCore(pubNub), DeleteFile { + @Throws(PubNubException::class) + override fun validateParams() { + if (channel.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + } + + @Throws(PubNubException::class) + override fun doWork(queryParams: HashMap): Call = + retrofitManager.filesService.deleteFile( + configuration.subscribeKey, + channel, + fileId, + fileName, + queryParams, + ) + + @Throws(PubNubException::class) + override fun createResponse(input: Response): PNDeleteFileResult = + if (input.isSuccessful) { + PNDeleteFileResult(input.code()) + } else { + throw PubNubException(PubNubError.HTTP_ERROR) + } + + override fun getAffectedChannels() = listOf(channel) + + override fun getAffectedChannelGroups(): List = listOf() + + override fun operationType(): PNOperationType = PNOperationType.FileOperation + + override fun isAuthRequired(): Boolean = true + + override fun isSubKeyRequired(): Boolean = true + + override fun isPubKeyRequired(): Boolean = false + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.FILE_PERSISTENCE +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DeleteFileImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DeleteFileImpl.kt deleted file mode 100644 index 8cfcb6a3e..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DeleteFileImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.files - -import com.pubnub.api.endpoints.files.DeleteFile -import com.pubnub.api.models.consumer.files.PNDeleteFileResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.deleteFile] - */ -class DeleteFileImpl internal constructor(deleteFile: DeleteFileInterface) : - DeleteFileInterface by deleteFile, - DeleteFile, - EndpointImpl(deleteFile) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DownloadFileEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DownloadFileEndpoint.kt new file mode 100644 index 000000000..1e51c78e4 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DownloadFileEndpoint.kt @@ -0,0 +1,72 @@ +package com.pubnub.internal.endpoints.files + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.api.endpoints.files.DownloadFile +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.files.PNDownloadFileResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import okhttp3.ResponseBody +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.downloadFile] + */ +class DownloadFileEndpoint( + private val channel: String, + private val fileName: String, + private val fileId: String, + private val cryptoModule: CryptoModule? = null, + pubNub: PubNubImpl, +) : EndpointCore(pubNub), DownloadFile { + @Throws(PubNubException::class) + override fun validateParams() { + if (channel.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + } + + @Throws(PubNubException::class) + override fun doWork(queryParams: HashMap): Call = + retrofitManager.filesService.downloadFile( + configuration.subscribeKey, + channel, + fileId, + fileName, + queryParams, + ) + + @Throws(PubNubException::class) + override fun createResponse(input: Response): PNDownloadFileResult { + if (!input.isSuccessful) { + throw PubNubException(PubNubError.HTTP_ERROR) + } + if (input.body() == null) { + throw PubNubException(PubNubError.INTERNAL_ERROR) + } + val bodyStream = input.body()!!.byteStream() + val byteStream = + cryptoModule?.decryptStream(bodyStream) + ?: bodyStream + + return PNDownloadFileResult(fileName, byteStream) + } + + override fun getAffectedChannels() = listOf(channel) + + override fun getAffectedChannelGroups(): List = listOf() + + override fun operationType(): PNOperationType = PNOperationType.FileOperation + + override fun isAuthRequired(): Boolean = true + + override fun isSubKeyRequired(): Boolean = true + + override fun isPubKeyRequired(): Boolean = false + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.FILE_PERSISTENCE +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DownloadFileImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DownloadFileImpl.kt deleted file mode 100644 index a8f8065ab..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/DownloadFileImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.files - -import com.pubnub.api.endpoints.files.DownloadFile -import com.pubnub.api.models.consumer.files.PNDownloadFileResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.downloadFile] - */ -class DownloadFileImpl internal constructor(downloadFile: DownloadFileInterface) : - DownloadFileInterface by downloadFile, - DownloadFile, - EndpointImpl(downloadFile) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/GenerateUploadUrlEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/GenerateUploadUrlEndpoint.kt new file mode 100644 index 000000000..2d2056af1 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/GenerateUploadUrlEndpoint.kt @@ -0,0 +1,86 @@ +package com.pubnub.internal.endpoints.files + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.files.FileUploadRequestDetails +import com.pubnub.internal.models.server.files.FormField +import com.pubnub.internal.models.server.files.GenerateUploadUrlPayload +import com.pubnub.internal.models.server.files.GeneratedUploadUrlResponse +import retrofit2.Call +import retrofit2.Response + +internal class GenerateUploadUrlEndpoint( + private val channel: String, + private val fileName: String, + pubNub: PubNubImpl, +) : EndpointCore(pubNub) { + @Throws(PubNubException::class) + override fun validateParams() { + if (channel.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + } + + @Throws(PubNubException::class) + override fun createResponse(input: Response): FileUploadRequestDetails { + if (input.body() == null) { + throw PubNubException(PubNubError.INTERNAL_ERROR).copy(errorMessage = "Empty body, but GeneratedUploadUrlResponse expected") + } + val response = input.body()!! + val keyFormField = getKeyFormField(response) + return FileUploadRequestDetails( + response.status, + response.data, + response.fileUploadRequest.url, + response.fileUploadRequest.method, + response.fileUploadRequest.expirationDate, + keyFormField, + response.fileUploadRequest.formFields, + ) + } + + @Throws(PubNubException::class) + private fun getKeyFormField(response: GeneratedUploadUrlResponse): FormField { + val formFields: List = response.fileUploadRequest.formFields + return formFields.find { it.key == "key" } ?: throw PubNubException(PubNubError.INTERNAL_ERROR).copy( + errorMessage = "Couldn't find `key` form field in GeneratedUploadUrlResponse", + ) + } + + override fun doWork(queryParams: HashMap): Call { + return retrofitManager.filesService.generateUploadUrl( + configuration.subscribeKey, + channel, + GenerateUploadUrlPayload(fileName), + queryParams, + ) + } + + override fun getAffectedChannels() = listOf(channel) + + override fun getAffectedChannelGroups(): List = listOf() + + override fun operationType(): PNOperationType = PNOperationType.FileOperation + + override fun isAuthRequired(): Boolean = true + + override fun isSubKeyRequired(): Boolean = true + + override fun isPubKeyRequired(): Boolean = false + + internal class Factory(private val pubNub: PubNubImpl) { + fun create( + channel: String, + fileName: String, + ): ExtendedRemoteAction { + return GenerateUploadUrlEndpoint(channel, fileName, pubNub) + } + } + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.FILE_PERSISTENCE +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/GetFileUrlEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/GetFileUrlEndpoint.kt new file mode 100644 index 000000000..171ebc042 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/GetFileUrlEndpoint.kt @@ -0,0 +1,113 @@ +package com.pubnub.internal.endpoints.files + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.files.GetFileUrl +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.files.PNFileUrlResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.api.v2.callbacks.Result +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.PubNubUtil +import okhttp3.ResponseBody +import retrofit2.Call +import retrofit2.Response +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.function.Consumer + +/** + * @see [PubNubImpl.getFileUrl] + */ +class GetFileUrlEndpoint( + private val channel: String, + private val fileName: String, + private val fileId: String, + pubNub: PubNubImpl, +) : EndpointCore(pubNub), GetFileUrl { + private lateinit var cachedCallback: Consumer> + private val executorService: ExecutorService = retrofitManager.getTransactionClientExecutorService() ?: Executors.newSingleThreadExecutor() + + @Throws(PubNubException::class) + override fun validateParams() { + super.validateParams() + if (channel.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + } + + // this code shouldn't call any outside services to return an url, but on the + // other hand the code that adds signature uses okhttp request. To produce + // properly constructed url the code creates a request which isn't executed + @Throws(PubNubException::class) + override fun sync(): PNFileUrlResult { + return try { + val baseParams: Map = createBaseParams() + val call: Call = + retrofitManager.filesService + .downloadFile( + configuration.subscribeKey, + channel, + fileId, + fileName, + baseParams, + ) + val signedRequest = + PubNubUtil.signRequest( + call.request(), + pubnub.configuration, + PubNubImpl.timestamp(), + ) + PNFileUrlResult(signedRequest.url.toString()) + } catch (e: Exception) { + throw PubNubException(errorMessage = e.message) + } + } + + // Endpoint class uses OkHttp's asynchronous calls to achieve async. Since in this class + // the code shouldn't call any outside endpoints it's necessary to achieve asynchronous + // behavior using other means than OkHttp. That's why the code is using executorService + // to asynchronously call sync() + override fun async(callback: Consumer>) { + cachedCallback = callback + executorService.execute { + try { + val res: PNFileUrlResult = sync() + callback.accept( + Result.success(res), + ) + } catch (ex: PubNubException) { + callback.accept(Result.failure(ex)) + } + } + } + + @Throws(PubNubException::class) + override fun doWork(queryParams: HashMap): Call { + throw PubNubException(PubNubError.INTERNAL_ERROR) + } + + @Throws(PubNubException::class) + override fun createResponse(input: Response): PNFileUrlResult { + throw PubNubException(PubNubError.INTERNAL_ERROR) + } + + override fun retry() { + async(cachedCallback) + } + + override fun getAffectedChannels() = listOf(channel) + + override fun getAffectedChannelGroups(): List = listOf() + + override fun operationType(): PNOperationType = PNOperationType.FileOperation + + override fun isAuthRequired(): Boolean = true + + override fun isSubKeyRequired(): Boolean = true + + override fun isPubKeyRequired(): Boolean = false + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.FILE_PERSISTENCE +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/GetFileUrlImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/GetFileUrlImpl.kt deleted file mode 100644 index 9b69dd6c1..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/GetFileUrlImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.files - -import com.pubnub.api.endpoints.files.GetFileUrl -import com.pubnub.api.models.consumer.files.PNFileUrlResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.getFileUrl] - */ -class GetFileUrlImpl internal constructor(getFileUrl: GetFileUrlInterface) : - GetFileUrlInterface by getFileUrl, - GetFileUrl, - EndpointImpl(getFileUrl) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/ListFilesEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/ListFilesEndpoint.kt new file mode 100644 index 000000000..e5e9868c8 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/ListFilesEndpoint.kt @@ -0,0 +1,88 @@ +package com.pubnub.internal.endpoints.files + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.files.ListFiles +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.files.PNListFilesResult +import com.pubnub.api.models.consumer.objects.PNPage +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.files.ListFilesResult +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.listFiles] + */ +class ListFilesEndpoint( + private val channel: String, + private val limit: Int? = null, + private val next: PNPage.PNNext? = null, + pubNub: PubNubImpl, +) : EndpointCore(pubNub), ListFiles { + @Throws(PubNubException::class) + override fun validateParams() { + if (channel.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + if (limit != null && limit !in MIN_LIMIT..MAX_LIMIT) { + throw PubNubException(PubNubError.INVALID_ARGUMENTS).copy( + errorMessage = "Limit should be in range from 1 to 100 (both inclusive)", + ) + } + if (next != null && (next.pageHash.isBlank())) { + throw PubNubException(PubNubError.INVALID_ARGUMENTS).copy( + errorMessage = "Next should not be an empty string", + ) + } + } + + @Throws(PubNubException::class) + override fun doWork(queryParams: HashMap): Call { + queryParams[LIMIT_QUERY_PARAM] = (limit ?: DEFAULT_LIMIT).toString() + if (next != null) { + queryParams[NEXT_PAGE_QUERY_PARAM] = next.pageHash + } + return retrofitManager.filesService.listFiles( + configuration.subscribeKey, + channel, + queryParams, + ) + } + + @Throws(PubNubException::class) + override fun createResponse(input: Response): PNListFilesResult { + return input.body()?.let { body -> + PNListFilesResult( + body.count, + body.next, + body.status, + body.data, + ) + } ?: throw PubNubException(PubNubError.INTERNAL_ERROR) + } + + override fun getAffectedChannels() = listOf(channel) + + override fun getAffectedChannelGroups(): List = listOf() + + override fun operationType(): PNOperationType = PNOperationType.FileOperation + + override fun isAuthRequired(): Boolean = true + + override fun isSubKeyRequired(): Boolean = true + + override fun isPubKeyRequired(): Boolean = false + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.FILE_PERSISTENCE + + companion object { + private const val LIMIT_QUERY_PARAM = "limit" + private const val NEXT_PAGE_QUERY_PARAM = "next" + private const val DEFAULT_LIMIT = 100 + private const val MIN_LIMIT = 1 + private const val MAX_LIMIT = 100 + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/ListFilesImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/ListFilesImpl.kt deleted file mode 100644 index 2cd6aca9c..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/ListFilesImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.files - -import com.pubnub.api.endpoints.files.ListFiles -import com.pubnub.api.models.consumer.files.PNListFilesResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.listFiles] - */ -class ListFilesImpl internal constructor(listFiles: ListFilesInterface) : - ListFilesInterface by listFiles, - ListFiles, - EndpointImpl(listFiles) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/PublishFileMessageEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/PublishFileMessageEndpoint.kt new file mode 100644 index 000000000..db7c45f67 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/PublishFileMessageEndpoint.kt @@ -0,0 +1,106 @@ +package com.pubnub.internal.endpoints.files + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.files.PublishFileMessage +import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.files.PNBaseFile +import com.pubnub.api.models.consumer.files.PNPublishFileMessageResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.crypto.encryptString +import com.pubnub.internal.extension.numericString +import com.pubnub.internal.extension.quoted +import com.pubnub.internal.models.server.files.FileUploadNotification +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.publishFileMessage] + */ +open class PublishFileMessageEndpoint( + private val channel: String, + fileName: String, + fileId: String, + private val message: Any? = null, + private val meta: Any? = null, + private val ttl: Int? = null, + private val shouldStore: Boolean? = null, + pubNub: PubNubImpl, +) : EndpointCore, PNPublishFileMessageResult>(pubNub), PublishFileMessage { + private val pnFile = PNBaseFile(fileId, fileName) + + @Throws(PubNubException::class) + override fun validateParams() { + if (channel.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + } + + @Throws(PubNubException::class) + override fun doWork(queryParams: HashMap): Call> { + val stringifiedMessage: String = pubnub.mapper.toJson(FileUploadNotification(message, pnFile)) + val messageAsString = configuration.cryptoModule?.encryptString(stringifiedMessage)?.quoted() ?: stringifiedMessage + meta?.let { + val stringifiedMeta: String = pubnub.mapper.toJson(it) + queryParams["meta"] = stringifiedMeta + } + shouldStore?.numericString?.let { queryParams["store"] = it } + ttl?.let { queryParams["ttl"] = it.toString() } + + return retrofitManager.filesService.notifyAboutFileUpload( + configuration.publishKey, + configuration.subscribeKey, + channel, + messageAsString, + queryParams, + ) + } + + @Throws(PubNubException::class) + override fun createResponse(input: Response>): PNPublishFileMessageResult { + return input.body()?.let { body -> + val timetoken = body[2].toString().toLong() + PNPublishFileMessageResult(timetoken) + } ?: throw PubNubException(PubNubError.INTERNAL_ERROR) + } + + override fun getAffectedChannels() = listOf(channel) + + override fun getAffectedChannelGroups(): List = listOf() + + override fun operationType(): PNOperationType = PNOperationType.FileOperation + + override fun isAuthRequired(): Boolean = true + + override fun isSubKeyRequired(): Boolean = true + + override fun isPubKeyRequired(): Boolean = true + + internal class Factory(private val pubNub: PubNubImpl) { + fun create( + channel: String, + fileName: String, + fileId: String, + message: Any? = null, + meta: Any? = null, + ttl: Int? = null, + shouldStore: Boolean? = null, + ): ExtendedRemoteAction { + return PublishFileMessageEndpoint( + channel = channel, + fileName = fileName, + fileId = fileId, + message = message, + meta = meta, + ttl = ttl, + shouldStore = shouldStore, + pubNub = pubNub, + ) + } + } + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PUBLISH +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/PublishFileMessageImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/PublishFileMessageImpl.kt deleted file mode 100644 index bd9390952..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/PublishFileMessageImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.files - -import com.pubnub.api.endpoints.files.PublishFileMessage -import com.pubnub.api.models.consumer.files.PNPublishFileMessageResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.publishFileMessage] - */ -class PublishFileMessageImpl internal constructor(publishFileMessage: PublishFileMessageInterface) : - PublishFileMessageInterface by publishFileMessage, - PublishFileMessage, - EndpointImpl(publishFileMessage) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/SendFileEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/SendFileEndpoint.kt new file mode 100644 index 000000000..6bc5a2c05 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/SendFileEndpoint.kt @@ -0,0 +1,136 @@ +package com.pubnub.internal.endpoints.files + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.api.endpoints.files.SendFile +import com.pubnub.api.endpoints.remoteaction.ComposableRemoteAction +import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction +import com.pubnub.api.endpoints.remoteaction.map +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.files.PNBaseFile +import com.pubnub.api.models.consumer.files.PNFileUploadResult +import com.pubnub.api.models.consumer.files.PNPublishFileMessageResult +import com.pubnub.api.v2.callbacks.Result +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.crypto.cryptor.InputStreamSeparator +import com.pubnub.internal.endpoints.remoteaction.RetryingRemoteAction +import com.pubnub.internal.models.server.files.FileUploadRequestDetails +import java.io.InputStream +import java.net.HttpURLConnection +import java.util.concurrent.ExecutorService +import java.util.concurrent.atomic.AtomicReference +import java.util.function.Consumer + +/** + * @see [PubNubImpl.sendFile] + */ +class SendFileEndpoint internal constructor( + private val channel: String, + private val fileName: String, + inputStream: InputStream, + private val message: Any? = null, + private val meta: Any? = null, + private val ttl: Int? = null, + private val shouldStore: Boolean? = null, + private val fileMessagePublishRetryLimit: Int, + private val executorService: ExecutorService, + generateUploadUrlFactory: GenerateUploadUrlEndpoint.Factory, + publishFileMessageFactory: PublishFileMessageEndpoint.Factory, + sendFileToS3Factory: UploadFileEndpoint.Factory, + cryptoModule: CryptoModule? = null, +) : SendFile { + private val sendFileMultistepAction: ExtendedRemoteAction = + sendFileComposedActions( + generateUploadUrlFactory, + publishFileMessageFactory, + sendFileToS3Factory, + inputStream, + cryptoModule, + ) + + @Throws(PubNubException::class) + override fun sync(): PNFileUploadResult { + validate() + return sendFileMultistepAction.sync() + } + + override fun async(callback: Consumer>) { + executorService.execute { + try { + validate() + sendFileMultistepAction.async(callback) + } catch (ex: PubNubException) { + callback.accept( + Result.failure(ex), + ) + } + } + } + + @Throws(PubNubException::class) + private fun validate() { + if (channel.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + if (fileName.isEmpty()) { + throw PubNubException(PubNubError.INVALID_ARGUMENTS).copy(errorMessage = "File name cannot be null nor empty") + } + } + + private fun sendFileComposedActions( + generateUploadUrlFactory: GenerateUploadUrlEndpoint.Factory, + publishFileMessageFactory: PublishFileMessageEndpoint.Factory, + sendFileToS3Factory: UploadFileEndpoint.Factory, + inputStream: InputStream, + cryptoModule: CryptoModule?, + ): ExtendedRemoteAction { + val result = AtomicReference() + + val content = + cryptoModule?.encryptStream(InputStreamSeparator(inputStream))?.use { + it.readBytes() + } ?: inputStream.readBytes() + return ComposableRemoteAction.firstDo(generateUploadUrlFactory.create(channel, fileName)) // generateUrl + .then { res -> + result.set(res) + sendFileToS3Factory.create(fileName, content, res) // upload to s3 + }.checkpoint().then { + val details = result.get() + RetryingRemoteAction.autoRetry( + publishFileMessageFactory.create( + channel = channel, + fileName = details.data.name, + fileId = details.data.id, + message = message, + meta = meta, + ttl = ttl, + shouldStore = shouldStore, + ), + fileMessagePublishRetryLimit, + executorService, + ) // publish file message + }.map { mapPublishFileMessageToFileUpload(result.get(), it) } + } + + private fun mapPublishFileMessageToFileUpload( + requestDetails: FileUploadRequestDetails, + res: PNPublishFileMessageResult, + ) = PNFileUploadResult( + res.timetoken, + HttpURLConnection.HTTP_OK, + PNBaseFile(requestDetails.data.id, requestDetails.data.name), + ) + + override fun retry() { + sendFileMultistepAction.retry() + } + + override fun silentCancel() { + sendFileMultistepAction.silentCancel() + } + + override fun operationType(): PNOperationType { + return PNOperationType.FileOperation + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/SendFileImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/SendFileImpl.kt deleted file mode 100644 index 4f3dfed0d..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/SendFileImpl.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.pubnub.internal.endpoints.files - -import com.pubnub.api.endpoints.files.SendFile -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.sendFile] - */ -class SendFileImpl internal constructor(sendFile: SendFileInterface) : SendFileInterface by sendFile, SendFile diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/UploadFileEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/UploadFileEndpoint.kt new file mode 100644 index 000000000..b77373485 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/files/UploadFileEndpoint.kt @@ -0,0 +1,208 @@ +package com.pubnub.internal.endpoints.files + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.v2.callbacks.Result +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.files.FileUploadRequestDetails +import com.pubnub.internal.models.server.files.FormField +import com.pubnub.internal.services.S3Service +import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.MultipartBody +import okhttp3.RequestBody.Companion.toRequestBody +import org.slf4j.LoggerFactory +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import java.io.IOException +import java.net.SocketException +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import java.util.function.Consumer +import javax.net.ssl.SSLException +import javax.xml.parsers.DocumentBuilderFactory + +internal class UploadFileEndpoint( + private val s3Service: S3Service, + private val fileName: String, + private val content: ByteArray, + private val key: FormField, + private val formParams: List, + private val baseUrl: String, +) : ExtendedRemoteAction { + private var call: Call? = null + + @Throws(PubNubException::class) + private fun prepareCall(): Call { + val builder = MultipartBody.Builder().setType(MultipartBody.FORM) + addFormParamsWithKeyFirst(key, formParams, builder) + val mediaType = getMediaType(formParams.findContentType()) + + builder.addFormDataPart(FILE_PART_MULTIPART, fileName, content.toRequestBody(mediaType, 0, content.size)) + return s3Service.upload(baseUrl, builder.build()) + } + + private fun List.findContentType(): String? { + return find { (key, _) -> + key.equals(CONTENT_TYPE_HEADER, ignoreCase = true) + }?.value + } + + private fun getMediaType(contentType: String?): MediaType { + return if (contentType == null) { + APPLICATION_OCTET_STREAM + } else { + try { + contentType.toMediaType() + } catch (t: Throwable) { + log.warn("Content-Type: $contentType was not recognized by MediaType.get", t) + APPLICATION_OCTET_STREAM + } + } + } + + @Throws(PubNubException::class) + override fun sync() { + call = prepareCall() + val serverResponse = + try { + call!!.execute() + } catch (e: IOException) { + throw PubNubException( + errorMessage = e.message, + affectedCall = call, + pubnubError = PubNubError.PARSING_ERROR, + cause = e, + ) + } + if (!serverResponse.isSuccessful) { + throw createException(serverResponse) + } + } + + override fun async(callback: Consumer>) { + try { + call = prepareCall() + call!!.enqueue( + object : Callback { + override fun onResponse( + performedCall: Call, + response: Response, + ) { + if (!response.isSuccessful) { + val ex = createException(response) + callback.accept(Result.failure(ex)) + return + } + callback.accept(Result.success(Unit)) + } + + override fun onFailure( + performedCall: Call, + throwable: Throwable, + ) { + if (call!!.isCanceled) { + return + } + val error = + when (throwable) { + is UnknownHostException, is SocketException, is SSLException -> PubNubError.CONNECT_EXCEPTION + is SocketTimeoutException -> PubNubError.SUBSCRIBE_TIMEOUT + else -> + if (performedCall.isCanceled) { + PubNubError.HTTP_ERROR + } else { + PubNubError.HTTP_ERROR + } + } + callback.accept( + Result.failure( + PubNubException(error).copy( + errorMessage = throwable.message ?: error.message, + cause = throwable, + ), + ), + ) + } + }, + ) + } catch (e: Throwable) { + callback.accept(Result.failure(PubNubException.from(e))) + } + } + + override fun retry() {} + + override fun silentCancel() { + if (!call!!.isCanceled) { + call!!.cancel() + } + } + + private fun createException(response: Response): PubNubException { + return try { + PubNubException( + errorMessage = response.readErrorMessage(), + affectedCall = call, + statusCode = response.code(), + ) + } catch (e: Exception) { + PubNubException( + errorMessage = e.message, + affectedCall = call, + statusCode = response.code(), + ) + } + } + + private fun Response.readErrorMessage(): String { + val dbFactory = DocumentBuilderFactory.newInstance() + dbFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) + dbFactory.isXIncludeAware = false + val dBuilder = dbFactory.newDocumentBuilder() + val doc = dBuilder.parse(errorBody()!!.byteStream()) + doc.documentElement.normalize() + val elements = doc.getElementsByTagName("Message") + return elements.item(0)?.firstChild?.nodeValue ?: "N/A" + } + + internal class Factory(private val pubNub: PubNubImpl) { + fun create( + fileName: String, + content: ByteArray, + fileUploadRequestDetails: FileUploadRequestDetails, + ): ExtendedRemoteAction { + return UploadFileEndpoint( + pubNub.retrofitManager.s3Service, + fileName, + content, + fileUploadRequestDetails.keyFormField, + fileUploadRequestDetails.formFields, + fileUploadRequestDetails.url, + ) + } + } + + companion object { + private val APPLICATION_OCTET_STREAM = "application/octet-stream".toMediaType() + private const val CONTENT_TYPE_HEADER = "Content-Type" + private const val FILE_PART_MULTIPART = "file" + private val log = LoggerFactory.getLogger(UploadFileEndpoint::class.java) + + private fun addFormParamsWithKeyFirst( + keyValue: FormField, + formParams: List, + builder: MultipartBody.Builder, + ) { + builder.addFormDataPart(keyValue.key, keyValue.value) + formParams + .filter { it.key != keyValue.key } + .forEach { builder.addFormDataPart(it.key, it.value) } + } + } + + override fun operationType(): PNOperationType = PNOperationType.FileOperation +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/AddMessageActionEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/AddMessageActionEndpoint.kt new file mode 100644 index 000000000..fec91da58 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/AddMessageActionEndpoint.kt @@ -0,0 +1,66 @@ +package com.pubnub.internal.endpoints.message_actions + +import com.google.gson.JsonObject +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.message_actions.AddMessageAction +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.message_actions.PNAddMessageActionResult +import com.pubnub.api.models.consumer.message_actions.PNMessageAction +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.objects_api.EntityEnvelope +import retrofit2.Call +import retrofit2.Response +import java.util.Locale + +/** + * @see [PubNubImpl.addMessageAction] + */ +class AddMessageActionEndpoint internal constructor( + pubnub: PubNubImpl, + override val channel: String, + override val messageAction: PNMessageAction, +) : EndpointCore, PNAddMessageActionResult>(pubnub), AddMessageAction { + override fun validateParams() { + super.validateParams() + if (channel.isBlank()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + if (messageAction.type.isBlank()) { + throw PubNubException(PubNubError.MESSAGE_ACTION_TYPE_MISSING) + } + if (messageAction.value.isBlank()) { + throw PubNubException(PubNubError.MESSAGE_ACTION_VALUE_MISSING) + } + } + + override fun getAffectedChannels() = listOf(channel) + + override fun doWork(queryParams: HashMap): Call> { + val body = + JsonObject().apply { + addProperty("type", messageAction.type) + addProperty("value", messageAction.value) + } + + return retrofitManager.messageActionService + .addMessageAction( + subKey = configuration.subscribeKey, + channel = channel, + messageTimetoken = messageAction.messageTimetoken.toString().lowercase(Locale.getDefault()), + body = body, + options = queryParams, + ) + } + + override fun createResponse(input: Response>): PNAddMessageActionResult = + PNAddMessageActionResult( + action = input.body()!!.data!!, + ) + + override fun operationType() = PNOperationType.PNAddMessageAction + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.MESSAGE_REACTION +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/AddMessageActionImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/AddMessageActionImpl.kt deleted file mode 100644 index 7e9b4a30a..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/AddMessageActionImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.message_actions - -import com.pubnub.api.endpoints.message_actions.AddMessageAction -import com.pubnub.api.models.consumer.message_actions.PNAddMessageActionResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.addMessageAction] - */ -class AddMessageActionImpl internal constructor(addMessageAction: AddMessageActionInterface) : - AddMessageActionInterface by addMessageAction, - AddMessageAction, - EndpointImpl(addMessageAction) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/GetMessageActionsEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/GetMessageActionsEndpoint.kt new file mode 100644 index 000000000..07b3455ac --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/GetMessageActionsEndpoint.kt @@ -0,0 +1,59 @@ +package com.pubnub.internal.endpoints.message_actions + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.message_actions.GetMessageActions +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.PNBoundedPage +import com.pubnub.api.models.consumer.message_actions.PNGetMessageActionsResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import retrofit2.Call +import retrofit2.Response +import java.util.Locale + +/** + * @see [PubNubImpl.getMessageActions] + */ +class GetMessageActionsEndpoint internal constructor( + pubnub: PubNubImpl, + override val channel: String, + override val page: PNBoundedPage, +) : EndpointCore(pubnub), GetMessageActions { + override fun validateParams() { + super.validateParams() + if (channel.isBlank()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + } + + override fun getAffectedChannels() = listOf(channel) + + override fun doWork(queryParams: HashMap): Call { + addQueryParams(queryParams) + + return retrofitManager.messageActionService + .getMessageActions( + configuration.subscribeKey, + channel, + queryParams, + ) + } + + override fun createResponse(input: Response): PNGetMessageActionsResult = + PNGetMessageActionsResult( + actions = input.body()!!.actions, + page = input.body()!!.page, + ) + + override fun operationType() = PNOperationType.PNGetMessageActions + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.MESSAGE_REACTION + + private fun addQueryParams(queryParams: MutableMap) { + page.start?.run { queryParams["start"] = this.toString().lowercase(Locale.getDefault()) } + page.end?.run { queryParams["end"] = this.toString().lowercase(Locale.getDefault()) } + page.limit?.run { queryParams["limit"] = this.toString().lowercase(Locale.getDefault()) } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/GetMessageActionsImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/GetMessageActionsImpl.kt deleted file mode 100644 index 0db56d267..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/GetMessageActionsImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.message_actions - -import com.pubnub.api.endpoints.message_actions.GetMessageActions -import com.pubnub.api.models.consumer.message_actions.PNGetMessageActionsResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.getMessageActions] - */ -class GetMessageActionsImpl internal constructor(getMessageActions: GetMessageActionsInterface) : - GetMessageActionsInterface by getMessageActions, - GetMessageActions, - EndpointImpl(getMessageActions) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/RemoveMessageActionEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/RemoveMessageActionEndpoint.kt new file mode 100644 index 000000000..82126bb31 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/RemoveMessageActionEndpoint.kt @@ -0,0 +1,51 @@ +package com.pubnub.internal.endpoints.message_actions + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.message_actions.RemoveMessageAction +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.message_actions.PNRemoveMessageActionResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import retrofit2.Call +import retrofit2.Response +import java.util.Locale + +/** + * @see [PubNubImpl.removeMessageAction] + */ +class RemoveMessageActionEndpoint internal constructor( + pubnub: PubNubImpl, + override val channel: String, + override val messageTimetoken: Long, + override val actionTimetoken: Long, +) : EndpointCore(pubnub), RemoveMessageAction { + override fun validateParams() { + super.validateParams() + if (channel.isBlank()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + } + + override fun getAffectedChannels() = listOf(channel) + + override fun doWork(queryParams: HashMap): Call { + return retrofitManager.messageActionService + .deleteMessageAction( + subKey = configuration.subscribeKey, + channel = channel, + messageTimetoken = messageTimetoken.toString().lowercase(Locale.getDefault()), + actionTimetoken = actionTimetoken.toString().lowercase(Locale.getDefault()), + options = queryParams, + ) + } + + override fun createResponse(input: Response): PNRemoveMessageActionResult { + return PNRemoveMessageActionResult() + } + + override fun operationType() = PNOperationType.PNDeleteMessageAction + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.MESSAGE_REACTION +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/RemoveMessageActionImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/RemoveMessageActionImpl.kt deleted file mode 100644 index 847eb143b..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/message_actions/RemoveMessageActionImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.message_actions - -import com.pubnub.api.endpoints.message_actions.RemoveMessageAction -import com.pubnub.api.models.consumer.message_actions.PNRemoveMessageActionResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.removeMessageAction] - */ -class RemoveMessageActionImpl internal constructor(removeMessageAction: RemoveMessageActionInterface) : - RemoveMessageActionInterface by removeMessageAction, - RemoveMessageAction, - EndpointImpl(removeMessageAction) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetAllChannelMetadataEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetAllChannelMetadataEndpoint.kt new file mode 100644 index 000000000..064635fe4 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetAllChannelMetadataEndpoint.kt @@ -0,0 +1,51 @@ +package com.pubnub.internal.endpoints.objects.channel + +import com.pubnub.api.endpoints.objects.channel.GetAllChannelMetadata +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.objects.PNPage +import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata +import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadataArrayResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.objects.internal.CollectionQueryParameters +import com.pubnub.internal.endpoints.objects.internal.IncludeQueryParam +import com.pubnub.internal.models.server.objects_api.EntityArrayEnvelope +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.getAllChannelMetadata] + */ +class GetAllChannelMetadataEndpoint internal constructor( + pubnub: PubNubImpl, + private val collectionQueryParameters: CollectionQueryParameters, + private val includeQueryParam: IncludeQueryParam, +) : EndpointCore, PNChannelMetadataArrayResult>(pubnub), + GetAllChannelMetadata { + override fun doWork(queryParams: HashMap): Call> { + val params = + queryParams + collectionQueryParameters.createCollectionQueryParams() + includeQueryParam.createIncludeQueryParams() + + return retrofitManager.objectsService.getAllChannelMetadata( + subKey = configuration.subscribeKey, + options = params, + ) + } + + override fun createResponse(input: Response>): PNChannelMetadataArrayResult { + return input.body()!!.let { arrayEnvelope -> + PNChannelMetadataArrayResult( + status = arrayEnvelope.status, + data = arrayEnvelope.data, + prev = arrayEnvelope.prev?.let { PNPage.PNPrev(it) }, + next = arrayEnvelope.next?.let { PNPage.PNNext(it) }, + totalCount = arrayEnvelope.totalCount, + ) + } + } + + override fun operationType(): PNOperationType = PNOperationType.PNGetAllChannelsMetadataOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.APP_CONTEXT +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetAllChannelMetadataImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetAllChannelMetadataImpl.kt deleted file mode 100644 index 307a457a5..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetAllChannelMetadataImpl.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.pubnub.internal.endpoints.objects.channel - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction -import com.pubnub.internal.DelegatingEndpoint -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.models.consumer.objects.channel.PNChannelMetadataArrayResult - -/** - * @see [PubNubImpl.getAllChannelMetadata] - */ -class GetAllChannelMetadataImpl internal constructor(getAllChannelMetadata: GetAllChannelMetadataEndpoint) : - DelegatingEndpoint( - getAllChannelMetadata, - ), - GetAllChannelMetadataInterface by getAllChannelMetadata, - com.pubnub.api.endpoints.objects.channel.GetAllChannelMetadata { - override fun convertAction( - remoteAction: ExtendedRemoteAction, - ): ExtendedRemoteAction { - return MappingRemoteAction( - remoteAction, - PNChannelMetadataArrayResult::toApi, - ) - } - } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetChannelMetadataEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetChannelMetadataEndpoint.kt new file mode 100644 index 000000000..e0b7ae22d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetChannelMetadataEndpoint.kt @@ -0,0 +1,44 @@ +package com.pubnub.internal.endpoints.objects.channel + +import com.pubnub.api.endpoints.objects.channel.GetChannelMetadata +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata +import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadataResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.objects.internal.IncludeQueryParam +import com.pubnub.internal.models.server.objects_api.EntityEnvelope +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.getChannelMetadata] + */ +class GetChannelMetadataEndpoint internal constructor( + pubnub: PubNubImpl, + private val channel: String, + private val includeQueryParam: IncludeQueryParam, +) : EndpointCore, PNChannelMetadataResult>(pubnub), GetChannelMetadata { + override fun doWork(queryParams: HashMap): Call> { + val params = queryParams + includeQueryParam.createIncludeQueryParams() + return retrofitManager.objectsService.getChannelMetadata( + subKey = configuration.subscribeKey, + channel = channel, + options = params, + ) + } + + override fun createResponse(input: Response>): PNChannelMetadataResult { + return input.body()!!.let { + PNChannelMetadataResult( + status = it.status, + data = it.data, + ) + } + } + + override fun operationType(): PNOperationType = PNOperationType.PNGetChannelMetadataOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.APP_CONTEXT +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetChannelMetadataImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetChannelMetadataImpl.kt deleted file mode 100644 index 1bf7d44b5..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/GetChannelMetadataImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.objects.channel - -import com.pubnub.api.endpoints.objects.channel.GetChannelMetadata -import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadataResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.getChannelMetadata] - */ -class GetChannelMetadataImpl internal constructor(getChannelMetadata: GetChannelMetadataInterface) : - GetChannelMetadata, - GetChannelMetadataInterface by getChannelMetadata, - EndpointImpl(getChannelMetadata) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/RemoveChannelMetadataEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/RemoveChannelMetadataEndpoint.kt new file mode 100644 index 000000000..8e0c311bc --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/RemoveChannelMetadataEndpoint.kt @@ -0,0 +1,31 @@ +package com.pubnub.internal.endpoints.objects.channel + +import com.pubnub.api.endpoints.objects.channel.RemoveChannelMetadata +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.objects.PNRemoveMetadataResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.objects_api.EntityEnvelope +import retrofit2.Call +import retrofit2.Response + +class RemoveChannelMetadataEndpoint( + pubnub: PubNubImpl, + private val channel: String, +) : EndpointCore, PNRemoveMetadataResult>(pubnub), RemoveChannelMetadata { + override fun doWork(queryParams: HashMap): Call> { + return retrofitManager.objectsService.deleteChannelMetadata( + subKey = configuration.subscribeKey, + channel = channel, + ) + } + + override fun createResponse(input: Response>): PNRemoveMetadataResult { + return PNRemoveMetadataResult(input.body()!!.status) + } + + override fun operationType(): PNOperationType = PNOperationType.PNRemoveChannelMetadataOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.APP_CONTEXT +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/RemoveChannelMetadataImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/RemoveChannelMetadataImpl.kt deleted file mode 100644 index 732ef8833..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/RemoveChannelMetadataImpl.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.pubnub.internal.endpoints.objects.channel - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction -import com.pubnub.api.models.consumer.objects.PNRemoveMetadataResult -import com.pubnub.internal.DelegatingEndpoint -import com.pubnub.internal.models.from - -class RemoveChannelMetadataImpl internal constructor(removeChannelMetadata: RemoveChannelMetadataEndpoint) : - DelegatingEndpoint( - removeChannelMetadata, - ), - RemoveChannelMetadataInterface by removeChannelMetadata, - com.pubnub.api.endpoints.objects.channel.RemoveChannelMetadata { - override fun convertAction( - remoteAction: ExtendedRemoteAction, - ): ExtendedRemoteAction { - return MappingRemoteAction(remoteAction, ::from) - } - } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/SetChannelMetadataEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/SetChannelMetadataEndpoint.kt new file mode 100644 index 000000000..1e9d8b497 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/SetChannelMetadataEndpoint.kt @@ -0,0 +1,59 @@ +package com.pubnub.internal.endpoints.objects.channel + +import com.pubnub.api.endpoints.objects.channel.SetChannelMetadata +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata +import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadataResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.objects.internal.IncludeQueryParam +import com.pubnub.internal.models.server.objects_api.ChannelMetadataInput +import com.pubnub.internal.models.server.objects_api.EntityEnvelope +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.setChannelMetadata] + */ +class SetChannelMetadataEndpoint internal constructor( + pubnub: PubNubImpl, + private val name: String?, + private val description: String?, + private val custom: Any?, + private val channel: String, + private val includeQueryParam: IncludeQueryParam, + private val type: String?, + private val status: String?, +) : EndpointCore, PNChannelMetadataResult>(pubnub), + SetChannelMetadata { + override fun doWork(queryParams: HashMap): Call> { + val params = queryParams + includeQueryParam.createIncludeQueryParams() + return retrofitManager.objectsService.setChannelMetadata( + subKey = configuration.subscribeKey, + body = + ChannelMetadataInput( + name = name, + custom = custom, + description = description, + status = status, + type = type, + ), + channel = channel, + options = params, + ) + } + + override fun createResponse(input: Response>): PNChannelMetadataResult { + return input.body()!!.let { + PNChannelMetadataResult( + status = it.status, + data = it.data, + ) + } + } + + override fun operationType(): PNOperationType = PNOperationType.PNSetChannelMetadataOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.APP_CONTEXT +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/SetChannelMetadataImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/SetChannelMetadataImpl.kt deleted file mode 100644 index efda2fe09..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/channel/SetChannelMetadataImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.objects.channel - -import com.pubnub.api.endpoints.objects.channel.SetChannelMetadata -import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadataResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.setChannelMetadata] - */ -class SetChannelMetadataImpl internal constructor(setChannelMetadata: SetChannelMetadataInterface) : - SetChannelMetadata, - SetChannelMetadataInterface by setChannelMetadata, - EndpointImpl(setChannelMetadata) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/internal/CollectionQueryParameters.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/internal/CollectionQueryParameters.kt new file mode 100644 index 000000000..f6bb0b6ee --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/internal/CollectionQueryParameters.kt @@ -0,0 +1,37 @@ +package com.pubnub.internal.endpoints.objects.internal + +import com.pubnub.api.models.consumer.objects.PNPage +import com.pubnub.api.models.consumer.objects.PNSortKey + +data class CollectionQueryParameters( + private val limit: Int? = null, + private val page: PNPage? = null, + private val filter: String? = null, + private val sort: Collection> = listOf(), + private val includeCount: Boolean = false, +) { + internal fun createCollectionQueryParams(): Map { + val additionalParams = mutableMapOf() + val f = filter + if (f != null) { + additionalParams["filter"] = f + } + if (sort.isNotEmpty()) { + additionalParams["sort"] = + sort.joinToString(",") { it.toSortParameter() } + } + if (limit != null) { + additionalParams["limit"] = limit.toString() + } + if (includeCount) { + additionalParams["count"] = includeCount.toString() + } + val p = page + when (p) { + is PNPage.PNNext -> additionalParams["start"] = p.pageHash + is PNPage.PNPrev -> additionalParams["end"] = p.pageHash + null -> {} + } + return additionalParams.toMap() + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/internal/IncludeQueryParam.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/internal/IncludeQueryParam.kt new file mode 100644 index 000000000..8be289b8b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/internal/IncludeQueryParam.kt @@ -0,0 +1,48 @@ +package com.pubnub.internal.endpoints.objects.internal + +import com.pubnub.api.models.consumer.objects.member.PNUUIDDetailsLevel +import com.pubnub.api.models.consumer.objects.membership.PNChannelDetailsLevel + +data class IncludeQueryParam( + private val includeCustom: Boolean = false, + private val includeChannelDetails: PNChannelDetailsLevel? = null, + private val includeUUIDDetails: PNUUIDDetailsLevel? = null, + private val includeType: Boolean = true, + private val includeStatus: Boolean = true, + private val includeChannelType: Boolean = false, + private val includeUuidType: Boolean = false +) { + internal fun createIncludeQueryParams(): Map { + val includeList = mutableListOf() + if (includeCustom) { + includeList.add("custom") + } + when (includeChannelDetails) { + PNChannelDetailsLevel.CHANNEL -> includeList.add("channel") + PNChannelDetailsLevel.CHANNEL_WITH_CUSTOM -> includeList.add("channel.custom") + null -> {} + } + when (includeUUIDDetails) { + PNUUIDDetailsLevel.UUID -> includeList.add("uuid") + PNUUIDDetailsLevel.UUID_WITH_CUSTOM -> includeList.add("uuid.custom") + null -> {} + } + if (includeType) { + includeList.add("type") + } + if (includeChannelType) { + includeList.add("channel.type") + } + if (includeUuidType) { + includeList.add("uuid.type") + } + if (includeStatus) { + includeList.add("status") + } + return if (includeList.isNotEmpty()) { + mapOf("include" to includeList.joinToString(",")) + } else { + mapOf() + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/GetChannelMembersEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/GetChannelMembersEndpoint.kt new file mode 100644 index 000000000..611bedbce --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/GetChannelMembersEndpoint.kt @@ -0,0 +1,42 @@ +package com.pubnub.internal.endpoints.objects.member + +import com.pubnub.api.endpoints.objects.member.GetChannelMembers +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.objects.member.PNMember +import com.pubnub.api.models.consumer.objects.member.PNMemberArrayResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.objects.internal.CollectionQueryParameters +import com.pubnub.internal.endpoints.objects.internal.IncludeQueryParam +import com.pubnub.internal.extension.toPNMemberArrayResult +import com.pubnub.internal.models.server.objects_api.EntityArrayEnvelope +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.getChannelMembers] + */ +class GetChannelMembersEndpoint internal constructor( + pubnub: PubNubImpl, + private val channel: String, + private val collectionQueryParameters: CollectionQueryParameters, + private val includeQueryParam: IncludeQueryParam, +) : EndpointCore, PNMemberArrayResult>(pubnub), GetChannelMembers { + override fun doWork(queryParams: HashMap): Call> { + val params = + queryParams + collectionQueryParameters.createCollectionQueryParams() + includeQueryParam.createIncludeQueryParams() + + return retrofitManager.objectsService.getChannelMembers( + channel = channel, + subKey = configuration.subscribeKey, + options = params, + ) + } + + override fun createResponse(input: Response>): PNMemberArrayResult = input.toPNMemberArrayResult() + + override fun operationType(): PNOperationType = PNOperationType.ObjectsOperation() + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.APP_CONTEXT +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/GetChannelMembersImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/GetChannelMembersImpl.kt deleted file mode 100644 index 5b24553a6..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/GetChannelMembersImpl.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.pubnub.internal.endpoints.objects.member - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction -import com.pubnub.internal.DelegatingEndpoint -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.models.consumer.objects.member.PNMemberArrayResult -import com.pubnub.internal.models.from - -/** - * @see [PubNubImpl.getChannelMembers] - */ -class GetChannelMembersImpl internal constructor(getChannelMembers: GetChannelMembersEndpoint) : - DelegatingEndpoint( - getChannelMembers, - ), - GetChannelMembersInterface by getChannelMembers, - com.pubnub.api.endpoints.objects.member.GetChannelMembers { - override fun convertAction( - remoteAction: ExtendedRemoteAction, - ): ExtendedRemoteAction { - return MappingRemoteAction( - remoteAction, - ::from, - ) - } - } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/ManageChannelMembersEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/ManageChannelMembersEndpoint.kt new file mode 100644 index 000000000..5dfc1080e --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/ManageChannelMembersEndpoint.kt @@ -0,0 +1,60 @@ +package com.pubnub.internal.endpoints.objects.member + +import com.pubnub.api.endpoints.objects.member.ManageChannelMembers +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.objects.member.MemberInput +import com.pubnub.api.models.consumer.objects.member.PNMember +import com.pubnub.api.models.consumer.objects.member.PNMemberArrayResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.objects.internal.CollectionQueryParameters +import com.pubnub.internal.endpoints.objects.internal.IncludeQueryParam +import com.pubnub.internal.extension.toPNMemberArrayResult +import com.pubnub.internal.models.server.objects_api.ChangeMemberInput +import com.pubnub.internal.models.server.objects_api.EntityArrayEnvelope +import com.pubnub.internal.models.server.objects_api.ServerMemberInput +import com.pubnub.internal.models.server.objects_api.UUIDId +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.manageChannelMembers] + */ +class ManageChannelMembersEndpoint( + pubnub: PubNubImpl, + private val uuidsToSet: Collection, + private val uuidsToRemove: Collection, + private val channel: String, + private val collectionQueryParameters: CollectionQueryParameters, + private val includeQueryParam: IncludeQueryParam, +) : EndpointCore, PNMemberArrayResult>(pubnub), ManageChannelMembers { + override fun doWork(queryParams: HashMap): Call> { + val params = + queryParams + collectionQueryParameters.createCollectionQueryParams() + includeQueryParam.createIncludeQueryParams() + + return retrofitManager.objectsService.patchChannelMembers( + channel = channel, + subKey = configuration.subscribeKey, + options = params, + body = + ChangeMemberInput( + delete = uuidsToRemove.map { ServerMemberInput(UUIDId(id = it)) }, + set = + uuidsToSet.map { + ServerMemberInput( + uuid = UUIDId(id = it.uuid), + custom = it.custom, + status = it.status, + ) + }, + ), + ) + } + + override fun createResponse(input: Response>): PNMemberArrayResult = input.toPNMemberArrayResult() + + override fun operationType(): PNOperationType = PNOperationType.ObjectsOperation() + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.APP_CONTEXT +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/ManageChannelMembersImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/ManageChannelMembersImpl.kt deleted file mode 100644 index 582356e99..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/member/ManageChannelMembersImpl.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.pubnub.internal.endpoints.objects.member - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction -import com.pubnub.internal.DelegatingEndpoint -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.models.consumer.objects.member.PNMemberArrayResult -import com.pubnub.internal.models.from - -/** - * @see [PubNubImpl.manageChannelMembers] - */ -class ManageChannelMembersImpl internal constructor(manageChannelMembers: ManageChannelMembersEndpoint) : - DelegatingEndpoint( - manageChannelMembers, - ), - ManageChannelMembersInterface by manageChannelMembers, - com.pubnub.api.endpoints.objects.member.ManageChannelMembers { - override fun convertAction( - remoteAction: ExtendedRemoteAction, - ): ExtendedRemoteAction { - return MappingRemoteAction( - remoteAction, - ::from, - ) - } - } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/GetMembershipsEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/GetMembershipsEndpoint.kt new file mode 100644 index 000000000..a4e9b33ca --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/GetMembershipsEndpoint.kt @@ -0,0 +1,44 @@ +package com.pubnub.internal.endpoints.objects.membership + +import com.pubnub.api.endpoints.objects.membership.GetMemberships +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembership +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.objects.internal.CollectionQueryParameters +import com.pubnub.internal.endpoints.objects.internal.IncludeQueryParam +import com.pubnub.internal.extension.toPNChannelMembershipArrayResult +import com.pubnub.internal.models.server.objects_api.EntityArrayEnvelope +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.getMemberships] + */ +class GetMembershipsEndpoint internal constructor( + pubnub: PubNubImpl, + private val uuid: String, + private val collectionQueryParameters: CollectionQueryParameters, + private val includeQueryParam: IncludeQueryParam, +) : EndpointCore, PNChannelMembershipArrayResult>(pubnub), + GetMemberships { + override fun doWork(queryParams: HashMap): Call> { + val params = + queryParams + collectionQueryParameters.createCollectionQueryParams() + includeQueryParam.createIncludeQueryParams() + + return retrofitManager.objectsService.getMemberships( + uuid = uuid, + subKey = configuration.subscribeKey, + options = params, + ) + } + + override fun createResponse(input: Response>): PNChannelMembershipArrayResult = + input.toPNChannelMembershipArrayResult() + + override fun operationType(): PNOperationType = PNOperationType.PNGetMembershipsOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.APP_CONTEXT +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/GetMembershipsImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/GetMembershipsImpl.kt deleted file mode 100644 index d5fedb403..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/GetMembershipsImpl.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.pubnub.internal.endpoints.objects.membership - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction -import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult -import com.pubnub.internal.DelegatingEndpoint -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.models.toApi - -/** - * @see [PubNubImpl.getMemberships] - */ -class GetMembershipsImpl internal constructor(getMemberships: GetMembershipsEndpoint) : - DelegatingEndpoint( - getMemberships, - ), - GetMembershipsInterface by getMemberships, - com.pubnub.api.endpoints.objects.membership.GetMemberships { - override fun convertAction( - remoteAction: ExtendedRemoteAction, - ): ExtendedRemoteAction { - return MappingRemoteAction( - remoteAction, - com.pubnub.internal.models.consumer.objects.membership.PNChannelMembershipArrayResult::toApi, - ) - } - } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/ManageMembershipsEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/ManageMembershipsEndpoint.kt new file mode 100644 index 000000000..f6bea51e8 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/ManageMembershipsEndpoint.kt @@ -0,0 +1,62 @@ +package com.pubnub.internal.endpoints.objects.membership + +import com.pubnub.api.endpoints.objects.membership.ManageMemberships +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.objects.membership.ChannelMembershipInput +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembership +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.objects.internal.CollectionQueryParameters +import com.pubnub.internal.endpoints.objects.internal.IncludeQueryParam +import com.pubnub.internal.extension.toPNChannelMembershipArrayResult +import com.pubnub.internal.models.server.objects_api.ChangeMembershipInput +import com.pubnub.internal.models.server.objects_api.ChannelId +import com.pubnub.internal.models.server.objects_api.EntityArrayEnvelope +import com.pubnub.internal.models.server.objects_api.ServerMembershipInput +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.manageMemberships] + */ +class ManageMembershipsEndpoint internal constructor( + pubnub: PubNubImpl, + private val channelsToSet: Collection, + private val channelsToRemove: Collection, + private val uuid: String, + private val collectionQueryParameters: CollectionQueryParameters, + private val includeQueryParam: IncludeQueryParam, +) : EndpointCore, PNChannelMembershipArrayResult>(pubnub), + ManageMemberships { + override fun doWork(queryParams: HashMap): Call> { + val params = + queryParams + collectionQueryParameters.createCollectionQueryParams() + + includeQueryParam.createIncludeQueryParams() + return retrofitManager.objectsService.patchMemberships( + uuid = uuid, + subKey = configuration.subscribeKey, + options = params, + body = + ChangeMembershipInput( + set = + channelsToSet.map { + ServerMembershipInput( + channel = ChannelId(id = it.channel), + custom = it.custom, + status = it.status, + ) + }, + delete = channelsToRemove.map { ServerMembershipInput(channel = ChannelId(id = it)) }, + ), + ) + } + + override fun createResponse(input: Response>): PNChannelMembershipArrayResult = + input.toPNChannelMembershipArrayResult() + + override fun operationType(): PNOperationType = PNOperationType.ObjectsOperation() + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.APP_CONTEXT +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/ManageMembershipsImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/ManageMembershipsImpl.kt deleted file mode 100644 index 0cf63323d..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/membership/ManageMembershipsImpl.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.pubnub.internal.endpoints.objects.membership - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction -import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult -import com.pubnub.internal.DelegatingEndpoint -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.models.toApi - -/** - * @see [PubNubImpl.manageMemberships] - */ -class ManageMembershipsImpl internal constructor(manageMemberships: ManageMembershipsEndpoint) : - DelegatingEndpoint( - manageMemberships, - ), - ManageMembershipsInterface by manageMemberships, - com.pubnub.api.endpoints.objects.membership.ManageMemberships { - override fun convertAction( - remoteAction: ExtendedRemoteAction, - ): ExtendedRemoteAction { - return MappingRemoteAction( - remoteAction, - com.pubnub.internal.models.consumer.objects.membership.PNChannelMembershipArrayResult::toApi, - ) - } - } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetAllUUIDMetadataEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetAllUUIDMetadataEndpoint.kt new file mode 100644 index 000000000..33a715995 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetAllUUIDMetadataEndpoint.kt @@ -0,0 +1,51 @@ +package com.pubnub.internal.endpoints.objects.uuid + +import com.pubnub.api.endpoints.objects.uuid.GetAllUUIDMetadata +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.objects.PNPage +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadataArrayResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.objects.internal.CollectionQueryParameters +import com.pubnub.internal.endpoints.objects.internal.IncludeQueryParam +import com.pubnub.internal.models.server.objects_api.EntityArrayEnvelope +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.getAllUUIDMetadata] + */ +class GetAllUUIDMetadataEndpoint internal constructor( + pubnub: PubNubImpl, + private val collectionQueryParameters: CollectionQueryParameters, + private val withInclude: IncludeQueryParam, +) : EndpointCore, PNUUIDMetadataArrayResult>(pubnub), + GetAllUUIDMetadata { + override fun doWork(queryParams: HashMap): Call> { + val params = + queryParams + collectionQueryParameters.createCollectionQueryParams() + withInclude.createIncludeQueryParams() + + return retrofitManager.objectsService.getAllUUIDMetadata( + subKey = configuration.subscribeKey, + options = params, + ) + } + + override fun createResponse(input: Response>): PNUUIDMetadataArrayResult { + return input.body()!!.let { arrayEnvelope -> + PNUUIDMetadataArrayResult( + status = arrayEnvelope.status, + data = arrayEnvelope.data, + prev = arrayEnvelope.prev?.let { PNPage.PNPrev(it) }, + next = arrayEnvelope.next?.let { PNPage.PNNext(it) }, + totalCount = arrayEnvelope.totalCount, + ) + } + } + + override fun operationType(): PNOperationType = PNOperationType.PNGetAllUUIDMetadataOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.APP_CONTEXT +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetAllUUIDMetadataImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetAllUUIDMetadataImpl.kt deleted file mode 100644 index 512cef1fe..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetAllUUIDMetadataImpl.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.pubnub.internal.endpoints.objects.uuid - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction -import com.pubnub.internal.DelegatingEndpoint -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.models.consumer.objects.uuid.PNUUIDMetadataArrayResult -import com.pubnub.internal.models.from - -/** - * @see [PubNubImpl.getAllUUIDMetadata] - */ -class GetAllUUIDMetadataImpl internal constructor(getAllUUIDMetadata: GetAllUUIDMetadataEndpoint) : - DelegatingEndpoint( - getAllUUIDMetadata, - ), - GetAllUUIDMetadataInterface by getAllUUIDMetadata, - com.pubnub.api.endpoints.objects.uuid.GetAllUUIDMetadata { - override fun convertAction( - remoteAction: ExtendedRemoteAction, - ): ExtendedRemoteAction { - return MappingRemoteAction( - remoteAction, - ::from, - ) - } - } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetUUIDMetadataEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetUUIDMetadataEndpoint.kt new file mode 100644 index 000000000..c72bf298b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetUUIDMetadataEndpoint.kt @@ -0,0 +1,44 @@ +package com.pubnub.internal.endpoints.objects.uuid + +import com.pubnub.api.endpoints.objects.uuid.GetUUIDMetadata +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadataResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.objects.internal.IncludeQueryParam +import com.pubnub.internal.models.server.objects_api.EntityEnvelope +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.getUUIDMetadata] + */ +class GetUUIDMetadataEndpoint internal constructor( + pubnub: PubNubImpl, + override val uuid: String, + private val includeQueryParam: IncludeQueryParam, +) : EndpointCore, PNUUIDMetadataResult>(pubnub), GetUUIDMetadata { + override fun doWork(queryParams: HashMap): Call> { + val params = queryParams + includeQueryParam.createIncludeQueryParams() + return retrofitManager.objectsService.getUUIDMetadata( + subKey = configuration.subscribeKey, + uuid = uuid, + options = params, + ) + } + + override fun createResponse(input: Response>): PNUUIDMetadataResult { + return input.body()!!.let { + PNUUIDMetadataResult( + status = it.status, + data = it.data, + ) + } + } + + override fun operationType(): PNOperationType = PNOperationType.PNGetUUIDMetadataOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.APP_CONTEXT +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetUUIDMetadataImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetUUIDMetadataImpl.kt deleted file mode 100644 index a294bc755..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/GetUUIDMetadataImpl.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.pubnub.internal.endpoints.objects.uuid - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction -import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadataResult -import com.pubnub.internal.DelegatingEndpoint -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.models.from -import com.pubnub.internal.models.consumer.objects.uuid.PNUUIDMetadataResult as PNUUIDMetadataResultInternal - -/** - * @see [PubNubImpl.getUUIDMetadata] - */ -class GetUUIDMetadataImpl internal constructor(getUUIDMetadata: GetUUIDMetadataEndpoint) : - DelegatingEndpoint(getUUIDMetadata), - GetUUIDMetadataInterface by getUUIDMetadata, - com.pubnub.api.endpoints.objects.uuid.GetUUIDMetadata { - override fun convertAction( - remoteAction: ExtendedRemoteAction, - ): ExtendedRemoteAction { - return MappingRemoteAction(remoteAction, ::from) - } - } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/RemoveUUIDMetadataEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/RemoveUUIDMetadataEndpoint.kt new file mode 100644 index 000000000..45ef82e1c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/RemoveUUIDMetadataEndpoint.kt @@ -0,0 +1,31 @@ +package com.pubnub.internal.endpoints.objects.uuid + +import com.pubnub.api.endpoints.objects.uuid.RemoveUUIDMetadata +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.objects.PNRemoveMetadataResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.objects_api.EntityEnvelope +import retrofit2.Call +import retrofit2.Response + +class RemoveUUIDMetadataEndpoint( + pubnub: PubNubImpl, + override val uuid: String? = null, +) : EndpointCore, PNRemoveMetadataResult>(pubnub), RemoveUUIDMetadata { + override fun doWork(queryParams: HashMap): Call> { + return retrofitManager.objectsService.deleteUUIDMetadata( + subKey = configuration.subscribeKey, + uuid = uuid ?: configuration.userId.value, + ) + } + + override fun createResponse(input: Response>): PNRemoveMetadataResult { + return PNRemoveMetadataResult(input.body()!!.status) + } + + override fun operationType(): PNOperationType = PNOperationType.PNRemoveUUIDMetadataOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.APP_CONTEXT +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/RemoveUUIDMetadataImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/RemoveUUIDMetadataImpl.kt deleted file mode 100644 index 494bcd94f..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/RemoveUUIDMetadataImpl.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.pubnub.internal.endpoints.objects.uuid - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction -import com.pubnub.api.models.consumer.objects.PNRemoveMetadataResult -import com.pubnub.internal.DelegatingEndpoint -import com.pubnub.internal.models.from - -class RemoveUUIDMetadataImpl internal constructor(removeUUIDMetadata: RemoveUUIDMetadataEndpoint) : - DelegatingEndpoint( - removeUUIDMetadata, - ), - RemoveUUIDMetadataInterface by removeUUIDMetadata, - com.pubnub.api.endpoints.objects.uuid.RemoveUUIDMetadata { - override fun convertAction( - remoteAction: ExtendedRemoteAction, - ): ExtendedRemoteAction { - return MappingRemoteAction(remoteAction, ::from) - } - } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/SetUUIDMetadataEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/SetUUIDMetadataEndpoint.kt new file mode 100644 index 000000000..375618ae5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/SetUUIDMetadataEndpoint.kt @@ -0,0 +1,64 @@ +package com.pubnub.internal.endpoints.objects.uuid + +import com.pubnub.api.endpoints.objects.uuid.SetUUIDMetadata +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadataResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.objects.internal.IncludeQueryParam +import com.pubnub.internal.models.server.objects_api.EntityEnvelope +import com.pubnub.internal.models.server.objects_api.UUIDMetadataInput +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.setUUIDMetadata] + */ +class SetUUIDMetadataEndpoint internal constructor( + pubnub: PubNubImpl, + private val uuid: String?, + private val name: String?, + private val externalId: String?, + private val profileUrl: String?, + private val email: String?, + private val custom: Any?, + private val withInclude: IncludeQueryParam, + private val type: String?, + private val status: String?, +) : EndpointCore, PNUUIDMetadataResult>(pubnub), SetUUIDMetadata { + override fun doWork(queryParams: HashMap): Call> { + val params = queryParams + withInclude.createIncludeQueryParams() + return retrofitManager.objectsService.setUUIDMetadata( + subKey = configuration.subscribeKey, + body = + UUIDMetadataInput( + name = name, + custom = custom, + email = email, + externalId = externalId, + profileUrl = profileUrl, + type = type, + status = status, + ), + uuid = uuid ?: configuration.userId.value, + options = params, + ) + } + + override fun createResponse(input: Response>): PNUUIDMetadataResult { + return input.body()!!.let { + PNUUIDMetadataResult( + status = it.status, + data = it.data, + ) + } + } + + override fun operationType(): PNOperationType { + return PNOperationType.PNSetUUIDMetadataOperation + } + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.APP_CONTEXT +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/SetUUIDMetadataImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/SetUUIDMetadataImpl.kt deleted file mode 100644 index 4d9c20c8c..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/objects/uuid/SetUUIDMetadataImpl.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.pubnub.internal.endpoints.objects.uuid - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction -import com.pubnub.internal.DelegatingEndpoint -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.models.consumer.objects.uuid.PNUUIDMetadataResult -import com.pubnub.internal.models.from - -/** - * @see [PubNubImpl.setUUIDMetadata] - */ -class SetUUIDMetadataImpl internal constructor(setUUIDMetadata: SetUUIDMetadataEndpoint) : - DelegatingEndpoint( - setUUIDMetadata, - ), - SetUUIDMetadataInterface by setUUIDMetadata, - com.pubnub.api.endpoints.objects.uuid.SetUUIDMetadata { - override fun convertAction( - remoteAction: ExtendedRemoteAction, - ): ExtendedRemoteAction { - return MappingRemoteAction(remoteAction, ::from) - } - } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/GetStateEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/GetStateEndpoint.kt new file mode 100644 index 000000000..dbf223682 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/GetStateEndpoint.kt @@ -0,0 +1,72 @@ +package com.pubnub.internal.endpoints.presence + +import com.google.gson.JsonElement +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.presence.GetState +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.presence.PNGetStateResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.Envelope +import com.pubnub.internal.toCsv +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.getPresenceState] + */ +class GetStateEndpoint internal constructor( + pubnub: PubNubImpl, + override val channels: List, + override val channelGroups: List, + override val uuid: String = pubnub.configuration.userId.value, +) : EndpointCore, PNGetStateResult>(pubnub), GetState { + override fun getAffectedChannels() = channels + + override fun getAffectedChannelGroups() = channelGroups + + override fun validateParams() { + super.validateParams() + if (channels.isEmpty() && channelGroups.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_AND_GROUP_MISSING) + } + } + + override fun doWork(queryParams: HashMap): Call> { + addQueryParams(queryParams) + + return retrofitManager.presenceService.getState( + configuration.subscribeKey, + channels.toCsv(), + uuid, + queryParams, + ) + } + + override fun createResponse(input: Response>): PNGetStateResult { + val stateMappings = hashMapOf() + if (channels.size == 1 && channelGroups.isEmpty()) { + stateMappings[channels.first()] = input.body()!!.payload!! + } else { + val it = pubnub.mapper.getObjectIterator(input.body()!!.payload!!) + while (it.hasNext()) { + val stateMapping = it.next() + stateMappings[stateMapping.key] = stateMapping.value + } + } + + return PNGetStateResult(stateByUUID = stateMappings) + } + + override fun operationType() = PNOperationType.PNGetState + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PRESENCE + + private fun addQueryParams(queryParams: MutableMap) { + if (channelGroups.isNotEmpty()) { + queryParams["channel-group"] = channelGroups.toCsv() + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/GetStateImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/GetStateImpl.kt deleted file mode 100644 index e2969ffe0..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/GetStateImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.presence - -import com.pubnub.api.endpoints.presence.GetState -import com.pubnub.api.models.consumer.presence.PNGetStateResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.getPresenceState] - */ -class GetStateImpl internal constructor(getState: GetStateInterface) : - GetStateInterface by getState, - GetState, - EndpointImpl(getState) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/HeartbeatEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/HeartbeatEndpoint.kt new file mode 100644 index 000000000..875ca843f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/HeartbeatEndpoint.kt @@ -0,0 +1,68 @@ +package com.pubnub.internal.endpoints.presence + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.PubNubUtil +import retrofit2.Call +import retrofit2.Response + +class HeartbeatEndpoint internal constructor( + pubnub: PubNubImpl, + val channels: List = listOf(), + val channelGroups: List = listOf(), + val state: Any? = null, +) : EndpointCore(pubnub) { + override fun getAffectedChannels() = channels + + override fun getAffectedChannelGroups() = channelGroups + + override fun validateParams() { + super.validateParams() + if (channels.isEmpty() && channelGroups.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_AND_GROUP_MISSING) + } + } + + override fun doWork(queryParams: HashMap): Call { + addQueryParams(queryParams) + + val channelsCsv = + if (channels.isNotEmpty()) { + channels.joinToString(",") + } else { + "," + } + + return retrofitManager.presenceService.heartbeat( + configuration.subscribeKey, + channelsCsv, + queryParams, + ) + } + + private fun addQueryParams(queryParams: HashMap) { + queryParams["heartbeat"] = pubnub.configuration.presenceTimeout.toString() + + if (channelGroups.isNotEmpty()) { + queryParams["channel-group"] = channelGroups.joinToString(",") + } + + state?.let { + queryParams["state"] = pubnub.mapper.toJson(it) + } + + PubNubUtil.maybeAddEeQueryParam(queryParams) + } + + override fun createResponse(input: Response): Boolean { + return true + } + + override fun operationType() = PNOperationType.PNHeartbeatOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PRESENCE +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/HereNowEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/HereNowEndpoint.kt new file mode 100644 index 000000000..ba0d8ae30 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/HereNowEndpoint.kt @@ -0,0 +1,142 @@ +package com.pubnub.internal.endpoints.presence + +import com.google.gson.JsonElement +import com.pubnub.api.endpoints.presence.HereNow +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.presence.PNHereNowChannelData +import com.pubnub.api.models.consumer.presence.PNHereNowOccupantData +import com.pubnub.api.models.consumer.presence.PNHereNowResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.Envelope +import com.pubnub.internal.toCsv +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.hereNow] + */ +class HereNowEndpoint internal constructor( + pubnub: PubNubImpl, + override val channels: List = emptyList(), + override val channelGroups: List = emptyList(), + override val includeState: Boolean = false, + override val includeUUIDs: Boolean = true, +) : EndpointCore, PNHereNowResult>(pubnub), HereNow { + private fun isGlobalHereNow() = channels.isEmpty() && channelGroups.isEmpty() + + override fun getAffectedChannels() = channels + + override fun getAffectedChannelGroups() = channelGroups + + override fun doWork(queryParams: HashMap): Call> { + addQueryParams(queryParams) + + return if (!isGlobalHereNow()) { + retrofitManager.presenceService.hereNow( + configuration.subscribeKey, + channels.toCsv(), + queryParams, + ) + } else { + retrofitManager.presenceService.globalHereNow( + configuration.subscribeKey, + queryParams, + ) + } + } + + override fun createResponse(input: Response>): PNHereNowResult { + return if (isGlobalHereNow() || (channels.size > 1 || channelGroups.isNotEmpty())) { + parseMultipleChannelResponse(input.body()?.payload!!) + } else { + parseSingleChannelResponse(input.body()!!) + } + } + + override fun operationType() = PNOperationType.PNHereNowOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PRESENCE + + private fun parseSingleChannelResponse(input: Envelope): PNHereNowResult { + val pnHereNowResult = + PNHereNowResult( + totalChannels = 1, + totalOccupancy = input.occupancy, + ) + + val pnHereNowChannelData = + PNHereNowChannelData( + channelName = channels[0], + occupancy = input.occupancy, + ) + + if (includeUUIDs) { + pnHereNowChannelData.occupants = prepareOccupantData(input.uuids!!) + pnHereNowResult.channels[channels[0]] = pnHereNowChannelData + } + + return pnHereNowResult + } + + private fun parseMultipleChannelResponse(input: JsonElement): PNHereNowResult { + val pnHereNowResult = + PNHereNowResult( + totalChannels = pubnub.mapper.elementToInt(input, "total_channels"), + totalOccupancy = pubnub.mapper.elementToInt(input, "total_occupancy"), + ) + + val it = pubnub.mapper.getObjectIterator(input, "channels") + + while (it.hasNext()) { + val entry = it.next() + val pnHereNowChannelData = + PNHereNowChannelData( + channelName = entry.key, + occupancy = pubnub.mapper.elementToInt(entry.value, "occupancy"), + ) + if (includeUUIDs) { + pnHereNowChannelData.occupants = prepareOccupantData(pubnub.mapper.getField(entry.value, "uuids")!!) + } + pnHereNowResult.channels[entry.key] = pnHereNowChannelData + } + + return pnHereNowResult + } + + private fun prepareOccupantData(input: JsonElement): MutableList { + val occupantsResults = mutableListOf() + + val it = pubnub.mapper.getArrayIterator(input) + while (it?.hasNext()!!) { + val occupant = it.next() + occupantsResults.add( + if (includeState) { + PNHereNowOccupantData( + uuid = pubnub.mapper.elementToString(occupant, "uuid")!!, + state = pubnub.mapper.getField(occupant, "state"), + ) + } else { + PNHereNowOccupantData( + uuid = pubnub.mapper.elementToString(occupant)!!, + ) + }, + ) + } + + return occupantsResults + } + + private fun addQueryParams(queryParams: MutableMap) { + if (includeState) { + queryParams["state"] = "1" + } + if (!includeUUIDs) { + queryParams["disable_uuids"] = "1" + } + if (channelGroups.isNotEmpty()) { + queryParams["channel-group"] = channelGroups.toCsv() + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/HereNowImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/HereNowImpl.kt deleted file mode 100644 index 70f99f62b..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/HereNowImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.presence - -import com.pubnub.api.endpoints.presence.HereNow -import com.pubnub.api.models.consumer.presence.PNHereNowResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.hereNow] - */ -class HereNowImpl internal constructor(hereNow: HereNowInterface) : - HereNowInterface by hereNow, - HereNow, - EndpointImpl(hereNow) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/LeaveEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/LeaveEndpoint.kt new file mode 100644 index 000000000..0a2e3e374 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/LeaveEndpoint.kt @@ -0,0 +1,48 @@ +package com.pubnub.internal.endpoints.presence + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.PubNubUtil +import com.pubnub.internal.toCsv +import retrofit2.Call +import retrofit2.Response + +class LeaveEndpoint internal constructor(pubnub: PubNubImpl) : EndpointCore(pubnub) { + var channels = emptyList() + var channelGroups = emptyList() + + override fun validateParams() { + super.validateParams() + if (channels.isEmpty() && channelGroups.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_AND_GROUP_MISSING) + } + } + + override fun getAffectedChannels() = channels + + override fun getAffectedChannelGroups() = channelGroups + + override fun doWork(queryParams: HashMap): Call { + addQueryParams(queryParams) + return retrofitManager.presenceService.leave( + configuration.subscribeKey, + channels.toCsv(), + queryParams, + ) + } + + private fun addQueryParams(queryParams: HashMap) { + queryParams["channel-group"] = channelGroups.toCsv() + PubNubUtil.maybeAddEeQueryParam(queryParams) + } + + override fun createResponse(input: Response) = true + + override fun operationType() = PNOperationType.PNUnsubscribeOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PRESENCE +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/SetStateEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/SetStateEndpoint.kt new file mode 100644 index 000000000..063ef3cf2 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/SetStateEndpoint.kt @@ -0,0 +1,74 @@ +package com.pubnub.internal.endpoints.presence + +import com.google.gson.JsonElement +import com.google.gson.JsonNull +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.presence.SetState +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.presence.PNSetStateResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.Envelope +import com.pubnub.internal.presence.eventengine.data.PresenceData +import com.pubnub.internal.toCsv +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.setPresenceState] + */ +class SetStateEndpoint internal constructor( + pubnub: PubNubImpl, + override val channels: List, + override val channelGroups: List, + override val state: Any, + override val uuid: String = pubnub.configuration.userId.value, + private val presenceData: PresenceData, +) : EndpointCore, PNSetStateResult>(pubnub), SetState { + override fun getAffectedChannels() = channels + + override fun getAffectedChannelGroups() = channelGroups + + override fun validateParams() { + super.validateParams() + if (channels.isEmpty() && channelGroups.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_AND_GROUP_MISSING) + } + } + + override fun doWork(queryParams: HashMap): Call> { + if (uuid == pubnub.configuration.userId.value) { + val stateCopy = pubnub.mapper.fromJson(pubnub.mapper.toJson(state), JsonElement::class.java) + presenceData.channelStates.putAll(channels.associateWith { stateCopy }) + } + + addQueryParams(queryParams) + + return retrofitManager.presenceService.setState( + configuration.subscribeKey, + channels.toCsv(), + uuid, + queryParams, + ) + } + + override fun createResponse(input: Response>): PNSetStateResult { + if (input.body()!!.payload!! is JsonNull) { + throw PubNubException(PubNubError.PARSING_ERROR) + } + return PNSetStateResult(state = input.body()!!.payload!!) + } + + override fun operationType() = PNOperationType.PNSetStateOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PRESENCE + + private fun addQueryParams(queryParams: MutableMap) { + if (channelGroups.isNotEmpty()) { + queryParams["channel-group"] = channelGroups.toCsv() + } + queryParams["state"] = pubnub.mapper.toJson(state) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/SetStateImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/SetStateImpl.kt deleted file mode 100644 index 2660976ed..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/SetStateImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.presence - -import com.pubnub.api.endpoints.presence.SetState -import com.pubnub.api.models.consumer.presence.PNSetStateResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.setPresenceState] - */ -class SetStateImpl internal constructor(setState: SetStateInterface) : - SetStateInterface by setState, - SetState, - EndpointImpl(setState) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/WhereNowEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/WhereNowEndpoint.kt new file mode 100644 index 000000000..2f5268949 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/WhereNowEndpoint.kt @@ -0,0 +1,35 @@ +package com.pubnub.internal.endpoints.presence + +import com.pubnub.api.endpoints.presence.WhereNow +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.presence.PNWhereNowResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.models.server.Envelope +import com.pubnub.internal.models.server.presence.WhereNowPayload +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.whereNow] + */ +class WhereNowEndpoint internal constructor( + pubnub: PubNubImpl, + override val uuid: String = pubnub.configuration.userId.value, +) : EndpointCore, PNWhereNowResult>(pubnub), WhereNow { + override fun doWork(queryParams: HashMap): Call> { + return retrofitManager.presenceService.whereNow( + configuration.subscribeKey, + uuid, + queryParams, + ) + } + + override fun createResponse(input: Response>): PNWhereNowResult = + PNWhereNowResult(channels = input.body()!!.payload!!.channels) + + override fun operationType() = PNOperationType.PNWhereNowOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PRESENCE +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/WhereNowImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/WhereNowImpl.kt deleted file mode 100644 index c082c914d..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/presence/WhereNowImpl.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.pubnub.internal.endpoints.presence - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.endpoints.remoteaction.MappingRemoteAction -import com.pubnub.internal.DelegatingEndpoint -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.models.consumer.presence.PNWhereNowResult -import com.pubnub.internal.models.toApi - -/** - * @see [PubNubImpl.whereNow] - */ -class WhereNowImpl internal constructor(whereNow: WhereNowEndpoint) : - DelegatingEndpoint(whereNow), - WhereNowInterface by whereNow, - com.pubnub.api.endpoints.presence.WhereNow { - override fun convertAction( - remoteAction: ExtendedRemoteAction, - ): ExtendedRemoteAction { - return MappingRemoteAction( - remoteAction, - PNWhereNowResult::toApi, - ) - } - } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/PublishEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/PublishEndpoint.kt new file mode 100644 index 000000000..c1a6a9ab3 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/PublishEndpoint.kt @@ -0,0 +1,108 @@ +package com.pubnub.internal.endpoints.pubsub + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.pubsub.Publish +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.PNPublishResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.crypto.encryptString +import com.pubnub.internal.extension.numericString +import com.pubnub.internal.extension.quoted +import com.pubnub.internal.extension.valueString +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.publish] + */ +class PublishEndpoint internal constructor( + pubnub: PubNubImpl, + override val message: Any, + override val channel: String, + override val meta: Any? = null, + override val shouldStore: Boolean? = null, + override val usePost: Boolean = false, + override val replicate: Boolean = true, + override val ttl: Int? = null, +) : EndpointCore, PNPublishResult>(pubnub), Publish { + override fun validateParams() { + super.validateParams() + if (channel.isBlank()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + } + + override fun getAffectedChannels() = listOf(channel) + + override fun doWork(queryParams: HashMap): Call> { + addQueryParams(queryParams) + + return if (usePost) { + val payload = getBodyMessage(message) + + retrofitManager.publishService.publishWithPost( + configuration.publishKey, + configuration.subscribeKey, + channel, + payload, + queryParams, + ) + } else { + // HTTP GET request + val stringifiedMessage = getParamMessage(message) + + retrofitManager.publishService.publish( + configuration.publishKey, + configuration.subscribeKey, + channel, + stringifiedMessage, + queryParams, + ) + } + } + + override fun createResponse(input: Response>): PNPublishResult = + PNPublishResult( + timetoken = input.body()!![2].toString().toLong(), + ) + + override fun operationType() = PNOperationType.PNPublishOperation + + override fun isPubKeyRequired() = true + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PUBLISH + + // region Parameters + + /** + * Add query params to passed HashMap + * + * @param queryParams hashMap to add parameters + */ + private fun addQueryParams(queryParams: MutableMap) { + meta?.run { queryParams["meta"] = pubnub.mapper.toJson(this) } + + shouldStore?.run { queryParams["store"] = this.numericString } + + ttl?.run { queryParams["ttl"] = this.toString() } + + if (!replicate) { + queryParams["norep"] = true.valueString + } + + queryParams["seqn"] = pubnub.publishSequenceManager.nextSequence().toString() + } + // endregion + + // region Message parsers + private fun getBodyMessage(message: Any): Any = configuration.cryptoModule?.encryptString(toJson(message)) ?: message + + private fun getParamMessage(message: Any): String = + configuration.cryptoModule?.encryptString(toJson(message))?.quoted() ?: toJson(message) + + private fun toJson(message: Any): String = pubnub.mapper.toJson(message) + // endregion +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/PublishImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/PublishImpl.kt deleted file mode 100644 index 081c0522d..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/PublishImpl.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.pubnub.internal.endpoints.pubsub - -import com.pubnub.api.models.consumer.PNPublishResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.publish] - */ -class PublishImpl internal constructor(publish: PublishEndpoint) : - com.pubnub.api.endpoints.pubsub.Publish, - PublishInterface by publish, - EndpointImpl(publish) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/SignalEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/SignalEndpoint.kt new file mode 100644 index 000000000..823c49b84 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/SignalEndpoint.kt @@ -0,0 +1,51 @@ +package com.pubnub.internal.endpoints.pubsub + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.pubsub.Signal +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.PNPublishResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import retrofit2.Call +import retrofit2.Response + +/** + * @see [PubNubImpl.signal] + */ +class SignalEndpoint internal constructor( + pubnub: PubNubImpl, + override val channel: String, + override val message: Any, +) : EndpointCore, PNPublishResult>(pubnub), Signal { + override fun validateParams() { + super.validateParams() + if (channel.isBlank()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + } + + override fun getAffectedChannels() = listOf(channel) + + override fun doWork(queryParams: HashMap): Call> { + return retrofitManager.signalService.signal( + pubKey = configuration.publishKey, + subKey = configuration.subscribeKey, + channel = channel, + message = pubnub.mapper.toJson(message), + options = queryParams, + ) + } + + override fun createResponse(input: Response>): PNPublishResult = + PNPublishResult( + timetoken = input.body()!![2].toString().toLong(), + ) + + override fun operationType() = PNOperationType.PNSignalOperation + + override fun isPubKeyRequired() = true + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PUBLISH +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/SignalImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/SignalImpl.kt deleted file mode 100644 index 1a0834312..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/SignalImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.pubsub - -import com.pubnub.api.endpoints.pubsub.Signal -import com.pubnub.api.models.consumer.PNPublishResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.signal] - */ -class SignalImpl internal constructor(signal: SignalInterface) : - Signal, - SignalInterface by signal, - EndpointImpl(signal) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/SubscribeEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/SubscribeEndpoint.kt new file mode 100644 index 000000000..9e779eccb --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/pubsub/SubscribeEndpoint.kt @@ -0,0 +1,77 @@ +package com.pubnub.internal.endpoints.pubsub + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.PubNubUtil +import com.pubnub.internal.models.server.SubscribeEnvelope +import com.pubnub.internal.toCsv +import retrofit2.Call +import retrofit2.Response + +class SubscribeEndpoint internal constructor(pubnub: PubNubImpl) : EndpointCore(pubnub) { + var channels = emptyList() + var channelGroups = emptyList() + var timetoken: Long? = null + var region: String? = null + var state: Any? = null + var filterExpression: String? = null + + override fun validateParams() { + super.validateParams() + if (channels.isEmpty() && channelGroups.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_AND_GROUP_MISSING) + } + } + + override fun getAffectedChannels() = channels + + override fun getAffectedChannelGroups() = channelGroups + + override fun doWork(queryParams: HashMap): Call { + addQueryParams(queryParams) + + return retrofitManager.subscribeService.subscribe( + configuration.subscribeKey, + channels.toCsv(), + queryParams, + ) + } + + private fun addQueryParams(queryParams: HashMap) { + if (channelGroups.isNotEmpty()) { + queryParams["channel-group"] = channelGroups.joinToString(",") + } + + if (!filterExpression.isNullOrBlank()) { + queryParams["filter-expr"] = filterExpression!! + } + + timetoken?.let { + queryParams["tt"] = it.toString() + } + + region?.let { + queryParams["tr"] = it + } + + queryParams["heartbeat"] = pubnub.configuration.presenceTimeout.toString() + + state?.let { + queryParams["state"] = pubnub.mapper.toJson(it) + } + + PubNubUtil.maybeAddEeQueryParam(queryParams) + } + + override fun createResponse(input: Response): SubscribeEnvelope { + return input.body()!! + } + + override fun operationType() = PNOperationType.PNSubscribeOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.SUBSCRIBE +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/AddChannelsToPushEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/AddChannelsToPushEndpoint.kt new file mode 100644 index 000000000..f5eb81182 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/AddChannelsToPushEndpoint.kt @@ -0,0 +1,81 @@ +package com.pubnub.internal.endpoints.push + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.push.AddChannelsToPush +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.enums.PNPushEnvironment +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.models.consumer.push.PNPushAddChannelResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.toCsv +import retrofit2.Call +import retrofit2.Response +import java.util.Locale + +/** + * @see [PubNubImpl.addPushNotificationsOnChannels] + */ +class AddChannelsToPushEndpoint internal constructor( + pubnub: PubNubImpl, + override val pushType: PNPushType, + override val channels: List, + override val deviceId: String, + override val topic: String? = null, + override val environment: PNPushEnvironment = PNPushEnvironment.DEVELOPMENT, +) : EndpointCore(pubnub), AddChannelsToPush { + override fun getAffectedChannels() = channels + + override fun validateParams() { + super.validateParams() + if (deviceId.isBlank()) { + throw PubNubException(PubNubError.DEVICE_ID_MISSING) + } + if (channels.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + if (pushType == PNPushType.APNS2 && topic.isNullOrBlank()) { + throw PubNubException(PubNubError.PUSH_TOPIC_MISSING) + } + } + + override fun doWork(queryParams: HashMap): Call { + addQueryParams(queryParams) + + return if (pushType != PNPushType.APNS2) { + retrofitManager.pushService + .modifyChannelsForDevice( + subKey = configuration.subscribeKey, + pushToken = deviceId, + options = queryParams, + ) + } else { + retrofitManager.pushService + .modifyChannelsForDeviceApns2( + subKey = configuration.subscribeKey, + deviceApns2 = deviceId, + options = queryParams, + ) + } + } + + override fun createResponse(input: Response): PNPushAddChannelResult = PNPushAddChannelResult() + + override fun operationType() = PNOperationType.PNAddPushNotificationsOnChannelsOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PUSH_NOTIFICATION + + private fun addQueryParams(queryParams: MutableMap) { + queryParams["add"] = channels.toCsv() + + if (pushType != PNPushType.APNS2) { + queryParams["type"] = pushType.toParamString() + return + } + + queryParams["environment"] = environment.name.lowercase(Locale.getDefault()) + topic?.run { queryParams["topic"] = this } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/AddChannelsToPushImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/AddChannelsToPushImpl.kt deleted file mode 100644 index bb852d2b6..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/AddChannelsToPushImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.push - -import com.pubnub.api.endpoints.push.AddChannelsToPush -import com.pubnub.api.models.consumer.push.PNPushAddChannelResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.addPushNotificationsOnChannels] - */ -class AddChannelsToPushImpl internal constructor(addChannelsToPush: AddChannelsToPushInterface) : - AddChannelsToPushInterface by addChannelsToPush, - AddChannelsToPush, - EndpointImpl(addChannelsToPush) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/ListPushProvisionsEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/ListPushProvisionsEndpoint.kt new file mode 100644 index 000000000..9033f4279 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/ListPushProvisionsEndpoint.kt @@ -0,0 +1,72 @@ +package com.pubnub.internal.endpoints.push + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.push.ListPushProvisions +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.enums.PNPushEnvironment +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.models.consumer.push.PNPushListProvisionsResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import retrofit2.Call +import retrofit2.Response +import java.util.Locale + +/** + * @see [PubNubImpl.auditPushChannelProvisions] + */ +class ListPushProvisionsEndpoint internal constructor( + pubnub: PubNubImpl, + override val pushType: PNPushType, + override val deviceId: String, + override val topic: String? = null, + override val environment: PNPushEnvironment = PNPushEnvironment.DEVELOPMENT, +) : EndpointCore, PNPushListProvisionsResult>(pubnub), ListPushProvisions { + override fun validateParams() { + super.validateParams() + if (deviceId.isBlank()) { + throw PubNubException(PubNubError.DEVICE_ID_MISSING) + } + if (pushType == PNPushType.APNS2 && topic.isNullOrBlank()) { + throw PubNubException(PubNubError.PUSH_TOPIC_MISSING) + } + } + + override fun doWork(queryParams: HashMap): Call> { + addQueryParams(queryParams) + + return if (pushType != PNPushType.APNS2) { + retrofitManager.pushService + .listChannelsForDevice( + subKey = configuration.subscribeKey, + pushToken = deviceId, + options = queryParams, + ) + } else { + retrofitManager.pushService + .listChannelsForDeviceApns2( + subKey = configuration.subscribeKey, + deviceApns2 = deviceId, + options = queryParams, + ) + } + } + + override fun createResponse(input: Response>): PNPushListProvisionsResult = PNPushListProvisionsResult(input.body()!!) + + override fun operationType() = PNOperationType.PNPushNotificationEnabledChannelsOperation + + private fun addQueryParams(queryParams: MutableMap) { + if (pushType != PNPushType.APNS2) { + queryParams["type"] = pushType.toParamString() + return + } + + queryParams["environment"] = environment.name.lowercase(Locale.getDefault()) + topic?.run { queryParams["topic"] = this } + } + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PUSH_NOTIFICATION +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/ListPushProvisionsImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/ListPushProvisionsImpl.kt deleted file mode 100644 index c2f131687..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/ListPushProvisionsImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.pubnub.internal.endpoints.push - -import com.pubnub.api.endpoints.push.ListPushProvisions -import com.pubnub.api.models.consumer.push.PNPushListProvisionsResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.auditPushChannelProvisions] - */ -class ListPushProvisionsImpl internal constructor(listPushProvisions: ListPushProvisionsInterface) : - ListPushProvisionsInterface by listPushProvisions, - ListPushProvisions, - EndpointImpl(listPushProvisions) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveAllPushChannelsForDeviceEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveAllPushChannelsForDeviceEndpoint.kt new file mode 100644 index 000000000..74bad7ee7 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveAllPushChannelsForDeviceEndpoint.kt @@ -0,0 +1,72 @@ +package com.pubnub.internal.endpoints.push + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.push.RemoveAllPushChannelsForDevice +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.enums.PNPushEnvironment +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.models.consumer.push.PNPushRemoveAllChannelsResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import retrofit2.Call +import retrofit2.Response +import java.util.Locale + +/** + * @see [PubNubImpl.removeAllPushNotificationsFromDeviceWithPushToken] + */ +class RemoveAllPushChannelsForDeviceEndpoint internal constructor( + pubnub: PubNubImpl, + override val pushType: PNPushType, + override val deviceId: String, + override val environment: PNPushEnvironment = PNPushEnvironment.DEVELOPMENT, + override val topic: String? = null, +) : EndpointCore(pubnub), RemoveAllPushChannelsForDevice { + override fun validateParams() { + super.validateParams() + if (deviceId.isBlank()) { + throw PubNubException(PubNubError.DEVICE_ID_MISSING) + } + if (pushType == PNPushType.APNS2 && topic.isNullOrBlank()) { + throw PubNubException(PubNubError.PUSH_TOPIC_MISSING) + } + } + + override fun doWork(queryParams: HashMap): Call { + addQueryParams(queryParams) + + return if (pushType != PNPushType.APNS2) { + retrofitManager.pushService + .removeAllChannelsForDevice( + subKey = configuration.subscribeKey, + pushToken = deviceId, + options = queryParams, + ) + } else { + retrofitManager.pushService + .removeAllChannelsForDeviceApns2( + subKey = configuration.subscribeKey, + deviceApns2 = deviceId, + options = queryParams, + ) + } + } + + override fun createResponse(input: Response): PNPushRemoveAllChannelsResult = PNPushRemoveAllChannelsResult() + + override fun operationType() = PNOperationType.PNRemoveAllPushNotificationsOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PUSH_NOTIFICATION + + private fun addQueryParams(queryParams: MutableMap) { + if (pushType != PNPushType.APNS2) { + queryParams["type"] = pushType.toParamString() + return + } + + queryParams["environment"] = environment.name.lowercase(Locale.getDefault()) + topic?.run { queryParams["topic"] = this } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveAllPushChannelsForDeviceImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveAllPushChannelsForDeviceImpl.kt deleted file mode 100644 index 745cfeed6..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveAllPushChannelsForDeviceImpl.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.pubnub.internal.endpoints.push - -import com.pubnub.api.endpoints.push.RemoveAllPushChannelsForDevice -import com.pubnub.api.models.consumer.push.PNPushRemoveAllChannelsResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.removeAllPushNotificationsFromDeviceWithPushToken] - */ -class RemoveAllPushChannelsForDeviceImpl internal constructor( - removeAllPushChannelsForDevice: RemoveAllPushChannelsForDeviceInterface, -) : RemoveAllPushChannelsForDeviceInterface by removeAllPushChannelsForDevice, - RemoveAllPushChannelsForDevice, - EndpointImpl(removeAllPushChannelsForDevice) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveChannelsFromPushEndpoint.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveChannelsFromPushEndpoint.kt new file mode 100644 index 000000000..5f4dbe477 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveChannelsFromPushEndpoint.kt @@ -0,0 +1,81 @@ +package com.pubnub.internal.endpoints.push + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.push.RemoveChannelsFromPush +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.enums.PNPushEnvironment +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.models.consumer.push.PNPushRemoveChannelResult +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.toCsv +import retrofit2.Call +import retrofit2.Response +import java.util.Locale + +/** + * @see [PubNubImpl.removePushNotificationsFromChannels] + */ +class RemoveChannelsFromPushEndpoint internal constructor( + pubnub: PubNubImpl, + override val pushType: PNPushType, + override val channels: List, + override val deviceId: String, + override val topic: String? = null, + override val environment: PNPushEnvironment = PNPushEnvironment.DEVELOPMENT, +) : EndpointCore(pubnub), RemoveChannelsFromPush { + override fun getAffectedChannels() = channels + + override fun validateParams() { + super.validateParams() + if (deviceId.isBlank()) { + throw PubNubException(PubNubError.DEVICE_ID_MISSING) + } + if (channels.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_MISSING) + } + if (pushType == PNPushType.APNS2 && topic.isNullOrBlank()) { + throw PubNubException(PubNubError.PUSH_TOPIC_MISSING) + } + } + + override fun doWork(queryParams: HashMap): Call { + addQueryParams(queryParams) + + return if (pushType != PNPushType.APNS2) { + retrofitManager.pushService + .modifyChannelsForDevice( + subKey = configuration.subscribeKey, + pushToken = deviceId, + options = queryParams, + ) + } else { + retrofitManager.pushService + .modifyChannelsForDeviceApns2( + subKey = configuration.subscribeKey, + deviceApns2 = deviceId, + options = queryParams, + ) + } + } + + override fun createResponse(input: Response): PNPushRemoveChannelResult = PNPushRemoveChannelResult() + + override fun operationType() = PNOperationType.PNRemovePushNotificationsFromChannelsOperation + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PUSH_NOTIFICATION + + private fun addQueryParams(queryParams: MutableMap) { + queryParams["remove"] = channels.toCsv() + + if (pushType != PNPushType.APNS2) { + queryParams["type"] = pushType.toParamString() + return + } + + queryParams["environment"] = environment.name.lowercase(Locale.getDefault()) + topic?.run { queryParams["topic"] = this } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveChannelsFromPushImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveChannelsFromPushImpl.kt deleted file mode 100644 index 01fb7e2e8..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/push/RemoveChannelsFromPushImpl.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.pubnub.internal.endpoints.push - -import com.pubnub.api.endpoints.push.RemoveChannelsFromPush -import com.pubnub.api.models.consumer.push.PNPushRemoveChannelResult -import com.pubnub.internal.EndpointImpl -import com.pubnub.internal.PubNubImpl - -/** - * @see [PubNubImpl.removePushNotificationsFromChannels] - */ -class RemoveChannelsFromPushImpl internal constructor( - removeChannelsFromPush: RemoveChannelsFromPushInterface, -) : RemoveChannelsFromPushInterface by removeChannelsFromPush, - RemoveChannelsFromPush, - EndpointImpl(removeChannelsFromPush) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/remoteaction/RetryingRemoteAction.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/remoteaction/RetryingRemoteAction.kt new file mode 100644 index 000000000..5759b6e50 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/endpoints/remoteaction/RetryingRemoteAction.kt @@ -0,0 +1,89 @@ +package com.pubnub.internal.endpoints.remoteaction + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.v2.callbacks.Result +import java.util.concurrent.ExecutorService +import java.util.function.Consumer + +internal class RetryingRemoteAction( + private val remoteAction: ExtendedRemoteAction, + private val maxNumberOfAutomaticRetries: Int, + private val executorService: ExecutorService, +) : ExtendedRemoteAction { + private lateinit var cachedCallback: Consumer> + + @Throws(PubNubException::class) + override fun sync(): T { + validate() + var thrownException: PubNubException? = null + for (i in 0 until maxNumberOfAutomaticRetries) { + thrownException = + try { + return remoteAction.sync() + } catch (ex: Throwable) { + PubNubException.from(ex) + } + } + throw thrownException!! + } + + override fun async(callback: Consumer>) { + cachedCallback = callback + executorService.execute( + Runnable { + try { + validate() + } catch (ex: PubNubException) { + callback.accept( + Result.failure(ex), + ) + return@Runnable + } + var lastException: Throwable? = null + for (i in 0 until maxNumberOfAutomaticRetries) { + try { + callback.accept(Result.success(remoteAction.sync())) + return@Runnable + } catch (e: Throwable) { + lastException = e + } + } + lastException?.let { exception -> + callback.accept(Result.failure(PubNubException.from(exception).copy(remoteAction = this))) + } + }, + ) + } + + override fun retry() { + async(cachedCallback) + } + + override fun silentCancel() { + remoteAction.silentCancel() + } + + override fun operationType(): PNOperationType { + return remoteAction.operationType() + } + + @Throws(PubNubException::class) + private fun validate() { + if (maxNumberOfAutomaticRetries < 1) { + throw PubNubException(PubNubError.INVALID_ARGUMENTS) + } + } + + companion object { + fun autoRetry( + remoteAction: ExtendedRemoteAction, + maxNumberOfAutomaticRetries: Int, + executorService: ExecutorService, + ): RetryingRemoteAction { + return RetryingRemoteAction(remoteAction, maxNumberOfAutomaticRetries, executorService) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Effect.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Effect.kt new file mode 100644 index 000000000..284299ba8 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Effect.kt @@ -0,0 +1,5 @@ +package com.pubnub.internal.eventengine + +internal interface Effect { + fun runEffect() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EffectDispatcher.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EffectDispatcher.kt new file mode 100644 index 000000000..4bae3d469 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EffectDispatcher.kt @@ -0,0 +1,52 @@ +package com.pubnub.internal.eventengine + +import org.slf4j.LoggerFactory +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +internal class EffectDispatcher( + private val effectFactory: EffectFactory, + private val effectSource: Source, + private val managedEffects: ConcurrentHashMap = ConcurrentHashMap(), + private val executorService: ExecutorService = Executors.newSingleThreadExecutor(), +) { + private val log = LoggerFactory.getLogger(EffectDispatcher::class.java) + + fun start() { + executorService.submit { + try { + while (true) { + val invocation = effectSource.take() + dispatch(invocation) + } + } catch (e: InterruptedException) { + Thread.currentThread().interrupt() + } + } + } + + fun stop() { + executorService.shutdownNow() + } + + internal fun dispatch(effectInvocation: T) { + log.trace("Dispatching effect: $effectInvocation") + when (val type = effectInvocation.type) { + is Cancel -> { + managedEffects.remove(type.idToCancel)?.cancel() + } + + is Managed -> { + managedEffects.remove(effectInvocation.id)?.cancel() + val managedEffect = effectFactory.create(effectInvocation) as? ManagedEffect ?: return + managedEffects[effectInvocation.id] = managedEffect + managedEffect.runEffect() + } + + is NonManaged -> { + effectFactory.create(effectInvocation)?.runEffect() + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EffectFactory.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EffectFactory.kt new file mode 100644 index 000000000..ad08b4360 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EffectFactory.kt @@ -0,0 +1,5 @@ +package com.pubnub.internal.eventengine + +internal interface EffectFactory { + fun create(effectInvocation: T): Effect? +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EffectInvocation.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EffectInvocation.kt new file mode 100644 index 000000000..510dc4d35 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EffectInvocation.kt @@ -0,0 +1,14 @@ +package com.pubnub.internal.eventengine + +internal interface EffectInvocation { + val id: String + val type: EffectInvocationType +} + +internal sealed interface EffectInvocationType + +internal data class Cancel(val idToCancel: String) : EffectInvocationType + +internal object Managed : EffectInvocationType + +internal object NonManaged : EffectInvocationType diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Event.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Event.kt new file mode 100644 index 000000000..4e5918739 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Event.kt @@ -0,0 +1,3 @@ +package com.pubnub.internal.eventengine + +internal interface Event diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EventEngine.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EventEngine.kt new file mode 100644 index 000000000..715671282 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EventEngine.kt @@ -0,0 +1,42 @@ +package com.pubnub.internal.eventengine + +import org.slf4j.LoggerFactory +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +internal class EventEngine>( + private val effectSink: Sink, + private val eventSource: Source, + private var currentState: S, + private val executorService: ExecutorService = Executors.newSingleThreadExecutor(), +) { + private val log = LoggerFactory.getLogger(EventEngine::class.java) + + fun start() { + executorService.submit { + try { + while (true) { + val event = eventSource.take() + performTransitionAndEmitEffects(event) + } + } catch (e: InterruptedException) { + Thread.currentThread().interrupt() + } + } + } + + fun stop() { + executorService.shutdownNow() + } + + internal fun performTransitionAndEmitEffects(event: Ev) { + log.trace( + "Current state is: ${currentState::class.simpleName} ; ${ + event::class.java.name.substringAfterLast('.').substringBefore('$') + } to be handled is: $event ", + ) + val (newState, invocations) = transition(currentState, event) + currentState = newState + invocations.forEach { invocation -> effectSink.add(invocation) } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EventEngineConf.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EventEngineConf.kt new file mode 100644 index 000000000..8daccac3d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EventEngineConf.kt @@ -0,0 +1,18 @@ +package com.pubnub.internal.eventengine + +internal interface EventEngineConf { + val eventSink: Sink + val eventSource: Source + val effectSink: Sink + val effectSource: Source +} + +internal class QueueEventEngineConf( + effectSinkSource: SinkSource = QueueSinkSource(), + eventSinkSource: SinkSource = QueueSinkSource(), +) : EventEngineConf { + override val eventSink: Sink = eventSinkSource + override val eventSource: Source = eventSinkSource + override val effectSink: Sink = effectSinkSource + override val effectSource: Source = effectSinkSource +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EventEngineManager.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EventEngineManager.kt new file mode 100644 index 000000000..eb7235a2c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/EventEngineManager.kt @@ -0,0 +1,21 @@ +package com.pubnub.internal.eventengine + +internal class EventEngineManager, Ee : EventEngine>( + private val eventEngine: Ee, + private val effectDispatcher: EffectDispatcher, + private val eventSink: Sink, +) { + fun addEventToQueue(event: Ev) { + eventSink.add(event) + } + + fun start() { + eventEngine.start() + effectDispatcher.start() + } + + fun stop() { + eventEngine.stop() + effectDispatcher.stop() + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/ManagedEffect.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/ManagedEffect.kt new file mode 100644 index 000000000..8a89d8443 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/ManagedEffect.kt @@ -0,0 +1,5 @@ +package com.pubnub.internal.eventengine + +internal interface ManagedEffect : Effect { + fun cancel() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/QueueSinkSource.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/QueueSinkSource.kt new file mode 100644 index 000000000..d95722fd8 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/QueueSinkSource.kt @@ -0,0 +1,16 @@ +package com.pubnub.internal.eventengine + +import java.util.concurrent.BlockingQueue +import java.util.concurrent.LinkedBlockingQueue + +internal interface SinkSource : Sink, Source + +internal class QueueSinkSource(private val queue: BlockingQueue = LinkedBlockingQueue()) : SinkSource { + override fun take(): T { + return queue.take() + } + + override fun add(item: T) { + queue.add(item) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Sink.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Sink.kt new file mode 100644 index 000000000..748bdd92e --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Sink.kt @@ -0,0 +1,5 @@ +package com.pubnub.internal.eventengine + +internal interface Sink { + fun add(item: T) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Source.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Source.kt new file mode 100644 index 000000000..76f51204f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Source.kt @@ -0,0 +1,5 @@ +package com.pubnub.internal.eventengine + +internal interface Source { + fun take(): T +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/State.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/State.kt new file mode 100644 index 000000000..17914311b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/State.kt @@ -0,0 +1,30 @@ +package com.pubnub.internal.eventengine + +import org.slf4j.LoggerFactory + +internal interface State> { + companion object { + internal val logger = LoggerFactory.getLogger(this::class.java) + } + + fun onEntry(): Set = setOf() + + fun onExit(): Set = setOf() + + fun transition(event: Ev): Pair> +} + +internal fun > S.noTransition(): Pair> = Pair(this, emptySet()) + +internal fun > S.transitionTo( + state: S, + vararg invocations: Ei, +): Pair> { + State.logger.trace( + "Transitioning from ${this::class.simpleName} to ${state::class.simpleName} with ${invocations.size} " + + "invocations: ${invocations.joinToString(", ")}", + ) + + val effectInvocations = this.onExit() + invocations + state.onEntry() + return Pair(state, effectInvocations) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Transition.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Transition.kt new file mode 100644 index 000000000..7d0bd398d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/eventengine/Transition.kt @@ -0,0 +1,6 @@ +package com.pubnub.internal.eventengine + +internal fun > transition( + state: S, + event: EV, +): Pair> = state.transition(event) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/Boolean.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/Boolean.kt new file mode 100644 index 000000000..9718acc58 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/Boolean.kt @@ -0,0 +1,12 @@ +package com.pubnub.internal.extension + +internal val Boolean.numericString: String + get() = + if (this) { + "1" + } else { + "0" + } + +internal val Boolean.valueString: String + get() = "$this" diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/Int.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/Int.kt new file mode 100644 index 000000000..0bd937c67 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/Int.kt @@ -0,0 +1,15 @@ +package com.pubnub.internal.extension + +fun Int.nonPositiveToNull() = + if (this < 1) { + null + } else { + this + } + +fun Int.limit(limit: Int): Int { + return when { + this > limit -> limit + else -> this + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/JsonElement.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/JsonElement.kt new file mode 100644 index 000000000..22f1b7c1b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/JsonElement.kt @@ -0,0 +1,60 @@ +package com.pubnub.internal.extension + +import com.google.gson.JsonElement +import com.pubnub.api.PubNubError +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.internal.crypto.decryptString +import com.pubnub.internal.managers.MapperManager +import org.slf4j.LoggerFactory + +private val log = LoggerFactory.getLogger("JsonElement") + +private const val PN_OTHER = "pn_other" + +internal fun JsonElement.tryDecryptMessage( + cryptoModule: CryptoModule?, + mapper: MapperManager, +): Pair { + cryptoModule ?: return this to null + + val inputText = + if (mapper.isJsonObject(this)) { + // property pn_other is used when we want to send encrypted Push Notification, not whole JSON object is encrypted but only value of pn_other property + if (mapper.hasField(this, PN_OTHER)) { + // JSON with pn_other property indicates that this is encrypted Push Notification + mapper.elementToString(this, PN_OTHER) + } else { + // plain JSON object indicates that this is not encrypted message + return this to logAndReturnDecryptionError() + } + } else if (isJsonPrimitive && asJsonPrimitive.isString) { + // String may represent not encrypted string or encrypted data. We will check this when decrypting. + mapper.elementToString(this) + } else { + // Input represents some other Json structure, such as JsonArray + return this to logAndReturnDecryptionError() + } + + val outputText = + try { + cryptoModule.decryptString(inputText!!) + } catch (e: Exception) { + return this to logAndReturnDecryptionError() + } + + var outputObject = mapper.fromJson(outputText, JsonElement::class.java) + + mapper.getField(this, PN_OTHER)?.let { + val objectNode = mapper.getAsObject(this) + mapper.putOnObject(objectNode, PN_OTHER, outputObject) + outputObject = objectNode + } + + return outputObject to null +} + +private fun logAndReturnDecryptionError(): PubNubError { + val pnError = PubNubError.CRYPTO_IS_CONFIGURED_BUT_MESSAGE_IS_NOT_ENCRYPTED + log.warn(pnError.message) + return pnError +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/RetrofitResponse.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/RetrofitResponse.kt new file mode 100644 index 000000000..3dbe195e3 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/RetrofitResponse.kt @@ -0,0 +1,31 @@ +package com.pubnub.internal.extension + +import com.pubnub.api.models.consumer.objects.PNPage +import com.pubnub.api.models.consumer.objects.member.PNMember +import com.pubnub.api.models.consumer.objects.member.PNMemberArrayResult +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembership +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult +import com.pubnub.internal.models.server.objects_api.EntityArrayEnvelope +import retrofit2.Response + +internal fun Response>.toPNMemberArrayResult(): PNMemberArrayResult = + body()!!.let { arrayEnvelope -> + PNMemberArrayResult( + status = arrayEnvelope.status, + data = arrayEnvelope.data, + totalCount = arrayEnvelope.totalCount, + next = arrayEnvelope.next?.let { PNPage.PNNext(it) }, + prev = arrayEnvelope.prev?.let { PNPage.PNPrev(it) }, + ) + } + +internal fun Response>.toPNChannelMembershipArrayResult(): PNChannelMembershipArrayResult = + body()!!.let { arrayEnvelope -> + PNChannelMembershipArrayResult( + status = arrayEnvelope.status, + data = arrayEnvelope.data, + totalCount = arrayEnvelope.totalCount, + next = arrayEnvelope.next?.let { PNPage.PNNext(it) }, + prev = arrayEnvelope.prev?.let { PNPage.PNPrev(it) }, + ) + } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/ScheduledExecutorService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/ScheduledExecutorService.kt new file mode 100644 index 000000000..43da71a98 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/ScheduledExecutorService.kt @@ -0,0 +1,10 @@ +package com.pubnub.internal.extension + +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.ScheduledFuture +import kotlin.time.Duration + +fun ScheduledExecutorService.scheduleWithDelay( + delay: Duration, + action: () -> Unit, +): ScheduledFuture<*> = schedule(action, delay.inWholeMilliseconds, java.util.concurrent.TimeUnit.MILLISECONDS) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/String.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/String.kt new file mode 100644 index 000000000..6ab4c9847 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/extension/String.kt @@ -0,0 +1,3 @@ +package com.pubnub.internal.extension + +internal fun String.quoted() = """"$this"""" diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/interceptor/SignatureInterceptor.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/interceptor/SignatureInterceptor.kt new file mode 100644 index 000000000..c40722c33 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/interceptor/SignatureInterceptor.kt @@ -0,0 +1,15 @@ +package com.pubnub.internal.interceptor + +import com.pubnub.api.v2.PNConfiguration +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.PubNubUtil +import okhttp3.Interceptor +import okhttp3.Response + +class SignatureInterceptor(private val configuration: PNConfiguration) : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val originalRequest = chain.request() + val request = PubNubUtil.signRequest(originalRequest, configuration, PubNubImpl.timestamp()) + return chain.proceed(request) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/BasePathManager.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/BasePathManager.kt new file mode 100644 index 000000000..10a99980e --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/BasePathManager.kt @@ -0,0 +1,74 @@ +package com.pubnub.internal.managers + +import com.pubnub.api.v2.PNConfiguration +import com.pubnub.api.v2.PNConfiguration.Companion.isValid + +internal class BasePathManager(private val config: PNConfiguration) { + /** + * for cache busting, the current subdomain number used. + */ + private var currentSubdomain = 1 + + /** + * if using cache busting, this is the max number of subdomains that are supported. + */ + private val MAX_SUBDOMAIN = 20 + + /** + * default subdomain used if cache busting is disabled. + */ + + private val DEFAULT_SUBDOMAIN = "ps" + + /** + * default base path if a custom one is not provided. + */ + + private val DEFAULT_BASE_PATH = "pndsn.com" + + fun basePath(): String { + val basePathBuilder = + StringBuilder("http") + .append( + if (config.secure) { + "s" + } else { + "" + }, + ) + .append("://") + + when { + config.origin.isValid() -> { + basePathBuilder.append(config.origin) + } + + config.cacheBusting -> { + basePathBuilder + .append("ps") + .append(currentSubdomain) + .append(".") + .append(DEFAULT_BASE_PATH) + + incrementSubdomain() + } + + else -> { + basePathBuilder + .append(DEFAULT_SUBDOMAIN) + .append(".") + .append(DEFAULT_BASE_PATH) + } + } + + return basePathBuilder.toString() + } + + private fun incrementSubdomain() { + if (currentSubdomain == MAX_SUBDOMAIN) { + currentSubdomain = 1 + } else { + currentSubdomain++ + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/DuplicationManager.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/DuplicationManager.kt new file mode 100644 index 000000000..ed6a8637f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/DuplicationManager.kt @@ -0,0 +1,27 @@ +package com.pubnub.internal.managers + +import com.pubnub.api.v2.PNConfiguration +import com.pubnub.internal.models.server.SubscribeMessage + +internal class DuplicationManager(private val config: PNConfiguration) { + private val hashHistory: ArrayList = ArrayList() + + private fun getKey(message: SubscribeMessage) = + with(message) { + "${publishMetaData?.publishTimetoken}-${payload.hashCode()}" + } + + @Synchronized + fun isDuplicate(message: SubscribeMessage) = hashHistory.contains(getKey(message)) + + @Synchronized + fun addEntry(message: SubscribeMessage) { + if (hashHistory.size >= config.maximumMessagesCacheSize) { + hashHistory.removeAt(0) + } + hashHistory.add(getKey(message)) + } + + @Synchronized + fun clearHistory() = hashHistory.clear() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/ListenerManager.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/ListenerManager.kt new file mode 100644 index 000000000..107b3c7d0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/ListenerManager.kt @@ -0,0 +1,154 @@ +package com.pubnub.internal.managers + +import com.pubnub.api.PubNub +import com.pubnub.api.callbacks.Listener +import com.pubnub.api.callbacks.SubscribeCallback +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.api.models.consumer.pubsub.PNEvent +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.api.models.consumer.pubsub.PNSignalResult +import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult +import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult +import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult +import com.pubnub.api.v2.callbacks.EventEmitter +import com.pubnub.api.v2.callbacks.EventListener +import com.pubnub.api.v2.callbacks.StatusEmitter +import com.pubnub.api.v2.callbacks.StatusListener +import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.internal.subscribe.eventengine.effect.MessagesConsumer +import com.pubnub.internal.subscribe.eventengine.effect.StatusConsumer +import java.util.concurrent.CopyOnWriteArrayList + +class ListenerManager(val pubnub: PubNub) : MessagesConsumer, StatusConsumer, EventEmitter, StatusEmitter { + private val listeners = CopyOnWriteArrayList() + + private val statusListeners get() = listeners.filterIsInstance() + private val eventListeners get() = listeners.filterIsInstance() + + /** + * Add a listener. + * + * @param listener The listener to be added. + */ + override fun addListener(listener: EventListener) { + listeners.add(listener) + } + + override fun removeListener(listener: Listener) { + listeners.remove(listener) + } + + override fun removeAllListeners() { + listeners.clear() + } + + // for use by v2 listeners + private val announcementCallbacks = CopyOnWriteArrayList() + private val subscriptionCallbacks get() = announcementCallbacks.filter { it.phase == AnnouncementCallback.Phase.SUBSCRIPTION } + private val setCallbacks get() = announcementCallbacks.filter { it.phase == AnnouncementCallback.Phase.SET } + + fun addListener(listener: SubscribeCallback) { + listeners.add(listener) + } + + override fun addListener(listener: StatusListener) { + listeners.add(listener) + } + + internal fun addAnnouncementCallback(listener: AnnouncementCallback) { + announcementCallbacks.add(listener) + } + + internal fun removeAnnouncementCallback(listener: AnnouncementCallback) { + announcementCallbacks.remove(listener) + } + + override fun announce(status: PNStatus) { + statusListeners.forEach { it.status(pubnub, status) } + } + + override fun announce(message: PNMessageResult) { + eventListeners.forEach { it.message(pubnub, message) } + val envelope = AnnouncementEnvelope(message) + subscriptionCallbacks.forEach { it.message(pubnub, envelope) } + setCallbacks.forEach { it.message(pubnub, envelope) } + } + + override fun announce(presence: PNPresenceEventResult) { + eventListeners.forEach { it.presence(pubnub, presence) } + val envelope = AnnouncementEnvelope(presence) + subscriptionCallbacks.forEach { it.presence(pubnub, envelope) } + setCallbacks.forEach { it.presence(pubnub, envelope) } + } + + override fun announce(signal: PNSignalResult) { + eventListeners.forEach { it.signal(pubnub, signal) } + val envelope = AnnouncementEnvelope(signal) + subscriptionCallbacks.forEach { it.signal(pubnub, envelope) } + setCallbacks.forEach { it.signal(pubnub, envelope) } + } + + override fun announce(messageAction: PNMessageActionResult) { + eventListeners.forEach { it.messageAction(pubnub, messageAction) } + val envelope = AnnouncementEnvelope(messageAction) + subscriptionCallbacks.forEach { it.messageAction(pubnub, envelope) } + setCallbacks.forEach { it.messageAction(pubnub, envelope) } + } + + override fun announce(pnObjectEventResult: PNObjectEventResult) { + eventListeners.forEach { it.objects(pubnub, pnObjectEventResult) } + val envelope = AnnouncementEnvelope(pnObjectEventResult) + subscriptionCallbacks.forEach { it.objects(pubnub, envelope) } + setCallbacks.forEach { it.objects(pubnub, envelope) } + } + + override fun announce(pnFileEventResult: PNFileEventResult) { + eventListeners.forEach { it.file(pubnub, pnFileEventResult) } + val envelope = AnnouncementEnvelope(pnFileEventResult) + subscriptionCallbacks.forEach { it.file(pubnub, envelope) } + setCallbacks.forEach { it.file(pubnub, envelope) } + } +} + +data class AnnouncementEnvelope( + val event: T, +) { + val acceptedBy = mutableSetOf() +} + +interface AnnouncementCallback { + enum class Phase { SUBSCRIPTION, SET } + + val phase: Phase + + fun message( + pubnub: PubNub, + envelope: AnnouncementEnvelope, + ) + + fun presence( + pubnub: PubNub, + envelope: AnnouncementEnvelope, + ) + + fun signal( + pubnub: PubNub, + envelope: AnnouncementEnvelope, + ) + + fun messageAction( + pubnub: PubNub, + envelope: AnnouncementEnvelope, + ) + + fun objects( + pubnub: PubNub, + envelope: AnnouncementEnvelope, + ) + + fun file( + pubnub: PubNub, + envelope: AnnouncementEnvelope, + ) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/MapperManager.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/MapperManager.kt new file mode 100644 index 000000000..69e2b9538 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/MapperManager.kt @@ -0,0 +1,333 @@ +package com.pubnub.internal.managers + +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.JsonArray +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonParseException +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 { + private val objectMapper: Gson + internal val converterFactory: Converter.Factory + + init { + val booleanAsIntAdapter = + object : TypeAdapter() { + override fun write( + out: JsonWriter?, + value: Boolean?, + ) { + if (value == null) { + out?.nullValue() + } else { + out?.value(value) + } + } + + override fun read(_in: JsonReader): Boolean { + val peek: JsonToken = _in.peek() + return when (peek) { + JsonToken.BOOLEAN -> _in.nextBoolean() + JsonToken.NUMBER -> _in.nextInt() != 0 + JsonToken.STRING -> java.lang.Boolean.parseBoolean(_in.nextString()) + else -> throw IllegalStateException("Expected BOOLEAN or NUMBER but was $peek") + } + } + } + + val patchValueTypeFactory = object : TypeAdapterFactory { + override fun create(gson: Gson, type: TypeToken): TypeAdapter? { + if (type.rawType != PatchValue::class.java) { + return null + } + val factory = this + return object : TypeAdapter() { + 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 + 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 + 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() + converterFactory = GsonConverterFactory.create(objectMapper) + } + + fun hasField( + element: JsonElement, + field: String, + ) = element.asJsonObject.has(field) + + fun getField( + element: JsonElement?, + field: String, + ): JsonElement? { + if (element?.isJsonObject!!) { + return element.asJsonObject.get(field) + } + return null + } + + fun getArrayIterator(element: JsonElement?) = element?.asJsonArray?.iterator() + + fun getArrayIterator( + element: JsonElement, + field: String, + ) = element.asJsonObject.get(field).asJsonArray.iterator() + + fun getObjectIterator(element: JsonElement) = element.asJsonObject.entrySet().iterator() + + fun getObjectIterator( + element: JsonElement, + field: String, + ) = element.asJsonObject.get(field).asJsonObject.entrySet().iterator() + + fun elementToString(element: JsonElement?) = element?.asString + + fun elementToString( + element: JsonElement?, + field: String, + ) = element?.asJsonObject?.get(field)?.asString + + fun elementToInt( + element: JsonElement, + field: String, + ) = element.asJsonObject.get(field).asInt + + fun isJsonObject(element: JsonElement) = element.isJsonObject + + fun getAsObject(element: JsonElement) = element.asJsonObject + + fun getAsBoolean( + element: JsonElement, + field: String, + ) = element.asJsonObject.get(field)?.asBoolean + .run { this != null } + + fun putOnObject( + element: JsonObject, + key: String, + value: JsonElement, + ) = element.add(key, value) + + fun getArrayElement( + element: JsonElement, + index: Int, + ) = element.asJsonArray.get(index) + + fun elementToLong(element: JsonElement) = element.asLong + + fun elementToLong( + element: JsonElement, + field: String, + ) = element.asJsonObject.get(field).asLong + + fun getAsArray(element: JsonElement) = element.asJsonArray + + fun toJsonTree(any: Any?) = objectMapper.toJsonTree(any) + + fun fromJson( + input: String?, + clazz: Class, + ): T { + return try { + this.objectMapper.fromJson(input, clazz) + } catch (e: JsonParseException) { + throw PubNubException( + pubnubError = PubNubError.PARSING_ERROR, + errorMessage = e.message, + ) + } + } + + fun fromJson( + input: String?, + typeOfT: Type, + ): T { + return try { + this.objectMapper.fromJson(input, typeOfT) + } catch (e: JsonParseException) { + throw PubNubException( + pubnubError = PubNubError.PARSING_ERROR, + errorMessage = e.message, + ) + } + } + + fun convertValue( + input: JsonElement?, + clazz: Class, + ): T { + return this.objectMapper.fromJson(input, clazz) as T + } + + fun convertValue( + o: Any?, + clazz: Class?, + ): T { + return this.objectMapper.fromJson(toJson(o), clazz) as T + } + + fun toJson(input: Any?): String { + try { + return if (input is List<*> && input.javaClass.isAnonymousClass) { + objectMapper.toJson(input, List::class.java) + } else if (input is Map<*, *> && input.javaClass.isAnonymousClass) { + objectMapper.toJson(input, Map::class.java) + } else if (input is Set<*> && input.javaClass.isAnonymousClass) { + objectMapper.toJson(input, Set::class.java) + } else { + objectMapper.toJson(input) + } + } catch (e: JsonParseException) { + throw PubNubException( + pubnubError = PubNubError.JSON_ERROR, + errorMessage = e.message, + ) + } + } + + private class JSONObjectAdapter : JsonSerializer, JsonDeserializer { + override fun serialize( + src: JSONObject?, + typeOfSrc: Type?, + context: JsonSerializationContext, + ): JsonElement? { + if (src == null) { + return null + } + val jsonObject = JsonObject() + val keys: Iterator = src.keys() + while (keys.hasNext()) { + val key = keys.next() + val value: Any = src.opt(key) + val jsonElement = context.serialize(value, value.javaClass) + jsonObject.add(key, jsonElement) + } + return jsonObject + } + + override fun deserialize( + json: JsonElement?, + typeOfT: Type?, + context: JsonDeserializationContext?, + ): JSONObject? { + return if (json == null) { + null + } else { + try { + JSONObject(json.toString()) + } catch (e: JSONException) { + e.printStackTrace() + throw JsonParseException(e) + } + } + } + } + + private class JSONArrayAdapter : JsonSerializer, JsonDeserializer { + override fun serialize( + src: JSONArray?, + typeOfSrc: Type?, + context: JsonSerializationContext, + ): JsonElement? { + if (src == null) { + return null + } + val jsonArray = JsonArray() + for (i in 0 until src.length()) { + val obj: Any = src.opt(i) + val jsonElement = context.serialize(obj, obj.javaClass) + jsonArray.add(jsonElement) + } + return jsonArray + } + + override fun deserialize( + json: JsonElement?, + typeOfT: Type?, + context: JsonDeserializationContext?, + ): JSONArray? { + return if (json == null) { + null + } else { + try { + JSONArray(json.toString()) + } catch (e: JSONException) { + e.printStackTrace() + throw JsonParseException(e) + } + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/PresenceEventEngineManager.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/PresenceEventEngineManager.kt new file mode 100644 index 000000000..3e0570d76 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/PresenceEventEngineManager.kt @@ -0,0 +1,9 @@ +package com.pubnub.internal.managers + +import com.pubnub.internal.eventengine.EventEngineManager +import com.pubnub.internal.presence.eventengine.PresenceEventEngine +import com.pubnub.internal.presence.eventengine.effect.PresenceEffectInvocation +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.presence.eventengine.state.PresenceState + +internal typealias PresenceEventEngineManager = EventEngineManager diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/PublishSequenceManager.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/PublishSequenceManager.kt new file mode 100644 index 000000000..d9e45210d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/PublishSequenceManager.kt @@ -0,0 +1,16 @@ +package com.pubnub.internal.managers + +import java.util.concurrent.atomic.AtomicInteger + +internal class PublishSequenceManager(private val maxSequence: Int) { + private val atomicSeq = AtomicInteger(1) + + internal fun nextSequence(): Int = + atomicSeq.getAndUpdate { + if (maxSequence == it) { + 1 + } else { + it + 1 + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/RetrofitManager.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/RetrofitManager.kt new file mode 100644 index 000000000..93a78e190 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/RetrofitManager.kt @@ -0,0 +1,182 @@ +package com.pubnub.internal.managers + +import com.pubnub.api.enums.PNLogVerbosity +import com.pubnub.api.v2.PNConfiguration +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.interceptor.SignatureInterceptor +import com.pubnub.internal.services.AccessManagerService +import com.pubnub.internal.services.ChannelGroupService +import com.pubnub.internal.services.FilesService +import com.pubnub.internal.services.HistoryService +import com.pubnub.internal.services.MessageActionService +import com.pubnub.internal.services.ObjectsService +import com.pubnub.internal.services.PresenceService +import com.pubnub.internal.services.PublishService +import com.pubnub.internal.services.PushService +import com.pubnub.internal.services.S3Service +import com.pubnub.internal.services.SignalService +import com.pubnub.internal.services.SubscribeService +import com.pubnub.internal.services.TimeService +import com.pubnub.internal.vendor.AppEngineFactory.Factory +import okhttp3.Call +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.jetbrains.annotations.TestOnly +import retrofit2.Retrofit +import java.util.concurrent.ExecutorService +import java.util.concurrent.TimeUnit + +class RetrofitManager( + val pubnub: PubNubImpl, + private val configuration: PNConfiguration, + @get:TestOnly internal var transactionClientInstance: OkHttpClient? = null, + @get:TestOnly internal var subscriptionClientInstance: OkHttpClient? = null, + @get:TestOnly internal var noSignatureClientInstance: OkHttpClient? = null, +) { + private var signatureInterceptor: SignatureInterceptor = SignatureInterceptor(configuration) + + internal val timeService: TimeService + internal val publishService: PublishService + internal val historyService: HistoryService + internal val presenceService: PresenceService + internal val messageActionService: MessageActionService + internal val signalService: SignalService + internal val channelGroupService: ChannelGroupService + internal val pushService: PushService + internal val accessManagerService: AccessManagerService + + internal val subscribeService: SubscribeService + internal val objectsService: ObjectsService + internal val filesService: FilesService + internal val s3Service: S3Service + + /** + * Use to get a new RetrofitManager with shared OkHttpClients while overriding configuration values. + */ + constructor(retrofitManager: RetrofitManager, configuration: PNConfiguration) : this( + retrofitManager.pubnub, + configuration, + retrofitManager.transactionClientInstance, + retrofitManager.subscriptionClientInstance, + retrofitManager.noSignatureClientInstance, + ) + + init { + if (!configuration.googleAppEngineNetworking) { + transactionClientInstance = createOkHttpClient(configuration.nonSubscribeReadTimeout, parentOkHttpClient = transactionClientInstance) + subscriptionClientInstance = createOkHttpClient(configuration.subscribeTimeout, parentOkHttpClient = subscriptionClientInstance) + noSignatureClientInstance = + createOkHttpClient(configuration.nonSubscribeReadTimeout, withSignature = false, parentOkHttpClient = noSignatureClientInstance) + } + + val transactionInstance = createRetrofit(transactionClientInstance) + val subscriptionInstance = createRetrofit(subscriptionClientInstance) + val noSignatureInstance = createRetrofit(noSignatureClientInstance) + + timeService = transactionInstance.create(TimeService::class.java) + publishService = transactionInstance.create(PublishService::class.java) + historyService = transactionInstance.create(HistoryService::class.java) + presenceService = transactionInstance.create(PresenceService::class.java) + messageActionService = transactionInstance.create(MessageActionService::class.java) + signalService = transactionInstance.create(SignalService::class.java) + channelGroupService = transactionInstance.create(ChannelGroupService::class.java) + pushService = transactionInstance.create(PushService::class.java) + accessManagerService = transactionInstance.create(AccessManagerService::class.java) + objectsService = transactionInstance.create(ObjectsService::class.java) + filesService = transactionInstance.create(FilesService::class.java) + s3Service = noSignatureInstance.create(S3Service::class.java) + + subscribeService = subscriptionInstance.create(SubscribeService::class.java) + } + + fun getTransactionClientExecutorService(): ExecutorService? { + return transactionClientInstance?.dispatcher?.executorService + } + + private fun createOkHttpClient( + readTimeout: Int, + withSignature: Boolean = true, + parentOkHttpClient: OkHttpClient? = null, + ): OkHttpClient { + val okHttpBuilder = parentOkHttpClient?.newBuilder() ?: OkHttpClient.Builder() + + okHttpBuilder + .retryOnConnectionFailure(false) + .readTimeout(readTimeout.toLong(), TimeUnit.SECONDS) + .connectTimeout(configuration.connectTimeout.toLong(), TimeUnit.SECONDS) + + with(configuration) { + if (logVerbosity == PNLogVerbosity.BODY) { + okHttpBuilder.addInterceptor( + HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + }, + ) + } + + if (httpLoggingInterceptor != null) { + okHttpBuilder.addInterceptor(httpLoggingInterceptor!!) + } + + if (sslSocketFactory != null && x509ExtendedTrustManager != null) { + okHttpBuilder.sslSocketFactory( + configuration.sslSocketFactory!!, + configuration.x509ExtendedTrustManager!!, + ) + } + connectionSpec?.let { okHttpBuilder.connectionSpecs(listOf(it)) } + hostnameVerifier?.let { okHttpBuilder.hostnameVerifier(it) } + proxy?.let { okHttpBuilder.proxy(it) } + proxySelector?.let { okHttpBuilder.proxySelector(it) } + proxyAuthenticator?.let { okHttpBuilder.proxyAuthenticator(it) } + certificatePinner?.let { okHttpBuilder.certificatePinner(it) } + } + + if (withSignature) { + okHttpBuilder.interceptors().removeAll { it is SignatureInterceptor } + okHttpBuilder.addInterceptor(signatureInterceptor) + } + + val okHttpClient = okHttpBuilder.build() + + configuration.maximumConnections?.let { okHttpClient.dispatcher.maxRequestsPerHost = it } + + return okHttpClient + } + + private fun createRetrofit(callFactory: Call.Factory?): Retrofit { + val retrofitBuilder = + Retrofit.Builder() + .baseUrl(pubnub.baseUrl) + .addConverterFactory(pubnub.mapper.converterFactory) + + if (configuration.googleAppEngineNetworking) { + retrofitBuilder.callFactory(Factory(configuration)) + } else if (callFactory != null) { + retrofitBuilder.callFactory(callFactory) + } else { + throw IllegalStateException("Can't instantiate PubNub") + } + return retrofitBuilder.build() + } + + fun destroy(force: Boolean = false) { + closeExecutor(transactionClientInstance, force) + closeExecutor(subscriptionClientInstance, force) + closeExecutor(noSignatureClientInstance, force) + } + + private fun closeExecutor( + client: OkHttpClient?, + force: Boolean, + ) { + if (client != null) { + client.dispatcher.cancelAll() + if (force) { + client.connectionPool.evictAll() + val executorService = client.dispatcher.executorService + executorService.shutdown() + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/SubscribeEventEngineManager.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/SubscribeEventEngineManager.kt new file mode 100644 index 000000000..9a1ecc6a5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/SubscribeEventEngineManager.kt @@ -0,0 +1,9 @@ +package com.pubnub.internal.managers + +import com.pubnub.internal.eventengine.EventEngineManager +import com.pubnub.internal.subscribe.eventengine.SubscribeEventEngine +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.state.SubscribeState + +internal typealias SubscribeEventEngineManager = EventEngineManager diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/TokenManager.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/TokenManager.kt new file mode 100644 index 000000000..6f5760e4b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/TokenManager.kt @@ -0,0 +1,14 @@ +package com.pubnub.internal.managers + +class TokenManager { + @Volatile + private var token: String? = null + + fun setToken(token: String?) { + this.token = token + } + + fun getToken(): String? { + return token + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/TokenParser.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/TokenParser.kt new file mode 100644 index 000000000..8abbb4da7 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/managers/TokenParser.kt @@ -0,0 +1,133 @@ +package com.pubnub.internal.managers + +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 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 { + fun unwrapToken(token: String): PNToken { + val byteArray = + com.pubnub.internal.vendor.Base64.decode( + token.toByteArray(StandardCharsets.UTF_8), + com.pubnub.internal.vendor.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 { + 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 { + if (depth > 3) { + throw PubNubException(pubnubError = PubNubError.INVALID_ACCESS_TOKEN, errorMessage = "Token is too deep") + } + val result = mutableMapOf() + 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 { + 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" + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/Converters.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/Converters.kt deleted file mode 100644 index 14662b545..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/Converters.kt +++ /dev/null @@ -1,467 +0,0 @@ -@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") - -package com.pubnub.internal.models - -import com.pubnub.api.UserId -import com.pubnub.api.models.consumer.access_manager.sum.SpacePermissions -import com.pubnub.api.models.consumer.access_manager.sum.UserPermissions -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant -import com.pubnub.api.models.consumer.access_manager.v3.PNChannelPatternGrant -import com.pubnub.api.models.consumer.access_manager.v3.PNChannelResourceGrant -import com.pubnub.api.models.consumer.access_manager.v3.PNSpacePatternPermissionsGrant -import com.pubnub.api.models.consumer.access_manager.v3.PNSpacePermissionsGrant -import com.pubnub.api.models.consumer.access_manager.v3.PNUUIDPatternGrant -import com.pubnub.api.models.consumer.access_manager.v3.PNUUIDResourceGrant -import com.pubnub.api.models.consumer.access_manager.v3.PNUserPatternPermissionsGrant -import com.pubnub.api.models.consumer.access_manager.v3.PNUserPermissionsGrant -import com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant -import com.pubnub.api.models.consumer.objects.PNKey -import com.pubnub.api.models.consumer.objects.PNMemberKey -import com.pubnub.api.models.consumer.objects.PNMembershipKey -import com.pubnub.api.models.consumer.objects.PNRemoveMetadataResult -import com.pubnub.api.models.consumer.objects.PNSortKey -import com.pubnub.api.models.consumer.objects.SortField -import com.pubnub.api.models.consumer.objects.member.MemberInput -import com.pubnub.api.models.consumer.objects.member.PNMemberArrayResult -import com.pubnub.api.models.consumer.objects.member.PNUUIDDetailsLevel -import com.pubnub.api.models.consumer.objects.membership.ChannelMembershipInput -import com.pubnub.api.models.consumer.objects.membership.PNChannelDetailsLevel -import com.pubnub.api.models.consumer.objects.membership.PNChannelMembership -import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArrayResult -import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata -import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadataArrayResult -import com.pubnub.api.models.consumer.presence.PNWhereNowResult -import com.pubnub.api.models.consumer.pubsub.BasePubSubResult -import com.pubnub.internal.SpaceId -import com.pubnub.internal.models.consumer.objects.member.PNMember -import com.pubnub.internal.models.consumer.pubsub.objects.PNDeleteChannelMetadataEventMessage -import com.pubnub.internal.models.consumer.pubsub.objects.PNDeleteMembershipEvent -import com.pubnub.internal.models.consumer.pubsub.objects.PNDeleteMembershipEventMessage -import com.pubnub.internal.models.consumer.pubsub.objects.PNDeleteUUIDMetadataEventMessage -import com.pubnub.internal.models.consumer.pubsub.objects.PNObjectEventMessage -import com.pubnub.internal.models.consumer.pubsub.objects.PNObjectEventResult -import com.pubnub.internal.models.consumer.pubsub.objects.PNSetChannelMetadataEventMessage -import com.pubnub.internal.models.consumer.pubsub.objects.PNSetMembershipEvent -import com.pubnub.internal.models.consumer.pubsub.objects.PNSetMembershipEventMessage -import com.pubnub.internal.models.consumer.pubsub.objects.PNSetUUIDMetadataEventMessage - -internal fun PNObjectEventResult.toApi(): com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult { - return com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult( - BasePubSubResult( - channel = this.channel, - subscription = this.subscription, - timetoken = this.timetoken, - userMetadata = this.userMetadata, - publisher = this.publisher, - ), - this.extractedMessage.toApi(), - ) -} - -private fun PNObjectEventMessage.toApi(): com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventMessage { - return when (this) { - is PNSetChannelMetadataEventMessage -> { - com.pubnub.api.models.consumer.pubsub.objects.PNSetChannelMetadataEventMessage( - source = this.source, - version = this.version, - event = this.event, - type = this.type, - data = this.data, - ) - } - - is PNDeleteChannelMetadataEventMessage -> { - com.pubnub.api.models.consumer.pubsub.objects.PNDeleteChannelMetadataEventMessage( - source = this.source, - version = this.version, - event = this.event, - type = this.type, - channel = this.channel, - ) - } - - is PNDeleteMembershipEventMessage -> { - com.pubnub.api.models.consumer.pubsub.objects.PNDeleteMembershipEventMessage( - source = this.source, - version = this.version, - event = this.event, - type = this.type, - data = this.data.toApi(), - ) - } - - is PNDeleteUUIDMetadataEventMessage -> { - com.pubnub.api.models.consumer.pubsub.objects.PNDeleteUUIDMetadataEventMessage( - source = this.source, - version = this.version, - event = this.event, - type = this.type, - uuid = this.uuid, - ) - } - - is PNSetMembershipEventMessage -> { - com.pubnub.api.models.consumer.pubsub.objects.PNSetMembershipEventMessage( - source = this.source, - version = this.version, - event = this.event, - type = this.type, - data = this.data.toApi(), - ) - } - - is PNSetUUIDMetadataEventMessage -> { - com.pubnub.api.models.consumer.pubsub.objects.PNSetUUIDMetadataEventMessage( - source = this.source, - version = this.version, - event = this.event, - type = this.type, - data = this.data.toApi(), - ) - } - } -} - -private fun PNUUIDMetadata.toApi(): PNUUIDMetadata { - return PNUUIDMetadata( - id = this.id, - name = this.name, - externalId = this.externalId, - profileUrl = this.profileUrl, - email = this.email, - custom = this.custom, - updated = this.updated, - eTag = this.eTag, - type = this.type, - status = this.status, - ) -} - -private fun PNSetMembershipEvent.toApi(): com.pubnub.api.models.consumer.pubsub.objects.PNSetMembershipEvent { - return com.pubnub.api.models.consumer.pubsub.objects.PNSetMembershipEvent( - channel = this.channel, - uuid = this.uuid, - custom = this.custom, - eTag = this.eTag, - updated = this.updated, - status = this.status, - ) -} - -private fun PNDeleteMembershipEvent.toApi(): com.pubnub.api.models.consumer.pubsub.objects.PNDeleteMembershipEvent { - return com.pubnub.api.models.consumer.pubsub.objects.PNDeleteMembershipEvent(this.channelId, this.uuid) -} - -internal fun List.toInternalChannelGrants(): List { - return map { - it.toInternal() - } -} - -private fun ChannelGrant.toInternal(): com.pubnub.internal.models.consumer.access_manager.v3.ChannelGrant { - return when (this) { - is PNChannelResourceGrant -> { - com.pubnub.internal.models.consumer.access_manager.v3.ChannelGrant.name( - name = id, - read = read, - write = write, - manage = manage, - delete = delete, - create = create, - get = get, - join = join, - update = update, - ) - } - - is PNChannelPatternGrant -> { - com.pubnub.internal.models.consumer.access_manager.v3.ChannelGrant.pattern( - pattern = id, - read = read, - write = write, - manage = manage, - delete = delete, - create = create, - get = get, - join = join, - update = update, - ) - } - - else -> { - throw IllegalStateException("Should never happen.") - } - } -} - -internal fun List.toInternalChannelGroupGrants(): List { - return map { it.toInternal() } -} - -private fun ChannelGroupGrant.toInternal(): com.pubnub.internal.models.consumer.access_manager.v3.ChannelGroupGrant { - return when (this) { - is com.pubnub.api.models.consumer.access_manager.v3.PNChannelGroupResourceGrant -> { - com.pubnub.internal.models.consumer.access_manager.v3.ChannelGroupGrant.id( - id = id, - read = read, - manage = manage, - ) - } - - is com.pubnub.api.models.consumer.access_manager.v3.PNChannelGroupPatternGrant -> { - com.pubnub.internal.models.consumer.access_manager.v3.ChannelGroupGrant.pattern( - pattern = id, - read = read, - manage = manage, - ) - } - - else -> { - throw IllegalStateException("Should never happen.") - } - } -} - -internal fun List.toInternalUuidGrants(): List { - return map { - it.toInternal() - } -} - -private fun UUIDGrant.toInternal(): com.pubnub.internal.models.consumer.access_manager.v3.UUIDGrant { - return when (this) { - is PNUUIDResourceGrant -> { - com.pubnub.internal.models.consumer.access_manager.v3.UUIDGrant.id( - id = id, - get = get, - update = update, - delete = delete, - ) - } - - is PNUUIDPatternGrant -> { - com.pubnub.internal.models.consumer.access_manager.v3.UUIDGrant.pattern( - pattern = id, - get = get, - update = update, - delete = delete, - ) - } - - else -> { - throw IllegalStateException("Should never happen.") - } - } -} - -internal fun List.toInternalSpacePermissions(): List { - return map { - it.toInternal() - } -} - -private fun SpacePermissions.toInternal(): com.pubnub.internal.models.consumer.access_manager.sum.SpacePermissions { - return when (this) { - is PNSpacePermissionsGrant -> { - com.pubnub.internal.models.consumer.access_manager.sum.SpacePermissions.id( - spaceId = SpaceId(id), - read = read, - write = write, - manage = manage, - delete = delete, - get = get, - join = join, - update = update, - ) - } - - is PNSpacePatternPermissionsGrant -> { - com.pubnub.internal.models.consumer.access_manager.sum.SpacePermissions.pattern( - pattern = id, - read = read, - write = write, - manage = manage, - delete = delete, - get = get, - join = join, - update = update, - ) - } - - else -> { - throw IllegalStateException("Should never happen.") - } - } -} - -internal fun List.toInternalUserPermissions(): List { - return map { - it.toInternal() - } -} - -private fun UserPermissions.toInternal(): com.pubnub.internal.models.consumer.access_manager.sum.UserPermissions { - return when (this) { - is PNUserPermissionsGrant -> { - com.pubnub.internal.models.consumer.access_manager.sum.UserPermissions.id( - userId = UserId(id), - get = get, - update = update, - delete = delete, - ) - } - - is PNUserPatternPermissionsGrant -> { - com.pubnub.internal.models.consumer.access_manager.sum.UserPermissions.pattern( - pattern = id, - get = get, - update = update, - delete = delete, - ) - } - - else -> { - throw IllegalStateException("Should never happen.") - } - } -} - -internal fun Collection>.toInternalSortKeys(): Collection> { - return map { - it.toInternal() - } -} - -private fun PNSortKey.toInternal(): com.pubnub.internal.models.consumer.objects.PNSortKey { - @Suppress("UNCHECKED_CAST") - val sortKey: T2 = - when (val key = this.key) { - is PNKey -> { - com.pubnub.internal.models.consumer.objects.PNKey.valueOf(key.name) as T2 - } - - is PNMembershipKey -> { - com.pubnub.internal.models.consumer.objects.PNMembershipKey.valueOf(key.name) as T2 - } - - is PNMemberKey -> { - com.pubnub.internal.models.consumer.objects.PNMemberKey.valueOf(key.name) as T2 - } - - else -> { - throw IllegalStateException("Should never happen.") - } - } - return when (this) { - is PNSortKey.PNAsc -> com.pubnub.internal.models.consumer.objects.PNSortKey.PNAsc(sortKey) - is PNSortKey.PNDesc -> com.pubnub.internal.models.consumer.objects.PNSortKey.PNDesc(sortKey) - } -} - -internal fun PNChannelDetailsLevel?.toInternal(): com.pubnub.internal.models.consumer.objects.membership.PNChannelDetailsLevel? { - if (this == null) { - return null - } - return com.pubnub.internal.models.consumer.objects.membership.PNChannelDetailsLevel.valueOf(this.name) -} - -internal fun PNUUIDDetailsLevel?.toInternal(): com.pubnub.internal.models.consumer.objects.member.PNUUIDDetailsLevel? { - if (this == null) { - return null - } - return com.pubnub.internal.models.consumer.objects.member.PNUUIDDetailsLevel.valueOf(this.name) -} - -internal fun List.toInternalChannelMemberships(): List { - return map { - it.toInternal() - } -} - -private fun ChannelMembershipInput.toInternal(): com.pubnub.internal.models.consumer.objects.membership.ChannelMembershipInput { - return com.pubnub.internal.models.consumer.objects.membership.PNChannelMembership.Partial( - channelId = channel, - custom = custom, - status = status, - ) -} - -internal fun Collection.toInternalMemberInputs(): List { - return map { - it.toInternal() - } -} - -private fun MemberInput.toInternal(): com.pubnub.internal.models.consumer.objects.member.MemberInput { - return PNMember.Partial( - uuidId = this.uuid, - custom = custom, - status = status, - ) -} - -fun com.pubnub.internal.models.consumer.objects.membership.PNChannelMembershipArrayResult.toApi(): PNChannelMembershipArrayResult { - return PNChannelMembershipArrayResult( - status = status, - data = data.map(::from), - totalCount = totalCount, - next = next, - prev = prev, - ) -} - -fun com.pubnub.internal.models.consumer.presence.PNWhereNowResult.toApi(): PNWhereNowResult { - return PNWhereNowResult(channels) -} - -fun from(data: com.pubnub.internal.models.consumer.objects.PNRemoveMetadataResult): PNRemoveMetadataResult { - return PNRemoveMetadataResult(data.status) -} - -fun from(data: com.pubnub.internal.models.consumer.objects.member.PNMemberArrayResult): com.pubnub.api.models.consumer.objects.member.PNMemberArrayResult { - return PNMemberArrayResult( - data.status, - data.data.map(::from), - data.totalCount, - data.next, - data.prev, - ) -} - -fun from(data: com.pubnub.internal.models.consumer.objects.member.PNMember): com.pubnub.api.models.consumer.objects.member.PNMember { - return com.pubnub.api.models.consumer.objects.member.PNMember( - data.uuid, - data.custom, - data.updated, - data.eTag, - data.status, - ) -} - -fun from(data: com.pubnub.internal.models.consumer.objects.membership.PNChannelMembership): PNChannelMembership { - return PNChannelMembership( - data.channel, - data.custom, - data.updated, - data.eTag, - data.status, - ) -} - -fun from(data: com.pubnub.internal.models.consumer.objects.uuid.PNUUIDMetadataArrayResult): PNUUIDMetadataArrayResult { - return PNUUIDMetadataArrayResult( - data.status, - data.data, - data.totalCount, - data.next, - data.prev, - ) -} - -fun from(data: com.pubnub.internal.models.consumer.objects.uuid.PNUUIDMetadataResult): com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadataResult { - return com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadataResult( - data.status, - data.data, - ) -} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/consumer/pubsub/objects/PNObjectEventResult.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/consumer/pubsub/objects/PNObjectEventResult.kt new file mode 100644 index 000000000..38c20817e --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/consumer/pubsub/objects/PNObjectEventResult.kt @@ -0,0 +1,183 @@ +package com.pubnub.internal.models.consumer.pubsub.objects + +import com.google.gson.JsonDeserializer +import com.google.gson.annotations.JsonAdapter +import com.google.gson.annotations.SerializedName +import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata +import com.pubnub.api.utils.PatchValue +import com.pubnub.internal.utils.PolymorphicDeserializer +import com.pubnub.internal.utils.UnwrapSingleField + +internal object ObjectExtractedMessageDeserializer : + JsonDeserializer by PolymorphicDeserializer.dispatchByFieldsValues( + fields = listOf("event", "type"), + mappingFieldValuesToClass = + mapOf( + listOf("set", "channel") to PNSetChannelMetadataEventMessage::class.java, + listOf("set", "uuid") to PNSetUUIDMetadataEventMessage::class.java, + listOf("set", "membership") to PNSetMembershipEventMessage::class.java, + listOf("delete", "channel") to PNDeleteChannelMetadataEventMessage::class.java, + listOf("delete", "uuid") to PNDeleteUUIDMetadataEventMessage::class.java, + listOf("delete", "membership") to PNDeleteMembershipEventMessage::class.java, + ), + ) + +@JsonAdapter(ObjectExtractedMessageDeserializer::class) +sealed class PNObjectEventMessage { + abstract val source: String + abstract val version: String + abstract val event: String + abstract val type: String +} + +data class PNSetChannelMetadataEventMessage( + override val source: String, + override val version: String, + override val event: String, + override val type: String, + val data: PNChannelMetadata, +) : PNObjectEventMessage() + +data class PNSetUUIDMetadataEventMessage( + override val source: String, + override val version: String, + override val event: String, + override val type: String, + val data: PNUUIDMetadata, +) : PNObjectEventMessage() + +data class PNSetMembershipEventMessage( + override val source: String, + override val version: String, + override val event: String, + override val type: String, + val data: PNSetMembershipEvent, +) : PNObjectEventMessage() + +data class PNDeleteMembershipEventMessage( + override val source: String, + override val version: String, + override val event: String, + override val type: String, + val data: PNDeleteMembershipEvent, +) : PNObjectEventMessage() + +data class PNDeleteChannelMetadataEventMessage( + override val source: String, + override val version: String, + override val event: String, + override val type: String, + @JsonAdapter(UnwrapSingleField::class) + @SerializedName("data") + val channel: String, +) : PNObjectEventMessage() + +data class PNDeleteUUIDMetadataEventMessage( + override val source: String, + override val version: String, + override val event: String, + override val type: String, + @JsonAdapter(UnwrapSingleField::class) + @SerializedName("data") + val uuid: String, +) : PNObjectEventMessage() + +data class PNSetMembershipEvent( + @JsonAdapter(UnwrapSingleField::class) + @SerializedName("channel") + val channel: String, + @JsonAdapter(UnwrapSingleField::class) + val uuid: String, + val custom: PatchValue?>?, + val eTag: String, + val updated: String, + val status: PatchValue?, +) + +data class PNDeleteMembershipEvent( + @JsonAdapter(UnwrapSingleField::class) + @SerializedName("channel") + val channelId: String, + @JsonAdapter(UnwrapSingleField::class) + val uuid: String, +) + +fun PNObjectEventMessage.toApi(): com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventMessage { + return when (this) { + is PNSetChannelMetadataEventMessage -> { + com.pubnub.api.models.consumer.pubsub.objects.PNSetChannelMetadataEventMessage( + source = this.source, + version = this.version, + event = this.event, + type = this.type, + data = this.data, + ) + } + + is PNDeleteChannelMetadataEventMessage -> { + com.pubnub.api.models.consumer.pubsub.objects.PNDeleteChannelMetadataEventMessage( + source = this.source, + version = this.version, + event = this.event, + type = this.type, + channel = this.channel, + ) + } + + is PNDeleteMembershipEventMessage -> { + com.pubnub.api.models.consumer.pubsub.objects.PNDeleteMembershipEventMessage( + source = this.source, + version = this.version, + event = this.event, + type = this.type, + data = this.data.toApi(), + ) + } + + is PNDeleteUUIDMetadataEventMessage -> { + com.pubnub.api.models.consumer.pubsub.objects.PNDeleteUUIDMetadataEventMessage( + source = this.source, + version = this.version, + event = this.event, + type = this.type, + uuid = this.uuid, + ) + } + + is PNSetMembershipEventMessage -> { + com.pubnub.api.models.consumer.pubsub.objects.PNSetMembershipEventMessage( + source = this.source, + version = this.version, + event = this.event, + type = this.type, + data = this.data.toApi(), + ) + } + + is PNSetUUIDMetadataEventMessage -> { + com.pubnub.api.models.consumer.pubsub.objects.PNSetUUIDMetadataEventMessage( + source = this.source, + version = this.version, + event = this.event, + type = this.type, + data = this.data, + ) + } + } +} + +private fun PNSetMembershipEvent.toApi(): com.pubnub.api.models.consumer.pubsub.objects.PNSetMembershipEvent { + return com.pubnub.api.models.consumer.pubsub.objects.PNSetMembershipEvent( + channel = this.channel, + uuid = this.uuid, + custom = this.custom, + eTag = this.eTag, + updated = this.updated, + status = this.status, + ) +} + +private fun PNDeleteMembershipEvent.toApi(): com.pubnub.api.models.consumer.pubsub.objects.PNDeleteMembershipEvent { + return com.pubnub.api.models.consumer.pubsub.objects.PNDeleteMembershipEvent(this.channelId, this.uuid) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/Envelope.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/Envelope.kt new file mode 100644 index 000000000..550898e4d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/Envelope.kt @@ -0,0 +1,14 @@ +package com.pubnub.internal.models.server + +import com.google.gson.JsonElement + +class Envelope { + internal val status: Int = 0 + internal val message: String? = null + internal val service: String? = null + internal val payload: T? = null + internal val occupancy: Int = 0 + internal val uuids: JsonElement? = null + internal val action: String? = null + internal val error: Boolean = false +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/FetchMessagesEnvelope.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/FetchMessagesEnvelope.kt new file mode 100644 index 000000000..cb49df76d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/FetchMessagesEnvelope.kt @@ -0,0 +1,14 @@ +package com.pubnub.internal.models.server + +import com.pubnub.internal.models.server.history.ServerFetchMessageItem + +data class FetchMessagesEnvelope( + val channels: Map>, + val more: FetchMessagesPage?, +) + +data class FetchMessagesPage( + val start: Long? = null, + val end: Long? = null, + val max: Int? = null, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/OriginationMetaData.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/OriginationMetaData.kt new file mode 100644 index 000000000..52a43debd --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/OriginationMetaData.kt @@ -0,0 +1,10 @@ +package com.pubnub.internal.models.server + +import com.google.gson.annotations.SerializedName + +class OriginationMetaData( + @SerializedName("t") + internal val timetoken: Long?, + @SerializedName("r") + internal val region: Int?, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/PresenceEnvelope.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/PresenceEnvelope.kt new file mode 100644 index 000000000..cf4d70260 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/PresenceEnvelope.kt @@ -0,0 +1,11 @@ +package com.pubnub.internal.models.server + +import com.google.gson.JsonElement + +class PresenceEnvelope( + internal val action: String? = null, + internal val uuid: String? = null, + internal val occupancy: Int? = null, + internal val timestamp: Long? = null, + internal val data: JsonElement? = null, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/PublishMetaData.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/PublishMetaData.kt new file mode 100644 index 000000000..779649493 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/PublishMetaData.kt @@ -0,0 +1,10 @@ +package com.pubnub.internal.models.server + +import com.google.gson.annotations.SerializedName + +class PublishMetaData( + @SerializedName("t") + internal val publishTimetoken: Long?, + @SerializedName("r") + internal val region: Int?, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/SubscribeEnvelope.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/SubscribeEnvelope.kt new file mode 100644 index 000000000..c7161ea62 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/SubscribeEnvelope.kt @@ -0,0 +1,10 @@ +package com.pubnub.internal.models.server + +import com.google.gson.annotations.SerializedName + +class SubscribeEnvelope( + @SerializedName("m") + internal val messages: List, + @SerializedName("t") + internal val metadata: SubscribeMetaData, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/SubscribeMessage.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/SubscribeMessage.kt new file mode 100644 index 000000000..8caa8e381 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/SubscribeMessage.kt @@ -0,0 +1,33 @@ +package com.pubnub.internal.models.server + +import com.google.gson.JsonElement +import com.google.gson.annotations.SerializedName +import com.pubnub.internal.workers.SubscribeMessageProcessor.Companion.TYPE_FILES +import com.pubnub.internal.workers.SubscribeMessageProcessor.Companion.TYPE_MESSAGE + +class SubscribeMessage( + @SerializedName("a") + internal val shard: String?, + @SerializedName("b") + internal val subscriptionMatch: String?, + @SerializedName("c") + internal val channel: String?, + @SerializedName("d") + internal val payload: JsonElement?, + @SerializedName("f") + internal val flags: String?, + @SerializedName("i") + internal val issuingClientId: String?, + @SerializedName("k") + internal val subscribeKey: String?, + @SerializedName("o") + internal val originationMetadata: OriginationMetaData?, + @SerializedName("p") + internal val publishMetaData: PublishMetaData?, + @SerializedName("u") + internal val userMetadata: JsonElement?, + @SerializedName("e") + internal val type: Int?, +) { + fun supportsEncryption() = type in arrayOf(null, TYPE_MESSAGE, TYPE_FILES) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/SubscribeMetaData.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/SubscribeMetaData.kt new file mode 100644 index 000000000..28826a80f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/SubscribeMetaData.kt @@ -0,0 +1,10 @@ +package com.pubnub.internal.models.server + +import com.google.gson.annotations.SerializedName + +class SubscribeMetaData( + @SerializedName("t") + internal val timetoken: Long, + @SerializedName("r") + internal val region: String, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/AccessManagerGrantPayload.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/AccessManagerGrantPayload.kt new file mode 100644 index 000000000..7bb7b97eb --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/AccessManagerGrantPayload.kt @@ -0,0 +1,28 @@ +package com.pubnub.internal.models.server.access_manager + +import com.google.gson.JsonElement +import com.google.gson.annotations.SerializedName +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeyData +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeysData + +class AccessManagerGrantPayload { + internal var level: String? = null + + internal var ttl = 0 + + @SerializedName("subscribe_key") + internal var subscribeKey: String? = null + + internal val channels: Map? = null + + @SerializedName("channel-groups") + internal val channelGroups: JsonElement? = null + + @SerializedName("auths") + internal val authKeys: Map? = null + + @SerializedName("uuids") + internal val uuids: Map? = null + + internal val channel: String? = null +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/v3/GrantTokenRequestBody.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/v3/GrantTokenRequestBody.kt new file mode 100644 index 000000000..d6ecc443b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/v3/GrantTokenRequestBody.kt @@ -0,0 +1,97 @@ +package com.pubnub.internal.models.server.access_manager.v3 + +import com.pubnub.api.PubNubException +import com.pubnub.api.models.TokenBitmask +import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant +import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant +import com.pubnub.api.models.consumer.access_manager.v3.PNGrant +import com.pubnub.api.models.consumer.access_manager.v3.PNPatternGrant +import com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant + +data class GrantTokenRequestBody( + val ttl: Int, + val permissions: GrantTokenPermissions, +) { + data class GrantTokenPermissions( + val resources: GrantTokenPermission, + val patterns: GrantTokenPermission, + val meta: Any? = null, + val uuid: String? = null, + ) + + data class GrantTokenPermission( + val channels: Map = emptyMap(), + val groups: Map = emptyMap(), + val uuids: Map = emptyMap(), + val spaces: Map = emptyMap(), + val users: Map = emptyMap(), + ) + + companion object { + @Throws(PubNubException::class) + fun of( + ttl: Int, + channels: List, + groups: List, + uuids: List, + meta: Any?, + uuid: String?, + ): GrantTokenRequestBody { + val resources = + GrantTokenPermission( + getResources(channels), + getResources(groups), + getResources(uuids), + ) + val patterns = + GrantTokenPermission( + getPatterns(channels), + getPatterns(groups), + getPatterns(uuids), + ) + val permissions = GrantTokenPermissions(resources, patterns, meta ?: emptyMap(), uuid) + return GrantTokenRequestBody(ttl, permissions) + } + + private fun getResources(resources: List): Map { + return resources + .filter { it !is PNPatternGrant } + .associate { it.id to calculateBitmask(it) } + } + + private fun getPatterns(resources: List): Map { + return resources + .filterIsInstance() + .associate { it.id to calculateBitmask(it) } + } + + private fun calculateBitmask(resource: PNGrant): Int { + var sum = 0 + if (resource.read) { + sum = sum or TokenBitmask.READ + } + if (resource.write) { + sum = sum or TokenBitmask.WRITE + } + if (resource.manage) { + sum = sum or TokenBitmask.MANAGE + } + if (resource.delete) { + sum = sum or TokenBitmask.DELETE + } + if (resource.create) { + sum = sum or TokenBitmask.CREATE + } + if (resource.get) { + sum = sum or TokenBitmask.GET + } + if (resource.join) { + sum = sum or TokenBitmask.JOIN + } + if (resource.update) { + sum = sum or TokenBitmask.UPDATE + } + return sum + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/v3/GrantTokenResponse.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/v3/GrantTokenResponse.kt new file mode 100644 index 000000000..47e0dc73d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/v3/GrantTokenResponse.kt @@ -0,0 +1,5 @@ +package com.pubnub.internal.models.server.access_manager.v3 + +data class GrantTokenResponse(val data: GrantTokenData) + +data class GrantTokenData(val token: String) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/v3/RevokeTokenResponse.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/v3/RevokeTokenResponse.kt new file mode 100644 index 000000000..44e2b7060 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/access_manager/v3/RevokeTokenResponse.kt @@ -0,0 +1,5 @@ +package com.pubnub.internal.models.server.access_manager.v3 + +data class RevokeTokenResponse(val status: Int, val data: RevokeTokenData, val service: String) + +data class RevokeTokenData(val message: String, val token: String) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/FileUploadNotification.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/FileUploadNotification.kt new file mode 100644 index 000000000..77c1a745f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/FileUploadNotification.kt @@ -0,0 +1,8 @@ +package com.pubnub.internal.models.server.files + +import com.pubnub.api.models.consumer.files.PNBaseFile + +data class FileUploadNotification( + val message: Any?, + val file: PNBaseFile, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/FileUploadRequestDetails.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/FileUploadRequestDetails.kt new file mode 100644 index 000000000..96819e7f9 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/FileUploadRequestDetails.kt @@ -0,0 +1,13 @@ +package com.pubnub.internal.models.server.files + +import com.pubnub.api.models.consumer.files.PNFile + +data class FileUploadRequestDetails( + val status: Int, + val data: PNFile, + val url: String, + val method: String, + val expirationDate: String, + val keyFormField: FormField, + val formFields: List, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/FormField.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/FormField.kt new file mode 100644 index 000000000..9323d87b5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/FormField.kt @@ -0,0 +1,6 @@ +package com.pubnub.internal.models.server.files + +data class FormField( + val key: String, + val value: String, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/GenerateUploadUrlPayload.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/GenerateUploadUrlPayload.kt new file mode 100644 index 000000000..e05491457 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/GenerateUploadUrlPayload.kt @@ -0,0 +1,3 @@ +package com.pubnub.internal.models.server.files + +data class GenerateUploadUrlPayload(val name: String) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/GeneratedUploadUrlResponse.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/GeneratedUploadUrlResponse.kt new file mode 100644 index 000000000..a22d6ea73 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/GeneratedUploadUrlResponse.kt @@ -0,0 +1,20 @@ +package com.pubnub.internal.models.server.files + +import com.google.gson.annotations.SerializedName +import com.pubnub.api.models.consumer.files.PNUploadedFile + +data class GeneratedUploadUrlResponse( + val status: Int, + val data: PNUploadedFile, + @SerializedName("file_upload_request") + val fileUploadRequest: FileUploadRequest, +) { + data class FileUploadRequest( + val url: String, + val method: String, + @SerializedName("expiration_date") + val expirationDate: String, + @SerializedName("form_fields") + val formFields: List, + ) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/ListFilesResult.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/ListFilesResult.kt new file mode 100644 index 000000000..02832e882 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/files/ListFilesResult.kt @@ -0,0 +1,11 @@ +package com.pubnub.internal.models.server.files + +import com.pubnub.api.models.consumer.files.PNUploadedFile +import com.pubnub.api.models.consumer.objects.PNPage + +data class ListFilesResult( + val count: Int, + val next: PNPage.PNNext?, + val status: Int, + val data: Collection, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/history/ServerFetchMessageItem.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/history/ServerFetchMessageItem.kt new file mode 100644 index 000000000..7cd8468d0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/history/ServerFetchMessageItem.kt @@ -0,0 +1,15 @@ +package com.pubnub.internal.models.server.history + +import com.google.gson.JsonElement +import com.google.gson.annotations.SerializedName +import com.pubnub.api.models.consumer.history.PNFetchMessageItem + +data class ServerFetchMessageItem( + val uuid: String?, + val message: JsonElement, + val meta: JsonElement?, + val timetoken: Long, + val actions: Map>>? = null, + @SerializedName("message_type") + val messageType: Int?, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/history/ServerFetchMessagesResult.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/history/ServerFetchMessagesResult.kt new file mode 100644 index 000000000..394d6081f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/history/ServerFetchMessagesResult.kt @@ -0,0 +1,8 @@ +package com.pubnub.internal.models.server.history + +import com.pubnub.api.models.consumer.PNBoundedPage + +data class ServerFetchMessagesResult( + val channels: Map>, + val page: PNBoundedPage?, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/message_actions/MessageActionsResponse.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/message_actions/MessageActionsResponse.kt new file mode 100644 index 000000000..c044de1a9 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/message_actions/MessageActionsResponse.kt @@ -0,0 +1,10 @@ +package com.pubnub.internal.models.server.message_actions + +import com.pubnub.api.models.consumer.PNBoundedPage +import com.pubnub.api.models.consumer.message_actions.PNMessageAction + +data class MessageActionsResponse( + val status: Int, + val data: List = listOf(), + val more: PNBoundedPage, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ChangeMemberInput.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ChangeMemberInput.kt new file mode 100644 index 000000000..d4534b996 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ChangeMemberInput.kt @@ -0,0 +1,6 @@ +package com.pubnub.internal.models.server.objects_api + +internal data class ChangeMemberInput( + val set: List = listOf(), + val delete: List = listOf(), +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ChangeMembershipInput.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ChangeMembershipInput.kt new file mode 100644 index 000000000..82f590c2e --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ChangeMembershipInput.kt @@ -0,0 +1,6 @@ +package com.pubnub.internal.models.server.objects_api + +internal data class ChangeMembershipInput( + val set: List = listOf(), + val delete: List = listOf(), +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ChannelMetadataInput.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ChannelMetadataInput.kt new file mode 100644 index 000000000..d601c5f39 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ChannelMetadataInput.kt @@ -0,0 +1,9 @@ +package com.pubnub.internal.models.server.objects_api + +internal data class ChannelMetadataInput( + val name: String? = null, + val description: String? = null, + val custom: Any? = null, + val type: String? = null, + val status: String? = null, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/EntityArrayEnvelope.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/EntityArrayEnvelope.kt new file mode 100644 index 000000000..76ce85777 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/EntityArrayEnvelope.kt @@ -0,0 +1,9 @@ +package com.pubnub.internal.models.server.objects_api + +data class EntityArrayEnvelope( + val status: Int = 0, + val data: Collection = listOf(), + val totalCount: Int? = null, + val next: String? = null, + val prev: String? = null, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/EntityEnvelope.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/EntityEnvelope.kt new file mode 100644 index 000000000..e5078f99f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/EntityEnvelope.kt @@ -0,0 +1,6 @@ +package com.pubnub.internal.models.server.objects_api + +data class EntityEnvelope( + val status: Int, + val data: T, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ServerMemberInput.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ServerMemberInput.kt new file mode 100644 index 000000000..16054673d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ServerMemberInput.kt @@ -0,0 +1,9 @@ +package com.pubnub.internal.models.server.objects_api + +internal data class ServerMemberInput( + val uuid: UUIDId, + val custom: Any? = null, + val status: String? = null, +) + +internal data class UUIDId(val id: String) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ServerMembershipInput.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ServerMembershipInput.kt new file mode 100644 index 000000000..f0d6914ab --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/ServerMembershipInput.kt @@ -0,0 +1,11 @@ +package com.pubnub.internal.models.server.objects_api + +internal data class ServerMembershipInput( + val channel: ChannelId, + val custom: Any? = null, + val status: String? = null, +) + +internal data class ChannelId( + val id: String, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/UUIDMetadataInput.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/UUIDMetadataInput.kt new file mode 100644 index 000000000..6e9e37ea6 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/objects_api/UUIDMetadataInput.kt @@ -0,0 +1,11 @@ +package com.pubnub.internal.models.server.objects_api + +internal data class UUIDMetadataInput( + val name: String? = null, + val externalId: String? = null, + val profileUrl: String? = null, + val email: String? = null, + val custom: Any? = null, + val type: String? = null, + val status: String? = null, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/presence/WhereNowPayload.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/presence/WhereNowPayload.kt new file mode 100644 index 000000000..c9b702b88 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/models/server/presence/WhereNowPayload.kt @@ -0,0 +1,3 @@ +package com.pubnub.internal.models.server.presence + +class WhereNowPayload internal constructor(val channels: List) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/Presence.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/Presence.kt new file mode 100644 index 000000000..19a04d5c0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/Presence.kt @@ -0,0 +1,191 @@ +package com.pubnub.internal.presence + +import com.pubnub.api.enums.PNHeartbeatNotificationOptions +import com.pubnub.internal.eventengine.EffectDispatcher +import com.pubnub.internal.eventengine.EventEngineConf +import com.pubnub.internal.managers.ListenerManager +import com.pubnub.internal.managers.PresenceEventEngineManager +import com.pubnub.internal.presence.eventengine.PresenceEventEngine +import com.pubnub.internal.presence.eventengine.data.PresenceData +import com.pubnub.internal.presence.eventengine.effect.PresenceEffectFactory +import com.pubnub.internal.presence.eventengine.effect.PresenceEffectInvocation +import com.pubnub.internal.presence.eventengine.effect.effectprovider.HeartbeatProvider +import com.pubnub.internal.presence.eventengine.effect.effectprovider.LeaveProvider +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import org.slf4j.LoggerFactory +import java.util.concurrent.ScheduledExecutorService +import kotlin.time.Duration + +internal interface Presence { + companion object { + internal fun create( + heartbeatProvider: HeartbeatProvider, + leaveProvider: LeaveProvider, + heartbeatInterval: Duration, + suppressLeaveEvents: Boolean, + heartbeatNotificationOptions: PNHeartbeatNotificationOptions, + listenerManager: ListenerManager, + eventEngineConf: EventEngineConf, + presenceData: PresenceData = PresenceData(), + sendStateWithHeartbeat: Boolean, + executorService: ScheduledExecutorService, + ): Presence { + if (heartbeatInterval <= Duration.ZERO) { + return PresenceNoOp(suppressLeaveEvents, leaveProvider) + } + + val effectFactory = + PresenceEffectFactory( + heartbeatProvider = heartbeatProvider, + leaveProvider = leaveProvider, + presenceEventSink = eventEngineConf.eventSink, + executorService = executorService, + heartbeatInterval = heartbeatInterval, + suppressLeaveEvents = suppressLeaveEvents, + heartbeatNotificationOptions = heartbeatNotificationOptions, + statusConsumer = listenerManager, + presenceData = presenceData, + sendStateWithHeartbeat = sendStateWithHeartbeat, + ) + + val eventEngineManager = + PresenceEventEngineManager( + eventEngine = + PresenceEventEngine( + effectSink = eventEngineConf.effectSink, + eventSource = eventEngineConf.eventSource, + ), + eventSink = eventEngineConf.eventSink, + effectDispatcher = + EffectDispatcher( + effectFactory = effectFactory, + effectSource = eventEngineConf.effectSource, + ), + ).also { it.start() } + + return EnabledPresence(eventEngineManager) + } + } + + fun joined( + channels: Set = emptySet(), + channelGroups: Set = emptySet(), + ) + + fun left( + channels: Set = emptySet(), + channelGroups: Set = emptySet(), + ) + + fun leftAll() + + fun presence( + channels: Set = emptySet(), + channelGroups: Set = emptySet(), + connected: Boolean = false, + ) { + if (connected) { + joined(channels, channelGroups) + } else { + left(channels, channelGroups) + } + } + + fun reconnect() + + fun disconnect() + + fun destroy() +} + +internal class PresenceNoOp( + private val suppressLeaveEvents: Boolean = false, + private val leaveProvider: LeaveProvider, +) : Presence { + private val log = LoggerFactory.getLogger(PresenceNoOp::class.java) + private val channels = mutableSetOf() + private val channelGroups = mutableSetOf() + + @Synchronized + override fun joined( + channels: Set, + channelGroups: Set, + ) { + this.channels.addAll(channels) + this.channelGroups.addAll(channelGroups) + } + + @Synchronized + override fun left( + channels: Set, + channelGroups: Set, + ) { + if (!suppressLeaveEvents && (channels.isNotEmpty() || channelGroups.isNotEmpty())) { + leaveProvider.getLeaveRemoteAction(channels, channelGroups).async { result -> + result.onFailure { + log.error("LeaveEffect failed", it) + } + } + } + this.channels.removeAll(channels) + this.channelGroups.removeAll(channelGroups) + } + + @Synchronized + override fun leftAll() { + if (!suppressLeaveEvents && (channels.isNotEmpty() || channelGroups.isNotEmpty())) { + leaveProvider.getLeaveRemoteAction(channels, channelGroups).async { result -> + result.onFailure { + log.error("LeaveEffect failed", it) + } + } + } + channels.clear() + channelGroups.clear() + } + + override fun reconnect() = noAction() + + override fun disconnect() = noAction() + + override fun destroy() = noAction() + + private fun noAction() { + // Presence Event Engine is not initialized so no action here + } +} + +internal class EnabledPresence( + private val presenceEventEngineManager: PresenceEventEngineManager, +) : Presence { + override fun joined( + channels: Set, + channelGroups: Set, + ) { + presenceEventEngineManager.addEventToQueue(PresenceEvent.Joined(channels, channelGroups)) + } + + override fun left( + channels: Set, + channelGroups: Set, + ) { + presenceEventEngineManager.addEventToQueue(PresenceEvent.Left(channels, channelGroups)) + } + + override fun leftAll() { + presenceEventEngineManager.addEventToQueue(PresenceEvent.LeftAll) + } + + override fun reconnect() { + presenceEventEngineManager.addEventToQueue(PresenceEvent.Reconnect) + } + + override fun disconnect() { + presenceEventEngineManager.addEventToQueue(PresenceEvent.Disconnect) + } + + override fun destroy() { + disconnect() + presenceEventEngineManager.stop() + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/PresenceEventEngine.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/PresenceEventEngine.kt new file mode 100644 index 000000000..e1818d11d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/PresenceEventEngine.kt @@ -0,0 +1,16 @@ +package com.pubnub.internal.presence.eventengine + +import com.pubnub.internal.eventengine.EventEngine +import com.pubnub.internal.eventengine.Sink +import com.pubnub.internal.eventengine.Source +import com.pubnub.internal.presence.eventengine.effect.PresenceEffectInvocation +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.presence.eventengine.state.PresenceState + +internal typealias PresenceEventEngine = EventEngine + +internal fun PresenceEventEngine( + effectSink: Sink, + eventSource: Source, + currentState: PresenceState = PresenceState.HeartbeatInactive, +): PresenceEventEngine = EventEngine(effectSink, eventSource, currentState) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/data/PresenceData.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/data/PresenceData.kt new file mode 100644 index 000000000..76a3e769c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/data/PresenceData.kt @@ -0,0 +1,7 @@ +package com.pubnub.internal.presence.eventengine.data + +import java.util.concurrent.ConcurrentHashMap + +internal class PresenceData { + internal val channelStates: ConcurrentHashMap = ConcurrentHashMap() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/HeartbeatEffect.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/HeartbeatEffect.kt new file mode 100644 index 000000000..01622064b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/HeartbeatEffect.kt @@ -0,0 +1,44 @@ +package com.pubnub.internal.presence.eventengine.effect + +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.api.enums.PNHeartbeatNotificationOptions +import com.pubnub.api.enums.PNStatusCategory +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.internal.eventengine.Effect +import com.pubnub.internal.eventengine.Sink +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.subscribe.eventengine.effect.StatusConsumer +import org.slf4j.LoggerFactory + +internal class HeartbeatEffect( + val heartbeatRemoteAction: RemoteAction, + val presenceEventSink: Sink, + val heartbeatNotificationOptions: PNHeartbeatNotificationOptions, + val statusConsumer: StatusConsumer, +) : Effect { + private val log = LoggerFactory.getLogger(HeartbeatEffect::class.java) + + override fun runEffect() { + log.trace("Running HeartbeatEffect") + heartbeatRemoteAction.async { result -> + result.onFailure { exception -> + if (heartbeatNotificationOptions == PNHeartbeatNotificationOptions.ALL || + heartbeatNotificationOptions == PNHeartbeatNotificationOptions.FAILURES + ) { + statusConsumer.announce(PNStatus(PNStatusCategory.PNHeartbeatFailed, PubNubException.from(exception))) + } + presenceEventSink.add( + PresenceEvent.HeartbeatFailure(PubNubException.from(exception)), + ) + }.onSuccess { + if (heartbeatNotificationOptions == PNHeartbeatNotificationOptions.ALL) { + statusConsumer.announce(PNStatus(PNStatusCategory.PNHeartbeatSuccess)) + } + presenceEventSink.add( + PresenceEvent.HeartbeatSuccess, + ) + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/LeaveEffect.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/LeaveEffect.kt new file mode 100644 index 000000000..9c738c94d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/LeaveEffect.kt @@ -0,0 +1,20 @@ +package com.pubnub.internal.presence.eventengine.effect + +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.internal.eventengine.Effect +import org.slf4j.LoggerFactory + +internal class LeaveEffect( + val leaveRemoteAction: RemoteAction, +) : Effect { + private val log = LoggerFactory.getLogger(LeaveEffect::class.java) + + override fun runEffect() { + log.trace("Running LeaveEffect") + leaveRemoteAction.async { result -> + result.onFailure { + log.error("LeaveEffect failed", it) + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/PresenceEffectFactory.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/PresenceEffectFactory.kt new file mode 100644 index 000000000..46ce65cc5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/PresenceEffectFactory.kt @@ -0,0 +1,64 @@ +package com.pubnub.internal.presence.eventengine.effect + +import com.pubnub.api.enums.PNHeartbeatNotificationOptions +import com.pubnub.internal.eventengine.Effect +import com.pubnub.internal.eventengine.EffectFactory +import com.pubnub.internal.eventengine.Sink +import com.pubnub.internal.presence.eventengine.data.PresenceData +import com.pubnub.internal.presence.eventengine.effect.effectprovider.HeartbeatProvider +import com.pubnub.internal.presence.eventengine.effect.effectprovider.LeaveProvider +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.subscribe.eventengine.effect.StatusConsumer +import java.util.concurrent.ScheduledExecutorService +import kotlin.time.Duration + +internal class PresenceEffectFactory( + private val heartbeatProvider: HeartbeatProvider, + private val leaveProvider: LeaveProvider, + private val presenceEventSink: Sink, + private val executorService: ScheduledExecutorService, + private val heartbeatInterval: Duration, + private val suppressLeaveEvents: Boolean, + private val heartbeatNotificationOptions: PNHeartbeatNotificationOptions, + private val statusConsumer: StatusConsumer, + private val presenceData: PresenceData, + private val sendStateWithHeartbeat: Boolean, +) : EffectFactory { + override fun create(effectInvocation: PresenceEffectInvocation): Effect? { + return when (effectInvocation) { + is PresenceEffectInvocation.Heartbeat -> { + val heartbeatRemoteAction = + heartbeatProvider.getHeartbeatRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + if (sendStateWithHeartbeat) { + presenceData.channelStates.filter { it.key in effectInvocation.channels } + } else { + null + }, + ) + HeartbeatEffect(heartbeatRemoteAction, presenceEventSink, heartbeatNotificationOptions, statusConsumer) + } + + is PresenceEffectInvocation.Leave -> { + if (!suppressLeaveEvents) { + val leaveRemoteAction = + leaveProvider.getLeaveRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + ) + LeaveEffect(leaveRemoteAction) + } else { + null + } + } + + is PresenceEffectInvocation.Wait -> { + WaitEffect(heartbeatInterval, presenceEventSink, executorService) + } + + PresenceEffectInvocation.CancelWait, + -> null + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/PresenceEffectInvocation.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/PresenceEffectInvocation.kt new file mode 100644 index 000000000..141e05e1b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/PresenceEffectInvocation.kt @@ -0,0 +1,27 @@ +package com.pubnub.internal.presence.eventengine.effect + +import com.pubnub.internal.eventengine.Cancel +import com.pubnub.internal.eventengine.EffectInvocation +import com.pubnub.internal.eventengine.EffectInvocationType +import com.pubnub.internal.eventengine.Managed +import com.pubnub.internal.eventengine.NonManaged + +internal sealed class PresenceEffectInvocation(override val type: EffectInvocationType) : EffectInvocation { + override val id: String = "any value for NonManged and Cancel effect" + + data class Heartbeat( + val channels: Set, + val channelGroups: Set, + ) : PresenceEffectInvocation(NonManaged) + + data class Leave( + val channels: Set, + val channelGroups: Set, + ) : PresenceEffectInvocation(NonManaged) + + class Wait : PresenceEffectInvocation(Managed) { + override val id: String = Wait::class.java.simpleName + } + + object CancelWait : PresenceEffectInvocation(Cancel(idToCancel = Wait::class.java.simpleName)) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/WaitEffect.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/WaitEffect.kt new file mode 100644 index 000000000..bb59cc33a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/WaitEffect.kt @@ -0,0 +1,46 @@ +package com.pubnub.internal.presence.eventengine.effect + +import com.pubnub.internal.eventengine.ManagedEffect +import com.pubnub.internal.eventengine.Sink +import com.pubnub.internal.extension.scheduleWithDelay +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import org.slf4j.LoggerFactory +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.ScheduledFuture +import kotlin.time.Duration + +internal class WaitEffect( + private val heartbeatInterval: Duration, + private val presenceEventSink: Sink, + private val executorService: ScheduledExecutorService, +) : ManagedEffect { + private val log = LoggerFactory.getLogger(WaitEffect::class.java) + + @Transient + private var cancelled: Boolean = false + + @Transient + private var scheduled: ScheduledFuture<*>? = null + + @Synchronized + override fun runEffect() { + log.trace("Running WaitEffect") + if (cancelled) { + return + } + + scheduled = + executorService.scheduleWithDelay(heartbeatInterval) { + presenceEventSink.add(PresenceEvent.TimesUp) + } + } + + @Synchronized + override fun cancel() { + if (cancelled) { + return + } + scheduled?.cancel(true) + cancelled = true + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/HeartbeatProvider.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/HeartbeatProvider.kt new file mode 100644 index 000000000..8dd0458ff --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/HeartbeatProvider.kt @@ -0,0 +1,11 @@ +package com.pubnub.internal.presence.eventengine.effect.effectprovider + +import com.pubnub.api.endpoints.remoteaction.RemoteAction + +internal fun interface HeartbeatProvider { + fun getHeartbeatRemoteAction( + channels: Set, + channelGroups: Set, + state: Map?, + ): RemoteAction +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/HeartbeatProviderImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/HeartbeatProviderImpl.kt new file mode 100644 index 000000000..798e1b83a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/HeartbeatProviderImpl.kt @@ -0,0 +1,20 @@ +package com.pubnub.internal.presence.eventengine.effect.effectprovider + +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.presence.HeartbeatEndpoint + +internal class HeartbeatProviderImpl(val pubNub: PubNubImpl) : HeartbeatProvider { + override fun getHeartbeatRemoteAction( + channels: Set, + channelGroups: Set, + state: Map?, + ): RemoteAction { + return HeartbeatEndpoint( + pubNub, + channels.toList(), + channelGroups.toList(), + state, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/LeaveProvider.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/LeaveProvider.kt new file mode 100644 index 000000000..0f9c8b629 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/LeaveProvider.kt @@ -0,0 +1,10 @@ +package com.pubnub.internal.presence.eventengine.effect.effectprovider + +import com.pubnub.api.endpoints.remoteaction.RemoteAction + +internal fun interface LeaveProvider { + fun getLeaveRemoteAction( + channels: Set, + channelGroups: Set, + ): RemoteAction +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/LeaveProviderImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/LeaveProviderImpl.kt new file mode 100644 index 000000000..09e0371ac --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/effect/effectprovider/LeaveProviderImpl.kt @@ -0,0 +1,17 @@ +package com.pubnub.internal.presence.eventengine.effect.effectprovider + +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.presence.LeaveEndpoint + +internal class LeaveProviderImpl(val pubNub: PubNubImpl) : LeaveProvider { + override fun getLeaveRemoteAction( + channels: Set, + channelGroups: Set, + ): RemoteAction { + return LeaveEndpoint(pubNub).apply { + this.channels = channels.toList() + this.channelGroups = channelGroups.toList() + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/event/PresenceEvent.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/event/PresenceEvent.kt new file mode 100644 index 000000000..518c47f1b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/event/PresenceEvent.kt @@ -0,0 +1,28 @@ +package com.pubnub.internal.presence.eventengine.event + +import com.pubnub.api.PubNubException +import com.pubnub.internal.eventengine.Event + +internal sealed class PresenceEvent : Event { + class Joined(channels: Set, channelGroups: Set) : PresenceEvent() { + val channels: Set = channels.toSet() + val channelGroups: Set = channelGroups.toSet() + } + + class Left(channels: Set, channelGroups: Set) : PresenceEvent() { + val channels: Set = channels.toSet() + val channelGroups: Set = channelGroups.toSet() + } + + object Reconnect : PresenceEvent() + + object Disconnect : PresenceEvent() + + object LeftAll : PresenceEvent() + + object TimesUp : PresenceEvent() // End of waiting period between heartbeats. It's time for next heartbeat. + + object HeartbeatSuccess : PresenceEvent() + + data class HeartbeatFailure(val reason: PubNubException) : PresenceEvent() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/state/PresenceState.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/state/PresenceState.kt new file mode 100644 index 000000000..cf5c7c323 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/presence/eventengine/state/PresenceState.kt @@ -0,0 +1,211 @@ +package com.pubnub.internal.presence.eventengine.state + +import com.pubnub.api.PubNubException +import com.pubnub.internal.eventengine.State +import com.pubnub.internal.eventengine.noTransition +import com.pubnub.internal.eventengine.transitionTo +import com.pubnub.internal.presence.eventengine.effect.PresenceEffectInvocation +import com.pubnub.internal.presence.eventengine.event.PresenceEvent + +internal sealed class PresenceState : State { + object HeartbeatInactive : PresenceState() { + override fun transition(event: PresenceEvent): Pair> { + return when (event) { + is PresenceEvent.Joined -> { + transitionTo(Heartbeating(event.channels, event.channelGroups)) + } + + else -> { + noTransition() + } + } + } + } + + class Heartbeating( + channels: Set, + channelGroups: Set, + ) : PresenceState() { + // toSet() is a must because we want to make sure that channels is immutable, and Handshaking constructor + // doesn't prevent from providing "channels" that is mutable set. + val channels = channels.toSet() + val channelGroups = channelGroups.toSet() + + override fun onEntry(): Set = setOf(PresenceEffectInvocation.Heartbeat(channels, channelGroups)) + + override fun transition(event: PresenceEvent): Pair> { + return when (event) { + is PresenceEvent.LeftAll -> { + transitionTo(HeartbeatInactive, PresenceEffectInvocation.Leave(channels, channelGroups)) + } + + is PresenceEvent.Disconnect -> { + transitionTo( + HeartbeatStopped(channels, channelGroups), + PresenceEffectInvocation.Leave(channels, channelGroups), + ) + } + + is PresenceEvent.Joined -> { + transitionTo(Heartbeating(channels + event.channels, channelGroups + event.channelGroups)) + } + + is PresenceEvent.Left -> { + if ((channels - event.channels).isEmpty() && (channelGroups - event.channelGroups).isEmpty()) { + transitionTo(HeartbeatInactive, PresenceEffectInvocation.Leave(channels, channelGroups)) + } else { + transitionTo( + Heartbeating(channels - event.channels, channelGroups - event.channelGroups), + PresenceEffectInvocation.Leave(event.channels, event.channelGroups), + ) + } + } + + is PresenceEvent.HeartbeatSuccess -> { + transitionTo(HeartbeatCooldown(channels, channelGroups)) + } + + is PresenceEvent.HeartbeatFailure -> { + transitionTo(HeartbeatFailed(channels, channelGroups, event.reason)) + } + + else -> { + noTransition() + } + } + } + } + + class HeartbeatStopped( + channels: Set, + channelGroups: Set, + ) : PresenceState() { + val channels = channels.toSet() + val channelGroups = channelGroups.toSet() + + override fun transition(event: PresenceEvent): Pair> { + return when (event) { + is PresenceEvent.LeftAll -> { + transitionTo(HeartbeatInactive) + } + + is PresenceEvent.Joined -> { + transitionTo(HeartbeatStopped(channels + event.channels, channelGroups + event.channelGroups)) + } + + is PresenceEvent.Left -> { + if ((channels - event.channels).isEmpty() && (channelGroups - event.channelGroups).isEmpty()) { + transitionTo(HeartbeatInactive) + } else { + transitionTo(HeartbeatStopped(channels - event.channels, channelGroups - event.channelGroups)) + } + } + + is PresenceEvent.Reconnect -> { + transitionTo(Heartbeating(channels, channelGroups)) + } + + else -> { + noTransition() + } + } + } + } + + class HeartbeatFailed( + channels: Set, + channelGroups: Set, + val reason: PubNubException?, + ) : PresenceState() { + val channels = channels.toSet() + val channelGroups = channelGroups.toSet() + + override fun transition(event: PresenceEvent): Pair> { + return when (event) { + is PresenceEvent.LeftAll -> { + transitionTo(HeartbeatInactive, PresenceEffectInvocation.Leave(channels, channelGroups)) + } + + is PresenceEvent.Joined -> { + transitionTo(Heartbeating(channels + event.channels, channelGroups + event.channelGroups)) + } + + is PresenceEvent.Left -> { + if ((channels - event.channels).isEmpty() && (channelGroups - event.channelGroups).isEmpty()) { + transitionTo(HeartbeatInactive, PresenceEffectInvocation.Leave(channels, channelGroups)) + } else { + transitionTo( + Heartbeating(channels - event.channels, channelGroups - event.channelGroups), + PresenceEffectInvocation.Leave(event.channels, event.channelGroups), + ) + } + } + + is PresenceEvent.Reconnect -> { + transitionTo(Heartbeating(channels, channelGroups)) + } + + is PresenceEvent.Disconnect -> { + transitionTo( + HeartbeatStopped(channels, channelGroups), + PresenceEffectInvocation.Leave(channels, channelGroups), + ) + } + + else -> { + noTransition() + } + } + } + } + + class HeartbeatCooldown( + channels: Set, + channelGroups: Set, + ) : PresenceState() { + val channels = channels.toSet() + val channelGroups = channelGroups.toSet() + + override fun onEntry(): Set = setOf(PresenceEffectInvocation.Wait()) + + override fun onExit(): Set = setOf(PresenceEffectInvocation.CancelWait) + + override fun transition(event: PresenceEvent): Pair> { + return when (event) { + is PresenceEvent.Joined -> { + transitionTo(Heartbeating(channels + event.channels, channelGroups + event.channelGroups)) + } + + is PresenceEvent.Left -> { + if ((channels - event.channels).isEmpty() && (channelGroups - event.channelGroups).isEmpty()) { + transitionTo(HeartbeatInactive, PresenceEffectInvocation.Leave(channels, channelGroups)) + } else { + transitionTo( + Heartbeating(channels - event.channels, channelGroups - event.channelGroups), + PresenceEffectInvocation.Leave(event.channels, event.channelGroups), + ) + } + } + + is PresenceEvent.TimesUp -> { + transitionTo(Heartbeating(channels, channelGroups)) + } + + is PresenceEvent.Disconnect -> { + transitionTo( + HeartbeatStopped(channels, channelGroups), + PresenceEffectInvocation.Leave(channels, channelGroups), + ) + } + + is PresenceEvent.LeftAll -> { + transitionTo(HeartbeatInactive, PresenceEffectInvocation.Leave(channels, channelGroups)) + } + + else -> { + noTransition() + } + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/retry/RetryableBase.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/retry/RetryableBase.kt new file mode 100644 index 000000000..ec64ca3c6 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/retry/RetryableBase.kt @@ -0,0 +1,142 @@ +package com.pubnub.internal.retry + +import com.pubnub.api.retry.RetryConfiguration +import com.pubnub.api.retry.RetryableEndpointGroup +import retrofit2.Response +import java.io.IOException +import java.net.ConnectException +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import javax.net.ssl.SSLHandshakeException +import kotlin.math.pow +import kotlin.random.Random +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds + +internal abstract class RetryableBase( + private val retryConfiguration: RetryConfiguration, + private val endpointGroupName: RetryableEndpointGroup, +) { + companion object { + const val MAX_RANDOM_DELAY_IN_MILLIS = 1000 // random delay should be between 0 and 1000 milliseconds + private const val TOO_MANY_REQUESTS = 429 + const val SERVICE_UNAVAILABLE = 503 + internal const val RETRY_AFTER_HEADER_NAME = "Retry-After" + private val retryableStatusCodes = + mapOf( + 429 to "TOO_MANY_REQUESTS", + 500 to "HTTP_INTERNAL_ERROR", + 502 to "HTTP_BAD_GATEWAY", + 503 to "HTTP_UNAVAILABLE", + 504 to "HTTP_GATEWAY_TIMEOUT", + 507 to "INSUFFICIENT_STORAGE", + 508 to "LOOP_DETECTED", + 510 to "NOT_EXTENDED", + 511 to "NETWORK_AUTHENTICATION_REQUIRED", + ) + internal val retryableExceptions = + listOf( + UnknownHostException::class.java, + SocketTimeoutException::class.java, + ConnectException::class.java, + SSLHandshakeException::class.java, + IOException::class.java, + ) + } + + private var exponentialMultiplier = 0.0 + protected val random = Random.Default + + internal val isRetryConfSetForThisRestCall = + when (retryConfiguration) { + is RetryConfiguration.None -> false + + is RetryConfiguration.Linear -> { + val excludedOperations = retryConfiguration.excludedOperations + endpointIsNotExcludedFromRetryConfiguration(excludedOperations) + } + + is RetryConfiguration.Exponential -> { + val excludedOperations = retryConfiguration.excludedOperations + endpointIsNotExcludedFromRetryConfiguration(excludedOperations) + } + } + + internal fun getDelayBasedOnResponse(response: Response): Duration { + val effectiveDelay: Duration = + if (response.raw().code == TOO_MANY_REQUESTS) { + calculateDelayForTooManyRequestError(response) + } else { + getDelayFromRetryConfiguration() + } + return effectiveDelay + } + + private fun getDelayBasedOnErrorCode( + errorCode: Int, + retryAfterHeaderValueInSec: Int, + ): Duration { + return if (errorCode == TOO_MANY_REQUESTS) { + getDelayForRetryAfterHeaderValue(retryAfterHeaderValueInSec) + } else { + getDelayFromRetryConfiguration() + } + } + + internal fun getDelayFromRetryConfiguration(): Duration { + return when (retryConfiguration) { + is RetryConfiguration.None -> 0.seconds + is RetryConfiguration.Linear -> retryConfiguration.delayInSec + is RetryConfiguration.Exponential -> { + val delay: Duration = retryConfiguration.minDelayInSec * 2.0.pow(exponentialMultiplier) + exponentialMultiplier++ + minOf(delay, retryConfiguration.maxDelayInSec) + } + } + } + + protected val maxRetryNumberFromConfiguration: Int = + when (retryConfiguration) { + is RetryConfiguration.None -> 0 + is RetryConfiguration.Linear -> retryConfiguration.maxRetryNumber + is RetryConfiguration.Exponential -> retryConfiguration.maxRetryNumber + } + + protected fun isErrorCodeRetryable(errorCode: Int) = retryableStatusCodes.containsKey(errorCode) + + protected fun getEffectiveDelay( + statusCode: Int, + retryAfterHeaderValue: Int, + ): Duration { + val delayBasedOnStatusCode = + getDelayBasedOnErrorCode( + errorCode = statusCode, + retryAfterHeaderValueInSec = retryAfterHeaderValue, + ) + val randomDelayInMillis = random.nextInt(MAX_RANDOM_DELAY_IN_MILLIS).milliseconds + return delayBasedOnStatusCode + randomDelayInMillis + } + + private fun calculateDelayForTooManyRequestError(response: Response): Duration { + val retryAfterInSec: String? = response.headers()[RETRY_AFTER_HEADER_NAME] + val delayInSeconds = retryAfterInSec?.toIntOrNull() + + return getDelayForRetryAfterHeaderValue(delayInSeconds) + } + + private fun getDelayForRetryAfterHeaderValue(delayInSeconds: Int?): Duration { + return when { + delayInSeconds != null && delayInSeconds > 0 -> { + delayInSeconds.seconds + } + + else -> { + getDelayFromRetryConfiguration() + } + } + } + + private fun endpointIsNotExcludedFromRetryConfiguration(excludedOperations: List): Boolean = + excludedOperations.contains(endpointGroupName).not() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/retry/RetryableCallback.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/retry/RetryableCallback.kt new file mode 100644 index 000000000..34c57120c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/retry/RetryableCallback.kt @@ -0,0 +1,103 @@ +package com.pubnub.internal.retry + +import com.pubnub.api.retry.RetryConfiguration +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.extension.scheduleWithDelay +import org.slf4j.LoggerFactory +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import java.util.concurrent.ScheduledExecutorService +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds + +internal abstract class RetryableCallback( + retryConfiguration: RetryConfiguration, + endpointGroupName: RetryableEndpointGroup, + private val call: Call, + private val isEndpointRetryable: Boolean, + private val executorService: ScheduledExecutorService, +) : Callback, RetryableBase(retryConfiguration, endpointGroupName) { + private val log = LoggerFactory.getLogger(this.javaClass.simpleName) + private var retryCount = 0 + private var exponentialMultiplier = 0.0 + + override fun onResponse( + call: Call, + response: Response, + ) { + if (shouldRetryOnResponse(response)) { + retryOnResponseWithError(response) + } else { + onFinalResponse(call, response) + } + } + + override fun onFailure( + call: Call, + t: Throwable, + ) { + if (shouldRetryOnFailure(t)) { + retryOnFailure() + } else { + onFinalFailure(call, t) + } + } + + private fun shouldRetryOnResponse(response: Response): Boolean { + return !response.isSuccessful && + retryCount < maxRetryNumberFromConfiguration && + isErrorCodeRetryable(response.raw().code) && + isRetryConfSetForThisRestCall && + isEndpointRetryable + } + + private fun shouldRetryOnFailure(t: Throwable): Boolean { + val exception = Exception(t) + return retryCount < maxRetryNumberFromConfiguration && + isExceptionRetryable(exception) && + isRetryConfSetForThisRestCall && + isEndpointRetryable + } + + private fun isExceptionRetryable(e: Exception): Boolean { + return e.cause?.let { cause -> + retryableExceptions.any { it.isInstance(cause) } + } ?: false + } + + private fun retryOnFailure() { + val effectiveDelay = getDelayFromRetryConfiguration() + retry(effectiveDelay) + } + + private fun retryOnResponseWithError(response: Response) { + val effectiveDelay: Duration = getDelayForRetryOnResponse(response) + retry(effectiveDelay) + } + + private fun retry(delay: Duration) { + retryCount++ + val randomDelayInMillis: Int = random.nextInt(MAX_RANDOM_DELAY_IN_MILLIS) + val effectiveDelay: Duration = delay + randomDelayInMillis.milliseconds + log.trace("Added random delay so effective retry delay is ${effectiveDelay.inWholeMilliseconds} millis") + // don't want to block the main thread in case of Android so using executorService + executorService.scheduleWithDelay(effectiveDelay) { + call.clone().enqueue(this) + } + } + + private fun getDelayForRetryOnResponse(response: Response): Duration { + return getDelayBasedOnResponse(response) + } + + abstract fun onFinalResponse( + call: Call, + response: Response, + ) + + abstract fun onFinalFailure( + call: Call, + t: Throwable, + ) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/retry/RetryableRestCaller.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/retry/RetryableRestCaller.kt new file mode 100644 index 000000000..edb6046dc --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/retry/RetryableRestCaller.kt @@ -0,0 +1,85 @@ +package com.pubnub.internal.retry + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.retry.RetryConfiguration +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.PubNubRetryableException +import okhttp3.ResponseBody.Companion.toResponseBody +import org.slf4j.LoggerFactory +import retrofit2.Call +import retrofit2.Response + +internal class RetryableRestCaller( + retryConfiguration: RetryConfiguration, + endpointGroupName: RetryableEndpointGroup, + private val isEndpointRetryable: Boolean, +) : RetryableBase(retryConfiguration, endpointGroupName) { + private val log = LoggerFactory.getLogger(this.javaClass.simpleName) + internal lateinit var call: Call + + internal fun execute(callToBeExecuted: Call): Response { + call = callToBeExecuted + var numberOfAttempts = 0 + while (true) { + val (response, exception) = executeRestCall() + if (!shouldRetry(response) || numberOfAttempts++ >= maxRetryNumberFromConfiguration) { + // http issue can be represented by error code inside unsuccessful response or by exception + // if it is represented by error code then we want to pass response for further processing + if (response.isSuccessful || exception == null) { + return response + } else { + throw exception + } + } + val randomDelayInMillis = random.nextInt(MAX_RANDOM_DELAY_IN_MILLIS) + val effectiveDelayInMillis = getDelayBasedOnResponse(response).inWholeMilliseconds + randomDelayInMillis + log.trace("Added random delay so effective retry delay is $effectiveDelayInMillis") + Thread.sleep(effectiveDelayInMillis) // we want to sleep here on current thread since this is synchronous call + + call = call.clone() + } + } + + private fun executeRestCall(): Pair, Exception?> { + var pubNubException: Exception? = null + try { + return try { + val response = call.execute() + Pair(response, pubNubException) + } catch (e: Exception) { + pubNubException = + PubNubException( + pubnubError = PubNubError.PARSING_ERROR, + errorMessage = e.toString(), + affectedCall = call, + ) + + if (isExceptionRetryable(e)) { + throw PubNubRetryableException( + pubnubError = PubNubError.CONNECT_EXCEPTION, + errorMessage = e.toString(), + statusCode = SERVICE_UNAVAILABLE, // all retryable exceptions internally are mapped to 503 error + ) + } else { + throw pubNubException + } + } + } catch (e: PubNubRetryableException) { + return Pair( + Response.error(e.statusCode, e.errorMessage?.toResponseBody() ?: "".toResponseBody()), + pubNubException, + ) + } + } + + private fun isExceptionRetryable(e: Exception): Boolean { + return retryableExceptions.any { it.isInstance(e) } + } + + private fun shouldRetry(response: Response) = + !response.isSuccessful && + isErrorCodeRetryable(response.raw().code) && + isRetryConfSetForThisRestCall && + isEndpointRetryable +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/AccessManagerService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/AccessManagerService.kt new file mode 100644 index 000000000..a9bced4f0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/AccessManagerService.kt @@ -0,0 +1,35 @@ +package com.pubnub.internal.services + +import com.pubnub.internal.models.server.Envelope +import com.pubnub.internal.models.server.access_manager.AccessManagerGrantPayload +import com.pubnub.internal.models.server.access_manager.v3.GrantTokenResponse +import com.pubnub.internal.models.server.access_manager.v3.RevokeTokenResponse +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Path +import retrofit2.http.QueryMap + +internal interface AccessManagerService { + @GET("/v2/auth/grant/sub-key/{subKey}") + fun grant( + @Path("subKey") subKey: String, + @QueryMap options: Map, + ): Call> + + @POST("/v3/pam/{subKey}/grant") + fun grantToken( + @Path("subKey") subKey: String?, + @Body body: Any?, + @QueryMap options: Map, + ): Call + + @DELETE("/v3/pam/{subKey}/grant/{token}") + fun revokeToken( + @Path("subKey") subKey: String, + @Path("token", encoded = true) token: String, + @QueryMap queryParams: Map, + ): Call +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/ChannelGroupService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/ChannelGroupService.kt new file mode 100644 index 000000000..307be7b52 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/ChannelGroupService.kt @@ -0,0 +1,43 @@ +package com.pubnub.internal.services + +import com.pubnub.internal.models.server.Envelope +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.QueryMap + +internal interface ChannelGroupService { + @GET("v1/channel-registration/sub-key/{subKey}/channel-group") + fun listAllChannelGroup( + @Path("subKey") subKey: String, + @QueryMap options: Map, + ): Call>> + + @GET("v1/channel-registration/sub-key/{subKey}/channel-group/{group}") + fun allChannelsChannelGroup( + @Path("subKey") subKey: String, + @Path("group") group: String, + @QueryMap options: Map, + ): Call>> + + @GET("v1/channel-registration/sub-key/{subKey}/channel-group/{group}") + fun addChannelChannelGroup( + @Path("subKey") subKey: String, + @Path("group") group: String, + @QueryMap options: Map, + ): Call + + @GET("v1/channel-registration/sub-key/{subKey}/channel-group/{group}") + fun removeChannel( + @Path("subKey") subKey: String, + @Path("group") group: String, + @QueryMap options: Map, + ): Call + + @GET("v1/channel-registration/sub-key/{subKey}/channel-group/{group}/remove") + fun deleteChannelGroup( + @Path("subKey") subKey: String, + @Path("group") group: String, + @QueryMap options: Map, + ): Call +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/FilesService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/FilesService.kt new file mode 100644 index 000000000..2e2ca63fb --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/FilesService.kt @@ -0,0 +1,61 @@ +package com.pubnub.internal.services + +import com.pubnub.internal.models.server.files.GenerateUploadUrlPayload +import com.pubnub.internal.models.server.files.GeneratedUploadUrlResponse +import com.pubnub.internal.models.server.files.ListFilesResult +import okhttp3.ResponseBody +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Path +import retrofit2.http.QueryMap + +interface FilesService { + @POST("/v1/files/{subKey}/channels/{channel}/generate-upload-url") + fun generateUploadUrl( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @Body body: GenerateUploadUrlPayload, + @QueryMap options: Map, + ): Call + + @GET("/v1/files/publish-file/{pubKey}/{subKey}/0/{channel}/0/{message}") + fun notifyAboutFileUpload( + @Path("pubKey") pubKey: String, + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @Path(value = "message") message: String, + @QueryMap options: Map, + ): Call> + + @GET("/v1/files/{subKey}/channels/{channel}/files") + fun listFiles( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @QueryMap options: Map, + ): Call + + @GET(GET_FILE_URL) + fun downloadFile( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @Path("fileId") fileId: String, + @Path("fileName") fileName: String, + @QueryMap options: Map, + ): Call + + @DELETE("/v1/files/{subKey}/channels/{channel}/files/{fileId}/{fileName}") + fun deleteFile( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @Path("fileId") fileId: String, + @Path("fileName") fileName: String, + @QueryMap options: Map, + ): Call + + companion object { + const val GET_FILE_URL = "/v1/files/{subKey}/channels/{channel}/files/{fileId}/{fileName}" + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/HistoryService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/HistoryService.kt new file mode 100644 index 000000000..ac4c543ae --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/HistoryService.kt @@ -0,0 +1,46 @@ +package com.pubnub.internal.services + +import com.google.gson.JsonElement +import com.pubnub.internal.models.server.FetchMessagesEnvelope +import retrofit2.Call +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.QueryMap + +internal interface HistoryService { + @GET("v2/history/sub-key/{subKey}/channel/{channel}") + fun fetchHistory( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @QueryMap options: Map, + ): Call + + @DELETE("v3/history/sub-key/{subKey}/channel/{channels}") + fun deleteMessages( + @Path("subKey") subKey: String, + @Path("channels") channels: String, + @QueryMap options: Map, + ): Call + + @GET("v3/history/sub-key/{subKey}/channel/{channels}") + fun fetchMessages( + @Path("subKey") subKey: String, + @Path("channels") channels: String, + @QueryMap options: Map, + ): Call + + @GET("v3/history-with-actions/sub-key/{subKey}/channel/{channel}") + fun fetchMessagesWithActions( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @QueryMap options: Map, + ): Call + + @GET("v3/history/sub-key/{subKey}/message-counts/{channels}") + fun fetchCount( + @Path("subKey") subKey: String, + @Path("channels") channels: String, + @QueryMap options: Map, + ): Call +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/MessageActionService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/MessageActionService.kt new file mode 100644 index 000000000..5b8b0fc9b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/MessageActionService.kt @@ -0,0 +1,42 @@ +package com.pubnub.internal.services + +import com.pubnub.api.models.consumer.message_actions.PNGetMessageActionsResult +import com.pubnub.api.models.consumer.message_actions.PNMessageAction +import com.pubnub.internal.models.server.objects_api.EntityEnvelope +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.Headers +import retrofit2.http.POST +import retrofit2.http.Path +import retrofit2.http.QueryMap + +internal interface +MessageActionService { + @POST("v1/message-actions/{subKey}/channel/{channel}/message/{messageTimetoken}") + @Headers("Content-Type: application/json; charset=UTF-8") + fun addMessageAction( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @Path("messageTimetoken") messageTimetoken: String, + @Body body: Any, + @QueryMap options: Map, + ): Call> + + @GET("v1/message-actions/{subKey}/channel/{channel}") + fun getMessageActions( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @QueryMap options: Map, + ): Call + + @DELETE("v1/message-actions/{subKey}/channel/{channel}/message/{messageTimetoken}/action/{actionTimetoken}") + fun deleteMessageAction( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @Path("messageTimetoken") messageTimetoken: String, + @Path("actionTimetoken") actionTimetoken: String, + @QueryMap options: Map, + ): Call +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/ObjectsService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/ObjectsService.kt new file mode 100644 index 000000000..b9d63b4fd --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/ObjectsService.kt @@ -0,0 +1,110 @@ +package com.pubnub.internal.services + +import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata +import com.pubnub.api.models.consumer.objects.member.PNMember +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembership +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata +import com.pubnub.internal.models.server.objects_api.ChangeMemberInput +import com.pubnub.internal.models.server.objects_api.ChangeMembershipInput +import com.pubnub.internal.models.server.objects_api.ChannelMetadataInput +import com.pubnub.internal.models.server.objects_api.EntityArrayEnvelope +import com.pubnub.internal.models.server.objects_api.EntityEnvelope +import com.pubnub.internal.models.server.objects_api.UUIDMetadataInput +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.Headers +import retrofit2.http.PATCH +import retrofit2.http.Path +import retrofit2.http.QueryMap + +internal interface ObjectsService { + @GET("v2/objects/{subKey}/uuids") + fun getAllUUIDMetadata( + @Path("subKey") subKey: String, + @QueryMap(encoded = true) options: Map = mapOf(), + ): Call> + + @GET("v2/objects/{subKey}/uuids/{uuid}") + fun getUUIDMetadata( + @Path("subKey") subKey: String, + @Path("uuid") uuid: String, + @QueryMap(encoded = true) options: Map = mapOf(), + ): Call> + + @PATCH("v2/objects/{subKey}/uuids/{uuid}") + @Headers("Content-Type: application/json; charset=UTF-8") + fun setUUIDMetadata( + @Path("subKey") subKey: String, + @Path("uuid") uuid: String, + @Body body: UUIDMetadataInput, + @QueryMap(encoded = true) options: Map = mapOf(), + ): Call> + + @DELETE("v2/objects/{subKey}/uuids/{uuid}") + fun deleteUUIDMetadata( + @Path("subKey") subKey: String, + @Path("uuid") uuid: String, + ): Call> + + @GET("v2/objects/{subKey}/channels") + fun getAllChannelMetadata( + @Path("subKey") subKey: String, + @QueryMap(encoded = true) options: Map = mapOf(), + ): Call> + + @GET("v2/objects/{subKey}/channels/{channel}") + fun getChannelMetadata( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @QueryMap(encoded = true) options: Map = mapOf(), + ): Call> + + @PATCH("v2/objects/{subKey}/channels/{channel}") + @Headers("Content-Type: application/json; charset=UTF-8") + fun setChannelMetadata( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @Body body: ChannelMetadataInput, + @QueryMap(encoded = true) options: Map = mapOf(), + ): Call> + + @DELETE("v2/objects/{subKey}/channels/{channel}") + fun deleteChannelMetadata( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + ): Call> + + @GET("/v2/objects/{subKey}/uuids/{uuid}/channels") + fun getMemberships( + @Path("subKey") subKey: String, + @Path("uuid") uuid: String, + @QueryMap(encoded = true) options: Map = mapOf(), + ): Call> + + @PATCH("/v2/objects/{subKey}/uuids/{uuid}/channels") + @Headers("Content-Type: application/json; charset=UTF-8") + fun patchMemberships( + @Path("subKey") subKey: String, + @Path("uuid") uuid: String, + @Body body: ChangeMembershipInput, + @QueryMap(encoded = true) options: Map = mapOf(), + ): Call> + + @GET("/v2/objects/{subKey}/channels/{channel}/uuids") + fun getChannelMembers( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @QueryMap(encoded = true) options: Map = mapOf(), + ): Call> + + @PATCH("/v2/objects/{subKey}/channels/{channel}/uuids") + @Headers("Content-Type: application/json; charset=UTF-8") + fun patchChannelMembers( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @Body body: ChangeMemberInput, + @QueryMap(encoded = true) options: Map = mapOf(), + ): Call> +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/PresenceService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/PresenceService.kt new file mode 100644 index 000000000..26f3b7911 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/PresenceService.kt @@ -0,0 +1,61 @@ +package com.pubnub.internal.services + +import com.google.gson.JsonElement +import com.pubnub.internal.models.server.Envelope +import com.pubnub.internal.models.server.presence.WhereNowPayload +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.QueryMap + +internal interface PresenceService { + @GET("v2/presence/sub-key/{subKey}/channel/{channel}/leave") + fun leave( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @QueryMap options: Map, + ): Call + + @GET("v2/presence/sub-key/{subKey}/channel/{channel}/heartbeat") + fun heartbeat( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @QueryMap options: Map, + ): Call + + @GET("v2/presence/sub-key/{subKey}/uuid/{uuid}") + fun whereNow( + @Path("subKey") subKey: String, + @Path("uuid") uuid: String, + @QueryMap options: Map, + ): Call> + + @GET("v2/presence/sub_key/{subKey}/channel/{channel}") + fun hereNow( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @QueryMap options: Map, + ): Call> + + @GET("v2/presence/sub_key/{subKey}") + fun globalHereNow( + @Path("subKey") subKey: String, + @QueryMap options: Map, + ): Call> + + @GET("v2/presence/sub-key/{subKey}/channel/{channel}/uuid/{uuid}") + fun getState( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @Path("uuid") uuid: String, + @QueryMap options: Map, + ): Call> + + @GET("v2/presence/sub-key/{subKey}/channel/{channel}/uuid/{uuid}/data") + fun setState( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @Path("uuid") uuid: String, + @QueryMap options: Map, + ): Call> +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/PublishService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/PublishService.kt new file mode 100644 index 000000000..af4549112 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/PublishService.kt @@ -0,0 +1,30 @@ +package com.pubnub.internal.services + +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.Headers +import retrofit2.http.POST +import retrofit2.http.Path +import retrofit2.http.QueryMap + +internal interface PublishService { + @GET("publish/{pubKey}/{subKey}/0/{channel}/0/{message}") + fun publish( + @Path("pubKey") pubKey: String, + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @Path(value = "message", encoded = false) message: String, + @QueryMap(encoded = false) options: Map, + ): Call> + + @POST("publish/{pubKey}/{subKey}/0/{channel}/0") + @Headers("Content-Type: application/json; charset=UTF-8") + fun publishWithPost( + @Path("pubKey") pubKey: String, + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @Body body: Any, + @QueryMap(encoded = false) options: Map, + ): Call> +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/PushService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/PushService.kt new file mode 100644 index 000000000..c0d2e3cff --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/PushService.kt @@ -0,0 +1,53 @@ +package com.pubnub.internal.services + +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.QueryMap + +internal interface PushService { + @GET("v1/push/sub-key/{subKey}/devices/{pushToken}") + fun modifyChannelsForDevice( + @Path("subKey") subKey: String, + @Path("pushToken") pushToken: String, + @QueryMap options: Map, + ): Call + + @GET("v1/push/sub-key/{subKey}/devices/{pushToken}/remove") + fun removeAllChannelsForDevice( + @Path("subKey") subKey: String, + @Path("pushToken") pushToken: String, + @QueryMap options: Map, + ): Call + + @GET("v1/push/sub-key/{subKey}/devices/{pushToken}") + fun listChannelsForDevice( + @Path("subKey") subKey: String, + @Path("pushToken") pushToken: String, + @QueryMap options: Map, + ): Call> + + // V2 (APNS2) + + // V2 (APNS2) + @GET("v2/push/sub-key/{subKey}/devices-apns2/{deviceApns2}") + fun modifyChannelsForDeviceApns2( + @Path("subKey") subKey: String, + @Path("deviceApns2") deviceApns2: String, + @QueryMap options: Map, + ): Call + + @GET("v2/push/sub-key/{subKey}/devices-apns2/{deviceApns2}") + fun listChannelsForDeviceApns2( + @Path("subKey") subKey: String, + @Path("deviceApns2") deviceApns2: String, + @QueryMap options: Map, + ): Call> + + @GET("v2/push/sub-key/{subKey}/devices-apns2/{deviceApns2}/remove") + fun removeAllChannelsForDeviceApns2( + @Path("subKey") subKey: String, + @Path("deviceApns2") deviceApns2: String, + @QueryMap options: Map, + ): Call +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/S3Service.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/S3Service.kt new file mode 100644 index 000000000..f19033700 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/S3Service.kt @@ -0,0 +1,15 @@ +package com.pubnub.internal.services + +import okhttp3.MultipartBody +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.POST +import retrofit2.http.Url + +interface S3Service { + @POST + fun upload( + @Url url: String, + @Body form: MultipartBody, + ): Call +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/SignalService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/SignalService.kt new file mode 100644 index 000000000..1913cf0d7 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/SignalService.kt @@ -0,0 +1,17 @@ +package com.pubnub.internal.services + +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.QueryMap + +internal interface SignalService { + @GET("/signal/{pubKey}/{subKey}/0/{channel}/0/{payload}") + fun signal( + @Path("pubKey") pubKey: String, + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @Path(value = "payload") message: String, + @QueryMap options: Map, + ): Call> +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/SubscribeService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/SubscribeService.kt new file mode 100644 index 000000000..5f875e4e6 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/SubscribeService.kt @@ -0,0 +1,16 @@ +package com.pubnub.internal.services + +import com.pubnub.internal.models.server.SubscribeEnvelope +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.QueryMap + +internal interface SubscribeService { + @GET("v2/subscribe/{subKey}/{channel}/0") + fun subscribe( + @Path("subKey") subKey: String, + @Path("channel") channel: String, + @QueryMap options: Map, + ): Call +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/TimeService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/TimeService.kt new file mode 100644 index 000000000..bbcf2ccf1 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/services/TimeService.kt @@ -0,0 +1,12 @@ +package com.pubnub.internal.services + +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.QueryMap + +internal interface TimeService { + @GET("/time/0") + fun fetchTime( + @QueryMap options: Map, + ): Call> +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/Subscribe.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/Subscribe.kt new file mode 100644 index 000000000..cd7b5c8d0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/Subscribe.kt @@ -0,0 +1,247 @@ +package com.pubnub.internal.subscribe + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.eventengine.EffectDispatcher +import com.pubnub.internal.managers.ListenerManager +import com.pubnub.internal.managers.SubscribeEventEngineManager +import com.pubnub.internal.presence.eventengine.data.PresenceData +import com.pubnub.internal.subscribe.eventengine.SubscribeEventEngine +import com.pubnub.internal.subscribe.eventengine.configuration.EventEnginesConf +import com.pubnub.internal.subscribe.eventengine.data.SubscriptionData +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectFactory +import com.pubnub.internal.subscribe.eventengine.effect.effectprovider.HandshakeProviderImpl +import com.pubnub.internal.subscribe.eventengine.effect.effectprovider.ReceiveMessagesProviderImpl +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent.SubscriptionChanged +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent.SubscriptionRestored +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import com.pubnub.internal.workers.SubscribeMessageProcessor + +internal const val PRESENCE_CHANNEL_SUFFIX = "-pnpres" + +internal class Subscribe( + private val subscribeEventEngineManager: SubscribeEventEngineManager, + private val presenceData: PresenceData, + private val subscriptionData: SubscriptionData = SubscriptionData(), +) { + companion object { + internal fun create( + pubNub: PubNubImpl, + listenerManager: ListenerManager, + eventEnginesConf: EventEnginesConf, + messageProcessor: SubscribeMessageProcessor, + presenceData: PresenceData, + sendStateWithSubscribe: Boolean, + ): Subscribe { + val subscribeEventEngineManager = + createAndStartSubscribeEventEngineManager( + pubNub, + messageProcessor, + eventEnginesConf, + listenerManager, + presenceData, + sendStateWithSubscribe, + ) + + return Subscribe(subscribeEventEngineManager, presenceData) + } + + private fun createAndStartSubscribeEventEngineManager( + pubNub: PubNubImpl, + messageProcessor: SubscribeMessageProcessor, + eventEnginesConf: EventEnginesConf, + listenerManager: ListenerManager, + presenceData: PresenceData, + sendStateWithSubscribe: Boolean, + ): SubscribeEventEngineManager { + val subscribeEffectFactory = + SubscribeEffectFactory( + handshakeProvider = HandshakeProviderImpl(pubNub), + receiveMessagesProvider = ReceiveMessagesProviderImpl(pubNub, messageProcessor), + subscribeEventSink = eventEnginesConf.subscribe.eventSink, + messagesConsumer = listenerManager, + statusConsumer = listenerManager, + presenceData = presenceData, + sendStateWithSubscribe = sendStateWithSubscribe, + ) + + val subscribeEventEngine = + SubscribeEventEngine( + effectSink = eventEnginesConf.subscribe.effectSink, + eventSource = eventEnginesConf.subscribe.eventSource, + ) + val subscribeEffectDispatcher = + EffectDispatcher( + effectFactory = subscribeEffectFactory, + effectSource = eventEnginesConf.subscribe.effectSource, + ) + + val subscribeEventEngineManager = + SubscribeEventEngineManager( + eventEngine = subscribeEventEngine, + effectDispatcher = subscribeEffectDispatcher, + eventSink = eventEnginesConf.subscribe.eventSink, + ).apply { + start() + } + return subscribeEventEngineManager + } + } + + @Synchronized + fun subscribe( + channels: Set, + channelGroups: Set, + withPresence: Boolean, + withTimetoken: Long = 0L, + ) { + throwExceptionIfChannelAndChannelGroupIsMissing(channels, channelGroups) + addChannelsToSubscriptionData(channels, withPresence) + addChannelGroupsToSubscriptionData(channelGroups, withPresence) + val channelsInLocalStorage = subscriptionData.channels + val channelGroupsInLocalStorage = subscriptionData.channelGroups + if (withTimetoken != 0L) { + val subscriptionRestoredEvent = + SubscriptionRestored( + channelsInLocalStorage, + channelGroupsInLocalStorage, + SubscriptionCursor( + withTimetoken, + region = null, + ), // we don't know region here. Subscribe response will return region. + ) + subscribeEventEngineManager.addEventToQueue(subscriptionRestoredEvent) + } else { + subscribeEventEngineManager.addEventToQueue( + SubscriptionChanged( + channelsInLocalStorage, + channelGroupsInLocalStorage, + ), + ) + } + } + + @Synchronized + fun unsubscribe( + channels: Set = emptySet(), + channelGroups: Set = emptySet(), + ) { + throwExceptionIfChannelAndChannelGroupIsMissing(channels, channelGroups) + removeChannelsFromSubscriptionData(channels) + removeChannelGroupsFromSubscriptionData(channelGroups) + + presenceData.channelStates.keys.removeAll(channels) + + if (subscriptionData.channels.size > 0 || subscriptionData.channelGroups.size > 0) { + val channelsInLocalStorage = subscriptionData.channels + val channelGroupsInLocalStorage = subscriptionData.channelGroups + subscribeEventEngineManager.addEventToQueue( + SubscriptionChanged( + channelsInLocalStorage, + channelGroupsInLocalStorage, + ), + ) + } else { + subscribeEventEngineManager.addEventToQueue(SubscribeEvent.UnsubscribeAll) + } + } + + @Synchronized + fun unsubscribeAll() { + removeAllChannelsFromLocalStorage() + removeAllChannelGroupsFromLocalStorage() + presenceData.channelStates.clear() + subscribeEventEngineManager.addEventToQueue(SubscribeEvent.UnsubscribeAll) + } + + @Synchronized + fun getSubscribedChannels(): List { + return subscriptionData.channels.toList().filter { !it.endsWith(PRESENCE_CHANNEL_SUFFIX) } + } + + @Synchronized + fun getSubscribedChannelGroups(): List { + return subscriptionData.channelGroups.toList().filter { !it.endsWith(PRESENCE_CHANNEL_SUFFIX) } + } + + fun disconnect() { + subscribeEventEngineManager.addEventToQueue(SubscribeEvent.Disconnect) + } + + fun reconnect(timetoken: Long = 0L) { + val event = + if (timetoken != 0L) { + SubscribeEvent.Reconnect(SubscriptionCursor(timetoken, region = null)) + } else { + SubscribeEvent.Reconnect() + } + subscribeEventEngineManager.addEventToQueue(event) + } + + @Synchronized + fun destroy() { + disconnect() + subscribeEventEngineManager.stop() + } + + private fun throwExceptionIfChannelAndChannelGroupIsMissing( + channels: Set, + channelGroups: Set, + ) { + if (channels.isEmpty() && channelGroups.isEmpty()) { + throw PubNubException(PubNubError.CHANNEL_OR_CHANNEL_GROUP_MISSING) + } + } + + private fun addChannelsToSubscriptionData( + channels: Set, + withPresence: Boolean, + ) { + subscriptionData.channels.addAll(channels) + if (withPresence) { + channels.forEach { + val presenceChannel = "$it$PRESENCE_CHANNEL_SUFFIX" + subscriptionData.channels.add(presenceChannel) + } + } + } + + private fun addChannelGroupsToSubscriptionData( + channelGroups: Set, + withPresence: Boolean, + ) { + subscriptionData.channelGroups.addAll(channelGroups) + if (withPresence) { + channelGroups.forEach { + val presenceChannelGroup = "$it$PRESENCE_CHANNEL_SUFFIX" + subscriptionData.channelGroups.add(presenceChannelGroup) + } + } + } + + private fun removeChannelGroupsFromSubscriptionData(channelGroups: Set) { + channelGroups.forEach { + subscriptionData.channelGroups.remove(it) + val presenceChannelGroup = "$it$PRESENCE_CHANNEL_SUFFIX" + subscriptionData.channelGroups.remove(presenceChannelGroup) + } + } + + private fun removeChannelsFromSubscriptionData(channels: Set) { + channels.forEach { + subscriptionData.channels.remove(it) + val presenceChannel = "$it$PRESENCE_CHANNEL_SUFFIX" + subscriptionData.channels.remove(presenceChannel) + } + } + + private fun removeAllChannelsFromLocalStorage() { + subscriptionData.channels.clear() + } + + private fun removeAllChannelGroupsFromLocalStorage() { + subscriptionData.channelGroups.clear() + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/SubscribeEventEngine.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/SubscribeEventEngine.kt new file mode 100644 index 000000000..cf49d7193 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/SubscribeEventEngine.kt @@ -0,0 +1,21 @@ +package com.pubnub.internal.subscribe.eventengine + +import com.pubnub.internal.eventengine.EventEngine +import com.pubnub.internal.eventengine.Sink +import com.pubnub.internal.eventengine.Source +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.state.SubscribeState + +internal typealias SubscribeEventEngine = EventEngine + +internal fun SubscribeEventEngine( + effectSink: Sink, + eventSource: Source, + currentState: SubscribeState = SubscribeState.Unsubscribed, +): SubscribeEventEngine = + EventEngine( + effectSink, + eventSource, + currentState, + ) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/configuration/EventEnginesConf.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/configuration/EventEnginesConf.kt new file mode 100644 index 000000000..4e35dddb8 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/configuration/EventEnginesConf.kt @@ -0,0 +1,14 @@ +package com.pubnub.internal.subscribe.eventengine.configuration + +import com.pubnub.internal.eventengine.EventEngineConf +import com.pubnub.internal.eventengine.QueueEventEngineConf +import com.pubnub.internal.presence.eventengine.effect.PresenceEffectInvocation +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent + +@Suppress("EXPOSED_PARAMETER_TYPE") +class EventEnginesConf( + val subscribe: EventEngineConf = QueueEventEngineConf(), + val presence: EventEngineConf = QueueEventEngineConf(), +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/data/SubscriptionData.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/data/SubscriptionData.kt new file mode 100644 index 000000000..e12d3a7b0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/data/SubscriptionData.kt @@ -0,0 +1,6 @@ +package com.pubnub.internal.subscribe.eventengine.data + +internal class SubscriptionData { + internal val channels: MutableSet = mutableSetOf() + internal val channelGroups: MutableSet = mutableSetOf() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitMessagesEffect.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitMessagesEffect.kt new file mode 100644 index 000000000..90a4cc85f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitMessagesEffect.kt @@ -0,0 +1,32 @@ +package com.pubnub.internal.subscribe.eventengine.effect + +import com.pubnub.api.models.consumer.pubsub.PNEvent +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.api.models.consumer.pubsub.PNSignalResult +import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult +import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult +import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult +import com.pubnub.internal.eventengine.Effect +import org.slf4j.LoggerFactory + +internal class EmitMessagesEffect( + private val messagesConsumer: MessagesConsumer, + private val messages: List, +) : Effect { + private val log = LoggerFactory.getLogger(EmitMessagesEffect::class.java) + + override fun runEffect() { + log.trace("Running EmitMessagesEffect") + for (message in messages) { + when (message) { + is PNMessageResult -> messagesConsumer.announce(message) + is PNPresenceEventResult -> messagesConsumer.announce(message) + is PNSignalResult -> messagesConsumer.announce(message) + is PNMessageActionResult -> messagesConsumer.announce(message) + is PNObjectEventResult -> messagesConsumer.announce(message) + is PNFileEventResult -> messagesConsumer.announce(message) + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitStatusEffect.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitStatusEffect.kt new file mode 100644 index 000000000..c6387ba22 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitStatusEffect.kt @@ -0,0 +1,17 @@ +package com.pubnub.internal.subscribe.eventengine.effect + +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.internal.eventengine.Effect +import org.slf4j.LoggerFactory + +internal class EmitStatusEffect( + private val statusConsumer: StatusConsumer, + private val status: PNStatus, +) : Effect { + private val log = LoggerFactory.getLogger(EmitStatusEffect::class.java) + + override fun runEffect() { + log.trace("Running EmitStatusEffect: $status") + statusConsumer.announce(status) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/HandshakeEffect.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/HandshakeEffect.kt new file mode 100644 index 000000000..3920db49b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/HandshakeEffect.kt @@ -0,0 +1,36 @@ +package com.pubnub.internal.subscribe.eventengine.effect + +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.internal.eventengine.ManagedEffect +import com.pubnub.internal.eventengine.Sink +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import org.slf4j.LoggerFactory + +internal class HandshakeEffect( + private val handshakeRemoteAction: RemoteAction, + private val subscribeEventSink: Sink, +) : ManagedEffect { + private val log = LoggerFactory.getLogger(HandshakeEffect::class.java) + + override fun runEffect() { + log.trace("Running HandshakeEffect") + + handshakeRemoteAction.async { result -> + result.onFailure { + subscribeEventSink.add( + SubscribeEvent.HandshakeFailure( + PubNubException.from(it), + ), + ) + }.onSuccess { cursor -> + subscribeEventSink.add(SubscribeEvent.HandshakeSuccess(cursor)) + } + } + } + + override fun cancel() { + handshakeRemoteAction.silentCancel() + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/MessagesConsumer.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/MessagesConsumer.kt new file mode 100644 index 000000000..32e0cc518 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/MessagesConsumer.kt @@ -0,0 +1,22 @@ +package com.pubnub.internal.subscribe.eventengine.effect + +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.api.models.consumer.pubsub.PNSignalResult +import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult +import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult +import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult + +internal interface MessagesConsumer { + fun announce(message: PNMessageResult) + + fun announce(presence: PNPresenceEventResult) + + fun announce(signal: PNSignalResult) + + fun announce(messageAction: PNMessageActionResult) + + fun announce(pnObjectEventResult: PNObjectEventResult) + + fun announce(pnFileEventResult: PNFileEventResult) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/ReceiveMessagesEffect.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/ReceiveMessagesEffect.kt new file mode 100644 index 000000000..b4c69c408 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/ReceiveMessagesEffect.kt @@ -0,0 +1,35 @@ +package com.pubnub.internal.subscribe.eventengine.effect + +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.internal.eventengine.ManagedEffect +import com.pubnub.internal.eventengine.Sink +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import org.slf4j.LoggerFactory + +internal class ReceiveMessagesEffect( + private val receiveMessagesRemoteAction: RemoteAction, + private val subscribeEventSink: Sink, +) : ManagedEffect { + private val log = LoggerFactory.getLogger(ReceiveMessagesEffect::class.java) + + override fun runEffect() { + log.trace("Running ReceiveMessagesEffect") + + receiveMessagesRemoteAction.async { result -> + result.onFailure { + subscribeEventSink.add( + SubscribeEvent.ReceiveFailure( + PubNubException.from(it), + ), + ) + }.onSuccess { + subscribeEventSink.add(SubscribeEvent.ReceiveSuccess(it.messages, it.subscriptionCursor)) + } + } + } + + override fun cancel() { + receiveMessagesRemoteAction.silentCancel() + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/ReconnectionPolicy.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/ReconnectionPolicy.kt new file mode 100644 index 000000000..e69de29bb diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/StatusConsumer.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/StatusConsumer.kt new file mode 100644 index 000000000..c6a3da3b0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/StatusConsumer.kt @@ -0,0 +1,7 @@ +package com.pubnub.internal.subscribe.eventengine.effect + +import com.pubnub.api.models.consumer.PNStatus + +internal interface StatusConsumer { + fun announce(status: PNStatus) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/SubscribeEffectFactory.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/SubscribeEffectFactory.kt new file mode 100644 index 000000000..61575c84f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/SubscribeEffectFactory.kt @@ -0,0 +1,67 @@ +package com.pubnub.internal.subscribe.eventengine.effect + +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.api.models.consumer.pubsub.PNEvent +import com.pubnub.internal.eventengine.Effect +import com.pubnub.internal.eventengine.EffectFactory +import com.pubnub.internal.eventengine.Sink +import com.pubnub.internal.presence.eventengine.data.PresenceData +import com.pubnub.internal.subscribe.eventengine.effect.effectprovider.HandshakeProvider +import com.pubnub.internal.subscribe.eventengine.effect.effectprovider.ReceiveMessagesProvider +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor + +internal data class ReceiveMessagesResult( + val messages: List, + val subscriptionCursor: SubscriptionCursor, +) + +internal class SubscribeEffectFactory( + private val handshakeProvider: HandshakeProvider, + private val receiveMessagesProvider: ReceiveMessagesProvider, + private val subscribeEventSink: Sink, + private val messagesConsumer: MessagesConsumer, + private val statusConsumer: StatusConsumer, + private val presenceData: PresenceData, + private val sendStateWithSubscribe: Boolean, +) : EffectFactory { + override fun create(effectInvocation: SubscribeEffectInvocation): Effect? { + return when (effectInvocation) { + is SubscribeEffectInvocation.EmitMessages -> { + EmitMessagesEffect(messagesConsumer, effectInvocation.messages) + } + + is SubscribeEffectInvocation.EmitStatus -> { + EmitStatusEffect(statusConsumer, effectInvocation.status) + } + + is SubscribeEffectInvocation.Handshake -> { + val handshakeRemoteAction = + handshakeProvider.getHandshakeRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + if (sendStateWithSubscribe) { + presenceData.channelStates.filter { it.key in effectInvocation.channels } + } else { + null + }, + ) + HandshakeEffect(handshakeRemoteAction, subscribeEventSink) + } + + is SubscribeEffectInvocation.ReceiveMessages -> { + val receiveMessagesRemoteAction: RemoteAction = + receiveMessagesProvider.getReceiveMessagesRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + effectInvocation.subscriptionCursor, + ) + ReceiveMessagesEffect(receiveMessagesRemoteAction, subscribeEventSink) + } + + SubscribeEffectInvocation.CancelHandshake, + SubscribeEffectInvocation.CancelReceiveMessages, + -> null + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/SubscribeEffectInvocation.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/SubscribeEffectInvocation.kt new file mode 100644 index 000000000..0f34dfea7 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/SubscribeEffectInvocation.kt @@ -0,0 +1,39 @@ +package com.pubnub.internal.subscribe.eventengine.effect + +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.api.models.consumer.pubsub.PNEvent +import com.pubnub.internal.eventengine.Cancel +import com.pubnub.internal.eventengine.EffectInvocation +import com.pubnub.internal.eventengine.EffectInvocationType +import com.pubnub.internal.eventengine.Managed +import com.pubnub.internal.eventengine.NonManaged +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor + +internal sealed class SubscribeEffectInvocation(override val type: EffectInvocationType) : EffectInvocation { + override val id: String = "any value for NonManged and Cancel effect" + + data class ReceiveMessages( + val channels: Set, + val channelGroups: Set, + val subscriptionCursor: SubscriptionCursor, + ) : SubscribeEffectInvocation(Managed) { + override val id: String = ReceiveMessages::class.java.simpleName + } + + object CancelReceiveMessages : + SubscribeEffectInvocation(Cancel(idToCancel = ReceiveMessages::class.java.simpleName)) + + data class Handshake( + val channels: Set, + val channelGroups: Set, + ) : SubscribeEffectInvocation(Managed) { + override val id: String = Handshake::class.java.simpleName + } + + object CancelHandshake : + SubscribeEffectInvocation(Cancel(idToCancel = Handshake::class.java.simpleName)) + + data class EmitStatus(val status: PNStatus) : SubscribeEffectInvocation(NonManaged) + + data class EmitMessages(val messages: List) : SubscribeEffectInvocation(NonManaged) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/HandshakeProvider.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/HandshakeProvider.kt new file mode 100644 index 000000000..ed28b2f7f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/HandshakeProvider.kt @@ -0,0 +1,12 @@ +package com.pubnub.internal.subscribe.eventengine.effect.effectprovider + +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor + +internal fun interface HandshakeProvider { + fun getHandshakeRemoteAction( + channels: Set, + channelGroups: Set, + state: Map?, + ): RemoteAction +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/HandshakeProviderImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/HandshakeProviderImpl.kt new file mode 100644 index 000000000..65356636c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/HandshakeProviderImpl.kt @@ -0,0 +1,23 @@ +package com.pubnub.internal.subscribe.eventengine.effect.effectprovider + +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.api.endpoints.remoteaction.map +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.pubsub.SubscribeEndpoint +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor + +internal class HandshakeProviderImpl(val pubNub: PubNubImpl) : HandshakeProvider { + override fun getHandshakeRemoteAction( + channels: Set, + channelGroups: Set, + state: Map?, + ): RemoteAction { + val subscribe = SubscribeEndpoint(pubNub) + subscribe.channels = channels.toList() + subscribe.channelGroups = channelGroups.toList() + subscribe.timetoken = 0 + subscribe.region = null + subscribe.state = state + return subscribe.map { SubscriptionCursor(it.metadata.timetoken, it.metadata.region) } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/ReceiveMessagesProvider.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/ReceiveMessagesProvider.kt new file mode 100644 index 000000000..1d0e6ed7d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/ReceiveMessagesProvider.kt @@ -0,0 +1,13 @@ +package com.pubnub.internal.subscribe.eventengine.effect.effectprovider + +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.internal.subscribe.eventengine.effect.ReceiveMessagesResult +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor + +internal fun interface ReceiveMessagesProvider { + fun getReceiveMessagesRemoteAction( + channels: Set, + channelGroups: Set, + subscriptionCursor: SubscriptionCursor, + ): RemoteAction +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/ReceiveMessagesProviderImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/ReceiveMessagesProviderImpl.kt new file mode 100644 index 000000000..f2bf97746 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/effect/effectprovider/ReceiveMessagesProviderImpl.kt @@ -0,0 +1,36 @@ +package com.pubnub.internal.subscribe.eventengine.effect.effectprovider + +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.api.endpoints.remoteaction.map +import com.pubnub.api.models.consumer.pubsub.PNEvent +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.pubsub.SubscribeEndpoint +import com.pubnub.internal.subscribe.eventengine.effect.ReceiveMessagesResult +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import com.pubnub.internal.workers.SubscribeMessageProcessor + +internal class ReceiveMessagesProviderImpl(val pubNub: PubNubImpl, val messageProcessor: SubscribeMessageProcessor) : + ReceiveMessagesProvider { + override fun getReceiveMessagesRemoteAction( + channels: Set, + channelGroups: Set, + subscriptionCursor: SubscriptionCursor, + ): RemoteAction { + val subscribe = SubscribeEndpoint(pubNub) + subscribe.channels = channels.toList() + subscribe.channelGroups = channelGroups.toList() + subscribe.timetoken = subscriptionCursor.timetoken + subscribe.region = subscriptionCursor.region + subscribe.filterExpression = pubNub.configuration.filterExpression.ifBlank { null } + return subscribe.map { + val sdkMessages: List = + it.messages.mapNotNull { + messageProcessor.processIncomingPayload(it) + } + ReceiveMessagesResult( + sdkMessages, + SubscriptionCursor(it.metadata.timetoken, it.metadata.region), + ) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/event/SubscribeEvent.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/event/SubscribeEvent.kt new file mode 100644 index 000000000..8eb1db843 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/event/SubscribeEvent.kt @@ -0,0 +1,36 @@ +package com.pubnub.internal.subscribe.eventengine.event + +import com.pubnub.api.PubNubException +import com.pubnub.api.models.consumer.pubsub.PNEvent +import com.pubnub.internal.eventengine.Event + +internal sealed class SubscribeEvent : Event { + class SubscriptionChanged(channels: Set, channelGroups: Set) : SubscribeEvent() { + val channels: Set = channels.toSet() + val channelGroups: Set = channelGroups.toSet() + } + + class SubscriptionRestored( + channels: Set, + channelGroups: Set, + val subscriptionCursor: SubscriptionCursor, + ) : SubscribeEvent() { + val channels: Set = channels.toSet() + val channelGroups: Set = channelGroups.toSet() + } + + object Disconnect : SubscribeEvent() + + data class Reconnect(val subscriptionCursor: SubscriptionCursor? = null) : SubscribeEvent() + + object UnsubscribeAll : SubscribeEvent() + + data class HandshakeSuccess(val subscriptionCursor: SubscriptionCursor) : SubscribeEvent() + + data class HandshakeFailure(val reason: PubNubException) : SubscribeEvent() + + data class ReceiveSuccess(val messages: List, val subscriptionCursor: SubscriptionCursor) : + SubscribeEvent() + + data class ReceiveFailure(val reason: PubNubException) : SubscribeEvent() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/event/SubscriptionCursor.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/event/SubscriptionCursor.kt new file mode 100644 index 000000000..8e76b5d50 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/event/SubscriptionCursor.kt @@ -0,0 +1,3 @@ +package com.pubnub.internal.subscribe.eventengine.event + +internal data class SubscriptionCursor(val timetoken: Long, val region: String?) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/state/SubscribeState.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/state/SubscribeState.kt new file mode 100644 index 000000000..b8fc62c30 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/subscribe/eventengine/state/SubscribeState.kt @@ -0,0 +1,362 @@ +package com.pubnub.internal.subscribe.eventengine.state + +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNStatusCategory +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.internal.eventengine.State +import com.pubnub.internal.eventengine.noTransition +import com.pubnub.internal.eventengine.transitionTo +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor + +internal sealed class SubscribeState : State { + object Unsubscribed : SubscribeState() { + override fun transition(event: SubscribeEvent): Pair> { + return when (event) { + is SubscribeEvent.SubscriptionChanged -> { + transitionTo(Handshaking(event.channels, event.channelGroups)) + } + + is SubscribeEvent.SubscriptionRestored -> { + transitionTo(Handshaking(event.channels, event.channelGroups, event.subscriptionCursor)) + } + + else -> { + noTransition() + } + } + } + } + + class Handshaking( + channels: Set, + channelGroups: Set, + val subscriptionCursor: SubscriptionCursor? = null, + ) : SubscribeState() { + // toSet() is a must because we want to make sure that channels is immutable, and Handshaking constructor + // doesn't prevent from providing "channels" that is mutable set. + val channels: Set = channels.toSet() + val channelGroups: Set = channelGroups.toSet() + + override fun onEntry() = setOf(SubscribeEffectInvocation.Handshake(channels, channelGroups)) + + override fun onExit() = setOf(SubscribeEffectInvocation.CancelHandshake) + + override fun transition(event: SubscribeEvent): Pair> { + return when (event) { + is SubscribeEvent.HandshakeSuccess -> { + val cursor = + subscriptionCursor?.copy(region = event.subscriptionCursor.region) + ?: event.subscriptionCursor + transitionTo( + Receiving(channels, channelGroups, cursor), + SubscribeEffectInvocation.EmitStatus( + PNStatus( + PNStatusCategory.PNConnectedCategory, + currentTimetoken = cursor.timetoken, + affectedChannels = channels.toList(), + affectedChannelGroups = channelGroups.toList(), + ), + ), + ) + } + + is SubscribeEvent.SubscriptionRestored -> { + transitionTo(Handshaking(event.channels, event.channelGroups, event.subscriptionCursor)) + } + + is SubscribeEvent.HandshakeFailure -> { + transitionTo( + HandshakeFailed(channels, channelGroups, event.reason, subscriptionCursor), + SubscribeEffectInvocation.EmitStatus( + PNStatus(PNStatusCategory.PNConnectionError, event.reason), + ), + ) + } + + is SubscribeEvent.SubscriptionChanged -> { + transitionTo(Handshaking(event.channels, event.channelGroups, subscriptionCursor)) + } + + is SubscribeEvent.Disconnect -> { + transitionTo(HandshakeStopped(channels, channelGroups, reason = null)) + } + + is SubscribeEvent.UnsubscribeAll -> { + transitionTo(Unsubscribed) + } + + else -> { + noTransition() + } + } + } + } + + class HandshakeStopped( + channels: Set, + channelGroups: Set, + val reason: PubNubException?, + ) : SubscribeState() { + val channels: Set = channels.toSet() + val channelGroups: Set = channelGroups.toSet() + + override fun transition(event: SubscribeEvent): Pair> { + return when (event) { + is SubscribeEvent.Reconnect -> { + transitionTo(Handshaking(channels, channelGroups, event.subscriptionCursor)) + } + + is SubscribeEvent.SubscriptionChanged -> { + transitionTo( + HandshakeStopped( + event.channels, + event.channelGroups, + reason = null, + ), + ) + } + + is SubscribeEvent.SubscriptionRestored -> { + transitionTo( + HandshakeStopped( + event.channels, + event.channelGroups, + reason = null, + ), + ) + } + + is SubscribeEvent.UnsubscribeAll -> { + transitionTo(Unsubscribed) + } + + else -> { + noTransition() + } + } + } + } + + class HandshakeFailed( + channels: Set, + channelGroups: Set, + val reason: PubNubException, + val subscriptionCursor: SubscriptionCursor? = null, + ) : SubscribeState() { + val channels: Set = channels.toSet() + val channelGroups: Set = channelGroups.toSet() + + override fun transition(event: SubscribeEvent): Pair> { + return when (event) { + is SubscribeEvent.SubscriptionChanged -> { + transitionTo(Handshaking(event.channels, event.channelGroups, subscriptionCursor)) + } + + is SubscribeEvent.SubscriptionRestored -> { + transitionTo( + Handshaking( + event.channels, + event.channelGroups, + event.subscriptionCursor, + ), + ) + } + + is SubscribeEvent.Reconnect -> { + transitionTo(Handshaking(channels, channelGroups, event.subscriptionCursor)) + } + + is SubscribeEvent.UnsubscribeAll -> { + transitionTo(Unsubscribed) + } + + else -> { + noTransition() + } + } + } + } + + class Receiving( + channels: Set, + channelGroups: Set, + val subscriptionCursor: SubscriptionCursor, + ) : SubscribeState() { + val channels: Set = channels.toSet() + val channelGroups: Set = channelGroups.toSet() + + override fun onEntry() = + setOf( + SubscribeEffectInvocation.ReceiveMessages( + channels, + channelGroups, + subscriptionCursor, + ), + ) + + override fun onExit() = setOf(SubscribeEffectInvocation.CancelReceiveMessages) + + override fun transition(event: SubscribeEvent): Pair> { + return when (event) { + is SubscribeEvent.ReceiveFailure -> { + transitionTo( + ReceiveFailed( + channels, + channelGroups, + subscriptionCursor, + event.reason, + ), + SubscribeEffectInvocation.EmitStatus( + PNStatus( + PNStatusCategory.PNUnexpectedDisconnectCategory, + event.reason, + ), + ), + ) + } + + is SubscribeEvent.Disconnect -> { + transitionTo( + state = ReceiveStopped(channels, channelGroups, subscriptionCursor), + SubscribeEffectInvocation.EmitStatus( + PNStatus(PNStatusCategory.PNDisconnectedCategory), + ), + ) + } + + is SubscribeEvent.SubscriptionChanged -> { + transitionTo( + Receiving(event.channels, event.channelGroups, subscriptionCursor), + SubscribeEffectInvocation.EmitStatus( + PNStatus( + PNStatusCategory.PNSubscriptionChanged, + currentTimetoken = subscriptionCursor.timetoken, + affectedChannels = event.channels, + affectedChannelGroups = event.channelGroups, + ), + ), + ) + } + + is SubscribeEvent.SubscriptionRestored -> { + transitionTo( + Receiving( + event.channels, + event.channelGroups, + SubscriptionCursor(event.subscriptionCursor.timetoken, subscriptionCursor.region), + ), + SubscribeEffectInvocation.EmitStatus( + PNStatus( + PNStatusCategory.PNSubscriptionChanged, + currentTimetoken = event.subscriptionCursor.timetoken, + affectedChannels = event.channels, + affectedChannelGroups = event.channelGroups, + ), + ), + ) + } + + is SubscribeEvent.ReceiveSuccess -> { + transitionTo( + state = Receiving(channels, channelGroups, event.subscriptionCursor), + SubscribeEffectInvocation.EmitMessages(event.messages), + ) + } + + is SubscribeEvent.UnsubscribeAll -> { + transitionTo( + state = Unsubscribed, + SubscribeEffectInvocation.EmitStatus( + PNStatus(PNStatusCategory.PNDisconnectedCategory), + ), + ) + } + + else -> { + noTransition() + } + } + } + } + + class ReceiveStopped( + channels: Set, + channelGroups: Set, + val subscriptionCursor: SubscriptionCursor, + ) : SubscribeState() { + val channels: Set = channels.toSet() + val channelGroups: Set = channelGroups.toSet() + + override fun transition(event: SubscribeEvent): Pair> { + return when (event) { + is SubscribeEvent.Reconnect -> { + transitionTo(Handshaking(channels, channelGroups, event.subscriptionCursor ?: subscriptionCursor)) + } + + is SubscribeEvent.SubscriptionChanged -> { + transitionTo( + ReceiveStopped( + event.channels, + event.channelGroups, + subscriptionCursor, + ), + ) + } + + is SubscribeEvent.SubscriptionRestored -> { + transitionTo( + ReceiveStopped( + event.channels, + event.channelGroups, + event.subscriptionCursor, + ), + ) + } + + is SubscribeEvent.UnsubscribeAll -> { + transitionTo(Unsubscribed) + } + + else -> { + noTransition() + } + } + } + } + + class ReceiveFailed( + channels: Set, + channelGroups: Set, + val subscriptionCursor: SubscriptionCursor, + val reason: PubNubException, + ) : SubscribeState() { + val channels: Set = channels.toSet() + val channelGroups: Set = channelGroups.toSet() + + override fun transition(event: SubscribeEvent): Pair> { + return when (event) { + is SubscribeEvent.Reconnect -> { + transitionTo(Handshaking(channels, channelGroups, event.subscriptionCursor ?: subscriptionCursor)) + } + + is SubscribeEvent.SubscriptionChanged -> { + transitionTo(Handshaking(event.channels, event.channelGroups, subscriptionCursor)) + } + + is SubscribeEvent.SubscriptionRestored -> { + transitionTo(Handshaking(event.channels, event.channelGroups, event.subscriptionCursor)) + } + + is SubscribeEvent.UnsubscribeAll -> { + transitionTo(Unsubscribed) + } + + else -> { + noTransition() + } + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/utils/PolymorphicDeserializer.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/utils/PolymorphicDeserializer.kt new file mode 100644 index 000000000..6f0fe5145 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/utils/PolymorphicDeserializer.kt @@ -0,0 +1,38 @@ +package com.pubnub.internal.utils + +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import java.lang.reflect.Type + +internal class PolymorphicDeserializer( + private val defaultClass: Class? = null, + private val findTargetClass: (JsonElement, Type) -> Class?, +) : JsonDeserializer { + companion object { + inline fun dispatchByFieldsValues( + fields: List, + mappingFieldValuesToClass: Map, Class>, + defaultClass: Class? = null, + ): JsonDeserializer { + val mappingWithList: Map, Class> = mappingFieldValuesToClass.mapKeys { it.key.toList() } + return PolymorphicDeserializer(defaultClass = defaultClass) { jsonElement, _ -> + val jsonObject = jsonElement.asJsonObject + val fieldsValues: List = fields.map { jsonObject[it].asString }.toList() + + mappingWithList[fieldsValues] + } + } + } + + override fun deserialize( + json: JsonElement, + typeOfT: Type, + context: JsonDeserializationContext, + ): T { + val targetClass = + findTargetClass(json, typeOfT) ?: defaultClass + ?: error("When deserializing to $typeOfT no target class have been found and default class was not provided") + return context.deserialize(json, targetClass) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/utils/UnwrapSingleField.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/utils/UnwrapSingleField.kt new file mode 100644 index 000000000..1849a714a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/utils/UnwrapSingleField.kt @@ -0,0 +1,21 @@ +package com.pubnub.internal.utils + +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import java.lang.reflect.Type + +internal class UnwrapSingleField : JsonDeserializer { + override fun deserialize( + json: JsonElement, + typeOfT: Type, + context: JsonDeserializationContext, + ): T { + val jsonObject = json.asJsonObject + if (jsonObject.keySet().size != 1) { + error("Couldn't unwrap field for object containing more than 1 field. Actual number of fields: ${jsonObject.keySet().size}") + } + val element = jsonObject[jsonObject.keySet().first()] + return context.deserialize(element, typeOfT) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/PNConfigurationImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/PNConfigurationImpl.kt index 8dab21e95..95897e815 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/PNConfigurationImpl.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/PNConfigurationImpl.kt @@ -5,7 +5,6 @@ import com.pubnub.api.crypto.CryptoModule import com.pubnub.api.enums.PNHeartbeatNotificationOptions import com.pubnub.api.enums.PNLogVerbosity import com.pubnub.api.retry.RetryConfiguration -import com.pubnub.api.v2.BasePNConfiguration import com.pubnub.api.v2.PNConfiguration import com.pubnub.api.v2.PNConfigurationOverride import okhttp3.Authenticator @@ -21,71 +20,81 @@ import javax.net.ssl.X509ExtendedTrustManager class PNConfigurationImpl( override val userId: UserId, - override val subscribeKey: String, - override val publishKey: String, - override val secretKey: String, - override val authKey: String, - override val cryptoModule: CryptoModule?, - override val origin: String, - override val secure: Boolean, - override val logVerbosity: PNLogVerbosity, - override val heartbeatNotificationOptions: PNHeartbeatNotificationOptions, - override val presenceTimeout: Int, - override val heartbeatInterval: Int, - override val subscribeTimeout: Int, - override val connectTimeout: Int, - override val nonSubscribeReadTimeout: Int, - override val cacheBusting: Boolean, - override val suppressLeaveEvents: Boolean, - override val maintainPresenceState: Boolean, - override val filterExpression: String, - override val includeInstanceIdentifier: Boolean, - override val includeRequestIdentifier: Boolean, - override val maximumConnections: Int?, - override val googleAppEngineNetworking: Boolean, - override val proxy: Proxy?, - override val proxySelector: ProxySelector?, - override val proxyAuthenticator: Authenticator?, - override val certificatePinner: CertificatePinner?, - override val httpLoggingInterceptor: HttpLoggingInterceptor?, - override val sslSocketFactory: SSLSocketFactory?, - override val x509ExtendedTrustManager: X509ExtendedTrustManager?, - override val connectionSpec: ConnectionSpec?, - override val hostnameVerifier: HostnameVerifier?, - override val fileMessagePublishRetryLimit: Int, - override val dedupOnSubscribe: Boolean, - override val maximumMessagesCacheSize: Int, - override val pnsdkSuffixes: Map, - override val retryConfiguration: RetryConfiguration, - override val managePresenceListManually: Boolean, -) : BasePNConfigurationImpl(userId), PNConfiguration, PNConfigurationOverride { - class Builder(defaultConfiguration: BasePNConfiguration) : - BasePNConfigurationImpl.Builder(defaultConfiguration), + override val subscribeKey: String = "", + override val publishKey: String = "", + override val secretKey: String = "", + override val authKey: String = "", + override val cryptoModule: CryptoModule? = null, + override val origin: String = "", + override val secure: Boolean = true, + override val logVerbosity: PNLogVerbosity = PNLogVerbosity.NONE, + override val heartbeatNotificationOptions: PNHeartbeatNotificationOptions = PNHeartbeatNotificationOptions.FAILURES, + override val presenceTimeout: Int = PRESENCE_TIMEOUT, + override val heartbeatInterval: Int = 0, + override val subscribeTimeout: Int = SUBSCRIBE_TIMEOUT, + override val connectTimeout: Int = CONNECT_TIMEOUT, + override val nonSubscribeReadTimeout: Int = NON_SUBSCRIBE_REQUEST_TIMEOUT, + override val cacheBusting: Boolean = false, + override val suppressLeaveEvents: Boolean = false, + override val maintainPresenceState: Boolean = true, + override val filterExpression: String = "", + override val includeInstanceIdentifier: Boolean = false, + override val includeRequestIdentifier: Boolean = true, + override val maximumConnections: Int? = null, + override val googleAppEngineNetworking: Boolean = false, + override val proxy: Proxy? = null, + override val proxySelector: ProxySelector? = null, + override val proxyAuthenticator: Authenticator? = null, + override val certificatePinner: CertificatePinner? = null, + override val httpLoggingInterceptor: HttpLoggingInterceptor? = null, + override val sslSocketFactory: SSLSocketFactory? = null, + override val x509ExtendedTrustManager: X509ExtendedTrustManager? = null, + override val connectionSpec: ConnectionSpec? = null, + override val hostnameVerifier: HostnameVerifier? = null, + override val fileMessagePublishRetryLimit: Int = 5, + override val dedupOnSubscribe: Boolean = false, + override val maximumMessagesCacheSize: Int = DEFAULT_DEDUPE_SIZE, + override val pnsdkSuffixes: Map = emptyMap(), + override val retryConfiguration: RetryConfiguration = RetryConfiguration.None, + override val managePresenceListManually: Boolean = false, +) : PNConfiguration, PNConfigurationOverride { + companion object { + const val DEFAULT_DEDUPE_SIZE = 100 + const val PRESENCE_TIMEOUT = 300 + const val MINIMUM_PRESENCE_TIMEOUT = 20 + const val NON_SUBSCRIBE_REQUEST_TIMEOUT = 10 + const val SUBSCRIBE_TIMEOUT = 310 + const val CONNECT_TIMEOUT = 5 + } + + class Builder(defaultConfiguration: PNConfiguration) : PNConfiguration.Builder, PNConfigurationOverride.Builder { + constructor(userId: UserId, subscribeKey: String) : this(PNConfigurationImpl(userId, subscribeKey)) + private val log = LoggerFactory.getLogger(this::class.simpleName) - override var userId: UserId = super.userId + override var userId: UserId = defaultConfiguration.userId - override var subscribeKey: String = super.subscribeKey + override var subscribeKey: String = defaultConfiguration.subscribeKey - override var publishKey: String = super.publishKey + override var publishKey: String = defaultConfiguration.publishKey - override var secretKey: String = super.secretKey + override var secretKey: String = defaultConfiguration.secretKey - override var authKey: String = super.authKey + override var authKey: String = defaultConfiguration.authKey - override var cryptoModule: CryptoModule? = super.cryptoModule + override var cryptoModule: CryptoModule? = defaultConfiguration.cryptoModule - override var origin: String = super.origin + override var origin: String = defaultConfiguration.origin - override var secure: Boolean = super.secure + override var secure: Boolean = defaultConfiguration.secure - override var logVerbosity: PNLogVerbosity = super.logVerbosity + override var logVerbosity: PNLogVerbosity = defaultConfiguration.logVerbosity - override var heartbeatNotificationOptions: PNHeartbeatNotificationOptions = super.heartbeatNotificationOptions + override var heartbeatNotificationOptions: PNHeartbeatNotificationOptions = defaultConfiguration.heartbeatNotificationOptions - override var presenceTimeout: Int = super.presenceTimeout + override var presenceTimeout: Int = defaultConfiguration.presenceTimeout set(value) { field = if (value < MINIMUM_PRESENCE_TIMEOUT) { @@ -97,11 +106,11 @@ class PNConfigurationImpl( heartbeatInterval = (presenceTimeout / 2) - 1 } - override var heartbeatInterval: Int = super.heartbeatInterval + override var heartbeatInterval: Int = defaultConfiguration.heartbeatInterval - override var subscribeTimeout: Int = super.subscribeTimeout + override var subscribeTimeout: Int = defaultConfiguration.subscribeTimeout - override var connectTimeout: Int = super.connectTimeout + override var connectTimeout: Int = defaultConfiguration.connectTimeout @Deprecated( "This setting relates to *read* timeout and was renamed to `nonSubscribeReadTimeout`", @@ -113,50 +122,50 @@ class PNConfigurationImpl( nonSubscribeReadTimeout = value } - override var nonSubscribeReadTimeout: Int = super.nonSubscribeReadTimeout + override var nonSubscribeReadTimeout: Int = defaultConfiguration.nonSubscribeReadTimeout - override var cacheBusting: Boolean = super.cacheBusting + override var cacheBusting: Boolean = defaultConfiguration.cacheBusting - override var suppressLeaveEvents: Boolean = super.suppressLeaveEvents + override var suppressLeaveEvents: Boolean = defaultConfiguration.suppressLeaveEvents - override var maintainPresenceState: Boolean = super.maintainPresenceState + override var maintainPresenceState: Boolean = defaultConfiguration.maintainPresenceState - override var filterExpression: String = super.filterExpression + override var filterExpression: String = defaultConfiguration.filterExpression - override var includeInstanceIdentifier: Boolean = super.includeInstanceIdentifier + override var includeInstanceIdentifier: Boolean = defaultConfiguration.includeInstanceIdentifier - override var includeRequestIdentifier: Boolean = super.includeRequestIdentifier + override var includeRequestIdentifier: Boolean = defaultConfiguration.includeRequestIdentifier - override var maximumConnections: Int? = super.maximumConnections + override var maximumConnections: Int? = defaultConfiguration.maximumConnections - override var googleAppEngineNetworking: Boolean = super.googleAppEngineNetworking + override var googleAppEngineNetworking: Boolean = defaultConfiguration.googleAppEngineNetworking - override var proxy: Proxy? = super.proxy + override var proxy: Proxy? = defaultConfiguration.proxy - override var proxySelector: ProxySelector? = super.proxySelector + override var proxySelector: ProxySelector? = defaultConfiguration.proxySelector - override var proxyAuthenticator: Authenticator? = super.proxyAuthenticator + override var proxyAuthenticator: Authenticator? = defaultConfiguration.proxyAuthenticator - override var certificatePinner: CertificatePinner? = super.certificatePinner + override var certificatePinner: CertificatePinner? = defaultConfiguration.certificatePinner - override var httpLoggingInterceptor: HttpLoggingInterceptor? = super.httpLoggingInterceptor + override var httpLoggingInterceptor: HttpLoggingInterceptor? = defaultConfiguration.httpLoggingInterceptor - override var sslSocketFactory: SSLSocketFactory? = super.sslSocketFactory + override var sslSocketFactory: SSLSocketFactory? = defaultConfiguration.sslSocketFactory - override var x509ExtendedTrustManager: X509ExtendedTrustManager? = super.x509ExtendedTrustManager + override var x509ExtendedTrustManager: X509ExtendedTrustManager? = defaultConfiguration.x509ExtendedTrustManager - override var connectionSpec: ConnectionSpec? = super.connectionSpec + override var connectionSpec: ConnectionSpec? = defaultConfiguration.connectionSpec - override var hostnameVerifier: HostnameVerifier? = super.hostnameVerifier + override var hostnameVerifier: HostnameVerifier? = defaultConfiguration.hostnameVerifier - override var fileMessagePublishRetryLimit: Int = super.fileMessagePublishRetryLimit - override var dedupOnSubscribe: Boolean = super.dedupOnSubscribe - override var maximumMessagesCacheSize: Int = super.maximumMessagesCacheSize - override var pnsdkSuffixes: Map = super.pnsdkSuffixes + override var fileMessagePublishRetryLimit: Int = defaultConfiguration.fileMessagePublishRetryLimit + override var dedupOnSubscribe: Boolean = defaultConfiguration.dedupOnSubscribe + override var maximumMessagesCacheSize: Int = defaultConfiguration.maximumMessagesCacheSize + override var pnsdkSuffixes: Map = defaultConfiguration.pnsdkSuffixes - override var retryConfiguration: RetryConfiguration = super.retryConfiguration + override var retryConfiguration: RetryConfiguration = defaultConfiguration.retryConfiguration - override var managePresenceListManually: Boolean = super.managePresenceListManually + override var managePresenceListManually: Boolean = defaultConfiguration.managePresenceListManually override fun build(): PNConfiguration { return PNConfigurationImpl( diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/DelegatingEventListener.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/DelegatingEventListener.kt deleted file mode 100644 index 712a85ecc..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/DelegatingEventListener.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.pubnub.internal.v2.callbacks - -import com.pubnub.api.BasePubNub -import com.pubnub.api.models.consumer.pubsub.PNMessageResult -import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult -import com.pubnub.api.models.consumer.pubsub.PNSignalResult -import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult -import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.models.toApi - -open class DelegatingEventListener(private val listener: EventListener) : EventListenerCore { - override fun message( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, - event: PNMessageResult, - ) { - listener.message(pubnub as PubNubImpl, event) - } - - override fun presence( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, - event: PNPresenceEventResult, - ) { - listener.presence(pubnub as PubNubImpl, event) - } - - override fun signal( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, - event: PNSignalResult, - ) { - listener.signal(pubnub as PubNubImpl, event) - } - - override fun messageAction( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, - event: PNMessageActionResult, - ) { - listener.messageAction(pubnub as PubNubImpl, event) - } - - override fun objects( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, - event: com.pubnub.internal.models.consumer.pubsub.objects.PNObjectEventResult, - ) { - listener.objects(pubnub as PubNubImpl, event.toApi()) - } - - override fun file( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, - event: PNFileEventResult, - ) { - listener.file(pubnub as PubNubImpl, event) - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - if (other !is DelegatingEventListener) { - return false - } - - if (listener != other.listener) { - return false - } - - return true - } - - override fun hashCode(): Int { - return listener.hashCode() - } -} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/DelegatingStatusListener.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/DelegatingStatusListener.kt deleted file mode 100644 index b1bb41d4a..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/DelegatingStatusListener.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.pubnub.internal.v2.callbacks - -import com.pubnub.api.BasePubNub -import com.pubnub.api.PubNub -import com.pubnub.api.models.consumer.PNStatus -import com.pubnub.api.v2.callbacks.StatusListener - -data class DelegatingStatusListener(private val listener: StatusListener) : com.pubnub.internal.v2.callbacks.StatusListenerCore { - override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, - status: PNStatus, - ) { - listener.status(pubnub as PubNub, status) - } -} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallback.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallback.kt deleted file mode 100644 index 4ff169223..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallback.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.pubnub.internal.v2.callbacks - -import com.pubnub.api.BasePubNub -import com.pubnub.api.PubNub -import com.pubnub.api.callbacks.SubscribeCallback -import com.pubnub.api.models.consumer.PNStatus - -data class DelegatingSubscribeCallback(private val listener: SubscribeCallback) : - DelegatingEventListener(listener), - com.pubnub.internal.callbacks.SubscribeCallback { - override fun status( - pubnub: BasePubNub<*, *, *, *, *, *, *, *>, - status: PNStatus, - ) { - listener.status(pubnub as PubNub, status) - } -} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImpl.kt new file mode 100644 index 000000000..6d77ccaa6 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImpl.kt @@ -0,0 +1,148 @@ +package com.pubnub.internal.v2.callbacks + +import com.pubnub.api.PubNub +import com.pubnub.api.callbacks.Listener +import com.pubnub.api.models.consumer.pubsub.PNEvent +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.api.models.consumer.pubsub.PNSignalResult +import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult +import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult +import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult +import com.pubnub.api.v2.callbacks.EventEmitter +import com.pubnub.api.v2.callbacks.EventListener +import com.pubnub.internal.managers.AnnouncementCallback +import com.pubnub.internal.managers.AnnouncementEnvelope +import org.jetbrains.annotations.TestOnly +import java.util.concurrent.CopyOnWriteArraySet + +class EventEmitterImpl( + override val phase: AnnouncementCallback.Phase, + private val accepts: (AnnouncementEnvelope) -> Boolean = { true }, +) : EventEmitter, AnnouncementCallback { + @get:TestOnly + val listeners = CopyOnWriteArraySet() + + override fun addListener(listener: EventListener) { + listeners.add(listener) + } + + override fun removeListener(listener: Listener) { + listeners.remove(listener) + } + + override fun removeAllListeners() { + listeners.clear() + } + + // EventEmitter + fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + listeners.forEach { + it.message(pubnub, pnMessageResult) + } + } + + fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + listeners.forEach { + it.presence(pubnub, pnPresenceEventResult) + } + } + + fun signal( + pubnub: PubNub, + pnSignalResult: PNSignalResult, + ) { + listeners.forEach { + it.signal(pubnub, pnSignalResult) + } + } + + fun messageAction( + pubnub: PubNub, + pnMessageActionResult: PNMessageActionResult, + ) { + listeners.forEach { + it.messageAction(pubnub, pnMessageActionResult) + } + } + + fun objects( + pubnub: PubNub, + objectEvent: PNObjectEventResult, + ) { + listeners.forEach { + it.objects(pubnub, objectEvent) + } + } + + fun file( + pubnub: PubNub, + pnFileEventResult: PNFileEventResult, + ) { + listeners.forEach { + it.file(pubnub, pnFileEventResult) + } + } + + // AnnouncementCallback + + override fun message( + pubnub: PubNub, + envelope: AnnouncementEnvelope, + ) { + if (accepts(envelope)) { + message(pubnub, envelope.event) + } + } + + override fun presence( + pubnub: PubNub, + envelope: AnnouncementEnvelope, + ) { + if (accepts(envelope)) { + presence(pubnub, envelope.event) + } + } + + override fun signal( + pubnub: PubNub, + envelope: AnnouncementEnvelope, + ) { + if (accepts(envelope)) { + signal(pubnub, envelope.event) + } + } + + override fun messageAction( + pubnub: PubNub, + envelope: AnnouncementEnvelope, + ) { + if (accepts(envelope)) { + messageAction(pubnub, envelope.event) + } + } + + override fun objects( + pubnub: PubNub, + envelope: AnnouncementEnvelope, + ) { + if (accepts(envelope)) { + objects(pubnub, envelope.event) + } + } + + override fun file( + pubnub: PubNub, + envelope: AnnouncementEnvelope, + ) { + if (accepts(envelope)) { + file(pubnub, envelope.event) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventListener.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventListener.kt new file mode 100644 index 000000000..11e5c1d48 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/EventListener.kt @@ -0,0 +1,84 @@ +// package com.pubnub.internal.v2.callbacks +// +// import com.pubnub.api.PubNub +// import com.pubnub.api.callbacks.Listener +// import com.pubnub.api.models.consumer.pubsub.PNMessageResult +// import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +// import com.pubnub.api.models.consumer.pubsub.PNSignalResult +// import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult +// import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult +// import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult +// +// interface EventListener : Listener { +// /** +// * Receive messages at subscribed channels. +// * +// * @see [PubNub.subscribe] +// * +// * @param pubnub The client instance which has this listener attached. +// * @param event Wrapper around the actual message content. +// */ +// fun message( +// pubnub: PubNub, +// event: PNMessageResult, +// ) {} +// +// /** +// * Receive presence events for channels subscribed with presence enabled via +// * passing [com.pubnub.api.v2.subscriptions.SubscriptionOptions.receivePresenceEvents] +// * in [com.pubnub.api.v2.entities.BaseChannel.subscription]. +// * +// * @param pubnub The client instance which has this listener attached. +// * @param event Wrapper around a presence event. +// */ +// fun presence( +// pubnub: PubNub, +// event: PNPresenceEventResult, +// ) {} +// +// /** +// * Receive signals at subscribed channels. +// * +// * @see [PubNub.signal] +// * +// * @param pubnub The client instance which has this listener attached. +// * @param event Wrapper around a signal event. +// */ +// fun signal( +// pubnub: PubNub, +// event: PNSignalResult, +// ) {} +// +// /** +// * Receive message actions for messages in subscribed channels. +// * +// * @param pubnub The client instance which has this listener attached. +// * @param event Wrapper around a message action event. +// */ +// fun messageAction( +// pubnub: PubNub, +// event: PNMessageActionResult, +// ) {} +// +// /** +// * Receive channel metadata and UUID metadata events in subscribed channels. +// * +// * @param pubnub The client instance which has this listener attached. +// * @param event Wrapper around the object event. +// */ +// fun objects( +// pubnub: PubNub, +// event: PNObjectEventResult, +// ) {} +// +// /** +// * Receive file events in subscribed channels. +// * +// * @param pubnub The client instance which has this listener attached. +// * @param event Wrapper around the file event. +// */ +// fun file( +// pubnub: PubNub, +// event: PNFileEventResult, +// ) {} +// } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/StatusListener.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/StatusListener.kt new file mode 100644 index 000000000..63410b1c6 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/callbacks/StatusListener.kt @@ -0,0 +1,20 @@ +// package com.pubnub.internal.v2.callbacks +// +// import com.pubnub.api.PubNub +// import com.pubnub.api.callbacks.Listener +// import com.pubnub.api.models.consumer.PNStatus +// +// interface StatusListener : Listener { +// /** +// * Receive status updates from the PubNub client. +// * +// * @see [PNStatus] +// * +// * @param pubnub The client instance which has this listener attached. +// * @param status Wrapper around the actual message content. +// */ +// fun status( +// pubnub: PubNub, +// status: PNStatus, +// ) +// } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/ChannelGroupImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/ChannelGroupImpl.kt index ae5f572ef..2b61f7762 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/ChannelGroupImpl.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/ChannelGroupImpl.kt @@ -1,15 +1,60 @@ package com.pubnub.internal.v2.entities -import com.pubnub.api.v2.callbacks.EventListener import com.pubnub.api.v2.entities.ChannelGroup -import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.ReceivePresenceEventsImpl +import com.pubnub.api.v2.subscriptions.SubscriptionOptions import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.subscribe.PRESENCE_CHANNEL_SUFFIX import com.pubnub.internal.v2.subscription.SubscriptionImpl -class ChannelGroupImpl(pubnub: PubNubImpl, channelGroupName: ChannelGroupName) : - BaseChannelGroupImpl( - pubnub.pubNubCore, - channelGroupName, - { channels, channelGroups, options -> SubscriptionImpl(pubnub, channels, channelGroups, options) }, - ), - ChannelGroup +open class ChannelGroupImpl(val pubnub: PubNubImpl, val channelGroupName: ChannelGroupName) : ChannelGroup { + override val name: String = channelGroupName.id + + override fun subscription(options: SubscriptionOptions): SubscriptionImpl { + val channelGroups = + buildSet { + add(channelGroupName) + if (options.allOptions.filterIsInstance().isNotEmpty()) { + add(channelGroupName.withPresence) + } + } + return SubscriptionImpl( + pubnub, + emptySet(), + channelGroups, + SubscriptionOptions.filter { result -> + channelGroups.any { it.id == result.subscription } + } + options, + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other !is ChannelGroupImpl) { + return false + } + + if (pubnub != other.pubnub) { + return false + } + if (name != other.name) { + return false + } + + return true + } + + override fun hashCode(): Int { + var result = pubnub.hashCode() + result = 31 * result + name.hashCode() + return result + } +} + +@JvmInline +value class ChannelGroupName(val id: String) { + val withPresence get() = ChannelGroupName("${this.id}$PRESENCE_CHANNEL_SUFFIX") + val isPresence get() = id.endsWith(PRESENCE_CHANNEL_SUFFIX) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/ChannelImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/ChannelImpl.kt index 56df3191c..b1889522c 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/ChannelImpl.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/ChannelImpl.kt @@ -4,21 +4,72 @@ import com.pubnub.api.endpoints.files.DeleteFile import com.pubnub.api.endpoints.files.SendFile import com.pubnub.api.endpoints.pubsub.Publish import com.pubnub.api.endpoints.pubsub.Signal -import com.pubnub.api.v2.callbacks.EventListener import com.pubnub.api.v2.entities.Channel -import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.ReceivePresenceEventsImpl +import com.pubnub.api.v2.subscriptions.SubscriptionOptions import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.subscribe.PRESENCE_CHANNEL_SUFFIX import com.pubnub.internal.v2.subscription.SubscriptionImpl import java.io.InputStream -class ChannelImpl(pubnub: PubNubImpl, channelName: ChannelName) : - BaseChannelImpl( - pubnub.pubNubCore, - channelName, - { channels, channelGroups, options -> SubscriptionImpl(pubnub, channels, channelGroups, options) }, - ), - Channel { - private val pubNubImpl: PubNubImpl = pubnub +open class ChannelImpl(val pubNubImpl: PubNubImpl, val channelName: ChannelName) : Channel { + override val name: String = channelName.id + + override fun subscription(options: SubscriptionOptions): SubscriptionImpl { + val channels = + buildSet { + add(channelName) + if (options.allOptions.filterIsInstance().isNotEmpty()) { + add(channelName.withPresence) + } + } + return SubscriptionImpl( + pubNubImpl, + channels, + emptySet(), + SubscriptionOptions.filter { result -> + // simple channel name or presence channel + if (channels.any { it.id == result.channel }) { + return@filter true + } + + // wildcard channels + if (name.endsWith(".*") && + ( + result.subscription == name || + result.channel.startsWith(name.substringBeforeLast("*")) + ) + ) { + return@filter true + } + return@filter false + } + options, + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other !is ChannelImpl) { + return false + } + + if (pubNubImpl != other.pubNubImpl) { + return false + } + if (name != other.name) { + return false + } + + return true + } + + override fun hashCode(): Int { + var result = pubNubImpl.hashCode() + result = 31 * result + name.hashCode() + return result + } override fun publish( message: Any, @@ -58,3 +109,9 @@ class ChannelImpl(pubnub: PubNubImpl, channelName: ChannelName) : return pubNubImpl.deleteFile(channelName.id, fileName, fileId) } } + +@JvmInline +value class ChannelName(val id: String) { + val withPresence get() = ChannelName("${this.id}$PRESENCE_CHANNEL_SUFFIX") + val isPresence get() = id.endsWith(PRESENCE_CHANNEL_SUFFIX) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/ChannelMetadataImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/ChannelMetadataImpl.kt index e2a90e1ca..5d304340c 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/ChannelMetadataImpl.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/ChannelMetadataImpl.kt @@ -1,15 +1,47 @@ package com.pubnub.internal.v2.entities -import com.pubnub.api.v2.callbacks.EventListener import com.pubnub.api.v2.entities.ChannelMetadata -import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.SubscriptionOptions import com.pubnub.internal.PubNubImpl import com.pubnub.internal.v2.subscription.SubscriptionImpl -class ChannelMetadataImpl(pubnub: PubNubImpl, channelName: ChannelName) : - BaseChannelMetadataImpl( - pubnub.pubNubCore, - channelName, - { channels, channelGroups, options -> SubscriptionImpl(pubnub, channels, channelGroups, options) }, - ), - ChannelMetadata +open class ChannelMetadataImpl(val pubnub: PubNubImpl, val channelName: ChannelName) : ChannelMetadata { + override val id: String = channelName.id + + override fun subscription(options: SubscriptionOptions): SubscriptionImpl { + val channels = setOf(channelName) + return SubscriptionImpl( + pubnub, + channels, + emptySet(), + SubscriptionOptions.filter { result -> + // simple channel name or presence channel + channels.any { it.id == result.channel } + } + options, + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other !is ChannelMetadataImpl) { + return false + } + + if (pubnub != other.pubnub) { + return false + } + if (id != other.id) { + return false + } + + return true + } + + override fun hashCode(): Int { + var result = pubnub.hashCode() + result = 31 * result + id.hashCode() + return result + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/UserMetadataImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/UserMetadataImpl.kt index 7dada194c..20beeee72 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/UserMetadataImpl.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/entities/UserMetadataImpl.kt @@ -1,15 +1,47 @@ package com.pubnub.internal.v2.entities -import com.pubnub.api.v2.callbacks.EventListener import com.pubnub.api.v2.entities.UserMetadata -import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.SubscriptionOptions import com.pubnub.internal.PubNubImpl import com.pubnub.internal.v2.subscription.SubscriptionImpl -class UserMetadataImpl(pubnub: PubNubImpl, channelName: ChannelName) : - BaseUserMetadataImpl( - pubnub.pubNubCore, - channelName, - { channels, channelGroups, options -> SubscriptionImpl(pubnub, channels, channelGroups, options) }, - ), - UserMetadata +open class UserMetadataImpl(val pubnub: PubNubImpl, val channelName: ChannelName) : UserMetadata { + override val id: String = channelName.id + + override fun subscription(options: SubscriptionOptions): SubscriptionImpl { + val channels = setOf(channelName) + return SubscriptionImpl( + pubnub, + channels, + emptySet(), + SubscriptionOptions.filter { result -> + // simple channel name or presence channel + channels.any { it.id == result.channel } + } + options, + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other !is UserMetadataImpl) { + return false + } + + if (pubnub != other.pubnub) { + return false + } + if (id != other.id) { + return false + } + + return true + } + + override fun hashCode(): Int { + var result = pubnub.hashCode() + result = 31 * result + id.hashCode() + return result + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/subscription/EmitterHelper.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/subscription/EmitterHelper.kt index c669c0a09..56cd346e5 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/subscription/EmitterHelper.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/subscription/EmitterHelper.kt @@ -7,59 +7,55 @@ import com.pubnub.api.models.consumer.pubsub.PNSignalResult import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult -import com.pubnub.api.v2.callbacks.BaseEventEmitter +import com.pubnub.api.v2.callbacks.EventEmitter import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.internal.v2.callbacks.DelegatingEventListener -import com.pubnub.internal.v2.callbacks.EventListenerCore -class EmitterHelper(eventEmitter: BaseEventEmitter) { +class EmitterHelper(eventEmitter: EventEmitter) { init { eventEmitter.addListener( - DelegatingEventListener( - object : EventListener { - override fun message( - pubnub: PubNub, - result: PNMessageResult, - ) { - onMessage?.invoke(result) - } + object : EventListener { + override fun message( + pubnub: PubNub, + result: PNMessageResult, + ) { + onMessage?.invoke(result) + } - override fun presence( - pubnub: PubNub, - result: PNPresenceEventResult, - ) { - onPresence?.invoke(result) - } + override fun presence( + pubnub: PubNub, + result: PNPresenceEventResult, + ) { + onPresence?.invoke(result) + } - override fun signal( - pubnub: PubNub, - result: PNSignalResult, - ) { - onSignal?.invoke(result) - } + override fun signal( + pubnub: PubNub, + result: PNSignalResult, + ) { + onSignal?.invoke(result) + } - override fun messageAction( - pubnub: PubNub, - result: PNMessageActionResult, - ) { - onMessageAction?.invoke(result) - } + override fun messageAction( + pubnub: PubNub, + result: PNMessageActionResult, + ) { + onMessageAction?.invoke(result) + } - override fun objects( - pubnub: PubNub, - result: PNObjectEventResult, - ) { - onObjects?.invoke(result) - } + override fun objects( + pubnub: PubNub, + result: PNObjectEventResult, + ) { + onObjects?.invoke(result) + } - override fun file( - pubnub: PubNub, - result: PNFileEventResult, - ) { - onFile?.invoke(result) - } - }, - ), + override fun file( + pubnub: PubNub, + result: PNFileEventResult, + ) { + onFile?.invoke(result) + } + }, ) } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/subscription/SubscriptionImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/subscription/SubscriptionImpl.kt index d86084c71..f0b0b63cd 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/subscription/SubscriptionImpl.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/subscription/SubscriptionImpl.kt @@ -1,7 +1,7 @@ package com.pubnub.internal.v2.subscription import com.pubnub.api.callbacks.Listener -import com.pubnub.api.callbacks.SubscribeCallback +import com.pubnub.api.models.consumer.pubsub.PNEvent import com.pubnub.api.models.consumer.pubsub.PNMessageResult import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult import com.pubnub.api.models.consumer.pubsub.PNSignalResult @@ -9,41 +9,120 @@ import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.subscriptions.BaseSubscription -import com.pubnub.api.v2.subscriptions.BaseSubscriptionSet +import com.pubnub.api.v2.subscriptions.EmptyOptions +import com.pubnub.api.v2.subscriptions.FilterImpl import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.SubscriptionCursor import com.pubnub.api.v2.subscriptions.SubscriptionOptions import com.pubnub.api.v2.subscriptions.SubscriptionSet import com.pubnub.internal.PubNubImpl -import com.pubnub.internal.v2.callbacks.DelegatingEventListener -import com.pubnub.internal.v2.callbacks.DelegatingSubscribeCallback +import com.pubnub.internal.managers.AnnouncementCallback +import com.pubnub.internal.managers.AnnouncementEnvelope +import com.pubnub.internal.v2.callbacks.EventEmitterImpl import com.pubnub.internal.v2.entities.ChannelGroupName import com.pubnub.internal.v2.entities.ChannelName -class SubscriptionImpl( - private val pubnub: PubNubImpl, - channels: Set, - channelGroups: Set, - options: SubscriptionOptions, -) : Subscription, BaseSubscriptionImpl(pubnub.pubNubCore, channels, channelGroups, options) { - override fun addListener(listener: EventListener) { - addListener(DelegatingEventListener(listener)) - } +interface SubscriptionInternal : Subscription { + val pubnub: PubNubImpl - override fun removeListener(listener: Listener) { - when (listener) { - is SubscribeCallback -> { - super.removeListener(DelegatingSubscribeCallback(listener)) - } + fun onSubscriptionActive(cursor: SubscriptionCursor) - is EventListener -> { - super.removeListener(DelegatingEventListener(listener)) - } + fun onSubscriptionInactive() - else -> { - super.removeListener(listener) + val channels: Set + val channelGroups: Set +} + +open class SubscriptionImpl( + override val pubnub: PubNubImpl, + channels: Set = emptySet(), + channelGroups: Set = emptySet(), + val options: SubscriptionOptions = EmptyOptions, + eventEmitterFactory: (SubscriptionImpl) -> EventEmitterImpl = { subscriptionImpl -> + EventEmitterImpl(AnnouncementCallback.Phase.SUBSCRIPTION, subscriptionImpl::accepts) + } +) : SubscriptionInternal { + @Volatile + var isActive = false + @Synchronized + set(newValue) { + if (!field && newValue) { // wasn't active && is now active + pubnub.listenerManager.addAnnouncementCallback(eventEmitter) + } else if (field && !newValue) { // was active && no longer active + pubnub.listenerManager.removeAnnouncementCallback(eventEmitter) } + field = newValue } + + override val channels: Set = channels.toSet() + override val channelGroups: Set = channelGroups.toSet() + + private val filters: List = options.allOptions.filterIsInstance() + + /** + * To ensure that events are delivered with timestamps growing monotonically, + * we will set this to the highest received timestamp and compare incoming messages against it. + * + * This will be reset on subscribe(cursor) with the value from the SubscriptionCursor. + */ + private var lastTimetoken: Long = 0L + + protected val eventEmitter = eventEmitterFactory(this) + + fun accepts(envelope: AnnouncementEnvelope): Boolean { + val event = envelope.event + val accepted = isActive && filters.all { filter -> filter.predicate(event) } && checkAndUpdateTimetoken(event) + if (accepted) { + envelope.acceptedBy += this@SubscriptionImpl + } + return accepted + } + + private fun checkAndUpdateTimetoken(result: PNEvent): Boolean { + result.timetoken?.let { resultTimetoken -> + if (resultTimetoken <= lastTimetoken) { + return false + } else { + lastTimetoken = resultTimetoken + return true + } + } ?: return false + } + + override fun subscribe(cursor: SubscriptionCursor) { + onSubscriptionActive(cursor) + pubnub.subscribe(this, cursor = cursor) + } + + override fun onSubscriptionActive(cursor: SubscriptionCursor) { + lastTimetoken = cursor.timetoken + isActive = true + } + + override fun unsubscribe() { + onSubscriptionInactive() + pubnub.unsubscribe(this) + } + + override fun onSubscriptionInactive() { + lastTimetoken = 0L + isActive = false + } + + override fun close() { + unsubscribe() + } + + override fun addListener(listener: EventListener) { + eventEmitter.addListener(listener) + } + + override fun removeAllListeners() { + eventEmitter.removeAllListeners() + } + + override fun removeListener(listener: Listener) { + eventEmitter.removeListener(listener) } private val emitterHelper = EmitterHelper(eventEmitter) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/subscription/SubscriptionSetImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/subscription/SubscriptionSetImpl.kt index c783d87f7..eac58eb25 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/subscription/SubscriptionSetImpl.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/subscription/SubscriptionSetImpl.kt @@ -1,7 +1,6 @@ package com.pubnub.internal.v2.subscription import com.pubnub.api.callbacks.Listener -import com.pubnub.api.callbacks.SubscribeCallback import com.pubnub.api.models.consumer.pubsub.PNMessageResult import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult import com.pubnub.api.models.consumer.pubsub.PNSignalResult @@ -10,41 +9,82 @@ import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResu import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult import com.pubnub.api.v2.callbacks.EventListener import com.pubnub.api.v2.subscriptions.Subscription +import com.pubnub.api.v2.subscriptions.SubscriptionCursor import com.pubnub.api.v2.subscriptions.SubscriptionSet -import com.pubnub.internal.PubNubCore -import com.pubnub.internal.v2.callbacks.DelegatingEventListener -import com.pubnub.internal.v2.callbacks.DelegatingSubscribeCallback - -class SubscriptionSetImpl( - pubnub: PubNubCore, - initialSubscriptions: Set, -) : SubscriptionSet, BaseSubscriptionSetImpl(pubnub, initialSubscriptions) { - override operator fun plusAssign(subscription: Subscription) { - add(subscription) +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.managers.AnnouncementCallback +import com.pubnub.internal.managers.AnnouncementEnvelope +import com.pubnub.internal.v2.callbacks.EventEmitterImpl +import java.util.concurrent.CopyOnWriteArraySet +import kotlin.collections.remove + +open class SubscriptionSetImpl( + val pubnub: PubNubImpl, + initialSubscriptions: Set, +) : SubscriptionSet { + private val _subscriptions: CopyOnWriteArraySet = CopyOnWriteArraySet() + override val subscriptions: Set get() = _subscriptions.toSet() as Set + private val eventEmitter = EventEmitterImpl(AnnouncementCallback.Phase.SET, ::accepts) + + private fun accepts(envelope: AnnouncementEnvelope<*>) = subscriptions.any { subscription -> subscription in envelope.acceptedBy } + + init { + require(initialSubscriptions.all { it.pubnub == pubnub }) { ERROR_WRONG_PUBNUB_INSTANCE } + _subscriptions.addAll(initialSubscriptions) + pubnub.listenerManager.addAnnouncementCallback(eventEmitter) } - override fun minusAssign(subscription: Subscription) { - remove(subscription) + override fun add(subscription: Subscription) { + require(subscription is SubscriptionInternal) { ERROR_SUBSCRIPTION_WRONG_CLASS } + require(subscription.pubnub == pubnub) { ERROR_WRONG_PUBNUB_INSTANCE } + _subscriptions.add(subscription) + } + + fun addInternal(subscriptionInternal: SubscriptionInternal) { + _subscriptions.add(subscriptionInternal) + } + + override fun remove(subscription: Subscription) { + _subscriptions.remove(subscription) + } + + fun removeInternal(subscriptionInternal: SubscriptionInternal) { + _subscriptions.remove(subscriptionInternal) + } + + override fun subscribe(cursor: SubscriptionCursor) { + _subscriptions.forEach { it.onSubscriptionActive(cursor) } + pubnub.subscribe(*_subscriptions.toTypedArray(), cursor = cursor) + } + + override fun unsubscribe() { + _subscriptions.forEach { it.onSubscriptionInactive() } + pubnub.unsubscribe(*_subscriptions.toTypedArray()) + } + + override fun close() { + unsubscribe() + pubnub.listenerManager.removeAnnouncementCallback(eventEmitter) } override fun addListener(listener: EventListener) { - addListener(DelegatingEventListener(listener)) + eventEmitter.addListener(listener) } override fun removeListener(listener: Listener) { - when (listener) { - is SubscribeCallback -> { - super.removeListener(DelegatingSubscribeCallback(listener)) - } + eventEmitter.removeListener(listener) + } + + override fun removeAllListeners() { + eventEmitter.removeAllListeners() + } - is EventListener -> { - super.removeListener(DelegatingEventListener(listener)) - } + override operator fun plusAssign(subscription: Subscription) { + add(subscription) + } - else -> { - super.removeListener(listener) - } - } + override fun minusAssign(subscription: Subscription) { + remove(subscription) } private val emitterHelper = EmitterHelper(eventEmitter) @@ -55,3 +95,9 @@ class SubscriptionSetImpl( override var onObjects: ((PNObjectEventResult) -> Unit)? by emitterHelper::onObjects override var onFile: ((PNFileEventResult) -> Unit)? by emitterHelper::onFile } + +private const val ERROR_SUBSCRIPTION_WRONG_CLASS = + "Only Subscriptions returned from objects created" + + "through the PubNub instance and their methods, such as channel(...).subscriptions() are supported." +private const val ERROR_WRONG_PUBNUB_INSTANCE = + "Adding Subscriptions from another PubNub instance to a SubscriptionSet is not allowed." diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/vendor/FileEncryptionUtil.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/vendor/FileEncryptionUtil.kt new file mode 100644 index 000000000..d3c923549 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/vendor/FileEncryptionUtil.kt @@ -0,0 +1,177 @@ +package com.pubnub.internal.vendor + +import com.pubnub.api.PubNubException +import com.pubnub.internal.PubNubImpl +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStream +import java.io.UnsupportedEncodingException +import java.security.InvalidAlgorithmParameterException +import java.security.InvalidKeyException +import java.security.NoSuchAlgorithmException +import java.security.SecureRandom +import java.security.spec.AlgorithmParameterSpec +import java.util.Locale +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +object FileEncryptionUtil { + private const val IV_SIZE_BYTES = 16 + const val ENCODING_UTF_8 = "UTF-8" + const val CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding" + + /** + * @see [PubNubImpl.encryptInputStream] + */ + @Throws(PubNubException::class) + fun encrypt( + inputStream: InputStream, + cipherKey: String, + ): InputStream { + return encryptToBytes(inputStream.readBytes(), cipherKey).inputStream() + } + + /** + * @see [PubNubImpl.decryptInputStream] + */ + @Throws(PubNubException::class) + fun decrypt( + inputStream: InputStream, + cipherKey: String, + ): InputStream { + return try { + val keyBytes = keyBytes(cipherKey) + val (ivBytes, dataToDecrypt) = loadIvAndDataFromInputStream(inputStream) + val decryptionCipher = decryptionCipher(keyBytes, ivBytes) + val decryptedBytes = decryptionCipher.doFinal(dataToDecrypt) + ByteArrayInputStream(decryptedBytes) + } catch (e: Exception) { + when (e) { + is NoSuchAlgorithmException, + is InvalidAlgorithmParameterException, + is NoSuchPaddingException, + is InvalidKeyException, + is IOException, + is IllegalBlockSizeException, + is BadPaddingException, + -> { + throw PubNubException(errorMessage = e.message) + } + + else -> throw e + } + } + } + + @Throws(PubNubException::class) + internal fun encryptToBytes( + bytesToEncrypt: ByteArray, + cipherKey: String, + ): ByteArray { + try { + ByteArrayOutputStream().use { byteArrayOutputStream -> + val randomIvBytes = randomIv() + byteArrayOutputStream.write(randomIvBytes) + + val keyBytes = keyBytes(cipherKey) + val encryptionCipher = encryptionCipher(keyBytes, randomIvBytes) + byteArrayOutputStream.write(encryptionCipher.doFinal(bytesToEncrypt)) + return byteArrayOutputStream.toByteArray() + } + } catch (e: Exception) { + when (e) { + is NoSuchAlgorithmException, + is InvalidAlgorithmParameterException, + is NoSuchPaddingException, + is InvalidKeyException, + is IOException, + is BadPaddingException, + is IllegalBlockSizeException, + -> { + throw PubNubException(errorMessage = e.message) + } + + else -> throw e + } + } + } + + @Throws(IOException::class) + private fun loadIvAndDataFromInputStream(inputStreamToEncrypt: InputStream): Pair { + val ivBytes = ByteArray(IV_SIZE_BYTES) + inputStreamToEncrypt.read(ivBytes, 0, IV_SIZE_BYTES) + return ivBytes to inputStreamToEncrypt.readBytes() + } + + @Throws( + NoSuchAlgorithmException::class, + NoSuchPaddingException::class, + InvalidKeyException::class, + InvalidAlgorithmParameterException::class, + ) + private fun encryptionCipher( + keyBytes: ByteArray, + ivBytes: ByteArray, + ): Cipher { + return cipher(keyBytes, ivBytes, Cipher.ENCRYPT_MODE) + } + + @Throws( + NoSuchAlgorithmException::class, + NoSuchPaddingException::class, + InvalidKeyException::class, + InvalidAlgorithmParameterException::class, + ) + private fun decryptionCipher( + keyBytes: ByteArray, + ivBytes: ByteArray, + ): Cipher { + return cipher(keyBytes, ivBytes, Cipher.DECRYPT_MODE) + } + + @Throws( + NoSuchAlgorithmException::class, + NoSuchPaddingException::class, + InvalidKeyException::class, + InvalidAlgorithmParameterException::class, + ) + private fun cipher( + keyBytes: ByteArray, + ivBytes: ByteArray, + mode: Int, + ): Cipher { + val cipher = Cipher.getInstance(CIPHER_TRANSFORMATION) + val iv: AlgorithmParameterSpec = IvParameterSpec(ivBytes) + val key = SecretKeySpec(keyBytes, "AES") + cipher.init(mode, key, iv) + return cipher + } + + @Throws(UnsupportedEncodingException::class, PubNubException::class) + private fun keyBytes(cipherKey: String): ByteArray { + return String( + com.pubnub.internal.vendor.Crypto.hexEncode( + com.pubnub.internal.vendor.Crypto.sha256( + cipherKey.toByteArray( + charset(ENCODING_UTF_8), + ), + ), + ), + charset(ENCODING_UTF_8), + ) + .substring(0, 32) + .lowercase(Locale.getDefault()).toByteArray(charset(ENCODING_UTF_8)) + } + + @Throws(NoSuchAlgorithmException::class) + private fun randomIv(): ByteArray { + val randomIv = ByteArray(IV_SIZE_BYTES) + SecureRandom.getInstance("SHA1PRNG").nextBytes(randomIv) + return randomIv + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessor.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessor.kt new file mode 100644 index 000000000..ffcc1e328 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessor.kt @@ -0,0 +1,251 @@ +package com.pubnub.internal.workers + +import com.google.gson.JsonElement +import com.google.gson.JsonNull +import com.pubnub.api.models.consumer.files.PNDownloadableFile +import com.pubnub.api.models.consumer.message_actions.PNMessageAction +import com.pubnub.api.models.consumer.pubsub.BasePubSubResult +import com.pubnub.api.models.consumer.pubsub.PNEvent +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.api.models.consumer.pubsub.PNSignalResult +import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult +import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult +import com.pubnub.api.models.consumer.pubsub.objects.ObjectPayload +import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult +import com.pubnub.api.v2.PNConfiguration +import com.pubnub.api.v2.PNConfiguration.Companion.isValid +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.PubNubUtil +import com.pubnub.internal.extension.tryDecryptMessage +import com.pubnub.internal.managers.DuplicationManager +import com.pubnub.internal.models.consumer.pubsub.objects.PNObjectEventMessage +import com.pubnub.internal.models.consumer.pubsub.objects.toApi +import com.pubnub.internal.models.server.PresenceEnvelope +import com.pubnub.internal.models.server.SubscribeMessage +import com.pubnub.internal.models.server.files.FileUploadNotification +import com.pubnub.internal.services.FilesService +import com.pubnub.internal.subscribe.PRESENCE_CHANNEL_SUFFIX +import org.slf4j.LoggerFactory + +internal class SubscribeMessageProcessor( + private val pubnub: PubNubImpl, + private val duplicationManager: DuplicationManager, +) { + private val log = LoggerFactory.getLogger("SubscribeMessageProcessor") + + companion object { + internal const val TYPE_MESSAGE = 0 + internal const val TYPE_SIGNAL = 1 + internal const val TYPE_OBJECT = 2 + internal const val TYPE_MESSAGE_ACTION = 3 + internal const val TYPE_FILES = 4 + } + + fun processIncomingPayload(message: SubscribeMessage): PNEvent? { + if (message.channel == null) { + return null + } + + val channel = message.channel + var subscriptionMatch = message.subscriptionMatch + val publishMetaData = message.publishMetaData + + if (channel == subscriptionMatch) { + subscriptionMatch = null + } + + if (pubnub.configuration.dedupOnSubscribe) { + if (duplicationManager.isDuplicate(message)) { + return null + } else { + duplicationManager.addEntry(message) + } + } + + if (message.channel.endsWith(PRESENCE_CHANNEL_SUFFIX)) { + val presencePayload = pubnub.mapper.convertValue(message.payload, PresenceEnvelope::class.java) + val strippedPresenceChannel = PubNubUtil.replaceLast(channel, PRESENCE_CHANNEL_SUFFIX, "") + val strippedPresenceSubscription = + subscriptionMatch?.let { + PubNubUtil.replaceLast(it, PRESENCE_CHANNEL_SUFFIX, "") + } + + val isHereNowRefresh = message.payload?.asJsonObject?.get("here_now_refresh") + + return PNPresenceEventResult( + event = presencePayload.action, + uuid = presencePayload.uuid, + timestamp = presencePayload.timestamp, + occupancy = presencePayload.occupancy, + state = presencePayload.data, + channel = strippedPresenceChannel, + subscription = strippedPresenceSubscription, + timetoken = publishMetaData?.publishTimetoken, + join = getDelta(message.payload?.asJsonObject?.get("join")), + leave = getDelta(message.payload?.asJsonObject?.get("leave")), + timeout = getDelta(message.payload?.asJsonObject?.get("timeout")), + hereNowRefresh = isHereNowRefresh != null && isHereNowRefresh.asBoolean, + ) + } else { + val (extractedMessage, error) = + message.payload?.tryDecryptMessage(pubnub.configuration.cryptoModule, pubnub.mapper) + ?: (null to null) + + if (extractedMessage == null) { + log.debug("unable to parse payload on #processIncomingMessages") + } + + val result = + BasePubSubResult( + channel = channel, + subscription = subscriptionMatch, + timetoken = publishMetaData?.publishTimetoken, + userMetadata = message.userMetadata, + publisher = message.issuingClientId, + ) + + return when (message.type) { + null -> { + PNMessageResult(result, extractedMessage!!, error) + } + + TYPE_MESSAGE -> { + PNMessageResult(result, extractedMessage!!, error) + } + + TYPE_SIGNAL -> { + PNSignalResult(result, extractedMessage!!) + } + + TYPE_OBJECT -> { + PNObjectEventResult( + result, + pubnub.mapper.convertValue( + extractedMessage, + PNObjectEventMessage::class.java, + ).toApi(), + ) + } + + TYPE_MESSAGE_ACTION -> { + val objectPayload = pubnub.mapper.convertValue(extractedMessage, ObjectPayload::class.java) + val data = objectPayload.data.asJsonObject + if (!data.has("uuid")) { + data.addProperty("uuid", result.publisher) + } + PNMessageActionResult( + result = result, + event = objectPayload.event, + data = pubnub.mapper.convertValue(data, PNMessageAction::class.java), + ) + } + + TYPE_FILES -> { + val fileUploadNotification = + pubnub.mapper.convertValue( + extractedMessage, + FileUploadNotification::class.java, + ) + PNFileEventResult( + channel = message.channel, + message = fileUploadNotification.message, + file = + PNDownloadableFile( + id = fileUploadNotification.file.id, + name = fileUploadNotification.file.name, + url = + buildFileUrl( + message.channel, + fileUploadNotification.file.id, + fileUploadNotification.file.name, + ), + ), + publisher = message.issuingClientId, + timetoken = result.timetoken, + jsonMessage = + fileUploadNotification.message?.let { pubnub.mapper.toJsonTree(it) } + ?: JsonNull.INSTANCE, + error = error, + ) + } + + else -> null + } + } + } + + private val formatFriendlyGetFileUrl = "%s" + FilesService.GET_FILE_URL.replace("\\{.*?\\}".toRegex(), "%s") + + private fun buildFileUrl( + channel: String, + fileId: String, + fileName: String, + ): String { + val basePath: String = + java.lang.String.format( + formatFriendlyGetFileUrl, + pubnub.baseUrl(), + pubnub.configuration.subscribeKey, + channel, + fileId, + fileName, + ) + val queryParams = ArrayList() + val authKey = + if (pubnub.configuration.authKey.isValid()) { + pubnub.configuration.authKey + } else { + null + } + + if (PubNubUtil.shouldSignRequest(pubnub.configuration)) { + val timestamp: Int = PubNubImpl.timestamp() + val signature: String = generateSignature(pubnub.configuration, basePath, authKey, timestamp) + queryParams.add(PubNubUtil.TIMESTAMP_QUERY_PARAM_NAME + "=" + timestamp) + queryParams.add(PubNubUtil.SIGNATURE_QUERY_PARAM_NAME + "=" + signature) + } + + authKey?.run { queryParams.add(PubNubUtil.AUTH_QUERY_PARAM_NAME + "=" + authKey) } + + return if (queryParams.isEmpty()) { + basePath + } else { + "$basePath?${queryParams.joinToString(separator = "&")}" + } + } + + private fun generateSignature( + configuration: PNConfiguration, + url: String, + authKey: String?, + timestamp: Int, + ): String { + val queryParams = mutableMapOf() + if (authKey != null) { + queryParams["auth"] = authKey + } + return PubNubUtil.generateSignature( + url, + queryParams, + "get", + null, + timestamp, + configuration.subscribeKey, + configuration.publishKey, + configuration.secretKey, + ) + } + + private fun getDelta(delta: JsonElement?): List { + val list = mutableListOf() + delta?.let { + it.asJsonArray.forEach { item: JsonElement? -> + item?.let { + list.add(it.asString) + } + } + } + return list + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/BasePNConfigurationImplTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/BasePNConfigurationImplTest.kt new file mode 100644 index 000000000..77490bffe --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/BasePNConfigurationImplTest.kt @@ -0,0 +1,16 @@ +package com.pubnub.api + +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.v2.PNConfigurationImpl +import org.junit.Assert.assertEquals +import org.junit.Test + +class PNConfigurationImplTest { + @Test + fun testDefaultTimeoutValues() { + val p = PubNubImpl(PNConfigurationImpl(userId = UserId(PubNubImpl.generateUUID()))) + assertEquals(300, p.configuration.presenceTimeout) + assertEquals(0, p.configuration.heartbeatInterval) + p.forceDestroy() + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/Keys.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/Keys.kt new file mode 100644 index 000000000..ed0a6e30b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/Keys.kt @@ -0,0 +1,24 @@ +package com.pubnub.api + +import org.aeonbits.owner.Config +import org.aeonbits.owner.ConfigFactory + +@Config.Sources("file:test.properties") +interface KeysConfig : Config { + @get:Config.Key("subKey") + val subscribeKey: String + + @get:Config.Key("pubKey") + val publishKey: String + + @get:Config.Key("pamSubKey") + val pamSubKey: String + + @get:Config.Key("pamPubKey") + val pamPubKey: String + + @get:Config.Key("pamSecKey") + val pamSecKey: String +} + +val Keys: KeysConfig = ConfigFactory.create(KeysConfig::class.java, System.getenv()) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/PNConfigurationTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/PNConfigurationTest.kt deleted file mode 100644 index ebfc3ff30..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/PNConfigurationTest.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.pubnub.api - -import com.pubnub.api.crypto.CryptoModule -import com.pubnub.api.enums.PNReconnectionPolicy -import com.pubnub.api.retry.RetryConfiguration -import com.pubnub.api.v2.PNConfigurationOverride -import com.pubnub.internal.BasePubNubImpl -import org.junit.Assert -import org.junit.Test - -class PNConfigurationTest { - @Suppress("DEPRECATION") - @Test(expected = PubNubException::class) - fun setUUIDToEmptyString() { - PNConfiguration("") - } - - @Suppress("DEPRECATION") - @Test(expected = PubNubException::class) - fun resetUUIDToEmptyString() { - val config = PNConfiguration(BasePubNubImpl.generateUUID()) - config.uuid = "" - } - - @Suppress("DEPRECATION") - @Test - fun resetUUIDToNonEmptyString() { - val config = PNConfiguration(BasePubNubImpl.generateUUID()) - val newUUID = BasePubNubImpl.generateUUID() - config.uuid = newUUID - - Assert.assertEquals(newUUID, config.userId.value) - } - - @Test - fun testDefaultTimeoutValues() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - Assert.assertEquals(300, config.presenceTimeout) - Assert.assertEquals(0, config.heartbeatInterval) - } - - @Test - fun testCustomTimeoutValues1() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - config.presenceTimeout = 100 - Assert.assertEquals(100, config.presenceTimeout) - Assert.assertEquals(49, config.heartbeatInterval) - } - - @Test - fun testCustomTimeoutValues2() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - config.heartbeatInterval = 100 - Assert.assertEquals(300, config.presenceTimeout) - Assert.assertEquals(100, config.heartbeatInterval) - } - - @Test - fun testCustomTimeoutValues3() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - config.heartbeatInterval = 40 - config.presenceTimeout = 50 - Assert.assertEquals(50, config.presenceTimeout) - Assert.assertEquals(24, config.heartbeatInterval) - } - - @Test - fun `reconnection policy should set retry configuration`() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - config.reconnectionPolicy = PNReconnectionPolicy.NONE - Assert.assertTrue(config.retryConfiguration is RetryConfiguration.None) - - config.reconnectionPolicy = PNReconnectionPolicy.LINEAR - Assert.assertTrue(config.retryConfiguration is RetryConfiguration.Linear) - - config.reconnectionPolicy = PNReconnectionPolicy.EXPONENTIAL - Assert.assertTrue(config.retryConfiguration is RetryConfiguration.Exponential) - } - - @Test - fun `maximumReconnectionRetries policy should reset retry configuration`() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - - config.reconnectionPolicy = PNReconnectionPolicy.LINEAR - config.maximumReconnectionRetries = 5 - Assert.assertTrue(config.retryConfiguration is RetryConfiguration.Linear) - Assert.assertEquals(5, (config.retryConfiguration as RetryConfiguration.Linear).maxRetryNumber) - - config.maximumReconnectionRetries = 10 - Assert.assertTrue(config.retryConfiguration is RetryConfiguration.Linear) - Assert.assertEquals(10, (config.retryConfiguration as RetryConfiguration.Linear).maxRetryNumber) - } - - @Test - fun `cryptomodule uses cipherKey when cryptomodule is not set`() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - - config.cryptoModule = null - config.cipherKey = "enigma" - Assert.assertNotNull(config.cryptoModule) - } - - @Test - fun `cryptomodule uses cryptomodule when cryptomodule is set`() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())) - val expectedCryptoModule = CryptoModule.createAesCbcCryptoModule("cipher") - config.cryptoModule = expectedCryptoModule - config.cipherKey = "enigma" - - Assert.assertEquals(expectedCryptoModule, config.cryptoModule) - } - - @Test - fun `create config override from existing config`() { - val config = PNConfiguration(UserId(BasePubNubImpl.generateUUID())).apply { - subscribeKey = "expectedSubscribe" - } - val override = PNConfigurationOverride.from(config).apply { - userId = UserId("override") - }.build() - Assert.assertEquals("override", override.userId.value) - Assert.assertEquals("expectedSubscribe", override.subscribeKey) - } -} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/PubNubUtilTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/PubNubUtilTest.kt new file mode 100644 index 000000000..ca14de604 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/PubNubUtilTest.kt @@ -0,0 +1,13 @@ +package com.pubnub.api + +import com.pubnub.internal.PubNubUtil +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers +import org.junit.Test + +class PubNubUtilTest { + @Test + fun signedSHA256ReturnPrecomputedSingleLine() { + assertThat(PubNubUtil.signSHA256("key", "data"), Matchers.`is`("UDH-PZicbRU3oBP6bnOdojRj_a7DtwE32Cjjas4iG9A=")) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/UserIdTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/UserIdTest.kt new file mode 100644 index 000000000..b50c060ef --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/UserIdTest.kt @@ -0,0 +1,10 @@ +package com.pubnub.api + +import org.junit.Test + +class UserIdTest { + @Test(expected = PubNubException::class) + fun setUserIdToEmptyString() { + UserId("") + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/CryptoModuleTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/CryptoModuleTest.kt new file mode 100644 index 000000000..129f1eb69 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/CryptoModuleTest.kt @@ -0,0 +1,385 @@ +package com.pubnub.api.crypto + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.crypto.cryptor.Cryptor +import com.pubnub.api.crypto.data.EncryptedData +import com.pubnub.api.crypto.data.EncryptedStreamData +import com.pubnub.internal.crypto.CryptoModuleImpl +import com.pubnub.internal.crypto.cryptor.AesCbcCryptor +import com.pubnub.internal.crypto.cryptor.LegacyCryptor +import org.hamcrest.CoreMatchers +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.containsInAnyOrder +import org.hamcrest.Matchers.hasSize +import org.junit.jupiter.api.Assertions.assertArrayEquals +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import java.io.ByteArrayInputStream +import java.io.InputStream +import java.util.Base64 +import org.hamcrest.Matchers.`is` as iz + +class CryptoModuleTest { + @Test + fun `can createLegacyCryptoModule`() { + // given + val cipherKey = "enigma" + + // when + val legacyCryptoModule = CryptoModule.createLegacyCryptoModule(cipherKey) as CryptoModuleImpl + + // then + assertTrue(legacyCryptoModule.primaryCryptor is LegacyCryptor) + assertThat(legacyCryptoModule.cryptorsForDecryptionOnly, hasSize(2)) + assertThat( + legacyCryptoModule.cryptorsForDecryptionOnly, + containsInAnyOrder( + listOf( + iz(CoreMatchers.instanceOf(AesCbcCryptor::class.java)), + iz(CoreMatchers.instanceOf(LegacyCryptor::class.java)), + ), + ), + ) + } + + @Test + fun `can createAesCbcCryptoModule`() { + // given + val cipherKey = "enigma" + + // when + val aesCbcCryptoModule = CryptoModule.createAesCbcCryptoModule(cipherKey) as CryptoModuleImpl + + // then + assertTrue(aesCbcCryptoModule.primaryCryptor is AesCbcCryptor) + assertThat(aesCbcCryptoModule.cryptorsForDecryptionOnly, hasSize(2)) + assertThat( + aesCbcCryptoModule.cryptorsForDecryptionOnly, + containsInAnyOrder( + listOf( + iz(CoreMatchers.instanceOf(AesCbcCryptor::class.java)), + iz(CoreMatchers.instanceOf(LegacyCryptor::class.java)), + ), + ), + ) + } + + @Test + fun `can createNewCryptoModule`() { + // given + val cipherKey = "enigma" + + // when + val newCryptoModule = CryptoModule.createNewCryptoModule(defaultCryptor = AesCbcCryptor(cipherKey)) as CryptoModuleImpl + + // then + assertTrue(newCryptoModule.primaryCryptor is AesCbcCryptor) + assertThat(newCryptoModule.cryptorsForDecryptionOnly, hasSize(1)) + assertThat( + newCryptoModule.cryptorsForDecryptionOnly.first(), + iz(CoreMatchers.instanceOf(AesCbcCryptor::class.java)), + ) + } + + @Test + fun `can decrypt encrypted message using LegacyCryptoModule with randomIV`() { + // given + val cipherKey = "enigma" + val legacyCryptoModuleWithRandomIv = CryptoModule.createLegacyCryptoModule(cipherKey) + val msgToEncrypt = "Hello world".toByteArray() + + // when + val encryptedMsg = legacyCryptoModuleWithRandomIv.encrypt(msgToEncrypt) + val decryptedMsg = legacyCryptoModuleWithRandomIv.decrypt(encryptedMsg) + + // then + assertArrayEquals(msgToEncrypt, decryptedMsg) + } + + @Test + fun `can decrypt encrypted message using LegacyCryptoModule with staticIV`() { + // given + val cipherKey = "enigma" + val legacyCryptoModuleWithStaticIv = CryptoModule.createLegacyCryptoModule(cipherKey, false) + val msgToEncrypt = "Hello world".toByteArray() + + // when + val encryptedMsg = legacyCryptoModuleWithStaticIv.encrypt(msgToEncrypt) + val decryptedMsg = legacyCryptoModuleWithStaticIv.decrypt(encryptedMsg) + + // then + assertArrayEquals(msgToEncrypt, decryptedMsg) + } + + @Test + fun `using LegacyCryptoModule can decrypt message that was encrypted with AesCbcCryptor`() { + // given + val cipherKey = "enigma" + val moduleWithAesCbcCryptorOnly = CryptoModule.createNewCryptoModule(defaultCryptor = AesCbcCryptor(cipherKey)) + val legacyCryptoModule = CryptoModule.createLegacyCryptoModule(cipherKey) + val msgToEncrypt = "Hello world".toByteArray() + + // when + val encryptedMsg = moduleWithAesCbcCryptorOnly.encrypt(msgToEncrypt) + val decryptedMsg = legacyCryptoModule.decrypt(encryptedMsg) + + // then + assertArrayEquals(msgToEncrypt, decryptedMsg) + } + + @Test + fun `using AesCbcCryptoModule can decrypt message that was encrypted with LegacyCryptor with randomIV `() { + // given + val cipherKey = "enigma" + val moduleWithLegacyCryptorOnlyWithRandomIV = + CryptoModule.createNewCryptoModule(defaultCryptor = LegacyCryptor(cipherKey)) + val aesCbcCryptoModule = CryptoModule.createAesCbcCryptoModule(cipherKey) + val msgToEncrypt = "Hello world".toByteArray() + + // when + val encryptedMsg = moduleWithLegacyCryptorOnlyWithRandomIV.encrypt(msgToEncrypt) + val decryptedMsg = aesCbcCryptoModule.decrypt(encryptedMsg) + + // then + assertArrayEquals(msgToEncrypt, decryptedMsg) + } + + @Test + fun `using AesCbcCryptoModule can decrypt message that was encrypted with LegacyCryptor with staticIV `() { + // given + val cipherKey = "enigma" + val moduleWithLegacyCryptorOnlyWithStaticIV = + CryptoModule.createNewCryptoModule(defaultCryptor = LegacyCryptor(cipherKey, false)) + val aesCbcCryptoModule = CryptoModule.createAesCbcCryptoModule(cipherKey, false) + val msgToEncrypt = "Hello world".toByteArray() + + // when + val encryptedMsg = moduleWithLegacyCryptorOnlyWithStaticIV.encrypt(msgToEncrypt) + val decryptedMsg = aesCbcCryptoModule.decrypt(encryptedMsg) + + // then + assertArrayEquals(msgToEncrypt, decryptedMsg) + } + + @Test + fun `can decrypt encrypted message using module with only AesCbcCryptor`() { + // given + val cipherKey = "enigma" + val aesCbcCryptoModule = CryptoModule.createAesCbcCryptoModule(cipherKey) + val msgToEncrypt = "Hello world".toByteArray() + + // when + val encryptedMsg = aesCbcCryptoModule.encrypt(msgToEncrypt) + val decryptedMsg = aesCbcCryptoModule.decrypt(encryptedMsg) + + // then + assertArrayEquals(msgToEncrypt, decryptedMsg) + } + + @Test + fun `can add the same module as a defaultCryptor and cryptorsForDecryptionOnly and have decryption working properly`() { + // given + val cipherKey = "enigma" + val legacyCryptor = LegacyCryptor(cipherKey) + val cryptoModule = + CryptoModule.createNewCryptoModule( + defaultCryptor = legacyCryptor, + cryptorsForDecryptionOnly = listOf(legacyCryptor, AesCbcCryptor(cipherKey)), + ) + val msgToEncrypt = "Hello world".toByteArray() + + // when + val encryptedMsg = cryptoModule.encrypt(msgToEncrypt) + val decryptedMsg = cryptoModule.decrypt(encryptedMsg) + + // then + assertArrayEquals(msgToEncrypt, decryptedMsg) + } + + @Test + fun `can decrypt encrypted message using custom cryptor `() { + // given + val customCryptor = myCustomCryptor() + val msgToEncrypt = "Hello world".toByteArray() + + // when + val encryptedMsg = customCryptor.encrypt(msgToEncrypt) + val decryptedMsg = customCryptor.decrypt(encryptedMsg) + + // then + assertArrayEquals(msgToEncrypt, decryptedMsg) + } + + @Test + fun `can decrypt encrypted message using cryptoModule with custom cryptor`() { + // given + val customCryptor = myCustomCryptor() + val cryptoModule = CryptoModule.createNewCryptoModule(defaultCryptor = customCryptor) + val msgToEncrypt = "Hello world".toByteArray() + + // when + val encryptedMsg = cryptoModule.encrypt(msgToEncrypt) + val decryptedMsg = cryptoModule.decrypt(encryptedMsg) + + // then + assertArrayEquals(msgToEncrypt, decryptedMsg) + } + + @ParameterizedTest + @MethodSource("legacyAndAesCbcCryptors") + fun `should throw exception when encrypting empty data`(cryptoModule: CryptoModule) { + // given + val dataToBeEncrypted = ByteArray(0) + + // when + val exception = + assertThrows(PubNubException::class.java) { + cryptoModule.encrypt(dataToBeEncrypted) + } + + // then + assertEquals("Encryption/Decryption of empty data not allowed.", exception.errorMessage) + assertEquals(PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, exception.pubnubError) + } + + @ParameterizedTest + @MethodSource("legacyAndAesCbcCryptors") + fun `should throw exception when decrypting empty data`(cryptoModule: CryptoModule) { + // given + val dataToBeDecrypted = ByteArray(0) + + // when + val exception = + assertThrows(PubNubException::class.java) { + cryptoModule.decrypt(dataToBeDecrypted) + } + + // then + assertEquals("Encryption/Decryption of empty data not allowed.", exception.errorMessage) + assertEquals(PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, exception.pubnubError) + } + + @ParameterizedTest + @MethodSource("legacyAndAesCbcCryptors") + fun `should throw exception when encrypting empty stream`(cryptoModule: CryptoModule) { + // given + val dataToBeEncrypted = ByteArray(0) + val streamToBeEncrypted = ByteArrayInputStream(dataToBeEncrypted) + + // when + val exception = + assertThrows(PubNubException::class.java) { + cryptoModule.encryptStream(streamToBeEncrypted) + } + + // then + assertEquals("Encryption/Decryption of empty data not allowed.", exception.errorMessage) + assertEquals(PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, exception.pubnubError) + } + + @ParameterizedTest + @MethodSource("legacyAndAesCbcCryptors") + fun `should throw exception when decrypting empty stream`(cryptoModule: CryptoModule) { + // given + val dataToBeDecrypted = ByteArray(0) + val streamToBeDecrypted = ByteArrayInputStream(dataToBeDecrypted) + + // when + val exception = + assertThrows(PubNubException::class.java) { + cryptoModule.decryptStream(streamToBeDecrypted) + } + + // then + assertEquals("Encryption/Decryption of empty data not allowed.", exception.errorMessage) + assertEquals(PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, exception.pubnubError) + } + + private fun myCustomCryptor() = + object : Cryptor { + override fun id(): ByteArray { + // Should return a ByteArray of exactly 4 bytes. + return byteArrayOf('C'.code.toByte(), 'U'.code.toByte(), 'S'.code.toByte(), 'T'.code.toByte()) + } + + override fun encrypt(data: ByteArray): EncryptedData { + return EncryptedData(metadata = null, data = data) + } + + override fun decrypt(encryptedData: EncryptedData): ByteArray { + return encryptedData.data + } + + override fun encryptStream(stream: InputStream): EncryptedStreamData { + return EncryptedStreamData(metadata = null, stream = stream) + } + + override fun decryptStream(encryptedData: EncryptedStreamData): InputStream { + return encryptedData.stream + } + } + + @ParameterizedTest + @MethodSource("decryptStreamSource") + fun decryptStreamEncryptedByGo( + expected: String, + encryptedBase64: String, + cipherKey: String, + ) { + val crypto = CryptoModule.createLegacyCryptoModule(cipherKey, true) + val decrypted = crypto.decryptStream(Base64.getDecoder().decode(encryptedBase64).inputStream()) + assertEquals(expected, String(decrypted.readBytes())) + } + + @ParameterizedTest + @MethodSource("encryptStreamDecryptStreamSource") + fun encryptStreamDecryptStream( + input: String, + cryptoModule: CryptoModule, + ) { + val encrypted = cryptoModule.encryptStream(input.byteInputStream()) + val decrypted = cryptoModule.decryptStream(encrypted) + assertEquals(input, String(decrypted.readBytes())) + } + + companion object { + @JvmStatic + fun decryptStreamSource(): List = + listOf( + Arguments.of( + "Hello world encrypted with legacyModuleRandomIv", + "T3J9iXI87PG9YY/lhuwmGRZsJgA5y8sFLtUpdFmNgrU1IAitgAkVok6YP7lacBiVhBJSJw39lXCHOLxl2d98Bg==", + "myCipherKey", + ), + Arguments.of( + "Hello world encrypted with aesCbcModule", + "UE5FRAFBQ1JIEKzlyoyC/jB1hrjCPY7zm+X2f7skPd0LBocV74cRYdrkRQ2BPKeA22gX/98pMqvcZtFB6TCGp3Zf1M8F730nlfk=", + "myCipherKey", + ), + ) + + @JvmStatic + fun encryptStreamDecryptStreamSource(): List = + listOf( + Arguments.of("Hello world1", CryptoModule.createLegacyCryptoModule("myCipherKey", true)), + Arguments.of("Hello world2", CryptoModule.createLegacyCryptoModule("myCipherKey", false)), + Arguments.of("Hello world3", CryptoModule.createAesCbcCryptoModule("myCipherKey", true)), + Arguments.of("Hello world4", CryptoModule.createAesCbcCryptoModule("myCipherKey", false)), + ) + + @JvmStatic + fun legacyAndAesCbcCryptors(): List = + listOf( + Arguments.of(CryptoModule.createLegacyCryptoModule("myCipherKey", true)), + Arguments.of(CryptoModule.createLegacyCryptoModule("myCipherKey", false)), + Arguments.of(CryptoModule.createAesCbcCryptoModule("myCipherKey", true)), + Arguments.of(CryptoModule.createAesCbcCryptoModule("myCipherKey", false)), + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/algorithm/AesCBCCryptorTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/algorithm/AesCBCCryptorTest.kt new file mode 100644 index 000000000..ac3d67cd8 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/algorithm/AesCBCCryptorTest.kt @@ -0,0 +1,147 @@ +package com.pubnub.api.crypto.algorithm + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.crypto.data.EncryptedData +import com.pubnub.api.crypto.data.EncryptedStreamData +import com.pubnub.internal.crypto.cryptor.AesCbcCryptor +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Assertions.assertArrayEquals +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +class AesCBCCryptorTest { + private lateinit var objectUnderTest: AesCbcCryptor + + companion object { + @JvmStatic + fun messageToBeEncrypted(): List = + listOf( + Arguments.of("Hello world"), + Arguments.of("Zaลผรณล‚ฤ‡ gฤ™ล›lฤ… jaลบล„"), // Polish + Arguments.of("เคนเฅˆเคฒเฅ‹ เคตเคฐเฅเคฒเฅเคก"), // Hindi + Arguments.of("ใ“ใ‚“ใซใกใฏไธ–็•Œ"), // Japan + Arguments.of("ไฝ ๅฅฝไธ–็•Œ"), // Chinese + ) + } + + @BeforeEach + fun setUp() { + objectUnderTest = AesCbcCryptor("enigma") + } + + @ParameterizedTest + @MethodSource("messageToBeEncrypted") + fun canDecryptTextWhatIsEncrypted(msgToBeEncrypted: String) { + // given + val msgToEncrypt = msgToBeEncrypted.toByteArray() + + // when + val encryptedMsg = objectUnderTest.encrypt(msgToEncrypt) + val decryptedMsg = objectUnderTest.decrypt(encryptedMsg) + + // then + assertArrayEquals(msgToEncrypt, decryptedMsg) + } + + @ParameterizedTest + @MethodSource("messageToBeEncrypted") + fun encryptingTwoTimesTheSameMessageProducesDifferentOutput(msgToBeEncrypted: String) { + // given + val msgToEncrypt = msgToBeEncrypted.toByteArray() + + // when + val encrypted1: EncryptedData = objectUnderTest.encrypt(msgToEncrypt) + val encrypted2: EncryptedData = objectUnderTest.encrypt(msgToEncrypt) + + // then + Assertions.assertFalse(encrypted1.data.contentEquals(encrypted2.data)) + } + + @ParameterizedTest + @MethodSource("messageToBeEncrypted") + fun encryptingTwoTimesDecryptedMsgIsTheSame(msgToBeEncrypted: String) { + // given + val msgToEncrypt = msgToBeEncrypted.toByteArray() + + // when + val encrypted1 = objectUnderTest.encrypt(msgToEncrypt) + val encrypted2 = objectUnderTest.encrypt(msgToEncrypt) + + // then + assertArrayEquals(msgToEncrypt, objectUnderTest.decrypt(encrypted1)) + assertArrayEquals(msgToEncrypt, objectUnderTest.decrypt(encrypted2)) + } + + @Test + fun `should throw exception when encrypting empty data`() { + // given + val msgToEncrypt = "".toByteArray() + + // when + val exception = + Assertions.assertThrows(PubNubException::class.java) { + objectUnderTest.encrypt(msgToEncrypt) + } + + // then + assertEquals("Encryption/Decryption of empty data not allowed.", exception.errorMessage) + assertEquals(PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, exception.pubnubError) + } + + @Test + fun `should throw exception when decrypting empty data`() { + // given + val msgToDecrypt = "".toByteArray() + val encryptedData = EncryptedData(data = msgToDecrypt) + + // when + val exception = + Assertions.assertThrows(PubNubException::class.java) { + objectUnderTest.decrypt(encryptedData) + } + + // then + assertEquals("Encryption/Decryption of empty data not allowed.", exception.errorMessage) + assertEquals(PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, exception.pubnubError) + } + + @Test + fun `should throw exception when encrypting empty stream`() { + // given + val msgToEncrypt = "" + val streamToEncrypt = msgToEncrypt.byteInputStream() + + // when + val exception = + Assertions.assertThrows(PubNubException::class.java) { + objectUnderTest.encryptStream(streamToEncrypt) + } + + // then + assertEquals("Encryption/Decryption of empty data not allowed.", exception.errorMessage) + assertEquals(PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, exception.pubnubError) + } + + @Test + fun `should throw exception when decrypting empty stream`() { + // given + val msgToDecrypt = "" + val streamToEncrypt = msgToDecrypt.byteInputStream() + val encryptedStreamData = EncryptedStreamData(stream = streamToEncrypt) + + // when + val exception = + Assertions.assertThrows(PubNubException::class.java) { + objectUnderTest.decryptStream(encryptedStreamData) + } + + // then + assertEquals("Encryption/Decryption of empty data not allowed.", exception.errorMessage) + assertEquals(PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, exception.pubnubError) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/algorithm/LegacyCryptorTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/algorithm/LegacyCryptorTest.kt new file mode 100644 index 000000000..5b0651cf1 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/algorithm/LegacyCryptorTest.kt @@ -0,0 +1,191 @@ +package com.pubnub.api.crypto.algorithm + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.crypto.data.EncryptedData +import com.pubnub.api.crypto.data.EncryptedStreamData +import com.pubnub.internal.crypto.cryptor.LegacyCryptor +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Assertions.assertArrayEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import org.junit.jupiter.params.provider.ValueSource +import java.io.ByteArrayInputStream + +class LegacyCryptorTest { + companion object { + @JvmStatic + fun messageToBeEncrypted(): List = + listOf( + Arguments.of("Hello world"), + Arguments.of("Zaลผรณล‚ฤ‡ gฤ™ล›lฤ… jaลบล„"), // Polish + Arguments.of("เคนเฅˆเคฒเฅ‹ เคตเคฐเฅเคฒเฅเคก"), // Hindi + Arguments.of("ใ“ใ‚“ใซใกใฏไธ–็•Œ"), // Japan + Arguments.of("ไฝ ๅฅฝไธ–็•Œ"), // Chinese + ) + } + + @ParameterizedTest + @MethodSource("messageToBeEncrypted") + fun canDecryptTextWhatIsEncryptedWithStaticIV(messageToBeEncrypted: String) { + // given + val cipherKey = "enigma" + val msgToEncrypt = messageToBeEncrypted.toByteArray() + + // when + val cryptor = LegacyCryptor(cipherKey = cipherKey, useRandomIv = false) + val encryptedMsg = cryptor.encrypt(msgToEncrypt) + val decryptedMsg = cryptor.decrypt(encryptedMsg) + + // then + assertArrayEquals(msgToEncrypt, decryptedMsg) + } + + @ParameterizedTest + @MethodSource("messageToBeEncrypted") + fun canDecryptTextWhatIsEncryptedWithRandomIV(messageToBeEncrypted: String) { + // given + val cipherKey = "enigma" + val msgToEncrypt = messageToBeEncrypted.toByteArray() + + // when + val cryptor = LegacyCryptor(cipherKey = cipherKey) + val encryptedMsg = cryptor.encrypt(msgToEncrypt) + val decryptedMsg = cryptor.decrypt(encryptedMsg) + + // then + assertArrayEquals(msgToEncrypt, decryptedMsg) + } + + @ParameterizedTest + @MethodSource("messageToBeEncrypted") + fun encryptingWithRandomIVTwoTimesTheSameMessageProducesDifferentOutput(messageToBeEncrypted: String) { + // given + val cipherKey = "enigma" + val msgToEncrypt = messageToBeEncrypted.toByteArray() + + // when + val cryptor = LegacyCryptor(cipherKey = cipherKey) + val encrypted1: EncryptedData = cryptor.encrypt(msgToEncrypt) + val encrypted2: EncryptedData = cryptor.encrypt(msgToEncrypt) + + // then + assertFalse(encrypted1.data.contentEquals(encrypted2.data)) + } + + @ParameterizedTest + @MethodSource("messageToBeEncrypted") + fun encryptingWithRandomIVTwoTimesDecryptedMsgIsTheSame(messageToBeEncrypted: String) { + // given + val cipherKey = "enigma" + val msgToEncrypt = messageToBeEncrypted.toByteArray() + + // when + val cryptor = LegacyCryptor(cipherKey = cipherKey) + val encrypted1 = cryptor.encrypt(msgToEncrypt) + val encrypted2 = cryptor.encrypt(msgToEncrypt) + + // then + assertArrayEquals(msgToEncrypt, cryptor.decrypt(encrypted1)) + assertArrayEquals(msgToEncrypt, cryptor.decrypt(encrypted2)) + } + + @ParameterizedTest + @ValueSource(booleans = [true, false]) + fun `should throw exception when encrypting empty data`(useRandomIv: Boolean) { + // given + val msgToEncrypt = "".toByteArray() + val cipherKey = "enigma" + val cryptor = LegacyCryptor(cipherKey = cipherKey, useRandomIv = useRandomIv) + + // when + val exception = + Assertions.assertThrows(PubNubException::class.java) { + cryptor.encrypt(msgToEncrypt) + } + + // then + Assertions.assertEquals("Encryption/Decryption of empty data not allowed.", exception.errorMessage) + Assertions.assertEquals(PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, exception.pubnubError) + } + + @Test + fun `should throw exception when decrypting data containing only initialization vector and cryptor has randomIv`() { + // given + val msgToDecrypt = ByteArray(16) { it.toByte() } // IV has 16 bytes + val encryptedData = EncryptedData(data = msgToDecrypt) + val cipherKey = "enigma" + val cryptor = LegacyCryptor(cipherKey = cipherKey, useRandomIv = true) + + // when + val exception = + Assertions.assertThrows(PubNubException::class.java) { + cryptor.decrypt(encryptedData) + } + + // then + Assertions.assertEquals("Encryption/Decryption of empty data not allowed.", exception.errorMessage) + Assertions.assertEquals(PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, exception.pubnubError) + } + + @Test + fun `should throw exception when decrypting empty data and cryptor has staticIv`() { + // given + val msgToDecrypt = "".toByteArray() + val encryptedData = EncryptedData(data = msgToDecrypt) + val cipherKey = "enigma" + val cryptor = LegacyCryptor(cipherKey = cipherKey, useRandomIv = false) + + // when + val exception = + Assertions.assertThrows(PubNubException::class.java) { + cryptor.decrypt(encryptedData) + } + + // then + Assertions.assertEquals("Encryption/Decryption of empty data not allowed.", exception.errorMessage) + Assertions.assertEquals(PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, exception.pubnubError) + } + + @Test + fun `should throw exception when encrypting empty stream`() { + // given + val msgToEncrypt = "" + val streamToEncrypt = msgToEncrypt.byteInputStream() + val cipherKey = "enigma" + val cryptor = LegacyCryptor(cipherKey = cipherKey, useRandomIv = false) + + // when + val exception = + Assertions.assertThrows(PubNubException::class.java) { + cryptor.encryptStream(streamToEncrypt) + } + + // then + Assertions.assertEquals("Encryption/Decryption of empty data not allowed.", exception.errorMessage) + Assertions.assertEquals(PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, exception.pubnubError) + } + + @Test + fun `should throw exception when decrypting empty stream`() { + // given + val msgToDecrypt = ByteArray(16) { it.toByte() } // IV has 16 bytes + val streamToEncrypt = ByteArrayInputStream(msgToDecrypt) + val encryptedStreamData = EncryptedStreamData(stream = streamToEncrypt) + val cipherKey = "enigma" + val cryptor = LegacyCryptor(cipherKey = cipherKey, useRandomIv = false) + + // when + val exception = + Assertions.assertThrows(PubNubException::class.java) { + cryptor.decryptStream(encryptedStreamData) + } + + // then + Assertions.assertEquals("Encryption/Decryption of empty data not allowed.", exception.errorMessage) + Assertions.assertEquals(PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, exception.pubnubError) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/cryptor/HeaderParserTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/cryptor/HeaderParserTest.kt new file mode 100644 index 000000000..a8e3478c0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/crypto/cryptor/HeaderParserTest.kt @@ -0,0 +1,125 @@ +package com.pubnub.api.crypto.cryptor + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.internal.crypto.cryptor.HeaderParser +import com.pubnub.internal.crypto.cryptor.ParseResult +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.`is` +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Assertions.fail +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class HeaderParserTest { + private lateinit var objectUnderTest: HeaderParser + + @BeforeEach + fun setUp() { + objectUnderTest = HeaderParser() + } + + @Test + fun `can create and parse data with header when cryptorDataSize is 1`() { + val cryptorId: ByteArray = + byteArrayOf('C'.code.toByte(), 'R'.code.toByte(), 'I'.code.toByte(), 'V'.code.toByte()) // "CRIV" + + val cipherKey = "enigma" + val cryptoModule = CryptoModule.createLegacyCryptoModule(cipherKey, false) + val cryptorData = + byteArrayOf(0x50, 0x56, 0x56, 0x56, 0x56, 0x01, 0x43, 0x52, 0x49, 0x56, 0x10, 0x10, 0x56, 0x56, 0x56, 0x10) + val cryptorHeader = objectUnderTest.createCryptorHeader(cryptorId, cryptorData) + + val dataToBeEncrypted = byteArrayOf('D'.code.toByte(), 'A'.code.toByte()) + val encryptedData = cryptoModule.encrypt(dataToBeEncrypted) + val headerWithData: ByteArray = cryptorHeader + encryptedData + val parseResult = objectUnderTest.parseDataWithHeader(headerWithData) + + when (parseResult) { + is ParseResult.NoHeader -> fail("Expected header") + is ParseResult.Success -> { + assertTrue(cryptorId.contentEquals(parseResult.cryptoId)) + assertTrue(cryptorData.contentEquals(parseResult.cryptorData)) + assertTrue(encryptedData.contentEquals(parseResult.encryptedData)) + } + } + } + + @Test + fun `can create and parse data with header when cryptorDataSize is 3`() { + val cryptorId: ByteArray = + byteArrayOf('C'.code.toByte(), 'R'.code.toByte(), 'I'.code.toByte(), 'V'.code.toByte()) // "CRIV" + val cryptorData = createByteArrayThatHas255Elements() + val cryptorHeader = objectUnderTest.createCryptorHeader(cryptorId, cryptorData) + + val dataToBeEncrypted = byteArrayOf('D'.code.toByte(), 'A'.code.toByte()) + val headerWithData: ByteArray = cryptorHeader + dataToBeEncrypted + val parseResult = objectUnderTest.parseDataWithHeader(headerWithData) + + when (parseResult) { + is ParseResult.NoHeader -> fail("Expected header") + is ParseResult.Success -> { + assertTrue(cryptorId.contentEquals(parseResult.cryptoId)) + assertTrue(cryptorData.contentEquals(parseResult.cryptorData)) + assertTrue(dataToBeEncrypted.contentEquals(parseResult.encryptedData)) + } + } + } + + @Test + fun `should return NoHeader when there is no sentinel`() { + val cryptorHeaderWithInvalidSentinel = + byteArrayOf( + 0x56, + 0x56, + 0x56, + 0x56, + 0x01, + 0x43, + 0x52, + 0x49, + 0x56, + 0x10, + 0x10, + 0x56, + 0x56, + 0x56, + 0x56, + 0x01, + 0x43, + 0x52, + 0x49, + 0x56, + 0x10, + 0x10, + ) + val parseResult = objectUnderTest.parseDataWithHeader(cryptorHeaderWithInvalidSentinel) + + assertThat(parseResult, `is`(ParseResult.NoHeader)) + } + + @Test + fun `should throw exception when input data are to short`() { + val cryptorHeaderWithToShortData = + byteArrayOf(80, 78, 69, 68, 1, 43, 52, 49, 56) + + val exception: PubNubException = + assertThrows(PubNubException::class.java) { + objectUnderTest.parseDataWithHeader(cryptorHeaderWithToShortData) + } + + assertEquals("Minimal size of encrypted data having Cryptor Data Header is: 10", exception.errorMessage) + assertEquals(PubNubError.CRYPTOR_DATA_HEADER_SIZE_TO_SMALL, exception.pubnubError) + } + + private fun createByteArrayThatHas255Elements(): ByteArray { + var byteArray: ByteArray = byteArrayOf() + for (i in 1..255) { + byteArray += byteArrayOf(i.toByte()) + } + return byteArray + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/endpoints/access/GrantTokenTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/endpoints/access/GrantTokenTest.kt new file mode 100644 index 000000000..19be22388 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/endpoints/access/GrantTokenTest.kt @@ -0,0 +1,115 @@ +package com.pubnub.api.endpoints.access + +import com.pubnub.api.SpaceId +import com.pubnub.api.UserId +import com.pubnub.api.models.consumer.access_manager.sum.SpacePermissions +import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.access.GrantTokenEndpoint +import com.pubnub.internal.managers.RetrofitManager +import com.pubnub.internal.models.server.access_manager.v3.GrantTokenData +import com.pubnub.internal.models.server.access_manager.v3.GrantTokenRequestBody +import com.pubnub.internal.models.server.access_manager.v3.GrantTokenResponse +import com.pubnub.internal.services.AccessManagerService +import com.pubnub.internal.v2.PNConfigurationImpl +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import io.mockk.spyk +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import retrofit2.Call +import retrofit2.Response + +internal class GrantTokenTest { + private lateinit var pubnub: PubNubImpl + + @BeforeEach + internal fun setUp() { + MockKAnnotations.init(this) + val pnConfiguration = + PNConfigurationImpl( + userId = UserId("myUserId"), + subscribeKey = "something", + secretKey = "something", + ) + pubnub = spyk(PubNubImpl(configuration = pnConfiguration)) + } + + @MockK + private lateinit var grantTokenEndpointMock: GrantTokenEndpoint + + @Test + fun can_createGrantTokenSimple() { + val expectedTTL = 1337 + val authorizedUserId = UserId("authorizedUserId") + val expectedToken = "token_value" + val grantTokenResult = PNGrantTokenResult(token = expectedToken) + every { + pubnub.grantToken( + ttl = any(), + authorizedUserId = any(), + spacesPermissions = any(), + usersPermissions = any(), + ) + } returns grantTokenEndpointMock + every { grantTokenEndpointMock.sync() } returns grantTokenResult + + val grantTokenEndpoint = + pubnub.grantToken( + ttl = expectedTTL, + authorizedUserId = authorizedUserId, + spacesPermissions = listOf(SpacePermissions.id(spaceId = SpaceId("mySpaceId"), read = true, delete = true)), + ) + val actualGrantTokenResult: PNGrantTokenResult? = grantTokenEndpoint.sync() + val token = actualGrantTokenResult!!.token + + assertEquals(expectedToken, token) + } + + @Test + fun can_createGrantToken() { + val expectedTTL = 1337 + val authorizedUserId = UserId("authorizedUserId") + val expectedToken = "token_value" + val spaceIdValue = "mySpaceId" + + val retrofitManager = mockk(relaxed = true) + val accessManagerService = mockk(relaxed = true) + val capturedBodies = mutableListOf() + val call = mockk>() + + every { pubnub.retrofitManager } returns retrofitManager + every { retrofitManager.accessManagerService } returns accessManagerService + every { accessManagerService.grantToken(any(), capture(capturedBodies), any()) } returns call + every { call.execute() } returns Response.success(GrantTokenResponse(GrantTokenData(expectedToken))) + + val actualGrantTokenResult: PNGrantTokenResult? = + pubnub.grantToken( + ttl = expectedTTL, + authorizedUserId = authorizedUserId, + spacesPermissions = + listOf( + SpacePermissions.id( + spaceId = SpaceId(spaceIdValue), + read = true, + delete = true, + ), + ), + ).sync() + + val capturedBody = capturedBodies[0] + + assertEquals(expectedToken, actualGrantTokenResult!!.token) + + val ttl: Int = (capturedBody as GrantTokenRequestBody).ttl + val permissions = capturedBody.permissions + + assertEquals(expectedTTL, ttl) + assertTrue(permissions.resources.channels.containsKey(spaceIdValue)) + assertEquals(authorizedUserId.value, permissions.uuid) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/endpoints/pubsub/PublishTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/endpoints/pubsub/PublishTest.kt new file mode 100644 index 000000000..2af305d68 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/endpoints/pubsub/PublishTest.kt @@ -0,0 +1,109 @@ +package com.pubnub.api.endpoints.pubsub + +import com.pubnub.api.UserId +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.pubsub.PublishEndpoint +import com.pubnub.internal.managers.RetrofitManager +import com.pubnub.internal.services.PublishService +import com.pubnub.internal.v2.PNConfigurationImpl +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot +import io.mockk.spyk +import okhttp3.Request +import okio.Timeout +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.not +import org.junit.Test +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +private const val TEST_MESSAGE = "test message in the bottle" +private const val TEST_CHANNEL = "testChannel" +private const val TEST_SUBKEY = "subKey" +private const val TEST_PUBKEY = "pubKey" +private const val CIPHER_KEY = "enigma" + +class PublishTest { + private val pnConfigurationHardcodedIV = + PNConfigurationImpl( + userId = UserId(PubNubImpl.generateUUID()), + subscribeKey = TEST_SUBKEY, + publishKey = TEST_PUBKEY, + cryptoModule = CryptoModule.createLegacyCryptoModule(CIPHER_KEY, false), + ) + private val pnConfigurationRandomIV = + PNConfigurationImpl( + userId = UserId(PubNubImpl.generateUUID()), + subscribeKey = TEST_SUBKEY, + publishKey = TEST_PUBKEY, + cryptoModule = CryptoModule.createLegacyCryptoModule(CIPHER_KEY, true), + ) + + private val pubNubHardcodedIVMock = spyk(PubNubImpl(pnConfigurationHardcodedIV)) + private val pubNubRandomIVMock = spyk(PubNubImpl(pnConfigurationRandomIV)) + + private val retrofitManagerMock = mockk() + private val publishServiceMock = mockk() + + private val encryptedMessageSlot = slot() + + class FakeCall : Call> { + override fun clone(): Call> = this + + override fun execute(): Response> = Response.success(listOf("test1", "test2", "10")) + + override fun enqueue(callback: Callback>) { + } + + override fun isExecuted(): Boolean = false + + override fun cancel() { + } + + override fun isCanceled(): Boolean = false + + override fun request(): Request = Request.Builder().build() + + override fun timeout(): Timeout = Timeout.NONE + } + + init { + every { + publishServiceMock.publish( + any(), + any(), + any(), + capture(encryptedMessageSlot), + any(), + ) + } answers { FakeCall() } + every { retrofitManagerMock.publishService } returns publishServiceMock + + every { pubNubHardcodedIVMock.configuration } returns pnConfigurationHardcodedIV + every { pubNubRandomIVMock.configuration } returns pnConfigurationRandomIV + + every { pubNubHardcodedIVMock.retrofitManager } returns retrofitManagerMock + every { pubNubRandomIVMock.retrofitManager } returns retrofitManagerMock + } + + @Test + fun `publish with encryption respects random IV setting`() { + val publishHardcodedIVUnderTest = + PublishEndpoint(pubnub = pubNubHardcodedIVMock, message = TEST_MESSAGE, channel = TEST_CHANNEL) + publishHardcodedIVUnderTest.sync() + + val encryptedMessageHardcodedIV = encryptedMessageSlot.captured + + val publishRandomIVUnderTest = + PublishEndpoint(pubnub = pubNubRandomIVMock, message = TEST_MESSAGE, channel = TEST_CHANNEL) + publishRandomIVUnderTest.sync() + + val encryptedMessageRandomIV = encryptedMessageSlot.captured + + assertThat(encryptedMessageRandomIV, not(`is`(encryptedMessageHardcodedIV))) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/BaseTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/BaseTest.kt new file mode 100644 index 000000000..f7175fd02 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/BaseTest.kt @@ -0,0 +1,72 @@ +package com.pubnub.api.legacy + +import com.github.tomakehurst.wiremock.WireMockServer +import com.github.tomakehurst.wiremock.client.WireMock +import com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig +import com.pubnub.api.UserId +import com.pubnub.api.enums.PNLogVerbosity +import com.pubnub.api.v2.PNConfiguration +import com.pubnub.internal.PubNubImpl +import com.pubnub.test.CommonUtils.defaultListenDuration +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import org.junit.After +import org.junit.Assert.assertTrue +import org.junit.Before + +abstract class BaseTest { + lateinit var wireMockServer: WireMockServer + protected val pubnub: PubNubImpl by lazy { + PubNubImpl(config.build()) + } + protected lateinit var config: PNConfiguration.Builder private set + + @Before + open fun beforeEach() { + wireMockServer = + WireMockServer( + wireMockConfig() + .bindAddress("localhost") + .dynamicPort(), + ) + wireMockServer.start() + WireMock.configureFor("http", "localhost", wireMockServer.port()) + defaultListenDuration = 2 + + onBefore() + } + + @After + open fun afterEach() { + wireMockServer.stop() + wireMockServer.findAllUnmatchedRequests().forEach { + println("Unmatched ${it.url}") + } + assertTrue(wireMockServer.findAllUnmatchedRequests().isEmpty()) + onAfter() + } + + open fun onBefore() { + initConfiguration() + } + + open fun onAfter() { + pubnub.forceDestroy() + } + + fun initConfiguration() { + config = createConfiguration() + } + + fun createConfiguration() = + PNConfiguration.builder(userId = UserId("myUUID"), "") { + subscribeKey = "mySubscribeKey" + publishKey = "myPublishKey" + origin = wireMockServer.baseUrl().toHttpUrlOrNull()!!.run { "$host:$port" } + secure = false + logVerbosity = PNLogVerbosity.BODY + } + + fun clearConfiguration() { + config = PNConfiguration.builder(userId = UserId(PubNubImpl.generateUUID()), "") + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/PubNubCoreTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/PubNubCoreTest.kt new file mode 100644 index 000000000..453044aab --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/PubNubCoreTest.kt @@ -0,0 +1,89 @@ +package com.pubnub.api.legacy + +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.api.retry.RetryConfiguration +import com.pubnub.internal.PubNubImpl +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +class PubNubImplTest : BaseTest() { + override fun onBefore() { + clearConfiguration() + } + + @Test + fun testCreateSuccess() { + assertEquals(true, pubnub.configuration.secure) + assertEquals("https://ps.pndsn.com", pubnub.baseUrl()) + } + + @Test + fun testEncryptConfigurationKey() { + config.cryptoModule = CryptoModule.createLegacyCryptoModule("cipherKey", false) + // initPubNub() + assertEquals("iALQtn3PfIXe74CT/wrS7g==", pubnub.encrypt("test1").trim()) + } + + @Test + fun testDecryptCustomKey() { + val cryptoModule = CryptoModule.createLegacyCryptoModule("cipherKey", false) + assertEquals("test1", pubnub.decrypt("iALQtn3PfIXe74CT/wrS7g==", cryptoModule).trim()) + } + + @Test + fun testDecryptConfigurationKey() { + config.cryptoModule = CryptoModule.createLegacyCryptoModule("cipherKey", false) + // initPubNub() + assertEquals("test1", pubnub.decrypt("iALQtn3PfIXe74CT/wrS7g==").trim()) + } + + @Test + fun testconfig() { + config.subscribeTimeout = 3000 + config.connectTimeout = 4000 + config.nonSubscribeRequestTimeout = 5000 + config.retryConfiguration = RetryConfiguration.None + // initPubNub() + + assertEquals("https://ps.pndsn.com", pubnub.baseUrl()) + assertEquals(3000, config.subscribeTimeout) + assertEquals(4000, config.connectTimeout) + assertEquals(5000, config.nonSubscribeRequestTimeout) + } + + @Test + fun getVersionAndTimeStamp() { + val version = PubNubImpl.SDK_VERSION + val timeStamp = PubNubImpl.timestamp() + assertEquals("9.2-DEV", version) + assertTrue(timeStamp > 0) + } + + @Test + fun pnGeneratesPnsdkWithSuffixes() { + val name1 = "key1" + val suffix1 = "value1/1.0.0" + val name2 = "key2" + val suffix2 = "value2/2.0.0" + val suffix11 = "value3/2.0.0" + + config.pnsdkSuffixes = buildMap { + put(name1, suffix1) + put(name2, suffix2) + put(name1, suffix11) + } + + val generatedPnsdk = pubnub.generatePnsdk() + assertEquals("PubNub-Kotlin/${PubNubImpl.SDK_VERSION} $suffix11 $suffix2", generatedPnsdk) + } + + @Test + fun pnGeneratesPnsdkWithPnsdkName() { + val pubNubCore = PubNubImpl( + config.build(), + pnsdkName = "PubNub-ABC", + ) + assertEquals(pubNubCore.generatePnsdk(), "PubNub-ABC/${PubNubImpl.SDK_VERSION}") + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/DeleteMessagesCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/DeleteMessagesCoreEndpointTest.kt new file mode 100644 index 000000000..85dd7b52f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/DeleteMessagesCoreEndpointTest.kt @@ -0,0 +1,125 @@ +package com.pubnub.api.legacy.endpoints + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.delete +import com.github.tomakehurst.wiremock.client.WireMock.deleteRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.failTest +import org.awaitility.Awaitility +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean + +class DeleteMessagesCoreEndpointTest : BaseTest() { + @Test + fun testSyncSuccess() { + stubFor( + delete(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/channel/mychannel,my_channel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "" + } + """.trimIndent(), + ), + ), + ) + + pubnub.deleteMessages( + channels = listOf("mychannel,my_channel"), + ).sync() + } + + @Test + fun testSyncAuthSuccess() { + stubFor( + delete(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/channel/mychannel,my_channel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "" + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "authKey" + // initPubNub() + + pubnub.deleteMessages( + channels = listOf("mychannel,my_channel"), + ).sync() + + val requests = findAll(deleteRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("authKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testFailure() { + stubFor( + delete(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/channel/mychannel,my_channel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 403, + "error": false, + "error_message": "wut" + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.deleteMessages( + channels = listOf("mychannel,my_channel"), + ).sync() + } catch (e: Exception) { + failTest() + } + } + + @Test + fun testAsyncSuccess() { + stubFor( + delete(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/channel/mychannel,my_channel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "" + } + """.trimIndent(), + ), + ), + ) + + val success = AtomicBoolean() + + pubnub.deleteMessages( + channels = listOf("mychannel,my_channel"), + ).async { result -> + assertFalse(result.isFailure) + success.set(true) + } + + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilTrue(success) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/EndpointCoreTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/EndpointCoreTest.kt new file mode 100644 index 000000000..ec24775cf --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/EndpointCoreTest.kt @@ -0,0 +1,349 @@ +package com.pubnub.api.legacy.endpoints + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.any +import com.github.tomakehurst.wiremock.client.WireMock.forbidden +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.matching.UrlPattern +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import com.pubnub.api.PubNubException +import com.pubnub.api.UserId +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.legacy.BaseTest +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.EndpointCore +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.v2.PNConfigurationImpl +import com.pubnub.test.listen +import okhttp3.Request +import okio.Timeout +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicReference + +class EndpointCoreTest : BaseTest() { + @Test + fun testDefaultInstanceParamSetting() { + assertTrue(pubnub.configuration.includeRequestIdentifier) + assertFalse(pubnub.configuration.includeInstanceIdentifier) + } + + // todo test other params + + @Test + fun testBaseParamsIncludeInstanceId() { + config.includeInstanceIdentifier = true + + fakeEndpoint { + assertTrue(it.containsKey("instanceid")) + }.sync() + } + + @Test + fun testBaseParamsNoIncludeInstanceId() { + fakeEndpoint { + assertEquals("myUUID", it["uuid"]) + assertFalse(it.containsKey("instanceid")) + }.sync() + } + + @Test + fun testBaseParamsIncludeRequestId() { + fakeEndpoint { + assertEquals("myUUID", it["uuid"]) + assertTrue(it.containsKey("requestid")) + }.sync() + } + + @Test + fun testBaseParamsNoIncludeRequestId() { + config.includeRequestIdentifier = false + // initPubNub() + + fakeEndpoint { + assertEquals("myUUID", it["uuid"]) + assertFalse(it.containsKey("requestid")) + }.sync() + } + + @Test + fun testBaseParamsPersistentRequestId() { + config.includeInstanceIdentifier = true + + val instanceId1 = AtomicReference() + val instanceId2 = AtomicReference() + + fakeEndpoint { + instanceId1.set(it["instanceid"]) + }.sync() + + fakeEndpoint { + instanceId2.set(it["instanceid"]) + }.sync() + + assertEquals(pubnub.instanceId, instanceId1.get()) + assertEquals(instanceId1.get(), instanceId2.get()) + } + + @Test + fun testOverrideConfiguration_instanceAndRequestId() { + config.includeInstanceIdentifier = true + config.includeRequestIdentifier = true + + fakeEndpoint { + println(it) + assertNull(it["instanceid"]) + assertNull(it["requestid"]) + }.apply { + overrideConfigurationInternal( + PNConfigurationImpl( + userId = config.userId, + includeRequestIdentifier = false, + includeInstanceIdentifier = false, + ), + ) + }.sync() + } + + @Test + fun testUuid() { + val expectedUuid = UserId(PubNubImpl.generateUUID()) + config.userId = expectedUuid + // initPubNub() + + fakeEndpoint { + assertEquals(expectedUuid.value, it["uuid"]) + }.sync() + } + + @Test + fun testOverrideConfiguration_Uuid() { + val expectedUuid = UserId(PubNubImpl.generateUUID()) + config.userId = UserId("someOtherUUID") + + fakeEndpoint { + assertEquals(expectedUuid.value, it["uuid"]) + }.apply { + overrideConfigurationInternal(PNConfigurationImpl(userId = expectedUuid)) + }.sync() + } + + @Test + fun testQueryParam() { + fakeEndpoint { + assertEquals("sf", it["city"]) + assertEquals(pubnub.configuration.userId.value, it["uuid"]) + assertEquals(4, it.size) + assertTrue(it.contains("pnsdk")) + assertTrue(it.contains("requestid")) + assertTrue(it.contains("uuid")) + }.apply { + queryParam += + mapOf( + "city" to "sf", + "uuid" to "overwritten", + ) + }.sync() + } + + @Test + fun testQueryParamEmpty() { + fakeEndpoint { + assertEquals(3, it.size) + assertTrue(it.contains("pnsdk")) + assertTrue(it.contains("requestid")) + assertTrue(it.contains("uuid")) + }.apply { + queryParam.clear() + }.sync() + } + + @Test + fun testQueryParamMissing() { + fakeEndpoint { + assertEquals(3, it.size) + assertTrue(it.contains("pnsdk")) + assertTrue(it.contains("requestid")) + assertTrue(it.contains("uuid")) + }.sync() + } + + @Test + fun testErrorAffectedChannelsAndChannelGroups() { + stubFor( + any(UrlPattern.ANY) + .willReturn( + aResponse() + .withStatus(400) + .withBody( + JsonObject().apply { + add( + "payload", + JsonObject().apply { + add( + "channels", + JsonArray().apply { + add("ch1") + add("ch2") + }, + ) + add("channel-groups", JsonArray().apply { add("cg1") }) + }, + ) + }.toString(), + ), + ), + ) + + val success = AtomicBoolean() + + pubnub.time() + .async { result -> + result.onFailure { + assertEquals(listOf("ch1", "ch2"), it.affectedChannels) + assertEquals(listOf("cg1"), it.affectedChannelGroups) + success.set(true) + } + } + + success.listen() + } + + @Test + fun testNoAffectedChannelsAndChannelGroups() { + stubFor( + any(UrlPattern.ANY) + .willReturn( + aResponse() + .withStatus(400) + .withBody("""{}"""), + ), + ) + + val success = AtomicBoolean() + + pubnub.time() + .async { result -> + result.onFailure { + it as PubNubException + assertTrue(it.affectedChannels.isEmpty()) + assertTrue(it.affectedChannelGroups.isEmpty()) + success.set(true) + } + } + + success.listen() + } + +// @Test // TODO investigate for Result changes +// fun testNoSecretKeySignatureParam() { +// pubnub.configuration.secretKey = "" +// +// stubFor( +// any(UrlPattern.ANY).willReturn( +// aResponse().withBody("""[100]""") +// ) +// ) +// +// val success = AtomicBoolean() +// +// pubnub.time() +// .async { result -> +// assertNull(status.param("signature")) +// success.set(true) +// } +// +// success.listen() +// } + +// @Test // TODO investigate for Result changes +// fun testSecretKeySignatureParam() { +// pubnub.configuration.secretKey = "mySecretKey" +// +// stubFor( +// any(UrlPattern.ANY).willReturn( +// aResponse().withBody("""[100]""") +// ) +// ) +// +// val success = AtomicBoolean() +// +// pubnub.time() +// .async { result -> +// assertNotNull(status.param("signature")) +// success.set(true) +// } +// +// success.listen() +// } + + @Test + fun testUnauthorized() { + stubFor( + any(UrlPattern.ANY) + .willReturn( + forbidden(), + ), + ) + + val success = AtomicBoolean() + + pubnub.time() + .async { result -> + assertTrue(result.isFailure) + result.onFailure { + assertEquals(403, (it as PubNubException).statusCode) + success.set(true) + } + } + + success.listen() + } + + private fun fakeEndpoint(paramsCondition: (map: HashMap) -> Unit) = + object : EndpointCore(pubnub) { + override fun doWork(queryParams: HashMap): Call { + paramsCondition.invoke(queryParams) + return fakeCall() + } + + override fun createResponse(input: Response) = this + + override fun operationType() = PNOperationType.PNSubscribeOperation + + override fun isSubKeyRequired() = false + + override fun isPubKeyRequired() = false + + override fun isAuthRequired() = false + + override fun getEndpointGroupName(): RetryableEndpointGroup = RetryableEndpointGroup.PUBLISH + } + + private fun fakeCall() = + object : Call { + override fun enqueue(callback: Callback) {} + + override fun isExecuted() = false + + override fun clone(): Call = this + + override fun isCanceled() = false + + override fun cancel() {} + + override fun execute(): Response = Response.success(null) + + override fun request() = Request.Builder().build() + + override fun timeout(): Timeout = Timeout.NONE + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/HeartbeatCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/HeartbeatCoreEndpointTest.kt new file mode 100644 index 000000000..3cba2c8ed --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/HeartbeatCoreEndpointTest.kt @@ -0,0 +1,261 @@ +package com.pubnub.api.legacy.endpoints + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.legacy.BaseTest +import com.pubnub.internal.endpoints.presence.HeartbeatEndpoint +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.failTest +import org.junit.Assert.assertEquals +import org.junit.Test + +class HeartbeatCoreEndpointTest : BaseTest() { + @Test + fun testSuccessOneChannel() { + config.presenceTimeout = 123 + // initPubNub() + + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + HeartbeatEndpoint(pubnub, listOf("ch1")).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + + val request = requests[0] + assertEquals("myUUID", request.queryParameter("uuid").firstValue()) + assertEquals("123", request.queryParameter("heartbeat").firstValue()) + } + + @Test + fun testSuccessManyChannels() { + config.presenceTimeout = 123 + // initPubNub() + + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1,ch2/heartbeat")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + HeartbeatEndpoint(pubnub, listOf("ch1", "ch2")).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + + val request = requests[0] + assertEquals("myUUID", request.queryParameter("uuid").firstValue()) + assertEquals("123", request.queryParameter("heartbeat").firstValue()) + } + + @Test + fun testSuccessOneChannelGroup() { + config.presenceTimeout = 123 + // initPubNub() + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/heartbeat")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + HeartbeatEndpoint(pubnub, channelGroups = listOf("cg1")).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + + val request = requests[0] + assertEquals("myUUID", request.queryParameter("uuid").firstValue()) + assertEquals("cg1", request.queryParameter("channel-group").firstValue()) + assertEquals("123", request.queryParameter("heartbeat").firstValue()) + } + + @Test + fun testSuccessManyChannelGroups() { + config.presenceTimeout = 123 + // initPubNub() + + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/heartbeat")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + HeartbeatEndpoint(pubnub, channelGroups = listOf("cg1", "cg2")).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + + val request = requests[0] + assertEquals("myUUID", request.queryParameter("uuid").firstValue()) + assertEquals("cg1,cg2", request.queryParameter("channel-group").firstValue()) + assertEquals("123", request.queryParameter("heartbeat").firstValue()) + } + + @Test + fun testMissingChannelAndGroupSync() { + config.presenceTimeout = 123 + // initPubNub() + + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + try { + HeartbeatEndpoint(pubnub).sync() + failTest() + } catch (e: Exception) { + assertPnException( + PubNubError.CHANNEL_AND_GROUP_MISSING, + e, + ) + } + } + + @Test + fun testIsAuthRequiredSuccessSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "myKey" + // initPubNub() + + HeartbeatEndpoint(pubnub, listOf("ch1")).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testBlankSubKeySync() { + config.presenceTimeout = 123 + + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + config.subscribeKey = " " + // initPubNub() + + try { + HeartbeatEndpoint(pubnub, listOf("ch1")).sync() + failTest() + } catch (e: Exception) { + assertPnException( + PubNubError.SUBSCRIBE_KEY_MISSING, + e, + ) + } + } + + @Test + fun testEmptySubKeySync() { + config.presenceTimeout = 123 + + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + config.subscribeKey = "" + // initPubNub() + + try { + HeartbeatEndpoint(pubnub, listOf("ch1")).sync() + failTest() + } catch (e: Exception) { + assertPnException( + PubNubError.SUBSCRIBE_KEY_MISSING, + e, + ) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/access/GrantEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/access/GrantEndpointTest.kt new file mode 100644 index 000000000..a42900d8d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/access/GrantEndpointTest.kt @@ -0,0 +1,1964 @@ +package com.pubnub.api.legacy.endpoints.access + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.matching +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.legacy.BaseTest +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeyData +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.failTest +import com.pubnub.test.SignatureUtils.decomposeAndVerifySignature +import org.awaitility.Awaitility +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean + +class GrantEndpointTest : BaseTest() { + override fun onBefore() { + super.onBefore() + config.secretKey = "secretKey" + config.includeInstanceIdentifier = true + config.includeRequestIdentifier = true + } + + @Test + fun noGroupsOneChannelOneKeyTest() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "user", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + ).sync() + + assertEquals(1, result.channels.size) + assertEquals(0, result.channelGroups.size) + assertEquals(1, result.channels["ch1"]!!.size) // todo replace with error + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key1"]!!.javaClass, + ) + + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun `can verify signature generated with config override`() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/aaa")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "user", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + pubnub // this creates the pubnub instance using the default config + // so we can now create a second config to use for override: + config.subscribeKey = "aaa" + config.publishKey = "bbb" + config.secretKey = "ccc" + val override = config.build() + + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + ).apply { + overrideConfiguration(override) + }.sync() + + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/aaa.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(override, requests[0]) + } + + @Test + fun noGroupsOneChannelTwoKeyTest() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "user", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1", "key2"), + channels = listOf("ch1"), + ).sync() + + assertEquals(1, result.channels.size) + assertEquals(0, result.channelGroups.size) + assertEquals(2, result.channels["ch1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key2"]!!.javaClass, + ) + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun noGroupsTwoChannelOneKeyTest() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1,ch2")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "user", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channels": { + "ch1": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "ch2": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1", "ch2"), + ).sync() + + assertEquals(2, result.channels.size) + assertEquals(0, result.channelGroups.size) + assertEquals(1, result.channels["ch1"]!!.size) + assertEquals(1, result.channels["ch2"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch2"]!!["key1"]!!.javaClass, + ) + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun noGroupsTwoChannelTwoKeyTest() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1,ch2")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "user", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channels": { + "ch1": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "ch2": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = (listOf("key1", "key2")), + channels = (listOf("ch1", "ch2")), + ).sync() + + assertEquals(2, result.channels.size) + assertEquals(0, result.channelGroups.size) + assertEquals(2, result.channels["ch1"]!!.size) + assertEquals(2, result.channels["ch2"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch2"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key2"]!!.javaClass, + ) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch2"]!!["key2"]!!.javaClass) + val requests = findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun oneGroupNoChannelOneKey() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "channel-group+auth", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel-groups": "cg1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1"), + channelGroups = listOf("cg1"), + ).sync() + + assertEquals(0, result.channels.size) + assertEquals(1, result.channelGroups.size) + assertEquals(1, result.channelGroups["cg1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key1"]!!.javaClass, + ) + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun oneGroupNoChannelTwoKey() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "channel-group+auth", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel-groups": "cg1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1", "key2"), + channelGroups = listOf("cg1"), + ).sync() + + assertEquals(0, result.channels.size) + assertEquals(1, result.channelGroups.size) + assertEquals(2, result.channelGroups["cg1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key2"]!!.javaClass, + ) + + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun oneGroupOneChannelOneKey() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "channel-group+auth", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + }, + "channel-groups": "cg1" + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + channelGroups = listOf("cg1"), + ).sync() + + assertEquals(1, result.channels.size) + assertEquals(1, result.channelGroups.size) + assertEquals(1, result.channelGroups["cg1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key1"]!!.javaClass, + ) + assertEquals(1, result.channels["ch1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key1"]!!.javaClass, + ) + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun oneGroupOneChannelTwoKey() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "channel-group+auth", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + }, + "channel-groups": "cg1" + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1", "key2"), + channels = listOf("ch1"), + channelGroups = listOf("cg1"), + ).sync() + + assertEquals(1, result.channels.size) + assertEquals(1, result.channelGroups.size) + assertEquals(2, result.channelGroups["cg1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key2"]!!.javaClass, + ) + assertEquals(2, result.channels["ch1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key1"]!!.javaClass, + ) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch1"]!!["key2"]!!.javaClass) + val requests = findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun oneGroupTwoChannelOneKey() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1,ch2")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "channel-group+auth", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channels": { + "ch1": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "ch2": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + } + }, + "channel-groups": "cg1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1", "ch2"), + channelGroups = listOf("cg1"), + ).sync() + + assertEquals(2, result.channels.size) + assertEquals(1, result.channelGroups.size) + assertEquals(1, result.channelGroups["cg1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key1"]!!.javaClass, + ) + assertEquals(1, result.channels["ch1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch2"]!!["key1"]!!.javaClass, + ) + val requests = findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun oneGroupTwoChannelTwoKey() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1,ch2")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "channel-group+auth", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channels": { + "ch1": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "ch2": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + } + }, + "channel-groups": "cg1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1", "key2"), + channels = listOf("ch1", "ch2"), + channelGroups = listOf("cg1"), + ).sync() + + assertEquals(2, result.channels.size) + assertEquals(1, result.channelGroups.size) + assertEquals(2, result.channelGroups["cg1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key2"]!!.javaClass, + ) + assertEquals(2, result.channels["ch1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key1"]!!.javaClass, + ) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch1"]!!["key2"]!!.javaClass) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch2"]!!["key1"]!!.javaClass) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch2"]!!["key2"]!!.javaClass) + + val requests = findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun twoGroupNoChannelOneKey() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel-group", matching("cg1,cg2")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "channel-group+auth", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel-groups": { + "cg1": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "cg2": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1"), + channelGroups = listOf("cg1", "cg2"), + ).sync() + + assertEquals(0, result.channels.size) + assertEquals(2, result.channelGroups.size) + assertEquals(1, result.channelGroups["cg1"]!!.size) + assertEquals(1, result.channelGroups["cg2"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg2"]!!["key1"]!!.javaClass, + ) + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun twoGroupNoChannelTwoKey() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel-group", matching("cg1,cg2")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "channel-group+auth", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel-groups": { + "cg1": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "cg2": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1", "key2"), + channelGroups = listOf("cg1", "cg2"), + ).sync() + + assertEquals(0, result.channels.size) + assertEquals(2, result.channelGroups.size) + assertEquals(2, result.channelGroups["cg1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key2"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg2"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg2"]!!["key2"]!!.javaClass, + ) + + val requests = findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun twoGroupOneChannelOneKey() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("channel-group", matching("cg1,cg2")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "channel-group+auth", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + }, + "channel-groups": { + "cg1": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "cg2": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + channelGroups = listOf("cg1", "cg2"), + ).sync() + + assertEquals(1, result.channels.size) + assertEquals(2, result.channelGroups.size) + assertEquals(1, result.channelGroups["cg1"]!!.size) + assertEquals(1, result.channelGroups["cg2"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg2"]!!["key1"]!!.javaClass, + ) + assertEquals(1, result.channels["ch1"]!!.size) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch1"]!!["key1"]!!.javaClass) + + val requests = findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun twoGroupOneChannelTwoKey() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("channel-group", matching("cg1,cg2")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "channel-group+auth", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + }, + "channel-groups": { + "cg1": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "cg2": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1", "key2"), + channels = listOf("ch1"), + channelGroups = listOf("cg1", "cg2"), + ).sync() + + assertEquals(1, result.channels.size) + assertEquals(2, result.channelGroups.size) + assertEquals(2, result.channelGroups["cg1"]!!.size) + assertEquals(2, result.channelGroups["cg2"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key2"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg2"]!!["key1"]!!.javaClass, + ) + assertEquals(PNAccessManagerKeyData::class.java, result.channelGroups["cg2"]!!["key2"]!!.javaClass) + assertEquals(2, result.channels["ch1"]!!.size) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch1"]!!["key1"]!!.javaClass) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch1"]!!["key2"]!!.javaClass) + + val requests = findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun twoGroupTwoChannelOneKey() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1,ch2")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "channel-group+auth", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channels": { + "ch1": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "ch2": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + } + }, + "channel-groups": { + "cg1": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "cg2": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1", "ch2"), + channelGroups = listOf("cg1", "cg2"), + ).sync() + + assertEquals(2, result.channels.size) + assertEquals(2, result.channelGroups.size) + assertEquals(1, result.channelGroups["cg1"]!!.size) + assertEquals(1, result.channelGroups["cg2"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg2"]!!["key1"]!!.javaClass, + ) + assertEquals(1, result.channels["ch1"]!!.size) + assertEquals(1, result.channels["ch2"]!!.size) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch1"]!!["key1"]!!.javaClass) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch2"]!!["key1"]!!.javaClass) + + val requests = findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun twoGroupTwoChannelTwoKey() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1,ch2")) + .withQueryParam("channel-group", matching("cg1,cg2")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "channel-group+auth", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channels": { + "ch1": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "ch2": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + } + }, + "channel-groups": { + "cg1": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "cg2": { + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + }, + "key2": { + "r": 0, + "w": 0, + "m": 0 + } + } + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1", "key2"), + channels = listOf("ch1", "ch2"), + channelGroups = listOf("cg1", "cg2"), + ).sync() + + assertEquals(2, result.channels.size) + assertEquals(2, result.channelGroups.size) + assertEquals(2, result.channelGroups["cg1"]!!.size) + assertEquals(2, result.channelGroups["cg2"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key1"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg1"]!!["key2"]!!.javaClass, + ) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channelGroups["cg2"]!!["key1"]!!.javaClass, + ) + assertEquals(PNAccessManagerKeyData::class.java, result.channelGroups["cg2"]!!["key2"]!!.javaClass) + assertEquals(2, result.channels["ch1"]!!.size) + assertEquals(2, result.channels["ch2"]!!.size) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch1"]!!["key1"]!!.javaClass) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch1"]!!["key2"]!!.javaClass) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch2"]!!["key1"]!!.javaClass) + assertEquals(PNAccessManagerKeyData::class.java, result.channels["ch2"]!!["key2"]!!.javaClass) + + val requests = findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun noGroupsOneChannelOneKeyTTLTest() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .withQueryParam("ttl", matching("1334")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "user", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + ttl = 1334, + ).sync() + + assertEquals(1, result.channels.size) + assertEquals(0, result.channelGroups.size) + assertEquals(1, result.channels["ch1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key1"]!!.javaClass, + ) + + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun noGroupsOneChannelOneReadKeyTest() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("1")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "user", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + read = true, + ).sync() + + assertEquals(1, result.channels.size) + assertEquals(0, result.channelGroups.size) + assertEquals(1, result.channels["ch1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key1"]!!.javaClass, + ) + + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun noGroupsOneChannelOneWriteKeyTest() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("1")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "user", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + write = true, + ).sync() + + assertEquals(1, result.channels.size) + assertEquals(0, result.channelGroups.size) + assertEquals(1, result.channels["ch1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key1"]!!.javaClass, + ) + + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun noGroupsOneChannelOneDeleteKeyTest() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .withQueryParam("d", matching("1")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "user", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + delete = true, + ).sync() + + assertEquals(1, result.channels.size) + assertEquals(0, result.channelGroups.size) + assertEquals(1, result.channels["ch1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key1"]!!.javaClass, + ) + + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun noGroupsOneChannelOneKeyManageTest() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("1")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "user", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + manage = true, + ).sync() + + assertEquals(1, result.channels.size) + assertEquals(0, result.channelGroups.size) + assertEquals(1, result.channels["ch1"]!!.size) + assertEquals( + PNAccessManagerKeyData::class.java, + result.channels["ch1"]!!["key1"]!!.javaClass, + ) + + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun testIsAuthRequiredSuccessSync() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "user", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + config.authKey = "myKey" + // initPubNub() + + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + ).sync() + + val requests = + findAll(getRequestedFor(urlMatching("/v2/auth/grant/sub-key/mySubscribeKey.*"))) + assertEquals(1, requests.size) + + decomposeAndVerifySignature(pubnub.configuration, requests[0]) + } + + @Test + fun testOperationTypeSuccessAsync() { + val atomic = AtomicBoolean(false) + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "user", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + ).async { result -> + assertFalse(result.isFailure) + atomic.set(true) + } + + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilTrue(atomic) + } + + @Test + fun testBlankSecretKey() { + config.secretKey = " " + // initPubNub() + + try { + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.SECRET_KEY_MISSING, e) + } + } + + @Test + fun testEmptySecretKey() { + config.secretKey = "" + // initPubNub() + try { + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.SECRET_KEY_MISSING, e) + } + } + + @Test + fun testBlankSubscribeKey() { + config.subscribeKey = " " + // initPubNub() + try { + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testEmptySubscribeKey() { + config.subscribeKey = "" + // initPubNub() + try { + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testMissingChannelsAndChannelGroup() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withStatus(200).withBody( + """ + { + "message": "Success", + "payload": { + "level": "subkey", + "subscribe_key": "mySubscribeKey", + "ttl": 1440, + "r": 0, + "w": 1, + "m": 0, + "d": 0 + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + try { + val grantResult = pubnub.grant().sync() + assertEquals("subkey", grantResult.level) + assertEquals(1440, grantResult.ttl) + assertEquals(0, grantResult.channels.size) + assertEquals(0, grantResult.channelGroups.size) + } catch (e: PubNubException) { + failTest() + } + } + + @Test + fun testNullPayload() { + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """{"message":"Success","service":"Access Manager","status":200}""", + ), + ), + ) + + try { + pubnub.grant( + authKeys = listOf("key1"), + channels = listOf("ch1"), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testNullAuthKeyAsync() { + val atomic = AtomicBoolean(false) + stubFor( + get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "message": "Success", + "payload": { + "level": "user", + "subscribe_key": "sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", + "ttl": 1, + "channel": "ch1", + "auths": { + "key1": { + "r": 0, + "w": 0, + "m": 0 + } + } + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + pubnub.grant( + channels = listOf("ch1"), + ).async { result -> + result.onSuccess { + atomic.set(true) + } + } + + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilTrue(atomic) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/AddChannelBaseChannelGroupCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/AddChannelBaseChannelGroupCoreEndpointTest.kt new file mode 100644 index 000000000..032b694f8 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/AddChannelBaseChannelGroupCoreEndpointTest.kt @@ -0,0 +1,144 @@ +package com.pubnub.api.legacy.endpoints.channel_groups + +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.matching +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.emptyJson +import org.awaitility.Awaitility +import org.hamcrest.core.IsEqual +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger + +class AddChannelBaseChannelGroupCoreEndpointTest : BaseTest() { + @Test + fun testSyncSuccess() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .withQueryParam("add", matching("ch1,ch2")) + .willReturn(emptyJson()), + ) + + pubnub.addChannelsToChannelGroup( + channelGroup = "groupA", + channels = listOf("ch1", "ch2"), + ).sync() + } + + @Test + fun testSyncGroupIsEmpty() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(emptyJson()), + ) + + try { + pubnub.addChannelsToChannelGroup( + channelGroup = "", + channels = listOf("ch1", "ch2"), + ).sync() + } catch (e: Exception) { + assertPnException(PubNubError.GROUP_MISSING, e) + } + } + + @Test + fun testIsSubRequiredSuccessSync() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .withQueryParam("add", matching("ch1,ch2")) + .willReturn(emptyJson()), + ) + + config.subscribeKey = " " + // initPubNub() + + try { + pubnub.addChannelsToChannelGroup( + channelGroup = "groupA", + channels = listOf("ch1", "ch2"), + ).sync() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testIsAuthRequiredSuccessSync() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .withQueryParam("add", matching("ch1,ch2")) + .willReturn(emptyJson()), + ) + config.authKey = "myKey" + // initPubNub() + + pubnub.addChannelsToChannelGroup( + channelGroup = "groupA", + channels = listOf("ch1", "ch2"), + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testOperationTypeSuccessAsync() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .withQueryParam("add", matching("ch1,ch2")) + .willReturn(emptyJson()), + ) + + val atomic = AtomicInteger(0) + + pubnub.addChannelsToChannelGroup( + channelGroup = "groupA", + channels = listOf("ch1", "ch2"), + ).async { result -> + assertFalse(result.isFailure) + result.onSuccess { + atomic.incrementAndGet() + } + } + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testErrorBodyForbidden() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .withQueryParam("add", matching("ch1,ch2")) + .willReturn(emptyJson().withStatus(403)), + ) + + val atomic = AtomicInteger(0) + + pubnub.addChannelsToChannelGroup( + channelGroup = "groupA", + channels = listOf("ch1", "ch2"), + ).async { result -> + assertTrue(result.isFailure) + result.onFailure { + assertEquals(403, it.statusCode) + atomic.incrementAndGet() + } + } + + Awaitility.await().atMost(15, TimeUnit.SECONDS).untilAtomic(atomic, IsEqual.equalTo(1)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/AllChannelsBaseChannelGroupCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/AllChannelsBaseChannelGroupCoreEndpointTest.kt new file mode 100644 index 000000000..2a2b0d756 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/AllChannelsBaseChannelGroupCoreEndpointTest.kt @@ -0,0 +1,152 @@ +package com.pubnub.api.legacy.endpoints.channel_groups + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import org.awaitility.Awaitility +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers +import org.hamcrest.core.IsEqual +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger + +class AllChannelsBaseChannelGroupCoreEndpointTest : BaseTest() { + @Test + fun testSyncSuccess() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": [ + "a", + "b" + ] + }, + "service": "ChannelGroups" + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.listChannelsForChannelGroup( + channelGroup = "groupA", + ).sync() + + assertThat(response.channels, Matchers.contains("a", "b")) + } + + @Test + fun testSyncEmptyGroup() { + try { + pubnub.listChannelsForChannelGroup( + channelGroup = " ", + ).sync() + } catch (e: Exception) { + assertPnException(PubNubError.GROUP_MISSING, e) + } + } + + @Test + fun testIsAuthRequiredSuccessSync() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": [ + "a", + "b" + ] + }, + "service": "ChannelGroups" + } + """.trimIndent(), + ), + ), + ) + config.authKey = "myKey" + + pubnub.listChannelsForChannelGroup( + channelGroup = "groupA", + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testOperationTypeSuccessAsync() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": [ + "a", + "b" + ] + }, + "service": "ChannelGroups" + } + """.trimIndent(), + ), + ), + ) + + val atomic = AtomicInteger(0) + + pubnub.listChannelsForChannelGroup( + channelGroup = "groupA", + ).async { result -> + assertFalse(result.isFailure) + result.onSuccess { + assertEquals(listOf("a", "b"), it.channels) + atomic.incrementAndGet() + } + } + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testIsSubRequiredSuccessSync() { + config.subscribeKey = " " + try { + pubnub.listChannelsForChannelGroup( + channelGroup = "groupA", + ).sync() + throw RuntimeException() + } catch (e: PubNubException) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/DeleteBaseChannelGroupCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/DeleteBaseChannelGroupCoreEndpointTest.kt new file mode 100644 index 000000000..3dadcfa53 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/DeleteBaseChannelGroupCoreEndpointTest.kt @@ -0,0 +1,114 @@ +package com.pubnub.api.legacy.endpoints.channel_groups + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.any +import com.github.tomakehurst.wiremock.client.WireMock.anyUrl +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.noContent +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.failTest +import org.awaitility.Awaitility +import org.hamcrest.core.IsEqual +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger + +class DeleteBaseChannelGroupCoreEndpointTest : BaseTest() { + @Test + fun testSyncSuccess() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA/remove")) + .willReturn( + aResponse().withBody( + """{"status":200,"message":"OK","payload":{},"service":"ChannelGroups"}""", + ), + ), + ) + + pubnub.deleteChannelGroup( + channelGroup = "groupA", + ).sync() + } + + @Test + fun testSyncEmptyGroup() { + stubFor(any(anyUrl()).willReturn(aResponse())) + + try { + pubnub.deleteChannelGroup( + channelGroup = " ", + ).sync() + failTest() + } catch (e: PubNubException) { + assertPnException(PubNubError.GROUP_MISSING, e) + } + } + + @Test + fun testIsAuthRequiredSuccessSync() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA/remove")) + .willReturn( + aResponse().withBody( + """{"status": 200, "message": "OK", "payload": {}, "service": "ChannelGroups"}""", + ), + ), + ) + + config.authKey = "myKey" + + pubnub.deleteChannelGroup( + channelGroup = "groupA", + ).sync() + + val requests = + findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testMalformedResponse() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA/remove")) + .willReturn(noContent()), + ) + + pubnub.deleteChannelGroup( + channelGroup = "groupA", + ).sync() + } + + @Test + fun testOperationTypeSuccessAsync() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA/remove")) + .willReturn( + aResponse().withBody( + """{"status": 200, "message": "OK", "payload": {}, "service": "ChannelGroups"}""", + ), + ), + ) + + val atomic = AtomicInteger(0) + + pubnub.deleteChannelGroup( + channelGroup = "groupA", + ).async { result -> + assertFalse(result.isFailure) + atomic.incrementAndGet() + } + + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAtomic(atomic, IsEqual.equalTo(1)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/ListAllBaseChannelGroupCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/ListAllBaseChannelGroupCoreEndpointTest.kt new file mode 100644 index 000000000..13b5ec0c2 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/ListAllBaseChannelGroupCoreEndpointTest.kt @@ -0,0 +1,159 @@ +package com.pubnub.api.legacy.endpoints.channel_groups + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.failTest +import org.awaitility.Awaitility +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers +import org.hamcrest.core.IsEqual +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger + +class ListAllBaseChannelGroupCoreEndpointTest : BaseTest() { + @Test + fun testSyncSuccess() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "groups": [ + "a", + "b" + ] + }, + "service": "ChannelGroups" + } + """.trimIndent(), + ), + ), + ) + + val response = pubnub.listAllChannelGroups().sync() + assertThat(response.groups, Matchers.contains("a", "b")) // todo matchers + } + + @Test + fun testNullPayload() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "ChannelGroups" + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.listAllChannelGroups().sync() + failTest() + } catch (e: PubNubException) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testNullBody() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group")) + .willReturn(aResponse()), + ) + + try { + pubnub.listAllChannelGroups().sync() + failTest() + } catch (e: PubNubException) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testIsAuthRequiredSuccessSync() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "groups": [ + "a", + "b" + ] + }, + "service": "ChannelGroups" + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "myKey" + + pubnub.listAllChannelGroups().sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testOperationTypeSuccessAsync() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "groups": [ + "a", + "b" + ] + }, + "service": "ChannelGroups" + } + """.trimIndent(), + ), + ), + ) + + val atomic = AtomicInteger(0) + + pubnub.listAllChannelGroups() + .async { result -> + assertFalse(result.isFailure) + atomic.incrementAndGet() + } + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, IsEqual.equalTo(1)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/RemoveChannelBaseChannelGroupCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/RemoveChannelBaseChannelGroupCoreEndpointTest.kt new file mode 100644 index 000000000..1917139ab --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/channel_groups/RemoveChannelBaseChannelGroupCoreEndpointTest.kt @@ -0,0 +1,104 @@ +package com.pubnub.api.legacy.endpoints.channel_groups + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.legacy.BaseTest +import org.awaitility.Awaitility +import org.hamcrest.core.IsEqual +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger + +class RemoveChannelBaseChannelGroupCoreEndpointTest : BaseTest() { + @Test + fun testSyncSuccess() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": {}, + "service": "ChannelGroups" + } + """.trimIndent(), + ), + ), + ) + + pubnub.removeChannelsFromChannelGroup( + channelGroup = "groupA", + channels = arrayListOf("ch1", "ch2"), + ).sync() + } + + @Test + fun testIsAuthRequiredSuccessSync() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": {}, + "service": "ChannelGroups" + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "myKey" + + pubnub.removeChannelsFromChannelGroup( + channelGroup = "groupA", + channels = arrayListOf("ch1", "ch2"), + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testOperationTypeSuccessAsync() { + stubFor( + get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": {}, + "service": "ChannelGroups" + } + """.trimIndent(), + ), + ), + ) + + val atomic = AtomicInteger(0) + + pubnub.removeChannelsFromChannelGroup( + channelGroup = "groupA", + channels = arrayListOf("ch1", "ch2"), + ).async { result -> + assertFalse(result.isFailure) + atomic.incrementAndGet() + } + + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAtomic(atomic, IsEqual.equalTo(1)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/GetFileUrlTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/GetFileUrlTest.kt new file mode 100644 index 000000000..d7d89823f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/GetFileUrlTest.kt @@ -0,0 +1,125 @@ +package com.pubnub.api.legacy.endpoints.files + +import com.pubnub.api.PubNubException +import com.pubnub.api.UserId +import com.pubnub.api.retry.RetryConfiguration +import com.pubnub.api.v2.PNConfiguration +import com.pubnub.internal.PubNubImpl +import okhttp3.HttpUrl.Companion.toHttpUrl +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers +import org.junit.Assert +import org.junit.Test + +class GetFileUrlTest { + private val channel = "channel" + private val fileName = "fileName" + private val fileId = "fileId" + private val defaultQueryParams: Set = HashSet(listOf("pnsdk", "requestid", "uuid")) + + @Test + @Throws(PubNubException::class) + fun noAdditionalQueryParamsWhenNotSecretNorAuth() { + // given + val pubnub = PubNubImpl(config().build()) + + // when + val url = + pubnub.getFileUrl( + channel = channel, + fileName = fileName, + fileId = fileId, + ).sync().url + + // then + val queryParamNames = queryParameterNames(url) + queryParamNames.removeAll(defaultQueryParams) + Assert.assertEquals(emptySet(), queryParamNames) + } + + @Test + @Throws(PubNubException::class) + fun signatureAndTimestampQueryParamsAreSetWhenSecret() { + // given + val pubnub = PubNubImpl(withSecret(config()).build()) + + // when + val url = + pubnub.getFileUrl( + channel = channel, + fileName = fileName, + fileId = fileId, + ).sync().url + + // then + val queryParamNames = queryParameterNames(url) + queryParamNames.removeAll(defaultQueryParams) + assertThat>(queryParamNames, Matchers.containsInAnyOrder("signature", "timestamp")) + } + + @Test + @Throws(PubNubException::class) + fun authQueryParamIsSetWhenAuth() { + // given + val pubnub = PubNubImpl(withAuth(config()).build()) + + // when + val url = + pubnub.getFileUrl( + channel = channel, + fileName = fileName, + fileId = fileId, + ).sync().url + + // then + val queryParamNames = queryParameterNames(url) + queryParamNames.removeAll(defaultQueryParams) + assertThat>(queryParamNames, Matchers.containsInAnyOrder("auth")) + } + + @Test + @Throws(PubNubException::class) + fun signatureAndTimestampAndAuthQueryParamsAreSetWhenSecretAndAuth() { + // given + val pubnub = PubNubImpl(withSecret(withAuth(config())).build()) + + // when + val url = + pubnub.getFileUrl( + channel = channel, + fileName = fileName, + fileId = fileId, + ).sync().url + + // then + println(url) + val queryParamNames = queryParameterNames(url) + queryParamNames.removeAll(defaultQueryParams) + assertThat>( + queryParamNames, + Matchers.containsInAnyOrder("auth", "signature", "timestamp"), + ) + } + + private fun config(): PNConfiguration.Builder { + val config = PNConfiguration.builder(userId = UserId(PubNubImpl.generateUUID()), "") + config.publishKey = "pk" + config.subscribeKey = "sk" + config.retryConfiguration = RetryConfiguration.Linear(delayInSec = 4, maxRetryNumber = 3) + return config + } + + private fun withSecret(config: PNConfiguration.Builder): PNConfiguration.Builder { + config.secretKey = "secK" + return config + } + + private fun withAuth(config: PNConfiguration.Builder): PNConfiguration.Builder { + config.authKey = "ak" + return config + } + + private fun queryParameterNames(url: String): MutableCollection { + return HashSet(url.toHttpUrl().queryParameterNames) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/SendFileTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/SendFileTest.kt new file mode 100644 index 000000000..7bf735c51 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/SendFileTest.kt @@ -0,0 +1,286 @@ +package com.pubnub.api.legacy.endpoints.files + +import com.pubnub.api.PubNubException +import com.pubnub.api.legacy.endpoints.remoteaction.TestRemoteAction +import com.pubnub.api.models.consumer.files.PNBaseFile +import com.pubnub.api.models.consumer.files.PNFileUploadResult +import com.pubnub.api.models.consumer.files.PNPublishFileMessageResult +import com.pubnub.api.retry.RetryConfiguration +import com.pubnub.api.v2.PNConfiguration +import com.pubnub.api.v2.callbacks.Result +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.endpoints.files.GenerateUploadUrlEndpoint +import com.pubnub.internal.endpoints.files.PublishFileMessageEndpoint +import com.pubnub.internal.endpoints.files.SendFileEndpoint +import com.pubnub.internal.endpoints.files.UploadFileEndpoint +import com.pubnub.internal.models.server.files.FileUploadRequestDetails +import com.pubnub.internal.models.server.files.FormField +import io.mockk.every +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify +import org.junit.Assert +import org.junit.jupiter.api.Test +import java.io.InputStream +import java.time.Instant +import java.util.concurrent.CountDownLatch +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger +import java.util.function.Consumer + +class SendFileTest : TestsWithFiles { + private val channel = "channel" + private val generateUploadUrlFactory: GenerateUploadUrlEndpoint.Factory = mockk {} + private val publishFileMessageFactory: PublishFileMessageEndpoint.Factory = mockk {} + private val sendFileToS3Factory: UploadFileEndpoint.Factory = mockk {} + + @Test + fun sync_happyPath() { + // given + val fileUploadRequestDetails = generateUploadUrlProperResponse() + val expectedResponse = pnFileUploadResult() + val publishFileMessageResult = PNPublishFileMessageResult(expectedResponse.timetoken) + every { generateUploadUrlFactory.create(any(), any()) } returns + TestRemoteAction.successful( + fileUploadRequestDetails, + ) + + every { sendFileToS3Factory.create(any(), any(), any()) } returns TestRemoteAction.successful(Unit) + val publishFileMessage: PublishFileMessageEndpoint = + AlwaysSuccessfulPublishFileMessage.create(publishFileMessageResult, getPubNubMock()) + every { publishFileMessageFactory.create(any(), any(), any()) } returns publishFileMessage + + // when + val result: PNFileUploadResult? = + inputStream().use { inputStream -> + sendFile(channel, fileName(), inputStream).sync() + } + + // then + Assert.assertEquals(expectedResponse, result) + } + + @Test + fun async_happyPath() { + // given + val countDownLatch = CountDownLatch(1) + val fileUploadRequestDetails = generateUploadUrlProperResponse() + val expectedResponse = pnFileUploadResult() + val publishFileMessageResult = PNPublishFileMessageResult(expectedResponse.timetoken) + every { generateUploadUrlFactory.create(any(), any()) } returns + TestRemoteAction.successful( + fileUploadRequestDetails, + ) + + every { sendFileToS3Factory.create(any(), any(), any()) } returns TestRemoteAction.successful(Unit) + val publishFileMessage: PublishFileMessageEndpoint = + AlwaysSuccessfulPublishFileMessage.create(publishFileMessageResult, getPubNubMock()) + every { publishFileMessageFactory.create(any(), any(), any()) } returns publishFileMessage + inputStream().use { inputStream -> + sendFile( + channel, + fileName(), + inputStream, + ).async { result -> + result.onSuccess { + Assert.assertEquals(expectedResponse, it) + countDownLatch.countDown() + } + } + } + Assert.assertTrue(countDownLatch.await(1, TimeUnit.SECONDS)) + } + + @Test + fun async_publishFileMessageRetry() { + // given + val countDownLatch = CountDownLatch(1) + val fileUploadRequestDetails = generateUploadUrlProperResponse() + val expectedResponse = pnFileUploadResult() + val publishFileMessageResult = PNPublishFileMessageResult(expectedResponse.timetoken) + val numberOfRetries = 5 + every { generateUploadUrlFactory.create(any(), any()) } returns + TestRemoteAction.successful( + fileUploadRequestDetails, + ) + + every { sendFileToS3Factory.create(any(), any(), any()) } returns TestRemoteAction.successful(Unit) + val publishFileMessage: PublishFileMessageEndpoint = + spyk( + FailingPublishFileMessage.create( + publishFileMessageResult, + numberOfRetries - 1, + getPubNubMock(), + ), + ) + every { publishFileMessageFactory.create(any(), any(), any()) } returns publishFileMessage + inputStream().use { inputStream -> + sendFile( + channel, + fileName(), + inputStream, + numberOfRetries, + ).async { result -> + result.onSuccess { + Assert.assertEquals(expectedResponse, it) + countDownLatch.countDown() + } + } + } + + // then + Assert.assertTrue(countDownLatch.await(1, TimeUnit.SECONDS)) + verify(exactly = numberOfRetries) { publishFileMessage.sync() } + } + + @Test + fun sync_publishFileMessageRetry() { + // given + val fileUploadRequestDetails = generateUploadUrlProperResponse() + val expectedResponse = pnFileUploadResult() + val publishFileMessageResult = PNPublishFileMessageResult(expectedResponse.timetoken) + val numberOfRetries = 5 + every { generateUploadUrlFactory.create(any(), any()) } returns + TestRemoteAction.successful( + fileUploadRequestDetails, + ) + + every { sendFileToS3Factory.create(any(), any(), any()) } returns TestRemoteAction.successful(Unit) + val publishFileMessage: PublishFileMessageEndpoint = + spyk( + FailingPublishFileMessage.create( + publishFileMessageResult, + numberOfRetries - 1, + getPubNubMock(), + ), + ) + every { publishFileMessageFactory.create(any(), any(), any()) } returns publishFileMessage + + // when + val result: PNFileUploadResult? = + inputStream().use { inputStream -> + sendFile(channel, fileName(), inputStream, numberOfRetries).sync() + } + + // then + Assert.assertEquals(expectedResponse, result) + verify(exactly = numberOfRetries) { publishFileMessage.sync() } + } + + private fun generateUploadUrlProperResponse(): FileUploadRequestDetails { + return FileUploadRequestDetails( + 200, + PNBaseFile("id", "name"), + "url", + "GET", + Instant.now().plusSeconds(50).toString(), + FormField("key", "value"), + emptyList(), + ) + } + + private fun pnFileUploadResult(): PNFileUploadResult { + return PNFileUploadResult(1337L, 200, PNBaseFile("id", "name")) + } + + private fun sendFile( + channel: String, + fileName: String, + inputStream: InputStream, + numberOfRetries: Int = 1, + ): SendFileEndpoint { + return SendFileEndpoint( + channel = channel, + fileName = fileName, + inputStream = inputStream, + generateUploadUrlFactory = generateUploadUrlFactory, + publishFileMessageFactory = publishFileMessageFactory, + sendFileToS3Factory = sendFileToS3Factory, + executorService = Executors.newSingleThreadExecutor(), + fileMessagePublishRetryLimit = numberOfRetries, + ) + } + + private fun getPubNubMock(): PubNubImpl { + val mockConfig = mockk() + val mockPubNub = mockk() + val retryConfiguration = RetryConfiguration.None + every { mockConfig.retryConfiguration } returns retryConfiguration + every { mockPubNub.configuration } returns mockConfig + return mockPubNub + } + + internal class FailingPublishFileMessage( + private val result: PNPublishFileMessageResult, + private val numberOfFailsBeforeSuccess: Int, + pubNub: PubNubImpl, + ) : + PublishFileMessageEndpoint( + channel = "channel", + fileName = "fileName", + fileId = "fileId", + pubNub = pubNub, + ) { + private val numberOfFails = AtomicInteger(0) + + override fun async(callback: Consumer>) { + if (numberOfFails.getAndAdd(1) < numberOfFailsBeforeSuccess) { + callback.accept(Result.failure(PubNubException())) + } else { + callback.accept(Result.success(result)) + } + } + + @Throws(PubNubException::class) + override fun sync(): PNPublishFileMessageResult { + if (numberOfFails.getAndAdd(1) < numberOfFailsBeforeSuccess) { + throw PubNubException() + } + return result + } + + companion object { + fun create( + result: PNPublishFileMessageResult, + numberOfFailsBeforeSuccess: Int, + pubNub: PubNubImpl, + ): PublishFileMessageEndpoint { + return FailingPublishFileMessage( + result, + numberOfFailsBeforeSuccess, + pubNub, + ) + } + } + } + + internal class AlwaysSuccessfulPublishFileMessage( + private val result: PNPublishFileMessageResult, + pubNub: PubNubImpl, + ) : + PublishFileMessageEndpoint( + channel = "channel", + fileName = "fileName", + fileId = "fileId", + pubNub = pubNub, + ) { + @Throws(PubNubException::class) + override fun sync(): PNPublishFileMessageResult { + return result + } + + override fun async(callback: Consumer>) { + callback.accept(Result.success(result)) + } + + companion object { + fun create( + result: PNPublishFileMessageResult, + pubNub: PubNubImpl, + ): PublishFileMessageEndpoint { + return AlwaysSuccessfulPublishFileMessage(result, pubNub) + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/TestsWithFiles.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/TestsWithFiles.kt new file mode 100644 index 000000000..6bb9f1aaa --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/TestsWithFiles.kt @@ -0,0 +1,19 @@ +package com.pubnub.api.legacy.endpoints.files + +import org.junit.jupiter.api.BeforeEach +import java.util.UUID + +interface TestsWithFiles { + fun fileName() = _fileName + + fun inputStream(content: String = "content") = content.byteInputStream() + + @BeforeEach + fun beforeEach() { + _fileName = "file_${UUID.randomUUID()}" + } + + companion object { + private lateinit var _fileName: String + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/UploadFileTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/UploadFileTest.kt new file mode 100644 index 000000000..367b633a1 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/files/UploadFileTest.kt @@ -0,0 +1,225 @@ +package com.pubnub.api.legacy.endpoints.files + +import com.pubnub.api.PubNubException +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.internal.endpoints.files.UploadFileEndpoint +import com.pubnub.internal.models.server.files.FormField +import com.pubnub.internal.services.S3Service +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.MultipartBody +import okhttp3.ResponseBody.Companion.toResponseBody +import okio.Buffer +import org.hamcrest.MatcherAssert.assertThat +import org.junit.Assert +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import retrofit2.Call +import retrofit2.Response +import java.io.InputStream +import java.util.Scanner +import org.hamcrest.Matchers.`is` as iz + +class UploadFileTest : TestsWithFiles { + private val s3Service: S3Service = mockk {} + + @Test + fun keyIsFirstInMultipart() { + // given + val captured = mutableListOf() + val uploadFile = + UploadFileEndpoint( + s3Service, + fileName(), + byteArrayOf(), + FormField("key", "keyValue"), + listOf(FormField("other", "otherValue")), + "https://s3.aws.com/bucket", + ) + every { s3Service.upload(any(), any()) } returns mockRetrofitSuccessfulCall {} + + // when + uploadFile.sync() + + // then + verify(exactly = 1) { s3Service.upload(any(), capture(captured)) } + + val capturedBody: MultipartBody = captured[0] + Assert.assertEquals( + "form-data; name=\"key\"", + capturedBody.part(0).headers!!["Content-Disposition"], + ) + assertPartExist("other", capturedBody.parts) + assertPartExist("file", capturedBody.parts) + } + + @Test + fun contentTypeIsUsedForFileIfPresentInFormFields() { + // given + val captured = mutableListOf() + val contentTypeValue = "application/json" + val uploadFile = + UploadFileEndpoint( + s3Service, + fileName(), + byteArrayOf(), + FormField("key", "keyValue"), + listOf(FormField("Content-Type", contentTypeValue)), + "https://s3.aws.com/bucket", + ) + every { s3Service.upload(any(), any()) } returns mockRetrofitSuccessfulCall { null } + + // when + uploadFile.sync() + + // then + verify(exactly = 1) { s3Service.upload(any(), capture(captured)) } + val capturedBody: MultipartBody = captured[0] + assertPartExist("file", capturedBody.parts) + val filePart = getPart("file", capturedBody.parts) + Assert.assertEquals(contentTypeValue.toMediaType(), filePart!!.body.contentType()) + } + + @Test + fun defaultContentTypeIsUsedForFileIfNotPresentInFormFields() { + // given + val captured = mutableListOf() + val uploadFile = + UploadFileEndpoint( + s3Service, + fileName(), + byteArrayOf(), + FormField("key", "keyValue"), + emptyList(), + "https://s3.aws.com/bucket", + ) + every { s3Service.upload(any(), any()) } returns mockRetrofitSuccessfulCall { null } + + // when + uploadFile.sync() + + // then + verify(exactly = 1) { s3Service.upload(any(), capture(captured)) } + val capturedBody: MultipartBody = captured[0] + assertPartExist("file", capturedBody.parts) + val filePart = getPart("file", capturedBody.parts) + Assert.assertEquals("application/octet-stream".toMediaType(), filePart!!.body.contentType()) + } + + @Test + fun testEncryptedFileContentIsSendProperly() { + // given + val captured = mutableListOf() + val content = "content" + val cipher = "enigma" + val cryptoModule = CryptoModule.createLegacyCryptoModule(cipher, true) + val encrypted = cryptoModule.encryptStream(content.byteInputStream()) + val uploadFile = + UploadFileEndpoint( + s3Service, + fileName(), + encrypted.readBytes(), + FormField("key", "keyValue"), + emptyList(), + "https://s3.aws.com/bucket", + ) + every { s3Service.upload(any(), any()) } returns mockRetrofitSuccessfulCall { null } + + // when + uploadFile.sync() + + // then + verify(exactly = 1) { s3Service.upload(any(), capture(captured)) } + val capturedBody: MultipartBody = captured[0] + assertPartExist("file", capturedBody.parts) + val filePart = getPart("file", capturedBody.parts) + val buffer = Buffer() + filePart?.body?.writeTo(buffer) + + val decrypted = cryptoModule.decryptStream(buffer.readByteArray().inputStream()).readBytes() + assertThat(String(decrypted), iz(content)) + assertEquals(content, String(decrypted)) + } + + @Test + fun errorMessageIsCopiedFromS3XMLResponse() { + // given + val uploadFile = + UploadFileEndpoint( + s3Service, + fileName(), + byteArrayOf(), + FormField("key", "keyValue"), + emptyList(), + "https://s3.aws.com/bucket", + ) + every { s3Service.upload(any(), any()) } returns + mockRetrofitErrorCall { + readToString( + UploadFileTest::class.java.getResourceAsStream("/entityTooLarge.xml")!!, + ) + } + + // when + try { + uploadFile.sync() + Assert.fail("Exception expected") + } catch (ex: PubNubException) { + // then + Assert.assertEquals("Your proposed upload exceeds the maximum allowed size", ex.errorMessage) + } + } + + private fun readToString(inputStream: InputStream): String { + val s = Scanner(inputStream).useDelimiter("\\A") + return if (s.hasNext()) { + s.next() + } else { + "" + } + } + + private fun getPart( + partName: String, + parts: List, + ): MultipartBody.Part? { + var result: MultipartBody.Part? = null + for (part in parts) { + if (part.headers!!["Content-Disposition"]!!.contains(partName)) { + result = part + break + } + } + return result + } + + private fun assertPartExist( + partName: String, + parts: List, + ) { + val result = getPart(partName, parts) + if (result == null) { + Assert.fail("There's no part $partName in parts $parts") + } + } + + companion object { + protected fun mockRetrofitSuccessfulCall(block: () -> T?): Call { + val mockCall: Call = mockk {} + every { mockCall.execute() } returns Response.success(block()) + return mockCall + } + + protected fun mockRetrofitErrorCall(block: () -> String): Call { + val mockCall: Call = mockk {} + every { mockCall.execute() } returns + Response.error( + 400, + block().toResponseBody("application/xml".toMediaType()), + ) + return mockCall + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/history/FetchMessagesCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/history/FetchMessagesCoreEndpointTest.kt new file mode 100644 index 000000000..c3b5f10cb --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/history/FetchMessagesCoreEndpointTest.kt @@ -0,0 +1,557 @@ +package com.pubnub.api.legacy.endpoints.history + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.api.legacy.BaseTest +import com.pubnub.api.models.consumer.PNBoundedPage +import com.pubnub.api.models.consumer.history.HistoryMessageType +import com.pubnub.internal.endpoints.FetchMessagesEndpoint +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.allOf +import org.hamcrest.Matchers.contains +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.hasEntry +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import kotlin.random.Random.Default.nextInt + +class FetchMessagesCoreEndpointTest : BaseTest() { + companion object { + private const val MAX_FOR_FETCH_MESSAGES = 100 + private const val MAX_FOR_FETCH_MESSAGES_WITH_ACTIONS = 25 + private const val MULTIPLE_CHANNELS_MAX_FOR_FETCH_MESSAGES = 25 + private const val EXPECTED_SINGLE_CHANNEL_DEFAULT_MESSAGES = 100 + private const val EXPECTED_MULTIPLE_CHANNEL_DEFAULT_MESSAGES = 25 + private const val EXPECTED_DEFAULT_MESSAGES_WITH_ACTIONS = 25 + private const val EXPECTED_MAX_MESSAGES_WITH_ACTIONS = 25 + private const val DEFAULT_INCLUDE_MESSAGE_ACTIONS = false + } + + val channel = "mychannel" + + @Test + fun testSyncSuccess() { + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/channel/mychannel,my_channel")).willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "my_channel": [ + { + "message": "hihi", + "uuid": "my-uuid", + "timetoken": "14698320467224036" + }, + { + "message": "Hey", + "uuid": "my-uuid", + "timetoken": "14698320468265639" + } + ], + "mychannel": [ + { + "message": "sample message", + "uuid": "my-uuid", + "timetoken": "14369823849575729" + } + ] + } + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.fetchMessages( + channels = listOf("mychannel", "my_channel"), + page = + PNBoundedPage( + limit = 25, + ), + ).sync() + + assertEquals(response.channels.size, 2) + + assertTrue(response.channels.containsKey("mychannel")) + assertTrue(response.channels.containsKey("my_channel")) + + assertEquals(response.channels["mychannel"]!!.size, 1) + assertEquals(response.channels["my_channel"]!!.size, 2) + + assertEquals(response.channels["mychannel"]!!.count { it.uuid == "my-uuid" }, 1) + assertEquals(response.channels["my_channel"]!!.count { it.uuid == "my-uuid" }, 2) + } + + @Test + fun testSyncWithoutUUIDSuccess() { + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/channel/mychannel,my_channel")).willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "my_channel": [ + { + "message": "hihi", + "timetoken": "14698320467224036" + }, + { + "message": "Hey", + "timetoken": "14698320468265639" + } + ], + "mychannel": [ + { + "message": "sample message", + "timetoken": "14369823849575729" + } + ] + } + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.fetchMessages( + channels = listOf("mychannel", "my_channel"), + page = + PNBoundedPage( + limit = 25, + ), + includeUUID = false, + ).sync() + + assertEquals(response.channels.size, 2) + assertTrue(response.channels.containsKey("mychannel")) + assertTrue(response.channels.containsKey("my_channel")) + + assertEquals(response.channels["mychannel"]!!.size, 1) + assertEquals(response.channels["my_channel"]!!.size, 2) + + assertEquals(response.channels["mychannel"]!!.count { it.uuid == null }, 1) + assertEquals(response.channels["my_channel"]!!.count { it.uuid == null }, 2) + } + + @Test + fun testSyncAuthSuccess() { + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/channel/mychannel,my_channel")).willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "my_channel": [ + { + "message": "hihi", + "timetoken": "14698320467224036" + }, + { + "message": "Hey", + "timetoken": "14698320468265639" + } + ], + "mychannel": [ + { + "message": "sample message", + "timetoken": "14369823849575729" + } + ] + } + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "authKey" + + val response = + pubnub.fetchMessages( + channels = listOf("mychannel", "my_channel"), + page = + PNBoundedPage( + limit = 25, + ), + ).sync() + + assertEquals(response.channels.size, 2) + assertTrue(response.channels.containsKey("mychannel")) + assertTrue(response.channels.containsKey("my_channel")) + assertEquals(response.channels["mychannel"]!!.size, 1) + assertEquals(response.channels["my_channel"]!!.size, 2) + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals("authKey", requests[0].queryParameter("auth").firstValue()) + assertEquals(1, requests.size) + } + + @Test + fun testSyncEncryptedSuccess() { + config.cryptoModule = CryptoModule.createLegacyCryptoModule("testCipher", false) + + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/channel/mychannel,my_channel")).willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "my_channel": [ + { + "message": "jC/yJ2y99BeYFYMQ7c53pg==", + "timetoken": "14797423056306675" + } + ], + "mychannel": [ + { + "message": "jC/yJ2y99BeYFYMQ7c53pg==", + "timetoken": "14797423056306675" + } + ] + } + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.fetchMessages( + channels = listOf("mychannel", "my_channel"), + page = + PNBoundedPage( + limit = 25, + ), + ).sync() + + assertEquals(response.channels.size, 2) + assertTrue(response.channels.containsKey("mychannel")) + assertTrue(response.channels.containsKey("my_channel")) + assertEquals(response.channels["mychannel"]!!.size, 1) + assertEquals(response.channels["my_channel"]!!.size, 1) + } + + @Test + fun forSingleChannelFetchMessagesAlwaysPassMaxWhenItIsInBound() { + // given + val maximumPerChannel = randomInt(MAX_FOR_FETCH_MESSAGES) + + // when + val result = + FetchMessagesEndpoint.effectiveMax( + maximumPerChannel = maximumPerChannel, + includeMessageActions = DEFAULT_INCLUDE_MESSAGE_ACTIONS, + numberOfChannels = 1, + ) + + // then + assertThat(result, equalTo(maximumPerChannel)) + } + + @Test + fun forSingleChannelFetchMessagesAlwaysPassDefaultWhenNonPositiveMaxSpecified() { + // given + val maximumPerChannel = -(randomInt(100) - 1) + + // when + val result = + FetchMessagesEndpoint.effectiveMax( + maximumPerChannel = maximumPerChannel, + includeMessageActions = DEFAULT_INCLUDE_MESSAGE_ACTIONS, + numberOfChannels = 1, + ) + + // then + assertThat(result, equalTo(EXPECTED_SINGLE_CHANNEL_DEFAULT_MESSAGES)) + } + + @Test + fun forSingleChannelFetchMessagesAlwaysPassDefaultWhenMaxNotSpecified() { + // given + val maximumPerChannel = null + + // when + val result = + FetchMessagesEndpoint.effectiveMax( + maximumPerChannel = maximumPerChannel, + includeMessageActions = DEFAULT_INCLUDE_MESSAGE_ACTIONS, + numberOfChannels = 1, + ) + + // then + assertThat(result, equalTo(EXPECTED_SINGLE_CHANNEL_DEFAULT_MESSAGES)) + } + + @Test + fun forSingleChannelFetchMessagesAlwaysPassDefaultMaxWhenMaxExceeds() { + // given + val maximumPerChannel = MAX_FOR_FETCH_MESSAGES + randomInt(MAX_FOR_FETCH_MESSAGES) + + // when + val result = + FetchMessagesEndpoint.effectiveMax( + maximumPerChannel = maximumPerChannel, + includeMessageActions = DEFAULT_INCLUDE_MESSAGE_ACTIONS, + numberOfChannels = 1, + ) + + // then + assertThat(result, equalTo(EXPECTED_SINGLE_CHANNEL_DEFAULT_MESSAGES)) + } + + @Test + fun forMultipleChannelsFetchMessagesAlwaysPassMaxWhenItIsInBound() { + // given + val maximumPerChannel = randomInt(MULTIPLE_CHANNELS_MAX_FOR_FETCH_MESSAGES) + + // when + val result = + FetchMessagesEndpoint.effectiveMax( + maximumPerChannel = maximumPerChannel, + includeMessageActions = DEFAULT_INCLUDE_MESSAGE_ACTIONS, + numberOfChannels = 2, + ) + + // then + assertThat(result, equalTo(maximumPerChannel)) + } + + @Test + fun forMultipleChannelsFetchMessagesAlwaysPassDefaultWhenNonPositiveMaxSpecified() { + // given + val maximumPerChannel = -(randomInt(100) - 1) + + // when + val result = + FetchMessagesEndpoint.effectiveMax( + maximumPerChannel = maximumPerChannel, + includeMessageActions = DEFAULT_INCLUDE_MESSAGE_ACTIONS, + numberOfChannels = 2, + ) + + // then + assertThat(result, equalTo(EXPECTED_MULTIPLE_CHANNEL_DEFAULT_MESSAGES)) + } + + @Test + fun forMultipleChannelsFetchMessagesAlwaysPassDefaultWhenMaxNotSpecified() { + // given + val maximumPerChannel = null + + // when + val result = + FetchMessagesEndpoint.effectiveMax( + maximumPerChannel = maximumPerChannel, + includeMessageActions = DEFAULT_INCLUDE_MESSAGE_ACTIONS, + numberOfChannels = 2, + ) + + // then + assertThat(result, equalTo(EXPECTED_MULTIPLE_CHANNEL_DEFAULT_MESSAGES)) + } + + @Test + fun forMultipleChannelsFetchMessagesAlwaysPassDefaultMaxWhenMaxExceeds() { + // given + val maximumPerChannel = + MULTIPLE_CHANNELS_MAX_FOR_FETCH_MESSAGES + randomInt(MULTIPLE_CHANNELS_MAX_FOR_FETCH_MESSAGES) + + // when + val result = + FetchMessagesEndpoint.effectiveMax( + maximumPerChannel = maximumPerChannel, + includeMessageActions = DEFAULT_INCLUDE_MESSAGE_ACTIONS, + numberOfChannels = 2, + ) + + // then + assertThat(result, equalTo(EXPECTED_MULTIPLE_CHANNEL_DEFAULT_MESSAGES)) + } + + @Test + fun forSingleChannelFetchMessagesWithActionAlwaysPassMaxWhenItIsInBound() { + // given + val maximumPerChannel = randomInt(MAX_FOR_FETCH_MESSAGES_WITH_ACTIONS) + + // when + val result = + FetchMessagesEndpoint.effectiveMax( + maximumPerChannel = maximumPerChannel, + includeMessageActions = true, + numberOfChannels = 1, + ) + + // then + assertThat(result, equalTo(maximumPerChannel)) + } + + @Test + fun forSingleChannelFetchMessagesWithActionAlwaysPassDefaultWhenMaxNotSpecified() { + // given + val maximumPerChannel = null + + // when + val result = + FetchMessagesEndpoint.effectiveMax( + maximumPerChannel = maximumPerChannel, + includeMessageActions = true, + numberOfChannels = 1, + ) + + // then + assertThat(result, equalTo(EXPECTED_DEFAULT_MESSAGES_WITH_ACTIONS)) + } + + @Test + fun forSingleChannelFetchMessagesWithActionAlwaysPassDefaultMaxWhenMaxExceeds() { + // given + val maximumPerChannel = MAX_FOR_FETCH_MESSAGES_WITH_ACTIONS + randomInt(MAX_FOR_FETCH_MESSAGES_WITH_ACTIONS) + + // when + val result = + FetchMessagesEndpoint.effectiveMax( + maximumPerChannel = maximumPerChannel, + includeMessageActions = true, + numberOfChannels = 1, + ) + + // then + assertThat(result, equalTo(EXPECTED_MAX_MESSAGES_WITH_ACTIONS)) + } + + @Test + fun testIncludeMessageTypeQueryParamIsPassedInFetchMessages() { + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/channel/$channel")).willReturn( + aResponse().withBody( + responseWithOneMessageForChannel(channel), + ), + ), + ) + + pubnub.fetchMessages( + channels = listOf(channel), + includeMessageType = true, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertThat( + requests[0].queryParams.map { (k, v) -> k to v.firstValue() }.toMap(), + allOf( + hasEntry(FetchMessagesEndpoint.INCLUDE_MESSAGE_TYPE_QUERY_PARAM, "true"), + ), + ) + } + + @Test + fun testMissingIncludeMessageTypeQueryParamIsSetToTrue() { + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/channel/$channel")).willReturn( + aResponse().withBody( + responseWithOneMessageForChannel(channel), + ), + ), + ) + + pubnub.fetchMessages( + channels = listOf(channel), + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertThat( + requests[0].queryParams.map { (k, v) -> k to v.firstValue() }.toMap(), + allOf( + hasEntry(FetchMessagesEndpoint.INCLUDE_MESSAGE_TYPE_QUERY_PARAM, "true"), + ), + ) + } + + @Test + fun testMessageTypesAreProperlyDeserialized() { + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/channel/$channel")).willReturn( + aResponse().withBody( + responseWithMessagesForChannelWithMessageType(channel), + ), + ), + ) + + val response = + pubnub.fetchMessages( + channels = listOf(channel), + includeMessageType = true, + ).sync() + + assertThat( + response.channels.values.flatMap { items -> items.map { it.messageType } }, + contains(HistoryMessageType.Message, HistoryMessageType.File), + ) + } + + private fun randomInt(max: Int): Int = nextInt(max) + 1 + + private fun responseWithOneMessageForChannel(channel: String): String { + return """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "$channel": [ + { + "message": "thisIsMessage", + "timetoken": "14797423056306675" + } + ] + } + } + """.trimIndent() + } + + private fun responseWithMessagesForChannelWithMessageType(channel: String): String { + return """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "$channel": [ + { + "message": "thisIsMessage1", + "timetoken": "14797423056306675", + "message_type": 0 + }, + { + "message": "thisIsMessage2", + "timetoken": "14797423056306676", + "message_type": 4 + } + ] + } + } + """.trimIndent() + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/history/HistoryCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/history/HistoryCoreEndpointTest.kt new file mode 100644 index 000000000..76c843094 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/history/HistoryCoreEndpointTest.kt @@ -0,0 +1,508 @@ +package com.pubnub.api.legacy.endpoints.history + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.anyUrl +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.failTest +import com.pubnub.test.listen +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test +import java.util.concurrent.atomic.AtomicBoolean + +class HistoryCoreEndpointTest : BaseTest() { + @Test + fun testSyncDisabled() { + val payload = + """ + [ + "Use of the history API requires the Storage & Playback which is not enabled for this subscribe key. Login to your PubNub Dashboard Account and enable Storage & Playback. Contact support@pubnub.com if you require further assistance.", + 0, + 0 + ] + """.trimIndent() + + stubFor( + get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(payload)), + ) + + try { + pubnub.history( + channel = "niceChannel", + ).sync() + throw RuntimeException() + } catch (e: PubNubException) { + assertPnException(PubNubError.HTTP_ERROR, e) + assertEquals("History is disabled", e.errorMessage) + } + } + + @Test + fun testSyncWithTokensDisabled() { + val payload = + """ + [ + "Use of the history API requires the Storage & Playback which is not enabled for this subscribe key. Login to your PubNub Dashboard Account and enable Storage & Playback. Contact support@pubnub.com if you require further assistance.", + 0, + 0 + ] + """.trimIndent() + + stubFor( + get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(payload)), + ) + + try { + pubnub.history( + channel = "niceChannel", + includeTimetoken = true, + ).sync() + failTest() + } catch (e: PubNubException) { + assertPnException(PubNubError.HTTP_ERROR, e) + assertEquals("History is disabled", e.errorMessage) + } + } + + @Test + fun testSyncSuccess() { + val historyItem1 = + mapOf( + "a" to 11, + "b" to 22, + ) + val historyEnvelope1 = + mapOf( + "timetoken" to 1111, + "message" to historyItem1, + ) + + val historyItem2 = + mapOf( + "a" to 33, + "b" to 44, + ) + val historyEnvelope2 = + mapOf( + "timetoken" to 2222, + "message" to historyItem2, + ) + + val historyItems = + listOf( + historyEnvelope1, + historyEnvelope2, + ) + val testArray = + listOf( + historyItems, + 1234, + 4321, + ) + + stubFor( + get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(pubnub.mapper.toJson(testArray))), + ) + + val result = + pubnub.history( + channel = "niceChannel", + includeTimetoken = true, + ).sync() + + with(result) { + assertEquals(1234L, startTimetoken) + assertEquals(4321L, endTimetoken) + + assertEquals(2, messages.size) + + assertEquals(1111L, messages[0].timetoken) + assertEquals(11, messages[0].entry.asJsonObject["a"].asInt) + assertEquals(22, messages[0].entry.asJsonObject["b"].asInt) + + assertEquals(2222L, messages[1].timetoken) + assertEquals(33, messages[1].entry.asJsonObject["a"].asInt) + assertEquals(44, messages[1].entry.asJsonObject["b"].asInt) + } + } + + @Test + fun testSyncAuthSuccess() { + config.authKey = "authKey" + + val historyItem1 = + mapOf( + "a" to 11, + "b" to 22, + ) + val historyEnvelope1 = + mapOf( + "timetoken" to 1111, + "message" to historyItem1, + ) + + val historyItem2 = + mapOf( + "a" to 33, + "b" to 44, + ) + val historyEnvelope2 = + mapOf( + "timetoken" to 2222, + "message" to historyItem2, + ) + + val historyItems = + listOf( + historyEnvelope1, + historyEnvelope2, + ) + val testArray = + listOf( + historyItems, + 1234, + 4321, + ) + + stubFor( + get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(pubnub.mapper.toJson(testArray))), + ) + + pubnub.history( + channel = "niceChannel", + includeTimetoken = true, + ).sync() + + val requests = findAll(getRequestedFor(anyUrl())) + assertEquals(1, requests.size) + assertEquals("authKey", requests.first().queryParameter("auth").firstValue()) + } + + @Test + fun testSyncEncryptedSuccess() { + config.cryptoModule = CryptoModule.createLegacyCryptoModule("testCipher", false) + + stubFor( + get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn( + aResponse().withBody( + """ + [ + [ + "EGwV+Ti43wh2TprPIq7o0KMuW5j6B3yWy352ucWIOmU=\n", + "EGwV+Ti43wh2TprPIq7o0KMuW5j6B3yWy352ucWIOmU=\n", + "EGwV+Ti43wh2TprPIq7o0KMuW5j6B3yWy352ucWIOmU=\n" + ], + 14606134331557852, + 14606134485013970 + ] + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.history( + channel = "niceChannel", + includeTimetoken = false, + ).sync() + + with(result) { + assertEquals(14606134331557852, startTimetoken) + assertEquals(14606134485013970, endTimetoken) + + assertEquals(3, messages.size) + + assertNull(messages[0].timetoken) + + assertEquals("m1", messages[0].entry.asJsonArray[0].asString) + assertEquals("m2", messages[0].entry.asJsonArray[1].asString) + assertEquals("m3", messages[0].entry.asJsonArray[2].asString) + + assertEquals("m1", messages[1].entry.asJsonArray[0].asString) + assertEquals("m2", messages[1].entry.asJsonArray[1].asString) + assertEquals("m3", messages[1].entry.asJsonArray[2].asString) + + assertEquals("m1", messages[2].entry.asJsonArray[0].asString) + assertEquals("m2", messages[2].entry.asJsonArray[1].asString) + assertEquals("m3", messages[2].entry.asJsonArray[2].asString) + } + } + + @Test + fun testSyncEncryptedWithPNOtherSuccess() { + config.cryptoModule = CryptoModule.createLegacyCryptoModule("hello", false) + + stubFor( + get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn( + aResponse().withBody( + """ + [ + [ + { + "pn_other": "6QoqmS9CnB3W9+I4mhmL7w==" + } + ], + 14606134331557852, + 14606134485013970 + ] + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.history( + channel = "niceChannel", + includeTimetoken = false, + ).sync() + + with(result) { + assertEquals(14606134331557852, startTimetoken) + assertEquals(14606134485013970, endTimetoken) + + assertEquals(1, messages.size) + + assertNull(messages[0].timetoken) + assertEquals("hey", messages[0].entry.asJsonObject.get("pn_other").asJsonObject.get("text").asString) + } + } + + @Test + fun testSyncSuccessWithoutTimeToken() { + val historyItem1 = + mapOf( + "a" to 11, + "b" to 22, + ) + val historyItem2 = + mapOf( + "a" to 33, + "b" to 44, + ) + + val testArray = + listOf( + listOf( + historyItem1, + historyItem2, + ), + 1234, + 4321, + ) + + stubFor( + get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(pubnub.mapper.toJson(testArray))), + ) + + val result = + pubnub.history( + channel = "niceChannel", + ).sync() + + with(result) { + assertEquals(1234, startTimetoken) + assertEquals(4321, endTimetoken) + + assertEquals(2, messages.size) + + assertNull(messages[0].timetoken) + assertEquals(11, messages[0].entry.asJsonObject["a"].asInt) + assertEquals(22, messages[0].entry.asJsonObject["b"].asInt) + + assertNull(messages[1].timetoken) + assertEquals(33, messages[1].entry.asJsonObject["a"].asInt) + assertEquals(44, messages[1].entry.asJsonObject["b"].asInt) + } + } + + @Test + fun testChannelIsEmpty() { + try { + pubnub.history( + channel = "", + ).sync() + throw RuntimeException() + } catch (e: PubNubException) { + assertPnException(PubNubError.CHANNEL_MISSING, e) + } + } + + @Test + fun testChannelIsBlank() { + try { + pubnub.history( + channel = " ", + ).sync() + throw RuntimeException() + } catch (e: PubNubException) { + assertPnException(PubNubError.CHANNEL_MISSING, e) + } + } + + @Test + fun testOperationTypeSuccessAsync() { + val historyEnvelope1 = + mapOf( + "timetoken" to 1111, + "message" to + mapOf( + "a" to 11, + "b" to 22, + ), + ) + + val historyEnvelope2 = + mapOf( + "timetoken" to 2222, + "message" to + mapOf( + "a" to 33, + "b" to 44, + ), + ) + + val historyItems = + listOf( + historyEnvelope1, + historyEnvelope2, + ) + val testArray = + listOf( + historyItems, + 1234, + 4321, + ) + + stubFor( + get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(pubnub.mapper.toJson(testArray))), + ) + + val success = AtomicBoolean() + + pubnub.history( + channel = "niceChannel", + includeTimetoken = true, + ).async { result -> + result.onSuccess { + with(it) { + assertEquals(1234L, startTimetoken) + assertEquals(4321L, endTimetoken) + + assertEquals(2, messages.size) + + assertEquals(1111L, messages[0].timetoken) + assertEquals(11, messages[0].entry.asJsonObject["a"].asInt) + assertEquals(22, messages[0].entry.asJsonObject["b"].asInt) + + assertEquals(2222L, messages[1].timetoken) + assertEquals(33, messages[1].entry.asJsonObject["a"].asInt) + assertEquals(44, messages[1].entry.asJsonObject["b"].asInt) + } + } + + success.set(true) + } + + success.listen() + } + + @Test + fun testSyncCountReverseStartEndSuccess() { + val historyItem1 = + mapOf( + "a" to 11, + "b" to 22, + ) + val historyEnvelope1 = + mapOf( + "timetoken" to 1111, + "message" to historyItem1, + ) + + val historyItem2 = + mapOf( + "a" to 33, + "b" to 44, + ) + val historyEnvelope2 = + mapOf( + "timetoken" to 2222, + "message" to historyItem2, + ) + + val historyItems = + listOf( + historyEnvelope1, + historyEnvelope2, + ) + val testArray = + listOf( + historyItems, + 1234, + 4321, + ) + + stubFor( + get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(pubnub.mapper.toJson(testArray))), + ) + + val result = + pubnub.history( + channel = "niceChannel", + count = 5, + reverse = true, + start = 1L, + end = 2L, + includeTimetoken = true, + ).sync() + + val requests = + findAll( + getRequestedFor( + urlMatching("/v2/history/sub-key/mySubscribeKey/channel/niceChannel.*"), + ), + ) + + assertEquals(1, requests.size) + + assertEquals("true", requests.first().queryParameter("reverse").firstValue()) + assertEquals("5", requests.first().queryParameter("count").firstValue()) + assertEquals("1", requests.first().queryParameter("start").firstValue()) + assertEquals("2", requests.first().queryParameter("end").firstValue()) + assertEquals("true", requests.first().queryParameter("include_token").firstValue()) + + with(result) { + assertEquals(1234L, startTimetoken) + assertEquals(4321L, endTimetoken) + + assertEquals(2, messages.size) + + assertEquals(1111L, messages[0].timetoken) + assertEquals(11, messages[0].entry.asJsonObject["a"].asInt) + assertEquals(22, messages[0].entry.asJsonObject["b"].asInt) + + assertEquals(2222L, messages[1].timetoken) + assertEquals(33, messages[1].entry.asJsonObject["a"].asInt) + assertEquals(44, messages[1].entry.asJsonObject["b"].asInt) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/history/MessageCountTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/history/MessageCountTest.kt new file mode 100644 index 000000000..6972a1499 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/history/MessageCountTest.kt @@ -0,0 +1,279 @@ +package com.pubnub.api.legacy.endpoints.history + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.legacy.BaseTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Test + +class MessageCountTest : BaseTest() { + @Test + fun testSingleChannelWithSingleToken() { + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/message-counts/my_channel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "my_channel": 19 + } + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.messageCounts( + channels = listOf("my_channel"), + channelsTimetoken = listOf(10000L), + ).sync() + + assertEquals(response.channels.size, 1) + assertFalse(response.channels.containsKey("channel_does_not_exist")) + assertTrue(response.channels.containsKey("my_channel")) + for ((key, value) in response.channels) { + assertEquals("my_channel", key) + assertEquals(java.lang.Long.valueOf("19"), value) + } + } + + @Test + fun testSingleChannelWithMultipleTokens() { + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/message-counts/my_channel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "my_channel": 19 + } + } + """.trimIndent(), + ), + ), + ) + var exception: PubNubException? = null + try { + pubnub.messageCounts( + channels = listOf("my_channel"), + channelsTimetoken = listOf(10000L, 20000L), + ).sync() + } catch (e: PubNubException) { + exception = e + } finally { + assertNotNull(exception) + assertEquals( + PubNubError.CHANNELS_TIMETOKEN_MISMATCH.message, + exception!!.pubnubError!!.message, + ) + } + } + + @Test + @Throws(PubNubException::class) + fun testMultipleChannelsWithSingleToken() { + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/message-counts/my_channel,new_channel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "my_channel": 19, + "new_channel": 5 + } + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.messageCounts( + channels = listOf("my_channel", "new_channel"), + channelsTimetoken = listOf(10000L), + ).sync() + + assertEquals(response.channels.size, 2) + assertFalse(response.channels.containsKey("channel_does_not_exist")) + assertTrue(response.channels.containsKey("my_channel")) + assertTrue(response.channels.containsKey("new_channel")) + for ((key, value) in response.channels) { + if (key == "my_channel") { + assertEquals(java.lang.Long.valueOf("19"), value) + } else if (key == "new_channel") { + assertEquals(java.lang.Long.valueOf("5"), value) + } + } + } + + @Test + @Throws(PubNubException::class) + fun testMultipleChannelsWithMultipleTokens() { + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/message-counts/my_channel,new_channel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "my_channel": 19, + "new_channel": 5 + } + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.messageCounts( + channels = listOf("my_channel", "new_channel"), + channelsTimetoken = listOf(10000L, 20000L), + ).sync() + + assertEquals(response.channels.size, 2) + assertFalse(response.channels.containsKey("channel_does_not_exist")) + assertTrue(response.channels.containsKey("my_channel")) + assertTrue(response.channels.containsKey("new_channel")) + for ((key, value) in response.channels) { + if (key == "my_channel") { + assertEquals(java.lang.Long.valueOf("19"), value) + } else if (key == "new_channel") { + assertEquals(java.lang.Long.valueOf("5"), value) + } + } + } + + @Test + fun testWitEmptyChannelsSingleToken() { + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/message-counts/my_channel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "my_channel": 19, + "new_channel": 5 + } + } + """.trimIndent(), + ), + ), + ) + var exception: PubNubException? = null + try { + pubnub.messageCounts( + channels = listOf(), + channelsTimetoken = listOf(10000L), + ).sync() + } catch (ex: PubNubException) { + exception = ex + } finally { + assertNotNull(exception) + assertEquals( + PubNubError.CHANNEL_MISSING.message, + exception!!.pubnubError!!.message, + ) + } + } + + @Test + fun testWithEmptyChannelsMultipleTokens() { + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/message-counts/my_channel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "my_channel": 19, + "new_channel": 5 + } + } + """.trimIndent(), + ), + ), + ) + var exception: PubNubException? = null + try { + pubnub.messageCounts( + channels = listOf(), + channelsTimetoken = listOf(10000L, 20000L), + ).sync() + } catch (ex: PubNubException) { + exception = ex + } finally { + assertNotNull(exception) + assertEquals( + PubNubError.CHANNEL_MISSING.message, + exception!!.pubnubError!!.message, + ) + } + } + + @Test + fun testChannelWithSingleEmptyToken() { + stubFor( + get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/message-counts/my_channel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "my_channel": 19 + } + } + """.trimIndent(), + ), + ), + ) + var exception: PubNubException? = null + try { + pubnub.messageCounts( + channels = listOf("my_channel"), + channelsTimetoken = listOf(), + ).sync() + } catch (ex: PubNubException) { + exception = ex + } finally { + assertNotNull(exception) + assertEquals( + PubNubError.TIMETOKEN_MISSING.message, + exception!!.pubnubError!!.message, + ) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/AddMessageActionCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/AddMessageActionCoreEndpointTest.kt new file mode 100644 index 000000000..91983ac08 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/AddMessageActionCoreEndpointTest.kt @@ -0,0 +1,378 @@ +package com.pubnub.api.legacy.endpoints.message_actions + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.equalToJson +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.noContent +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.legacy.BaseTest +import com.pubnub.api.models.consumer.message_actions.PNMessageAction +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.emptyJson +import com.pubnub.test.CommonUtils.failTest +import com.pubnub.test.listen +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.concurrent.atomic.AtomicBoolean + +class AddMessageActionCoreEndpointTest : BaseTest() { + @Test + fun testSyncSuccess() { + stubFor( + post(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123")) + .withRequestBody(equalToJson("""{"type":"emoji","value":"smiley"}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": { + "messageTimetoken": "123", + "type": "emoji", + "uuid": "someUuid", + "value": "smiley", + "actionTimetoken": "1000" + } + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.addMessageAction( + channel = "coolChannel", + messageAction = + PNMessageAction( + type = "emoji", + value = "smiley", + messageTimetoken = 123, + ), + ).sync() + + assertEquals(result.messageTimetoken, 123L) + assertEquals(result.type, "emoji") + assertEquals(result.uuid, "someUuid") + assertEquals(result.value, "smiley") + assertEquals(result.actionTimetoken, 1000L) + } + + @Test + fun testAsyncSuccess() { + stubFor( + post(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123")) + .withRequestBody(equalToJson("""{"type":"emoji","value":"smiley"}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": { + "messageTimetoken": "123", + "type": "emoji", + "uuid": "someUuid", + "value": "smiley", + "actionTimetoken": "1000" + } + } + """.trimIndent(), + ), + ), + ) + + val success = AtomicBoolean() + + pubnub.addMessageAction( + channel = "coolChannel", + messageAction = + PNMessageAction( + type = "emoji", + value = "smiley", + messageTimetoken = 123, + ), + ).async { result -> + assertFalse(result.isFailure) + result.onSuccess { + assertEquals(it.messageTimetoken, 123L) + assertEquals(it.type, "emoji") + assertEquals(it.uuid, "someUuid") + assertEquals(it.value, "smiley") + assertEquals(it.actionTimetoken, 1000L) + success.set(true) + } + } + + success.listen() + } + + @Test + fun testMalformedJson() { + stubFor( + post(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123")) + .withRequestBody(equalToJson("""{"type":"emoji","value":"smiley"}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": { + "messageTimetoken": "123", + "type": "emoji", + "uuid": "someUuid", + "value": "smiley", + "actionTimetoken": "1000" + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.addMessageAction( + channel = "coolChannel", + messageAction = + PNMessageAction( + type = "emoji", + value = "smiley", + messageTimetoken = 123, + ), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testEmptyBody() { + stubFor( + post(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123")) + .withRequestBody(equalToJson("""{"type":"emoji","value":"smiley"}""")) + .willReturn(emptyJson()), + ) + + try { + pubnub.addMessageAction( + channel = "coolChannel", + messageAction = + PNMessageAction( + type = "emoji", + value = "smiley", + messageTimetoken = 123, + ), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testNoBody() { + stubFor( + post(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123")) + .withRequestBody(equalToJson("""{"type":"emoji","value":"smiley"}""")) + .willReturn(noContent()), + ) + + try { + pubnub.addMessageAction( + channel = "coolChannel", + messageAction = + PNMessageAction( + type = "emoji", + value = "smiley", + messageTimetoken = 123, + ), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testNoData() { + stubFor( + post(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123")) + .withRequestBody(equalToJson("""{"type":"emoji","value":"smiley"}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.addMessageAction( + channel = "coolChannel", + messageAction = + PNMessageAction( + type = "emoji", + value = "smiley", + messageTimetoken = 123, + ), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testNullData() { + stubFor( + post(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123")) + .withRequestBody(equalToJson("""{"type":"emoji","value":"smiley"}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": null + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.addMessageAction( + channel = "coolChannel", + messageAction = + PNMessageAction( + type = "emoji", + value = "smiley", + messageTimetoken = 123, + ), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testBlankChannel() { + try { + pubnub.addMessageAction( + channel = " ", + messageAction = + PNMessageAction( + type = "emoji", + value = "smiley", + messageTimetoken = 123, + ), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.CHANNEL_MISSING, e) + } + } + + @Test + fun testBlankType() { + try { + pubnub.addMessageAction( + channel = "coolChannel", + messageAction = + PNMessageAction( + type = " ", + value = "smiley", + messageTimetoken = 123, + ), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.MESSAGE_ACTION_TYPE_MISSING, e) + } + } + + @Test + fun testBlankValue() { + try { + pubnub.addMessageAction( + channel = "coolChannel", + messageAction = + PNMessageAction( + type = "emoji", + value = " ", + messageTimetoken = 123, + ), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.MESSAGE_ACTION_VALUE_MISSING, e) + } + } + + @Test + fun testInvalidSubKey() { + config.subscribeKey = " " + try { + pubnub.addMessageAction( + channel = "coolChannel", + messageAction = + PNMessageAction( + type = "emoji", + value = " ", + messageTimetoken = 123, + ), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testAuthKeyRequired() { + stubFor( + post(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123")) + .withRequestBody(equalToJson("""{"type":"emoji","value":"smiley"}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": { + "messageTimetoken": "123", + "type": "emoji", + "uuid": "someUuid", + "value": "smiley", + "actionTimetoken": "1000" + } + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "authKey" + + pubnub.addMessageAction( + channel = "coolChannel", + messageAction = + PNMessageAction( + type = "emoji", + value = "smiley", + messageTimetoken = 123, + ), + ).sync() + + val requests = findAll(postRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + + assertEquals("authKey", requests[0].queryParameter("auth").firstValue()) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/GetMessageActionCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/GetMessageActionCoreEndpointTest.kt new file mode 100644 index 000000000..705f3a908 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/GetMessageActionCoreEndpointTest.kt @@ -0,0 +1,438 @@ +package com.pubnub.api.legacy.endpoints.message_actions + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.matching +import com.github.tomakehurst.wiremock.client.WireMock.noContent +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.legacy.BaseTest +import com.pubnub.api.models.consumer.PNBoundedPage +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.emptyJson +import com.pubnub.test.CommonUtils.failTest +import com.pubnub.test.listen +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.concurrent.atomic.AtomicBoolean + +class GetMessageActionCoreEndpointTest : BaseTest() { + @Test + fun testSyncSuccess() { + stubFor( + get(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": [ + { + "messageTimetoken": "123", + "type": "emoji", + "uuid": "someUuid", + "value": "smiley", + "actionTimetoken": "1000" + } + ] + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.getMessageActions( + channel = "coolChannel", + ).sync() + + assertEquals(result.actions.size, 1) + assertEquals(result.actions[0].messageTimetoken, 123L) + assertEquals(result.actions[0].type, "emoji") + assertEquals(result.actions[0].uuid, "someUuid") + assertEquals(result.actions[0].value, "smiley") + assertEquals(result.actions[0].actionTimetoken, 1000L) + } + + @Test + fun testSyncSuccessMultipleActions() { + stubFor( + get(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": [ + { + "messageTimetoken": "123", + "type": "emoji", + "uuid": "uuid_1", + "value": "โค๏ธ", + "actionTimetoken": "1000" + }, + { + "messageTimetoken": "456", + "type": "reaction", + "uuid": "uuid_2", + "value": "๐Ÿ‘", + "actionTimetoken": "2000" + }, + { + "messageTimetoken": "789", + "type": "emoji", + "uuid": "uuid_2", + "value": "๐Ÿ˜„", + "actionTimetoken": "3000" + } + ] + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.getMessageActions( + channel = "coolChannel", + ).sync() + + assertEquals(result.actions.size, 3) + + assertEquals(result.actions[0].messageTimetoken, 123L) + assertEquals(result.actions[0].type, "emoji") + assertEquals(result.actions[0].uuid, "uuid_1") + assertEquals(result.actions[0].value, "โค๏ธ") + assertEquals(result.actions[0].actionTimetoken, 1000L) + + assertEquals(result.actions[1].messageTimetoken, 456L) + assertEquals(result.actions[1].type, "reaction") + assertEquals(result.actions[1].uuid, "uuid_2") + assertEquals(result.actions[1].value, "๐Ÿ‘") + assertEquals(result.actions[1].actionTimetoken, 2000L) + + assertEquals(result.actions[2].messageTimetoken, 789L) + assertEquals(result.actions[2].type, "emoji") + assertEquals(result.actions[2].uuid, "uuid_2") + assertEquals(result.actions[2].value, "๐Ÿ˜„") + assertEquals(result.actions[2].actionTimetoken, 3000L) + } + + @Test + fun testAsyncSuccess() { + stubFor( + get(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": [ + { + "messageTimetoken": "123", + "type": "emoji", + "uuid": "someUuid", + "value": "smiley", + "actionTimetoken": "1000" + } + ] + } + """.trimIndent(), + ), + ), + ) + + val success = AtomicBoolean() + + pubnub.getMessageActions( + channel = "coolChannel", + ).async { result -> + assertFalse(result.isFailure) + result.onSuccess { + assertEquals(it.actions.size, 1) + assertEquals(it.actions[0].messageTimetoken, 123L) + assertEquals(it.actions[0].type, "emoji") + assertEquals(it.actions[0].uuid, "someUuid") + assertEquals(it.actions[0].value, "smiley") + assertEquals(it.actions[0].actionTimetoken, 1000L) + success.set(true) + } + } + + success.listen() + } + + @Test + fun testMalformedJson() { + stubFor( + get(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": [ + { + "messageTimetoken": "123", + "type": "emoji", + "uuid": "someUuid", + "value": "smiley" + "actionTimetoken": "1000" + } + ] + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.getMessageActions( + channel = "coolChannel", + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testEmptyBody() { + stubFor( + get(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel")) + .willReturn(emptyJson()), + ) + + try { + pubnub.getMessageActions( + channel = "coolChannel", + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testNoBody() { + stubFor( + get(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel")) + .willReturn(noContent()), + ) + + try { + pubnub.getMessageActions( + channel = "coolChannel", + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testNoData() { + stubFor( + get(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.getMessageActions( + channel = "coolChannel", + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testEmptyData() { + stubFor( + get(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200 + "data": [] + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.getMessageActions( + channel = "coolChannel", + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testBlankChannel() { + try { + pubnub.getMessageActions( + channel = " ", + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.CHANNEL_MISSING, e) + } + } + + @Test + fun testInvalidSubKey() { + config.subscribeKey = " " + try { + pubnub.getMessageActions( + channel = "coolChannel", + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testAuthKeyRequired() { + stubFor( + get(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": [ + { + "messageTimetoken": "123", + "type": "emoji", + "uuid": "someUuid", + "value": "smiley", + "actionTimetoken": "1000" + } + ] + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "authKey" + + pubnub.getMessageActions( + channel = "coolChannel", + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + + assertEquals("authKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testOptionalParams() { + stubFor( + get(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel")) + .withQueryParam("limit", matching("10")) + .withQueryParam("start", matching("15")) + .withQueryParam("end", matching("20")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": [ + { + "messageTimetoken": "123", + "type": "emoji", + "uuid": "someUuid", + "value": "smiley", + "actionTimetoken": "1000" + } + ] + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "authKey" + + pubnub.getMessageActions( + channel = "coolChannel", + page = + PNBoundedPage( + start = 15, + end = 20, + limit = 10, + ), + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + + assertEquals("10", requests[0].queryParameter("limit").firstValue()) + assertEquals("15", requests[0].queryParameter("start").firstValue()) + assertEquals("20", requests[0].queryParameter("end").firstValue()) + } + + @Test + fun testNoOptionalParams() { + stubFor( + get(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel")) + .withQueryParam("limit", absent()) + .withQueryParam("start", absent()) + .withQueryParam("end", absent()) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": [ + { + "messageTimetoken": "123", + "type": "emoji", + "uuid": "someUuid", + "value": "smiley", + "actionTimetoken": "1000" + } + ] + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "authKey" + + pubnub.getMessageActions( + channel = "coolChannel", + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + + assertFalse(requests[0].queryParameter("limit").isPresent) + assertFalse(requests[0].queryParameter("start").isPresent) + assertFalse(requests[0].queryParameter("end").isPresent) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/ReceiveMessageActions.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/ReceiveMessageActions.kt new file mode 100644 index 000000000..45b7dc3c0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/ReceiveMessageActions.kt @@ -0,0 +1,236 @@ +package com.pubnub.api.legacy.endpoints.message_actions + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNub +import com.pubnub.api.callbacks.SubscribeCallback +import com.pubnub.api.legacy.BaseTest +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.api.models.consumer.pubsub.PNSignalResult +import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult +import com.pubnub.test.CommonUtils.failTest +import com.pubnub.test.listen +import org.junit.Assert.assertEquals +import org.junit.Test +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger + +class ReceiveMessageActions : BaseTest() { + @Test + fun testReceiveMessageAction() { + stubFor( + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "1000", + "r": 12 + }, + "m": [ + { + "a": "1", + "f": 0, + "e": 3, + "i": "client-1639ed91", + "p": { + "t": "1000", + "r": 12 + }, + "k": "mySubscribeKey", + "c": "coolChannel", + "d": { + "source": "actions", + "version": "1.0", + "data": { + "messageTimetoken": "500", + "type": "reaction", + "value": "smiley", + "actionTimetoken": "600" + }, + "event": "added" + } + } + ] + } + """.trimIndent(), + ), + ), + ) + + val success = AtomicBoolean() + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + } + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + failTest() + } + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + failTest() + } + + override fun signal( + pubnub: PubNub, + pnSignalResult: PNSignalResult, + ) { + failTest() + } + + override fun messageAction( + pubnub: PubNub, + pnMessageActionResult: PNMessageActionResult, + ) { + assertEquals(pnMessageActionResult.channel, "coolChannel") + assertEquals(pnMessageActionResult.messageAction.messageTimetoken, 500L) + assertEquals(pnMessageActionResult.messageAction.uuid, "client-1639ed91") + assertEquals(pnMessageActionResult.messageAction.actionTimetoken, 600L) + assertEquals(pnMessageActionResult.messageAction.type, "reaction") + assertEquals(pnMessageActionResult.messageAction.value, "smiley") + success.set(true) + } + }, + ) + + pubnub.subscribe( + channels = listOf("coolChannel"), + ) + + success.listen() + } + + @Test + fun testReceiveMessageActionMulti() { + stubFor( + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "1000", + "r": 12 + }, + "m": [ + { + "a": "1", + "f": 0, + "e": 3, + "i": "client-1639ed91", + "p": { + "t": "1000", + "r": 12 + }, + "k": "mySubscribeKey", + "c": "coolChannel", + "d": { + "source": "actions", + "version": "1.0", + "data": { + "messageTimetoken": "500", + "type": "reaction", + "value": "smiley", + "actionTimetoken": "600" + }, + "event": "added" + } + }, + { + "a": "1", + "f": 0, + "e": 3, + "i": "client-1639ed91", + "p": { + "t": "1000", + "r": 12 + }, + "k": "mySubscribeKey", + "c": "coolChannel", + "d": { + "source": "actions", + "version": "1.0", + "data": { + "messageTimetoken": "500", + "type": "reaction", + "value": "grinning", + "actionTimetoken": "699" + }, + "event": "added" + } + } + ] + } + """.trimIndent(), + ), + ), + ) + + val count = AtomicInteger() + val success = AtomicBoolean() + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + } + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + failTest() + pnMessageResult.message + } + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + failTest() + } + + override fun signal( + pubnub: PubNub, + pnSignalResult: PNSignalResult, + ) { + failTest() + } + + override fun messageAction( + pubnub: PubNub, + pnMessageActionResult: PNMessageActionResult, + ) { + count.incrementAndGet() + if (count.get() == 2) { + success.set(true) + } + } + }, + ) + + pubnub.subscribe( + channels = listOf("coolChannel"), + ) + + success.listen() + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/RemoveMessageActionCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/RemoveMessageActionCoreEndpointTest.kt new file mode 100644 index 000000000..4018cd214 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/message_actions/RemoveMessageActionCoreEndpointTest.kt @@ -0,0 +1,250 @@ +package com.pubnub.api.legacy.endpoints.message_actions + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.delete +import com.github.tomakehurst.wiremock.client.WireMock.deleteRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.noContent +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.emptyJson +import com.pubnub.test.CommonUtils.failTest +import com.pubnub.test.listen +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.concurrent.atomic.AtomicBoolean + +class RemoveMessageActionCoreEndpointTest : BaseTest() { + @Test + fun testSyncSuccess() { + stubFor( + delete(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123/action/100")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": {} + } + """.trimIndent(), + ), + ), + ) + + pubnub.removeMessageAction( + channel = "coolChannel", + messageTimetoken = 123L, + actionTimetoken = 100L, + ).sync() + } + + @Test + fun testAsyncSuccess() { + stubFor( + delete(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123/action/100")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": {} + } + """.trimIndent(), + ), + ), + ) + + val success = AtomicBoolean() + + pubnub.removeMessageAction( + channel = "coolChannel", + messageTimetoken = 123L, + actionTimetoken = 100L, + ).async { result -> + assertFalse(result.isFailure) + success.set(true) + } + + success.listen() + } + + @Test + fun testMalformedJson() { + stubFor( + delete(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123/action/100")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200 + "data": { + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.removeMessageAction( + channel = "coolChannel", + messageTimetoken = 123L, + actionTimetoken = 100L, + ).sync() + } catch (e: Exception) { + failTest() + } + } + + @Test + fun testEmptyBody() { + stubFor( + delete(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123/action/100")) + .willReturn(emptyJson()), + ) + + try { + pubnub.removeMessageAction( + channel = "coolChannel", + messageTimetoken = 123L, + actionTimetoken = 100L, + ).sync() + } catch (e: Exception) { + failTest() + } + } + + @Test + fun testNoBody() { + stubFor( + delete(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123/action/100")) + .willReturn(noContent()), + ) + + try { + pubnub.removeMessageAction( + channel = "coolChannel", + messageTimetoken = 123L, + actionTimetoken = 100L, + ).sync() + } catch (e: Exception) { + failTest() + } + } + + @Test + fun testNoData() { + stubFor( + delete(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123/action/100")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200 + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.removeMessageAction( + channel = "coolChannel", + messageTimetoken = 123L, + actionTimetoken = 100L, + ).sync() + } catch (e: Exception) { + failTest() + } + } + + @Test + fun testNullData() { + stubFor( + delete(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123/action/100")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": null + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.removeMessageAction( + channel = "coolChannel", + messageTimetoken = 123L, + actionTimetoken = 100L, + ).sync() + } catch (e: Exception) { + failTest() + } + } + + @Test + fun testBlankChannel() { + try { + pubnub.removeMessageAction( + channel = " ", + messageTimetoken = 123L, + actionTimetoken = 100L, + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.CHANNEL_MISSING, e) + } + } + + @Test + fun testInvalidSubKey() { + config.subscribeKey = " " + try { + pubnub.removeMessageAction( + channel = "coolChannel", + messageTimetoken = 123L, + actionTimetoken = 100L, + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testAuthKeyRequired() { + stubFor( + delete(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/coolChannel/message/123/action/100")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "data": {} + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "authKey" + + pubnub.removeMessageAction( + channel = "coolChannel", + messageTimetoken = 123L, + actionTimetoken = 100L, + ).sync() + + val requests = findAll(deleteRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + + assertEquals("authKey", requests[0].queryParameter("auth").firstValue()) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/GetStateCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/GetStateCoreEndpointTest.kt new file mode 100644 index 000000000..c10543ce8 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/GetStateCoreEndpointTest.kt @@ -0,0 +1,453 @@ +package com.pubnub.api.legacy.endpoints.presence + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import org.awaitility.Awaitility +import org.hamcrest.core.IsEqual +import org.junit.Assert.assertEquals +import org.junit.Test +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger + +class GetStateCoreEndpointTest : BaseTest() { + @Test + fun testOneChannelSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.getPresenceState( + channels = listOf("testChannel"), + uuid = "sampleUUID", + ).sync() + + val ch1Data = result.stateByUUID["testChannel"]!! + assertEquals(pubnub.mapper.elementToInt(ch1Data, "age"), 20) + assertEquals(pubnub.mapper.elementToString(ch1Data, "status"), "online") + } + + @Test + fun testOneChannelWithoutUUIDSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.getPresenceState( + channels = listOf("testChannel"), + ).sync() + + val ch1Data = result.stateByUUID["testChannel"]!! + assertEquals(pubnub.mapper.elementToInt(ch1Data, "age"), 20) + assertEquals(pubnub.mapper.elementToString(ch1Data, "status"), "online") + } + + @Test + fun testFailedPayloadSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn( + aResponse().withBody( + """ + { "status": 200, "message": "OK", "payload": "age" : 20 + """.trimIndent(), + ), + ), + ) + + try { + pubnub.getPresenceState( + channels = listOf("testChannel"), + uuid = "sampleUUID", + ).sync() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testMultipleChannelSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1,ch2/uuid/sampleUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "ch1": { + "age": 20, + "status": "online" + }, + "ch2": { + "age": 100, + "status": "offline" + } + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.getPresenceState( + channels = listOf("ch1", "ch2"), + uuid = "sampleUUID", + ).sync() + + val ch1Data = result.stateByUUID["ch1"]!! + assertEquals(pubnub.mapper.elementToInt(ch1Data, "age"), 20) + assertEquals(pubnub.mapper.elementToString(ch1Data, "status"), "online") + + val ch2Data = result.stateByUUID["ch2"]!! + assertEquals(pubnub.mapper.elementToInt(ch2Data, "age"), 100) + assertEquals(pubnub.mapper.elementToString(ch2Data, "status"), "offline") + } + + @Test + fun testOneChannelGroupSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/uuid/sampleUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "chcg1": { + "age": 20, + "status": "online" + }, + "chcg2": { + "age": 100, + "status": "offline" + } + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.getPresenceState( + channelGroups = listOf("cg1"), + uuid = "sampleUUID", + ).sync() + + val ch1Data = result.stateByUUID["chcg1"]!! + assertEquals(pubnub.mapper.elementToInt(ch1Data, "age"), 20) + assertEquals(pubnub.mapper.elementToString(ch1Data, "status"), "online") + + val ch2Data = result.stateByUUID["chcg2"]!! + assertEquals(pubnub.mapper.elementToInt(ch2Data, "age"), 100) + assertEquals(pubnub.mapper.elementToString(ch2Data, "status"), "offline") + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("cg1", requests[0].queryParameter("channel-group").firstValue()) + } + + @Test + fun testManyChannelGroupSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/uuid/sampleUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "chcg1": { + "age": 20, + "status": "online" + }, + "chcg2": { + "age": 100, + "status": "offline" + } + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.getPresenceState( + channelGroups = listOf("cg1", "cg2"), + uuid = "sampleUUID", + ).sync() + + val ch1Data = result.stateByUUID["chcg1"]!! + assertEquals(pubnub.mapper.elementToInt(ch1Data, "age"), 20) + assertEquals(pubnub.mapper.elementToString(ch1Data, "status"), "online") + + val ch2Data = result.stateByUUID["chcg2"]!! + assertEquals(pubnub.mapper.elementToInt(ch2Data, "age"), 100) + assertEquals(pubnub.mapper.elementToString(ch2Data, "status"), "offline") + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("cg1,cg2", requests[0].queryParameter("channel-group").firstValue()) + } + + @Test + fun testCombinationSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/uuid/sampleUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "chcg1": { + "age": 20, + "status": "online" + }, + "chcg2": { + "age": 100, + "status": "offline" + } + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.getPresenceState( + channels = listOf("ch1"), + channelGroups = listOf("cg1", "cg2"), + uuid = "sampleUUID", + ).sync() + + val ch1Data = result.stateByUUID["chcg1"]!! + assertEquals(pubnub.mapper.elementToInt(ch1Data, "age"), 20) + assertEquals(pubnub.mapper.elementToString(ch1Data, "status"), "online") + + val ch2Data = result.stateByUUID["chcg2"]!! + assertEquals(pubnub.mapper.elementToInt(ch2Data, "age"), 100) + assertEquals(pubnub.mapper.elementToString(ch2Data, "status"), "offline") + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("cg1,cg2", requests[0].queryParameter("channel-group").firstValue()) + } + + @Test + fun testMissingChannelAndGroupSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.getPresenceState( + uuid = "sampleUUID", + ).sync() + } catch (e: Exception) { + assertPnException(PubNubError.CHANNEL_AND_GROUP_MISSING, e) + } + } + + @Test + fun testIsAuthRequiredSuccessSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "myKey" + + pubnub.getPresenceState( + channels = listOf("testChannel"), + uuid = "sampleUUID", + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testOperationTypeSuccessAsync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val atomic = AtomicInteger(0) + + pubnub.getPresenceState( + channels = listOf("testChannel"), + uuid = "sampleUUID", + ).async { result -> + result.onSuccess { + atomic.incrementAndGet() + } + } + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testBlankSubKeySync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + config.subscribeKey = " " + + try { + pubnub.getPresenceState( + channels = listOf("testChannel"), + uuid = "sampleUUID", + ).sync() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testEmptySubKeySync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + config.subscribeKey = "" + + try { + pubnub.getPresenceState( + channels = listOf("testChannel"), + uuid = "sampleUUID", + ).sync() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/HereNowCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/HereNowCoreEndpointTest.kt new file mode 100644 index 000000000..412003e0b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/HereNowCoreEndpointTest.kt @@ -0,0 +1,537 @@ +package com.pubnub.api.legacy.endpoints.presence + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.anyUrl +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching +import com.pubnub.api.PubNubError.SUBSCRIBE_KEY_MISSING +import com.pubnub.api.PubNubException +import com.pubnub.api.legacy.BaseTest +import com.pubnub.api.models.consumer.presence.PNHereNowResult +import org.awaitility.Awaitility +import org.hamcrest.core.IsEqual +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test +import java.util.concurrent.TimeUnit.SECONDS +import java.util.concurrent.atomic.AtomicInteger + +class HereNowCoreEndpointTest : BaseTest() { + @Test + fun testMultipleChannelStateSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/ch1,ch2")).willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "total_occupancy": 3, + "total_channels": 2, + "channels": { + "ch1": { + "occupancy": 1, + "uuids": [ + { + "uuid": "user1", + "state": { + "age": 10 + } + } + ] + }, + "ch2": { + "occupancy": 2, + "uuids": [ + { + "uuid": "user1", + "state": { + "age": 10 + } + }, + { + "uuid": "user3", + "state": { + "age": 30 + } + } + ] + } + } + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.hereNow( + channels = listOf("ch1", "ch2"), + includeState = true, + ).sync() + + assertEquals(response.totalChannels, 2) + assertEquals(response.totalOccupancy, 3) + + assertEquals(response.channels["ch1"]!!.channelName, "ch1") + assertEquals(response.channels["ch1"]!!.occupancy, 1) + assertEquals(response.channels["ch1"]!!.occupants.size, 1) + assertEquals(response.channels["ch1"]!!.occupants[0].uuid, "user1") + assertEquals(response.channels["ch1"]!!.occupants[0].state.toString(), """{"age":10}""") + + assertEquals(response.channels["ch2"]!!.channelName, "ch2") + assertEquals(response.channels["ch2"]!!.occupancy, 2) + assertEquals(response.channels["ch2"]!!.occupants.size, 2) + assertEquals(response.channels["ch2"]!!.occupants[0].uuid, "user1") + assertEquals(response.channels["ch2"]!!.occupants[0].state.toString(), """{"age":10}""") + assertEquals(response.channels["ch2"]!!.occupants[1].uuid, "user3") + assertEquals(response.channels["ch2"]!!.occupants[1].state.toString(), """{"age":30}""") + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("1", requests[0].queryParameter("state").firstValue()) + } + + @Test + fun testMultipleChannelSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/ch1,ch2")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "total_occupancy": 3, + "total_channels": 2, + "channels": { + "ch1": { + "occupancy": 1, + "uuids": [ + { + "uuid": "user1" + } + ] + }, + "ch2": { + "occupancy": 2, + "uuids": [ + { + "uuid": "user1" + }, + { + "uuid": "user3" + } + ] + } + } + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.hereNow( + channels = listOf("ch1", "ch2"), + includeState = true, + ).sync() + + assertEquals(response.totalChannels, 2) + assertEquals(response.totalOccupancy, 3) + + assertEquals(response.channels["ch1"]!!.channelName, "ch1") + assertEquals(response.channels["ch1"]!!.occupancy, 1) + assertEquals(response.channels["ch1"]!!.occupants.size, 1) + assertEquals(response.channels["ch1"]!!.occupants[0].uuid, "user1") + assertNull(response.channels["ch1"]!!.occupants[0].state) + + assertEquals(response.channels["ch2"]!!.channelName, "ch2") + assertEquals(response.channels["ch2"]!!.occupancy, 2) + assertEquals(response.channels["ch2"]!!.occupants.size, 2) + assertEquals(response.channels["ch2"]!!.occupants[0].uuid, "user1") + assertNull(response.channels["ch2"]!!.occupants[0].state) + assertEquals(response.channels["ch2"]!!.occupants[1].uuid, "user3") + assertNull(response.channels["ch2"]!!.occupants[1].state) + + val requests = findAll(getRequestedFor(anyUrl())) + assertEquals(1, requests.size) + assertEquals("1", requests[0].queryParameter("state").firstValue()) + } + + @Test + fun testMultipleChannelWithoutStateSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/game1,game2")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": { + "game1": { + "uuids": [ + "a3ffd012-a3b9-478c-8705-64089f24d71e" + ], + "occupancy": 1 + } + }, + "total_channels": 1, + "total_occupancy": 1 + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.hereNow( + channels = listOf("game1", "game2"), + includeState = false, + ).sync() + + assertEquals(response.totalChannels, 1) + assertEquals(response.totalOccupancy, 1) + + assertEquals(response.channels["game1"]!!.channelName, "game1") + assertEquals(response.channels["game1"]!!.occupancy, 1) + assertEquals(response.channels["game1"]!!.occupants.size, 1) + assertEquals( + response.channels["game1"]!!.occupants[0].uuid, + "a3ffd012-a3b9-478c-8705-64089f24d71e", + ) + assertNull(response.channels["game1"]!!.occupants[0].state) + } + + @Test + fun testMultipleChannelWithoutStateUUIDsSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/game1,game2")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": { + "game1": { + "occupancy": 1 + } + }, + "total_channels": 1, + "total_occupancy": 1 + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.hereNow( + channels = listOf("game1", "game2"), + includeState = false, + includeUUIDs = false, + ).sync() + + assertEquals(response.totalChannels, 1) + assertEquals(response.totalOccupancy, 1) + + assertEquals(response.channels["game1"]!!.channelName, "game1") + assertEquals(response.channels["game1"]!!.occupancy, 1) + assertTrue(response.channels["game1"]!!.occupants.isEmpty()) + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("1", requests[0].queryParameter("disable_uuids").firstValue()) + } + + @Test + fun testSingularChannelWithoutStateUUIDsSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/game1")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "occupancy": 3 + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.hereNow( + channels = listOf("game1"), + includeUUIDs = false, + ).sync() + + assertEquals(response.totalChannels, 1) + assertEquals(response.totalOccupancy, 3) + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("1", requests[0].queryParameter("disable_uuids").firstValue()) + } + + @Test + fun testSingularChannelWithoutStateSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/game1")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "uuids": [ + "a3ffd012-a3b9-478c-8705-64089f24d71e" + ], + "occupancy": 1 + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.hereNow( + channels = listOf("game1"), + includeState = false, + ).sync() + + assertEquals(response.totalChannels, 1) + assertEquals(response.totalOccupancy, 1) + assertEquals(response.channels.size, 1) + assertEquals(response.channels["game1"]!!.occupancy, 1) + assertEquals(response.channels["game1"]!!.occupants.size, 1) + assertEquals( + response.channels["game1"]!!.occupants[0].uuid, + "a3ffd012-a3b9-478c-8705-64089f24d71e", + ) + assertEquals(response.channels["game1"]!!.occupants[0].state, null) + } + + @Test + fun testSingularChannelSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/game1")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "uuids": [ + { + "uuid": "a3ffd012-a3b9-478c-8705-64089f24d71e", + "state": { + "age": 10 + } + } + ], + "occupancy": 1 + } + """.trimIndent(), + ), + ), + ) + val response = + pubnub.hereNow( + channels = listOf("game1"), + includeState = true, + ).sync() + + assertEquals(response.totalChannels, 1) + assertEquals(response.totalOccupancy, 1) + assertEquals(response.channels.size, 1) + assertEquals(response.channels["game1"]!!.occupancy, 1) + assertEquals(response.channels["game1"]!!.occupants.size, 1) + assertEquals( + response.channels["game1"]!!.occupants[0].uuid, + "a3ffd012-a3b9-478c-8705-64089f24d71e", + ) + assertEquals(response.channels["game1"]!!.occupants[0].state.toString(), """{"age":10}""") + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("1", requests[0].queryParameter("state").firstValue()) + } + + @Test + fun testSingularChannelAndGroupSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/game1")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": {}, + "total_channels": 0, + "total_occupancy": 0 + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.hereNow( + channels = listOf("game1"), + channelGroups = listOf("grp1"), + includeState = true, + ).sync() + + assertEquals(response.totalOccupancy, 0) + } + + @Test + fun testIsAuthRequiredSuccessSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/ch1,ch2")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "total_occupancy": 3, + "total_channels": 2, + "channels": { + "ch1": { + "occupancy": 1, + "uuids": [ + { + "uuid": "user1", + "state": { + "age": 10 + } + } + ] + }, + "ch2": { + "occupancy": 2, + "uuids": [ + { + "uuid": "user1", + "state": { + "age": 10 + } + }, + { + "uuid": "user3", + "state": { + "age": 30 + } + } + ] + } + } + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "myKey" + + pubnub.hereNow( + channels = listOf("ch1", "ch2"), + includeState = true, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testOperationTypeSuccessAsync() { + stubFor( + get(urlPathMatching("/v2/presence/sub_key/mySubscribeKey.*")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": { + "ch1": { + "occupancy": 1, + "uuids": [ + "myUUID" + ] + } + }, + "total_channels": 1, + "total_occupancy": 1 + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val atomic = AtomicInteger(0) + + pubnub.hereNow().async { result -> + result.onSuccess { + assertEquals(1, it.channels.size) + assertEquals(pubnub.configuration.userId.value, it.channels["ch1"]!!.occupants[0].uuid) + atomic.incrementAndGet() + } + } + + Awaitility.await().atMost(5, SECONDS).untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testEmptySubKeySync() { + config.subscribeKey = "" + + var response: PNHereNowResult? = null + try { + response = + pubnub.hereNow( + channels = listOf("ch1", "ch2"), + includeState = true, + ).sync() + } catch (e: Exception) { + assertNull(response) + assertTrue((e as PubNubException).pubnubError == SUBSCRIBE_KEY_MISSING) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/LeaveTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/LeaveTest.kt new file mode 100644 index 000000000..859aaedd2 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/LeaveTest.kt @@ -0,0 +1,297 @@ +package com.pubnub.api.legacy.endpoints.presence + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.legacy.BaseTest +import com.pubnub.internal.endpoints.presence.LeaveEndpoint +import com.pubnub.test.CommonUtils.assertPnException +import org.awaitility.Awaitility +import org.hamcrest.core.IsEqual +import org.junit.Assert.assertEquals +import org.junit.Test +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean + +class LeaveTest : BaseTest() { + @Test + fun subscribeChannelSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + + LeaveEndpoint(pubnub).apply { + channels = listOf("coolChannel") + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + } + + @Test + fun subscribeChannelsSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel,coolChannel2/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + + LeaveEndpoint(pubnub).apply { + channels = listOf("coolChannel", "coolChannel2") + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + } + + @Test + fun subscribeChannelsWithGroupSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel,coolChannel2/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + + LeaveEndpoint(pubnub).apply { + channels = listOf("coolChannel", "coolChannel2") + channelGroups = listOf("cg1") + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("cg1", requests[0].queryParameter("channel-group").firstValue()) + } + + @Test + fun subscribeChannelsWithGroupASync() { + val statusArrived = AtomicBoolean() + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel,coolChannel2/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + + LeaveEndpoint(pubnub).apply { + channels = listOf("coolChannel", "coolChannel2") + channelGroups = listOf("cg1") + }.async { result -> + result.onSuccess { + statusArrived.set(true) + } + } + + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic( + statusArrived, + IsEqual.equalTo(true), + ) + } + + @Test + fun subscribeGroupsSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + + LeaveEndpoint(pubnub).apply { + channelGroups = listOf("cg1", "cg2") + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("cg1,cg2", requests[0].queryParameter("channel-group").firstValue()) + } + + @Test + fun subscribeGroupSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + + LeaveEndpoint(pubnub).apply { + channelGroups = listOf("cg1") + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("cg1", requests[0].queryParameter("channel-group").firstValue()) + } + + @Test + fun testMissingChannelAndGroupSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + + try { + LeaveEndpoint(pubnub).apply {}.sync() + } catch (e: Exception) { + assertPnException(PubNubError.CHANNEL_AND_GROUP_MISSING, e) + } + } + + @Test + fun testBlankSubKeySync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + + config.subscribeKey = " " + + try { + LeaveEndpoint(pubnub).apply {}.sync() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testEmptySubKeySync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + + config.subscribeKey = "" + + try { + LeaveEndpoint(pubnub).apply {}.sync() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testIsAuthRequiredSuccessSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "myKey" + + LeaveEndpoint(pubnub).apply { + channels = listOf("coolChannel") + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/StateSetCoreEndpointEETest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/StateSetCoreEndpointEETest.kt new file mode 100644 index 000000000..395f2085a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/StateSetCoreEndpointEETest.kt @@ -0,0 +1,65 @@ +package com.pubnub.api.legacy.endpoints.presence + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.equalToJson +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.matching +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.google.gson.JsonObject +import com.pubnub.api.legacy.BaseTest +import com.pubnub.internal.endpoints.presence.SetStateEndpoint +import com.pubnub.internal.presence.eventengine.data.PresenceData +import org.junit.Assert.assertEquals +import org.junit.Test + +class StateSetCoreEndpointEETest : BaseTest() { + override fun onBefore() { + initConfiguration() + // initPubNub() + } + + @Test + fun applyStateForChannelSetsPresenceData() { + val presenceData = PresenceData() + + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("state", equalToJson("""{"age":20}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + SetStateEndpoint( + channels = listOf("testChannel"), + channelGroups = listOf(), + state = mapOf("age" to 20), + pubnub = pubnub, + presenceData = presenceData, + ).sync() + + assertEquals( + mapOf( + "testChannel" to + JsonObject().apply { + addProperty("age", 20) + }, + ), + presenceData.channelStates, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/StateSetCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/StateSetCoreEndpointTest.kt new file mode 100644 index 000000000..d8cfc2c91 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/StateSetCoreEndpointTest.kt @@ -0,0 +1,426 @@ +package com.pubnub.api.legacy.endpoints.presence + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.equalToJson +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.matching +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.failTest +import org.junit.Assert.assertEquals +import org.junit.Test + +class StateSetCoreEndpointTest : BaseTest() { + @Test + fun applyStateForChannelSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("state", equalToJson("""{"age":20}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.setPresenceState( + channels = listOf("testChannel"), + state = mapOf("age" to 20), + ).sync() + + assertEquals(pubnub.mapper.elementToInt(result.state, "age"), 20) + assertEquals(pubnub.mapper.elementToString(result.state, "status"), "online") + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + } + + @Test + fun applyStateForSomebodyElseChannelSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/someoneElseUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("state", equalToJson("""{"age":20}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.setPresenceState( + channels = listOf("testChannel"), + state = mapOf("age" to 20), + uuid = "someoneElseUUID", + ).sync() + + assertEquals(pubnub.mapper.elementToInt(result.state, "age"), 20) + assertEquals(pubnub.mapper.elementToString(result.state, "status"), "online") + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + } + + @Test + fun applyStateForChannelsSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel,testChannel2/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("state", equalToJson("""{"age":20}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.setPresenceState( + channels = listOf("testChannel", "testChannel2"), + state = mapOf("age" to 20), + ).sync() + + assertEquals(pubnub.mapper.elementToInt(result.state, "age"), 20) + assertEquals(pubnub.mapper.elementToString(result.state, "status"), "online") + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + } + + @Test + fun applyStateForChannelGroupSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("state", equalToJson("""{"age":20}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.setPresenceState( + channelGroups = listOf("cg1"), + state = mapOf("age" to 20), + ).sync() + + assertEquals(pubnub.mapper.elementToInt(result.state, "age"), 20) + assertEquals(pubnub.mapper.elementToString(result.state, "status"), "online") + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + } + + @Test + fun applyStateForChannelGroupsSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("state", equalToJson("""{"age":20}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.setPresenceState( + channelGroups = listOf("cg1", "cg2"), + state = mapOf("age" to 20), + ).sync() + + assertEquals(pubnub.mapper.elementToInt(result.state, "age"), 20) + assertEquals(pubnub.mapper.elementToString(result.state, "status"), "online") + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("cg1,cg2", requests[0].queryParameter("channel-group").firstValue()) + } + + @Test + fun applyStateForMixSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("state", equalToJson("""{"age":20}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val result = + pubnub.setPresenceState( + channels = listOf("ch1"), + channelGroups = listOf("cg1", "cg2"), + state = mapOf("age" to 20), + ).sync() + + assertEquals(pubnub.mapper.elementToInt(result.state, "age"), 20) + assertEquals(pubnub.mapper.elementToString(result.state, "status"), "online") + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + } + + @Test + fun applyNon200Sync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("state", equalToJson("""{"age":20}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ).withStatus(400), + ), + ) + + try { + pubnub.setPresenceState( + channels = listOf("ch1"), + channelGroups = listOf("cg1", "cg2"), + state = mapOf("age" to 20), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.HTTP_ERROR, e) + } + } + + @Test + fun testIsAuthRequiredSuccessSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("state", equalToJson("""{"age":20}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "myKey" + + pubnub.setPresenceState( + channels = listOf("testChannel"), + state = mapOf("age" to 20), + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testBlankSubKeySync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("state", equalToJson("""{"age":20}""")) + .willReturn( + aResponse().withBody( + "{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : " + + "20, \"status\" : \"online\" }, \"service\": \"Presence\"}", + ), + ), + ) + + config.subscribeKey = " " + + try { + pubnub.setPresenceState( + channels = listOf("testChannel"), + state = mapOf("age" to 20), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testEmptySubKeySync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("state", equalToJson("""{"age":20}""")) + .willReturn( + aResponse().withBody( + "{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : " + + "20, \"status\" : \"online\" }, \"service\": \"Presence\"}", + ), + ), + ) + + config.subscribeKey = "" + + try { + pubnub.setPresenceState( + channels = listOf("testChannel"), + state = mapOf("age" to 20), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testChannelAndGroupMissingSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("state", equalToJson("""{"age":20}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "age": 20, + "status": "online" + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.setPresenceState( + state = mapOf("age" to 20), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.CHANNEL_AND_GROUP_MISSING, e) + } + } + + @Test + fun testNullPayloadSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("state", equalToJson("""{"age":20}""")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.setPresenceState( + channels = listOf("testChannel"), + state = mapOf("age" to 20), + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/WhereNowCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/WhereNowCoreEndpointTest.kt new file mode 100644 index 000000000..9743667ae --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/presence/WhereNowCoreEndpointTest.kt @@ -0,0 +1,389 @@ +package com.pubnub.api.legacy.endpoints.presence + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.failTest +import org.awaitility.Awaitility +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers +import org.hamcrest.core.IsEqual +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import java.util.concurrent.TimeUnit.SECONDS +import java.util.concurrent.atomic.AtomicInteger + +class WhereNowCoreEndpointTest : BaseTest() { + @Test + fun testSyncSuccess() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": [ + "a", + "b" + ] + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val response = pubnub.whereNow().sync() + assertThat(response.channels, Matchers.contains("a", "b")) + } + + @Test + fun testSyncSuccessCustomUUID() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/customUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": [ + "a", + "b" + ] + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val response = + pubnub.whereNow( + uuid = "customUUID", + ).sync() + + assertThat(response.channels, Matchers.contains("a", "b")) + } + + @Test + fun testSyncBrokenWithString() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn( + aResponse().withBody( + "{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\"" + + ": [zimp]}, \"service\": \"Presence\"}", + ), + ), + ) + + try { + pubnub.whereNow().sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testSyncBrokenWithoutJSON() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn( + aResponse().withBody( + "{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": " + + "zimp}, \"service\": \"Presence\"}", + ), + ), + ) + + try { + pubnub.whereNow().sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testSyncBrokenWithout200() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn( + aResponse() + .withStatus(404) + .withBody( + "{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}," + + " \"service\": \"Presence\"}", + ), + ), + ) + + try { + pubnub.whereNow().sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.HTTP_ERROR, e) + } + } + + @Test + fun testAsyncSuccess() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": [ + "a", + "b" + ] + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val atomic = AtomicInteger(0) + + pubnub.whereNow().async { result -> + assertFalse(result.isFailure) + result.onSuccess { + assertThat(it.channels, Matchers.contains("a", "b")) + atomic.incrementAndGet() + } + } + + Awaitility.await().atMost(5, SECONDS).untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testAsyncBrokenWithString() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn( + aResponse().withBody( + "{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": " + + "[zimp]}, \"service\": \"Presence\"}", + ), + ), + ) + + val atomic = AtomicInteger(0) + + pubnub.whereNow().async { result -> + assertTrue(result.isFailure) + result.onFailure { + assertPnException(PubNubError.PARSING_ERROR, it) + atomic.incrementAndGet() + } + } + + Awaitility.await().atMost(5, SECONDS).untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testAsyncBrokenWithoutJSON() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn( + aResponse().withBody( + "{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": " + + "zimp}, \"service\": \"Presence\"}", + ), + ), + ) + + val atomic = AtomicInteger(0) + + pubnub.whereNow().async { result -> + assertTrue(result.isFailure) + result.onFailure { + assertPnException(PubNubError.PARSING_ERROR, it) + atomic.incrementAndGet() + } + } + + Awaitility.await().atMost(5, SECONDS).untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testAsyncBrokenWithout200() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn( + aResponse() + .withStatus(400) + .withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": [ + "a", + "b" + ] + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + val atomic = AtomicInteger(0) + + pubnub.whereNow().async { result -> + assertTrue(result.isFailure) + result.onFailure { + assertPnException(PubNubError.HTTP_ERROR, it) + atomic.incrementAndGet() + } + } + + Awaitility.await().atMost(5, SECONDS).untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testIsAuthRequiredSuccessSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": [ + "a", + "b" + ] + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + config.authKey = "myKey" + + pubnub.whereNow().sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testBlankSubKeySync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": [ + "a", + "b" + ] + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + config.subscribeKey = " " + + try { + pubnub.whereNow().sync() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testEmptySubKeySync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": [ + "a", + "b" + ] + }, + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + config.subscribeKey = "" + + try { + pubnub.whereNow().sync() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testNullPayloadSync() { + stubFor( + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + try { + pubnub.whereNow().sync() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/PublishTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/PublishTest.kt new file mode 100644 index 000000000..a7ef671cc --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/PublishTest.kt @@ -0,0 +1,540 @@ +package com.pubnub.api.legacy.endpoints.pubsub + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.equalToJson +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.matching +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.failTest +import org.awaitility.Awaitility +import org.hamcrest.core.IsEqual +import org.json.JSONArray +import org.json.JSONObject +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.nio.charset.Charset +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger + +class PublishTest : BaseTest() { + @Test + fun testFireSuccessSync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.fire( + channel = "coolChannel", + message = "hi", + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + assertEquals("true", requests[0].queryParameter("norep").firstValue()) + assertEquals("0", requests[0].queryParameter("store").firstValue()) + } + + @Test + fun testNoRepSuccessSync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = "hi", + replicate = false, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + assertEquals("true", requests[0].queryParameter("norep").firstValue()) + } + + @Test + fun testRepDefaultSuccessSync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hirep%22")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = "hirep", + replicate = false, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + assertEquals("true", requests[0].queryParameter("norep").firstValue()) + } + + @Test + fun testSuccessSync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = "hi", + replicate = false, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + } + + @Test + fun testSuccessSequenceSync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = "hi", + ).sync() + + pubnub.publish( + channel = "coolChannel", + message = "hi", + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(2, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + assertEquals("1", requests[0].queryParameter("seqn").firstValue()) + assertEquals("2", requests[1].queryParameter("seqn").firstValue()) + } + + @Test + fun testSuccessPostSync() { + stubFor( + post(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = listOf("m1", "m2"), + usePost = true, + ).sync() + + val requests = findAll(postRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + assertEquals("""["m1","m2"]""", String(requests[0].body, Charset.forName("UTF-8"))) + } + + @Test + fun testSuccessStoreFalseSync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = "hi", + shouldStore = false, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("0", requests[0].queryParameter("store").firstValue()) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + } + + @Test + fun testSuccessStoreTrueSync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = "hi", + shouldStore = true, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("1", requests[0].queryParameter("store").firstValue()) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + } + + @Test + fun testSuccessMetaSync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("meta", equalToJson("""["m1","m2"]""")) + .withQueryParam("store", matching("0")) + .withQueryParam("seqn", matching("1")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = "hi", + meta = listOf("m1", "m2"), + shouldStore = false, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + } + + @Test + fun testSuccessAuthKeySync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + config.authKey = "authKey" + + pubnub.publish( + channel = "coolChannel", + message = "hi", + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("authKey", requests[0].queryParameter("auth").firstValue()) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + } + + @Test + fun testSuccessIntSync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/10")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = 10, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + } + + @Test + fun testSuccessArraySync() { + stubFor( + get( + urlPathEqualTo( + "/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/[%22a%22,%22b%22,%22c%22]", + ), + ).willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + val l = listOf("a", "b", "c") + + pubnub.publish( + channel = "coolChannel", + message = l, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + } + + @Test + fun testSuccessArrayEncryptedSync() { + stubFor( + get( + urlPathEqualTo( + "/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22HFP7V6bDwBLrwc1t8Rnrog==%22", + ), + ) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + config.cryptoModule = CryptoModule.createLegacyCryptoModule("testCipher", false) + + pubnub.publish( + channel = "coolChannel", + message = listOf("m1", "m2"), + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + } + + @Test + fun testSuccessPostEncryptedSync() { + stubFor( + post(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + config.cryptoModule = CryptoModule.createLegacyCryptoModule("testCipher", false) + + pubnub.publish( + channel = "coolChannel", + message = listOf("m1", "m2"), + usePost = true, + ).sync() + + val requests = findAll(postRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + assertEquals( + """"HFP7V6bDwBLrwc1t8Rnrog=="""", + String(requests[0].body, Charset.forName("UTF-8")), + ) + } + + @Test + fun testSuccessHashMapSync() { + val params: MutableMap = HashMap() + params["a"] = 10 + params["z"] = "test" + stubFor( + get( + urlPathEqualTo( + "/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%7B%22a%22:10,%22z%22:%22test%22%7D", + ), + ) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = params, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + } + + private class TestPojo() { + constructor(s1: String, s2: String) : this() { + field1 = s1 + field2 = s2 + } + + private var field1: String? = null + private var field2: String? = null + } + + @Test + fun testSuccessPOJOSync() { + val testPojo = TestPojo("10", "20") + stubFor( + get( + urlPathEqualTo( + "/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%7B%22field1%22:%2210%22,%22field2%22:%2220%22%7D", + ), + ) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = testPojo, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + } + + @Test + fun testJSONObject() { + val testMessage = JSONObject() + testMessage.put("hi", "test") + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%7B%22hi%22:%22test%22%7D")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = testMessage.toMap(), + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + } + + @Test + fun testJSONList() { + val testMessage = JSONArray() + testMessage.put("hi") + testMessage.put("hi2") + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/[%22hi%22,%22hi2%22]")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = testMessage.toList(), + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myUUID", requests[0].queryParameter("uuid").firstValue()) + } +// @Deprecated("Channel is required parameter now") +// @Test +// fun testMissingChannel() { +// stubFor( +// get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) +// .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")) +// ) +// +// try { +// pubnub.publish( +// message = "hi" +// ).sync() +// failTest() +// } catch (e: PubNubException) { +// assertPnException(PubNubError.CHANNEL_MISSING, e) +// } +// } + + @Test + fun testEmptyChannel() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("""[1,"Sent","158832720000000000"]""")), + ) + + try { + pubnub.publish( + channel = " ", + message = "hi", + ).sync() + failTest() + } catch (e: PubNubException) { + assertPnException(PubNubError.CHANNEL_MISSING, e) + } + } + + @Test + fun testOperationTypeSuccessAsync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + val atomic = AtomicInteger(0) + + pubnub.publish( + channel = "coolChannel", + message = "hi", + ).async { result -> + result.onSuccess { + atomic.incrementAndGet() + } + } + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testEmptySubKeySync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hirep%22")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + config.subscribeKey = "" + + try { + pubnub.publish( + channel = "coolChannel", + message = "hirep", + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testEmptyPublishKeySync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hirep%22")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + config.publishKey = "" + + try { + pubnub.publish( + channel = "coolChannel", + message = "hirep", + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PUBLISH_KEY_MISSING, e) + } + } + + @Test + fun testTTLShouldStoryDefaultSuccessSync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = "hi", + ttl = 10, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("10", requests[0].queryParameter("ttl").firstValue()) + } + + @Test + fun testTTLShouldStoreFalseSuccessSync() { + stubFor( + get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("""[1,"Sent","15883272000000000"]""")), + ) + + pubnub.publish( + channel = "coolChannel", + message = "hi", + shouldStore = false, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("0", requests[0].queryParameter("store").firstValue()) + assertFalse(requests[0].queryParameter("ttl").isPresent) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/SignalTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/SignalTest.kt new file mode 100644 index 000000000..35e34a551 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/SignalTest.kt @@ -0,0 +1,191 @@ +package com.pubnub.api.legacy.endpoints.pubsub + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.pubnub.api.PubNub +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.callbacks.SubscribeCallback +import com.pubnub.api.legacy.BaseTest +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.api.models.consumer.pubsub.PNSignalResult +import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.listen +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.UUID +import java.util.concurrent.atomic.AtomicBoolean + +class SignalTest : BaseTest() { + @Test + fun testSignalGetSuccessSync() { + stubFor( + get(urlMatching("/signal/myPublishKey/mySubscribeKey/0/coolChannel.*")) + .willReturn( + aResponse() + .withBody( + """ + [ + 1, + "Sent", + "1000" + ] + """.trimIndent(), + ), + ), + ) + + val payload = mapOf("text" to "hello") + + pubnub.signal( + channel = "coolChannel", + message = payload, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/signal.*"))) + assertEquals(1, requests.size) + val request = requests[0] + assertEquals("myUUID", request.queryParameter("uuid").firstValue()) + + val httpUrl = request.absoluteUrl.toHttpUrlOrNull() + var decodedSignalPayload: String? = null + if (httpUrl != null) { + decodedSignalPayload = httpUrl.pathSegments[httpUrl.pathSize - 1] + } + assertEquals(pubnub.mapper.toJson(payload), decodedSignalPayload) + } + + @Test + fun testSignalGetSuccessAsync() { + stubFor( + get(urlMatching("/signal/myPublishKey/mySubscribeKey/0/coolChannel.*")) + .willReturn( + aResponse() + .withBody( + """ + [ + 1, + "Sent", + "1000" + ] + """.trimIndent(), + ), + ), + ) + + val payload = UUID.randomUUID().toString() + + val success = AtomicBoolean() + + pubnub.signal( + channel = "coolChannel", + message = payload, + ).async { result -> + assertFalse(result.isFailure) + result.onSuccess { + assertEquals("1000", it.timetoken.toString()) + success.set(true) + } + } + + success.listen() + } + + @Test + fun testSignalSuccessReceive() { + stubFor( + get(urlMatching("/v2/subscribe/mySubscribeKey/coolChannel/0.*")) + .willReturn( + aResponse().withBody( + """ + { + "m": [ + { + "c": "coolChannel", + "f": "0", + "i": "uuid", + "d": "hello", + "e": 1, + "p": { + "t": 1000, + "r": 1 + }, + "k": "mySubscribeKey", + "b": "coolChannel" + } + ], + "t": { + "r": "56", + "t": 1000 + } + } + """.trimIndent(), + ), + ), + ) + + val success = AtomicBoolean() + + pubnub.addListener( + object : SubscribeCallback() { + override fun signal( + pubnub: PubNub, + pnSignalResult: PNSignalResult, + ) { + assertEquals("coolChannel", pnSignalResult.channel) + assertEquals("hello", pnSignalResult.message.asString) + assertEquals("uuid", pnSignalResult.publisher) + success.set(true) + } + + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) {} + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) {} + + override fun messageAction( + pubnub: PubNub, + pnMessageActionResult: PNMessageActionResult, + ) {} + }, + ) + + pubnub.subscribe( + channels = listOf("coolChannel"), + ) + + success.listen() + } + + @Test + fun testSignalFailBlankChannel() { + try { + pubnub.signal( + channel = " ", + message = UUID.randomUUID().toString(), + ).sync() + throw RuntimeException() + } catch (e: PubNubException) { + assertPnException(PubNubError.CHANNEL_MISSING, e) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/SubscribeCoreEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/SubscribeCoreEndpointTest.kt new file mode 100644 index 000000000..d8d2b1007 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/pubsub/SubscribeCoreEndpointTest.kt @@ -0,0 +1,561 @@ +package com.pubnub.api.legacy.endpoints.pubsub + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.matching +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.legacy.BaseTest +import com.pubnub.internal.endpoints.pubsub.SubscribeEndpoint +import com.pubnub.internal.models.server.SubscribeMessage +import com.pubnub.test.CommonUtils.assertPnException +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +class SubscribeCoreEndpointTest : BaseTest() { + @Test + fun subscribeChannelSync() { + stubFor( + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "someSubKey", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + val subscribeEnvelope = + SubscribeEndpoint(pubnub).apply { + channels = listOf("coolChannel") + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("1", subscribeEnvelope.metadata.region) + assertEquals(14607577960932487L, subscribeEnvelope.metadata.timetoken) + assertEquals(1, subscribeEnvelope.messages.size) + + val subscribeMessage: SubscribeMessage = subscribeEnvelope.messages[0] + assertEquals("4", subscribeMessage.shard) + assertEquals("0", subscribeMessage.flags) + assertEquals("coolChannel", subscribeMessage.channel) + assertEquals("coolChan-bnel", subscribeMessage.subscriptionMatch) + assertEquals("someSubKey", subscribeMessage.subscribeKey) + assertEquals("Client-g5d4g", subscribeMessage.issuingClientId) + assertEquals("""{"text":"Enter Message Here"}""", subscribeMessage.payload.toString()) + } + + @Test + fun subscribeChannelsSync() { + stubFor( + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "someSubKey", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + val subscribeEnvelope = + SubscribeEndpoint(pubnub).apply { + channels = listOf("coolChannel") + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("1", subscribeEnvelope.metadata.region) + assertTrue(subscribeEnvelope.metadata.timetoken == 14607577960932487L) + assertEquals(1, subscribeEnvelope.messages.size) + + val subscribeMessage: SubscribeMessage = subscribeEnvelope.messages[0] + assertEquals("4", subscribeMessage.shard) + assertEquals("0", subscribeMessage.flags) + assertEquals("coolChannel", subscribeMessage.channel) + assertEquals("coolChan-bnel", subscribeMessage.subscriptionMatch) + assertEquals("someSubKey", subscribeMessage.subscribeKey) + assertEquals("Client-g5d4g", subscribeMessage.issuingClientId) + assertEquals("""{"text":"Enter Message Here"}""", subscribeMessage.payload.toString()) + } + + @Test + fun subscribeChannelsAuthSync() { + config.authKey = "authKey" + + stubFor( + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel,coolChannel2/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + SubscribeEndpoint(pubnub).apply { + channels = listOf("coolChannel", "coolChannel2") + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("authKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun subscribeChannelsWithGroupSync() { + stubFor( + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel,coolChannel2/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + SubscribeEndpoint(pubnub).apply { + channels = listOf("coolChannel", "coolChannel2") + channelGroups = listOf("cg1") + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("cg1", requests[0].queryParameter("channel-group").firstValue()) + } + + @Test + fun subscribeGroupsSync() { + stubFor( + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + SubscribeEndpoint(pubnub).apply { + channelGroups = listOf("cg1", "cg2") + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("cg1,cg2", requests[0].queryParameter("channel-group").firstValue()) + } + + @Test + @Throws(PubNubException::class) + fun subscribeGroupSync() { + stubFor( + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + SubscribeEndpoint(pubnub).apply { + channelGroups = listOf("cg1") + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("cg1", requests[0].queryParameter("channel-group").firstValue()) + } + + @Test + @Throws(PubNubException::class) + fun subscribeWithTimeTokenSync() { + stubFor( + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + SubscribeEndpoint(pubnub).apply { + channelGroups = listOf("cg1") + timetoken = 1337L + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("cg1", requests[0].queryParameter("channel-group").firstValue()) + assertEquals("1337", requests[0].queryParameter("tt").firstValue()) + } + + @Test + fun subscribeWithFilter() { + stubFor( + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/,/0")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("filter-expr", matching("this=1&that=cool")) + .withQueryParam("channel-group", matching("cg1")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + SubscribeEndpoint(pubnub).apply { + channelGroups = listOf("cg1") + filterExpression = "this=1&that=cool" + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + } + + @Test + fun subscribeWithRegion() { + stubFor( + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + SubscribeEndpoint(pubnub).apply { + channelGroups = listOf("cg1") + region = "10" + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("cg1", requests[0].queryParameter("channel-group").firstValue()) + assertEquals("10", requests[0].queryParameter("tr").firstValue()) + } + + @Test + fun subscribeMissingChannelAndGroupSync() { + try { + SubscribeEndpoint(pubnub).apply { + }.sync() + throw RuntimeException() + } catch (e: PubNubException) { + assertPnException(PubNubError.CHANNEL_AND_GROUP_MISSING, e) + } + } + + @Test + fun testMissingSubKeySync() { + config.subscribeKey = " " + + try { + SubscribeEndpoint(pubnub).apply { + }.sync() + throw RuntimeException() + } catch (e: PubNubException) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun stopAndReconnect() { + stubFor( + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel,coolChannel2/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + SubscribeEndpoint(pubnub).apply { + channels = listOf("coolChannel", "coolChannel2") + }.sync() + + pubnub.disconnect() + pubnub.reconnect() + + SubscribeEndpoint(pubnub).apply { + channels = listOf("coolChannel", "coolChannel2") + }.sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(2, requests.size) + } + + @Test + fun testSuccessIncludeState() { + config.presenceTimeout = 123 + + stubFor( + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch1,ch2/0")) + .willReturn(aResponse().withStatus(200)), + ) + + try { + SubscribeEndpoint(pubnub).apply { + channels = listOf("ch1", "ch2") + state = + mapOf( + "CH1" to "this-is-channel1", + "CH2" to "this-is-channel2", + ) + }.sync() + } catch (e: Exception) { + // e.printStackTrace() + } + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + + val request = requests[0] + assertEquals("myUUID", request.queryParameter("uuid").firstValue()) + assertEquals("123", request.queryParameter("heartbeat").firstValue()) + assertEquals( + """{"CH1":"this-is-channel1","CH2":"this-is-channel2"}""", + request.queryParameter("state").firstValue(), + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/AddChannelsToPushTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/AddChannelsToPushTest.kt new file mode 100644 index 000000000..084ad5dc4 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/AddChannelsToPushTest.kt @@ -0,0 +1,186 @@ +package com.pubnub.api.legacy.endpoints.push + +import com.github.tomakehurst.wiremock.client.WireMock +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.failTest +import com.pubnub.test.listen +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.concurrent.atomic.AtomicBoolean + +class AddChannelsToPushTest : BaseTest() { + @Test + fun testAddAppleSuccessSync() { + WireMock.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(WireMock.aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + pubnub.addPushNotificationsOnChannels( + deviceId = "niceDevice", + pushType = PNPushType.APNS, + channels = listOf("ch1", "ch2", "ch3"), + ).sync() + + val requests = WireMock.findAll(WireMock.getRequestedFor(WireMock.urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("ch1,ch2,ch3", requests[0].queryParameter("add").firstValue()) + assertEquals("apns", requests[0].queryParameter("type").firstValue()) + assertFalse(requests[0].queryParameter("environment").isPresent) + assertFalse(requests[0].queryParameter("topic").isPresent) + } + + @Test + fun testAddFirebaseSuccessSync() { + WireMock.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(WireMock.aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + pubnub.addPushNotificationsOnChannels( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + channels = listOf("ch1", "ch2", "ch3"), + ).sync() + + val requests = WireMock.findAll(WireMock.getRequestedFor(WireMock.urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("ch1,ch2,ch3", requests[0].queryParameter("add").firstValue()) + assertEquals("gcm", requests[0].queryParameter("type").firstValue()) + assertFalse(requests[0].queryParameter("environment").isPresent) + assertFalse(requests[0].queryParameter("topic").isPresent) + } + + @Test + fun testAddMicrosoftSuccessSync() { + WireMock.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(WireMock.aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + pubnub.addPushNotificationsOnChannels( + deviceId = "niceDevice", + pushType = PNPushType.MPNS, + channels = listOf("ch1", "ch2", "ch3"), + ).sync() + + val requests = WireMock.findAll(WireMock.getRequestedFor(WireMock.urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("ch1,ch2,ch3", requests[0].queryParameter("add").firstValue()) + assertEquals("mpns", requests[0].queryParameter("type").firstValue()) + assertFalse(requests[0].queryParameter("environment").isPresent) + assertFalse(requests[0].queryParameter("topic").isPresent) + } + + @Test + fun testAddApns2SuccessSync() { + WireMock.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/v2/push/sub-key/mySubscribeKey/devices-apns2/niceDevice")) + .willReturn(WireMock.aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + pubnub.addPushNotificationsOnChannels( + deviceId = "niceDevice", + pushType = PNPushType.APNS2, + channels = listOf("ch1", "ch2", "ch3"), + topic = "topic", + ).sync() + + val requests = WireMock.findAll(WireMock.getRequestedFor(WireMock.urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("ch1,ch2,ch3", requests[0].queryParameter("add").firstValue()) + assertEquals("development", requests[0].queryParameter("environment").firstValue()) + assertEquals("topic", requests[0].queryParameter("topic").firstValue()) + assertFalse(requests[0].queryParameter("type").isPresent) + } + + @Test + fun testIsAuthRequiredSuccessAdd() { + WireMock.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(WireMock.aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + config.authKey = "myKey" + + pubnub.addPushNotificationsOnChannels( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + channels = listOf("ch1", "ch2", "ch3"), + ).sync() + + val requests = WireMock.findAll(WireMock.getRequestedFor(WireMock.urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testOperationTypeSuccessAdd() { + WireMock.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(WireMock.aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + val success = AtomicBoolean() + + pubnub.addPushNotificationsOnChannels( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + channels = listOf("ch1", "ch2", "ch3"), + ).async { result -> + assertFalse(result.isFailure) + success.set(true) + } + + success.listen() + } + + @Test + fun testEmptySubscribeKeyAdd() { + config.subscribeKey = "" + + try { + pubnub.addPushNotificationsOnChannels( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + channels = listOf("ch1", "ch2", "ch3"), + ).sync() + failTest() + } catch (e: PubNubException) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testEmptyDeviceIdAdd() { + try { + pubnub.addPushNotificationsOnChannels( + pushType = PNPushType.FCM, + channels = listOf("ch1", "ch2", "ch3"), + deviceId = " ", + ).sync() + failTest() + } catch (e: PubNubException) { + assertPnException(PubNubError.DEVICE_ID_MISSING, e) + } + } + + @Test + fun testEmptyChannelsAdd() { + try { + pubnub.addPushNotificationsOnChannels( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + channels = emptyList(), + ).sync() + failTest() + } catch (e: PubNubException) { + assertPnException(PubNubError.CHANNEL_MISSING, e) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/ListPushProvisionsTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/ListPushProvisionsTest.kt new file mode 100644 index 000000000..c8bce61f5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/ListPushProvisionsTest.kt @@ -0,0 +1,308 @@ +package com.pubnub.api.legacy.endpoints.push + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.ok +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNPushEnvironment +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.failTest +import com.pubnub.test.listen +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.UUID +import java.util.concurrent.atomic.AtomicBoolean + +class ListPushProvisionsTest : BaseTest() { + @Test + fun testAppleSuccessSync() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("""["ch1", "ch2", "ch3"]""")), + ) + + val response = + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.APNS, + topic = "irrelevant", + ).sync() + + assertEquals(listOf("ch1", "ch2", "ch3"), response.channels) + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("apns", requests[0].queryParameter("type").firstValue()) + assertFalse(requests[0].queryParameter("environment").isPresent) + assertFalse(requests[0].queryParameter("topic").isPresent) + } + + @Test + fun testFirebaseSuccessSync() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("""["ch1", "ch2", "ch3"]""")), + ) + + val response = + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + topic = "irrelevant", + ).sync() + + assertEquals(listOf("ch1", "ch2", "ch3"), response.channels) + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("gcm", requests[0].queryParameter("type").firstValue()) + assertFalse(requests[0].queryParameter("environment").isPresent) + assertFalse(requests[0].queryParameter("topic").isPresent) + } + + @Test + fun testMicrosoftSuccessSync() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("""["ch1", "ch2", "ch3"]""")), + ) + + val response = + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.MPNS, + topic = "irrelevant", + ).sync() + + assertEquals(listOf("ch1", "ch2", "ch3"), response.channels) + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("mpns", requests[0].queryParameter("type").firstValue()) + assertFalse(requests[0].queryParameter("environment").isPresent) + assertFalse(requests[0].queryParameter("topic").isPresent) + } + + @Test + fun testApns2SuccessSync() { + stubFor( + get(urlPathEqualTo("/v2/push/sub-key/mySubscribeKey/devices-apns2/niceDevice")) + .willReturn(aResponse().withBody("""["ch1", "ch2", "ch3"]""")), + ) + + val response = + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.APNS2, + topic = "news", + ).sync() + + assertEquals(listOf("ch1", "ch2", "ch3"), response.channels) + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("development", requests[0].queryParameter("environment").firstValue()) + assertEquals("news", requests[0].queryParameter("topic").firstValue()) + } + + @Test + fun testApns2SuccessSync_Environment() { + stubFor( + get(urlPathEqualTo("/v2/push/sub-key/mySubscribeKey/devices-apns2/niceDevice")) + .willReturn(aResponse().withBody("""["ch1", "ch2", "ch3"]""")), + ) + + val response = + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.APNS2, + topic = "news", + environment = PNPushEnvironment.PRODUCTION, + ).sync() + + assertEquals(listOf("ch1", "ch2", "ch3"), response.channels) + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("production", requests[0].queryParameter("environment").firstValue()) + assertEquals("news", requests[0].queryParameter("topic").firstValue()) + } + + @Test + fun testEmptyArray() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[]")), + ) + + val response = + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + ).sync() + + assertEquals(emptyList(), response.channels) + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + } + + @Test + fun testEmptyBody() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("")), + ) + + try { + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + ).sync() + failTest() + } catch (e: Exception) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testMalformedResponseSync() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(ok().withBody("{")), + ) + + try { + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + ).sync() + failTest() + } catch (e: PubNubException) { + assertPnException(PubNubError.PARSING_ERROR, e) + } + } + + @Test + fun testMalformedResponseAsync() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(ok().withBody("{")), + ) + + val success = AtomicBoolean() + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + ).async { result -> + result.onFailure { + assertPnException(PubNubError.PARSING_ERROR, it) + success.set(true) + } + } + success.listen() + } + + @Test + fun testIsAuthRequiredSuccess() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("""["ch1", "ch2", "ch3"]""")), + ) + + config.authKey = "myKey" + + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.APNS, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testOperationTypeSuccess() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("""["ch1", "ch2", "ch3"]""")), + ) + + val success = AtomicBoolean() + + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.APNS, + ).async { result -> + assertFalse(result.isFailure) + result.onSuccess { +// assertTrue(status.affectedChannels.isEmpty()) //TODO should we have this +// assertTrue(status.affectedChannelGroups.isEmpty()) + success.set(true) + } + } + + success.listen() + } + + @Test + fun testNullSubscribeKey() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("""["ch1", "ch2", "ch3"]""")), + ) + + config.subscribeKey = " " + + try { + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.APNS, + ).sync() + failTest("Didn't throw SUBSCRIBE_KEY_MISSING") + } catch (e: Exception) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testValidationNoTopic() { + try { + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.APNS2, + ).sync() + failTest("Should throw no topic") + } catch (e: PubNubException) { + assertPnException(PubNubError.PUSH_TOPIC_MISSING, e) + } + } + + @Test + fun testValidationDefaultEnvironment() { + stubFor( + get(urlPathEqualTo("/v2/push/sub-key/mySubscribeKey/devices-apns2/niceDevice")) + .willReturn(aResponse().withBody("""["ch1", "ch2", "ch3"]""")), + ) + + pubnub.auditPushChannelProvisions( + deviceId = "niceDevice", + pushType = PNPushType.APNS2, + topic = UUID.randomUUID().toString(), + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("development", requests[0].queryParameter("environment").firstValue()) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/PushPayloadHelperHelperTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/PushPayloadHelperHelperTest.kt new file mode 100644 index 000000000..f08f83642 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/PushPayloadHelperHelperTest.kt @@ -0,0 +1,723 @@ +package com.pubnub.api.legacy.endpoints.push + +import com.google.gson.GsonBuilder +import com.pubnub.api.enums.PNPushEnvironment +import com.pubnub.api.legacy.BaseTest +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.APNSPayload +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.APNSPayload.APNS2Configuration +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.APNSPayload.APS +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.FCMPayload +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.FCMPayloadV2 +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.FCMPayloadV2.AndroidConfig.AndroidFcmOptions +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.FCMPayloadV2.AndroidConfig.AndroidMessagePriority +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.FCMPayloadV2.AndroidConfig.AndroidNotification.LightSettings +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.FCMPayloadV2.AndroidConfig.AndroidNotification.NotificationPriority +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.FCMPayloadV2.AndroidConfig.AndroidNotification.Visibility +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.FCMPayloadV2.ApnsConfig +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.FCMPayloadV2.ApnsConfig.ApnsFcmOptions +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.FCMPayloadV2.FcmOptions +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.FCMPayloadV2.WebpushConfig +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.FCMPayloadV2.WebpushConfig.WebpushFcmOptions +import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper.MPNSPayload +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test + +class PushPayloadHelperHelperTest : BaseTest() { + @Test + fun testPayloads_Missing() { + val pushPayloadHelper = PushPayloadHelper() + val map = pushPayloadHelper.build() + assertTrue(map.isEmpty()) + } + + @Test + fun testPayloads_Null() { + val pushPayloadHelper = PushPayloadHelper() + pushPayloadHelper.apnsPayload = null + pushPayloadHelper.commonPayload = null + pushPayloadHelper.fcmPayload = null + pushPayloadHelper.fcmPayloadV2 = null + pushPayloadHelper.mpnsPayload = null + val map = pushPayloadHelper.build() + assertTrue(map.isEmpty()) + } + + @Test + fun testPayloads_Empty() { + val pushPayloadHelper = PushPayloadHelper() + pushPayloadHelper.apnsPayload = APNSPayload() + pushPayloadHelper.commonPayload = HashMap() + pushPayloadHelper.fcmPayload = FCMPayload() + pushPayloadHelper.fcmPayloadV2 = FCMPayloadV2() + pushPayloadHelper.mpnsPayload = MPNSPayload() + val map = pushPayloadHelper.build() + assertTrue(map.isEmpty()) + } + + @Test + fun testApple_Empty() { + val pushPayloadHelper = PushPayloadHelper() + val apnsPayload = APNSPayload() + apnsPayload.aps = APS() + apnsPayload.apns2Configurations = ArrayList() + val map = pushPayloadHelper.build() + assertTrue(map.isEmpty()) + } + + @Test + fun testApple_Valid() { + val pushPayloadHelper = PushPayloadHelper() + + val apnsPayload = APNSPayload() + + val aps = APS() + aps.alert = "alert" + aps.badge = 5 + + apnsPayload.aps = aps + apnsPayload.apns2Configurations = emptyList() + apnsPayload.custom = + hashMapOf( + "key_1" to "1", + "key_2" to 2, + ) + pushPayloadHelper.apnsPayload = apnsPayload + + val map = pushPayloadHelper.build() + + val pnApnsDataMap = map["pn_apns"] as Map<*, *> + assertEquals("1", pnApnsDataMap["key_1"]) + assertEquals(2, pnApnsDataMap["key_2"]) + + val apsMap = pnApnsDataMap["aps"] as Map<*, *> + assertEquals("alert", apsMap["alert"]) + assertEquals(5, apsMap["badge"]) + + val pushList = pnApnsDataMap["pn_push"] as List<*> + assertEquals(0, pushList.size) + } + + @Test + fun testApple_Aps() { + val pushPayloadHelper = PushPayloadHelper() + val apnsPayload = APNSPayload() + + apnsPayload.aps = + APS().apply { + alert = "alert" + badge = 5 + } + pushPayloadHelper.apnsPayload = apnsPayload + + apnsPayload.custom = mapOf("key_2" to "2") + + val map = pushPayloadHelper.build() + + val pnApnsDataMap = map["pn_apns"] as Map<*, *> + val apsMap = pnApnsDataMap["aps"] as Map<*, *> + + assertEquals("alert", apsMap["alert"]) + assertEquals(5, apsMap["badge"]) + assertFalse(apsMap.containsKey("sound")) + assertFalse(pnApnsDataMap.containsKey("pn_push")) + assertFalse(pnApnsDataMap.containsKey("key_1")) + assertEquals("2", pnApnsDataMap["key_2"]) + } + + @Suppress("UNCHECKED_CAST") + @Test + fun testApple_PnPushArray() { + val pushPayloadHelper = PushPayloadHelper() + + val target1 = + APNS2Configuration.Target().apply { + environment = PNPushEnvironment.DEVELOPMENT + topic = "topic_1" + } + val target2 = + APNS2Configuration.Target().apply { + environment = PNPushEnvironment.PRODUCTION + } + val target3 = + APNS2Configuration.Target().apply { + environment = PNPushEnvironment.PRODUCTION + topic = "topic_3" + excludeDevices = listOf("ex_1", "ex_2") + } + val target4 = + APNS2Configuration.Target().apply { + environment = null + topic = null + excludeDevices = null + } + val target5 = + APNS2Configuration.Target().apply { + environment = PNPushEnvironment.PRODUCTION + topic = "topic_5" + excludeDevices = listOf() + } + val target6 = + APNS2Configuration.Target().apply { + topic = "topic_6" + excludeDevices = null + } + val target7 = APNS2Configuration.Target() + + val apns2Config1 = + APNS2Configuration().apply { + collapseId = "collapse_1" + expiration = "exp_1" + version = "v1" + targets = null + authMethod = APNS2Configuration.APNS2AuthMethod.TOKEN + } + val apns2Config2 = + APNS2Configuration().apply { + collapseId = "collapse_2" + expiration = "exp_2" + version = "v2" + targets = emptyList() + } + val apns2Config3 = + APNS2Configuration().apply { + collapseId = null + expiration = "" + version = "v3" + targets = listOf(target1, target2, target3, target4, target5, target6, target7) + } + val apns2Config4 = + APNS2Configuration().apply { + collapseId = null + expiration = null + version = null + } + val apns2Config5 = APNS2Configuration() + + pushPayloadHelper.apnsPayload = + APNSPayload().apply { + apns2Configurations = + listOf( + apns2Config1, apns2Config2, apns2Config3, apns2Config4, apns2Config5, + ) + } + + val map = pushPayloadHelper.build() + + val apnsMap = map["pn_apns"] as Map<*, *> + val pnPushList = apnsMap["pn_push"] as List> + + assertEquals(3, pnPushList.size) + + assertEquals("exp_1", pnPushList[0]["expiration"]) + assertEquals("collapse_1", pnPushList[0]["collapse_id"]) + assertEquals("v1", pnPushList[0]["version"]) + assertEquals("token", pnPushList[0]["auth_method"]) + assertFalse(pnPushList[0].containsKey("targets")) + + assertEquals("exp_2", pnPushList[1]["expiration"]) + assertEquals("collapse_2", pnPushList[1]["collapse_id"]) + assertEquals("v2", pnPushList[1]["version"]) + assertFalse(pnPushList[1].containsKey("targets")) + + assertEquals("", pnPushList[2]["expiration"]) + assertFalse(pnPushList[2].containsKey("collapse_id")) + assertEquals("v3", pnPushList[2]["version"]) + assertTrue(pnPushList[2].containsKey("targets")) + + val pnTargetsMap = pnPushList[2]["targets"] as List> + + assertEquals("development", pnTargetsMap[0]["environment"]) + assertEquals("topic_1", pnTargetsMap[0]["topic"]) + assertFalse(pnTargetsMap[0].containsKey("excludeDevices")) + + assertEquals("production", pnTargetsMap[1]["environment"]) + assertFalse(pnTargetsMap[1].containsKey("topic_2")) + assertFalse(pnTargetsMap[1].containsKey("excludeDevices")) + + assertEquals("production", pnTargetsMap[2]["environment"]) + assertEquals("topic_3", pnTargetsMap[2]["topic"]) + assertEquals("ex_1", (pnTargetsMap[2]["excluded_devices"] as List<*>?)!![0]) + assertEquals("ex_2", (pnTargetsMap[2]["excluded_devices"] as List<*>?)!![1]) + + assertEquals("production", pnTargetsMap[3]["environment"]) + assertFalse(pnTargetsMap[3].containsKey("topic_5")) + assertFalse(pnTargetsMap[3].containsKey("excludeDevices")) + + assertFalse(pnTargetsMap[4].containsKey("environment")) + assertEquals("topic_6", pnTargetsMap[4]["topic"]) + assertFalse(pnTargetsMap[4].containsKey("environment")) + } + + @Test + fun testApple_Aps_Empty() { + val pushPayloadHelper = PushPayloadHelper() + + val apnsPayload = APNSPayload() + apnsPayload.aps = APS() + pushPayloadHelper.apnsPayload = apnsPayload + + val map = pushPayloadHelper.build() + assertTrue(map.isEmpty()) + } + + @Test + fun testCommonPayload_Valid() { + val map = + PushPayloadHelper().apply { + commonPayload = + mapOf( + "common_key_1" to 1, + "common_key_2" to "2", + "common_key_3" to true, + ) + }.build() + + assertEquals(map["common_key_1"], 1) + assertEquals(map["common_key_2"], "2") + assertEquals(map["common_key_3"], true) + } + + @Test + fun testCommonPayload_Invalid() { + val pushPayloadHelper = PushPayloadHelper() + + pushPayloadHelper.commonPayload = mapOf() + + val map = pushPayloadHelper.build() + assertTrue(map.isEmpty()) + } + + @Test + fun testGoogle_Valid_legacy1() { + val pushPayloadHelper = PushPayloadHelper() + + pushPayloadHelper.fcmPayload = + FCMPayload().apply { + notification = + FCMPayload.Notification().apply { + body = "Notification body" + image = null + title = "" + } + custom = mapOf("a" to "a", "b" to 1) + data = mapOf("data_1" to "a", "data_2" to 1) + } + + val map = pushPayloadHelper.build() + + val pnFcmMap = map["pn_gcm"] as Map<*, *> + assertNotNull(pnFcmMap) + + val pnFcmDataMap = pnFcmMap["data"] as Map<*, *> + val pnFcmNotificationsMap = pnFcmMap["notification"] as Map<*, *> + + assertNotNull(pnFcmDataMap) + assertNotNull(pnFcmNotificationsMap) + + assertEquals(pnFcmMap["a"], "a") + assertEquals(pnFcmMap["b"], 1) + assertEquals(pnFcmDataMap["data_1"], "a") + assertEquals(pnFcmDataMap["data_2"], 1) + assertEquals(pnFcmNotificationsMap["body"], "Notification body") + assertEquals(pnFcmNotificationsMap["image"], null) + assertEquals(pnFcmNotificationsMap["title"], "") + } + + @Test + fun testGoogle_Valid_1() { + val pushPayloadHelper = PushPayloadHelper() + + pushPayloadHelper.fcmPayloadV2 = + FCMPayloadV2().apply { + data = mapOf("data_1" to "aa") + notification = + FCMPayloadV2.Notification().apply { + body = "Notification body" + image = "image" + title = "" + } + android = FCMPayloadV2.AndroidConfig().apply { + collapseKey = "collapseKey" + priority = AndroidMessagePriority.HIGH + ttl = "ttl" + restrictedPackageName = "restricted" + data = mapOf("android_data_1" to "a") + notification = FCMPayloadV2.AndroidConfig.AndroidNotification().apply { + title = "title" + body = "body" + icon = "icon" + color = "color" + sound = "sound" + tag = "tag" + clickAction = "clickAction_1" + bodyLocKey = "bodyLocKey" + bodyLocArgs = listOf("a", "b") + titleLocKey = "titleLocKey" + titleLocArgs = listOf("b", "c") + channelId = "channelId" + ticker = "ticker" + sticky = true + eventTime = "eventTime" + localOnly = false + notificationPriority = NotificationPriority.PRIORITY_LOW + defaultSound = true + defaultVibrateTimings = true + defaultLightSettings = true + vibrateTimings = listOf("d", "e") + visibility = Visibility.SECRET + notificationCount = 4 + lightSettings = LightSettings().apply { + this.color = LightSettings.Color(1f, 1f, 1f, 1f) + this.lightOnDuration = "4" + this.lightOffDuration = "5" + } + image = "image" + } + fcmOptions = AndroidFcmOptions().apply { + this.analyticsLabel = "analytics_1" + } + directBootOk = true + } + webpush = WebpushConfig().apply { + headers = mapOf("header_1" to "aaa") + data = mapOf("web_data_1" to "") + notification = mapOf("notification" to "aaa") + fcmOptions = WebpushFcmOptions().apply { + link = "link" + analyticsLabel = "analytics_2" + } + } + apns = ApnsConfig().apply { + headers = mapOf("header_2" to "bbb") + payload = mapOf("payload_2" to "ccc") + fcmOptions = ApnsFcmOptions().apply { + analyticsLabel = "analytics_3" + image = "image" + } + } + fcmOptions = FcmOptions().apply { + analyticsLabel = "analytics_4" + } + } + + val map = pushPayloadHelper.build() + val gson = GsonBuilder().setPrettyPrinting().create() + val json = gson.toJson(map) + + val expected = """ + { + "pn_fcm": { + "data": { + "data_1": "aa" + }, + "notification": { + "title": "", + "body": "Notification body", + "image": "image" + }, + "android": { + "collapse_key": "collapseKey", + "priority": "HIGH", + "ttl": "ttl", + "restricted_package_name": "restricted", + "data": { + "android_data_1": "a" + }, + "notification": { + "title": "title", + "body": "body", + "icon": "icon", + "color": "color", + "sound": "sound", + "tag": "tag", + "click_action": "clickAction_1", + "body_loc_key": "bodyLocKey", + "body_loc_args": [ + "a", + "b" + ], + "title_loc_key": "titleLocKey", + "title_loc_args": [ + "b", + "c" + ], + "channel_id": "channelId", + "ticker": "ticker", + "sticky": true, + "event_time": "eventTime", + "local_only": false, + "notification_priority": "PRIORITY_LOW", + "default_sound": true, + "default_vibrate_timings": true, + "default_light_settings": true, + "vibrate_timings": [ + "d", + "e" + ], + "visibility": "SECRET", + "notification_count": 4, + "light_settings": { + "color": { + "red": 1.0, + "green": 1.0, + "blue": 1.0, + "alpha": 1.0 + }, + "light_on_duration": "4", + "light_off_duration": "5" + }, + "image": "image" + }, + "fcm_options": { + "analytics_label": "analytics_1" + }, + "direct_boot_ok": true + }, + "webpush": { + "headers": { + "header_1": "aaa" + }, + "data": { + "web_data_1": "" + }, + "notification": { + "notification": "aaa" + }, + "fcm_options": { + "link": "link", + "analytics_label": "analytics_2" + } + }, + "apns": { + "headers": { + "header_2": "bbb" + }, + "payload": { + "payload_2": "ccc" + }, + "fcm_options": { + "analytics_label": "analytics_3", + "image": "image" + } + }, + "fcm_options": { + "analytics_label": "analytics_4" + } + } + } + """.trimIndent() + + assertEquals(expected, json) + } + + @Test + fun testGoogle_Empty() { + val pushPayloadHelper = PushPayloadHelper() + val fcmPayload = FCMPayloadV2() + pushPayloadHelper.fcmPayloadV2 = fcmPayload + val map = pushPayloadHelper.build() + val pnFcmMap = map["pn_fcm"] + assertNull(pnFcmMap) + } + + @Test + fun testGoogle_EmptyNotification() { + val pushPayloadHelper = PushPayloadHelper() + val notification = FCMPayloadV2.Notification() + val customMap = HashMap() + customMap["key_1"] = "1" + customMap["key_2"] = "2" + val fcmPayload = FCMPayloadV2() + fcmPayload.notification = notification + fcmPayload.data = customMap + pushPayloadHelper.fcmPayloadV2 = fcmPayload + val map = pushPayloadHelper.build() + val pnFcmMap = map["pn_fcm"] as Map<*, *> + val pnFcmNotificationMap = pnFcmMap["notification"] + assertNull(pnFcmNotificationMap) + } + + @Test + fun testGoogle_EmptyData() { + val pushPayloadHelper = PushPayloadHelper() + val fcmPayload = FCMPayloadV2() + val dataMap = HashMap() + fcmPayload.data = dataMap + pushPayloadHelper.fcmPayloadV2 = fcmPayload + val map = pushPayloadHelper.build() + val pnFcmMap = map["pn_fcm"] + assertNull(pnFcmMap) + } + + @Test + fun testGoogle_Valid_2() { + val pushPayloadHelper = PushPayloadHelper() + val fcmPayload = + FCMPayloadV2().apply { + data = + mapOf( + "key_1" to "value_1", + "key_2" to "2", + "key_3" to "true", + "key_4" to "", + ) + } + pushPayloadHelper.fcmPayloadV2 = fcmPayload + val map = pushPayloadHelper.build() + + val pnFcmMap = map["pn_fcm"] as Map<*, *> + val pnFcmDataMap = pnFcmMap["data"] as Map<*, *> + assertNotNull(pnFcmDataMap) + assertFalse(pnFcmDataMap.isEmpty()) + assertTrue(pnFcmDataMap.containsKey("key_1")) + assertTrue(pnFcmDataMap.containsKey("key_2")) + assertTrue(pnFcmDataMap.containsKey("key_3")) + assertTrue(pnFcmDataMap.containsKey("key_4")) + assertNotNull(pnFcmDataMap["key_1"]) + assertNotNull(pnFcmDataMap["key_2"]) + assertNotNull(pnFcmDataMap["key_3"]) + assertNotNull(pnFcmDataMap["key_4"]) + } + + @Test + fun testGoogle_Custom() { + val pushPayloadHelper = PushPayloadHelper() + pushPayloadHelper.fcmPayloadV2 = + FCMPayloadV2().apply { + data = + mapOf( + "key_1" to "value_1", + "key_2" to "2", + "key_3" to "true", + "key_4" to "", + ) + } + val map = pushPayloadHelper.build() + + val pnFcmMap = (map["pn_fcm"] as Map<*, *>)["data"] as Map + assertNotNull(pnFcmMap) + assertFalse(pnFcmMap.isEmpty()) + assertTrue(pnFcmMap.containsKey("key_1")) + assertTrue(pnFcmMap.containsKey("key_2")) + assertTrue(pnFcmMap.containsKey("key_3")) + assertTrue(pnFcmMap.containsKey("key_4")) + assertNotNull(pnFcmMap["key_1"]) + assertNotNull(pnFcmMap["key_2"]) + assertNotNull(pnFcmMap["key_3"]) + assertNotNull(pnFcmMap["key_4"]) + } + + @Test + fun testMicrosoft_Missing() { + val pushPayloadHelper = PushPayloadHelper() + + val mpnsPayload = + MPNSPayload().apply { + backContent = "Back Content" + backTitle = "Back Title" + count = 1 + title = "Title" + type = "Type" + custom = + mapOf( + "a" to "a", + "b" to 1, + "c" to "", + ) + } + pushPayloadHelper.mpnsPayload = mpnsPayload + val map = pushPayloadHelper.build() + + val pnMpnsMap = map["pn_mpns"] as HashMap<*, *> + assertNotNull(pnMpnsMap) + assertEquals(pnMpnsMap["back_content"], "Back Content") + assertEquals(pnMpnsMap["back_title"], "Back Title") + assertEquals(pnMpnsMap["count"], 1) + assertEquals(pnMpnsMap["title"], "Title") + assertEquals(pnMpnsMap["type"], "Type") + assertEquals(pnMpnsMap["a"], "a") + assertEquals(pnMpnsMap["b"], 1) + assertEquals(pnMpnsMap["c"], "") + } + + @Test + fun testMicrosoft_Valid() { + val pushPayloadHelper = PushPayloadHelper() + pushPayloadHelper.mpnsPayload = + MPNSPayload().apply { + backContent = "Back Content" + backTitle = "Back Title" + count = 1 + title = "Title" + type = "Type" + custom = + mapOf( + "a" to "a", + "b" to 1, + "c" to "", + ) + } + + val map = pushPayloadHelper.build() + val pnMpnsMap = map["pn_mpns"] as Map<*, *> + + assertNotNull(pnMpnsMap) + assertEquals(pnMpnsMap["back_content"], "Back Content") + assertEquals(pnMpnsMap["back_title"], "Back Title") + assertEquals(pnMpnsMap["count"], 1) + assertEquals(pnMpnsMap["title"], "Title") + assertEquals(pnMpnsMap["type"], "Type") + assertEquals(pnMpnsMap["a"], "a") + assertEquals(pnMpnsMap["b"], 1) + assertEquals(pnMpnsMap["c"], "") + assertEquals(pnMpnsMap["d"], null) + } + + @Test + fun testMicrosoft_Empty() { + val pushPayloadHelper = PushPayloadHelper() + val mpnsPayload = MPNSPayload() + pushPayloadHelper.mpnsPayload = mpnsPayload + val map = pushPayloadHelper.build() + val pnMpnsMap = map["pn_mpns"] + assertNull(pnMpnsMap) + } + + @Test + fun testMicrosoft_Custom() { + val pushPayloadHelper = PushPayloadHelper() + pushPayloadHelper.mpnsPayload = + MPNSPayload().apply { + backContent = "" + backTitle = "Back Title" + count = 1 + title = null + type = "Type" + custom = + mapOf( + "a" to "a", + "b" to 1, + "c" to "", + ) + } + + val map = pushPayloadHelper.build() + + val pnMpnsMap = map["pn_mpns"] as Map<*, *> + assertNotNull(pnMpnsMap) + assertEquals("", pnMpnsMap["back_content"]) + assertEquals("Back Title", pnMpnsMap["back_title"]) + assertEquals(1, pnMpnsMap["count"]) + assertFalse(pnMpnsMap.containsKey("title")) + assertEquals("Type", pnMpnsMap["type"]) + assertEquals("a", pnMpnsMap["a"]) + assertEquals(1, pnMpnsMap["b"]) + assertEquals("", pnMpnsMap["c"]) + assertFalse(pnMpnsMap.containsKey("d")) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/RemoveAllPushChannelsForDeviceTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/RemoveAllPushChannelsForDeviceTest.kt new file mode 100644 index 000000000..e0d562f6f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/RemoveAllPushChannelsForDeviceTest.kt @@ -0,0 +1,168 @@ +package com.pubnub.api.legacy.endpoints.push + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.failTest +import com.pubnub.test.listen +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.concurrent.atomic.AtomicBoolean + +class RemoveAllPushChannelsForDeviceTest : BaseTest() { + @Test + fun testAppleSuccessSyncRemoveAll() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + pubnub.removeAllPushNotificationsFromDeviceWithPushToken( + deviceId = "niceDevice", + pushType = PNPushType.APNS, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("apns", requests[0].queryParameter("type").firstValue()) + assertFalse(requests[0].queryParameter("environment").isPresent) + assertFalse(requests[0].queryParameter("topic").isPresent) + } + + @Test + fun testFirebaseSuccessSyncRemoveAll() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + pubnub.removeAllPushNotificationsFromDeviceWithPushToken( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("gcm", requests[0].queryParameter("type").firstValue()) + assertFalse(requests[0].queryParameter("environment").isPresent) + assertFalse(requests[0].queryParameter("topic").isPresent) + } + + @Test + fun testMicrosoftSuccessSyncRemoveAll() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + pubnub.removeAllPushNotificationsFromDeviceWithPushToken( + deviceId = "niceDevice", + pushType = PNPushType.MPNS, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("mpns", requests[0].queryParameter("type").firstValue()) + assertFalse(requests[0].queryParameter("environment").isPresent) + assertFalse(requests[0].queryParameter("topic").isPresent) + } + + @Test + fun testApns2SuccessSyncRemoveAll() { + stubFor( + get(urlPathEqualTo("/v2/push/sub-key/mySubscribeKey/devices-apns2/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + pubnub.removeAllPushNotificationsFromDeviceWithPushToken( + deviceId = "niceDevice", + pushType = PNPushType.APNS2, + topic = "news", + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals( + "development", + requests[0].queryParameter("environment").firstValue(), + ) + assertEquals("news", requests[0].queryParameter("topic").firstValue()) + assertFalse(requests[0].queryParameter("type").isPresent) + } + + @Test + fun testIsAuthRequiredSuccessRemoveAll() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + config.authKey = "myKey" + + pubnub.removeAllPushNotificationsFromDeviceWithPushToken( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + ).sync() + + val requests = findAll(getRequestedFor(urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testOperationTypeSuccessRemoveAll() { + stubFor( + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + val success = AtomicBoolean() + + pubnub.removeAllPushNotificationsFromDeviceWithPushToken( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + ).async { result -> + assertFalse(result.isFailure) + success.set(true) + } + + success.listen() + } + + @Test + fun testEmptySubscribeKeyRemoveAll() { + config.subscribeKey = " " + + try { + pubnub.removeAllPushNotificationsFromDeviceWithPushToken( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + ).sync() + failTest() + } catch (e: PubNubException) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testEmptyDeviceIdRemoveAll() { + try { + pubnub.removeAllPushNotificationsFromDeviceWithPushToken( + pushType = PNPushType.FCM, + deviceId = " ", + ).sync() + } catch (e: PubNubException) { + assertPnException(PubNubError.DEVICE_ID_MISSING, e) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/RemoveChannelsFromPushTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/RemoveChannelsFromPushTest.kt new file mode 100644 index 000000000..a2a09663c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/push/RemoveChannelsFromPushTest.kt @@ -0,0 +1,192 @@ +package com.pubnub.api.legacy.endpoints.push + +import com.github.tomakehurst.wiremock.client.WireMock +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.legacy.BaseTest +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.failTest +import com.pubnub.test.listen +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import java.util.concurrent.atomic.AtomicBoolean + +class RemoveChannelsFromPushTest : BaseTest() { + @Test + fun testRemoveAppleSuccessSync() { + WireMock.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(WireMock.aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + pubnub.removePushNotificationsFromChannels( + deviceId = "niceDevice", + pushType = PNPushType.APNS, + channels = listOf("chr1", "chr2", "chr3"), + ).sync() + + val requests = WireMock.findAll(WireMock.getRequestedFor(WireMock.urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("apns", requests[0].queryParameter("type").firstValue()) + assertEquals("chr1,chr2,chr3", requests[0].queryParameter("remove").firstValue()) + assertFalse(requests[0].queryParameter("environment").isPresent) + assertFalse(requests[0].queryParameter("topic").isPresent) + } + + @Test + fun testRemoveFirebaseSuccessSync() { + WireMock.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(WireMock.aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + pubnub.removePushNotificationsFromChannels( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + channels = listOf("chr1", "chr2", "chr3"), + ).sync() + + val requests = WireMock.findAll(WireMock.getRequestedFor(WireMock.urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("gcm", requests[0].queryParameter("type").firstValue()) + assertEquals("chr1,chr2,chr3", requests[0].queryParameter("remove").firstValue()) + assertFalse(requests[0].queryParameter("environment").isPresent) + assertFalse(requests[0].queryParameter("topic").isPresent) + } + + @Test + fun testRemoveMicrosoftSuccessSync() { + WireMock.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(WireMock.aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + pubnub.removePushNotificationsFromChannels( + deviceId = "niceDevice", + pushType = PNPushType.MPNS, + channels = listOf("chr1", "chr2", "chr3"), + ).sync() + + val requests = WireMock.findAll(WireMock.getRequestedFor(WireMock.urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("mpns", requests[0].queryParameter("type").firstValue()) + assertEquals("chr1,chr2,chr3", requests[0].queryParameter("remove").firstValue()) + assertFalse(requests[0].queryParameter("environment").isPresent) + assertFalse(requests[0].queryParameter("topic").isPresent) + } + + @Test + fun testRemoveApns2SuccessSync() { + WireMock.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/v2/push/sub-key/mySubscribeKey/devices-apns2/niceDevice")) + .willReturn(WireMock.aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + pubnub.removePushNotificationsFromChannels( + deviceId = "niceDevice", + pushType = PNPushType.APNS2, + channels = listOf("chr1", "chr2", "chr3"), + topic = "news", + ).sync() + + val requests = WireMock.findAll(WireMock.getRequestedFor(WireMock.urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("chr1,chr2,chr3", requests[0].queryParameter("remove").firstValue()) + assertEquals( + "development", + requests[0].queryParameter("environment").firstValue(), + ) + assertEquals("news", requests[0].queryParameter("topic").firstValue()) + assertFalse(requests[0].queryParameter("type").isPresent) + } + + @Test + fun testIsAuthRequiredSuccessRemove() { + WireMock.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(WireMock.aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + config.authKey = "myKey" + + pubnub.removePushNotificationsFromChannels( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + channels = listOf("chr1", "chr2", "chr3"), + topic = "news", + ).sync() + + val requests = WireMock.findAll(WireMock.getRequestedFor(WireMock.urlMatching("/.*"))) + assertEquals(1, requests.size) + assertEquals("myKey", requests[0].queryParameter("auth").firstValue()) + } + + @Test + fun testOperationTypeSuccessRemove() { + WireMock.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(WireMock.aResponse().withBody("[1, \"Modified Channels\"]")), + ) + + val success = AtomicBoolean() + + pubnub.removePushNotificationsFromChannels( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + channels = listOf("chr1", "chr2", "chr3"), + ).async { result -> + assertFalse(result.isFailure) +// assertEquals(status.affectedChannels, listOf("chr1", "chr2", "chr3")) //TODO check if we can have this in exception +// assertTrue(status.affectedChannelGroups.isEmpty()) + success.set(true) + } + + success.listen() + } + + @Test + fun testEmptySubscribeKeyRemove() { + config.subscribeKey = " " + + try { + pubnub.removePushNotificationsFromChannels( + deviceId = "niceDevice", + pushType = PNPushType.FCM, + channels = listOf("chr1", "chr2", "chr3"), + ).sync() + failTest() + } catch (e: PubNubException) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + + @Test + fun testEmptyDeviceId() { + try { + pubnub.removePushNotificationsFromChannels( + pushType = PNPushType.FCM, + deviceId = " ", + channels = listOf("chr1", "chr2", "chr3"), + ).sync() + failTest() + } catch (e: PubNubException) { + assertPnException(PubNubError.DEVICE_ID_MISSING, e) + } + } + + @Test + fun testEmptyChannels() { + try { + pubnub.removePushNotificationsFromChannels( + pushType = PNPushType.FCM, + deviceId = "niceDevice", + channels = emptyList(), + ).sync() + failTest() + } catch (e: PubNubException) { + assertPnException(PubNubError.CHANNEL_MISSING, e) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/CancellableRemoteAction.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/CancellableRemoteAction.kt new file mode 100644 index 000000000..d3735aa3c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/CancellableRemoteAction.kt @@ -0,0 +1,26 @@ +package com.pubnub.api.legacy.endpoints.remoteaction + +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction +import com.pubnub.api.v2.callbacks.Result +import java.util.concurrent.Executors +import java.util.function.Consumer + +internal interface CancellableRemoteAction : ExtendedRemoteAction { + override fun sync(): T { + throw PubNubException("Cancelled") + } + + override fun retry() {} + + fun doAsync(callback: (result: Result) -> Unit) + + override fun async(callback: Consumer>) { + Executors.newSingleThreadExecutor() + .execute { + doAsync { + callback.accept(it) + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/ComposableRemoteActionTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/ComposableRemoteActionTest.kt new file mode 100644 index 000000000..dad4fd84d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/ComposableRemoteActionTest.kt @@ -0,0 +1,179 @@ +package com.pubnub.api.legacy.endpoints.remoteaction + +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.remoteaction.ComposableRemoteAction.Companion.firstDo +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.v2.callbacks.Result +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers +import org.junit.Assert +import org.junit.Test +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger + +class ComposableRemoteActionTest { + @Test + @Throws(PubNubException::class) + fun sync_happyPath() { + // given + val composedAction: RemoteAction = + firstDo(TestRemoteAction.successful(668)) + .then { TestRemoteAction.successful(it * 2) } + .then { TestRemoteAction.successful(it + 1) } + + // when + val result = composedAction.sync() + + // then + Assert.assertEquals(1337, result.toLong()) + } + + @Test + @Throws(InterruptedException::class) + fun async_happyPath() { + // given + val latch = CountDownLatch(1) + val res = AtomicInteger(0) + val composedAction: RemoteAction = + firstDo(TestRemoteAction.successful(668)) + .then { TestRemoteAction.successful(it * 2) } + .then { TestRemoteAction.successful(it + 1) } + + // when + composedAction.async { result -> + result.onSuccess { + res.set(it) + latch.countDown() + } + } + + // then + Assert.assertTrue(latch.await(1, TimeUnit.SECONDS)) + Assert.assertEquals(1337, res.get().toLong()) + } + + @Test(expected = PubNubException::class) + @Throws(PubNubException::class) + fun sync_whenFirstFails_RestIsNotCalled() { + firstDo(TestRemoteAction.failing()) + .then { + Assert.fail("fail") + TestRemoteAction.successful(15) + }.sync() + } + + @Test + @Throws(InterruptedException::class) + fun async_whenFirstFails_RestIsNotCalled() { + // given + val latch = CountDownLatch(1) + val successful: TestRemoteAction = TestRemoteAction.successful(15) + firstDo(TestRemoteAction.failing()) + .then { successful } // when + .async { _ -> latch.countDown() } + + // then + Assert.assertTrue(latch.await(1, TimeUnit.SECONDS)) + assertThat(successful.howManyTimesAsyncCalled(), Matchers.`is`(0)) + } + + @Test + @Throws(InterruptedException::class) + fun cancel_cancelsCurrentlyRunningTask_RestIsNotCalled() { + // given + val cancelSynchronisingLatch = CountDownLatch(1) + val resultSynchronisingLatch = CountDownLatch(1) + val firstAsyncFinished = AtomicBoolean(false) + val longRunningTask: CancellableRemoteAction = + object : CancellableRemoteAction { + @Throws(InterruptedException::class) + override fun doAsync(callback: (result: Result) -> Unit) { + cancelSynchronisingLatch.await() + println("async") + callback(Result.success(null)) + firstAsyncFinished.set(true) + resultSynchronisingLatch.countDown() + } + + override fun silentCancel() { + println("silentCancel") + cancelSynchronisingLatch.countDown() + } + + override fun operationType(): PNOperationType { + return PNOperationType.FileOperation + } + } + val successful: TestRemoteAction = TestRemoteAction.successful(15) + val composedAction: RemoteAction = firstDo(longRunningTask).then { successful } + composedAction.async { _ -> } + + // when + composedAction.silentCancel() + + // then + Assert.assertTrue(resultSynchronisingLatch.await(1, TimeUnit.SECONDS)) + Assert.assertTrue(firstAsyncFinished.get()) + assertThat(successful.howManyTimesAsyncCalled(), Matchers.`is`(0)) + } + + @Test + @Throws(InterruptedException::class) + fun retry_withoutCheckpointStartsFromBeginning() { + // given + val countDownLatch = CountDownLatch(2) + val firstSuccessful: TestRemoteAction = TestRemoteAction.successful(1) + val secondSuccessful: TestRemoteAction = TestRemoteAction.successful(1) + val firstFailing: TestRemoteAction = TestRemoteAction.failingFirstCall(1) + val composedAction: RemoteAction = + firstDo(firstSuccessful) + .then { secondSuccessful } + .then { firstFailing } + + // when + composedAction.async { result -> + countDownLatch.countDown() + result.onFailure { + it.remoteAction?.retry() + } + } + + // then + Assert.assertTrue(countDownLatch.await(1000, TimeUnit.MILLISECONDS)) + assertThat(firstSuccessful.howManyTimesAsyncCalled(), Matchers.`is`(2)) + assertThat(secondSuccessful.howManyTimesAsyncCalled(), Matchers.`is`(2)) + assertThat(firstFailing.howManyTimesAsyncCalled(), Matchers.`is`(2)) + } + + @Test + @Throws(InterruptedException::class) + fun retry_startsFromCheckpoint() { + // given + val countDownLatch = CountDownLatch(2) + val firstSuccessful: TestRemoteAction = TestRemoteAction.successful(1) + val secondSuccessful: TestRemoteAction = TestRemoteAction.successful(1) + val firstFailing: TestRemoteAction = TestRemoteAction.failingFirstCall(1) + val composedAction: RemoteAction = + firstDo(firstSuccessful) + .checkpoint() + .then { secondSuccessful } + .then { firstFailing } + + // when + composedAction.async { result -> + countDownLatch.countDown() + result.onFailure { + it.remoteAction?.retry() + } + } + + // then + Assert.assertTrue(countDownLatch.await(1000, TimeUnit.MILLISECONDS)) + assertThat(firstSuccessful.howManyTimesAsyncCalled(), Matchers.`is`(1)) + assertThat(secondSuccessful.howManyTimesAsyncCalled(), Matchers.`is`(2)) + assertThat(firstFailing.howManyTimesAsyncCalled(), Matchers.`is`(2)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/RetryingRemoteActionTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/RetryingRemoteActionTest.kt new file mode 100644 index 000000000..154cbcabf --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/RetryingRemoteActionTest.kt @@ -0,0 +1,196 @@ +package com.pubnub.api.legacy.endpoints.remoteaction + +import com.pubnub.api.PubNubException +import com.pubnub.internal.endpoints.remoteaction.RetryingRemoteAction +import io.mockk.spyk +import io.mockk.verify +import org.junit.Assert +import org.junit.Test +import java.util.concurrent.CountDownLatch +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit + +class RetryingRemoteActionTest { + var expectedValue = 5 + var numberOfRetries = 2 + var timeoutMs = 1000 + var executorService = Executors.newSingleThreadExecutor() + + @Test + @Throws(PubNubException::class) + fun whenSucceedsWrappedActionIsCalledOnce() { + // given + val remoteAction: TestRemoteAction = spyk(TestRemoteAction.successful(expectedValue)) + val retryingRemoteAction = + RetryingRemoteAction.autoRetry( + remoteAction, + numberOfRetries, + executorService, + ) + + // when + val result = retryingRemoteAction.sync() + + // then + Assert.assertEquals(expectedValue, result) + verify(exactly = 1) { remoteAction.sync() } + } + + @Test + @Throws(PubNubException::class) + fun whenFailingOnceWrappedActionIsCalledTwice() { + // given + val remoteAction: TestRemoteAction = spyk(TestRemoteAction.failingFirstCall(expectedValue)) + val retryingRemoteAction = + RetryingRemoteAction.autoRetry( + remoteAction, + numberOfRetries, + executorService, + ) + + // when + val result = retryingRemoteAction.sync() + + // then + Assert.assertEquals(expectedValue, result) + verify(exactly = numberOfRetries) { remoteAction.sync() } + } + + @Test + @Throws(PubNubException::class) + fun whenFailingAlwaysWrappedActionIsCalledTwiceAndThrows() { + // given + val remoteAction: TestRemoteAction = spyk(TestRemoteAction.failing()) + val retryingRemoteAction = + RetryingRemoteAction.autoRetry( + remoteAction, + numberOfRetries, + executorService, + ) + + // when + try { + retryingRemoteAction.sync() + Assert.fail("Exception expected") + } catch (ex: PubNubException) { + // then + verify(exactly = numberOfRetries) { remoteAction.sync() } + } + } + + @Test + @Throws(InterruptedException::class) + fun whenSucceedsWrappedActionIsCalledOnceAndPassesResult() { + // given + val remoteAction: TestRemoteAction = spyk(TestRemoteAction.successful(expectedValue)) + val retryingRemoteAction = + RetryingRemoteAction.autoRetry( + remoteAction, + numberOfRetries, + executorService, + ) + val asyncSynchronization = CountDownLatch(1) + val noFailureCallsSynchronization = CountDownLatch(1) + + // when + retryingRemoteAction.async { result -> + // then + result.onSuccess { + Assert.assertEquals(expectedValue, it) + verify(exactly = 1) { remoteAction.sync() } + asyncSynchronization.countDown() + }.onFailure { + noFailureCallsSynchronization.countDown() + } + } + if (!asyncSynchronization.await(3, TimeUnit.SECONDS)) { + Assert.fail("Callback have not been called") + } + + if (noFailureCallsSynchronization.await(3, TimeUnit.SECONDS)) { + Assert.fail("onFailure has been called when it shouldn't!") + } + } + + @Test + @Throws(InterruptedException::class) + fun whenFailingOnceWrappedActionIsCalledTwiceAndPassesResult() { + // given + val remoteAction: TestRemoteAction = spyk(TestRemoteAction.failingFirstCall(expectedValue)) + val retryingRemoteAction = + RetryingRemoteAction.autoRetry( + remoteAction, + numberOfRetries, + executorService, + ) + val asyncSynchronization = CountDownLatch(1) + + // when + retryingRemoteAction.async { result -> + // then + result.onSuccess { + Assert.assertEquals(expectedValue, it) + verify(exactly = numberOfRetries) { remoteAction.sync() } + asyncSynchronization.countDown() + } + } + if (!asyncSynchronization.await(3, TimeUnit.SECONDS)) { + Assert.fail("Callback have not been called") + } + } + + @Test + @Throws(InterruptedException::class) + fun whenFailingAlwaysWrappedActionIsCalledTwiceAndPassesError() { + // given + val remoteAction: TestRemoteAction = spyk(TestRemoteAction.failing()) + val retryingRemoteAction = + RetryingRemoteAction.autoRetry( + remoteAction, + numberOfRetries, + executorService, + ) + val asyncSynchronization = CountDownLatch(1) + + // when + retryingRemoteAction.async { result -> + // then + Assert.assertTrue(result.isFailure) + verify(exactly = numberOfRetries) { remoteAction.sync() } + asyncSynchronization.countDown() + } + if (!asyncSynchronization.await(3, TimeUnit.SECONDS)) { + Assert.fail("Callback have not been called") + } + } + + @Test + @Throws(InterruptedException::class) + fun whenRetryWrappedActionWillBeCalledTwiceTheUsualTime() { + // given + val remoteAction: TestRemoteAction = spyk(TestRemoteAction.failing()) + val retryingRemoteAction = + RetryingRemoteAction.autoRetry( + remoteAction, + numberOfRetries, + executorService, + ) + val asyncSynchronization = CountDownLatch(2) + + // when + retryingRemoteAction.async { result -> + // then + if (asyncSynchronization.count == 1L) { + Assert.assertTrue(result.isFailure) + verify(exactly = 2 * numberOfRetries) { remoteAction.sync() } + } + asyncSynchronization.countDown() + if (asyncSynchronization.count == 1L) { + result.onFailure { it.remoteAction?.retry() } + } + } + if (!asyncSynchronization.await(3, TimeUnit.SECONDS)) { + Assert.fail("Callback have not been called") + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/TestRemoteAction.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/TestRemoteAction.kt new file mode 100644 index 000000000..676f23546 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/endpoints/remoteaction/TestRemoteAction.kt @@ -0,0 +1,93 @@ +package com.pubnub.api.legacy.endpoints.remoteaction + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.v2.callbacks.Result +import java.util.concurrent.Executor +import java.util.concurrent.Executors +import java.util.concurrent.atomic.AtomicInteger +import java.util.function.Consumer + +class TestRemoteAction internal constructor( + private val output: Output?, + private val failingStrategy: FailingStrategy, +) : ExtendedRemoteAction { + private val executor: Executor = Executors.newSingleThreadExecutor() + private val asyncCallmeter = AtomicInteger(0) + private val callsToFail: AtomicInteger + private lateinit var callback: Consumer> + + @Throws(PubNubException::class) + override fun sync(): Output { + return if (failingStrategy == FailingStrategy.ALWAYS_FAIL) { + throw PubNubException(PubNubError.INTERNAL_ERROR) + } else if (failingStrategy == FailingStrategy.FAIL_FIRST_CALLS && callsToFail.getAndDecrement() > 0) { + throw PubNubException(PubNubError.INTERNAL_ERROR) + } else { + output!! + } + } + + override fun async(callback: Consumer>) { + this.callback = callback + asyncCallmeter.incrementAndGet() + executor.execute { + if (failingStrategy == FailingStrategy.ALWAYS_FAIL) { + callback.accept(Result.failure(PubNubException(remoteAction = this))) + } else if (failingStrategy == FailingStrategy.FAIL_FIRST_CALLS && callsToFail.getAndDecrement() > 0) { + callback.accept(Result.failure(PubNubException(remoteAction = this))) + } else { + callback.accept(Result.success(output!!)) + } + } + } + + override fun retry() { + async(callback) + } + + override fun silentCancel() {} + + fun howManyTimesAsyncCalled(): Int { + return asyncCallmeter.get() + } + + internal enum class FailingStrategy(var numberOfCalls: Int) { + NEVER_FAIL(0), + ALWAYS_FAIL(0), + FAIL_FIRST_CALLS(1), + } + + companion object { + fun failing(): TestRemoteAction { + return TestRemoteAction( + null, + FailingStrategy.ALWAYS_FAIL, + ) + } + + fun failingFirstCall(output: T): TestRemoteAction { + return TestRemoteAction( + output, + FailingStrategy.FAIL_FIRST_CALLS, + ) + } + + fun successful(output: T): TestRemoteAction { + return TestRemoteAction( + output, + FailingStrategy.NEVER_FAIL, + ) + } + } + + init { + callsToFail = AtomicInteger(failingStrategy.numberOfCalls) + } + + override fun operationType(): PNOperationType { + return PNOperationType.FileOperation + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/managers/BasePathManagerTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/managers/BasePathManagerTest.kt new file mode 100644 index 000000000..f9c5c0a88 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/managers/BasePathManagerTest.kt @@ -0,0 +1,123 @@ +package com.pubnub.api.legacy.managers + +import com.pubnub.api.legacy.BaseTest +import com.pubnub.internal.managers.BasePathManager +import org.junit.Assert.assertEquals +import org.junit.Test + +class BasePathManagerTest : BaseTest() { + override fun onBefore() { + clearConfiguration() + } + + @Test + fun stdOriginNotSecure() { + config.secure = false + // initPubNub() + val basePathManager = BasePathManager(config.build()) + assertEquals("http://ps.pndsn.com", basePathManager.basePath()) + } + + @Test + fun stdOriginSecure() { + config.secure = true + // initPubNub() + val basePathManager = BasePathManager(config.build()) + assertEquals("https://ps.pndsn.com", basePathManager.basePath()) + } + + @Test + fun customOriginNotSecure() { + config.origin = "custom.origin.com" + config.secure = false + // initPubNub() + val basePathManager = BasePathManager(config.build()) + assertEquals("http://custom.origin.com", basePathManager.basePath()) + } + + @Test + fun customOriginSecure() { + config.origin = "custom.origin.com" + config.secure = true + // initPubNub() + val basePathManager = BasePathManager(config.build()) + assertEquals("https://custom.origin.com", basePathManager.basePath()) + } + + @Test + fun customOriginNotSecureWithCacheBusting() { + config.origin = "custom.origin.com" + config.cacheBusting = true + config.secure = false + // initPubNub() + val basePathManager = BasePathManager(config.build()) + assertEquals("http://custom.origin.com", basePathManager.basePath()) + } + + @Test + fun customOriginSecureWithCacheBusting() { + config.origin = "custom.origin.com" + config.secure = true + config.cacheBusting = true + // initPubNub() + val basePathManager = BasePathManager(config.build()) + assertEquals("https://custom.origin.com", basePathManager.basePath()) + } + + @Test + fun cacheBustingNotSecure() { + config.cacheBusting = true + config.secure = false + // initPubNub() + val basePathManager = BasePathManager(config.build()) + assertEquals("http://ps1.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps2.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps3.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps4.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps5.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps6.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps7.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps8.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps9.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps10.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps11.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps12.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps13.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps14.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps15.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps16.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps17.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps18.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps19.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps20.pndsn.com", basePathManager.basePath()) + assertEquals("http://ps1.pndsn.com", basePathManager.basePath()) + } + + @Test + fun cacheBustingSecure() { + config.cacheBusting = true + // initPubNub() + val basePathManager = BasePathManager(config.build()) + assertEquals("https://ps1.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps2.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps3.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps4.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps5.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps6.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps7.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps8.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps9.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps10.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps11.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps12.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps13.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps14.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps15.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps16.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps17.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps18.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps19.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps20.pndsn.com", basePathManager.basePath()) + assertEquals("https://ps1.pndsn.com", basePathManager.basePath()) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/managers/PublishSequenceManagerTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/managers/PublishSequenceManagerTest.kt new file mode 100644 index 000000000..6875aa7d5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/managers/PublishSequenceManagerTest.kt @@ -0,0 +1,18 @@ +package com.pubnub.api.legacy.managers + +import com.pubnub.api.legacy.BaseTest +import com.pubnub.internal.managers.PublishSequenceManager +import org.junit.Assert.assertEquals +import org.junit.Test + +class PublishSequenceManagerTest : BaseTest() { + @Test + fun testSequenceManager() { + val publishSequenceManager = PublishSequenceManager(2) + + assertEquals(1, publishSequenceManager.nextSequence()) + assertEquals(2, publishSequenceManager.nextSequence()) + assertEquals(1, publishSequenceManager.nextSequence()) + assertEquals(2, publishSequenceManager.nextSequence()) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/managers/SubscriptionManagerTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/managers/SubscriptionManagerTest.kt new file mode 100644 index 000000000..cb51d3699 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/managers/SubscriptionManagerTest.kt @@ -0,0 +1,3345 @@ +package com.pubnub.api.legacy.managers + +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.matching +import com.github.tomakehurst.wiremock.client.WireMock.notFound +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching +import com.github.tomakehurst.wiremock.matching.UrlPathPattern +import com.google.gson.reflect.TypeToken +import com.pubnub.api.PubNub +import com.pubnub.api.callbacks.SubscribeCallback +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.api.enums.PNHeartbeatNotificationOptions +import com.pubnub.api.enums.PNStatusCategory +import com.pubnub.api.legacy.BaseTest +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.api.retry.RetryConfiguration +import com.pubnub.internal.PubNubUtil +import com.pubnub.internal.managers.MapperManager +import com.pubnub.internal.toCsv +import com.pubnub.test.CommonUtils.emptyJson +import org.awaitility.Awaitility +import org.hamcrest.Matchers +import org.hamcrest.core.IsEqual +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Ignore +import org.junit.Test +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger + +class SubscriptionManagerTest : BaseTest() { + private val subscribeUrlMatcher = Regex("(/v2/subscribe/[^/]+/)(.+)?(/.+)") + private val presenceUrlMatcher = Regex("(/v2/presence/sub-key/[^/]+/channel/)(.+)(/.+)") + + // gets UrlPathPattern that matches the URL with channels in any order (order doesn't matter) + private fun getMatchingUrlWithChannels(url: String): UrlPathPattern { + // /v2/subscribe/mySubscribeKey/((ch2-pnpres|ch1-pnpres|ch2|ch1),?){4}/0 + val matches = + subscribeUrlMatcher.matchEntire(url) ?: presenceUrlMatcher.matchEntire(url) + ?: throw IllegalArgumentException("Unable to match $url, only /v2/subscribe/... or /v2/presence/... supported") + val channels = matches.groupValues[2].split(",") + return urlPathMatching("${matches.groupValues[1]}((${channels.joinToString("|")}),?){${channels.size}}${matches.groupValues[3]}") + } + + @Test + fun testGetSubscribedChannels() { + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + val channels = pubnub.getSubscribedChannels() + assertTrue(channels.contains("ch1")) + assertTrue(channels.contains("ch2")) + } + + @Test + fun testGetSubscribedEmptyChannel() { + val gotMessages = AtomicInteger() + config.retryConfiguration = RetryConfiguration.None + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + pubnub.subscribe( + channels = listOf(""), + ) + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + gotMessages.addAndGet(1) + } + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + gotMessages.addAndGet(1) + } + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + gotMessages.addAndGet(1) + } + }, + ) + + Awaitility.await() + .atMost(3, TimeUnit.SECONDS) + .untilAtomic(gotMessages, IsEqual.equalTo(0)) + } + + @Test + fun testGetSubscribedEmptyChannelGroup() { + val gotMessages = AtomicInteger() + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey//0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + pubnub.subscribe( + channelGroups = listOf(""), + ) + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + gotMessages.addAndGet(1) + } + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + gotMessages.addAndGet(1) + } + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + gotMessages.addAndGet(1) + } + }, + ) + + Awaitility.await() + .atMost(3, TimeUnit.SECONDS) + .untilAtomic(gotMessages, IsEqual.equalTo(0)) + } + + @Test + fun testGetSubscribedChannelGroups() { + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + pubnub.subscribe( + channelGroups = listOf("cg1", "cg2"), + ) + + val groups: List = pubnub.getSubscribedChannelGroups() + assertTrue(groups.contains("cg1")) + assertTrue(groups.contains("cg2")) + } + + @Test + fun testPubNubUnsubscribeAll() { + stubFor( + get(urlPathMatching("/v2/subscribe/mySubscribeKey/.*")) + .willReturn(emptyJson()), + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + channelGroups = listOf("cg1", "cg2"), + withPresence = true, + ) + + var channels = pubnub.getSubscribedChannels() + assertTrue(channels.contains("ch1")) + assertTrue(channels.contains("ch2")) + + var groups = pubnub.getSubscribedChannelGroups() + assertTrue(groups.contains("cg1")) + assertTrue(groups.contains("cg2")) + + pubnub.unsubscribeAll() + + channels = pubnub.getSubscribedChannels() + assertEquals(0, channels.size) + + groups = pubnub.getSubscribedChannelGroups() + assertEquals(0, groups.size) + } + + @Test + fun testSubscribeBuilder() { + val gotStatus = AtomicInteger() + + val gotMessage = AtomicBoolean() + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Publisher-A", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "o": { + "t": "14737141991877032", + "r": 2 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message" + }, + "b": "coolChannel" + } + ] + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { + gotStatus.addAndGet(1) + } + } + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) + assertTrue(requests.size > 0) + assertEquals("Message", MapperManager().elementToString(pnMessageResult.message, "text")) + assertEquals("coolChannel", pnMessageResult.channel) + assertEquals(null, pnMessageResult.subscription) + assertEquals("Publisher-A", pnMessageResult.publisher) + gotMessage.set(true) + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + Awaitility.await() + .atMost(3, TimeUnit.SECONDS) + .untilAtomic(gotMessage, IsEqual.equalTo(true)) + + Awaitility.await() + .atMost(3, TimeUnit.SECONDS) + .untilAtomic(gotStatus, IsEqual.equalTo(1)) + } + + @Test + fun testSubscribeDuplicateDisabledBuilder() { + val gotMessages = AtomicInteger() + + stubHandshaking(2) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("2")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "5", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Publisher-A", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "o": { + "t": "14737141991877032", + "r": 2 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message" + }, + "b": "coolChannel" + }, + { + "a": "4", + "f": 0, + "i": "Publisher-A", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "o": { + "t": "14737141991877032", + "r": 2 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message" + }, + "b": "coolChannel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("5")) + .willReturn(emptyJson()), + ) + + pubnub.addListener( + object : SubscribeCallback() { + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + gotMessages.addAndGet(1) + } + + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + Awaitility.await() + .atMost(3, TimeUnit.SECONDS) + .untilAtomic(gotMessages, IsEqual.equalTo(2)) + } + + @Test + fun testSubscribeDuplicateBuilder() { + config.dedupOnSubscribe = true + + val gotMessages = AtomicInteger() + stubHandshaking(2) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("2")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "10", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Publisher-A", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "o": { + "t": "14737141991877032", + "r": 2 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message" + }, + "b": "coolChannel" + }, + { + "a": "4", + "f": 0, + "i": "Publisher-A", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "o": { + "t": "14737141991877032", + "r": 2 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message" + }, + "b": "coolChannel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("10")) + .willReturn(emptyJson()), + ) + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + gotMessages.addAndGet(1) + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + Awaitility.await() + .atMost(3, TimeUnit.SECONDS) + .untilAtomic(gotMessages, IsEqual.equalTo(1)) + } + + @Test + fun testSubscribeDuplicateWithLimitBuilder() { + config.dedupOnSubscribe = true + config.maximumMessagesCacheSize = 1 + + val gotMessages = AtomicInteger() + + stubHandshaking(2) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("2")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "5", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Publisher-A", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "o": { + "t": "14737141991877032", + "r": 2 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message1" + }, + "b": "coolChannel" + }, + { + "a": "4", + "f": 0, + "i": "Publisher-A", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "o": { + "t": "14737141991877032", + "r": 2 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message2" + }, + "b": "coolChannel" + }, + { + "a": "4", + "f": 0, + "i": "Publisher-A", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "o": { + "t": "14737141991877032", + "r": 2 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message1" + }, + "b": "coolChannel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("5")) + .willReturn(notFound()), + ) + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + gotMessages.addAndGet(1) + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + Awaitility.await() + .atMost(3, TimeUnit.SECONDS) + .untilAtomic(gotMessages, IsEqual.equalTo(3)) + } + + @Test + fun testSubscribeBuilderWithAccessManager403Error() { + val gotStatus = AtomicInteger() + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn( + aResponse().withStatus(403).withBody( + """ + { + "message": "Forbidden", + "payload": { + "channels": [ + "ch1", + "ch2" + ], + "channel-groups": [ + ":cg1", + ":cg2" + ] + }, + "error": true, + "service": "Access Manager", + "status": 403 + } + """.trimIndent(), + ), + ), + ) + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNConnectionError && pnStatus.exception?.statusCode == 403) { + assertEquals(listOf("ch1", "ch2"), pnStatus.exception?.affectedChannels) + assertEquals(listOf("cg1", "cg2"), pnStatus.exception?.affectedChannelGroups) + gotStatus.addAndGet(1) + } + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + Awaitility.await() + .atMost(2, TimeUnit.SECONDS) + .untilAtomic(gotStatus, IsEqual.equalTo(1)) + } + + @Test + fun testNamingSubscribeChannelGroupBuilder() { + val gotStatus = AtomicBoolean() + val gotMessage = AtomicBoolean() + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message" + }, + "b": "coolChannelGroup" + } + ] + } + """.trimIndent(), + ), + ), + ) + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { + assertEquals(2, pnStatus.affectedChannels.size) + gotStatus.set(true) + } + } + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) + assertTrue(requests.size > 0) + assertEquals("Message", MapperManager().elementToString(pnMessageResult.message, "text")) + assertEquals("coolChannel", pnMessageResult.channel) + assertEquals("coolChannelGroup", pnMessageResult.subscription) + gotMessage.set(true) + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + Awaitility.await().atMost(4, TimeUnit.SECONDS).untilTrue(gotMessage) + Awaitility.await().atMost(4, TimeUnit.SECONDS).untilTrue(gotStatus) + } + + @Test + fun testPresenceSubscribeBuilder() { + val gotStatus = AtomicInteger() + val gotMessage = AtomicBoolean() + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14614512228786519", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "p": { + "t": "14614512228418349", + "r": 2 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel-pnpres", + "d": { + "action": "join", + "timestamp": 1461451222, + "uuid": "4a6d5df7-e301-4e73-a7b7-6af9ab484eb0", + "occupancy": 1 + }, + "b": "coolChannel-pnpres" + } + ] + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { + gotStatus.addAndGet(1) + } + } + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) + assertTrue(requests.size >= 1) + assertEquals("coolChannel", pnPresenceEventResult.channel) + assertEquals(null, pnPresenceEventResult.subscription) + gotMessage.set(true) + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + Awaitility.await() + .atMost(2, TimeUnit.SECONDS) + .untilAtomic(gotMessage, IsEqual.equalTo(true)) + + Awaitility.await() + .atMost(2, TimeUnit.SECONDS) + .untilAtomic(gotStatus, IsEqual.equalTo(1)) + } + + @Test + fun testPresenceChannelGroupSubscribeBuilder() { + val gotStatus = AtomicInteger() + val gotMessage = AtomicBoolean() + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14614512228786519", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "p": { + "t": "14614512228418349", + "r": 2 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel-pnpres", + "d": { + "action": "join", + "timestamp": 1461451222, + "uuid": "4a6d5df7-e301-4e73-a7b7-6af9ab484eb0", + "occupancy": 1 + }, + "b": "coolChannelGroup-pnpres" + } + ] + } + """.trimIndent(), + ), + ), + ) + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { + gotStatus.addAndGet(1) + } + } + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) + assertTrue(requests.size >= 1) + assertEquals("coolChannel", pnPresenceEventResult.channel) + assertEquals("coolChannelGroup", pnPresenceEventResult.subscription) + gotMessage.set(true) + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + Awaitility.await() + .atMost(2, TimeUnit.SECONDS) + .untilAtomic(gotMessage, IsEqual.equalTo(true)) + + Awaitility.await() + .atMost(2, TimeUnit.SECONDS) + .untilAtomic(gotStatus, IsEqual.equalTo(1)) + } + + @Test + fun testSubscribeSlidingBuilder() { + val gotMessage1 = AtomicBoolean() + val gotMessage2 = AtomicBoolean() + val gotMessage3 = AtomicBoolean() + + stubHandshaking(2) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("2")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "3", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("3")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "10", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message3" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("10")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "20", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Message10" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("20")) + .willReturn(emptyJson()), + ) + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + } + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + println("--==" + pnMessageResult.message) + when (pnMessageResult.message.asJsonObject["text"].asString) { +// "Message" -> { // TODO is it ever the case that we get messages on TT=0? +// gotMessage1.set(true) +// } + + "Message3" -> { + gotMessage2.set(true) + } + + "Message10" -> { + gotMessage3.set(true) + } + } + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + +// Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic( +// gotMessage1, +// IsEqual.equalTo(true) +// ) + + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic( + gotMessage2, + IsEqual.equalTo(true), + ) + + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic( + gotMessage3, + IsEqual.equalTo(true), + ) + } + + @Test + fun testSubscribeBuilderNumber() { + val atomic = AtomicInteger(0) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": 10, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) + assertTrue(requests.size >= 1) + assertEquals(10, pnMessageResult.message.asInt) + atomic.addAndGet(1) + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, Matchers.greaterThan(0)) + } + + @Test + fun testSubscribeBuilderWithMetadata() { + val atomic = AtomicInteger(0) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14858178301085322", + "r": 7 + }, + "m": [ + { + "a": "4", + "f": 512, + "i": "02a7b822-220c-49b0-90c4-d9cbecc0fd85", + "s": 1, + "p": { + "t": "14858178301075219", + "r": 7 + }, + "k": "demo-36", + "c": "chTest", + "u": { + "status_update": { + "lat": 55.752023906250656, + "lon": 37.61749036080494, + "driver_id": 4722 + } + }, + "d": { + "City": "Goiania", + "Name": "Marcelo" + } + } + ] + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) + assertTrue(requests.size >= 1) + assertEquals( + """{"status_update":{"lat":55.752023906250656,"lon":37.61749036080494,"driver_id":4722}}""", + pnMessageResult.userMetadata.toString(), + ) + atomic.addAndGet(1) + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, Matchers.greaterThan(0)) + } + + @Test + fun testSubscribeBuilderWithState() { + val expectedPayload = + PubNubUtil.urlDecode( + """%7B%22ch1%22%3A%5B%22p1%22%2C%22p2%22%5D%7D""", + ) + + val expectedMap = + pubnub.mapper.fromJson>( + expectedPayload, + object : TypeToken?>() {}.type, + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")).willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/presence/sub-key/mySubscribeKey/channel/ch2,ch1/heartbeat")).willReturn( + emptyJson(), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/presence/sub-key/mySubscribeKey/channel/ch1/uuid/myUUID/data")).willReturn( + emptyJson(), + ), + ) + + config.presenceTimeout = 20 + config.heartbeatNotificationOptions = PNHeartbeatNotificationOptions.ALL + config.maintainPresenceState = true + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + // do nothing + } + }, + ) + + try { + pubnub.setPresenceState( + channels = listOf("ch1"), + channelGroups = listOf("cg2"), + state = listOf("p1", "p2"), + ).sync() + } catch (e: Exception) { + // ignore + } + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + channelGroups = listOf("cg1", "cg2"), + ) + + Awaitility.await().atMost(2, TimeUnit.SECONDS).until { verifyCalls(expectedMap) } + } + + private fun verifyCalls(expectedMap: Map): Boolean { + val subscribeRequests = + findAll( + getRequestedFor( + getMatchingUrlWithChannels( + """/v2/subscribe/${pubnub.configuration.subscribeKey}/ch2,ch1/.*""", + ), + ), + ) + val subCond = + subscribeRequests.any { + try { + val stateString = PubNubUtil.urlDecode(it.queryParameter("state").firstValue()) + val actualMap: HashMap = + pubnub.mapper.fromJson( + stateString, + object : TypeToken>() {}.type, + ) + actualMap == expectedMap + } catch (e: Exception) { + false + } + } + val heartbeatRequests = + findAll( + getRequestedFor( + getMatchingUrlWithChannels( + """/v2/presence/sub-key/${pubnub.configuration.subscribeKey}/channel/ch2,ch1/heartbeat.*""", + ), + ), + ) + val heartbeatCond = + heartbeatRequests.all { + it.queryParams.containsKey("state") + } + + return subCond && heartbeatCond + } + + @Test + fun testSubscribeChannelGroupBuilder() { + val atomic = AtomicBoolean() + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) + for (request in requests) { + val channelGroupQuery = request.queryParameter("channel-group") + if (channelGroupQuery != null && channelGroupQuery.firstValue() == "cg1,cg2") { + atomic.set(true) + } + } + } + }, + ) + + pubnub.subscribe( + channelGroups = listOf("cg1", "cg2"), + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilTrue(atomic) + } + + @Test + fun testSubscribeChannelGroupWithPresenceBuilder() { + val atomic = AtomicInteger(0) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) + for (request in requests) { + val channelGroups = + request.queryParameter("channel-group") + .firstValue() + .split(",") + .toMutableList() + .sorted() + if ("cg1,cg1-pnpres,cg2,cg2-pnpres" == channelGroups.toCsv()) { + atomic.addAndGet(1) + } + } + } + }, + ) + + pubnub.subscribe( + channelGroups = listOf("cg1", "cg2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, Matchers.greaterThan(0)) + } + + @Test + fun testSubscribeWithFilterExpressionBuilder() { + val atomic = AtomicBoolean() + + config.filterExpression = "much=filtering" + + stubHandshaking(2) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("filter-expr", matching("much=filtering")) + .withQueryParam("tt", matching("2")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "5", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("5")) + .willReturn(notFound()), + ) + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + val requests = + findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) + assertTrue(requests.size > 0) + atomic.set(true) + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilTrue(atomic) + } + + @Test + fun testSubscribeWithEncryption() { + val atomic = AtomicInteger(0) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14718972508742569", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 512, + "i": "ff374d0b-b866-40db-9ced-42d205bb808b", + "p": { + "t": "14718972508739738", + "r": 1 + }, + "k": "demo-36", + "c": "max_ch1", + "d": "6QoqmS9CnB3W9+I4mhmL7w==" + } + ] + } + """.trimIndent(), + ), + ), + ) + config.cryptoModule = CryptoModule.createLegacyCryptoModule("hello", false) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) + assertTrue(requests.size > 0) + assertEquals("hey", MapperManager().elementToString(pnMessageResult.message, "text")) + atomic.addAndGet(1) + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, Matchers.greaterThan(0)) + } + + @Test + fun testSubscribeWithEncryptionPNOther() { + val atomic = AtomicInteger(0) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14718972508742569", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 512, + "i": "ff374d0b-b866-40db-9ced-42d205bb808b", + "p": { + "t": "14718972508739738", + "r": 1 + }, + "k": "demo-36", + "c": "max_ch1", + "d": { + "pn_other": "6QoqmS9CnB3W9+I4mhmL7w==" + } + } + ] + } + """.trimIndent(), + ), + ), + ) + + config.cryptoModule = CryptoModule.createLegacyCryptoModule("hello", false) + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) + assertTrue(requests.size > 0) + assertEquals( + "hey", + pnMessageResult.message.asJsonObject["pn_other"].asJsonObject["text"].asString, + ) + atomic.addAndGet(1) + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, Matchers.greaterThan(0)) + } + + @Test + fun testSubscribePresenceBuilder() { + val atomic = AtomicInteger(0) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2-pnpres,ch1-pnpres,ch2,ch1/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + val requests = + findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) + assertTrue(requests.size >= 1) + assertEquals("""{"text":"Enter Message Here"}""", pnMessageResult.message.toString()) + atomic.addAndGet(1) + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, Matchers.greaterThan(0)) + } + + @Test + fun testSubscribePresencePayloadHereNowRefreshDeltaBuilder() { + val atomic = AtomicInteger(0) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14901247588021627", + "r": 2 + }, + "m": [ + { + "a": "4", + "f": 0, + "p": { + "t": "14901247587675704", + "r": 1 + }, + "k": "demo-36", + "c": "moon-interval-deltas-pnpres", + "d": { + "action": "interval", + "timestamp": 1490124758, + "occupancy": 2, + "here_now_refresh": true, + "join": [ + "2220E216-5A30-49AD-A89C-1E0B5AE26AD7", + "4262AE3F-3202-4487-BEE0-1A0D91307DEB" + ] + }, + "b": "moon-interval-deltas-pnpres" + } + ] + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + if (atomic.get() == 0) { + assertEquals(true, pnPresenceEventResult.hereNowRefresh) + assertTrue(pnPresenceEventResult.occupancy!! == 2) + atomic.incrementAndGet() + } + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testSubscribePresencePayloadJoinDeltaBuilder() { + val atomic = AtomicInteger(0) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14901247588021627", + "r": 2 + }, + "m": [ + { + "a": "4", + "f": 0, + "p": { + "t": "14901247587675704", + "r": 1 + }, + "k": "demo-36", + "c": "moon-interval-deltas-pnpres", + "d": { + "action": "interval", + "timestamp": 1490124758, + "occupancy": 2, + "join": [ + "2220E216-5A30-49AD-A89C-1E0B5AE26AD7", + "4262AE3F-3202-4487-BEE0-1A0D91307DEB" + ] + }, + "b": "moon-interval-deltas-pnpres" + } + ] + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + if (atomic.get() == 0) { + val joinList: MutableList = ArrayList() + joinList.add("2220E216-5A30-49AD-A89C-1E0B5AE26AD7") + joinList.add("4262AE3F-3202-4487-BEE0-1A0D91307DEB") + assertEquals("interval", pnPresenceEventResult.event) + assertEquals(joinList, pnPresenceEventResult.join) + assertTrue(pnPresenceEventResult.occupancy!! == 2) + atomic.incrementAndGet() + } + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testSubscribePresencePayloadLeaveDeltaBuilder() { + val atomic = AtomicInteger(0) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14901247588021627", + "r": 2 + }, + "m": [ + { + "a": "4", + "f": 0, + "p": { + "t": "14901247587675704", + "r": 1 + }, + "k": "demo-36", + "c": "moon-interval-deltas-pnpres", + "d": { + "action": "interval", + "timestamp": 1490124758, + "occupancy": 2, + "leave": [ + "2220E216-5A30-49AD-A89C-1E0B5AE26AD7", + "4262AE3F-3202-4487-BEE0-1A0D91307DEB" + ] + }, + "b": "moon-interval-deltas-pnpres" + } + ] + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + if (atomic.get() == 0) { + val leaveList: MutableList = ArrayList() + leaveList.add("2220E216-5A30-49AD-A89C-1E0B5AE26AD7") + leaveList.add("4262AE3F-3202-4487-BEE0-1A0D91307DEB") + assertEquals("interval", pnPresenceEventResult.event) + assertEquals(leaveList, pnPresenceEventResult.leave) + assertTrue(pnPresenceEventResult.occupancy!! == 2) + atomic.incrementAndGet() + } + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testSubscribePresencePayloadTimeoutDeltaBuilder() { + val atomic = AtomicInteger(0) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14901247588021627", + "r": 2 + }, + "m": [ + { + "a": "4", + "f": 0, + "p": { + "t": "14901247587675704", + "r": 1 + }, + "k": "demo-36", + "c": "moon-interval-deltas-pnpres", + "d": { + "action": "interval", + "timestamp": 1490124758, + "occupancy": 2, + "timeout": [ + "2220E216-5A30-49AD-A89C-1E0B5AE26AD7", + "4262AE3F-3202-4487-BEE0-1A0D91307DEB" + ] + }, + "b": "moon-interval-deltas-pnpres" + } + ] + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + if (atomic.get() == 0) { + val timeoutList = + listOf( + "2220E216-5A30-49AD-A89C-1E0B5AE26AD7", + "4262AE3F-3202-4487-BEE0-1A0D91307DEB", + ) + assertEquals("interval", pnPresenceEventResult.event) + assertEquals(timeoutList, pnPresenceEventResult.timeout) + assertTrue(pnPresenceEventResult.occupancy!! == 2) + atomic.incrementAndGet() + } + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testSubscribePresencePayloadBuilder() { + val atomic = AtomicInteger(0) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14614512228786519", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "p": { + "t": "14614512228418349", + "r": 2 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel-pnpres", + "d": { + "action": "join", + "timestamp": 1461451222, + "uuid": "4a6d5df7-e301-4e73-a7b7-6af9ab484eb0", + "occupancy": 1 + }, + "b": "coolChannel-pnpres" + } + ] + } + """.trimIndent(), + ), + ), + ) + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + if (atomic.get() == 0) { + assertEquals("join", pnPresenceEventResult.event) + assertEquals("4a6d5df7-e301-4e73-a7b7-6af9ab484eb0", pnPresenceEventResult.uuid) + assertTrue(pnPresenceEventResult.occupancy!! == 1) + assertTrue(pnPresenceEventResult.timestamp!! == 1461451222L) + atomic.incrementAndGet() + } + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, IsEqual.equalTo(1)) + } + + @Test + fun testSubscribePresenceStateCallback() { + val atomic = AtomicBoolean() + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch10,ch10-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14637536741734954", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 512, + "p": { + "t": "14637536740940378", + "r": 1 + }, + "k": "demo-36", + "c": "ch10-pnpres", + "d": { + "action": "join", + "timestamp": 1463753674, + "uuid": "24c9bb19-1fcd-4c40-a6f1-522a8a1329ef", + "occupancy": 3 + }, + "b": "ch10-pnpres" + }, + { + "a": "4", + "f": 512, + "p": { + "t": "14637536741726901", + "r": 1 + }, + "k": "demo-36", + "c": "ch10-pnpres", + "d": { + "action": "state-change", + "timestamp": 1463753674, + "data": { + "state": "cool" + }, + "uuid": "24c9bb19-1fcd-4c40-a6f1-522a8a1329ef", + "occupancy": 3 + }, + "b": "ch10-pnpres" + } + ] + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + if (pnPresenceEventResult.event == "state-change") { + if (pnPresenceEventResult.state!!.asJsonObject.has("state") && + pnPresenceEventResult.state!!.asJsonObject.get("state").asString == "cool" + ) { + atomic.set(true) + } + } + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch10"), + withPresence = true, + ) + + Awaitility.await() + .atMost(2, TimeUnit.SECONDS) + .untilAtomic( + atomic, + IsEqual.equalTo(true), + ) + } + + @Test + fun testSubscribeRegionBuilder() { + val atomic = AtomicBoolean() + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 8 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) {} + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + val requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))) + if (requests.size > 1) { + assertEquals("8", requests[1].queryParameter("tr").firstValue()) + atomic.set(true) + } + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, IsEqual.equalTo(true)) + } + + @Test + fun testRemoveListener() { + stubFor( + get(urlPathMatching("/v2/subscribe/mySubscribeKey/.*")) + .willReturn(emptyJson()), + ) + + val atomic = AtomicInteger(0) + val sub1: SubscribeCallback = + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + atomic.addAndGet(1) + } + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + atomic.addAndGet(1) + } + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + atomic.addAndGet(1) + } + } + + pubnub.addListener(sub1) + pubnub.removeListener(sub1) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(2, TimeUnit.SECONDS) + .untilAtomic(atomic, IsEqual.equalTo(0)) + } + + @Test + fun testUnsubscribe() { + val statusReceived = AtomicBoolean() + val messageReceived = AtomicBoolean() + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch2-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + stubFor( + get(getMatchingUrlWithChannels("/v2/presence/sub-key/mySubscribeKey/channel/ch1/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + val sub1: SubscribeCallback = + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { + pubnub.unsubscribe( + channels = listOf("ch1"), + ) + } else if (pnStatus.category == PNStatusCategory.PNDisconnectedCategory || + pnStatus.category == PNStatusCategory.PNSubscriptionChanged && + !pnStatus.affectedChannels.contains("ch1") + ) { + statusReceived.set(true) + } + } + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + val requests = + findAll( + getRequestedFor( + getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch2-pnpres/0"), + ), + ) + if (requests.isNotEmpty()) { + messageReceived.set(true) + } + } + } + pubnub.addListener(sub1) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(1, TimeUnit.SECONDS) + .untilAtomic( + messageReceived, + IsEqual.equalTo(true), + ) + + Awaitility.await() + .atMost(1, TimeUnit.SECONDS) + .untilAtomic( + statusReceived, + IsEqual.equalTo(true), + ) + } + + @Test + fun testAllHeartbeats() { + val statusRecieved = AtomicBoolean() + + config.presenceTimeout = 20 + config.heartbeatNotificationOptions = PNHeartbeatNotificationOptions.ALL + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + stubFor( + get(getMatchingUrlWithChannels("/v2/presence/sub-key/mySubscribeKey/channel/ch2,ch1/heartbeat")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + val sub1: SubscribeCallback = + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNHeartbeatSuccess) { + statusRecieved.set(true) + } + } + } + pubnub.addListener(sub1) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(2, TimeUnit.SECONDS) + .untilAtomic(statusRecieved, IsEqual.equalTo(true)) + } + + @Test + fun testAllHeartbeatsViaPresence() { + val statusReceived = AtomicBoolean() + config.presenceTimeout = 20 + config.heartbeatNotificationOptions = PNHeartbeatNotificationOptions.ALL + + stubFor( + get(getMatchingUrlWithChannels("/v2/presence/sub-key/mySubscribeKey/channel/ch2,ch1/heartbeat")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + val sub1: SubscribeCallback = + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNHeartbeatSuccess) { + statusReceived.set(true) + } + } + } + assertNotNull(sub1) + pubnub.addListener(sub1) + + pubnub.presence( + channels = listOf("ch1", "ch2"), + connected = true, + ) + + Awaitility.await() + .atMost(2, TimeUnit.SECONDS) + .untilAtomic( + statusReceived, + IsEqual.equalTo(true), + ) + } + + @Test + @Ignore("Presence EE doesn't support this right now") // TODO + fun testAllHeartbeatsLeaveViaPresence() { + val statusReceived = AtomicBoolean() + config.heartbeatNotificationOptions = PNHeartbeatNotificationOptions.ALL + + stubFor( + get(getMatchingUrlWithChannels("/v2/presence/sub-key/mySubscribeKey/channel/ch1,ch2/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + val sub1: SubscribeCallback = + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + println(pnStatus) +// if (pnStatus.operation == PNOperationType.PNUnsubscribeOperation && !pnStatus.error) { + if (pnStatus.category == PNStatusCategory.PNDisconnectedCategory) { // TODO what is this trying to test really? + statusReceived.set(true) + } + } + } + + pubnub.addListener(sub1) + + pubnub.presence( + channels = listOf("ch1", "ch2"), + connected = false, + ) + + Awaitility.await() + .atMost(2, TimeUnit.SECONDS) + .untilAtomic(statusReceived, IsEqual.equalTo(true)) + } + + @Test + fun testSuccessOnFailureVerbosityHeartbeats() { + val statusReceived = AtomicBoolean() + + config.presenceTimeout = 20 + config.heartbeatNotificationOptions = PNHeartbeatNotificationOptions.FAILURES + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "5", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/presence/sub-key/mySubscribeKey/channel/ch2,ch1/heartbeat")) + .willReturn(aResponse().withStatus(404).withBody("{}")), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .withQueryParam("tt", matching("5")) + .willReturn(notFound()), + ) + + val sub1: SubscribeCallback = + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNHeartbeatFailed) { + statusReceived.set(true) + } + } + } + + pubnub.addListener(sub1) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(2, TimeUnit.SECONDS) + .untilAtomic(statusReceived, IsEqual.equalTo(true)) + } + + @Test + fun testFailedHeartbeats() { + val statusReceived = AtomicBoolean() + + config.presenceTimeout = 20 + config.heartbeatNotificationOptions = PNHeartbeatNotificationOptions.ALL + + stubHandshaking(2, "ch2,ch1,ch2-pnpres,ch1-pnpres") + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .withQueryParam("tt", matching("2")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "5", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/presence/sub-key/mySubscribeKey/channel/ch2,ch1/heartbeat")) + .willReturn(emptyJson().withStatus(403)), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .withQueryParam("tt", matching("5")) + .willReturn(notFound()), + ) + + val sub1: SubscribeCallback = + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNHeartbeatFailed) { + statusReceived.set(true) + } + } + } + pubnub.addListener(sub1) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(2, TimeUnit.SECONDS) + .untilAtomic(statusReceived, IsEqual.equalTo(true)) + } + + @Test + fun testSilencedHeartbeats() { + val statusReceived = AtomicBoolean() + + config.heartbeatNotificationOptions = PNHeartbeatNotificationOptions.NONE + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [{ + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + } + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + }] + } + """.trimIndent(), + ), + ), + ) + + val sub1: SubscribeCallback = + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNHeartbeatSuccess) { + statusReceived.set(true) + } + } + } + + pubnub.addListener(sub1) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(2, TimeUnit.SECONDS) + .untilAtomic(statusReceived, IsEqual.equalTo(false)) + } + + @Test + fun testFailedNoneHeartbeats() { + val statusReceived = AtomicBoolean() + + config.presenceTimeout = 20 + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/presence/sub-key/mySubscribeKey/channel/ch2,ch1/heartbeat")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + + val sub1: SubscribeCallback = + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category != PNStatusCategory.PNHeartbeatSuccess) { + statusReceived.set(true) + } + } + } + pubnub.addListener(sub1) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(4, TimeUnit.SECONDS) + .untilTrue(statusReceived) + } + + @Test + fun testHeartbeatsDisabled() { + val subscribeSuccess = AtomicBoolean() + val heartbeatFail = AtomicBoolean() + + config.heartbeatNotificationOptions = PNHeartbeatNotificationOptions.ALL + + assertEquals(PNHeartbeatNotificationOptions.ALL, pubnub.configuration.heartbeatNotificationOptions) + assertEquals(300, pubnub.configuration.presenceTimeout) + assertEquals(0, pubnub.configuration.heartbeatInterval) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch1,ch1-pnpres/0")) + .willReturn( + aResponse() + .withBody( + """ + { + "t": { + "t": null, + "r": 12 + }, + "m": [] + } + """.trimIndent(), + ) + .withStatus(200), + ), + ) + stubFor( + get(getMatchingUrlWithChannels("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .willReturn( + aResponse() + .withStatus(200) + .withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { + subscribeSuccess.set(true) + } + if (pnStatus.category == PNStatusCategory.PNHeartbeatSuccess) { + heartbeatFail.set(true) + } + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1"), + withPresence = true, + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .until { subscribeSuccess.get() && !heartbeatFail.get() } + } + + @Test + fun testHeartbeatsEnabled() { + val subscribeSuccess = AtomicBoolean() + val heartbeatSuccess = AtomicBoolean() + + config.heartbeatNotificationOptions = PNHeartbeatNotificationOptions.ALL + config.presenceTimeout = 20 + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch1,ch1-pnpres/0")) + .willReturn( + aResponse() + .withBody( + """ + { + "t": { + "t": null, + "r": 12 + }, + "m": [] + } + """.trimIndent(), + ) + .withStatus(200), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .willReturn( + aResponse() + .withStatus(200) + .withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent(), + ), + ), + ) + + pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { + subscribeSuccess.set(true) + } + if (pnStatus.category == PNStatusCategory.PNHeartbeatSuccess) { + heartbeatSuccess.set(true) + } + } + }, + ) + + pubnub.subscribe( + channels = listOf("ch1"), + withPresence = true, + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .until { subscribeSuccess.get() && heartbeatSuccess.get() } + } + + @Test + fun testMinimumPresenceValueNoInterval() { + config.presenceTimeout = 10 + assertEquals(20, pubnub.configuration.presenceTimeout) + assertEquals(9, pubnub.configuration.heartbeatInterval) + } + + @Test + fun testMinimumPresenceValueWithInterval() { + config.presenceTimeout = 20 + config.heartbeatInterval = 50 + assertEquals(20, pubnub.configuration.presenceTimeout) + assertEquals(50, pubnub.configuration.heartbeatInterval) + } + + @Test + fun testUnsubscribeAll() { + val statusReceived = AtomicBoolean() + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch2-pnpres/0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "14607577960932487", + "r": 1 + }, + "m": [ + { + "a": "4", + "f": 0, + "i": "Client-g5d4g", + "p": { + "t": "14607577960925503", + "r": 1 + }, + "k": "sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", + "c": "coolChannel", + "d": { + "text": "Enter Message Here" + }, + "b": "coolChan-bnel" + } + ] + } + """.trimIndent(), + ), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/presence/sub-key/mySubscribeKey/channel/ch1/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/presence/sub-key/mySubscribeKey/channel/ch2/leave")) + .willReturn( + aResponse().withBody( + """ + { + "status": 200, + "message": "OK", + "service": "Presence", + "action": "leave" + } + """.trimIndent(), + ), + ), + ) + + val sub1: SubscribeCallback = + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + if (pnStatus.category == PNStatusCategory.PNConnectedCategory) { + pubnub.unsubscribe( + channels = listOf("ch1"), + ) + } + + if (pnStatus.category == PNStatusCategory.PNSubscriptionChanged) { + if ("ch1" !in pnStatus.affectedChannels) { + pubnub.unsubscribe( + channels = listOf("ch2"), + ) + } + } + + if (pnStatus.category == PNStatusCategory.PNDisconnectedCategory) { + statusReceived.set(true) + } + } + } + + pubnub.addListener(sub1) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withPresence = true, + ) + + Awaitility.await() + .atMost(4, TimeUnit.SECONDS) + .untilTrue(statusReceived) + } + + @Test + fun testSubscribeWithCustomTimetoken() { + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", equalTo("0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "999", + "r": 1 + }, + "m": [] + } + """.trimIndent(), + ), + ), + ) + + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", equalTo("555")) + .willReturn(emptyJson()), + ) + + pubnub.subscribe( + channels = listOf("ch1", "ch2"), + withTimetoken = 555L, + ) + + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .pollDelay(2, TimeUnit.SECONDS) + .until { + val requests = + findAll( + getRequestedFor(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", equalTo("555")), + ) + assertEquals(1, requests.size) + true + } + } + + private fun stubHandshaking( + withNextTimetoken: Int = 5, + withChannels: String = "ch2,ch1", + ) { + stubFor( + get(getMatchingUrlWithChannels("/v2/subscribe/mySubscribeKey/$withChannels/0")) + .withQueryParam("tt", matching("0")) + .willReturn( + aResponse().withBody( + """ + { + "t": { + "t": "$withNextTimetoken", + "r": 1 + } + } + """, + ), + ), + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/vendor/EncryptDecryptTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/vendor/EncryptDecryptTest.kt new file mode 100644 index 000000000..3697467d6 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/legacy/vendor/EncryptDecryptTest.kt @@ -0,0 +1,73 @@ +package com.pubnub.api.legacy.vendor + +import com.pubnub.api.PubNubException +import org.junit.Assert +import org.junit.Test +import java.io.IOException + +class EncryptDecryptTest { + @Test + @Throws(IOException::class, PubNubException::class) + fun canDecryptTextWhatIsEncryptedWithStaticIV() { + // given + val cipherKey = "enigma" + val msgToEncrypt = "Hello world" + + // when + val crypto = com.pubnub.internal.vendor.Crypto(cipherKey) + val encryptedMsg = crypto.encrypt(msgToEncrypt) + val decryptedMsg = crypto.decrypt(encryptedMsg) + + // then + Assert.assertEquals(msgToEncrypt, decryptedMsg) + } + + @Test + @Throws(IOException::class, PubNubException::class) + fun canDecryptTextWhatIsEncryptedWithRandomIV() { + // given + val cipherKey = "enigma" + val msgToEncrypt = "Hello world" + + // when + val crypto = com.pubnub.internal.vendor.Crypto(cipherKey, true) + val encryptedMsg = crypto.encrypt(msgToEncrypt) + val decryptedMsg = crypto.decrypt(encryptedMsg) + + // then + Assert.assertEquals(msgToEncrypt, decryptedMsg) + } + + @Test + @Throws(PubNubException::class) + fun encryptingWithRandomIVTwoTimesTheSameMessageProducesDifferentOutput() { + // given + val cipherKey = "enigma" + val msgToEncrypt = "Hello world" + + // when + val crypto = com.pubnub.internal.vendor.Crypto(cipherKey, true) + val encrypted1 = crypto.encrypt(msgToEncrypt) + val encrypted2 = crypto.encrypt(msgToEncrypt) + + // then + Assert.assertNotEquals(encrypted1, encrypted2) + } + + @Test + @Throws(PubNubException::class) + fun encryptingWithRandomIVTwoTimesDecryptedMsgIsTheSame() { + // given + val cipherKey = "enigma" + val msgToEncrypt = "Hello world" + + // when + val crypto = com.pubnub.internal.vendor.Crypto(cipherKey, true) + val encrypted1 = crypto.encrypt(msgToEncrypt) + val encrypted2 = crypto.encrypt(msgToEncrypt) + + // then + Assert.assertEquals(msgToEncrypt, crypto.decrypt(encrypted1)) + Assert.assertEquals(msgToEncrypt, crypto.decrypt(encrypted2)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/retry/RetryConfigurationTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/retry/RetryConfigurationTest.kt new file mode 100644 index 000000000..21578fdb3 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/api/retry/RetryConfigurationTest.kt @@ -0,0 +1,44 @@ +package com.pubnub.api.retry + +import org.junit.Assert.assertEquals +import org.junit.Test + +class RetryConfigurationTest { + @Test + fun `should set delay to 3 in RetryConfiguration Linear when user set it lower than 3`() { + val retryConfiguration = RetryConfiguration.Linear(delayInSec = 1, maxRetryNumber = 10) + + assertEquals(2, retryConfiguration.delayInSec.inWholeSeconds) + } + + @Test + fun `should set maxRetry to 10 in RetryConfiguration Linear when user set it above 10`() { + val retryConfiguration = RetryConfiguration.Linear(delayInSec = 3, maxRetryNumber = 11) + + assertEquals(10, retryConfiguration.maxRetryNumber) + } + + @Test + fun `should set minDelayInSec to 2 in RetryConfiguration Exponential when user set it lower than 2`() { + val retryConfiguration = + RetryConfiguration.Exponential(minDelayInSec = 1, maxDelayInSec = 10, maxRetryNumber = 10) + + assertEquals(2, retryConfiguration.minDelayInSec.inWholeSeconds) + } + + @Test + fun `should set maxRetry to 6 in RetryConfiguration Exponential when user set it above 6`() { + val retryConfiguration = + RetryConfiguration.Exponential(minDelayInSec = 5, maxDelayInSec = 10, maxRetryNumber = 10) + + assertEquals(6, retryConfiguration.maxRetryNumber) + } + + @Test + fun `should set maxDelayInSec to 150 in RetryConfiguration Exponential when user set it above 150`() { + val retryConfiguration = + RetryConfiguration.Exponential(minDelayInSec = 5, maxDelayInSec = 10, maxRetryNumber = 10) + + assertEquals(6, retryConfiguration.maxRetryNumber) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/ContractTestConfig.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/ContractTestConfig.kt new file mode 100644 index 000000000..743ad33d5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/ContractTestConfig.kt @@ -0,0 +1,40 @@ +package com.pubnub.contract + +import org.aeonbits.owner.Config +import org.aeonbits.owner.ConfigFactory + +@Config.Sources("file:../../test.properties") +interface ContractTestKeysConfig : Config { + @get:Config.Key("subKey") + val subKey: String + + @get:Config.Key("pubKey") + val pubKey: String + + @get:Config.Key("pamSubKey") + val pamSubKey: String + + @get:Config.Key("pamPubKey") + val pamPubKey: String + + @get:Config.Key("pamSecKey") + val pamSecKey: String + + @get:Config.Key("serverHostPort") + val serverHostPort: String + + @get:Config.Key("serverMock") + @get:Config.DefaultValue("true") + val serverMock: Boolean + + @get:Config.Key("dataFilesLocation") + @get:Config.DefaultValue("src/test/resources/sdk-specifications/features/data") + val dataFilesLocation: String + + @get:Config.Key("cryptoFilesLocation") + @get:Config.DefaultValue("src/test/resources/sdk-specifications/features/encryption/assets") + val cryptoFilesLocation: String +} + +val ContractTestConfig: ContractTestKeysConfig = + ConfigFactory.create(ContractTestKeysConfig::class.java, System.getenv()) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/Hooks.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/Hooks.kt new file mode 100644 index 000000000..09ac75512 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/Hooks.kt @@ -0,0 +1,64 @@ +package com.pubnub.contract + +import io.cucumber.java.After +import io.cucumber.java.Before +import io.cucumber.java.Scenario +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.junit.Assert.fail +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +class Hooks { + private val interceptor = HttpLoggingInterceptor() + private val mockPubnubService: MockPubnubService = + Retrofit.Builder() + .client(OkHttpClient.Builder().addInterceptor(interceptor).build()) + .baseUrl("http://" + ContractTestConfig.serverHostPort) + .addConverterFactory(GsonConverterFactory.create()) + .build().create(MockPubnubService::class.java) + + @Before + fun before(scenario: Scenario) { + if (!ContractTestConfig.serverMock) { + return + } + scenario.contractName()?.let { + mockPubnubService.init(options = mapOf("__contract__script__" to it)).execute() + } + } + + @After + fun after(scenario: Scenario) { + if (!ContractTestConfig.serverMock) { + return + } + scenario.contractName()?.let { + val responseBody = mockPubnubService.expect().execute().body() + if (responseBody == null) { + fail("Expect response body is null") + } else { + if (responseBody.expectations.pending.isNotEmpty() || + responseBody.expectations.failed.isNotEmpty() + ) { + fail( + """ + Scenario ${responseBody.contract} considered failure: + pending - ${responseBody.expectations.pending.joinToString()}, + failed - ${responseBody.expectations.failed.joinToString()} + """.trimIndent(), + ) + } + } + } + } + + private fun Scenario.contractName(): String? { + return sourceTagNames + .filter { it: String -> it.startsWith("@contract") } + .map { it: String -> + it.split("=").toTypedArray()[1] + } + .firstOrNull() + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/MockPubnubService.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/MockPubnubService.kt new file mode 100644 index 000000000..0b120cb05 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/MockPubnubService.kt @@ -0,0 +1,25 @@ +package com.pubnub.contract + +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.QueryMap + +interface MockPubnubService { + @GET("init") + fun init( + @QueryMap options: Map, + ): Call + + @GET("expect") + fun expect(): Call +} + +data class ExpectResponse( + val contract: String, + val expectations: Expectations, +) { + data class Expectations( + val pending: List, + val failed: List, + ) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/RunMainCucumberTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/RunMainCucumberTest.kt new file mode 100644 index 000000000..f475be2ef --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/RunMainCucumberTest.kt @@ -0,0 +1,21 @@ +package com.pubnub.contract + +import io.cucumber.junit.Cucumber +import io.cucumber.junit.CucumberOptions +import org.junit.runner.RunWith + +@RunWith(Cucumber::class) +@CucumberOptions( + features = ["src/test/resources/sdk-specifications/features"], + tags = "not @skip and not @na=kotlin and not @beta", + plugin = ["pretty", "summary", "junit:build/reports/cucumber-reports/main.xml"], +) +class RunMainCucumberTest + +@RunWith(Cucumber::class) +@CucumberOptions( + features = ["src/test/resources/sdk-specifications/features"], + tags = "not @skip and not @na=kotlin and @beta", + plugin = ["pretty", "summary", "junit:build/reports/cucumber-reports/beta.xml"], +) +class RunBetaCucumberTest diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/Utils.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/Utils.kt new file mode 100644 index 000000000..f991e20d9 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/Utils.kt @@ -0,0 +1,32 @@ +package com.pubnub.contract + +import com.google.gson.Gson +import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata +import com.pubnub.api.models.consumer.objects.member.PNMember +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembership +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata +import java.io.File +import java.nio.file.Files +import java.nio.file.Paths + +val gson = Gson() + +fun readDataFile(personaName: String) = + File( + "${ContractTestConfig.dataFilesLocation}/${personaName.lowercase()}.json", + ).readText(Charsets.UTF_8) + +inline fun readResourceFromDataFile(dataFileName: String): T = gson.fromJson(readDataFile(dataFileName), T::class.java) + +fun loadPersonaUUIDMetadata(personaName: String): PNUUIDMetadata = readResourceFromDataFile(personaName) + +fun loadChannelMetadata(channelName: String): PNChannelMetadata = readResourceFromDataFile(channelName) + +fun loadChannelMembership(membershipName: String): PNChannelMembership = readResourceFromDataFile(membershipName) + +fun loadMember(memberName: String): PNMember = readResourceFromDataFile(memberName) + +fun getFileContentAsByteArray(fileName: String): ByteArray { + val cryptoFileLocation = "${ContractTestConfig.cryptoFilesLocation}" + return Files.readAllBytes(Paths.get(cryptoFileLocation, fileName)) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/parameter/PermissionType.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/parameter/PermissionType.kt new file mode 100644 index 000000000..a3d091235 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/parameter/PermissionType.kt @@ -0,0 +1,18 @@ +package com.pubnub.contract.access.parameter + +import io.cucumber.java.ParameterType + +enum class PermissionType { + READ, + WRITE, + GET, + MANAGE, + UPDATE, + JOIN, + DELETE, +} + +@ParameterType(".*") +fun permissionType(name: String): PermissionType { + return PermissionType.valueOf(name) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/parameter/ResourceType.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/parameter/ResourceType.kt new file mode 100644 index 000000000..99e4a4e44 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/parameter/ResourceType.kt @@ -0,0 +1,31 @@ +package com.pubnub.contract.access.parameter + +import com.pubnub.api.models.consumer.access_manager.v3.PNToken +import io.cucumber.java.ParameterType + +enum class ResourceType { + CHANNEL, + CHANNEL_GROUP, + UUID, +} + +@ParameterType(".*") +fun resourceType(name: String): ResourceType { + return ResourceType.valueOf(name) +} + +fun PNToken.resourcePermissionsMap(resourceType: ResourceType): Map { + return when (resourceType) { + ResourceType.CHANNEL -> resources.channels + ResourceType.CHANNEL_GROUP -> resources.channelGroups + ResourceType.UUID -> resources.uuids + } +} + +fun PNToken.patternPermissionsMap(resourceType: ResourceType): Map { + return when (resourceType) { + ResourceType.CHANNEL -> patterns.channels + ResourceType.CHANNEL_GROUP -> patterns.channelGroups + ResourceType.UUID -> patterns.uuids + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/parameter/TTLType.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/parameter/TTLType.kt new file mode 100644 index 000000000..bc7e89a66 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/parameter/TTLType.kt @@ -0,0 +1,8 @@ +package com.pubnub.contract.access.parameter + +import io.cucumber.java.ParameterType + +@ParameterType(".*") +fun ttl(ttl: String): Long { + return ttl.toLong() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/state/GrantTokenState.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/state/GrantTokenState.kt new file mode 100644 index 000000000..2ab21d487 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/state/GrantTokenState.kt @@ -0,0 +1,38 @@ +package com.pubnub.contract.access.state + +import com.pubnub.api.models.consumer.access_manager.v3.PNGrant +import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult +import com.pubnub.api.models.consumer.access_manager.v3.PNToken + +class GrantTokenState { + var parsedToken: PNToken? = null + var TTL: Long? = null + var result: PNGrantTokenResult? = null + var authorizedUUID: String? = null + val definedGrants: MutableList = mutableListOf() + var currentGrant: FutureCallGrant? = null + set(value) { + if (value != null) { + definedGrants.add(value) + } + field = value + } + var currentResourcePermissions: PNToken.PNResourcePermissions? = null +} + +class FutureCallGrant( + private val initGrant: PNGrant, +) { + private val actions: MutableList<(PNGrant) -> PNGrant> = mutableListOf() + + fun addAction(action: (PNGrant) -> PNGrant): FutureCallGrant { + actions.add(action) + return this + } + + fun evaluate(): PNGrant { + return actions.fold(initGrant) { acc, act -> + act(acc) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/step/GivenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/step/GivenSteps.kt new file mode 100644 index 000000000..7a02014b5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/step/GivenSteps.kt @@ -0,0 +1,278 @@ +@file:Suppress("INVISIBLE_REFERENCE") + +package com.pubnub.contract.access.step + +import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant +import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant +import com.pubnub.api.models.consumer.access_manager.v3.PNChannelGroupPatternGrant +import com.pubnub.api.models.consumer.access_manager.v3.PNChannelGroupResourceGrant +import com.pubnub.api.models.consumer.access_manager.v3.PNChannelPatternGrant +import com.pubnub.api.models.consumer.access_manager.v3.PNChannelResourceGrant +import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult +import com.pubnub.api.models.consumer.access_manager.v3.PNUUIDPatternGrant +import com.pubnub.api.models.consumer.access_manager.v3.PNUUIDResourceGrant +import com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant +import com.pubnub.contract.access.parameter.PermissionType +import com.pubnub.contract.access.parameter.ResourceType +import com.pubnub.contract.access.state.FutureCallGrant +import com.pubnub.contract.access.state.GrantTokenState +import com.pubnub.contract.state.World +import io.cucumber.java.en.And +import io.cucumber.java.en.Given +import kotlin.random.Random + +class GivenSteps(private val grantTokenState: GrantTokenState, private val world: World) { + private val tokenWithAll = + "qEF2AkF0GmEI03xDdHRsGDxDcmVzpURjaGFuoWljaGFubmVsLTEY70NncnChb2NoYW5uZWxfZ3JvdXAtMQVDdXNyoENzcGOgRHV1aWShZnV1aWQtMRhoQ3BhdKVEY2hhbqFtXmNoYW5uZWwtXFMqJBjvQ2dycKF0XjpjaGFubmVsX2dyb3VwLVxTKiQFQ3VzcqBDc3BjoER1dWlkoWpedXVpZC1cUyokGGhEbWV0YaBEdXVpZHR0ZXN0LWF1dGhvcml6ZWQtdXVpZENzaWdYIPpU-vCe9rkpYs87YUrFNWkyNq8CVvmKwEjVinnDrJJc" + + @Given("I have a known token containing UUID pattern Permissions") + fun i_have_a_known_token_containing_uuid_pattern_permissions() { + grantTokenState.result = PNGrantTokenResult(tokenWithAll) + } + + @Given("I have a known token containing UUID resource permissions") + fun i_have_a_known_token_containing_uuid_resource_permissions() { + grantTokenState.result = PNGrantTokenResult(tokenWithAll) + } + + @Given("I have a known token containing an authorized UUID") + fun i_have_a_known_token_containing_an_authorized_uuid() { + grantTokenState.result = PNGrantTokenResult(tokenWithAll) + } + + @Given("the {string} {resourceType} pattern access permissions") + fun the_channel_pattern_access_permissions( + pattern: String, + resourceType: ResourceType, + ) { + when (resourceType) { + ResourceType.CHANNEL -> grantTokenState.currentGrant = FutureCallGrant(ChannelGrant.pattern(pattern)) + ResourceType.CHANNEL_GROUP -> + grantTokenState.currentGrant = + FutureCallGrant(ChannelGroupGrant.pattern(pattern)) + + ResourceType.UUID -> grantTokenState.currentGrant = FutureCallGrant(UUIDGrant.pattern(pattern)) + } + } + + @Given("the {string} {resourceType} resource access permissions") + fun the_channel_resource_access_permissions( + name: String, + resourceType: ResourceType, + ) { + when (resourceType) { + ResourceType.CHANNEL -> grantTokenState.currentGrant = FutureCallGrant(ChannelGrant.name(name)) + ResourceType.CHANNEL_GROUP -> grantTokenState.currentGrant = FutureCallGrant(ChannelGroupGrant.id(name)) + ResourceType.UUID -> grantTokenState.currentGrant = FutureCallGrant(UUIDGrant.id(name)) + } + } + + @Given("the TTL {ttl}") + fun the_ttl(ttl: Long) { + grantTokenState.TTL = ttl + } + + @And("grant resource permission {permissionType}") + fun grant_resource_permission(permissionType: PermissionType) { + grant_permission(permissionType) + } + + @And("grant pattern permission {permissionType}") + fun grant_pattern_permission(permissionType: PermissionType) { + grant_permission(permissionType) + } + + @Given("deny resource permission GET") + fun deny_resource_permission_get() { + // in grant token everything is denied by default + } + + @Given("a token") + fun a_token() { + val characters: List = ('a'..'z') + ('A'..'Z') + ('0'..'9') + '-' + world.tokenString = + (1..30).map { Random.nextInt(0, characters.size) }.map { characters[it] }.joinToString("") + } + + @Given("a valid token with permissions to publish with channel {string}") + fun a_valid_token_with_permissions_to_publish_with_channel( + @Suppress("UNUSED_PARAMETER") string: String?, + ) { + return a_token() + } + + @Given("an expired token with permissions to publish with channel {string}") + fun an_expired_token_with_permissions_to_publish_with_channel( + @Suppress("UNUSED_PARAMETER") string: String, + ) { + return a_token() + } + + @Given("the token string {string}") + fun the_token_string(string: String) { + world.tokenString = string + } + + private fun grant_permission(permissionType: PermissionType) { + when (permissionType) { + PermissionType.READ -> + grantTokenState.currentGrant?.let { futureCall -> + futureCall.addAction { + when (it) { + is PNChannelPatternGrant -> { + it.copy(read = true) + } + + is PNChannelResourceGrant -> { + it.copy(read = true) + } + + is PNChannelGroupPatternGrant -> { + it.copy(read = true) + } + + is PNChannelGroupResourceGrant -> { + it.copy(read = true) + } + + else -> throw RuntimeException() + } + } + } + + PermissionType.WRITE -> + grantTokenState.currentGrant?.let { futureCall -> + futureCall.addAction { + when (it) { + is PNChannelResourceGrant -> { + it.copy(write = true) + } + + is PNChannelPatternGrant -> { + it.copy(write = true) + } + + else -> throw RuntimeException() + } + } + } + + PermissionType.GET -> + grantTokenState.currentGrant?.let { futureCall -> + futureCall.addAction { + when (it) { + is PNChannelResourceGrant -> { + it.copy(get = true) + } + + is PNChannelPatternGrant -> { + it.copy(get = true) + } + + is PNUUIDResourceGrant -> { + it.copy(get = true) + } + + is PNUUIDPatternGrant -> { + it.copy(get = true) + } + + else -> throw RuntimeException() + } + } + } + + PermissionType.MANAGE -> + grantTokenState.currentGrant?.let { futureCall -> + futureCall.addAction { + when (it) { + is PNChannelPatternGrant -> { + it.copy(manage = true) + } + + is PNChannelResourceGrant -> { + it.copy(manage = true) + } + + is PNChannelGroupPatternGrant -> { + it.copy(manage = true) + } + + is PNChannelGroupResourceGrant -> { + it.copy(manage = true) + } + + else -> throw RuntimeException() + } + } + } + + PermissionType.UPDATE -> + grantTokenState.currentGrant?.let { futureCall -> + futureCall.addAction { + when (it) { + is PNChannelResourceGrant -> { + it.copy(update = true) + } + + is PNChannelPatternGrant -> { + it.copy(update = true) + } + + is PNUUIDResourceGrant -> { + it.copy(update = true) + } + + is PNUUIDPatternGrant -> { + it.copy(update = true) + } + + else -> throw RuntimeException() + } + } + } + + PermissionType.JOIN -> + grantTokenState.currentGrant?.let { futureCall -> + futureCall.addAction { + when (it) { + is PNChannelResourceGrant -> { + it.copy(join = true) + } + + is PNChannelPatternGrant -> { + it.copy(join = true) + } + + else -> throw RuntimeException() + } + } + } + + PermissionType.DELETE -> + grantTokenState.currentGrant?.let { futureCall -> + futureCall.addAction { + when (it) { + is PNChannelResourceGrant -> { + it.copy(delete = true) + } + + is PNChannelPatternGrant -> { + it.copy(delete = true) + } + + is PNUUIDResourceGrant -> { + it.copy(delete = true) + } + + is PNUUIDPatternGrant -> { + it.copy(delete = true) + } + + else -> throw RuntimeException() + } + } + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/step/ThenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/step/ThenSteps.kt new file mode 100644 index 000000000..d70300ee3 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/step/ThenSteps.kt @@ -0,0 +1,112 @@ +package com.pubnub.contract.access.step + +import com.pubnub.api.models.consumer.access_manager.v3.PNToken +import com.pubnub.contract.access.parameter.PermissionType +import com.pubnub.contract.access.parameter.ResourceType +import com.pubnub.contract.access.parameter.patternPermissionsMap +import com.pubnub.contract.access.parameter.resourcePermissionsMap +import com.pubnub.contract.access.state.GrantTokenState +import com.pubnub.contract.state.World +import io.cucumber.java.en.Then +import org.hamcrest.MatcherAssert +import org.hamcrest.Matchers + +class ThenSteps( + private val grantTokenState: GrantTokenState, + private val world: World, +) { + @Then("the authorized UUID {string}") + fun authorized_uuid(uuid: String) { + grantTokenState.authorizedUUID = uuid + } + + @Then("the parsed token output contains the authorized UUID {string}") + fun the_parsed_token_output_contains_the_authorized_uuid(uuid: String) { + val token = grantTokenState.parsedToken!! + MatcherAssert.assertThat(token.authorizedUUID, Matchers.`is`(uuid)) + } + + @Then("the token contains the authorized UUID {string}") + fun the_token_contains_the_authorized_uuid(uuid: String) { + val result = grantTokenState.result!! + val parsedToken = world.pubnub.parseToken(result.token) + MatcherAssert.assertThat(parsedToken.authorizedUUID, Matchers.`is`(uuid)) + } + + @Then("the token contains the TTL {ttl}") + fun the_token_contains_the_ttl(ttl: Long) { + val result = grantTokenState.result!! + val token = world.pubnub.parseToken(result.token) + MatcherAssert.assertThat(token.ttl, Matchers.`is`(ttl)) + } + + @Then("the token does not contain an authorized uuid") + fun the_token_does_not_contain_an_authorized_uuid() { + val result = grantTokenState.result!! + val token = world.pubnub.parseToken(result.token) + MatcherAssert.assertThat(token.authorizedUUID, Matchers.nullValue()) + } + + @Then("the token has {string} {resourceType} resource access permissions") + fun the_token_has_channel_resource_access_permissions( + name: String, + resourceType: ResourceType, + ) { + val token = parsedToken()!! + val permissions = token.resourcePermissionsMap(resourceType)[name] + MatcherAssert.assertThat( + "Token doesn't contain required permissions $token", + permissions, + Matchers.notNullValue(), + ) + grantTokenState.currentResourcePermissions = permissions + } + + @Then("the token has {string} {resourceType} pattern access permissions") + fun the_token_has_channel_pattern_access_permissions( + name: String, + resourceType: ResourceType, + ) { + val token = parsedToken()!! + val permissions = token.patternPermissionsMap(resourceType)[name] + MatcherAssert.assertThat( + "Token doesn't contain required permissions $token", + permissions, + Matchers.notNullValue(), + ) + grantTokenState.currentResourcePermissions = permissions + } + + @Then("token resource permission {permissionType}") + fun token_resource_permission(permissionType: PermissionType) { + assertPermissions(permissionType) + } + + @Then("token pattern permission {permissionType}") + fun token_pattern_permission(permissionType: PermissionType) { + assertPermissions(permissionType) + } + + @Then("I get confirmation that token has been revoked") + fun i_get_confirmation_that_token_has_been_revoked() { + MatcherAssert.assertThat(world.pnException, Matchers.nullValue()) + } + + private fun assertPermissions(permissionType: PermissionType) { + val permissions = grantTokenState.currentResourcePermissions!! + when (permissionType) { + PermissionType.READ -> MatcherAssert.assertThat(permissions.read, Matchers.`is`(true)) + PermissionType.WRITE -> MatcherAssert.assertThat(permissions.write, Matchers.`is`(true)) + PermissionType.GET -> MatcherAssert.assertThat(permissions.get, Matchers.`is`(true)) + PermissionType.MANAGE -> MatcherAssert.assertThat(permissions.manage, Matchers.`is`(true)) + PermissionType.UPDATE -> MatcherAssert.assertThat(permissions.update, Matchers.`is`(true)) + PermissionType.JOIN -> MatcherAssert.assertThat(permissions.join, Matchers.`is`(true)) + PermissionType.DELETE -> MatcherAssert.assertThat(permissions.delete, Matchers.`is`(true)) + } + } + + private fun parsedToken(): PNToken? { + return grantTokenState.parsedToken + ?: grantTokenState.result?.let { world.pubnub.parseToken(it.token) } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/step/WhenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/step/WhenSteps.kt new file mode 100644 index 000000000..d035b57c2 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/access/step/WhenSteps.kt @@ -0,0 +1,80 @@ +package com.pubnub.contract.access.step + +import com.pubnub.api.PubNubException +import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant +import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant +import com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant +import com.pubnub.contract.access.state.GrantTokenState +import com.pubnub.contract.state.World +import io.cucumber.java.en.When +import org.junit.Assert + +class WhenSteps( + private val grantTokenState: GrantTokenState, + private val world: World, +) { + @When("I grant a token specifying those permissions") + fun grant_token() { + val definedGrants = grantTokenState.definedGrants.map { it.evaluate() } + @Suppress("DEPRECATION") + grantTokenState.result = + world.pubnub.grantToken( + ttl = grantTokenState.TTL?.toInt() ?: throw RuntimeException("TTL expected"), + authorizedUUID = grantTokenState.authorizedUUID, + channels = definedGrants.filterIsInstance(ChannelGrant::class.java), + channelGroups = definedGrants.filterIsInstance(ChannelGroupGrant::class.java), + uuids = definedGrants.filterIsInstance(UUIDGrant::class.java), + ).sync() + } + + @When("I attempt to grant a token specifying those permissions") + fun i_attempt_to_grant_a_token_specifying_those_permissions() { + try { + grant_token() + Assert.fail("Expected exception") + } catch (ex: PubNubException) { + world.pnException = ex + } catch (ex: AssertionError) { + throw ex + } catch (t: Throwable) { + Assert.fail("Expected PubNubException but got throwable $t") + } + } + + @When("I parse the token") + fun i_parse_the_token() { + grantTokenState.parsedToken = world.pubnub.parseToken(grantTokenState.result?.token!!) + } + + @When("I revoke a token") + fun i_revoke_the_token() { + try { + world.pubnub.revokeToken(world.tokenString!!).sync() + } catch (e: PubNubException) { + world.pnException = e + } + } + + @When("I publish a message using that auth token with channel {string}") + fun i_publish_a_message_using_that_auth_token_with_channel(channel: String) { + world.pubnub.setToken(world.tokenString) + world.pubnub.publish( + channel = channel, + message = "Message", + ).sync() + } + + @When("I attempt to publish a message using that auth token with channel {string}") + fun i_attempt_to_publish_a_message_using_that_auth_token_with_channel(channel: String) { + world.pubnub.setToken(world.tokenString) + + try { + world.pubnub.publish( + channel = channel, + message = "Message", + ).sync() + } catch (e: PubNubException) { + world.pnException = e + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/state/ChannelMetadataState.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/state/ChannelMetadataState.kt new file mode 100644 index 000000000..275ef86ef --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/state/ChannelMetadataState.kt @@ -0,0 +1,10 @@ +package com.pubnub.contract.channelmetadata.state + +import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata + +class ChannelMetadataState { + var channelMetadatas: Collection? = null + var channelMetadata: PNChannelMetadata? = null + lateinit var channelId: String + var name: String? = null +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/GivenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/GivenSteps.kt new file mode 100644 index 000000000..c7c9438e6 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/GivenSteps.kt @@ -0,0 +1,21 @@ +package com.pubnub.contract.channelmetadata.step + +import com.pubnub.contract.channelmetadata.state.ChannelMetadataState +import com.pubnub.contract.loadChannelMetadata +import io.cucumber.java.en.Given + +class GivenSteps( + private val channelMetadataState: ChannelMetadataState, +) { + @Given("the id for {string} channel") + fun the_id_for_channel(channelFileName: String) { + val channel = loadChannelMetadata(channelFileName) + channelMetadataState.channelId = channel.id + } + + @Given("the data for {string} channel") + fun the_data_for_channel(channelFileName: String) { + val channel = loadChannelMetadata(channelFileName) + channelMetadataState.channelMetadata = channel + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/ThenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/ThenSteps.kt new file mode 100644 index 000000000..039a74122 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/ThenSteps.kt @@ -0,0 +1,38 @@ +package com.pubnub.contract.channelmetadata.step + +import com.pubnub.contract.channelmetadata.state.ChannelMetadataState +import com.pubnub.contract.loadChannelMetadata +import io.cucumber.java.en.Then +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers +import org.hamcrest.core.Is.`is` as iz + +class ThenSteps( + private val channelMetadataState: ChannelMetadataState, +) { + @Then("the channel metadata for {string} channel") + fun the_channel_metadata_for_channel(channelFileName: String) { + val channelMetadata = loadChannelMetadata(channelFileName) + assertThat(channelMetadataState.channelMetadata, iz(channelMetadata)) + } + + @Then("the channel metadata for {string} channel contains updated") + fun the_channel_metadata_for_channel_contains_updated( + @Suppress("UNUSED_PARAMETER") channelFileName: String, + ) { + assertThat(channelMetadataState.channelMetadata?.updated, iz(Matchers.notNullValue())) + } + + @Then("the response contains list with {string} and {string} channel metadata") + fun the_response_contains_list_with_and_channel_metadata( + firstChannelName: String, + secondChannelName: String, + ) { + val firstChannel = loadChannelMetadata(firstChannelName) + val secondChannel = loadChannelMetadata(secondChannelName) + assertThat( + channelMetadataState.channelMetadatas, + Matchers.containsInAnyOrder(firstChannel, secondChannel), + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/WhenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/WhenSteps.kt new file mode 100644 index 000000000..b3f63ac57 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/channelmetadata/step/WhenSteps.kt @@ -0,0 +1,71 @@ +package com.pubnub.contract.channelmetadata.step + +import com.pubnub.contract.channelmetadata.state.ChannelMetadataState +import com.pubnub.contract.state.World +import io.cucumber.java.en.When + +class WhenSteps( + private val world: World, + private val channelMetadataState: ChannelMetadataState, +) { + @When("I get the channel metadata") + fun i_get_the_channel_metadata() { + world.pubnub.getChannelMetadata(channel = channelMetadataState.channelId).sync()?.let { + channelMetadataState.channelMetadata = it.data + world.responseStatus = it.status + } + } + + @When("I set the channel metadata") + fun i_set_the_channel_metadata() { + val channelMetadata = channelMetadataState.channelMetadata!! + world.pubnub.setChannelMetadata( + channel = channelMetadata.id, + 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 + } + } + + @When("I get the channel metadata with custom") + fun i_get_the_channel_metadata_with_custom() { + world.pubnub.getChannelMetadata( + channel = channelMetadataState.channelId, + includeCustom = true, + ).sync().let { + channelMetadataState.channelMetadata = it.data + world.responseStatus = it.status + } + } + + @When("I remove the channel metadata") + fun i_remove_the_channel_metadata() { + world.responseStatus = + world.pubnub.removeChannelMetadata( + channel = channelMetadataState.channelId, + ).sync().status + } + + @When("I get all channel metadata") + fun i_get_all_channel_metadata() { + world.pubnub.getAllChannelMetadata().sync().let { + channelMetadataState.channelMetadatas = it.data + world.responseStatus = it.status + } + } + + @When("I get all channel metadata with custom") + fun i_get_all_channel_metadata_with_custom() { + world.pubnub.getAllChannelMetadata( + includeCustom = true, + ).sync().let { + channelMetadataState.channelMetadatas = it.data + world.responseStatus = it.status + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/crypto/CryptoModuleState.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/crypto/CryptoModuleState.kt new file mode 100644 index 000000000..2bf1821c1 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/crypto/CryptoModuleState.kt @@ -0,0 +1,16 @@ +package com.pubnub.contract.crypto + +import com.pubnub.api.PubNubError + +class CryptoModuleState { + var defaultCryptorType: String? = null + var decryptionOnlyCryptorType: String? = null + var cryptorCipherKey: String? = null + var initializationVectorType: String? = null + var decryptionError: PubNubError? = null + var encryptionError: PubNubError? = null + var encryptedData: ByteArray? = null + var decryptedData: ByteArray? = null + var fileContent: ByteArray? = null + var encryptionType: String? = null +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/crypto/CryptoModuleSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/crypto/CryptoModuleSteps.kt new file mode 100644 index 000000000..9f198d5c4 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/crypto/CryptoModuleSteps.kt @@ -0,0 +1,243 @@ +package com.pubnub.contract.crypto + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.api.crypto.cryptor.Cryptor +import com.pubnub.contract.getFileContentAsByteArray +import com.pubnub.internal.crypto.cryptor.AesCbcCryptor +import com.pubnub.internal.crypto.cryptor.LegacyCryptor +import com.pubnub.internal.vendor.FileEncryptionUtil +import io.cucumber.java.en.Given +import io.cucumber.java.en.Then +import io.cucumber.java.en.When +import org.junit.jupiter.api.Assertions.assertArrayEquals +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertTrue +import java.io.ByteArrayInputStream + +private const val LEGACY_NEW = "legacy" +private const val AES_CBC = "acrh" +private const val RANDOM_IV = "random" +private const val CRYPTION_TYPE_BINARY = "binary" // not a stream +private const val CRYPTION_TYPE_STREAM = "stream" + +class CryptoModuleSteps( + private val cryptoModuleState: CryptoModuleState, +) { + @Given("Crypto module with {string} cryptor") + fun crypto_module_with_cryptor(cryptorType: String) { + cryptoModuleState.defaultCryptorType = cryptorType + } + + @Given("with {string} cipher key") + fun cryptor_with_cipher_key(cipherKey: String) { + cryptoModuleState.cryptorCipherKey = cipherKey + } + + @Given("with {string} vector") + fun cryptor_with_initialization_vector(initializationVectorType: String) { + cryptoModuleState.initializationVectorType = initializationVectorType + } + + @Suppress("UNUSED_PARAMETER") + @Given("Legacy code with {string} cipher key and {string} vector") + fun legacy_code_with_cipher_key_and_vector( + cipherKey: String, + initializationVectorType: String, + ) { + // this is fine, nothing here + } + + @Given("Crypto module with default {string} and additional {string} cryptors") + fun crypto_module_with_default_cryptor_and_additional_cryptor( + defaultCryptorType: String, + decryptionCryptorType: String, + ) { + cryptoModuleState.defaultCryptorType = defaultCryptorType + cryptoModuleState.decryptionOnlyCryptorType = decryptionCryptorType + } + + @When("I decrypt {string} file") + fun I_decrypt_file(fileName: String) { + val encryptedFileContent = getFileContentAsByteArray(fileName) + var cryptoModule: CryptoModule? = null + if (cryptoModuleState.defaultCryptorType == AES_CBC) { + cryptoModule = CryptoModule.createNewCryptoModule(AesCbcCryptor(cryptoModuleState.cryptorCipherKey!!)) + } else if (cryptoModuleState.defaultCryptorType == LEGACY_NEW) { + cryptoModule = CryptoModule.createNewCryptoModule(LegacyCryptor(cryptoModuleState.cryptorCipherKey!!)) + } + + try { + cryptoModule?.decrypt(encryptedData = encryptedFileContent) + } catch (e: PubNubException) { + cryptoModuleState.decryptionError = e.pubnubError + } + } + + @When("I encrypt {string} file as {string}") + fun I_encrypt_file( + fileName: String, + encryptionType: String, + ) { + val notEncryptedFileContent = getFileContentAsByteArray(fileName) + cryptoModuleState.fileContent = notEncryptedFileContent + cryptoModuleState.encryptionType = encryptionType + val cryptoModule = createCryptoModuleForEncryption() + var encryptedData: ByteArray = byteArrayOf() + try { + encryptedData = + when (encryptionType) { + CRYPTION_TYPE_BINARY -> cryptoModule.encrypt(notEncryptedFileContent) + CRYPTION_TYPE_STREAM -> cryptoModule.encryptStream(notEncryptedFileContent.inputStream()).readBytes() + else -> throw PubNubException("Invalid encryptionType type. Should be binary or stream") + } + } catch (e: PubNubException) { + cryptoModuleState.encryptionError = e.pubnubError + } + cryptoModuleState.encryptedData = encryptedData + } + + private fun createCryptoModuleForEncryption(): CryptoModule { + val randoIv: Boolean = cryptoModuleState.initializationVectorType == RANDOM_IV + + val defaultCryptorType = cryptoModuleState.defaultCryptorType + val cryptor = createCryptor(defaultCryptorType!!, cryptoModuleState.cryptorCipherKey!!, randoIv) + val cryptoModule = CryptoModule.createNewCryptoModule(cryptor) + return cryptoModule + } + + @When("I decrypt {string} file as {string}") + fun I_decrypt_file_as_binary( + encryptedFile: String, + decryptionType: String, + ) { + val cryptoModule: CryptoModule = createCryptoModuleForDecryption() + + val encryptedFileContent = getFileContentAsByteArray(encryptedFile) + var decryptedData = ByteArray(0) + try { + decryptedData = + when (decryptionType) { + CRYPTION_TYPE_BINARY -> cryptoModule.decrypt(encryptedFileContent) + CRYPTION_TYPE_STREAM -> cryptoModule.decryptStream(encryptedFileContent.inputStream()).readBytes() + else -> throw PubNubException("Invalid decryptionType type. Should be binary or stream") + } + } catch (e: PubNubException) { + cryptoModuleState.decryptionError = e.pubnubError + } + cryptoModuleState.decryptedData = decryptedData + } + + private fun createCryptoModuleForDecryption(): CryptoModule { + val defaultCryptorType = cryptoModuleState.defaultCryptorType + val cipherKey = cryptoModuleState.cryptorCipherKey!! + val randoIv: Boolean = cryptoModuleState.initializationVectorType == RANDOM_IV + val cryptoModule: CryptoModule + if (cryptoModuleState.decryptionOnlyCryptorType == null) { + cryptoModule = + when (defaultCryptorType) { + LEGACY_NEW -> { + CryptoModule.createNewCryptoModule(LegacyCryptor(cipherKey, randoIv)) + } + + AES_CBC -> { + CryptoModule.createNewCryptoModule(AesCbcCryptor(cipherKey)) + } + + else -> throw PubNubException("Invalid cryptor type") + } + } else { + val decryptionOnlyCryptorType = cryptoModuleState.decryptionOnlyCryptorType + val defaultCryptor = createCryptor(defaultCryptorType!!, cipherKey, randoIv) + val decryptionOnlyCryptor = createCryptor(decryptionOnlyCryptorType!!, cipherKey, randoIv) + cryptoModule = CryptoModule.createNewCryptoModule(defaultCryptor, listOf(decryptionOnlyCryptor)) + } + return cryptoModule + } + + private fun createCryptor( + cryptorType: String, + cipherKey: String, + useRandomIv: Boolean, + ): Cryptor { + return when (cryptorType) { + LEGACY_NEW -> { + LegacyCryptor(cipherKey, useRandomIv) + } + + AES_CBC -> { + AesCbcCryptor(cipherKey) + } + + else -> { + throw PubNubException("Invalid cryptor type") + } + } + } + + @Then("I receive {string}") + fun I_receive_outcome(outcome: String) { + when (outcome) { + "unknown cryptor error" -> { + assertTrue( + cryptoModuleState.decryptionError == PubNubError.UNKNOWN_CRYPTOR || cryptoModuleState.decryptionError == PubNubError.CRYPTOR_HEADER_VERSION_UNKNOWN, + ) + } + + "decryption error" -> { + val isDecryptionError01 = + PubNubError.CRYPTOR_DATA_HEADER_SIZE_TO_SMALL == cryptoModuleState.decryptionError + val isDecryptionError02 = + PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED == cryptoModuleState.decryptionError + val isDecryptionError03 = PubNubError.UNKNOWN_CRYPTOR == cryptoModuleState.decryptionError + assertTrue(isDecryptionError01 || isDecryptionError02 || isDecryptionError03) + } + + "success" -> assertNull(cryptoModuleState.decryptionError) + "encryption error" -> + assertEquals( + PubNubError.ENCRYPTION_AND_DECRYPTION_OF_EMPTY_DATA_NOT_ALLOWED, + cryptoModuleState.encryptionError, + ) + } + } + + @Then("Successfully decrypt an encrypted file with legacy code") + fun successfully_decrypt_an_encrypted_file_with_legacy_code() { + val encryptedData = cryptoModuleState.encryptedData + val encryptedDataAsStringBase64 = + String(com.pubnub.internal.vendor.Base64.encode(encryptedData, com.pubnub.internal.vendor.Base64.NO_WRAP)) + val randoIv: Boolean = cryptoModuleState.initializationVectorType == RANDOM_IV + val cipherKey = cryptoModuleState.cryptorCipherKey + + val encryptionType = cryptoModuleState.encryptionType + val decryptedDataAsString: String = + when (encryptionType) { + CRYPTION_TYPE_BINARY -> { + val crypto = com.pubnub.internal.vendor.Crypto(cipherKey, randoIv) + crypto.decrypt(encryptedDataAsStringBase64) + } + + CRYPTION_TYPE_STREAM -> { + val byteArrayInputStream = ByteArrayInputStream(encryptedData) + val decryptedStreamAsByteArray = + FileEncryptionUtil.decrypt(byteArrayInputStream, cipherKey!!).readBytes() + String(decryptedStreamAsByteArray) + } + + else -> { + throw PubNubException("Invalid cryptor type") + } + } + + assertEquals(String(cryptoModuleState.fileContent!!), decryptedDataAsString) + } + + @Then("Decrypted file content equal to the {string} file content") + fun decrypted_file_content_equal_to_the_source_file_content(sourceFileName: String) { + val sourceFileContent = getFileContentAsByteArray(sourceFileName) + assertArrayEquals(sourceFileContent, cryptoModuleState.decryptedData) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/state/MemberState.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/state/MemberState.kt new file mode 100644 index 000000000..fa1f6f701 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/state/MemberState.kt @@ -0,0 +1,14 @@ +package com.pubnub.contract.member.state + +import com.pubnub.api.models.consumer.objects.member.PNMember +import com.pubnub.contract.channelmetadata.state.ChannelMetadataState + +class MemberState( + private val channelMetadataState: ChannelMetadataState, +) { + fun channelId(): String = channelMetadataState.channelId + + val members: MutableCollection = mutableListOf() + val membersToRemove: MutableCollection = mutableListOf() + lateinit var returnedMembers: Collection +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/step/GivenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/step/GivenSteps.kt new file mode 100644 index 000000000..65bee1582 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/step/GivenSteps.kt @@ -0,0 +1,19 @@ +package com.pubnub.contract.member.step + +import com.pubnub.contract.loadMember +import com.pubnub.contract.member.state.MemberState +import io.cucumber.java.en.Given + +class GivenSteps( + private val memberState: MemberState, +) { + @Given("the data for {string} member") + fun the_data_for_member(memberName: String) { + memberState.members.add(loadMember(memberName)) + } + + @Given("the data for {string} member that we want to remove") + fun the_data_for_member_that_we_want_to_delete(memberName: String) { + memberState.membersToRemove.add(loadMember(memberName)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/step/ThenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/step/ThenSteps.kt new file mode 100644 index 000000000..31a77eafb --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/step/ThenSteps.kt @@ -0,0 +1,33 @@ +package com.pubnub.contract.member.step + +import com.pubnub.contract.loadMember +import com.pubnub.contract.member.state.MemberState +import io.cucumber.java.en.Then +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers + +class ThenSteps( + private val memberState: MemberState, +) { + @Then("the response contains list with {string} and {string} members") + fun the_response_contains_list_with_and_members( + firstMemberName: String, + secondMemberName: String, + ) { + val firstMember = loadMember(firstMemberName) + val secondMember = loadMember(secondMemberName) + assertThat(memberState.returnedMembers, Matchers.containsInAnyOrder(firstMember, secondMember)) + } + + @Then("the response contains list with {string} member") + fun the_response_contains_list_with_member(memberName: String) { + val member = loadMember(memberName) + assertThat(memberState.returnedMembers, Matchers.hasItem(member)) + } + + @Then("the response does not contain list with {string} member") + fun the_response_does_not_contain_list_with_member(memberName: String) { + val member = loadMember(memberName) + assertThat(memberState.returnedMembers, Matchers.not(Matchers.hasItem(member))) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/step/WhenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/step/WhenSteps.kt new file mode 100644 index 000000000..7afd542c2 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/member/step/WhenSteps.kt @@ -0,0 +1,84 @@ +package com.pubnub.contract.member.step + +import com.pubnub.api.models.consumer.objects.member.PNMember +import com.pubnub.api.models.consumer.objects.member.PNUUIDDetailsLevel +import com.pubnub.contract.member.state.MemberState +import com.pubnub.contract.state.World +import io.cucumber.java.en.When + +class WhenSteps( + private val world: World, + private val memberState: MemberState, +) { + @When("I get the channel members") + fun i_get_the_channel_members() { + world.pubnub.getChannelMembers(channel = memberState.channelId()).sync().let { + memberState.returnedMembers = it.data + world.responseStatus = it.status + } + } + + @When("I set a channel member including custom and UUID with custom") + fun i_set_a_channel_member_including_custom_and_uuid_with_custom() { + val uuids = memberState.members.map { PNMember.Partial(uuidId = it.uuid?.id!!) } + world.pubnub.setChannelMembers( + channel = memberState.channelId(), + includeCustom = true, + includeUUIDDetails = PNUUIDDetailsLevel.UUID_WITH_CUSTOM, + uuids = uuids, + ).sync().let { + memberState.returnedMembers = it.data + world.responseStatus = it.status + } + } + + @When("I get the channel members including custom and UUID custom information") + fun i_get_the_channel_members_including_custom_and_uuid_custom_information() { + world.pubnub.getChannelMembers( + channel = memberState.channelId(), + includeCustom = true, + includeUUIDDetails = PNUUIDDetailsLevel.UUID_WITH_CUSTOM, + ).sync().let { + memberState.returnedMembers = it.data + world.responseStatus = it.status + } + } + + @When("I set a channel member") + fun i_set_a_channel_member() { + val uuids = memberState.members.map { PNMember.Partial(uuidId = it.uuid.id) } + world.pubnub.setChannelMembers( + channel = memberState.channelId(), + uuids = uuids, + ).sync().let { + memberState.returnedMembers = it.data + world.responseStatus = it.status + } + } + + @When("I remove a channel member") + fun i_remove_a_channel_member() { + val uuids = memberState.membersToRemove.map { it.uuid.id } + world.pubnub.removeChannelMembers( + channel = memberState.channelId(), + uuids = uuids, + ).sync().let { + memberState.returnedMembers = it.data + world.responseStatus = it.status + } + } + + @When("I manage channel members") + fun i_manage_channel_members() { + val uuidsToSet = memberState.members.map { PNMember.Partial(uuidId = it.uuid.id) } + val uuidsToRemove = memberState.membersToRemove.map { it.uuid.id } + world.pubnub.manageChannelMembers( + channel = memberState.channelId(), + uuidsToSet = uuidsToSet, + uuidsToRemove = uuidsToRemove, + ).sync().let { + memberState.returnedMembers = it.data + world.responseStatus = it.status + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/state/MembershipState.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/state/MembershipState.kt new file mode 100644 index 000000000..3526b05ba --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/state/MembershipState.kt @@ -0,0 +1,13 @@ +package com.pubnub.contract.uuidmetadata.state + +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembership + +class MembershipState( + private val uuidMetadataState: UUIDMetadataState, +) { + fun uuid(): String? = uuidMetadataState.uuid + + lateinit var returnedMemberships: Collection + val membershipsToRemove: MutableCollection = mutableListOf() + val memberships: MutableCollection = mutableListOf() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/step/GivenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/step/GivenSteps.kt new file mode 100644 index 000000000..a793d2d9d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/step/GivenSteps.kt @@ -0,0 +1,19 @@ +package com.pubnub.contract.membership.step + +import com.pubnub.contract.loadChannelMembership +import com.pubnub.contract.uuidmetadata.state.MembershipState +import io.cucumber.java.en.Given + +class GivenSteps( + private val membershipState: MembershipState, +) { + @Given("the data for {string} membership") + fun the_data_for_membership(channelMembershipName: String) { + membershipState.memberships.add(loadChannelMembership(channelMembershipName)) + } + + @Given("the data for {string} membership that we want to remove") + fun the_data_for_membership_that_we_want_to_remove(channelMembershipName: String) { + membershipState.membershipsToRemove.add(loadChannelMembership(channelMembershipName)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/step/ThenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/step/ThenSteps.kt new file mode 100644 index 000000000..35b0fc5be --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/step/ThenSteps.kt @@ -0,0 +1,41 @@ +package com.pubnub.contract.membership.step + +import com.pubnub.contract.loadChannelMembership +import com.pubnub.contract.uuidmetadata.state.MembershipState +import io.cucumber.java.en.Then +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.containsInAnyOrder +import org.hamcrest.Matchers.hasItem +import org.hamcrest.Matchers.not + +class ThenSteps( + private val membershipState: MembershipState, +) { + @Then("the response contains list with {string} and {string} memberships") + fun the_response_contains_list_with_and_memberships( + firstChannelMembershipName: String, + secondChannelMembershipName: String, + ) { + val firstChannelMembership = loadChannelMembership(firstChannelMembershipName) + val secondChannelMembership = loadChannelMembership(secondChannelMembershipName) + + assertThat( + membershipState.returnedMemberships, + containsInAnyOrder(firstChannelMembership, secondChannelMembership), + ) + } + + @Then("the response contains list with {string} membership") + fun the_response_contains_list_with_and_membership(channelMembershipName: String) { + val channelMembership = loadChannelMembership(channelMembershipName) + + assertThat(membershipState.returnedMemberships, hasItem(channelMembership)) + } + + @Then("the response does not contain list with {string} membership") + fun the_response_does_not_contain_list_with_patient_membership_membership(channelMembershipName: String) { + val channelMembership = loadChannelMembership(channelMembershipName) + + assertThat(membershipState.returnedMemberships, not(hasItem(channelMembership))) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/step/WhenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/step/WhenSteps.kt new file mode 100644 index 000000000..483e5529a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/membership/step/WhenSteps.kt @@ -0,0 +1,123 @@ +package com.pubnub.contract.membership.step + +import com.pubnub.api.models.consumer.objects.membership.PNChannelDetailsLevel +import com.pubnub.api.models.consumer.objects.membership.PNChannelMembership +import com.pubnub.contract.state.World +import com.pubnub.contract.uuidmetadata.state.MembershipState +import io.cucumber.java.en.When + +class WhenSteps( + private val world: World, + private val membershipState: MembershipState, +) { + @When("I get the memberships") + fun i_get_the_memberships() { + world.pubnub.getMemberships(uuid = membershipState.uuid()).sync().let { + membershipState.returnedMemberships = it.data + world.responseStatus = it.status + } + } + + @When("I get the memberships for current user") + fun i_get_the_memberships_for_current_user() { + world.pubnub.getMemberships().sync().let { + membershipState.returnedMemberships = it.data + world.responseStatus = it.status + } + } + + @When("I get the memberships including custom and channel custom information") + fun i_get_the_memberships_including_custom_and_channel_custom_information() { + world.pubnub.getMemberships( + uuid = membershipState.uuid(), + includeCustom = true, + includeChannelDetails = PNChannelDetailsLevel.CHANNEL_WITH_CUSTOM, + ).sync().let { + membershipState.returnedMemberships = it.data + world.responseStatus = it.status + } + } + + @When("I set the membership") + fun i_set_the_membership() { + val channels = + membershipState.memberships.map { + PNChannelMembership.Partial( + channelId = it.channel.id, + custom = it.custom?.value, + status = it.status?.value, + ) + } + + world.pubnub.setMemberships( + uuid = membershipState.uuid(), + channels = channels, + ).sync().let { + world.responseStatus = it.status + membershipState.returnedMemberships = it.data + } + } + + @When("I set the membership for current user") + fun i_set_the_membership_for_current_user() { + val channels = + membershipState.memberships.map { + PNChannelMembership.Partial( + channelId = it.channel.id, + custom = it.custom?.value, + status = it.status?.value, + ) + } + + world.pubnub.setMemberships( + channels = channels, + ).sync().let { + world.responseStatus = it.status + membershipState.returnedMemberships = it.data + } + } + + @When("I remove the membership") + fun i_remove_the_membership() { + val channels = membershipState.memberships.map { it.channel.id } + world.pubnub.removeMemberships( + uuid = membershipState.uuid(), + channels = channels, + ).sync().let { + world.responseStatus = it.status + membershipState.returnedMemberships = it.data + } + } + + @When("I remove the membership for current user") + fun i_remove_the_membership_for_current_user() { + val channels = membershipState.memberships.map { it.channel.id } + world.pubnub.removeMemberships( + channels = channels, + ).sync().let { + world.responseStatus = it.status + membershipState.returnedMemberships = it.data + } + } + + @When("I manage memberships") + fun i_manage_memberships() { + val channelsToSet = + membershipState.memberships.map { + PNChannelMembership.Partial( + channelId = it.channel.id, + custom = it.custom?.value, + status = it.status?.value, + ) + } + val channelsToRemove = membershipState.membershipsToRemove.map { it.channel.id } + world.pubnub.manageMemberships( + channelsToSet = channelsToSet, + channelsToRemove = channelsToRemove, + uuid = membershipState.uuid(), + ).sync().let { + world.responseStatus = it.status + membershipState.returnedMemberships = it.data + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/parameter/SpaceIdType.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/parameter/SpaceIdType.kt new file mode 100644 index 000000000..3c02addea --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/parameter/SpaceIdType.kt @@ -0,0 +1,9 @@ +package com.pubnub.contract.parameter + +import com.pubnub.api.SpaceId +import io.cucumber.java.ParameterType + +@ParameterType("'(.*)'") +fun spaceId(stringId: String): SpaceId { + return SpaceId(stringId) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/presence/eventEngine/step/PresenceEventEngineSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/presence/eventEngine/step/PresenceEventEngineSteps.kt new file mode 100644 index 000000000..a15e4ce0f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/presence/eventEngine/step/PresenceEventEngineSteps.kt @@ -0,0 +1,137 @@ +package com.pubnub.contract.presence.eventEngine.step + +import com.pubnub.api.PubNub +import com.pubnub.api.callbacks.SubscribeCallback +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.contract.subscribe.eventEngine.state.EventEngineState +import io.cucumber.datatable.DataTable +import io.cucumber.java.en.Given +import io.cucumber.java.en.Then +import io.cucumber.java.en.When +import org.awaitility.Awaitility +import org.awaitility.kotlin.await +import org.hamcrest.CoreMatchers.hasItem +import org.hamcrest.MatcherAssert +import org.hamcrest.Matchers +import org.hamcrest.core.IsEqual +import org.junit.jupiter.api.Assertions.assertTrue +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger + +class PresenceEventEngineSteps(private val state: EventEngineState) { + @Given("heartbeatInterval set to {string}, timeout set to {string} and suppressLeaveEvents set to {string}") + fun heartbeatInterval_set_timeout_and_suppressLeaveEvents_set( + heartbeatInterval: String, + timeout: String, + suppressLeaveEvents: String, + ) { + state.configuration.presenceTimeout = timeout.toInt() + state.configuration.heartbeatInterval = heartbeatInterval.toInt() + state.configuration.suppressLeaveEvents = suppressLeaveEvents.toBooleanStrict() + } + + @When("I join {string}, {string}, {string} channels") + fun I_join_channels( + firstChannel: String, + secondChannel: String, + thirdChannel: String, + ) { + state.pubnub.subscribe(channels = listOf(firstChannel, secondChannel, thirdChannel)) + } + + @When("I join {string}, {string}, {string} channels with presence") + fun I_join_channels_with_presence( + firstChannel: String, + secondChannel: String, + thirdChannel: String, + ) { + state.pubnub.subscribe( + channels = listOf(firstChannel, secondChannel, thirdChannel), + withPresence = true, + ) + } + + @When("The timeout expires") + fun The_timeout_expires() { + Thread.sleep(10000) + } + + @Then("I wait {string} seconds") + fun I_wait_seconds(waitingTimeInSeconds: String) { + Thread.sleep(waitingTimeInSeconds.toLong() * 1000) + } + + @Then("I wait for getting Presence joined events") + fun I_wait_for_getting_Presence_joined_events() { + val atomic = AtomicInteger(0) + state.pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + // do nothing + } + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + if (pnPresenceEventResult.event == "join" && (pnPresenceEventResult.channel == "first" || pnPresenceEventResult.channel == "second" || pnPresenceEventResult.channel == "third")) { + atomic.incrementAndGet() + } + } + }, + ) + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, IsEqual.equalTo(3)) + } + + @Then("I receive an error in my heartbeat response") + fun I_receive_an_error_in_my_heartbeat_response() { + await.pollInterval(50, TimeUnit.MILLISECONDS).atMost(3, TimeUnit.SECONDS).untilAsserted { + val expectedNames = "event" to "HEARTBEAT_FAILURE" + MatcherAssert.assertThat(state.presenceQueuedElements, hasItem(expectedNames)) + } + } + + @Then("I leave {string} and {string} channels") + fun I_leave_channels( + firstChannel: String, + secondChannel: String, + ) { + state.pubnub.unsubscribe(channels = listOf(firstChannel, secondChannel)) + } + + @Then("I observe the following Events and Invocations of the Presence EE:") + fun i_observe_the_following(dataTable: DataTable) { + await.pollInterval(50, TimeUnit.MILLISECONDS).atMost(3, TimeUnit.SECONDS).untilAsserted { + if (state.configuration.suppressLeaveEvents) { + // when suppressLeaveEvents is true in Kotlin SDK we do invocation LEAVE, but it will not execute LeaveEffect + // other SDK don't do invocation LEAVE and cucumber test doesn't expect LEAVE invocation. + // In Kotlin SDK we have EventEngine.performTransitionAndEmitEffects method that is + // common for Presence EE and Subscribe EE. I don't want to pollute this method with if statement that check + // if suppressLeaveEvents is true. That's why I remove LEAVE invocation from the queue. + state.presenceQueuedElements.remove("invocation" to "LEAVE") + } + val expectedNames = dataTable.asMaps().map { it["type"] to it["name"] }.toList() + MatcherAssert.assertThat(state.presenceQueuedElements, Matchers.`is`(expectedNames)) + } + } + + @Then("I don't observe any Events and Invocations of the Presence EE") + fun i_dont_observe_any_events_and_invocations_of_the_presence_ee() { + Thread.sleep(3000) + assertTrue(state.presenceQueuedElements.isEmpty()) + } + + @Then("I leave {string} and {string} channels with presence") + fun I_leave_channels_with_presence( + firstChannel: String, + secondChannel: String, + ) { + state.pubnub.unsubscribe(channels = listOf(firstChannel, secondChannel)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/state/World.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/state/World.kt new file mode 100644 index 000000000..391cfd2cc --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/state/World.kt @@ -0,0 +1,29 @@ +package com.pubnub.contract.state + +import com.pubnub.api.PubNubException +import com.pubnub.api.UserId +import com.pubnub.api.enums.PNLogVerbosity +import com.pubnub.api.v2.PNConfiguration +import com.pubnub.contract.ContractTestConfig +import com.pubnub.internal.PubNubImpl + +interface WorldState { + val configuration: PNConfiguration.Builder + var pnException: PubNubException? + var tokenString: String? + var responseStatus: Int? +} + +class World : WorldState { + override val configuration: PNConfiguration.Builder = + PNConfiguration.builder(userId = UserId(PubNubImpl.generateUUID()), "").apply { + origin = ContractTestConfig.serverHostPort + secure = false + logVerbosity = PNLogVerbosity.BODY + } + + val pubnub: PubNubImpl by lazy { PubNubImpl(configuration.build()) } + override var pnException: PubNubException? = null + override var tokenString: String? = null + override var responseStatus: Int? = null +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/step/ErrorMessageAndDetailsStep.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/step/ErrorMessageAndDetailsStep.kt new file mode 100644 index 000000000..8446795bb --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/step/ErrorMessageAndDetailsStep.kt @@ -0,0 +1,18 @@ +package com.pubnub.contract.step + +import com.pubnub.contract.state.World +import io.cucumber.java.en.Then +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers + +class ErrorMessageAndDetailsStep(private val world: World) { + @Then("I see the error message {string} and details {string}") + fun i_see_the_error_message_and_details( + message: String, + details: String, + ) { + val exception = world.pnException!! + assertThat(exception.errorMessage, Matchers.containsString(message)) + assertThat(exception.errorMessage, Matchers.containsString(details)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/step/KeysetStep.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/step/KeysetStep.kt new file mode 100644 index 000000000..e22e21465 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/step/KeysetStep.kt @@ -0,0 +1,50 @@ +package com.pubnub.contract.step + +import com.pubnub.contract.ContractTestConfig +import com.pubnub.contract.state.World +import io.cucumber.java.en.Given +import org.hamcrest.MatcherAssert +import org.hamcrest.Matchers + +class KeysetStep(private val world: World) { + @Given("I have a keyset with access manager enabled") + fun i_have_a_keyset_with_access_manager_enabled() { + MatcherAssert.assertThat(ContractTestConfig.pamPubKey, Matchers.notNullValue()) + MatcherAssert.assertThat(ContractTestConfig.pamSubKey, Matchers.notNullValue()) + MatcherAssert.assertThat(ContractTestConfig.pamSecKey, Matchers.notNullValue()) + world.configuration.apply { + subscribeKey = ContractTestConfig.pamSubKey + publishKey = ContractTestConfig.pamPubKey + secretKey = ContractTestConfig.pamSecKey + } + } + + @Given("I have a keyset with access manager enabled - without secret key") + fun i_have_a_keyset_with_access_manager_enabled_without_secret_key() { + MatcherAssert.assertThat(ContractTestConfig.pubKey, Matchers.notNullValue()) + MatcherAssert.assertThat(ContractTestConfig.subKey, Matchers.notNullValue()) + + world.configuration.apply { + subscribeKey = ContractTestConfig.subKey + publishKey = ContractTestConfig.pubKey + } + } + + @Given("I have a keyset with Objects V2 enabled") + fun i_have_a_keyset_with_objects_v2_enabled() { + MatcherAssert.assertThat(ContractTestConfig.subKey, Matchers.notNullValue()) + world.configuration.apply { + subscribeKey = ContractTestConfig.subKey + } + } + + @Given("the demo keyset with event engine enabled") + fun the_demo_keyset_with_event_engine_enabled() { + i_have_a_keyset_with_access_manager_enabled_without_secret_key() + } + + @Given("the demo keyset with Presence EE enabled") + fun the_demo_keyset_with_Presence_EE_enabled() { + the_demo_keyset_with_event_engine_enabled() + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/step/ThenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/step/ThenSteps.kt new file mode 100644 index 000000000..b06d54586 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/step/ThenSteps.kt @@ -0,0 +1,93 @@ +package com.pubnub.contract.step + +import com.pubnub.contract.state.World +import io.cucumber.java.en.Then +import org.json.JSONObject +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue + +class ThenSteps(private val world: World) { + @Then.Thens( + Then("an error is returned"), + Then("an auth error is returned"), + ) + fun an_error_is_returned() { + assertNotNull(world.pnException) + } + + @Then("the result is successful") + fun the_result_is_successful() { + assertNull(world.pnException) + } + + @Then("I receive a successful response") + fun i_receive_a_successful_response() { + assertNull(world.pnException) + assertEquals(200, world.responseStatus) + } + + @Then("the error status code is {int}") + fun the_error_status_code_is(statusCode: Int) { + assertEquals(statusCode, world.pnException?.statusCode) + } + + @Then.Thens(Then("the error message is {string}"), Then("the auth error message is {string}")) + fun the_error_message_is(message: String) { + assertTrue( + "Exception ${world.pnException} should contain message $message", + world.pnException?.message?.contains(message) ?: false, + ) + } + + @Then("the error source is {string}") + fun the_error_source_is(source: String) { + assertTrue( + "Exception ${world.pnException} should contain source $source", + world.pnException?.message?.contains(source) ?: false, + ) + } + + @Then("the error detail message is {string}") + fun the_error_detail_message_is(details: String) { + assertTrue( + "Exception ${world.pnException} should contain error details $details", + world.pnException?.message?.contains(details) ?: false, + ) + } + + @Then("the error detail location is {string}") + fun the_error_detail_location_is(location: String) { + assertTrue( + "Exception ${world.pnException} should contain location $location", + world.pnException?.message?.contains(location) ?: false, + ) + } + + @Then("the error detail location type is {string}") + fun the_error_detail_location_type_is(locationType: String) { + assertTrue( + "Exception ${world.pnException} should contain locationType $locationType", + world.pnException?.message?.contains(locationType) ?: false, + ) + } + + @Then("the error detail message is not empty") + fun the_error_detail_message_is_not_empty() { + assertTrue( + errorJsonObject().getJSONObject("error") + .getJSONArray("details") + .map { it as JSONObject } + .mapNotNull { it.getString("message") } + .any { it.isNotBlank() }, + ) + } + + @Then("the error service is {string}") + fun the_error_service_is(string: String) { + assertEquals(string, errorJsonObject().getString("service")) + } + + private fun errorJsonObject(): JSONObject = JSONObject(world.pnException!!.errorMessage) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/state/EventEngineState.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/state/EventEngineState.kt new file mode 100644 index 000000000..9db409156 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/state/EventEngineState.kt @@ -0,0 +1,32 @@ +package com.pubnub.contract.subscribe.eventEngine.state + +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.api.models.consumer.pubsub.PNEvent +import com.pubnub.contract.state.World +import com.pubnub.contract.state.WorldState +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.eventengine.QueueEventEngineConf +import com.pubnub.internal.subscribe.eventengine.configuration.EventEnginesConf +import java.util.concurrent.CopyOnWriteArrayList + +internal fun testEventEnginesConf( + subscribeQueuedElements: MutableList>, + presenceQueuedElements: MutableList>, +) = EventEnginesConf( + QueueEventEngineConf(TestSinkSource(subscribeQueuedElements), TestSinkSource(subscribeQueuedElements)), + QueueEventEngineConf(TestSinkSource(presenceQueuedElements), TestSinkSource(presenceQueuedElements)), +) + +class EventEngineState(world: World) : WorldState by world { + val subscribeQueuedElements: MutableList> = CopyOnWriteArrayList() + val presenceQueuedElements: MutableList> = CopyOnWriteArrayList() + val channelName: String = "MyChannel_01" + val messagesList: MutableList = CopyOnWriteArrayList() + val statusesList: MutableList = CopyOnWriteArrayList() + val pubnub: PubNubImpl by lazy { + PubNubImpl( + configuration.build(), + eventEnginesConf = testEventEnginesConf(subscribeQueuedElements, presenceQueuedElements), + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/state/TestSinkSource.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/state/TestSinkSource.kt new file mode 100644 index 000000000..dced3cf8a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/state/TestSinkSource.kt @@ -0,0 +1,31 @@ +package com.pubnub.contract.subscribe.eventEngine.state + +import com.pubnub.internal.eventengine.EffectInvocation +import com.pubnub.internal.eventengine.Event +import com.pubnub.internal.eventengine.QueueSinkSource +import com.pubnub.internal.eventengine.SinkSource +import com.pubnub.internal.eventengine.Source + +internal class TestSinkSource( + private val testSink: MutableList>, + private val sinkSource: QueueSinkSource = QueueSinkSource(), +) : SinkSource, Source by sinkSource { + private fun String.toSnakeCase(): String { + val pattern = "(?<=.)[A-Z]".toRegex() + return this.replace(pattern, "_$0").lowercase() + } + + override fun add(item: T) { + testSink.add(item.type() to item.name()) + sinkSource.add(item) + } + + private fun T.name() = this!!::class.simpleName!!.toSnakeCase().uppercase() + + private fun T.type(): String = + when (this) { + is Event -> "event" + is EffectInvocation -> "invocation" + else -> "unknown" + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/step/EventEngineSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/step/EventEngineSteps.kt new file mode 100644 index 000000000..c74890edb --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/subscribe/eventEngine/step/EventEngineSteps.kt @@ -0,0 +1,176 @@ +package com.pubnub.contract.subscribe.eventEngine.step + +import com.pubnub.api.PubNub +import com.pubnub.api.callbacks.SubscribeCallback +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.api.models.consumer.pubsub.PNSignalResult +import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult +import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult +import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult +import com.pubnub.api.retry.RetryConfiguration +import com.pubnub.contract.subscribe.eventEngine.state.EventEngineState +import io.cucumber.datatable.DataTable +import io.cucumber.java.en.Given +import io.cucumber.java.en.Then +import io.cucumber.java.en.When +import org.awaitility.kotlin.await +import org.hamcrest.CoreMatchers +import org.hamcrest.MatcherAssert +import org.hamcrest.Matchers +import java.util.concurrent.TimeUnit +import kotlin.time.Duration.Companion.milliseconds + +class EventEngineSteps(private val state: EventEngineState) { + @Given("a linear reconnection policy with {int} retries") + fun a_linear_reconnection_policy_with_retries(maxRetries: Int) { + state.configuration.retryConfiguration = + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + RetryConfiguration.Linear.createForTest(delayInSec = 1.milliseconds, maxRetryNumber = maxRetries, isInternal = true) + } + + @When("I subscribe") + fun i_subscribe() { + state.pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + state.statusesList.add(pnStatus) + } + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + state.messagesList.add(pnMessageResult) + } + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + state.messagesList.add(pnPresenceEventResult) + } + + override fun signal( + pubnub: PubNub, + pnSignalResult: PNSignalResult, + ) { + state.messagesList.add(pnSignalResult) + } + + override fun messageAction( + pubnub: PubNub, + pnMessageActionResult: PNMessageActionResult, + ) { + state.messagesList.add(pnMessageActionResult) + } + + override fun objects( + pubnub: PubNub, + objectEvent: PNObjectEventResult, + ) { + state.messagesList.add(objectEvent) + } + + override fun file( + pubnub: PubNub, + pnFileEventResult: PNFileEventResult, + ) { + state.messagesList.add(pnFileEventResult) + } + }, + ) + + state.pubnub.subscribe(channels = listOf(state.channelName)) + } + + @When("I subscribe with timetoken {long}") + fun i_subscribe_with_timetoken(timetoken: Long) { + state.pubnub.addListener( + object : SubscribeCallback() { + override fun status( + pubnub: PubNub, + pnStatus: PNStatus, + ) { + state.statusesList.add(pnStatus) + } + + override fun message( + pubnub: PubNub, + pnMessageResult: PNMessageResult, + ) { + state.messagesList.add(pnMessageResult) + } + + override fun presence( + pubnub: PubNub, + pnPresenceEventResult: PNPresenceEventResult, + ) { + state.messagesList.add(pnPresenceEventResult) + } + + override fun signal( + pubnub: PubNub, + pnSignalResult: PNSignalResult, + ) { + state.messagesList.add(pnSignalResult) + } + + override fun messageAction( + pubnub: PubNub, + pnMessageActionResult: PNMessageActionResult, + ) { + state.messagesList.add(pnMessageActionResult) + } + + override fun objects( + pubnub: PubNub, + objectEvent: PNObjectEventResult, + ) { + state.messagesList.add(objectEvent) + } + + override fun file( + pubnub: PubNub, + pnFileEventResult: PNFileEventResult, + ) { + state.messagesList.add(pnFileEventResult) + } + }, + ) + + state.pubnub.subscribe( + channels = listOf(state.channelName), + withTimetoken = timetoken, + ) + } + + @Then("I receive the message in my subscribe response") + fun i_receive_the_message_in_my_subscribe_response() { + await.pollInterval(50, TimeUnit.MILLISECONDS).atMost(2, TimeUnit.SECONDS).untilAsserted { + MatcherAssert.assertThat( + state.messagesList.map { it::class.java }, + CoreMatchers.hasItems(PNMessageResult::class.java), + ) + } + } + + @Then("I observe the following:") + fun i_observe_the_following(dataTable: DataTable) { + await.pollInterval(50, TimeUnit.MILLISECONDS).atMost(500, TimeUnit.MILLISECONDS).untilAsserted { + val expectedNames = dataTable.asMaps().map { it["type"] to it["name"] }.toList() + MatcherAssert.assertThat(state.subscribeQueuedElements, Matchers.`is`(expectedNames)) + } + } + + @Then("I receive an error in my subscribe response") + fun i_receive_an_error_in_my_subscribe_response() { + await.pollInterval(50, TimeUnit.MILLISECONDS).atMost(3, TimeUnit.SECONDS).untilAsserted { + MatcherAssert.assertThat(state.statusesList.map { it.error }, CoreMatchers.hasItems(true)) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/state/UUIDMetadataState.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/state/UUIDMetadataState.kt new file mode 100644 index 000000000..20bc6fe18 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/state/UUIDMetadataState.kt @@ -0,0 +1,9 @@ +package com.pubnub.contract.uuidmetadata.state + +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata + +class UUIDMetadataState { + var uuidMetadatas: Collection? = null + var uuidMetadata: PNUUIDMetadata? = null + var uuid: String? = null +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/GivenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/GivenSteps.kt new file mode 100644 index 000000000..e94446719 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/GivenSteps.kt @@ -0,0 +1,31 @@ +package com.pubnub.contract.uuidmetadata.step + +import com.pubnub.api.UserId +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata +import com.pubnub.contract.loadPersonaUUIDMetadata +import com.pubnub.contract.state.World +import com.pubnub.contract.uuidmetadata.state.UUIDMetadataState +import io.cucumber.java.en.Given + +class GivenSteps( + private val world: World, + private val uuidMetadataState: UUIDMetadataState, +) { + @Given("the id for {string} persona") + fun the_uuid_for_persona(personaName: String) { + val pnUUIDMetadata: PNUUIDMetadata = loadPersonaUUIDMetadata(personaName) + uuidMetadataState.uuid = pnUUIDMetadata.id + } + + @Given("current user is {string} persona") + fun given_current_user_is_persona(personaName: String) { + val pnUUIDMetadata: PNUUIDMetadata = loadPersonaUUIDMetadata(personaName) + world.configuration.userId = UserId(pnUUIDMetadata.id) + } + + @Given("the data for {string} persona") + fun the_data_for_persona(personaName: String) { + val pnUUIDMetadata: PNUUIDMetadata = loadPersonaUUIDMetadata(personaName) + uuidMetadataState.uuidMetadata = pnUUIDMetadata + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/ThenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/ThenSteps.kt new file mode 100644 index 000000000..bb928ca1e --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/ThenSteps.kt @@ -0,0 +1,36 @@ +package com.pubnub.contract.uuidmetadata.step + +import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata +import com.pubnub.contract.loadPersonaUUIDMetadata +import com.pubnub.contract.uuidmetadata.state.UUIDMetadataState +import io.cucumber.java.en.Then +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers +import org.hamcrest.core.Is.`is` as iz + +class ThenSteps( + private val uuidMetadataState: UUIDMetadataState, +) { + @Then("the UUID metadata for {string} persona") + fun the_uuid_metadata_for_persona(personaName: String) { + val alice: PNUUIDMetadata = loadPersonaUUIDMetadata(personaName) + assertThat(uuidMetadataState.uuidMetadata, iz(alice)) + } + + @Then("the UUID metadata for {string} persona contains updated") + fun the_uuid_metadata_for_persona_contains_updated( + @Suppress("UNUSED_PARAMETER") personaName: String, + ) { + assertThat(uuidMetadataState.uuidMetadata?.updated, iz(Matchers.notNullValue())) + } + + @Then("the response contains list with {string} and {string} UUID metadata") + fun the_response_contains_list_with_UUID_metadata( + personName: String, + otherPersonName: String, + ) { + val firstPersona = loadPersonaUUIDMetadata(personName) + val secondPersona = loadPersonaUUIDMetadata(otherPersonName) + assertThat(uuidMetadataState.uuidMetadatas, Matchers.containsInAnyOrder(firstPersona, secondPersona)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/WhenSteps.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/WhenSteps.kt new file mode 100644 index 000000000..a9f20c82b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/contract/uuidmetadata/step/WhenSteps.kt @@ -0,0 +1,70 @@ +package com.pubnub.contract.uuidmetadata.step + +import com.pubnub.contract.state.World +import com.pubnub.contract.uuidmetadata.state.UUIDMetadataState +import io.cucumber.java.en.When + +class WhenSteps( + private val world: World, + private val uuidMetadataState: UUIDMetadataState, +) { + @When("I get the UUID metadata") + fun i_get_uuid_metadata() { + world.pubnub.getUUIDMetadata(uuid = uuidMetadataState.uuid).sync()?.let { + uuidMetadataState.uuidMetadata = it.data + world.responseStatus = it.status + } + } + + @When("I get the UUID metadata with custom for current user") + fun i_get_uuid_metadata_with_custom_for_current_user() { + world.pubnub.getUUIDMetadata(includeCustom = true).sync()?.let { + uuidMetadataState.uuidMetadata = it.data + world.responseStatus = it.status + } + } + + @When("I set the UUID metadata") + fun i_set_the_uuid_metadata() { + val uuidMetadata = uuidMetadataState.uuidMetadata!! + world.pubnub.setUUIDMetadata( + uuid = uuidMetadata.id, + custom = uuidMetadata.custom, + 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 + } + } + + @When("I remove the UUID metadata") + fun i_remove_uuid_metadata_for_id() { + world.responseStatus = world.pubnub.removeUUIDMetadata(uuid = uuidMetadataState.uuid).sync()?.status + } + + @When("I remove the UUID metadata for current user") + fun i_remove_uuid_metadata_of_current_user() { + world.responseStatus = world.pubnub.removeUUIDMetadata().sync()?.status + } + + @When("I get all UUID metadata") + fun i_get_all_uuid_metadata() { + world.pubnub.getAllUUIDMetadata().sync()?.let { + uuidMetadataState.uuidMetadatas = it.data + world.responseStatus = it.status + } + } + + @When("I get all UUID metadata with custom") + fun i_get_all_uuid_metadata_with_custom() { + world.pubnub.getAllUUIDMetadata(includeCustom = true).sync()?.let { + uuidMetadataState.uuidMetadatas = it.data + world.responseStatus = it.status + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/endpoints/DelegatingEndpointTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/endpoints/DelegatingEndpointTest.kt deleted file mode 100644 index 060b513b1..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/endpoints/DelegatingEndpointTest.kt +++ /dev/null @@ -1,94 +0,0 @@ -package com.pubnub.internal.endpoints - -import com.pubnub.api.endpoints.remoteaction.ExtendedRemoteAction -import com.pubnub.api.enums.PNOperationType -import com.pubnub.api.v2.BasePNConfiguration -import com.pubnub.api.v2.callbacks.Result -import com.pubnub.api.v2.callbacks.getOrThrow -import com.pubnub.internal.DelegatingEndpoint -import com.pubnub.internal.EndpointInterface -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import java.util.function.Consumer - -internal class DelegatingEndpointTest { - private lateinit var delegatingEndpoint: DelegatingEndpoint - - var silentCancelCalled = false - var retryCalled = false - - val action = - object : EndpointInterface { - override fun operationType(): PNOperationType { - return PNOperationType.FileOperation - } - - override fun retry() { - retryCalled = true - } - - override fun sync(): Boolean { - return true - } - - override fun silentCancel() { - silentCancelCalled = true - } - - override fun overrideConfiguration(configuration: BasePNConfiguration) { - TODO("Not yet implemented") - } - - override val configuration: BasePNConfiguration - get() = TODO("Not yet implemented") - - override fun async(callback: Consumer>) { - callback.accept(Result.success(true)) - } - } - - @BeforeEach - fun setUp() { - silentCancelCalled = false - retryCalled = false - delegatingEndpoint = - object : DelegatingEndpoint(action) { - override fun convertAction(remoteAction: ExtendedRemoteAction): ExtendedRemoteAction { - return remoteAction - } - } - } - - @Test - fun sync() { - val result = delegatingEndpoint.sync() - assertTrue(result) - } - - @Test - fun async() { - delegatingEndpoint.async { result -> - assertTrue(result.isSuccess) - assertTrue(result.getOrThrow()) - } - } - - @Test - fun retry() { - delegatingEndpoint.retry() - assertTrue(retryCalled) - } - - @Test - fun silentCancel() { - delegatingEndpoint.silentCancel() - assertTrue(silentCancelCalled) - } - - @Test - fun operationType() { - assertEquals(PNOperationType.FileOperation, delegatingEndpoint.operationType()) - } -} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/eventengine/EffectDispatcherTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/eventengine/EffectDispatcherTest.kt new file mode 100644 index 000000000..cf23a352a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/eventengine/EffectDispatcherTest.kt @@ -0,0 +1,157 @@ +package com.pubnub.internal.eventengine + +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation +import io.mockk.every +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.hasKey +import org.hamcrest.Matchers.not +import org.junit.jupiter.api.Test +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ScheduledExecutorService + +class EffectDispatcherTest { + private val executorService: ScheduledExecutorService = mockk() + + internal sealed class TestEffectInvocation(override val type: EffectInvocationType) : EffectInvocation { + override val id: String = this::class.java.simpleName + } + + internal object TestEffect : + TestEffectInvocation(Managed) + + internal object ImmediateEndingTestEffect : + TestEffectInvocation(Managed) + + internal object CancelTestEffect : TestEffectInvocation(Cancel(TestEffect::class.java.simpleName)) + + internal class EffectHandlerFactoryImpl : EffectFactory { + override fun create(effectInvocation: TestEffectInvocation): ManagedEffect { + return object : ManagedEffect { + override fun runEffect() { + when (effectInvocation) { + is ImmediateEndingTestEffect -> { + } + + else -> { + } + } + } + + override fun cancel() { + } + } + } + } + + @Test + fun managedEffectIsNotEvictedTillCancelled() { + // given + val managedEffects = ConcurrentHashMap() + val effectDispatcher = + EffectDispatcher( + effectFactory = EffectHandlerFactoryImpl(), + managedEffects = managedEffects, + effectSource = QueueSinkSource(), + executorService = executorService, + ) + + // when + effectDispatcher.dispatch(TestEffect) + + // then + assertThat(managedEffects, hasKey(TestEffect.id)) + } + + @Test + fun managedEffectIsEvictedAfterCancel() { + // given + val managedEffects = ConcurrentHashMap() + val effectDispatcher = + EffectDispatcher( + effectFactory = EffectHandlerFactoryImpl(), + managedEffects = managedEffects, + effectSource = QueueSinkSource(), + executorService = executorService, + ) + + // when + effectDispatcher.dispatch(TestEffect) + effectDispatcher.dispatch(CancelTestEffect) + + // then + assertThat(managedEffects, not(hasKey(TestEffect.id))) + } + + @Test + fun canCancelEvictedEffect() { + // given + val managedEffects = ConcurrentHashMap() + val effectDispatcher = + EffectDispatcher( + effectFactory = EffectHandlerFactoryImpl(), + managedEffects = managedEffects, + effectSource = QueueSinkSource(), + executorService = executorService, + ) + + // when + effectDispatcher.dispatch(TestEffect) + effectDispatcher.dispatch(CancelTestEffect) + effectDispatcher.dispatch(CancelTestEffect) + + // then + assertThat(managedEffects, not(hasKey(TestEffect.id))) + } + + @Test + fun puttingEffectWithSameIdCancelsTheFirstOne() { + // given + val managedEffects = ConcurrentHashMap() + val effectHandlerFactory = EffectHandlerFactoryImpl() + val managedEffect = spyk(effectHandlerFactory.create(TestEffect)) + managedEffects[TestEffect.id] = managedEffect + val effectDispatcher = + EffectDispatcher( + effectFactory = effectHandlerFactory, + managedEffects = managedEffects, + effectSource = QueueSinkSource(), + executorService = executorService, + ) + + // when + effectDispatcher.dispatch(TestEffect) + + // then + verify(exactly = 1) { managedEffect.cancel() } + assertThat(managedEffects, hasKey(TestEffect.id)) + } + + @Test + fun `can handle NonManaged effect`() { + // given + val managedEffects = ConcurrentHashMap() + val emitMessagesInvocation: SubscribeEffectInvocation.EmitMessages = + SubscribeEffectInvocation.EmitMessages(listOf()) + val effectFactory: EffectFactory = mockk() + val effectDispatcher = + EffectDispatcher( + effectFactory = effectFactory, + managedEffects = managedEffects, + effectSource = QueueSinkSource(), + executorService = executorService, + ) + val effect: Effect = mockk() + every { effect.runEffect() } returns Unit + every { effectFactory.create(emitMessagesInvocation) } returns effect + + // when + effectDispatcher.dispatch(emitMessagesInvocation) + + // then + verify { effectFactory.create(emitMessagesInvocation) } + verify { effect.runEffect() } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/eventengine/EventEngineManagerTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/eventengine/EventEngineManagerTest.kt new file mode 100644 index 000000000..658b14b45 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/eventengine/EventEngineManagerTest.kt @@ -0,0 +1,64 @@ +package com.pubnub.internal.eventengine + +import com.pubnub.internal.presence.eventengine.PresenceEventEngine +import com.pubnub.internal.presence.eventengine.effect.PresenceEffectInvocation +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.presence.eventengine.state.PresenceState +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class EventEngineManagerTest { + private lateinit var objectUnderTest: EventEngineManager + private val eventEngine: PresenceEventEngine = mockk() + private val effectDispatcher: EffectDispatcher = mockk() + private val eventSink: Sink = mockk() + + @BeforeEach + fun setUp() { + objectUnderTest = EventEngineManager(eventEngine, effectDispatcher, eventSink) + } + + @Test + fun `should add event to eventSink when adding event to queue`() { + // given + val event: PresenceEvent = mockk() + every { eventSink.add(event) } returns Unit + + // when + objectUnderTest.addEventToQueue(event) + + // then + verify { eventSink.add(event) } + } + + @Test + fun `should start eventEngine and effectDispatcher when calling start`() { + // given + every { eventEngine.start() } returns Unit + every { effectDispatcher.start() } returns Unit + + // when + objectUnderTest.start() + + // then + verify { eventEngine.start() } + verify { effectDispatcher.start() } + } + + @Test + fun `should stop eventEngine and effectDispatcher when calling stop`() { + // given + every { eventEngine.stop() } returns Unit + every { effectDispatcher.stop() } returns Unit + + // when + objectUnderTest.stop() + + // then + verify { eventEngine.stop() } + verify { effectDispatcher.stop() } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/eventengine/EventEngineTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/eventengine/EventEngineTest.kt new file mode 100644 index 000000000..fd96ac9cf --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/eventengine/EventEngineTest.kt @@ -0,0 +1,43 @@ +package com.pubnub.internal.eventengine + +import com.pubnub.contract.subscribe.eventEngine.state.TestSinkSource +import io.mockk.mockk +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import java.util.concurrent.ScheduledExecutorService + +class EventEngineTest { + internal object TestEffectInvocation : EffectInvocation { + override val id: String = "id" + override val type: EffectInvocationType = NonManaged + } + + internal object TestState : State { + override fun transition(event: Event): Pair> { + return transitionTo(TestState, TestEffectInvocation) + } + } + + private val executorService: ScheduledExecutorService = mockk() + + @Test + internal fun `should pass invocations for handling when event provided`() { + // given + val queuedElements = mutableListOf>() + val eventEngineConf = + QueueEventEngineConf(effectSinkSource = TestSinkSource(queuedElements)) + val eventEngine = + EventEngine( + effectSink = eventEngineConf.effectSink, + eventSource = eventEngineConf.eventSource, + currentState = TestState, + executorService = executorService, + ) + + // when + eventEngine.performTransitionAndEmitEffects(object : Event {}) + + // then + Assertions.assertEquals(listOf("invocation" to "TEST_EFFECT_INVOCATION"), queuedElements) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/extension/JsonElementTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/extension/JsonElementTest.kt new file mode 100644 index 000000000..88a4e0a00 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/extension/JsonElementTest.kt @@ -0,0 +1,157 @@ +package com.pubnub.internal.extension + +import com.google.gson.Gson +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import com.pubnub.api.PubNubError +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.internal.managers.MapperManager +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +class JsonElementTest { + companion object { + val gson = Gson() + + @JvmStatic + fun cryptoModuleConfiguration(): List { + val cipherKey = "enigma" + return listOf( + Arguments.of( + JsonPrimitive("Hello world."), + "X/wLZqR/I+4eU7WwtKycTwy9+L9e7m2BhklK9ZQWtwI=", + CryptoModule.createLegacyCryptoModule(cipherKey = cipherKey, randomIv = true), + ), + Arguments.of( + JsonPrimitive("Hello world."), + "bk8x+ZEg+Roq8ngUo7lfFg==", + CryptoModule.createLegacyCryptoModule(cipherKey = cipherKey, randomIv = false), + ), + Arguments.of( + JsonPrimitive("Hello world."), + "UE5FRAFBQ1JIEK8FoITMdCOcG1mPQrtf31N5ZtA2MY/6QPLJ1DURjnwo", + CryptoModule.createAesCbcCryptoModule(cipherKey = cipherKey, randomIv = true), + ), + Arguments.of( + gson.fromJson("{\"name\":\"joe\",\"age\":48}", JsonObject::class.java), + "4vKzF7L8TWPwtnipqZZo4UGSbpjQi5mX4jkX29Rr/Cc306fe2kBLzhNm820UBQ1+", + CryptoModule.createLegacyCryptoModule(cipherKey = cipherKey, randomIv = true), + ), + Arguments.of( + gson.fromJson("{\"name\":\"joe\",\"age\":48}", JsonObject::class.java), + "Pjwel1wltV2CipfQrQWulh526ZdMr49BpF0Z8A5+Vms=", + CryptoModule.createLegacyCryptoModule(cipherKey = cipherKey, randomIv = false), + ), + Arguments.of( + gson.fromJson("{\"name\":\"joe\",\"age\":48}", JsonObject::class.java), + "UE5FRAFBQ1JIEDFd1QNYJ9lygFSiK54LohgnsVgmb1YhPS5R5px8KvtUe/NbnYBjqGnslADAgNbOtQ==", + CryptoModule.createAesCbcCryptoModule(cipherKey = cipherKey, randomIv = true), + ), + Arguments.of( + gson.fromJson("[{\"name\":\"joe\",\"age\":48}]", JsonArray::class.java), + "UE5FRAFBQ1JIEIKCMD5/p4GpVKFOe+OR1HhF2VFIGxHxOUixt/eAd7DBAeWYfCoiS4VBAcqVyC3MuQ==", + CryptoModule.createAesCbcCryptoModule(cipherKey = cipherKey, randomIv = true), + ), + ) + } + + @JvmStatic + fun unencryptedMessage(): List { + return listOf( + Arguments.of("unencryptedMessage"), + Arguments.of("this is unencrypted message"), + Arguments.of("this is not encrypted message"), + ) + } + } + + private lateinit var objectUnderTest: JsonElement + + @ParameterizedTest + @MethodSource("unencryptedMessage") + fun `when history message is not encrypted string and crypto is configured should log error and return error in response and return unencrypted message `( + unencryptedMessage: String, + ) { + // given + objectUnderTest = JsonPrimitive(unencryptedMessage) + val cipherKey = "enigma" + val cryptoModule = CryptoModule.createLegacyCryptoModule(cipherKey = cipherKey, randomIv = true) + val mapper = MapperManager() + + // when + val (jsonElement, errorMessage) = objectUnderTest.tryDecryptMessage(cryptoModule, mapper) + + // then + assertEquals(objectUnderTest, jsonElement) + assertEquals(PubNubError.CRYPTO_IS_CONFIGURED_BUT_MESSAGE_IS_NOT_ENCRYPTED, errorMessage) + } + + @ParameterizedTest + @MethodSource("cryptoModuleConfiguration") + fun `when history message is encrypted string and crypto is configured should return decrypted message`( + message: JsonElement, + encryptedMessage: String, + cryptoModule: CryptoModule, + ) { + // given + objectUnderTest = JsonPrimitive(encryptedMessage) + val mapper = MapperManager() + + // when + val (jsonElement, errorMessage) = objectUnderTest.tryDecryptMessage(cryptoModule, mapper) + + // then + assertEquals(message, jsonElement) + assertNull(errorMessage) + } + + @ParameterizedTest + @MethodSource("cryptoModuleConfiguration") + fun `when history message is not encrypted JSON object and crypto is configured should log error and return error in response and return unencrypted message `( + message: JsonElement, + encryptedMessage: String, + cryptoModule: CryptoModule, + ) { + // given + objectUnderTest = message + val mapper = MapperManager() + + // when + val (jsonElement, errorMessage) = objectUnderTest.tryDecryptMessage(cryptoModule, mapper) + + // then + assertEquals(objectUnderTest, jsonElement) + assertEquals(PubNubError.CRYPTO_IS_CONFIGURED_BUT_MESSAGE_IS_NOT_ENCRYPTED, errorMessage) + } + + @ParameterizedTest + @MethodSource("cryptoModuleConfiguration") + fun `when history message contains JSON object with pn_other property and crypto is configured should decrypt content of pn_other`( + message: JsonElement, + encryptedMessage: String, + cryptoModule: CryptoModule, + ) { + // given + val messageWithPNOther = generateMessageWithPNOther(JsonPrimitive(encryptedMessage)) + objectUnderTest = messageWithPNOther + val mapper = MapperManager() + + // when + val (jsonElement, errorMessage) = objectUnderTest.tryDecryptMessage(cryptoModule, mapper) + + // then + assertEquals(messageWithPNOther, jsonElement) + assertNull(errorMessage) + } + + private fun generateMessageWithPNOther(pnOtherEncryptedValue: JsonElement): JsonObject { + return JsonObject().apply { + add("pn_other", pnOtherEncryptedValue) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/managers/MapperManagerTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/managers/MapperManagerTest.kt new file mode 100644 index 000000000..ce31bb4aa --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/managers/MapperManagerTest.kt @@ -0,0 +1,139 @@ +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 { + @Test + @Throws(PubNubException::class) + fun toJson_anonymousList() { + val mapperManager = MapperManager() + val expected = "[1,2,3]" + val anonList: List = + object : ArrayList() { + init { + add(1) + add(2) + add(3) + } + } + val regularList: MutableList = ArrayList() + regularList.add(1) + regularList.add(2) + regularList.add(3) + val json1 = mapperManager.toJson(anonList) + val json2 = mapperManager.toJson(regularList) + assertEquals(expected, json1) + assertEquals(expected, json2) + } + + @Test + @Throws(PubNubException::class) + fun toJson_anonymousMap() { + val mapperManager = MapperManager() + val expected = "{\"city\":\"Toronto\"}" + val anonMap: HashMap = + object : HashMap() { + init { + put("city", "Toronto") + } + } + val regularMap = HashMap() + regularMap["city"] = "Toronto" + val json1 = mapperManager.toJson(anonMap) + val json2 = mapperManager.toJson(regularMap) + assertEquals(expected, json1) + assertEquals(expected, json2) + } + + @Test + @Throws(PubNubException::class) + fun toJson_anonymousSet() { + val mapperManager = MapperManager() + val expected = "[1,2,3]" + val anonSet: Set = + object : HashSet() { + init { + add(1) + add(2) + add(3) + } + } + val regularSet: MutableSet = HashSet() + regularSet.add(1) + regularSet.add(2) + regularSet.add(3) + val json1 = mapperManager.toJson(anonSet) + val json2 = mapperManager.toJson(regularSet) + assertEquals(expected, json1) + assertEquals(expected, json2) + } + + @Test + fun fromJson_numbers() { + val mapperManager = MapperManager() + val map = mapOf( + "a" to 12345678, + "b" to 1.2313, + "c" to 943809302493249023L, + "d" to 1, + "e" to 0, + "f" to "943809302493249023" + ) + val json = mapperManager.toJson(map) + val decodedMap = mapperManager.fromJson>(json, Map::class.java) + decodedMap.forEach { t, u -> + println("$t: $u (${u!!::class}") + } + assertEquals(map["a"], decodedMap["a"].tryLong()?.toInt()) + assertEquals(map["b"], decodedMap["b"].tryDouble()) + assertEquals(map["c"], decodedMap["c"].tryLong()) + assertEquals(map["d"], decodedMap["d"].tryLong()?.toInt()) + assertEquals(map["e"], decodedMap["e"].tryLong()?.toInt()) + 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? { + return when (this) { + is Number -> toLong() + is String -> toLongOrNull() + else -> null + } +} + +private fun Any?.tryDouble(): Double? { + return when (this) { + is Number -> toDouble() + is String -> toDoubleOrNull() + else -> null + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/managers/RetrofitManagerTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/managers/RetrofitManagerTest.kt new file mode 100644 index 000000000..5775057fc --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/managers/RetrofitManagerTest.kt @@ -0,0 +1,52 @@ +package com.pubnub.internal.managers + +import com.pubnub.api.legacy.BaseTest +import com.pubnub.internal.interceptor.SignatureInterceptor +import org.junit.Assert +import org.junit.Test + +class RetrofitManagerTest : BaseTest() { + @Test + fun `retrofit manager created from another has shared OkHttpClients data`() { + val retrofitManager = RetrofitManager(pubnub, pubnub.configuration) + + val clonedRetrofitManager = RetrofitManager(retrofitManager, pubnub.configuration) + + Assert.assertEquals( + retrofitManager.subscriptionClientInstance!!.dispatcher, + clonedRetrofitManager.subscriptionClientInstance!!.dispatcher, + ) + Assert.assertEquals( + retrofitManager.transactionClientInstance!!.dispatcher, + clonedRetrofitManager.transactionClientInstance!!.dispatcher, + ) + Assert.assertEquals( + retrofitManager.noSignatureClientInstance!!.dispatcher, + clonedRetrofitManager.noSignatureClientInstance!!.dispatcher, + ) + + Assert.assertEquals( + retrofitManager.subscriptionClientInstance!!.connectionPool, + clonedRetrofitManager.subscriptionClientInstance!!.connectionPool, + ) + Assert.assertEquals( + retrofitManager.transactionClientInstance!!.connectionPool, + clonedRetrofitManager.transactionClientInstance!!.connectionPool, + ) + Assert.assertEquals( + retrofitManager.noSignatureClientInstance!!.connectionPool, + clonedRetrofitManager.noSignatureClientInstance!!.connectionPool, + ) + } + + @Test + fun `retrofit manager created from another has separate SignatureInterceptors`() { + val retrofitManager = RetrofitManager(pubnub, pubnub.configuration) + val clonedRetrofitManager = RetrofitManager(retrofitManager, pubnub.configuration) + + Assert.assertNotEquals( + retrofitManager.transactionClientInstance!!.interceptors.single { it is SignatureInterceptor }, + clonedRetrofitManager.subscriptionClientInstance!!.interceptors.single { it is SignatureInterceptor }, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/managers/TokenParserTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/managers/TokenParserTest.kt new file mode 100644 index 000000000..886a5696f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/managers/TokenParserTest.kt @@ -0,0 +1,69 @@ +package com.pubnub.internal.managers + +import com.pubnub.api.models.consumer.access_manager.v3.PNToken +import org.junit.Test +import org.junit.jupiter.api.Assertions.assertEquals +import java.math.BigInteger + +class TokenParserTest { + @Test + fun parseTokenWithMeta() { + val expectedParsedToken = + PNToken( + version = 2, + timestamp = 1632335843, + ttl = 1440, + authorizedUUID = "myauthuuid1", + resources = + PNToken.PNTokenResources( + channels = + mapOf( + "ch1" to + PNToken.PNResourcePermissions( + read = true, write = true, manage = true, delete = true, get = true, update = true, join = true, + ), + ), + channelGroups = + mapOf( + "cg1" to + PNToken.PNResourcePermissions( + read = true, write = true, manage = true, delete = true, get = true, update = true, join = true, + ), + ), + uuids = + mapOf( + "uuid1" to + PNToken.PNResourcePermissions( + read = true, write = true, manage = true, delete = true, get = true, update = true, join = true, + ), + ), + ), + patterns = + PNToken.PNTokenResources( + uuids = + mapOf( + "^\$" to + PNToken.PNResourcePermissions( + read = true, + write = false, + manage = false, + delete = false, + get = false, + update = false, + join = false, + ), + ), + ), + meta = + mapOf( + "score" to BigInteger.valueOf(100), + "color" to "red", + "author" to "pandu", + ), + ) + val tokenWithMeta = + "qEF2AkF0GmFLd-NDdHRsGQWgQ3Jlc6VEY2hhbqFjY2gxGP9DZ3JwoWNjZzEY_0N1c3KgQ3NwY6BEdXVpZKFldXVpZDEY_0NwYXSlRGNoYW6gQ2dycKBDdXNyoENzcGOgRHV1aWShYl4kAURtZXRho2VzY29yZRhkZWNvbG9yY3JlZGZhdXRob3JlcGFuZHVEdXVpZGtteWF1dGh1dWlkMUNzaWdYIP2vlxHik0EPZwtgYxAW3-LsBaX_WgWdYvtAXpYbKll3" + val parsed = TokenParser().unwrapToken(tokenWithMeta) + assertEquals(expectedParsedToken, parsed) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/PresenceTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/PresenceTest.kt new file mode 100644 index 000000000..7cab14aee --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/PresenceTest.kt @@ -0,0 +1,180 @@ +package com.pubnub.internal.presence + +import com.pubnub.api.enums.PNHeartbeatNotificationOptions +import com.pubnub.contract.subscribe.eventEngine.state.TestSinkSource +import com.pubnub.internal.eventengine.EventEngineConf +import com.pubnub.internal.eventengine.QueueEventEngineConf +import com.pubnub.internal.managers.ListenerManager +import com.pubnub.internal.presence.eventengine.data.PresenceData +import com.pubnub.internal.presence.eventengine.effect.PresenceEffectInvocation +import com.pubnub.internal.presence.eventengine.effect.effectprovider.LeaveProvider +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.subscribe.eventengine.effect.successfulRemoteAction +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Assertions.assertInstanceOf +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.Arguments.arguments +import org.junit.jupiter.params.provider.MethodSource +import java.util.concurrent.ScheduledExecutorService +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds + +private const val CHANNEL_01 = "channel01" +private const val CHANNEL_GROUPS_01 = "channelGroups01" + +internal class PresenceTest { + private val listenerManager: ListenerManager = mockk() + private val executorService: ScheduledExecutorService = mockk() + + companion object { + @JvmStatic + fun eventAndMethodProvider(): List { + return listOf( + arguments( + "JOINED", + { presence: Presence -> presence.joined(setOf(CHANNEL_01), setOf(CHANNEL_GROUPS_01)) }, + ), + arguments("LEFT", { presence: Presence -> presence.left(setOf(CHANNEL_01), setOf(CHANNEL_GROUPS_01)) }), + arguments("LEFT_ALL", { presence: Presence -> presence.leftAll() }), + arguments("JOINED", { presence: Presence -> + presence.presence( + setOf(CHANNEL_01), + setOf(CHANNEL_GROUPS_01), + connected = true, + ) + }), + arguments("LEFT", { presence: Presence -> + presence.presence( + setOf(CHANNEL_01), + setOf(CHANNEL_GROUPS_01), + connected = false, + ) + }), + ) + } + } + + @ParameterizedTest + @MethodSource("eventAndMethodProvider") + fun `should pass correct event for each method call`( + eventName: String, + method: Presence.() -> Unit, + ) { + // given + val queuedElements = mutableListOf>() + val presenceData = PresenceData() + val presence = + Presence.create( + listenerManager = listenerManager, + eventEngineConf = QueueEventEngineConf(eventSinkSource = TestSinkSource(queuedElements)), + presenceData = presenceData, + ) + + // when + presence.method() + + // then + Assertions.assertEquals(listOf("event" to eventName), queuedElements) + } + + @Test + fun `create returns PresenceNoOp if heartbeat interval is 0`() { + // when + val presence = Presence.create(listenerManager = listenerManager, heartbeatInterval = 0.seconds) + + // then + assertThat(presence, Matchers.isA(PresenceNoOp::class.java)) + } + + @Test + fun `Leave events not created when suppressLeaveEvents is true and heartbeat interval is 0`() { + // given + val leaveProviderMock: LeaveProvider = mockk() + val presence = + Presence.create( + listenerManager = listenerManager, + heartbeatInterval = 0.seconds, + suppressLeaveEvents = true, + leaveProvider = leaveProviderMock, + ) + + // when + presence.joined(setOf("abc")) + presence.leftAll() + + // then + verify(exactly = 0) { leaveProviderMock.getLeaveRemoteAction(any(), any()) } + } + + @Test + fun `Leave events created when suppressLeaveEvents is false and heartbeat interval is 0`() { + // given + val leaveProviderMock: LeaveProvider = mockk() + every { leaveProviderMock.getLeaveRemoteAction(any(), any()) } returns successfulRemoteAction(true) + + val presence = + Presence.create( + listenerManager = listenerManager, + heartbeatInterval = 0.seconds, + suppressLeaveEvents = false, + leaveProvider = leaveProviderMock, + ) + + // when + presence.joined(setOf("abc")) + presence.leftAll() + + // then + verify { leaveProviderMock.getLeaveRemoteAction(any(), any()) } + } + + @Test + fun `Leave events created when suppressLeaveEvents is false and heartbeat interval is 0 and presence(false) called`() { + // given + val leaveProviderMock: LeaveProvider = mockk() + every { leaveProviderMock.getLeaveRemoteAction(any(), any()) } returns successfulRemoteAction(true) + + val presence = + Presence.create( + listenerManager = listenerManager, + heartbeatInterval = 0.seconds, + suppressLeaveEvents = false, + leaveProvider = leaveProviderMock, + ) + + // when + presence.presence(channels = setOf("abc"), connected = false) + + // then + assertInstanceOf(PresenceNoOp::class.java, presence) + verify { leaveProviderMock.getLeaveRemoteAction(any(), any()) } + } + + private fun Presence.Companion.create( + listenerManager: ListenerManager, + heartbeatInterval: Duration = 3.seconds, + heartbeatNotificationOptions: PNHeartbeatNotificationOptions = PNHeartbeatNotificationOptions.ALL, + eventEngineConf: EventEngineConf = QueueEventEngineConf(), + presenceData: PresenceData = PresenceData(), + suppressLeaveEvents: Boolean = false, + leaveProvider: LeaveProvider = LeaveProvider { _, _ -> successfulRemoteAction(true) }, + ) = create( + heartbeatProvider = { _, _, _ -> successfulRemoteAction(true) }, + leaveProvider = leaveProvider, + heartbeatInterval = heartbeatInterval, + suppressLeaveEvents = suppressLeaveEvents, + heartbeatNotificationOptions = heartbeatNotificationOptions, + listenerManager = listenerManager, + eventEngineConf = eventEngineConf, + presenceData = presenceData, + sendStateWithHeartbeat = true, + executorService = executorService, + ) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/HeartbeatEffectTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/HeartbeatEffectTest.kt new file mode 100644 index 000000000..3bd0d966d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/HeartbeatEffectTest.kt @@ -0,0 +1,140 @@ +package com.pubnub.internal.presence.eventengine.effect + +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNHeartbeatNotificationOptions +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.subscribe.eventengine.effect.StatusConsumer +import com.pubnub.internal.subscribe.eventengine.effect.TestEventSink +import com.pubnub.internal.subscribe.eventengine.effect.failingRemoteAction +import com.pubnub.internal.subscribe.eventengine.effect.successfulRemoteAction +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import org.awaitility.Awaitility +import org.awaitility.Durations +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import java.time.Duration + +class HeartbeatEffectTest { + private val eventSink = TestEventSink() + private val reason = PubNubException("Unknown error") + private val heartbeatNotificationOptions: PNHeartbeatNotificationOptions = PNHeartbeatNotificationOptions.ALL + private val statusConsumer: StatusConsumer = mockk() + + companion object { + @JvmStatic + fun successfulHeartbeatWithNotificationOptions(): List = + listOf( + Arguments.of(PNHeartbeatNotificationOptions.NONE, false), + Arguments.of(PNHeartbeatNotificationOptions.FAILURES, false), + Arguments.of(PNHeartbeatNotificationOptions.ALL, true), + ) + + @JvmStatic + fun unsuccessfulHeartbeatWithNotificationOptions(): List = + listOf( + Arguments.of(PNHeartbeatNotificationOptions.NONE, false), + Arguments.of(PNHeartbeatNotificationOptions.FAILURES, true), + Arguments.of(PNHeartbeatNotificationOptions.ALL, true), + ) + } + + @Test + fun `should deliver HeartbeatSuccess event when HeartbeatEffect succeeded`() { + // given + every { statusConsumer.announce(any()) } returns Unit + val heartbeatEffect = + HeartbeatEffect(successfulRemoteAction(true), eventSink, heartbeatNotificationOptions, statusConsumer) + + // when + heartbeatEffect.runEffect() + + // then + Awaitility.await() + .atMost(Durations.ONE_SECOND) + .with() + .pollInterval(Duration.ofMillis(20)) + .untilAsserted { + assertEquals(listOf(PresenceEvent.HeartbeatSuccess), eventSink.events) + } + } + + @Test + fun `should deliver HeartbeatFailure event when HeartbeatEffect failed`() { + // given + every { statusConsumer.announce(any()) } returns Unit + val heartbeatEffect = + HeartbeatEffect(failingRemoteAction(reason), eventSink, heartbeatNotificationOptions, statusConsumer) + // when + heartbeatEffect.runEffect() + + // then + Awaitility.await() + .atMost(Durations.ONE_SECOND) + .with() + .pollInterval(Duration.ofMillis(20)) + .untilAsserted { + assertEquals(listOf(PresenceEvent.HeartbeatFailure(reason)), eventSink.events) + } + } + + @ParameterizedTest + @MethodSource("successfulHeartbeatWithNotificationOptions") + fun `should announce status when HeartbeatEffect succeeded`( + pnHeartbeatNotificationOptions: PNHeartbeatNotificationOptions, + shouldAnnounce: Boolean, + ) { + // given + val heartbeatNotificationOptions: PNHeartbeatNotificationOptions = pnHeartbeatNotificationOptions + val heartbeatEffect = + HeartbeatEffect(successfulRemoteAction(true), eventSink, heartbeatNotificationOptions, statusConsumer) + + // when + heartbeatEffect.runEffect() + + // then + Awaitility.await() + .atMost(Durations.ONE_SECOND) + .with() + .pollInterval(Duration.ofMillis(20)) + .untilAsserted { + if (shouldAnnounce) { + verify(exactly = 1) { statusConsumer.announce(any()) } + } else { + verify(exactly = 0) { statusConsumer.announce(any()) } + } + } + } + + @ParameterizedTest + @MethodSource("unsuccessfulHeartbeatWithNotificationOptions") + fun `should announce status when HeartbeatEffect failed`( + pnHeartbeatNotificationOptions: PNHeartbeatNotificationOptions, + shouldAnnounce: Boolean, + ) { + // given + val heartbeatNotificationOptions: PNHeartbeatNotificationOptions = pnHeartbeatNotificationOptions + val heartbeatEffect = + HeartbeatEffect(failingRemoteAction(reason), eventSink, heartbeatNotificationOptions, statusConsumer) + + // when + heartbeatEffect.runEffect() + + // then + Awaitility.await() + .atMost(Durations.ONE_SECOND) + .with() + .pollInterval(Duration.ofMillis(20)) + .untilAsserted { + if (shouldAnnounce) { + verify(exactly = 1) { statusConsumer.announce(any()) } + } else { + verify(exactly = 0) { statusConsumer.announce(any()) } + } + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/LeaveEffectTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/LeaveEffectTest.kt new file mode 100644 index 000000000..3315c4e2e --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/LeaveEffectTest.kt @@ -0,0 +1,26 @@ +package com.pubnub.internal.presence.eventengine.effect + +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.verify +import org.junit.jupiter.api.Test + +class LeaveEffectTest { + private val leaveRemoteAction: RemoteAction = mockk() + + @Test + fun `when runEffect is executed should call leaveRemoteAction`() { + // given + val leaveEffect = LeaveEffect(leaveRemoteAction) + every { leaveRemoteAction.async(any()) } just Runs + + // when + leaveEffect.runEffect() + + // then + verify { leaveRemoteAction.async(any()) } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/PresenceEffectFactoryTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/PresenceEffectFactoryTest.kt new file mode 100644 index 000000000..248cf16bf --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/PresenceEffectFactoryTest.kt @@ -0,0 +1,200 @@ +package com.pubnub.internal.presence.eventengine.effect + +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.api.enums.PNHeartbeatNotificationOptions +import com.pubnub.internal.eventengine.Sink +import com.pubnub.internal.presence.eventengine.data.PresenceData +import com.pubnub.internal.presence.eventengine.effect.effectprovider.HeartbeatProvider +import com.pubnub.internal.presence.eventengine.effect.effectprovider.LeaveProvider +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.subscribe.eventengine.effect.StatusConsumer +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.core.IsInstanceOf +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import java.util.concurrent.ScheduledExecutorService +import kotlin.time.Duration.Companion.seconds + +class PresenceEffectFactoryTest { + private lateinit var presenceEffectFactory: PresenceEffectFactory + + private val heartbeatProvider = mockk() + private val leaveProvider = mockk() + private val presenceEventSink = mockk>() + private val executorService = mockk() + private val heartbeatInterval = 10.seconds + private val channels = setOf("channel1") + private val channelGroups = setOf("channelGroup1") + private val heartbeatRemoteAction: RemoteAction = mockk() + private val leaveRemoteAction: RemoteAction = mockk() + private val suppressLeaveEvents = true + private val heartbeatNotificationOptions: PNHeartbeatNotificationOptions = mockk() + private val statusConsumer: StatusConsumer = mockk() + private val presenceData = PresenceData() + + @BeforeEach + fun setUp() { + presenceData.channelStates.clear() + presenceEffectFactory = + PresenceEffectFactory( + heartbeatProvider, + leaveProvider, + presenceEventSink, + executorService, + heartbeatInterval, + suppressLeaveEvents, + heartbeatNotificationOptions, + statusConsumer, + presenceData, + true, + ) + } + + @Test + fun `should return Heartbeat effect when getting Heartbeat invocation`() { + // given + val effectInvocation = PresenceEffectInvocation.Heartbeat(channels, channelGroups) + every { + heartbeatProvider.getHeartbeatRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + any(), + ) + } returns heartbeatRemoteAction + + // when + val effect: HeartbeatEffect = presenceEffectFactory.create(effectInvocation) as HeartbeatEffect + + // then + assertThat(effect, IsInstanceOf.instanceOf(HeartbeatEffect::class.java)) + assertEquals(heartbeatRemoteAction, effect.heartbeatRemoteAction) + assertEquals(presenceEventSink, effect.presenceEventSink) + assertEquals(heartbeatNotificationOptions, effect.heartbeatNotificationOptions) + assertEquals(statusConsumer, effect.statusConsumer) + } + + @Test + fun `should include State from PresenceData into Heartbeat effect when getting Heartbeat invocation`() { + // given + presenceData.channelStates[channels.first()] = mapOf("aaa" to "bbb") + presenceData.channelStates["nonSubscribedChannel"] = mapOf("aaa" to "bbb") + + val effectInvocation = PresenceEffectInvocation.Heartbeat(channels, channelGroups) + every { + heartbeatProvider.getHeartbeatRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + any(), + ) + } returns heartbeatRemoteAction + + // when + presenceEffectFactory.create(effectInvocation) + + // then + verify { + heartbeatProvider.getHeartbeatRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + mapOf( + channels.first() to + mapOf( + "aaa" to "bbb", + ), + ), + ) + } + } + + @Test + fun `should not include State from PresenceData into Heartbeat when sending state is disabled`() { + // given + presenceData.channelStates[channels.first()] = mapOf("aaa" to "bbb") + val effectInvocation = PresenceEffectInvocation.Heartbeat(channels, channelGroups) + every { + heartbeatProvider.getHeartbeatRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + any(), + ) + } returns heartbeatRemoteAction + + // when + PresenceEffectFactory( + heartbeatProvider, + leaveProvider, + presenceEventSink, + executorService, + heartbeatInterval, + suppressLeaveEvents, + heartbeatNotificationOptions, + statusConsumer, + presenceData, + false, + ).create(effectInvocation) + + // then + verify { + heartbeatProvider.getHeartbeatRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + null, + ) + } + } + + @Test + fun `should return null when getting Leave invocation while suppressLeaveEvents is true`() { + // given + val effectInvocation = PresenceEffectInvocation.Leave(channels, channelGroups) + every { + leaveProvider.getLeaveRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + ) + } returns leaveRemoteAction + + // when + val effect = presenceEffectFactory.create(effectInvocation) + + // then + assertNull(effect) + } + + @Test + fun `should return Leave effect when getting Leave invocation while suppressLeaveEvents is false`() { + // given + presenceEffectFactory = + PresenceEffectFactory( + heartbeatProvider, + leaveProvider, + presenceEventSink, + executorService, + heartbeatInterval, + suppressLeaveEvents = false, + heartbeatNotificationOptions, + statusConsumer, + PresenceData(), + true, + ) + val effectInvocation = PresenceEffectInvocation.Leave(channels, channelGroups) + every { + leaveProvider.getLeaveRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + ) + } returns leaveRemoteAction + + // when + val effect: LeaveEffect = presenceEffectFactory.create(effectInvocation) as LeaveEffect + + // then + assertThat(effect, IsInstanceOf.instanceOf(LeaveEffect::class.java)) + assertEquals(leaveRemoteAction, effect.leaveRemoteAction) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/WaitEffectTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/WaitEffectTest.kt new file mode 100644 index 000000000..a279cde47 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/effect/WaitEffectTest.kt @@ -0,0 +1,85 @@ +package com.pubnub.internal.presence.eventengine.effect + +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.subscribe.eventengine.effect.TestEventSink +import org.awaitility.Awaitility +import org.awaitility.Durations +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService +import kotlin.time.Duration.Companion.milliseconds + +class WaitEffectTest { + private val heartbeatInterval = 1.milliseconds + private val presenceEventSink = TestEventSink() + + companion object { + private val executorService: ScheduledExecutorService = Executors.newScheduledThreadPool(10) + + @JvmStatic + @AfterAll + fun tearDown() { + executorService.shutdownNow() + } + } + + @Test + fun `should deliver TimesUp event when WaitEffect finishes successfully`() { + // given + val waitEffect = WaitEffect(heartbeatInterval, presenceEventSink, executorService) + + // when + waitEffect.runEffect() + + // then + Awaitility.await() + .atMost(Durations.ONE_SECOND) + .with() + .pollInterval(java.time.Duration.ofMillis(20)) + .untilAsserted { + assertEquals(listOf(PresenceEvent.TimesUp), presenceEventSink.events) + } + } + + @Test + fun `should not deliver TimesUp event when cancelled on time`() { + // given + val waitEffect = WaitEffect(20.milliseconds, presenceEventSink, executorService) + + // when + waitEffect.runEffect() + waitEffect.cancel() + + assertEquals(emptyList(), presenceEventSink.events) + + // then + Awaitility.await() + .during(java.time.Duration.ofMillis(200)) + .with() + .pollInterval(java.time.Duration.ofMillis(20)) + .untilAsserted { + assertEquals(emptyList(), presenceEventSink.events) + } + } + + @Test + fun `should not deliver TimesUp event when cancelled before runEffect`() { + // given + val waitEffect = WaitEffect(heartbeatInterval, presenceEventSink, executorService) + + // when + waitEffect.cancel() + waitEffect.runEffect() + + // then + Awaitility.await() + .during(java.time.Duration.ofMillis(200)) + .with() + .pollInterval(java.time.Duration.ofMillis(20)) + .untilAsserted { + assertEquals(emptyList(), presenceEventSink.events) + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/event/PresenceEventTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/event/PresenceEventTest.kt new file mode 100644 index 000000000..2639b5226 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/event/PresenceEventTest.kt @@ -0,0 +1,42 @@ +package com.pubnub.internal.presence.eventengine.event + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class PresenceEventTest { + @Test + fun `channel and channelGroup in Joined event should be immutable set`() { + // given + val channelName = "Channel01" + val channelGroupName = "ChannelGroup01" + val myMutableSetOfChannels = mutableSetOf(channelName) + val myMutableSetOfChannelGroups = mutableSetOf(channelGroupName) + val joined: PresenceEvent.Joined = PresenceEvent.Joined(myMutableSetOfChannels, myMutableSetOfChannelGroups) + + // when + myMutableSetOfChannels.remove(channelName) + myMutableSetOfChannelGroups.remove(channelGroupName) + + // then + Assertions.assertTrue(joined.channels.contains(channelName)) + Assertions.assertTrue(joined.channelGroups.contains(channelGroupName)) + } + + @Test + fun `channel and channelGroup in Left event should be immutable set`() { + // given + val channelName = "Channel01" + val channelGroupName = "ChannelGroup01" + val myMutableSetOfChannels = mutableSetOf(channelName) + val myMutableSetOfChannelGroups = mutableSetOf(channelGroupName) + val left: PresenceEvent.Left = PresenceEvent.Left(myMutableSetOfChannels, myMutableSetOfChannelGroups) + + // when + myMutableSetOfChannels.remove(channelName) + myMutableSetOfChannelGroups.remove(channelGroupName) + + // then + Assertions.assertTrue(left.channels.contains(channelName)) + Assertions.assertTrue(left.channelGroups.contains(channelGroupName)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatCooldownStateTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatCooldownStateTest.kt new file mode 100644 index 000000000..86db7a9fe --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatCooldownStateTest.kt @@ -0,0 +1,206 @@ +package com.pubnub.internal.presence.eventengine.state.transition + +import com.pubnub.api.PubNubException +import com.pubnub.internal.eventengine.transition +import com.pubnub.internal.presence.eventengine.effect.PresenceEffectInvocation +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.presence.eventengine.state.PresenceState +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class TransitionFromHeartbeatCooldownStateTest { + val channels = setOf("Channel01", "Channel02") + val channelGroups = setOf("ChannelGroup01", "ChannelGroup02") + val reason = PubNubException("Test") + + @Test + fun `channel and channelGroup should be immutable set`() { + // given + val channelName = "Channel01" + val channelGroupName = "ChannelGroup01" + val myMutableSetOfChannels = mutableSetOf(channelName) + val myMutableSetOfChannelGroups = mutableSetOf(channelGroupName) + val heartbeatCooldown: PresenceState.HeartbeatCooldown = + PresenceState.HeartbeatCooldown(myMutableSetOfChannels, myMutableSetOfChannelGroups) + + // when + myMutableSetOfChannels.remove(channelName) + myMutableSetOfChannelGroups.remove(channelGroupName) + + // then + Assertions.assertTrue(heartbeatCooldown.channels.contains(channelName)) + Assertions.assertTrue(heartbeatCooldown.channelGroups.contains(channelGroupName)) + } + + @Test + fun `should transit from HEARTBEAT_COOLDOWN to INACTIVE and create CANCEL_WAIT and LEAVE invocations when there is LEFT_ALL event`() { + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatCooldown(channels, channelGroups), + PresenceEvent.LeftAll, + ) + + // then + Assertions.assertEquals(PresenceState.HeartbeatInactive, newState) + Assertions.assertEquals( + setOf( + PresenceEffectInvocation.CancelWait, + PresenceEffectInvocation.Leave(channels, channelGroups), + ), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEAT_COOLDOWN to INACTIVE and create CANCEL_WAIT and LEAVE invocations when there is LEFT event and no channels remain`() { + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatCooldown(channels, channelGroups), + PresenceEvent.Left(channels, channelGroups), + ) + + // then + Assertions.assertEquals(PresenceState.HeartbeatInactive, newState) + Assertions.assertEquals( + setOf( + PresenceEffectInvocation.CancelWait, + PresenceEffectInvocation.Leave(channels, channelGroups), + ), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEAT_COOLDOWN to HEARTBEATING and create CANCEL_WAIT, LEAVE and HEARTBEAT invocations when there is LEFT event`() { + // given + val channelToLeave = setOf("Channel01") + val channelGroupToLeave = setOf("ChannelGroup01") + + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatCooldown(channels, channelGroups), + PresenceEvent.Left(channelToLeave, channelGroupToLeave), + ) + + // then + Assertions.assertTrue(newState is PresenceState.Heartbeating) + val heartbeating = newState as PresenceState.Heartbeating + Assertions.assertEquals(channels - channelToLeave, heartbeating.channels) + Assertions.assertEquals(channelGroups - channelGroupToLeave, heartbeating.channelGroups) + Assertions.assertEquals( + setOf( + PresenceEffectInvocation.CancelWait, + PresenceEffectInvocation.Leave(channelToLeave, channelGroupToLeave), + PresenceEffectInvocation.Heartbeat(channels - channelToLeave, channelGroups - channelGroupToLeave), + ), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEAT_COOLDOWN to HEARTBEAT_STOPPED and create CANCEL_WAIT and LEAVE invocations when there is DISCONNECT event`() { + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatCooldown(channels, channelGroups), + PresenceEvent.Disconnect, + ) + + // then + Assertions.assertTrue(newState is PresenceState.HeartbeatStopped) + val heartbeatStopped = newState as PresenceState.HeartbeatStopped + Assertions.assertEquals(channels, heartbeatStopped.channels) + Assertions.assertEquals(channelGroups, heartbeatStopped.channelGroups) + Assertions.assertEquals( + setOf( + PresenceEffectInvocation.CancelWait, + PresenceEffectInvocation.Leave(channels, channelGroups), + ), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEAT_COOLDOWN to HEARTBEATING and create CANCEL_WAIT and HEARTBEAT invocations when there is JOINED event`() { + // given + val newChannels = channels + setOf("NewChannel") + val newChannelGroup = channelGroups + setOf("NewChannelGroup") + + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatCooldown(channels, channelGroups), + PresenceEvent.Joined(newChannels, newChannelGroup), + ) + + // then + Assertions.assertTrue(newState is PresenceState.Heartbeating) + val heartbeating = newState as PresenceState.Heartbeating + Assertions.assertEquals(newChannels, heartbeating.channels) + Assertions.assertEquals(newChannelGroup, heartbeating.channelGroups) + Assertions.assertEquals( + setOf( + PresenceEffectInvocation.CancelWait, + PresenceEffectInvocation.Heartbeat(newChannels, newChannelGroup), + ), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEAT_COOLDOWN to HEARTBEATING and create CANCEL_WAIT and HEARTBEAT invocations when there is STATE_SET event`() { + // given + val newChannels = channels + setOf("NewChannel") + val newChannelGroup = channelGroups + setOf("NewChannelGroup") + + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatCooldown(channels, channelGroups), + PresenceEvent.Joined(newChannels, newChannelGroup), + ) + + // then + Assertions.assertTrue(newState is PresenceState.Heartbeating) + val heartbeating = newState as PresenceState.Heartbeating + Assertions.assertEquals(newChannels, heartbeating.channels) + Assertions.assertEquals(newChannelGroup, heartbeating.channelGroups) + Assertions.assertEquals( + setOf( + PresenceEffectInvocation.CancelWait, + PresenceEffectInvocation.Heartbeat(newChannels, newChannelGroup), + ), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEAT_COOLDOWN to HEARTBEATING and create CANCEL_WAIT and HEARTBEAT invocations when there is TIMES_UP event`() { + // given + val newChannels = channels + setOf("NewChannel") + val newChannelGroup = channelGroups + setOf("NewChannelGroup") + + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatCooldown(channels, channelGroups), + PresenceEvent.Joined(newChannels, newChannelGroup), + ) + + // then + Assertions.assertTrue(newState is PresenceState.Heartbeating) + val heartbeating = newState as PresenceState.Heartbeating + Assertions.assertEquals(newChannels, heartbeating.channels) + Assertions.assertEquals(newChannelGroup, heartbeating.channelGroups) + Assertions.assertEquals( + setOf( + PresenceEffectInvocation.CancelWait, + PresenceEffectInvocation.Heartbeat(newChannels, newChannelGroup), + ), + invocations, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatFailedStateTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatFailedStateTest.kt new file mode 100644 index 000000000..c4cf6df59 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatFailedStateTest.kt @@ -0,0 +1,159 @@ +package com.pubnub.internal.presence.eventengine.state.transition + +import com.pubnub.api.PubNubException +import com.pubnub.internal.eventengine.transition +import com.pubnub.internal.presence.eventengine.effect.PresenceEffectInvocation +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.presence.eventengine.state.PresenceState +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class TransitionFromHeartbeatFailedStateTest { + val channels = setOf("Channel01", "Channel02") + val channelGroups = setOf("ChannelGroup01", "ChannelGroup02") + val reason = PubNubException("Test") + + @Test + fun `channel and channelGroup should be immutable set`() { + // given + val channelName = "Channel01" + val channelGroupName = "ChannelGroup01" + val myMutableSetOfChannels = mutableSetOf(channelName) + val myMutableSetOfChannelGroups = mutableSetOf(channelGroupName) + val heartbeatFailed: PresenceState.HeartbeatFailed = + PresenceState.HeartbeatFailed(myMutableSetOfChannels, myMutableSetOfChannelGroups, reason) + + // when + myMutableSetOfChannels.remove(channelName) + myMutableSetOfChannelGroups.remove(channelGroupName) + + // then + Assertions.assertTrue(heartbeatFailed.channels.contains(channelName)) + Assertions.assertTrue(heartbeatFailed.channelGroups.contains(channelGroupName)) + } + + @Test + fun `should transit from HEARTBEAT_FAILED to INACTIVE and create LEAVE invocation when there is LEFT_ALL event`() { + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatFailed(channels, channelGroups, reason), + PresenceEvent.LeftAll, + ) + + // then + Assertions.assertEquals(PresenceState.HeartbeatInactive, newState) + Assertions.assertEquals( + setOf(PresenceEffectInvocation.Leave(channels, channelGroups)), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEAT_FAILED to INACTIVE and create LEAVE invocation when there is LEFT event and no channels remain`() { + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatFailed(channels, channelGroups, reason), + PresenceEvent.Left(channels, channelGroups), + ) + + // then + Assertions.assertEquals(PresenceState.HeartbeatInactive, newState) + Assertions.assertEquals( + setOf(PresenceEffectInvocation.Leave(channels, channelGroups)), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEAT_FAILED to HEARTBEATING and creat HEARTBEAT invocation when there is JOINED event`() { + // given + val newChannels = channels + setOf("NewChannel") + val newChannelGroup = channelGroups + setOf("NewChannelGroup") + + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatFailed(channels, channelGroups, reason), + PresenceEvent.Joined(newChannels, newChannelGroup), + ) + + // then + Assertions.assertTrue(newState is PresenceState.Heartbeating) + val heartbeating = newState as PresenceState.Heartbeating + Assertions.assertEquals(newChannels, heartbeating.channels) + Assertions.assertEquals(newChannelGroup, heartbeating.channelGroups) + Assertions.assertEquals( + setOf(PresenceEffectInvocation.Heartbeat(newChannels, newChannelGroup)), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEAT_FAILED to HEARTBEATING and creat HEARTBEAT invocation when there is RECONNECT event`() { + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatFailed(channels, channelGroups, reason), + PresenceEvent.Reconnect, + ) + + // then + Assertions.assertTrue(newState is PresenceState.Heartbeating) + val heartbeating = newState as PresenceState.Heartbeating + Assertions.assertEquals(channels, heartbeating.channels) + Assertions.assertEquals(channelGroups, heartbeating.channelGroups) + Assertions.assertEquals( + setOf(PresenceEffectInvocation.Heartbeat(channels, channelGroups)), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEAT_FAILED to HEARTBEATING and creat HEARTBEAT and LEAVE invocation when there is LEFT event`() { + // given + val channelToLeave = setOf("Channel01") + val channelGroupToLeave = setOf("ChannelGroup01") + + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatFailed(channels, channelGroups, reason), + PresenceEvent.Left(channelToLeave, channelGroupToLeave), + ) + + // then + Assertions.assertTrue(newState is PresenceState.Heartbeating) + val heartbeating = newState as PresenceState.Heartbeating + Assertions.assertEquals(channels - channelToLeave, heartbeating.channels) + Assertions.assertEquals(channelGroups - channelGroupToLeave, heartbeating.channelGroups) + Assertions.assertEquals( + setOf( + PresenceEffectInvocation.Leave(channelToLeave, channelGroupToLeave), + PresenceEffectInvocation.Heartbeat(channels - channelToLeave, channelGroups - channelGroupToLeave), + ), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEAT_FAILED to HEARTBEAT_STOPED when there is DISCONNECT event`() { + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatFailed(channels, channelGroups, reason), + PresenceEvent.Disconnect, + ) + + // then + Assertions.assertTrue(newState is PresenceState.HeartbeatStopped) + val heartbeatStopped = newState as PresenceState.HeartbeatStopped + Assertions.assertEquals(channels, heartbeatStopped.channels) + Assertions.assertEquals(channelGroups, heartbeatStopped.channelGroups) + Assertions.assertEquals( + setOf(PresenceEffectInvocation.Leave(channels, channelGroups)), + invocations, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatInactiveStateTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatInactiveStateTest.kt new file mode 100644 index 000000000..640264acd --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatInactiveStateTest.kt @@ -0,0 +1,62 @@ +package com.pubnub.internal.presence.eventengine.state.transition + +import com.pubnub.internal.eventengine.transition +import com.pubnub.internal.presence.eventengine.effect.PresenceEffectInvocation +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.presence.eventengine.state.PresenceState +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class TransitionFromHeartbeatInactiveStateTest { + val channels = setOf("Channel01") + val channelGroups = setOf("ChannelGroup01") + + @Test + fun `should transit from INACTIVE to HEARTBEATING and creat HEARTBEAT invocation when there is JOINED event`() { + // given + val newChannels = channels + setOf("NewChannel") + val newChannelGroup = channelGroups + setOf("NewChannelGroup") + + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatInactive, + PresenceEvent.Joined(newChannels, newChannelGroup), + ) + + // then + Assertions.assertTrue(newState is PresenceState.Heartbeating) + val heartbeating = newState as PresenceState.Heartbeating + Assertions.assertEquals(newChannels, heartbeating.channels) + Assertions.assertEquals(newChannelGroup, heartbeating.channelGroups) + assertEquals( + setOf(PresenceEffectInvocation.Heartbeat(newChannels, newChannelGroup)), + invocations, + ) + } + + @Test + fun `should not make transition from INACTIVE and create no invocation when there is LEFT event`() { + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatInactive, + PresenceEvent.Left(channels, channelGroups), + ) + + // then + assertEquals(PresenceState.HeartbeatInactive, newState) + assertEquals(emptySet(), invocations) + } + + @Test + fun `should not make transition from INACTIVE when there is RECONNECT event and create no invocation`() { + // when + val (newState, invocations) = transition(PresenceState.HeartbeatInactive, PresenceEvent.Reconnect) + + // then + assertEquals(PresenceState.HeartbeatInactive, newState) + assertEquals(emptySet(), invocations) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatStoppedStateTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatStoppedStateTest.kt new file mode 100644 index 000000000..2c5e45846 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatStoppedStateTest.kt @@ -0,0 +1,124 @@ +package com.pubnub.internal.presence.eventengine.state.transition + +import com.pubnub.internal.eventengine.transition +import com.pubnub.internal.presence.eventengine.effect.PresenceEffectInvocation +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.presence.eventengine.state.PresenceState +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class TransitionFromHeartbeatStoppedStateTest { + val channels = setOf("Channel01", "Channel02") + val channelGroups = setOf("ChannelGroup01", "ChannelGroup02") + + @Test + fun `channel and channelGroup should be immutable set`() { + // given + val channelName = "Channel01" + val channelGroupName = "ChannelGroup01" + val myMutableSetOfChannels = mutableSetOf(channelName) + val myMutableSetOfChannelGroups = mutableSetOf(channelGroupName) + val heartbeatStopped: PresenceState.HeartbeatStopped = + PresenceState.HeartbeatStopped(myMutableSetOfChannels, myMutableSetOfChannelGroups) + + // when + myMutableSetOfChannels.remove(channelName) + myMutableSetOfChannelGroups.remove(channelGroupName) + + // then + assertTrue(heartbeatStopped.channels.contains(channelName)) + assertTrue(heartbeatStopped.channelGroups.contains(channelGroupName)) + } + + @Test + fun `should transit from HEARTBEAT_STOPPED to INACTIVE when there is LEFT_ALL event`() { + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatStopped(channels, channelGroups), + PresenceEvent.LeftAll, + ) + + // then + assertEquals(PresenceState.HeartbeatInactive, newState) + assertEquals(emptySet(), invocations) + } + + @Test + fun `should transit from HEARTBEAT_STOPPED to INACTIVE when there is LEFT event and no channels remain`() { + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatStopped(channels, channelGroups), + PresenceEvent.Left(channels, channelGroups), + ) + + // then + assertTrue(newState is PresenceState.HeartbeatInactive) + assertTrue(invocations.isEmpty()) + } + + @Test + fun `should transit from HEARTBEAT_STOPPED to HEARTBEAT_STOPPED when there is JOIN event`() { + // given + val newChannels = channels + setOf("NewChannel") + val newChannelGroup = channelGroups + setOf("NewChannelGroup") + + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatStopped(channels, channelGroups), + PresenceEvent.Joined(newChannels, newChannelGroup), + ) + + // then + assertTrue(newState is PresenceState.HeartbeatStopped) + val heartbeatStopped = newState as PresenceState.HeartbeatStopped + assertEquals(newChannels, heartbeatStopped.channels) + assertEquals(newChannelGroup, heartbeatStopped.channelGroups) + assertEquals(emptySet(), invocations) + } + + @Test + fun `should transit from HEARTBEAT_STOPPED to HEARTBEAT_STOPPED when there is LEFT event`() { + // given + val channelToLeave = setOf("Channel01") + val channelGroupToLeave = setOf("ChannelGroup01") + + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatStopped(channels, channelGroups), + PresenceEvent.Left(channelToLeave, channelGroupToLeave), + ) + + // then + assertTrue(newState is PresenceState.HeartbeatStopped) + val heartbeatStopped = newState as PresenceState.HeartbeatStopped + assertEquals(channels - channelToLeave, heartbeatStopped.channels) + assertEquals(channelGroups - channelGroupToLeave, heartbeatStopped.channelGroups) + assertEquals(emptySet(), invocations) + } + + @Test + fun `should transit from HEARTBEAT_STOPPED to HEARTBEATING and creat HEARTBEAT invocation when there is RECONNECT event`() { + // when + val (newState, invocations) = + transition( + PresenceState.HeartbeatStopped(channels, channelGroups), + PresenceEvent.Reconnect, + ) + + // then + Assertions.assertTrue(newState is PresenceState.Heartbeating) + val heartbeating = newState as PresenceState.Heartbeating + Assertions.assertEquals(channels, heartbeating.channels) + Assertions.assertEquals(channelGroups, heartbeating.channelGroups) + assertEquals( + setOf(PresenceEffectInvocation.Heartbeat(channels, channelGroups)), + invocations, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatingStateTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatingStateTest.kt new file mode 100644 index 000000000..2f644e2cb --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/presence/eventengine/state/transition/TransitionFromHeartbeatingStateTest.kt @@ -0,0 +1,183 @@ +package com.pubnub.internal.presence.eventengine.state.transition + +import com.pubnub.api.PubNubException +import com.pubnub.internal.eventengine.transition +import com.pubnub.internal.presence.eventengine.effect.PresenceEffectInvocation +import com.pubnub.internal.presence.eventengine.event.PresenceEvent +import com.pubnub.internal.presence.eventengine.state.PresenceState +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class TransitionFromHeartbeatingStateTest { + val channels = setOf("Channel01", "Channel02") + val channelGroups = setOf("ChannelGroup01", "ChannelGroup02") + val reason = PubNubException("Test") + + @Test + fun `channel and channelGroup should be immutable set`() { + // given + val channelName = "Channel01" + val channelGroupName = "ChannelGroup01" + val myMutableSetOfChannels = mutableSetOf(channelName) + val myMutableSetOfChannelGroups = mutableSetOf(channelGroupName) + val heartbeating: PresenceState.Heartbeating = + PresenceState.Heartbeating(myMutableSetOfChannels, myMutableSetOfChannelGroups) + + // when + myMutableSetOfChannels.remove(channelName) + myMutableSetOfChannelGroups.remove(channelGroupName) + + // then + Assertions.assertTrue(heartbeating.channels.contains(channelName)) + Assertions.assertTrue(heartbeating.channelGroups.contains(channelGroupName)) + } + + @Test + fun `should transit from HEARTBEATING to INACTIVE and create LEAVE invocations when there is LEFT_ALL event`() { + // when + val (newState, invocations) = + transition( + PresenceState.Heartbeating(channels, channelGroups), + PresenceEvent.LeftAll, + ) + + // then + Assertions.assertEquals(PresenceState.HeartbeatInactive, newState) + Assertions.assertEquals( + setOf(PresenceEffectInvocation.Leave(channels, channelGroups)), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEATING to HEARTBEAT_STOPPED and create LEAVE invocations when there is DISCONNECT event`() { + // when + val (newState, invocations) = + transition( + PresenceState.Heartbeating(channels, channelGroups), + PresenceEvent.Disconnect, + ) + + // then + Assertions.assertTrue(newState is PresenceState.HeartbeatStopped) + val heartbeatStopped = newState as PresenceState.HeartbeatStopped + Assertions.assertEquals(channels, heartbeatStopped.channels) + Assertions.assertEquals(channelGroups, heartbeatStopped.channelGroups) + Assertions.assertEquals( + setOf(PresenceEffectInvocation.Leave(channels, channelGroups)), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEATING to HEARTBEATING and create HEARTBEAT invocations when there is JOINED event`() { + // given + val newChannels = setOf("NewChannel") + val newChannelGroup = setOf("NewChannelGroup") + + // when + val (newState, invocations) = + transition( + PresenceState.Heartbeating(channels, channelGroups), + PresenceEvent.Joined(newChannels, newChannelGroup), + ) + + // then + Assertions.assertTrue(newState is PresenceState.Heartbeating) + val heartbeating = newState as PresenceState.Heartbeating + Assertions.assertEquals(channels + newChannels, heartbeating.channels) + Assertions.assertEquals(channelGroups + newChannelGroup, heartbeating.channelGroups) + Assertions.assertEquals( + setOf( + PresenceEffectInvocation.Heartbeat(channels + newChannels, channelGroups + newChannelGroup), + ), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEATING to INACTIVE and create LEAVE and HEARTBEAT invocations when there is LEFT event and no channels remain`() { + // given + val channelToLeave = channels + val channelGroupToLeave = channelGroups + + // when + val (newState, invocations) = + transition( + PresenceState.Heartbeating(channels, channelGroups), + PresenceEvent.Left(channelToLeave, channelGroupToLeave), + ) + + // then + Assertions.assertTrue(newState is PresenceState.HeartbeatInactive) + Assertions.assertEquals( + setOf( + PresenceEffectInvocation.Leave(channels, channelGroups), + ), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEATING to HEARTBEATING and create LEAVE and HEARTBEAT invocations when there is LEFT event`() { + // given + val channelToLeave = setOf("Channel01") + val channelGroupToLeave = setOf("ChannelGroup01") + + // when + val (newState, invocations) = + transition( + PresenceState.Heartbeating(channels, channelGroups), + PresenceEvent.Left(channelToLeave, channelGroupToLeave), + ) + + // then + Assertions.assertTrue(newState is PresenceState.Heartbeating) + val heartbeating = newState as PresenceState.Heartbeating + Assertions.assertEquals(channels - channelToLeave, heartbeating.channels) + Assertions.assertEquals(channelGroups - channelGroupToLeave, heartbeating.channelGroups) + Assertions.assertEquals( + setOf( + PresenceEffectInvocation.Leave(channelToLeave, channelGroupToLeave), + PresenceEffectInvocation.Heartbeat(channels - channelToLeave, channelGroups - channelGroupToLeave), + ), + invocations, + ) + } + + @Test + fun `should transit from HEARTBEATING to HEARTBEAT_COOLDOWN and create WAIT invocations when there is HEARTBEAT_SUCCESS event`() { + // when + val (newState, invocations) = + transition( + PresenceState.Heartbeating(channels, channelGroups), + PresenceEvent.HeartbeatSuccess, + ) + + // then + Assertions.assertTrue(newState is PresenceState.HeartbeatCooldown) + val heartbeatCooldown = newState as PresenceState.HeartbeatCooldown + Assertions.assertEquals(channels, heartbeatCooldown.channels) + Assertions.assertEquals(channelGroups, heartbeatCooldown.channelGroups) + Assertions.assertTrue(invocations.any { it is PresenceEffectInvocation.Wait }) + Assertions.assertEquals(1, invocations.size) + } + + @Test + fun `should transit from HEARTBEATING to HEARTBEAT_FAILED when there is HEARTBEAT_FAILURE event`() { + // when + val (newState, invocations) = + transition( + PresenceState.Heartbeating(channels, channelGroups), + PresenceEvent.HeartbeatFailure(reason), + ) + + // then + Assertions.assertTrue(newState is PresenceState.HeartbeatFailed) + val heartbeatFailed = newState as PresenceState.HeartbeatFailed + Assertions.assertEquals(channels, heartbeatFailed.channels) + Assertions.assertEquals(channelGroups, heartbeatFailed.channelGroups) + Assertions.assertEquals(reason, heartbeatFailed.reason) + Assertions.assertEquals(0, invocations.size) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/retry/RetryableCallbackTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/retry/RetryableCallbackTest.kt new file mode 100644 index 000000000..13acbf1ab --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/retry/RetryableCallbackTest.kt @@ -0,0 +1,256 @@ +package com.pubnub.internal.retry + +import com.pubnub.api.retry.RetryConfiguration +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.test.listen +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import okhttp3.ResponseBody +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.atomic.AtomicBoolean +import kotlin.time.Duration.Companion.milliseconds + +class RetryableCallbackTest { + private lateinit var mockCall: Call + private lateinit var mockResponse: Response + private var onFinalResponseCalled = false + private var onFinalFailureCalled = false + + @BeforeEach + fun setUp() { + mockCall = mockk(relaxed = true) + mockResponse = mockk(relaxed = true) + } + + companion object { + private val executorService: ScheduledExecutorService = Executors.newScheduledThreadPool(10) + + @JvmStatic + @AfterAll + fun tearDown() { + executorService.shutdownNow() + } + } + + private fun getRetryableCallback( + retryConfiguration: RetryConfiguration = RetryConfiguration.None, + onFinalFailureFinished: AtomicBoolean = AtomicBoolean(false), + onFinalResponseFinished: AtomicBoolean = AtomicBoolean(false), + endpointGroupName: RetryableEndpointGroup = RetryableEndpointGroup.MESSAGE_PERSISTENCE, + ): RetryableCallback { + return object : RetryableCallback( + retryConfiguration = retryConfiguration, + endpointGroupName = endpointGroupName, + call = mockCall, + isEndpointRetryable = true, + executorService = executorService, + ) { + override fun onFinalResponse( + call: Call, + response: Response, + ) { + onFinalResponseCalled = true + onFinalResponseFinished.set(true) + } + + override fun onFinalFailure( + call: Call, + t: Throwable, + ) { + onFinalFailureCalled = true + onFinalFailureFinished.set(true) + } + } + } + + @Test + fun `should not retry when response is successful`() { + // given + val retryableCallback = getRetryableCallback() + every { mockResponse.isSuccessful } returns true + + // when + retryableCallback.onResponse(mockCall, mockResponse) + + // then + Assertions.assertTrue(onFinalResponseCalled) + } + + @Test + fun `should not retry when response is not successful and retryConfiguration not defined`() { + // given + val retryableCallback = getRetryableCallback() + val errorResponse: Response = Response.error(500, ResponseBody.create(null, "")) + + // when + retryableCallback.onResponse(mockCall, errorResponse) + + // then + Assertions.assertTrue(onFinalResponseCalled) + verify(exactly = 0) { mockCall.enqueue(retryableCallback) } + verify(exactly = 0) { mockCall.clone() } + } + + @Test + fun `should retry when linear retryConfiguration is set and SocketTimeoutException`() { + // given + val success = AtomicBoolean() + + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + val retryableCallback = + getRetryableCallback( + retryConfiguration = + RetryConfiguration.Linear.createForTest( + delayInSec = 10.milliseconds, + maxRetryNumber = 3, + isInternal = true, + ), + onFinalResponseFinished = success, + ) + val errorResponse: Response = Response.error(500, ResponseBody.create(null, "")) + every { mockResponse.isSuccessful } returns false + every { mockResponse.code() } returns 500 // Assuming 500 is a retryable error + val successfulResponse: Response = Response.success(null) + every { mockCall.enqueue(any()) } answers { + val callback = arg>(0) + callback.onFailure(mockCall, UnknownHostException()) + } andThenAnswer { + val callback = arg>(0) + callback.onFailure(mockCall, UnknownHostException()) + } andThenAnswer { + val callback = arg>(0) + callback.onResponse(mockCall, successfulResponse) + } + every { mockCall.clone() } returns mockCall + + // when + retryableCallback.onResponse(mockCall, errorResponse) + success.listen(10) + + // then + verify(exactly = 3) { mockCall.enqueue(retryableCallback) } + verify(exactly = 3) { mockCall.clone() } + } + + @Test + fun `should retry onResponse when exponential retryConfiguration is set and retryable error 500 occurs`() { + // given + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + val retryConfiguration = + RetryConfiguration.Exponential.createForTest( + minDelayInSec = 10.milliseconds, + maxDelayInSec = 15.milliseconds, + maxRetryNumber = 2, + isInternal = true, + ) + val success = AtomicBoolean() + val retryableCallback = + getRetryableCallback(retryConfiguration = retryConfiguration, onFinalResponseFinished = success) + val errorResponse: Response = Response.error(500, ResponseBody.create(null, "")) + every { mockResponse.isSuccessful } returns false + every { mockResponse.code() } returns 500 // Assuming 500 is a retryable error + val successfulResponse: Response = Response.success(null) + every { mockCall.enqueue(any()) } answers { + val callback = arg>(0) + callback.onResponse(mockCall, errorResponse) + } andThenAnswer { + val callback = arg>(0) + callback.onResponse(mockCall, successfulResponse) + } + every { mockCall.clone() } returns mockCall + + // when + retryableCallback.onResponse(mockCall, errorResponse) + success.listen(10) + + // then + verify(exactly = 2) { mockCall.enqueue(retryableCallback) } + verify(exactly = 2) { mockCall.clone() } + Assertions.assertTrue(onFinalResponseCalled) + } + + @Test + fun `should retry onFailure when exponential retryConfiguration is set and SocketTimeoutException`() { + // given + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + val retryConfiguration = + RetryConfiguration.Exponential.createForTest( + minDelayInSec = 10.milliseconds, + maxDelayInSec = 15.milliseconds, + maxRetryNumber = 2, + isInternal = true, + ) + val success = AtomicBoolean() + val retryableCallback = + getRetryableCallback(retryConfiguration = retryConfiguration, onFinalResponseFinished = success) + every { mockResponse.isSuccessful } returns false + every { mockResponse.code() } returns 500 // Assuming 500 is a retryable error + val successfulResponse: Response = Response.success(null) + every { mockCall.enqueue(any()) } answers { + val callback = arg>(0) + callback.onFailure(mockCall, SocketTimeoutException()) + } andThenAnswer { + val callback = arg>(0) + callback.onResponse(mockCall, successfulResponse) + } + every { mockCall.clone() } returns mockCall + + // when + retryableCallback.onFailure(mockCall, SocketTimeoutException()) + success.listen(10) + + // then + verify(exactly = 2) { mockCall.enqueue(retryableCallback) } + verify(exactly = 2) { mockCall.clone() } + Assertions.assertTrue(onFinalResponseCalled) + } + + @Test + fun `should retry onFailure and fail when exponential retryConfiguration is set and UnknownHostException`() { + // given + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + val retryConfiguration = + RetryConfiguration.Exponential.createForTest( + minDelayInSec = 10.milliseconds, + maxDelayInSec = 15.milliseconds, + maxRetryNumber = 2, + isInternal = true, + ) + val success = AtomicBoolean() + val retryableCallback = + getRetryableCallback(retryConfiguration = retryConfiguration, onFinalFailureFinished = success) + every { mockResponse.isSuccessful } returns false + every { mockResponse.code() } returns 500 // Assuming 500 is a retryable error + every { mockCall.enqueue(any()) } answers { + val callback = arg>(0) + callback.onFailure(mockCall, UnknownHostException()) + } andThenAnswer { + val callback = arg>(0) + callback.onFailure(mockCall, UnknownHostException()) + } andThenAnswer { + val callback = arg>(0) + callback.onFailure(mockCall, UnknownHostException()) + } + every { mockCall.clone() } returns mockCall + + // when + retryableCallback.onFailure(mockCall, UnknownHostException()) + success.listen(10) + + // then + verify(exactly = 2) { mockCall.enqueue(retryableCallback) } + verify(exactly = 2) { mockCall.clone() } + Assertions.assertTrue(onFinalFailureCalled) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/retry/RetryableRestCallerTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/retry/RetryableRestCallerTest.kt new file mode 100644 index 000000000..c802e3d13 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/retry/RetryableRestCallerTest.kt @@ -0,0 +1,396 @@ +package com.pubnub.internal.retry + +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.retry.RetryConfiguration +import com.pubnub.api.retry.RetryableEndpointGroup +import com.pubnub.internal.models.server.FetchMessagesEnvelope +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import okhttp3.ResponseBody +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import retrofit2.Call +import retrofit2.Response +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds + +private const val RETRY_AFTER_HEADER_NAME = "Retry-After" +private const val RETRY_AFTER_VALUE = "3" + +class RetryableRestCallerTest { + private fun getRetryableRestCaller( + retryConfiguration: RetryConfiguration, + isEndpointRetryable: Boolean = true, + endpointGroupName: RetryableEndpointGroup = RetryableEndpointGroup.MESSAGE_PERSISTENCE, + ): RetryableRestCaller { + return RetryableRestCaller(retryConfiguration, endpointGroupName, isEndpointRetryable) + } + + @Test + fun `should use Retry-after header from 429 error when value available and linear retryConfiguration is set`() { + // given + val retryConfiguration = RetryConfiguration.Linear(delayInSec = 2, maxRetryNumber = 2) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration) + + val response = mockk>() + every { response.raw().code } returns 429 + every { response.headers()[RETRY_AFTER_HEADER_NAME] } returns RETRY_AFTER_VALUE + + // when + val delay: Duration = retryableRestCaller.getDelayBasedOnResponse(response) + + // then + Assertions.assertEquals(RETRY_AFTER_VALUE.toLong(), delay.inWholeSeconds) + } + + @Test + fun `should use delay from retryConfiguration when value of Retry-after header from 429 error is null and linear retryConfiguration is set`() { + // given + val delayInSec = 2 + val retryConfiguration = RetryConfiguration.Linear(delayInSec = delayInSec, maxRetryNumber = 2) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration) + + val response = mockk>() + every { response.raw().code } returns 429 + every { response.headers()[RETRY_AFTER_HEADER_NAME] } returns null + // when + val delay = retryableRestCaller.getDelayBasedOnResponse(response) + + // then + Assertions.assertEquals(delayInSec.toLong(), delay.inWholeSeconds) + } + + @Test + fun `should use delay from retryConfiguration when error is nt 429 and linear retryConfiguration is set`() { + // given + val delayInSec = 2 + val retryConfiguration = RetryConfiguration.Linear(delayInSec = delayInSec, maxRetryNumber = 2) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration) + + val response = mockk>() + every { response.raw().code } returns 500 + every { response.headers()[RETRY_AFTER_HEADER_NAME] } returns null + // when + val delay = retryableRestCaller.getDelayBasedOnResponse(response) + + // then + Assertions.assertEquals(delayInSec.toLong(), delay.inWholeSeconds) + } + + @Test + fun `should use delay from retryConfiguration when value of Retry-after header from 429 error can not be parsed to int and linear retryConfiguration is set`() { + // given + val delayInSec = 2 + val retryConfiguration = RetryConfiguration.Linear(delayInSec = delayInSec, maxRetryNumber = 2) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration) + + val response = mockk>() + every { response.raw().code } returns 429 + every { response.headers()[RETRY_AFTER_HEADER_NAME] } returns "20 seconds" + + // when + val delay = retryableRestCaller.getDelayBasedOnResponse(response) + + // then + Assertions.assertEquals(delayInSec.toLong(), delay.inWholeSeconds) + } + + @Test + fun `should calculate delay in exponential manner when exponential retryConfiguration is set`() { + // given + val retryConfiguration = + RetryConfiguration.Exponential( + minDelayInSec = 2, + maxDelayInSec = 7, + maxRetryNumber = 2, + excludedOperations = emptyList(), + ) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration) + + // when + val delayFromRetryConfiguration01 = retryableRestCaller.getDelayFromRetryConfiguration() + val delayFromRetryConfiguration02 = retryableRestCaller.getDelayFromRetryConfiguration() + val delayFromRetryConfiguration03 = retryableRestCaller.getDelayFromRetryConfiguration() + val delayFromRetryConfiguration04 = retryableRestCaller.getDelayFromRetryConfiguration() + + // then + Assertions.assertEquals(2, delayFromRetryConfiguration01.inWholeSeconds) + Assertions.assertEquals(4, delayFromRetryConfiguration02.inWholeSeconds) + Assertions.assertEquals(7, delayFromRetryConfiguration03.inWholeSeconds) + Assertions.assertEquals(7, delayFromRetryConfiguration04.inWholeSeconds) + } + + @Test + fun `when retryConfiguration is None then each restCall is excluded from retry`() { + // given + val retryConfiguration = RetryConfiguration.None + val retryableRestCaller = getRetryableRestCaller(retryConfiguration) + + // when + val retryConfigurationSetForThisRestCall = + retryableRestCaller.isRetryConfSetForThisRestCall + + // then + Assertions.assertFalse(retryConfigurationSetForThisRestCall) + } + + @Test + fun `when retryConfiguration is Linear and endpoint belong to RetryableEndpointGroup that is excluded from retryConfiguration then restCall is excluded from retry`() { + // given + val retryConfiguration = + RetryConfiguration.Linear( + delayInSec = 2, + maxRetryNumber = 2, + excludedOperations = listOf(RetryableEndpointGroup.MESSAGE_PERSISTENCE), + ) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration) + + // when + val retryConfigurationSetForThisRestCall = retryableRestCaller.isRetryConfSetForThisRestCall + + // then + Assertions.assertFalse(retryConfigurationSetForThisRestCall) + } + + @Test + fun `when retryConfiguration is Exponential and endpoint belong to RetryableEndpointGroup that is excluded from retryConfiguration then restCall is excluded from retry`() { + // given + val retryConfiguration = + RetryConfiguration.Exponential( + minDelayInSec = 2, + maxDelayInSec = 7, + maxRetryNumber = 2, + excludedOperations = listOf(RetryableEndpointGroup.MESSAGE_PERSISTENCE), + ) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration) + + // when + val retryConfigurationSetForThisRestCall = retryableRestCaller.isRetryConfSetForThisRestCall + + // then + Assertions.assertFalse(retryConfigurationSetForThisRestCall) + } + + @Test + fun `when retryConfiguration is Exponential and endpoint belong to RetryableEndpointGroup that is not excluded from retryConfiguration then restCall is retryable`() { + // given + val retryConfiguration = + RetryConfiguration.Exponential( + minDelayInSec = 2, + maxDelayInSec = 7, + maxRetryNumber = 2, + excludedOperations = listOf(), + ) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration) + + // when + val retryConfigurationSetForThisRestCall = retryableRestCaller.isRetryConfSetForThisRestCall + + // then + Assertions.assertTrue(retryConfigurationSetForThisRestCall) + } + + @Test + fun `should execute rest call once when first attempt successful`() { + // given + val retryConfiguration = RetryConfiguration.Linear(delayInSec = 2, maxRetryNumber = 2) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration) + val mockCall = mockk>() + val successfulResponse: Response = Response.success(null) + every { mockCall.execute() } returns successfulResponse + + // when + val response = retryableRestCaller.execute(mockCall) + + // then + verify(exactly = 0) { mockCall.clone() } + verify(exactly = 1) { mockCall.execute() } + Assertions.assertTrue(response.isSuccessful) + } + + @Test + fun `should return unsuccessful response when first attempt failed and retryConfiguration not set`() { + // given + val retryConfiguration = RetryConfiguration.None + val retryableRestCaller = getRetryableRestCaller(retryConfiguration) + val mockCall = mockk>() + val errorResponse: Response = Response.error(429, ResponseBody.create(null, "")) + every { mockCall.execute() } returns errorResponse + + // when + val response = retryableRestCaller.execute(mockCall) + + // then + verify(exactly = 0) { mockCall.clone() } + verify(exactly = 1) { mockCall.execute() } + Assertions.assertFalse(response.isSuccessful) + } + + @Test + fun `should return unsuccessful response when first attempt failed and retryConfiguration set and error code is not retryable`() { + // given + val retryConfiguration = RetryConfiguration.Linear(delayInSec = 2, maxRetryNumber = 2) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration) + val errorResponse: Response = Response.error(404, ResponseBody.create(null, "")) + val mockCall = mockk>() + + every { mockCall.execute() } returns errorResponse + + // when + val response1 = retryableRestCaller.execute(mockCall) + + // then + verify(exactly = 0) { mockCall.clone() } + verify(exactly = 1) { mockCall.execute() } + Assertions.assertFalse(response1.isSuccessful) + } + + @Test + fun `should return unsuccessful response when first attempt failed and endpoint excluded from retryConfiguration`() { + // given + val retryConfiguration = + RetryConfiguration.Linear( + delayInSec = 2, + maxRetryNumber = 2, + excludedOperations = listOf(RetryableEndpointGroup.MESSAGE_PERSISTENCE), + ) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration) + val errorResponse: Response = Response.error(500, ResponseBody.create(null, "")) + val mockCall = mockk>() + + every { mockCall.execute() } returns errorResponse + + // when + val response1 = retryableRestCaller.execute(mockCall) + + // then + verify(exactly = 0) { mockCall.clone() } + verify(exactly = 1) { mockCall.execute() } + Assertions.assertFalse(response1.isSuccessful) + } + + @Test + fun `should return unsuccessful response when first attempt failed and endpoint is not retryable`() { + // given + val retryConfiguration = + RetryConfiguration.Linear( + delayInSec = 2, + maxRetryNumber = 2, + excludedOperations = listOf(RetryableEndpointGroup.MESSAGE_PERSISTENCE), + ) + val retryableRestCaller = + getRetryableRestCaller(retryConfiguration = retryConfiguration, isEndpointRetryable = false) + val errorResponse: Response = Response.error(500, ResponseBody.create(null, "")) + val mockCall = mockk>() + every { mockCall.execute() } returns errorResponse + + // when + val response1 = retryableRestCaller.execute(mockCall) + + // then + verify(exactly = 0) { mockCall.clone() } + verify(exactly = 1) { mockCall.execute() } + Assertions.assertFalse(response1.isSuccessful) + } + + @Test + fun `should retry successfully when linear retryConfiguration is set and response is not successful and http error is 500 and endpoint is not excluded from retryConfiguration`() { + // given + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + val retryConfiguration = + RetryConfiguration.Linear.createForTest(delayInSec = 10.milliseconds, maxRetryNumber = 3, isInternal = true) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration = retryConfiguration) + val mockCall = mockk>() + val errorResponse: Response = + Response.error(500, ResponseBody.create(null, "")) + val successfulResponse: Response = Response.success(null) + every { mockCall.execute() } returns errorResponse andThen errorResponse andThen errorResponse andThen successfulResponse + every { mockCall.clone() } returns mockCall + + // when + val response1 = retryableRestCaller.execute(mockCall) + + // then + verify(exactly = 3) { mockCall.clone() } + verify(exactly = 4) { mockCall.execute() } + Assertions.assertTrue(response1.isSuccessful) + } + + @Test + fun `should retry successfully when exponential retryConfiguration is set and response is not successful and http error is 500 and endpoint is not excluded from retryConfiguration`() { + // given + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + val retryConfiguration = + RetryConfiguration.Exponential.createForTest( + minDelayInSec = 10.milliseconds, + maxDelayInSec = 15.milliseconds, + maxRetryNumber = 2, + excludedOperations = emptyList(), + isInternal = true, + ) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration = retryConfiguration) + val mockCall = mockk>() + val errorResponse: Response = + Response.error(500, ResponseBody.create(null, "")) + val successfulResponse: Response = Response.success(null) + every { mockCall.execute() } returns errorResponse andThen errorResponse andThen successfulResponse + every { mockCall.clone() } returns mockCall + + // when + val response1 = retryableRestCaller.execute(mockCall) + + // then + verify(exactly = 2) { mockCall.clone() } + verify(exactly = 3) { mockCall.execute() } + Assertions.assertTrue(response1.isSuccessful) + } + + @Test + fun `should retry successfully when linear retryConfiguration is set and SocketTimeoutException is thrown`() { + // given + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + val retryConfiguration = + RetryConfiguration.Linear.createForTest(delayInSec = 10.milliseconds, maxRetryNumber = 2, isInternal = true) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration = retryConfiguration) + val mockCall = mockk>() + val successfulResponse: Response = Response.success(null) + every { mockCall.execute() } throws SocketTimeoutException() andThenThrows SocketTimeoutException() andThen successfulResponse + every { mockCall.clone() } returns mockCall + + // when + val response1 = retryableRestCaller.execute(mockCall) + + // then + verify(exactly = 2) { mockCall.clone() } + verify(exactly = 3) { mockCall.execute() } + Assertions.assertTrue(response1.isSuccessful) + } + + @Test + fun `should retry and throw exception when linear retryConfiguration is set and UnknownHostException is thrown`() { + // given + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + val retryConfiguration = + RetryConfiguration.Linear.createForTest(delayInSec = 10.milliseconds, maxRetryNumber = 2, isInternal = true) + val retryableRestCaller = getRetryableRestCaller(retryConfiguration = retryConfiguration) + val mockCall = mockk>() + every { mockCall.execute() } throws UnknownHostException() andThenThrows UnknownHostException() andThenThrows UnknownHostException() + every { mockCall.clone() } returns mockCall + + // when + val exception = + Assertions.assertThrows(PubNubException::class.java) { + retryableRestCaller.execute(mockCall) + } + + // then + verify(exactly = 2) { mockCall.clone() } + verify(exactly = 3) { mockCall.execute() } + Assertions.assertEquals("java.net.UnknownHostException", exception.errorMessage) + Assertions.assertEquals(PubNubError.PARSING_ERROR, exception.pubnubError) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/SubscribeTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/SubscribeTest.kt new file mode 100644 index 000000000..61e79c9ce --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/SubscribeTest.kt @@ -0,0 +1,190 @@ +package com.pubnub.internal.subscribe + +import com.pubnub.internal.managers.SubscribeEventEngineManager +import com.pubnub.internal.presence.eventengine.data.PresenceData +import com.pubnub.internal.subscribe.eventengine.data.SubscriptionData +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import io.mockk.CapturingSlot +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot +import io.mockk.verify +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +private const val CHANNEL_01 = "channel01" +private const val CHANNEL_02 = "channel02" +private const val CHANNEL_03 = "channel03" + +private const val CHANNEL_GROUPS_01 = "channelGroups01" +private const val CHANNEL_GROUPS_02 = "channelGroups02" +private const val CHANNEL_GROUPS_03 = "channelGroups03" + +private const val PNPRES = "-pnpres" + +internal class SubscribeTest { + private val channelsInSubscriptionData = mutableSetOf(CHANNEL_01, CHANNEL_02) + private val channelGroupsInSubscriptionData = mutableSetOf(CHANNEL_GROUPS_01, CHANNEL_GROUPS_02) + private lateinit var objectUnderTest: Subscribe + private val subscribeEventEngineManager: SubscribeEventEngineManager = mockk() + private var subscriptionData: SubscriptionData = createSubscriptionStateContainingValues() + private val withPresence = false + private val withTimetoken = 12345345452L + private val subscribeEvent: CapturingSlot = slot() + private val presenceData = PresenceData() + + @BeforeEach + internal fun setUp() { + every { subscribeEventEngineManager.addEventToQueue(capture(subscribeEvent)) } returns Unit + objectUnderTest = Subscribe(subscribeEventEngineManager, presenceData, subscriptionData) + } + + @Test + fun `should store channels and channelGroups in local storage and pass SubscriptionChange event for handling when timeToken is zero`() { + val channelToSubscribe = CHANNEL_03 + val channelGroupsToSubscribe = CHANNEL_GROUPS_03 + + objectUnderTest.subscribe(setOf(channelToSubscribe), setOf(channelGroupsToSubscribe), withPresence) + + verify { subscribeEventEngineManager.addEventToQueue(any()) } + + val capturedSubscriptionChanged = subscribeEvent.captured + assertTrue(capturedSubscriptionChanged is SubscribeEvent.SubscriptionChanged) + val subscriptionChanged = capturedSubscriptionChanged as SubscribeEvent.SubscriptionChanged + assertEquals(channelsInSubscriptionData + channelToSubscribe, subscriptionChanged.channels) + assertEquals(channelGroupsInSubscriptionData + channelGroupsToSubscribe, subscriptionChanged.channelGroups) + } + + @Test + fun `should store channels and channelGroups in local storage and pass SubscriptionRestored event for handling when timeToken is non zero`() { + val channelToSubscribe = CHANNEL_03 + val channelGroupsToSubscribe = CHANNEL_GROUPS_03 + + objectUnderTest.subscribe( + setOf(channelToSubscribe), + setOf(channelGroupsToSubscribe), + withPresence, + withTimetoken, + ) + + verify { subscribeEventEngineManager.addEventToQueue(any()) } + + val capturedSubscribeEvent = subscribeEvent.captured + assertTrue(capturedSubscribeEvent is SubscribeEvent.SubscriptionRestored) + val subscriptionRestored = capturedSubscribeEvent as SubscribeEvent.SubscriptionRestored + assertEquals(channelsInSubscriptionData + channelToSubscribe, subscriptionRestored.channels) + assertEquals(channelGroupsInSubscriptionData + channelGroupsToSubscribe, subscriptionRestored.channelGroups) + } + + @Test + fun `should remove channels and channelGroups from local storage and pass SubscriptionChange event for handling when at least one channel or channelGroup left in storage`() { + objectUnderTest.subscribe( + setOf(CHANNEL_01, CHANNEL_02), + setOf(CHANNEL_GROUPS_01, CHANNEL_GROUPS_02), + withPresence = true, + withTimetoken, + ) + val channelsToUnsubscribe: Set = setOf(CHANNEL_01) + val channelGroupsToUnsubscribe: Set = setOf(CHANNEL_GROUPS_01) + + objectUnderTest.unsubscribe(channelsToUnsubscribe, channelGroupsToUnsubscribe) + + verify { subscribeEventEngineManager.addEventToQueue(any()) } + val capturedSubscriptionChanged = subscribeEvent.captured + assertTrue(capturedSubscriptionChanged is SubscribeEvent.SubscriptionChanged) + val subscriptionChanged = capturedSubscriptionChanged as SubscribeEvent.SubscriptionChanged + assertEquals(setOf(CHANNEL_02, "$CHANNEL_02$PNPRES"), subscriptionChanged.channels) + assertEquals(setOf(CHANNEL_GROUPS_02, "$CHANNEL_GROUPS_02$PNPRES"), subscriptionChanged.channelGroups) + assertEquals(setOf(CHANNEL_02, "$CHANNEL_02$PNPRES"), subscriptionData.channels) + assertEquals(setOf(CHANNEL_GROUPS_02, "$CHANNEL_GROUPS_02$PNPRES"), subscriptionData.channelGroups) + } + + @Test + fun `should remove channels and channelGroups from local storage and pass UnsubscribeAll event for handling when no channel nor channelGroup left in storage`() { + val channelsToUnsubscribe: Set = setOf(CHANNEL_01, CHANNEL_02) + val channelGroupsToUnsubscribe: Set = setOf(CHANNEL_GROUPS_01, CHANNEL_GROUPS_02) + + objectUnderTest.unsubscribe(channelsToUnsubscribe, channelGroupsToUnsubscribe) + + verify { subscribeEventEngineManager.addEventToQueue(any()) } + assertEquals(SubscribeEvent.UnsubscribeAll, subscribeEvent.captured) + assertTrue(subscriptionData.channels.size == 0) + assertTrue(subscriptionData.channelGroups.size == 0) + } + + @Test + fun `should remove all channels and channelGroups from local storage when unsubscribeAll`() { + objectUnderTest.unsubscribeAll() + + verify { subscribeEventEngineManager.addEventToQueue(any()) } + assertEquals(SubscribeEvent.UnsubscribeAll, subscribeEvent.captured) + assertTrue(subscriptionData.channels.size == 0) + assertTrue(subscriptionData.channelGroups.size == 0) + } + + @Test + fun `should return subscribed channels from local storage when getSubscribedChannels`() { + val subscribedChannels = objectUnderTest.getSubscribedChannels() + + assertEquals(channelsInSubscriptionData.toList(), subscribedChannels) + } + + @Test + fun `should return subscribed channelGroups from local storage when getSubscribedChannelGroups`() { + val subscribedChannelGroups = objectUnderTest.getSubscribedChannelGroups() + + assertEquals(channelGroupsInSubscriptionData.toList(), subscribedChannelGroups) + } + + @Test + fun `should pass disconnect event for handling when disconnect`() { + objectUnderTest.disconnect() + + assertEquals(SubscribeEvent.Disconnect, subscribeEvent.captured) + } + + @Test + fun `should pass reconnect event for handling when reconnect`() { + objectUnderTest.reconnect() + + assertEquals(SubscribeEvent.Reconnect(), subscribeEvent.captured) + } + + @Test + fun `should remove presence data when unsubscribing`() { + // given + val channel = "someChannel" + presenceData.channelStates[channel] = Any() + + // when + objectUnderTest.unsubscribe(setOf(channel)) + + // then + assertFalse(channel in presenceData.channelStates.keys) + } + + @Test + fun `should remove all presence data when unsubscribeAll`() { + // given + val channel = "someChannel" + val otherChannel = "otherChannel" + presenceData.channelStates[channel] = Any() + presenceData.channelStates[otherChannel] = Any() + + // when + objectUnderTest.unsubscribeAll() + + // then + assertTrue(presenceData.channelStates.isEmpty()) + } + + private fun createSubscriptionStateContainingValues(): SubscriptionData { + subscriptionData = SubscriptionData() + subscriptionData.channels.addAll(channelsInSubscriptionData) + subscriptionData.channelGroups.addAll(channelGroupsInSubscriptionData) + return subscriptionData + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitMessagesEffectTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitMessagesEffectTest.kt new file mode 100644 index 000000000..9b43d703c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitMessagesEffectTest.kt @@ -0,0 +1,250 @@ +package com.pubnub.internal.subscribe.eventengine.effect + +import com.google.gson.JsonPrimitive +import com.pubnub.api.models.consumer.files.PNDownloadableFile +import com.pubnub.api.models.consumer.message_actions.PNMessageAction +import com.pubnub.api.models.consumer.pubsub.BasePubSubResult +import com.pubnub.api.models.consumer.pubsub.PNEvent +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.api.models.consumer.pubsub.PNSignalResult +import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult +import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult +import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult +import com.pubnub.api.models.consumer.pubsub.objects.PNSetMembershipEvent +import com.pubnub.api.models.consumer.pubsub.objects.PNSetMembershipEventMessage +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot +import io.mockk.verify +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class EmitMessagesEffectTest { + private val messagesConsumer: MessagesConsumer = mockk() + private val message = "Message1" + private val messageActionType = "reaction" + private val objectEventType = "membership" + + @Test + fun `should announce PNMessageResult when PNMessageResult exist in messages`() { + // given + val messages: List = listOf(PNMessageResult(createBasePubSubResult(), JsonPrimitive(message))) + val emitMessagesEffect = EmitMessagesEffect(messagesConsumer, messages) + val pnEventCapture = slot() + every { messagesConsumer.announce(capture(pnEventCapture)) } returns Unit + + // when + emitMessagesEffect.runEffect() + + // then + verify(exactly = 1) { messagesConsumer.announce(capture(pnEventCapture)) } + assertEquals(message, pnEventCapture.captured.message.asString) + } + + @Test + fun `should announce PNSignalResult when PNSignalResult exist in messages`() { + // given + val messages: List = listOf(PNSignalResult(createBasePubSubResult(), JsonPrimitive(message))) + val emitMessagesEffect = EmitMessagesEffect(messagesConsumer, messages) + val pnEventCapture = slot() + every { messagesConsumer.announce(capture(pnEventCapture)) } returns Unit + + // when + emitMessagesEffect.runEffect() + + // then + verify(exactly = 1) { messagesConsumer.announce(capture(pnEventCapture)) } + assertEquals(message, pnEventCapture.captured.message.asString) + } + + @Test + fun `should announce PNFileEventResult when PNFileEventResult exist in messages`() { + // given + val messages: List = listOf(createPnFileEventResult(message)) + val emitMessagesEffect = EmitMessagesEffect(messagesConsumer, messages) + val pnEventCapture = slot() + every { messagesConsumer.announce(capture(pnEventCapture)) } returns Unit + + // when + emitMessagesEffect.runEffect() + + // then + verify(exactly = 1) { messagesConsumer.announce(capture(pnEventCapture)) } + assertEquals(message, pnEventCapture.captured.jsonMessage.asString) + } + + @Test + fun `should announce PNMessageActionResult when PNMessageActionResult exist in messages`() { + // given + val messages: List = listOf(createPnMessageActionResult(messageActionType)) + val emitMessagesEffect = EmitMessagesEffect(messagesConsumer, messages) + val pnEventCapture = slot() + every { messagesConsumer.announce(capture(pnEventCapture)) } returns Unit + + // when + emitMessagesEffect.runEffect() + + // then + verify(exactly = 1) { messagesConsumer.announce(capture(pnEventCapture)) } + assertEquals(messageActionType, pnEventCapture.captured.data.type) + } + + @Test + fun `should announce PNObjectEventResult when PNObjectEventResult exist in messages`() { + // given + val messages: List = listOf(createPnObjectEventResult(objectEventType)) + val emitMessagesEffect = EmitMessagesEffect(messagesConsumer, messages) + val pnEventCapture = slot() + every { messagesConsumer.announce(capture(pnEventCapture)) } returns Unit + + // when + emitMessagesEffect.runEffect() + + // then + verify(exactly = 1) { messagesConsumer.announce(capture(pnEventCapture)) } + assertEquals(objectEventType, pnEventCapture.captured.extractedMessage.type) + } + + @Test + fun `should announce all types of messages when provided`() { + // given + val messagesConsumer = CreateMessagesConsumerImpl() + val basePubSubResult = createBasePubSubResult() + val pnMessageResult = PNMessageResult(basePubSubResult, JsonPrimitive(message)) + val pnPresenceEventResult = PNPresenceEventResult(channel = "channel") + val pnFileEventResult = createPnFileEventResult(message) + val pnSignalResult = PNSignalResult(basePubSubResult, JsonPrimitive("604C7")) + val pnMessageActionResult = createPnMessageActionResult(messageActionType) + val pnObjectEventResult = createPnObjectEventResult(objectEventType) + + val listOfDifferentMessages = + createListOfDifferentMessages( + pnMessageResult, + pnPresenceEventResult, + pnFileEventResult, + pnSignalResult, + pnMessageActionResult, + pnObjectEventResult, + ) + val emitMessagesEffect = EmitMessagesEffect(messagesConsumer, listOfDifferentMessages) + + // when + emitMessagesEffect.runEffect() + + // then + assertTrue(messagesConsumer.pnMessageResultList.contains(pnMessageResult)) + assertTrue(messagesConsumer.pnPresenceEventResultList.contains(pnPresenceEventResult)) + assertTrue(messagesConsumer.pnFileEventResultList.contains(pnFileEventResult)) + assertTrue(messagesConsumer.pnSignalResultList.contains(pnSignalResult)) + assertTrue(messagesConsumer.pnMessageActionResultList.contains(pnMessageActionResult)) + assertTrue(messagesConsumer.pnObjectEventResultList.contains(pnObjectEventResult)) + } + + private fun createListOfDifferentMessages( + pnMessageResult: PNMessageResult, + pnPresenceEventResult: PNPresenceEventResult, + pnFileEventResult: PNFileEventResult, + pnSignalResult: PNSignalResult, + pnMessageActionResult: PNMessageActionResult, + pnObjectEventResult: PNObjectEventResult, + ): List { + return listOf( + pnMessageResult, + pnPresenceEventResult, + pnFileEventResult, + pnSignalResult, + pnMessageActionResult, + pnObjectEventResult, + ) + } + + private fun createPnObjectEventResult(objectEventType: String): PNObjectEventResult { + val pnSetMembershipEvent = + PNSetMembershipEvent( + "ThisIsMyChannelBE8DC5FD86", + "client-dc55ef8d-2595-4835-9826-53906e9c25fe", + null, + "AZO/t53al7m8fw", + "2023-05-05T14:26:55.824848794Z", + null, + ) + val pnSetMembershipEventMessage = + PNSetMembershipEventMessage("objects", "2.0", "set", objectEventType, pnSetMembershipEvent) + val pnObjectEventResult = PNObjectEventResult(createBasePubSubResult(), pnSetMembershipEventMessage) + return pnObjectEventResult + } + + private fun createPnMessageActionResult(messageActionType: String): PNMessageActionResult { + val pnMessageAction = + PNMessageAction( + messageActionType, + "\uD83D\uDE07\uD83E\uDD16\uD83E\uDD22\uD83D\uDE1E\uD83D\uDE03", + 16832965735787913, + ) + val pnMessageActionResult = PNMessageActionResult(createBasePubSubResult(), "added", pnMessageAction) + return pnMessageActionResult + } + + private fun createPnFileEventResult(message: String): PNFileEventResult { + val pnDownloadableFile = + PNDownloadableFile( + "99c70a2f-cdaf-49a1-813c-33f7e1aa560f", + "fileNamech_1683295897374_04C360CB60.txt", + "https://ps.pndsn.com/v1/files/sub-c-cb8b98b4-cd27-11ec-b360-1a35c262c233/channels/ch_1683295897374_04C360CB60/files/99c70a2f-cdaf-49a1-813c-33f7e1aa560f/fileNamech_1683295897374_04C360CB60.txt", + ) + val pnFileEventResult = + PNFileEventResult( + "channelFile", + 16832958984018219, + "client-123", + "This is message", + pnDownloadableFile, + JsonPrimitive(message), + ) + return pnFileEventResult + } + + private fun createBasePubSubResult() = + BasePubSubResult( + "channel1", + "my.*", + 16832048617009353L, + null, + "client-c2804687-7d25-4f0b-a442-e3820265b46c", + ) +} + +class CreateMessagesConsumerImpl : MessagesConsumer { + val pnMessageResultList = mutableListOf() + val pnPresenceEventResultList = mutableListOf() + val pnSignalResultList = mutableListOf() + val pnMessageActionResultList = mutableListOf() + val pnObjectEventResultList = mutableListOf() + val pnFileEventResultList = mutableListOf() + + override fun announce(message: PNMessageResult) { + pnMessageResultList.add(message) + } + + override fun announce(presence: PNPresenceEventResult) { + pnPresenceEventResultList.add(presence) + } + + override fun announce(signal: PNSignalResult) { + pnSignalResultList.add(signal) + } + + override fun announce(messageAction: PNMessageActionResult) { + pnMessageActionResultList.add(messageAction) + } + + override fun announce(pnObjectEventResult: PNObjectEventResult) { + pnObjectEventResultList.add(pnObjectEventResult) + } + + override fun announce(pnFileEventResult: PNFileEventResult) { + pnFileEventResultList.add(pnFileEventResult) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitStatusEffectTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitStatusEffectTest.kt new file mode 100644 index 000000000..bba52937d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/EmitStatusEffectTest.kt @@ -0,0 +1,29 @@ +package com.pubnub.internal.subscribe.eventengine.effect + +import com.pubnub.api.models.consumer.PNStatus +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot +import io.mockk.verify +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class EmitStatusEffectTest { + private val statusConsumer: StatusConsumer = mockk() + val status: PNStatus = mockk() + + @Test + fun `should announce status when status provided`() { + // given + val emitStatusEffect = EmitStatusEffect(statusConsumer, status) + val pnStatusCapture = slot() + every { statusConsumer.announce(capture(pnStatusCapture)) } returns Unit + + // when + emitStatusEffect.runEffect() + + // then + verify { statusConsumer.announce(capture(pnStatusCapture)) } + assertEquals(status, pnStatusCapture.captured) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/HandshakeEffectTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/HandshakeEffectTest.kt new file mode 100644 index 000000000..b06620f8c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/HandshakeEffectTest.kt @@ -0,0 +1,124 @@ +package com.pubnub.internal.subscribe.eventengine.effect + +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.api.v2.callbacks.Result +import com.pubnub.internal.eventengine.Sink +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import io.mockk.spyk +import io.mockk.verify +import org.awaitility.Awaitility +import org.awaitility.Durations +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import java.time.Duration +import java.util.concurrent.Executors +import java.util.function.Consumer + +class HandshakeEffectTest { + private val subscribeEventSink = TestEventSink() + private val subscriptionCursor = SubscriptionCursor(1337L, "1337") + private val reason = PubNubException("Unknown error") + + @Test + fun `should deliver HandshakeSuccess event when HandshakeEffect succeeded`() { + // given + val handshakeEffect = HandshakeEffect(successfulRemoteAction(subscriptionCursor), subscribeEventSink) + + // when + handshakeEffect.runEffect() + + // then + Awaitility.await() + .atMost(Durations.ONE_SECOND) + .with() + .pollInterval(Duration.ofMillis(20)) + .untilAsserted { + assertEquals(listOf(SubscribeEvent.HandshakeSuccess(subscriptionCursor)), subscribeEventSink.events) + } + } + + @Test + fun `should deliver HandshakeFailure event when HandshakeEffect failed`() { + // given + val handshakeEffect = HandshakeEffect(failingRemoteAction(reason), subscribeEventSink) + + // when + handshakeEffect.runEffect() + + // then + Awaitility.await() + .atMost(Durations.ONE_SECOND) + .with() + .pollInterval(Duration.ofMillis(20)) + .untilAsserted { + assertEquals(listOf(SubscribeEvent.HandshakeFailure(reason)), subscribeEventSink.events) + } + } + + @Test + fun `should cancel remoteAction when cancel effect`() { + // given + val remoteAction: RemoteAction = spyk() + val handshakeEffect = HandshakeEffect(remoteAction, subscribeEventSink) + + // when + handshakeEffect.cancel() + + // then + verify { remoteAction.silentCancel() } + } +} + +class TestEventSink : Sink { + val events = mutableListOf() + + override fun add(item: T) { + events.add(item) + } +} + +fun successfulRemoteAction(result: T): RemoteAction = + object : RemoteAction { + private val executors = Executors.newSingleThreadExecutor() + + override fun sync(): T { + throw PubNubException("Sync not supported") + } + + override fun retry() { + TODO("Not yet implemented") + } + + override fun silentCancel() { + } + + override fun async(callback: Consumer>) { + executors.submit { + callback.accept(Result.success(result)) + } + } + } + +fun failingRemoteAction(exception: PubNubException = PubNubException("Exception")): RemoteAction = + object : RemoteAction { + private val executors = Executors.newSingleThreadExecutor() + + override fun sync(): T { + throw PubNubException("Sync not supported") + } + + override fun retry() { + TODO("Not yet implemented") + } + + override fun silentCancel() { + } + + override fun async(callback: Consumer>) { + executors.submit { + callback.accept(Result.failure(exception)) + } + } + } diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/ReceiveMessagesEffectTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/ReceiveMessagesEffectTest.kt new file mode 100644 index 000000000..0b373cd6a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/ReceiveMessagesEffectTest.kt @@ -0,0 +1,89 @@ +package com.pubnub.internal.subscribe.eventengine.effect + +import com.google.gson.JsonPrimitive +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.api.models.consumer.pubsub.BasePubSubResult +import com.pubnub.api.models.consumer.pubsub.PNEvent +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import io.mockk.spyk +import io.mockk.verify +import org.awaitility.Awaitility +import org.awaitility.Durations +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import java.time.Duration + +class ReceiveMessagesEffectTest { + private val subscriptionCursor = SubscriptionCursor(1337L, "1337") + private val subscribeEventSink = TestEventSink() + private val messages: List = createPNMessageResultList() + private val receiveMessageResult = ReceiveMessagesResult(messages, subscriptionCursor) + private val reason = PubNubException("Test") + + @Test + fun `should deliver ReceiveSuccess event when ReceiveMessagesEffect succeeded `() { + // given + val receiveMessagesEffect = + ReceiveMessagesEffect(successfulRemoteAction(receiveMessageResult), subscribeEventSink) + + // when + receiveMessagesEffect.runEffect() + + // then + Awaitility.await() + .atMost(Durations.ONE_SECOND) + .with() + .pollInterval(Duration.ofMillis(20)) + .untilAsserted { + assertEquals( + listOf(SubscribeEvent.ReceiveSuccess(messages, subscriptionCursor)), + subscribeEventSink.events, + ) + } + } + + @Test + fun `should deliver ReceiveFailure event when ReceiveMessagesEffect failed `() { + // given + val receiveMessagesEffect = ReceiveMessagesEffect(failingRemoteAction(reason), subscribeEventSink) + + // when + receiveMessagesEffect.runEffect() + + // then + Awaitility.await() + .atMost(Durations.ONE_SECOND) + .with() + .pollInterval(Duration.ofMillis(20)) + .untilAsserted { assertEquals(listOf(SubscribeEvent.ReceiveFailure(reason)), subscribeEventSink.events) } + } + + @Test + fun `should cancel remoteAction when cancel effect`() { + // given + val remoteAction: RemoteAction = spyk() + val receiveMessagesEffect = ReceiveMessagesEffect(remoteAction, subscribeEventSink) + + // when + receiveMessagesEffect.cancel() + + // then + verify { remoteAction.silentCancel() } + } +} + +fun createPNMessageResultList(message: String = "Test"): List { + val basePubSubResult = + BasePubSubResult( + "channel1", + "my.*", + 16832048617009353L, + null, + "client-c2804687-7d25-4f0b-a442-e3820265b46c", + ) + val pnMessageResult = PNMessageResult(basePubSubResult, JsonPrimitive(message)) + return listOf(pnMessageResult) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/SubscribeEffectFactoryTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/SubscribeEffectFactoryTest.kt new file mode 100644 index 000000000..29739f12f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/effect/SubscribeEffectFactoryTest.kt @@ -0,0 +1,217 @@ +package com.pubnub.internal.subscribe.eventengine.effect + +import com.pubnub.api.endpoints.remoteaction.RemoteAction +import com.pubnub.api.enums.PNStatusCategory +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.internal.eventengine.ManagedEffect +import com.pubnub.internal.eventengine.Sink +import com.pubnub.internal.presence.eventengine.data.PresenceData +import com.pubnub.internal.subscribe.eventengine.effect.effectprovider.HandshakeProvider +import com.pubnub.internal.subscribe.eventengine.effect.effectprovider.ReceiveMessagesProvider +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.core.IsInstanceOf.instanceOf +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class SubscribeEffectFactoryTest { + private val handshakeProvider = mockk() + private val receiveMessageProvider = mockk() + private val messagesConsumer = mockk() + private val subscribeEventSink = mockk>() + private val channels = setOf("channel1") + private val channelGroups = setOf("channelGroup1") + private lateinit var subscribeEffectFactory: SubscribeEffectFactory + private val subscriptionCursor = SubscriptionCursor(1337L, "1337") + private val handshakeRemoteAction: RemoteAction = mockk() + private val receiveMessagesRemoteAction: RemoteAction = mockk() + private val statusConsumer = mockk() + private val presenceData = PresenceData() + + @BeforeEach + fun setUp() { + presenceData.channelStates.clear() + subscribeEffectFactory = + SubscribeEffectFactory( + handshakeProvider, + receiveMessageProvider, + subscribeEventSink, + messagesConsumer, + statusConsumer, + presenceData, + true, + ) + } + + @Test + fun `should return emitMessages effect when getting EmitMessages invocation`() { + // when + val effect = subscribeEffectFactory.create(SubscribeEffectInvocation.EmitMessages(messages = listOf())) + + // then + assertThat(effect, instanceOf(EmitMessagesEffect::class.java)) + } + + @Test + fun `should return emitStatus effect when getting EmitStatus invocation`() { + // when + val effect = + subscribeEffectFactory.create( + SubscribeEffectInvocation.EmitStatus( + PNStatus( + PNStatusCategory.PNConnectedCategory, + currentTimetoken = 0L, + affectedChannels = channels.toList(), + affectedChannelGroups = channelGroups.toList(), + ), + ), + ) + + // then + assertThat(effect, instanceOf(EmitStatusEffect::class.java)) + } + + @Test + fun `should return handshake effect when getting Handshake invocation`() { + // given + val effectInvocation = SubscribeEffectInvocation.Handshake(channels, channelGroups) + every { + handshakeProvider.getHandshakeRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + any(), + ) + } returns handshakeRemoteAction + // when + val managedEffect = + subscribeEffectFactory.create(SubscribeEffectInvocation.Handshake(channels, channelGroups)) + + // then + assertThat(managedEffect, instanceOf(HandshakeEffect::class.java)) + assertThat(managedEffect, instanceOf(ManagedEffect::class.java)) + } + + @Test + fun `should include state from PresenceData into handshake effect when getting Handshake invocation`() { + // given + presenceData.channelStates[channels.first()] = mapOf("aaa" to "bbb") + presenceData.channelStates["nonSubscribedChannel"] = mapOf("aaa" to "bbb") + + val effectInvocation = SubscribeEffectInvocation.Handshake(channels, channelGroups) + every { + handshakeProvider.getHandshakeRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + any(), + ) + } returns handshakeRemoteAction + + // when + subscribeEffectFactory.create(SubscribeEffectInvocation.Handshake(channels, channelGroups)) + + // then + verify { + handshakeProvider.getHandshakeRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + mapOf( + "channel1" to + mapOf( + "aaa" to "bbb", + ), + ), + ) + } + } + + @Test + fun `should not include state from PresenceData into handshake effect when sendStateWithSubscribe == false`() { + // given + subscribeEffectFactory = + SubscribeEffectFactory( + handshakeProvider, + receiveMessageProvider, + subscribeEventSink, + messagesConsumer, + statusConsumer, + presenceData, + false, + ) + presenceData.channelStates[channels.first()] = mapOf("aaa" to "bbb") + val effectInvocation = SubscribeEffectInvocation.Handshake(channels, channelGroups) + every { + handshakeProvider.getHandshakeRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + any(), + ) + } returns handshakeRemoteAction + + // when + subscribeEffectFactory.create(SubscribeEffectInvocation.Handshake(channels, channelGroups)) + + // then + verify { + handshakeProvider.getHandshakeRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + null, + ) + } + } + + @Test + fun `should return receiveMessages effect when getting ReceiveMessages invocation`() { + // given + val effectInvocation = + SubscribeEffectInvocation.ReceiveMessages( + channels, + channelGroups, + subscriptionCursor, + ) + every { + receiveMessageProvider.getReceiveMessagesRemoteAction( + effectInvocation.channels, + effectInvocation.channelGroups, + effectInvocation.subscriptionCursor, + ) + } returns receiveMessagesRemoteAction + + // when + val managedEffect = + subscribeEffectFactory.create( + SubscribeEffectInvocation.ReceiveMessages( + channels, + channelGroups, + subscriptionCursor, + ), + ) + + // then + assertThat(managedEffect, instanceOf(ReceiveMessagesEffect::class.java)) + assertThat(managedEffect, instanceOf(ManagedEffect::class.java)) + } + + @Test + fun `should return null when getting CancelHandshake invocation`() { + // when + val managedEffect = subscribeEffectFactory.create(SubscribeEffectInvocation.CancelHandshake) + + // then + assertNull(managedEffect) + } + + @Test + fun `should return null when getting CancelReceiveMessages invocation`() { + // when + val managedEffect = subscribeEffectFactory.create(SubscribeEffectInvocation.CancelReceiveMessages) + + // then + assertNull(managedEffect) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/event/SubscribeEventTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/event/SubscribeEventTest.kt new file mode 100644 index 000000000..68ed1e5a6 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/event/SubscribeEventTest.kt @@ -0,0 +1,48 @@ +package com.pubnub.internal.subscribe.eventengine.event + +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class SubscribeEventTest { + private val timeToken = 12345345452L + private val region = "42" + private val subscriptionCursor = SubscriptionCursor(timeToken, region) + + @Test + fun `channel and channelGroup in SubscriptionChanged event should be immutable set`() { + // given + val channelName = "Channel01" + val channelGroupName = "ChannelGroup01" + val myMutableSetOfChannels = mutableSetOf(channelName) + val myMutableSetOfChannelGroups = mutableSetOf(channelGroupName) + val subscriptionChanged: SubscribeEvent.SubscriptionChanged = + SubscribeEvent.SubscriptionChanged(myMutableSetOfChannels, myMutableSetOfChannelGroups) + + // when + myMutableSetOfChannels.remove(channelName) + myMutableSetOfChannelGroups.remove(channelGroupName) + + // then + assertTrue(subscriptionChanged.channels.contains(channelName)) + assertTrue(subscriptionChanged.channelGroups.contains(channelGroupName)) + } + + @Test + fun `channel and channelGroup in SubscriptionRestored event should be immutable set`() { + // given + val channelName = "Channel01" + val channelGroupName = "ChannelGroup01" + val myMutableSetOfChannels = mutableSetOf(channelName) + val myMutableSetOfChannelGroups = mutableSetOf(channelGroupName) + val subscriptionRestored: SubscribeEvent.SubscriptionRestored = + SubscribeEvent.SubscriptionRestored(myMutableSetOfChannels, myMutableSetOfChannelGroups, subscriptionCursor) + + // when + myMutableSetOfChannels.remove(channelName) + myMutableSetOfChannelGroups.remove(channelGroupName) + + // then + assertTrue(subscriptionRestored.channels.contains(channelName)) + assertTrue(subscriptionRestored.channelGroups.contains(channelGroupName)) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/SubscribeEventConsumerWorkerTransitionFunctionTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/SubscribeEventConsumerWorkerTransitionFunctionTest.kt new file mode 100644 index 000000000..e839f0a20 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/SubscribeEventConsumerWorkerTransitionFunctionTest.kt @@ -0,0 +1,73 @@ +package com.pubnub.internal.subscribe.eventengine.worker + +import com.pubnub.api.enums.PNStatusCategory +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.api.models.consumer.pubsub.PNEvent +import com.pubnub.internal.eventengine.transition +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import com.pubnub.internal.subscribe.eventengine.state.SubscribeState +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class SubscribeEventConsumerWorkerTransitionFunctionTest { + @Test + fun can_transit_from_state_UNSUBSCRIBED_to_HANDSHAKING_on_SubscriptionChange_event_then_from_HANDSHAKING_to_RECEIVING_on_HandshakingSuccess() { + // given + val channels = setOf("Channel1") + val channelGroups = setOf("ChannelGroup1") + val timeToken = 12345345452L + val region = "42" + val subscriptionCursor = SubscriptionCursor(timeToken, region) + + val subscriptionChangeSubscribeEvent = SubscribeEvent.SubscriptionChanged(channels, channelGroups) + val messages = listOf() + + // when + val (handshaking, effectInvocationsForSubscriptionChange) = + transition( + SubscribeState.Unsubscribed, + subscriptionChangeSubscribeEvent, + ) + val (receiving01, effectInvocationsForHandshakingSuccess) = + transition( + handshaking, + SubscribeEvent.HandshakeSuccess(subscriptionCursor), + ) + val (_, effectInvocationsForReceivingSuccess) = + transition( + receiving01, + SubscribeEvent.ReceiveSuccess(messages, subscriptionCursor), + ) + + // then + Assertions.assertEquals( + setOf( + SubscribeEffectInvocation.Handshake(channels, channelGroups), + SubscribeEffectInvocation.CancelHandshake, + SubscribeEffectInvocation.EmitStatus( + PNStatus( + PNStatusCategory.PNConnectedCategory, + currentTimetoken = timeToken, + affectedChannels = channels.toList(), + affectedChannelGroups = channelGroups.toList(), + ), + ), + SubscribeEffectInvocation.ReceiveMessages(channels, channelGroups, subscriptionCursor), + SubscribeEffectInvocation.CancelReceiveMessages, + SubscribeEffectInvocation.EmitMessages(listOf()), + SubscribeEffectInvocation.EmitStatus( + PNStatus( + PNStatusCategory.PNConnectedCategory, + currentTimetoken = timeToken, + affectedChannels = channels.toList(), + affectedChannelGroups = channelGroups.toList(), + ), + ), + SubscribeEffectInvocation.ReceiveMessages(channels, channelGroups, subscriptionCursor), + ), + effectInvocationsForSubscriptionChange + effectInvocationsForHandshakingSuccess + effectInvocationsForReceivingSuccess, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromHandshakeFailedStateTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromHandshakeFailedStateTest.kt new file mode 100644 index 000000000..bacb0c5e0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromHandshakeFailedStateTest.kt @@ -0,0 +1,146 @@ +package com.pubnub.internal.subscribe.eventengine.worker + +import com.pubnub.api.PubNubException +import com.pubnub.internal.eventengine.transition +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import com.pubnub.internal.subscribe.eventengine.state.SubscribeState +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +internal class TransitionFromHandshakeFailedStateTest { + val channels = setOf("Channel1") + val channelGroups = setOf("ChannelGroup1") + val exception = PubNubException("Test") + val timetoken = 12345345452L + val region = "42" + val subscriptionCursor = SubscriptionCursor(timetoken, region) + + @Test + fun `channel and channelGroup should be immutable set`() { + // given + val channelName = "Channel01" + val channelGroupName = "ChannelGroup01" + val myMutableSetOfChannels = mutableSetOf(channelName) + val myMutableSetOfChannelGroups = mutableSetOf(channelGroupName) + val handshakeFailed: SubscribeState.HandshakeFailed = + SubscribeState.HandshakeFailed(myMutableSetOfChannels, myMutableSetOfChannelGroups, exception) + + // when + myMutableSetOfChannels.remove(channelName) + myMutableSetOfChannelGroups.remove(channelGroupName) + + // then + assertTrue(handshakeFailed.channels.contains(channelName)) + assertTrue(handshakeFailed.channelGroups.contains(channelGroupName)) + } + + @Test + fun can_transit_from_HANDSHAKE_FAILED_to_HANDSHAKING_when_there_is_SUBSCRIPTION_CHANGED_event() { + // given + val newChannels = channels + setOf("NewChannel") + val newChannelGroup = channelGroups + setOf("NewChannelGroup") + + // when + val (state, invocations) = + transition( + SubscribeState.HandshakeFailed(channels, channelGroups, exception, subscriptionCursor), + SubscribeEvent.SubscriptionChanged(newChannels, newChannelGroup), + ) + + // then + assertTrue(state is SubscribeState.Handshaking) + val handshaking = state as SubscribeState.Handshaking + assertEquals(newChannels, handshaking.channels) + assertEquals(newChannelGroup, handshaking.channelGroups) + assertEquals(subscriptionCursor, handshaking.subscriptionCursor) + assertEquals( + setOf(SubscribeEffectInvocation.Handshake(newChannels, newChannelGroup)), + invocations, + ) + } + + @Test + fun can_transit_from_HANDSHAKE_FAILED_to_RECEIVING_when_there_is_SUBSCRIPTION_RESTORED_event() { + // when + val (state, invocations) = + transition( + SubscribeState.HandshakeFailed(channels, channelGroups, exception), + SubscribeEvent.SubscriptionRestored(channels, channelGroups, subscriptionCursor), + ) + + // then + assertTrue(state is SubscribeState.Handshaking) + state as SubscribeState.Handshaking + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(subscriptionCursor, state.subscriptionCursor) + assertEquals( + setOf(SubscribeEffectInvocation.Handshake(channels, channelGroups)), + invocations, + ) + } + + @Test + fun can_transit_from_HANDSHAKE_FAILED_to_HANDSHAKING_when_there_is_RECONNECT_event() { + // when + val (state, invocations) = + transition( + SubscribeState.HandshakeFailed(channels, channelGroups, exception), + SubscribeEvent.Reconnect(), + ) + + // then + assertTrue(state is SubscribeState.Handshaking) + state as SubscribeState.Handshaking + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals( + setOf(SubscribeEffectInvocation.Handshake(channels, channelGroups)), + invocations, + ) + } + + @Test + fun can_transit_from_HANDSHAKE_FAILED_to_HANDSHAKING_when_there_is_RECONNECT_event_with_timeToken() { + // given + val timeTokenFromReconnect = 99945345452L + val subscriptionCursorForReconnect = SubscriptionCursor(timeTokenFromReconnect, null) + + // when + val (state, invocations) = + transition( + SubscribeState.HandshakeFailed(channels, channelGroups, exception), + SubscribeEvent.Reconnect(subscriptionCursorForReconnect), + ) + + // then + assertTrue(state is SubscribeState.Handshaking) + val handshaking = state as SubscribeState.Handshaking + assertEquals(channels, handshaking.channels) + assertEquals(channelGroups, handshaking.channelGroups) + assertEquals(subscriptionCursorForReconnect, handshaking.subscriptionCursor) + assertEquals( + setOf(SubscribeEffectInvocation.Handshake(channels, channelGroups)), + invocations, + ) + } + + @Test + fun can_transit_from_HANDSHAKING_FAILED_to_UNSUBSRIBED_when_there_is_UNSUBSCRIBE_ALL_event() { + // when + val (state, invocations) = + transition( + SubscribeState.HandshakeFailed(channels, channelGroups, exception), + SubscribeEvent.UnsubscribeAll, + ) + + // then + assertEquals(SubscribeState.Unsubscribed, state) + assertEquals(0, invocations.size) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromHandshakeStoppedStateTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromHandshakeStoppedStateTest.kt new file mode 100644 index 000000000..d48d04cdc --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromHandshakeStoppedStateTest.kt @@ -0,0 +1,132 @@ +package com.pubnub.internal.subscribe.eventengine.worker + +import com.pubnub.api.PubNubException +import com.pubnub.internal.eventengine.transition +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import com.pubnub.internal.subscribe.eventengine.state.SubscribeState +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class TransitionFromHandshakeStoppedStateTest { + private val channels = setOf("Channel1") + private val channelGroups = setOf("ChannelGroup1") + private val reason = PubNubException("Test") + private val timeToken = 12345345452L + private val region = "42" + private val subscriptionCursor = SubscriptionCursor(timeToken, region) + + @Test + fun `channel and channelGroup should be immutable set`() { + // given + val channelName = "Channel01" + val channelGroupName = "ChannelGroup01" + val myMutableSetOfChannels = mutableSetOf(channelName) + val myMutableSetOfChannelGroups = mutableSetOf(channelGroupName) + val handshakeStopped: SubscribeState.HandshakeStopped = + SubscribeState.HandshakeStopped(myMutableSetOfChannels, myMutableSetOfChannelGroups, reason) + + // when + myMutableSetOfChannels.remove(channelName) + myMutableSetOfChannelGroups.remove(channelGroupName) + + // then + assertTrue(handshakeStopped.channels.contains(channelName)) + assertTrue(handshakeStopped.channelGroups.contains(channelGroupName)) + } + + @Test + fun can_transit_from_HANDSHAKE_STOPPED_to_HANDSHAKING_when_there_is_RECONNECT_event_with_timetoken() { + // given + val timeTokenFromReconnect = 99945345452L + val subscriptionCursorForReconnect = SubscriptionCursor(timeTokenFromReconnect, null) + + // when + val (state, invocations) = + transition( + SubscribeState.HandshakeStopped(channels, channelGroups, reason), + SubscribeEvent.Reconnect(subscriptionCursorForReconnect), + ) + + // then + assertTrue(state is SubscribeState.Handshaking) + state as SubscribeState.Handshaking + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(subscriptionCursorForReconnect, state.subscriptionCursor) + assertEquals(setOf(SubscribeEffectInvocation.Handshake(channels, channelGroups)), invocations) + } + + @Test + fun can_transit_from_HANDSHAKE_STOPPED_to_HANDSHAKING_when_there_is_RECONNECT_event() { + // when + val (state, invocations) = + transition( + SubscribeState.HandshakeStopped(channels, channelGroups, reason), + SubscribeEvent.Reconnect(), + ) + + // then + assertTrue(state is SubscribeState.Handshaking) + state as SubscribeState.Handshaking + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(setOf(SubscribeEffectInvocation.Handshake(channels, channelGroups)), invocations) + } + + @Test + fun can_transit_from_HANDSHAKE_STOPPED_to_UNSUBSRIBED_when_there_is_UNSUBSRIBED_ALL_event() { + // when + val (state, invocations) = + transition( + SubscribeState.HandshakeStopped(channels, channelGroups, reason), + SubscribeEvent.UnsubscribeAll, + ) + + // then + assertEquals(SubscribeState.Unsubscribed, state) + assertEquals(0, invocations.size) + } + + @Test + fun can_transit_from_HANDSHAKE_STOPPED_to_HANDSHAKE_STOPPED_when_there_is_SUBSCRIPTION_CHANGED_event() { + // when + val (state, invocations) = + transition( + SubscribeState.HandshakeStopped(channels, channelGroups, reason), + SubscribeEvent.SubscriptionChanged(channels, channelGroups), + ) + + // then + assertTrue(state is SubscribeState.HandshakeStopped) + state as SubscribeState.HandshakeStopped + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(null, state.reason) + assertEquals(0, invocations.size) + } + + @Test + fun can_transit_from_HANDSHAKE_STOPPED_to_HANDSHAKE_STOPPED_when_there_is_SUBSCRIPTION_RESTORED_event() { + // when + val (state, invocations) = + transition( + SubscribeState.HandshakeStopped(channels, channelGroups, reason), + SubscribeEvent.SubscriptionRestored(channels, channelGroups, subscriptionCursor), + ) + + // then + assertTrue(state is SubscribeState.HandshakeStopped) + state as SubscribeState.HandshakeStopped + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(null, state.reason) + assertEquals(0, invocations.size) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromHandshakingStateTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromHandshakingStateTest.kt new file mode 100644 index 000000000..f03ad9d27 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromHandshakingStateTest.kt @@ -0,0 +1,234 @@ +package com.pubnub.internal.subscribe.eventengine.worker + +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNStatusCategory +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.internal.eventengine.transition +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation.CancelHandshake +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation.EmitStatus +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation.Handshake +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation.ReceiveMessages +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import com.pubnub.internal.subscribe.eventengine.state.SubscribeState +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.core.IsInstanceOf.instanceOf +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class TransitionFromHandshakingStateTest { + private val channels = setOf("Channel1") + private val channelGroups = setOf("ChannelGroup1") + private val timeToken = 12345345452L + private val region = "42" + private val subscriptionCursor = SubscriptionCursor(timeToken, region) + private val reason = PubNubException("test") + + @Test + fun `channel and channelGroup should be immutable set`() { + // given + val channelName = "Channel01" + val channelGroupName = "ChannelGroup01" + val myMutableSetOfChannels = mutableSetOf(channelName) + val myMutableSetOfChannelGroups = mutableSetOf(channelGroupName) + val handshaking: SubscribeState.Handshaking = + SubscribeState.Handshaking(myMutableSetOfChannels, myMutableSetOfChannelGroups, subscriptionCursor = null) + + // when + myMutableSetOfChannels.remove(channelName) + myMutableSetOfChannelGroups.remove(channelGroupName) + + // then + assertTrue(handshaking.channels.contains(channelName)) + assertTrue(handshaking.channelGroups.contains(channelGroupName)) + } + + @Test + fun can_transit_from_HANDSHAKING_that_has_cursor_that_is_null_to_RECEIVING_when_there_is_HANDSHAKE_SUCCESS_event() { + // when + val (state, invocations) = + transition( + SubscribeState.Handshaking(channels, channelGroups, subscriptionCursor = null), + SubscribeEvent.HandshakeSuccess(subscriptionCursor), + ) + + // then + assertTrue(state is SubscribeState.Receiving) + val receiving = state as SubscribeState.Receiving + assertEquals(channels, receiving.channels) + assertEquals(channelGroups, receiving.channelGroups) + assertEquals(subscriptionCursor, receiving.subscriptionCursor) + assertEquals( + setOf( + CancelHandshake, + EmitStatus( + PNStatus( + PNStatusCategory.PNConnectedCategory, + currentTimetoken = timeToken, + affectedChannels = channels.toList(), + affectedChannelGroups = channelGroups.toList(), + ), + ), + ReceiveMessages(channels, channelGroups, subscriptionCursor), + ), + invocations, + ) + } + + @Test + fun can_transit_from_HANDSHAKING_that_has_cursor_that_is_not_null_to_RECEIVING_when_there_is_HANDSHAKE_SUCCESS_event() { + // given + val regionReturnedByHandshake = "12" + val timeTokenForHandshake = 99945345452L + val subscriptionCursorForHandshaking = SubscriptionCursor(timeTokenForHandshake, null) + val subscriptionCursorReturnedByHandshake = SubscriptionCursor(timeToken, regionReturnedByHandshake) + + // when + val (state, invocations) = + transition( + SubscribeState.Handshaking( + channels, + channelGroups, + subscriptionCursor = subscriptionCursorForHandshaking, + ), + SubscribeEvent.HandshakeSuccess(subscriptionCursorReturnedByHandshake), + ) + + // then + assertTrue(state is SubscribeState.Receiving) + val receiving = state as SubscribeState.Receiving + assertEquals(channels, receiving.channels) + assertEquals(channelGroups, receiving.channelGroups) + assertEquals(SubscriptionCursor(timeTokenForHandshake, regionReturnedByHandshake), receiving.subscriptionCursor) + assertEquals( + setOf( + CancelHandshake, + EmitStatus( + PNStatus( + PNStatusCategory.PNConnectedCategory, + currentTimetoken = timeTokenForHandshake, + affectedChannels = channels.toList(), + affectedChannelGroups = channelGroups.toList(), + ), + ), + ReceiveMessages( + channels, + channelGroups, + SubscriptionCursor(timeTokenForHandshake, regionReturnedByHandshake), + ), + ), + invocations, + ) + } + + @Test + fun can_transit_from_HANDSHAKING_to_HANDSHAKING_when_there_is_SUBSCRIPTION_RESTORED_event() { + // when + val (state, invocations) = + transition( + SubscribeState.Handshaking(channels, channelGroups), + SubscribeEvent.SubscriptionRestored(channels, channelGroups, subscriptionCursor), + ) + + // then + assertTrue(state is SubscribeState.Handshaking) + val handshaking = state as SubscribeState.Handshaking + assertEquals(channels, handshaking.channels) + assertEquals(channelGroups, handshaking.channelGroups) + assertEquals(subscriptionCursor, handshaking.subscriptionCursor) + assertEquals( + setOf( + CancelHandshake, + Handshake(channels, channelGroups), + ), + invocations, + ) + } + + @Test + fun can_transit_from_HANDSHAKING_to_HANDSHAKING_when_there_is_SUBSCRIPTION_CHANGED_event() { + // given + val newChannels = setOf("newChannel1") + val newChannelGroups = setOf("newChannelGroup1") + + // when + val (state, invocations) = + transition( + SubscribeState.Handshaking(channels, channelGroups, subscriptionCursor), + SubscribeEvent.SubscriptionChanged(newChannels, newChannelGroups), + ) + + // then + assertTrue(state is SubscribeState.Handshaking) + val handshaking = state as SubscribeState.Handshaking + assertEquals(newChannels, handshaking.channels) + assertEquals(newChannelGroups, handshaking.channelGroups) + assertEquals(subscriptionCursor, handshaking.subscriptionCursor) + assertEquals( + setOf( + CancelHandshake, + Handshake(newChannels, newChannelGroups), + ), + invocations, + ) + } + + @Test + fun can_transit_from_HANDSHAKING_to_HANDSHAKING_FAILED_when_there_is_HANDSHAKING_FAILURE_event() { + // when + val (state, invocations) = + transition( + SubscribeState.Handshaking(channels, channelGroups, subscriptionCursor), + SubscribeEvent.HandshakeFailure(reason), + ) + + // then + assertTrue(state is SubscribeState.HandshakeFailed) + val handshakeFailed = state as SubscribeState.HandshakeFailed + assertEquals(channels, handshakeFailed.channels) + assertEquals(channelGroups, handshakeFailed.channelGroups) + assertEquals(reason, handshakeFailed.reason) + assertEquals(subscriptionCursor, handshakeFailed.subscriptionCursor) + assertEquals( + setOf( + CancelHandshake, + EmitStatus( + PNStatus( + category = PNStatusCategory.PNConnectionError, + exception = reason, + ), + ), + ), + invocations, + ) + } + + @Test + fun can_transit_from_HANDSHAKING_to_HANDSHAKING_STOPPED_when_there_is_DISCONNECT_event() { + // when + val (state, invocations) = + transition( + SubscribeState.Handshaking(channels, channelGroups), + SubscribeEvent.Disconnect, + ) + + // then + assertThat(state, instanceOf(SubscribeState.HandshakeStopped::class.java)) + assertEquals(setOf(CancelHandshake), invocations) + } + + @Test + fun can_transit_from_HANDSHAKING_to_UNSUBSRIBED_when_there_is_UNSUBSCRIBE_ALL_event() { + // when + val (state, invocations) = + transition( + SubscribeState.Handshaking(channels, channelGroups), + SubscribeEvent.UnsubscribeAll, + ) + + // then + assertEquals(SubscribeState.Unsubscribed, state) + assertEquals(setOf(CancelHandshake), invocations) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromReceiveFailedStateTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromReceiveFailedStateTest.kt new file mode 100644 index 000000000..c92718439 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromReceiveFailedStateTest.kt @@ -0,0 +1,161 @@ +package com.pubnub.internal.subscribe.eventengine.worker + +import com.pubnub.api.PubNubException +import com.pubnub.internal.eventengine.transition +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import com.pubnub.internal.subscribe.eventengine.state.SubscribeState +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +internal class TransitionFromReceiveFailedStateTest { + val channels = setOf("Channel1") + val channelGroups = setOf("ChannelGroup1") + val exception = PubNubException("Test") + val timetoken = 12345345452L + val region = "42" + val subscriptionCursor = SubscriptionCursor(timetoken, region) + val reason = PubNubException("Test") + + @Test + fun `channel and channelGroup should be immutable set`() { + // given + val channelName = "Channel01" + val channelGroupName = "ChannelGroup01" + val myMutableSetOfChannels = mutableSetOf(channelName) + val myMutableSetOfChannelGroups = mutableSetOf(channelGroupName) + val receiveFailed: SubscribeState.ReceiveFailed = + SubscribeState.ReceiveFailed( + myMutableSetOfChannels, + myMutableSetOfChannelGroups, + subscriptionCursor, + reason, + ) + + // when + myMutableSetOfChannels.remove(channelName) + myMutableSetOfChannelGroups.remove(channelGroupName) + + // then + assertTrue(receiveFailed.channels.contains(channelName)) + assertTrue(receiveFailed.channelGroups.contains(channelGroupName)) + } + + @Test + fun can_transit_from_RECEIVE_FAILED_to_HANDSHAKING_when_there_is_SUBSCRIPTION_CHANGED_event() { + // given + val newChannels = channels + setOf("NewChannel") + val newChannelGroup = channelGroups + setOf("NewChannelGroup") + + // when + val (state, effectInvocations) = + transition( + SubscribeState.ReceiveFailed(channels, channelGroups, subscriptionCursor, reason), + SubscribeEvent.SubscriptionChanged(newChannels, newChannelGroup), + ) + + // then + assertTrue(state is SubscribeState.Handshaking) + state as SubscribeState.Handshaking // Safe cast + + assertEquals(newChannels, state.channels) + assertEquals(newChannelGroup, state.channelGroups) + assertEquals(subscriptionCursor, state.subscriptionCursor) + assertEquals( + setOf(SubscribeEffectInvocation.Handshake(newChannels, newChannelGroup)), + effectInvocations, + ) + } + + @Test + fun can_transit_from_RECEIVE_FAILED_to_HANDSHAKING_when_there_is_SUBSCRIPTION_RESTORED_event() { + // given + val eventChannels = setOf("ChannelZ") + val eventChannelGroups = setOf("ChannelGroupZ") + val timetoken = 99945345452L + val region = "1" + val eventSubscriptionCursor = SubscriptionCursor(timetoken, region) + + // when + val (state, effectInvocations) = + transition( + SubscribeState.ReceiveFailed(channels, channelGroups, subscriptionCursor, reason), + SubscribeEvent.SubscriptionRestored(eventChannels, eventChannelGroups, eventSubscriptionCursor), + ) + // then + assertTrue(state is SubscribeState.Handshaking) + state as SubscribeState.Handshaking // Safe cast + + assertEquals(eventChannels, state.channels) + assertEquals(eventChannelGroups, state.channelGroups) + assertEquals(eventSubscriptionCursor, state.subscriptionCursor) + assertEquals( + setOf(SubscribeEffectInvocation.Handshake(eventChannels, eventChannelGroups)), + effectInvocations, + ) + } + + @Test + fun can_transit_from_RECEIVE_FAILED_to_HANDSHAKING_when_there_is_RECONNECT_event() { + // given + val timeTokenFromReconnect = 99945345452L + val subscriptionCursorForReconnect = SubscriptionCursor(timeTokenFromReconnect, null) + + // when + val (state, effectInvocations) = + transition( + SubscribeState.ReceiveFailed(channels, channelGroups, subscriptionCursor, reason), + SubscribeEvent.Reconnect(subscriptionCursorForReconnect), + ) + + // then + assertTrue(state is SubscribeState.Handshaking) + state as SubscribeState.Handshaking + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(subscriptionCursorForReconnect, state.subscriptionCursor) + assertEquals( + setOf(SubscribeEffectInvocation.Handshake(channels, channelGroups)), + effectInvocations, + ) + } + + @Test + fun can_transit_from_RECEIVE_FAILED_to_HANDSHAKING_when_there_is_RECONNECT_event_with_reconnectCursor() { + // when + val (state, effectInvocations) = + transition( + SubscribeState.ReceiveFailed(channels, channelGroups, subscriptionCursor, reason), + SubscribeEvent.Reconnect(), + ) + + // then + assertTrue(state is SubscribeState.Handshaking) + state as SubscribeState.Handshaking + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(subscriptionCursor, state.subscriptionCursor) + assertEquals( + setOf(SubscribeEffectInvocation.Handshake(channels, channelGroups)), + effectInvocations, + ) + } + + @Test + fun can_transit_from_RECEIVE_FAILED_to_UNSUBSRIBED_when_there_is_UNSUBSRIBED_ALL_event() { + // when + val (state, invocations) = + transition( + SubscribeState.ReceiveFailed(channels, channelGroups, subscriptionCursor, reason), + SubscribeEvent.UnsubscribeAll, + ) + + // then + assertEquals(SubscribeState.Unsubscribed, state) + assertEquals(0, invocations.size) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromReceiveStoppedStateTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromReceiveStoppedStateTest.kt new file mode 100644 index 000000000..dd0ec5b2f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromReceiveStoppedStateTest.kt @@ -0,0 +1,141 @@ +package com.pubnub.internal.subscribe.eventengine.worker + +import com.pubnub.internal.eventengine.transition +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import com.pubnub.internal.subscribe.eventengine.state.SubscribeState +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class TransitionFromReceiveStoppedStateTest { + private val channels = setOf("Channel1") + private val channelGroups = setOf("ChannelGroup1") + private val timeToken = 12345345452L + private val region = "42" + private val subscriptionCursor = SubscriptionCursor(timeToken, region) + + @Test + fun `channel and channelGroup should be immutable set`() { + // given + val channelName = "Channel01" + val channelGroupName = "ChannelGroup01" + val myMutableSetOfChannels = mutableSetOf(channelName) + val myMutableSetOfChannelGroups = mutableSetOf(channelGroupName) + val receiveStopped: SubscribeState.ReceiveStopped = + SubscribeState.ReceiveStopped(myMutableSetOfChannels, myMutableSetOfChannelGroups, subscriptionCursor) + + // when + myMutableSetOfChannels.remove(channelName) + myMutableSetOfChannelGroups.remove(channelGroupName) + + // then + assertTrue(receiveStopped.channels.contains(channelName)) + assertTrue(receiveStopped.channelGroups.contains(channelGroupName)) + } + + @Test + fun can_transit_from_RECEIVE_STOPPED_to_HANDSHAKING_when_there_is_RECONNECT_event() { + // when + val (state, invocations) = + transition( + SubscribeState.ReceiveStopped(channels, channelGroups, subscriptionCursor), + SubscribeEvent.Reconnect(), + ) + + // then + assertTrue(state is SubscribeState.Handshaking) + state as SubscribeState.Handshaking + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(subscriptionCursor, state.subscriptionCursor) + assertEquals( + setOf( + SubscribeEffectInvocation.Handshake(channels, channelGroups), + ), + invocations, + ) + } + + @Test + fun can_transit_from_RECEIVE_STOPPED_to_HANDSHAKING_when_there_is_RECONNECT_event_with_reconnectCursor() { + // given + val timeTokenFromReconnect = 99945345452L + val subscriptionCursorForReconnect = SubscriptionCursor(timeTokenFromReconnect, null) + + // when + val (state, invocations) = + transition( + SubscribeState.ReceiveStopped(channels, channelGroups, subscriptionCursor), + SubscribeEvent.Reconnect(subscriptionCursorForReconnect), + ) + + // then + assertTrue(state is SubscribeState.Handshaking) + state as SubscribeState.Handshaking + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(subscriptionCursorForReconnect, state.subscriptionCursor) + assertEquals( + setOf( + SubscribeEffectInvocation.Handshake(channels, channelGroups), + ), + invocations, + ) + } + + @Test + fun can_transit_from_RECEIVE_STOPPED_to_UNSUBSRIBED_when_there_is_UNSUBSRIBED_ALL_event() { + // when + val (state, invocations) = + transition( + SubscribeState.ReceiveStopped(channels, channelGroups, subscriptionCursor), + SubscribeEvent.UnsubscribeAll, + ) + + // then + assertEquals(SubscribeState.Unsubscribed, state) + assertEquals(0, invocations.size) + } + + @Test + fun can_transit_from_RECEIVE_STOPPED_to_RECEIVE_STOPPED_when_there_is_SUBSCRIPTION_CHANGED_event() { + // when + val (state, invocations) = + transition( + SubscribeState.ReceiveStopped(channels, channelGroups, subscriptionCursor), + SubscribeEvent.SubscriptionChanged(channels, channelGroups), + ) + + // then + assertTrue(state is SubscribeState.ReceiveStopped) + state as SubscribeState.ReceiveStopped + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(subscriptionCursor, state.subscriptionCursor) + assertEquals(0, invocations.size) + } + + @Test + fun can_transit_from_RECEIVE_STOPPED_to_RECEIVE_STOPPED_when_there_is_SUBSCRIPTION_RESTORED_event() { + // when + val (state, invocations) = + transition( + SubscribeState.ReceiveStopped(channels, channelGroups, subscriptionCursor), + SubscribeEvent.SubscriptionRestored(channels, channelGroups, subscriptionCursor), + ) + + // then + assertTrue(state is SubscribeState.ReceiveStopped) + state as SubscribeState.ReceiveStopped + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(subscriptionCursor, state.subscriptionCursor) + assertEquals(0, invocations.size) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromReceivingStateTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromReceivingStateTest.kt new file mode 100644 index 000000000..4864f91b3 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromReceivingStateTest.kt @@ -0,0 +1,240 @@ +package com.pubnub.internal.subscribe.eventengine.worker + +import com.google.gson.JsonObject +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.enums.PNStatusCategory +import com.pubnub.api.models.consumer.PNStatus +import com.pubnub.api.models.consumer.pubsub.BasePubSubResult +import com.pubnub.api.models.consumer.pubsub.PNEvent +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.internal.eventengine.transition +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import com.pubnub.internal.subscribe.eventengine.state.SubscribeState +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class TransitionFromReceivingStateTest { + private val channels = setOf("Channel1") + private val channelGroups = setOf("ChannelGroup1") + private val timeToken = 12345345452L + private val region = "42" + private val subscriptionCursor = SubscriptionCursor(timeToken, region) + private val reason = PubNubException(PubNubError.PARSING_ERROR) + + @Test + fun can_transit_from_RECEIVING_to_RECEIVE_FAILED_when_there_is_RECEIVE_FAILURE_event() { + // when + val (state, invocations) = + transition( + SubscribeState.Receiving(channels, channelGroups, subscriptionCursor), + SubscribeEvent.ReceiveFailure(reason), + ) + + // then + Assertions.assertTrue(state is SubscribeState.ReceiveFailed) + state as SubscribeState.ReceiveFailed + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(subscriptionCursor, state.subscriptionCursor) + assertEquals(reason, state.reason) + assertEquals( + setOf( + SubscribeEffectInvocation.CancelReceiveMessages, + SubscribeEffectInvocation.EmitStatus( + PNStatus( + category = PNStatusCategory.PNUnexpectedDisconnectCategory, + exception = reason, + ), + ), + ), + invocations, + ) + } + + @Test + fun can_transit_from_RECEIVING_to_RECEIVE_STOPPED_when_there_is_DISCONNECT_event() { + // when + val (state, invocations) = + transition( + SubscribeState.Receiving(channels, channelGroups, subscriptionCursor), + SubscribeEvent.Disconnect, + ) + + // then + Assertions.assertTrue(state is SubscribeState.ReceiveStopped) + state as SubscribeState.ReceiveStopped + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(subscriptionCursor, state.subscriptionCursor) + assertEquals( + setOf( + SubscribeEffectInvocation.CancelReceiveMessages, + SubscribeEffectInvocation.EmitStatus( + PNStatus(PNStatusCategory.PNDisconnectedCategory), + ), + ), + invocations, + ) + } + + @Test + fun can_transit_from_RECEIVING_to_RECEIVING_when_there_is_SUBSCRIPTION_CHANGED_event() { + // given + // when + val (state, invocations) = + transition( + SubscribeState.Receiving(channels, channelGroups, subscriptionCursor), + SubscribeEvent.SubscriptionChanged(channels, channelGroups), + ) + + // then + Assertions.assertTrue(state is SubscribeState.Receiving) + state as SubscribeState.Receiving + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(subscriptionCursor, state.subscriptionCursor) + assertEquals( + setOf( + SubscribeEffectInvocation.CancelReceiveMessages, + SubscribeEffectInvocation.EmitStatus( + createSubscriptionChangedStatus( + state.subscriptionCursor, + channels, + channelGroups, + ), + ), + SubscribeEffectInvocation.ReceiveMessages(channels, channelGroups, subscriptionCursor), + ), + invocations, + ) + } + + @Test + fun can_transit_from_RECEIVING_to_RECEIVING_when_there_is_SUBSCRIPTION_RESTORED_event() { + // given + val regionStoredInStoredInReceive = "12" + val subscriptionCursorStoredInReceiving = SubscriptionCursor(timeToken, regionStoredInStoredInReceive) + val timeTokenFromSubscriptionRestored = 99945345452L + val subscriptionCursorStoredInSubscriptionRestored = SubscriptionCursor(timeTokenFromSubscriptionRestored, null) + + // when + val (state, invocations) = + transition( + SubscribeState.Receiving(channels, channelGroups, subscriptionCursorStoredInReceiving), + SubscribeEvent.SubscriptionRestored(channels, channelGroups, subscriptionCursorStoredInSubscriptionRestored), + ) + + // then + val expectedSubscriptionCursor = + SubscriptionCursor(timeTokenFromSubscriptionRestored, regionStoredInStoredInReceive) + Assertions.assertTrue(state is SubscribeState.Receiving) + state as SubscribeState.Receiving + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(expectedSubscriptionCursor, state.subscriptionCursor) + assertEquals( + setOf( + SubscribeEffectInvocation.CancelReceiveMessages, + SubscribeEffectInvocation.EmitStatus( + createSubscriptionChangedStatus( + state.subscriptionCursor, + channels, + channelGroups, + ), + ), + SubscribeEffectInvocation.ReceiveMessages(channels, channelGroups, expectedSubscriptionCursor), + ), + invocations, + ) + } + + @Test + fun can_transit_from_RECEIVING_to_RECEIVING_when_there_is_RECEIVE_SUCCESS_event() { + // given + val pnMessageResult: PNEvent = createPnMessageResult(channels.first()) + val messages: List = listOf(pnMessageResult) + + // when + val (state, invocations) = + transition( + SubscribeState.Receiving(channels, channelGroups, subscriptionCursor), + SubscribeEvent.ReceiveSuccess(messages, subscriptionCursor), + ) + + // then + Assertions.assertTrue(state is SubscribeState.Receiving) + state as SubscribeState.Receiving + + assertEquals(channels, state.channels) + assertEquals(channelGroups, state.channelGroups) + assertEquals(subscriptionCursor, state.subscriptionCursor) + assertEquals( + setOf( + SubscribeEffectInvocation.CancelReceiveMessages, + SubscribeEffectInvocation.EmitMessages(messages), + SubscribeEffectInvocation.ReceiveMessages(channels, channelGroups, subscriptionCursor), + ), + invocations, + ) + } + + @Test + fun can_transit_from_RECEIVING_to_UNSUBSRIBED_when_there_is_UNSUBSCRIBE_ALL_event() { + // when + val (state, invocations) = + transition( + SubscribeState.Receiving(channels, channelGroups, subscriptionCursor), + SubscribeEvent.UnsubscribeAll, + ) + + // then + assertEquals(SubscribeState.Unsubscribed, state) + assertEquals( + setOf( + SubscribeEffectInvocation.CancelReceiveMessages, + SubscribeEffectInvocation.EmitStatus( + PNStatus(PNStatusCategory.PNDisconnectedCategory), + ), + ), + invocations, + ) + } + + private fun createPnMessageResult(channel1: String): PNMessageResult { + val pubSubResult = + BasePubSubResult( + channel = channel1, + subscription = null, + timetoken = 16814672398636798, + userMetadata = null, + publisher = "client-d4d5bdeb-02b7-4505-bfc0-82bad22057d6", + ) + val message = + JsonObject().apply { + addProperty("publisher", "client-6c42e3e2-dd3b-487b-a5bf-c2b6be59a15f") + addProperty("text", "D5631DA5FF") + addProperty("uncd", "-!?+=") + } + + return PNMessageResult(pubSubResult, message) + } +} + +internal fun createSubscriptionChangedStatus( + cursor: SubscriptionCursor, + channels: Collection, + channelGroups: Collection, +) = PNStatus( + PNStatusCategory.PNSubscriptionChanged, + currentTimetoken = cursor.timetoken, + affectedChannels = channels, + affectedChannelGroups = channelGroups, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromUnsubscribedStateTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromUnsubscribedStateTest.kt new file mode 100644 index 000000000..25bb30a30 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/subscribe/eventengine/worker/TransitionFromUnsubscribedStateTest.kt @@ -0,0 +1,63 @@ +package com.pubnub.internal.subscribe.eventengine.worker + +import com.pubnub.internal.eventengine.transition +import com.pubnub.internal.subscribe.eventengine.effect.SubscribeEffectInvocation +import com.pubnub.internal.subscribe.eventengine.event.SubscribeEvent +import com.pubnub.internal.subscribe.eventengine.event.SubscriptionCursor +import com.pubnub.internal.subscribe.eventengine.state.SubscribeState +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +internal class TransitionFromUnsubscribedStateTest { + val channels = setOf("Channel1") + val channelGroups = setOf("ChannelGroup1") + val timeToken = 12345345452L + val region = "42" + val subscriptionCursor = SubscriptionCursor(timeToken, region) + + @Test + fun can_transit_from_UNSUBSRIBED_to_HANDSHAKING_when_there_is_subscriptionChangeEvent() { + // when + val (state, invocations) = + transition( + SubscribeState.Unsubscribed, + SubscribeEvent.SubscriptionChanged(channels, channelGroups), + ) + + // then + Assertions.assertTrue(state is SubscribeState.Handshaking) + val handshaking = state as SubscribeState.Handshaking + assertEquals(channels, handshaking.channels) + assertEquals(channelGroups, handshaking.channelGroups) + assertEquals( + setOf( + SubscribeEffectInvocation.Handshake(channels, channelGroups), + ), + invocations, + ) + } + + @Test + fun can_transit_from_UNSUBSRIBED_to_RECEIVING_when_there_is_subscriptionRestoredEvent() { + // when + val (state, invocations) = + transition( + SubscribeState.Unsubscribed, + SubscribeEvent.SubscriptionRestored(channels, channelGroups, subscriptionCursor), + ) + + // then + Assertions.assertTrue(state is SubscribeState.Handshaking) + val handshaking = state as SubscribeState.Handshaking + assertEquals(channels, handshaking.channels) + assertEquals(channelGroups, handshaking.channelGroups) + assertEquals(subscriptionCursor, handshaking.subscriptionCursor) + assertEquals( + setOf( + SubscribeEffectInvocation.Handshake(channels, channelGroups), + ), + invocations, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/CoreEndpointTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/CoreEndpointTestSuite.kt new file mode 100644 index 000000000..58c8ddb9a --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/CoreEndpointTestSuite.kt @@ -0,0 +1,350 @@ +package com.pubnub.internal.suite + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.findAll +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.noContent +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.github.tomakehurst.wiremock.stubbing.StubMapping +import com.pubnub.api.Endpoint +import com.pubnub.api.PubNubError +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.legacy.BaseTest +import com.pubnub.api.v2.callbacks.getOrThrow +import com.pubnub.test.CommonUtils.assertPnException +import com.pubnub.test.CommonUtils.emptyJson +import com.pubnub.test.CommonUtils.failTest +import com.pubnub.test.await +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Test + +typealias AsyncCheck = (result: com.pubnub.api.v2.callbacks.Result) -> Unit + +abstract class CoreEndpointTestSuite, R> : BaseTest() { + private lateinit var expectedStub: StubMapping + + abstract fun pnOperation(): PNOperationType + + abstract fun requiredKeys(): Int + + abstract fun snippet(): T + + abstract fun verifyResultExpectations(result: R) + + abstract fun successfulResponseBody(): String + + abstract fun unsuccessfulResponseBodyList(): List + + abstract fun mappingBuilder(): MappingBuilder + + abstract fun affectedChannelsAndGroups(): Pair, List> + + open fun optionalScenarioList(): List> = emptyList() + + open fun voidResponse() = false + + override fun onBefore() { + super.onBefore() + expectedStub = stubFor(mappingBuilder().willReturn(aResponse().withBody(successfulResponseBody()))) + config.includeInstanceIdentifier = false + config.includeRequestIdentifier = false + } + + @Test + fun testSuccessAsync() { + snippet().await { result -> + assertFalse(result.isFailure) + verifyResultExpectations(result.getOrThrow()) + } + } + + @Test + fun testSuccessSync() { + runSync() + } + + @Test + fun testUnsuccessfulResponsesSync() { + wireMockServer.removeStub(expectedStub) + + unsuccessfulResponseBodyList().forEach { + val stub = stubFor(mappingBuilder().willReturn(aResponse().withBody(it))) + try { + testSuccessSync() + failTest() + } catch (e: Exception) { + e.printStackTrace() + assertPnException(PubNubError.PARSING_ERROR, e) + } + wireMockServer.removeStub(stub) + } + } + + @Test + fun testUnsuccessfulResponsesAsync() { + wireMockServer.removeStub(expectedStub) + + unsuccessfulResponseBodyList().forEach { + val stub = stubFor(mappingBuilder().willReturn(aResponse().withBody(it))) + snippet().await { result -> + assertTrue(result.isFailure) + assertPnException(PubNubError.PARSING_ERROR, result.exceptionOrNull()) + } + wireMockServer.removeStub(stub) + } + } + + @Test + fun testUsualWrongResponses() { + wireMockServer.removeStub(expectedStub) + + val map = + hashMapOf( + "empty_json" to emptyJson(), + "no_content" to noContent(), + "malformed" to aResponse().withBody("{"), + ) + + if (pnOperation() == PNOperationType.PNSubscribeOperation) { + map.remove("empty_json") + } + + map.forEach { + val stub = stubFor(mappingBuilder().willReturn(it.value)) + try { + val result = snippet().sync() + if (voidResponse()) { + assertNotNull(result) + } else { + failTest(it.key) + } + } catch (e: Exception) { + if (voidResponse()) { + failTest(it.key) + } + assertPnException(PubNubError.PARSING_ERROR, e) + } + + snippet().await { result -> + if (!voidResponse()) { + assertTrue(result.isFailure) + } else { + assertFalse(result.isFailure) + } + + if (!voidResponse()) { + assertPnException(PubNubError.PARSING_ERROR, result.exceptionOrNull()) + } + } + + wireMockServer.removeStub(stub) + } + } + + @Test + fun testOptionalResponsesSync() { + wireMockServer.removeStub(expectedStub) + + optionalScenarioList().forEach { + val stub = stubFor(mappingBuilder().willReturn(it.build())) + + try { + snippet().sync() + if (it.result == com.pubnub.internal.suite.Result.FAIL) { + failTest() + } + } catch (e: Exception) { + if (it.result == com.pubnub.internal.suite.Result.FAIL) { + it.pnError?.let { pubNubError -> + assertPnException(pubNubError, e) + } + } + } + + wireMockServer.removeStub(stub) + } + } + + @Test + fun testOptionalResponsesAsync() { + wireMockServer.removeStub(expectedStub) + + optionalScenarioList().forEach { + val stub = stubFor(mappingBuilder().willReturn(it.build())) + + snippet().await { result -> + it.additionalChecks.invoke(result) + if (it.result == com.pubnub.internal.suite.Result.SUCCESS) { + assertFalse(result.isFailure) + } else if (it.result == com.pubnub.internal.suite.Result.FAIL) { + assertTrue(result.isFailure) + it.pnError?.let { pubNubError -> + assertPnException(pubNubError, result.exceptionOrNull()) + } + } + } + + wireMockServer.removeStub(stub) + } + } + +// @Test // TODO can't test url encoding this way +// fun testUrlEncoding() { +// snippet().apply { +// queryParam += getSpecialCharsMap().map { +// it.name to it.regular +// }.toMap() +// }.await { result -> +// assertFalse(result.isFailure) +// +// getSpecialCharsMap().shuffled().forEach { +// val encodedParam = status.encodedParam(it.name) +// assertEquals(it.encoded, encodedParam) +// } +// } +// } + + @Test + fun testSubscribeKey() { + config.subscribeKey = " " + try { + testSuccessSync() + if (requiredKeys().contains(com.pubnub.internal.suite.SUB)) { + failTest() + } + } catch (e: Exception) { + if (requiredKeys().contains(com.pubnub.internal.suite.SUB)) { + assertPnException(PubNubError.SUBSCRIBE_KEY_MISSING, e) + } + } + } + + @Test + fun testPublishKey() { + config.publishKey = " " + try { + testSuccessSync() + if (requiredKeys().contains(com.pubnub.internal.suite.PUB)) { + failTest() + } + } catch (e: Exception) { + if (requiredKeys().contains(com.pubnub.internal.suite.PUB)) { + assertPnException(PubNubError.PUBLISH_KEY_MISSING, e) + } + } + } + + @Test + fun testAuthKeySync() { + config.authKey = "someAuthKey" + testSuccessSync() + + val requests = findAll(anyRequestedFor(mappingBuilder().build().request.urlMatcher)) + + if (requiredKeys().contains(com.pubnub.internal.suite.AUTH)) { + assertEquals(pubnub.configuration.authKey, requests.last().queryParameter("auth").firstValue()) + } else { + requests.forEach { + assertFalse(it.queryParams.containsKey("auth")) + } + } + } + +// private fun testAuthKeyAsync() { // TODO can't look at params this way +// pubnub.configuration.authKey = "someAuthKey" +// +// snippet().await { result -> +// assertFalse(result.isFailure) +// +// if (requiredKeys().contains(AUTH)) { +// assertEquals(pubnub.configuration.authKey, status.param("auth")) +// } else { +// assertNull(status.param("auth")) +// } +// } +// } + + @Test + fun testSecretKey() { + config.secretKey = " " + try { + testSuccessSync() + if (requiredKeys().contains(com.pubnub.internal.suite.SEC)) { + failTest() + } + } catch (e: Exception) { + if (requiredKeys().contains(com.pubnub.internal.suite.SEC)) { + assertPnException(PubNubError.SECRET_KEY_MISSING, e) + } + } + } + + private fun runSync() { + val result = snippet().sync() + verifyResultExpectations(result) + } + + private fun stubTimeEndpoint() { + stubFor( + get(urlMatching("/time/0.*")) + .willReturn( + aResponse() + .withBody("[1000]"), + ), + ) + } +} + +private fun extractKeys(value: Int): List { + val keys = mutableListOf() + var n = value + while (n > 0) { + val power = + { + var res = 0 + for (i in n downTo 1) { + if (i and i - 1 == 0) { + res = i + break + } + } + res + }.invoke() + keys.add(power) + n -= power + } + return keys +} + +val SUB = 0b001 +val PUB = 0b010 +val AUTH = 0b100 +val SEC = 0b1000 + +private fun Int.contains(sub: Int): Boolean { + return com.pubnub.internal.suite.extractKeys(this).contains(sub) +} + +class OptionalScenario { + var responseBuilder: ResponseDefinitionBuilder.() -> ResponseDefinitionBuilder = { this } + + var additionalChecks: com.pubnub.internal.suite.AsyncCheck = { result: com.pubnub.api.v2.callbacks.Result -> } + var result: com.pubnub.internal.suite.Result = com.pubnub.internal.suite.Result.SUCCESS + var pnError: PubNubError? = null + + internal fun build(): ResponseDefinitionBuilder { + return aResponse().responseBuilder() + } +} + +enum class Result { + SUCCESS, + FAIL, +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/TimeTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/TimeTestSuite.kt new file mode 100644 index 000000000..24c708e18 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/TimeTestSuite.kt @@ -0,0 +1,56 @@ +package com.pubnub.internal.suite + +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.Time +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.PNTimeResult +import com.pubnub.api.v2.callbacks.getOrThrow +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue + +class TimeTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNTimeOperation + + override fun requiredKeys() = 0 + + override fun snippet(): Time { + return pubnub.time() + } + + override fun verifyResultExpectations(result: PNTimeResult) { + assertEquals(1000, result.timetoken) + } + + override fun successfulResponseBody() = """[1000]""" + + override fun unsuccessfulResponseBodyList() = + listOf( + """{}""", + """[false]""", + ) + + override fun optionalScenarioList(): List> { + return listOf( + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { withBody("[wrong]") } + result = com.pubnub.internal.suite.Result.FAIL + additionalChecks = { result -> + assertTrue(result.isFailure) + } + }, + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { withBody("[123]") } + additionalChecks = { result -> + assertFalse(result.isFailure) + assertEquals(123, result.getOrThrow().timetoken) + } + }, + ) + } + + override fun mappingBuilder() = get(urlPathEqualTo("/time/0")) + + override fun affectedChannelsAndGroups() = emptyList() to emptyList() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/AddChannelChannelGroupTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/AddChannelChannelGroupTestSuite.kt new file mode 100644 index 000000000..a5d3608da --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/AddChannelChannelGroupTestSuite.kt @@ -0,0 +1,47 @@ +package com.pubnub.internal.suite.channel_groups + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.channel_groups.AddChannelChannelGroup +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAddChannelResult + +class AddChannelChannelGroupTestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNAddChannelsToGroupOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): AddChannelChannelGroup { + return pubnub.addChannelsToChannelGroup( + channelGroup = "cg1", + channels = listOf("ch1", "ch2"), + ) + } + + override fun verifyResultExpectations(result: PNChannelGroupsAddChannelResult) { + } + + override fun successfulResponseBody() = + """ + { + "status": 200, + "message": "OK", + "service": "channel-registry", + "error": false + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder(): MappingBuilder { + return get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/cg1")) + .withQueryParam("add", equalTo("ch1,ch2")) + } + + override fun affectedChannelsAndGroups() = listOf("ch1", "ch2") to listOf("cg1") + + override fun voidResponse() = true +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/AllChannelsChannelGroupTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/AllChannelsChannelGroupTestSuite.kt new file mode 100644 index 000000000..ae08f453c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/AllChannelsChannelGroupTestSuite.kt @@ -0,0 +1,100 @@ +package com.pubnub.internal.suite.channel_groups + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.endpoints.channel_groups.AllChannelsChannelGroup +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAllChannelsResult +import com.pubnub.api.v2.callbacks.getOrThrow +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue + +class AllChannelsChannelGroupTestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNChannelsForGroupOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): AllChannelsChannelGroup { + return pubnub.listChannelsForChannelGroup( + channelGroup = "cg1", + ) + } + + override fun verifyResultExpectations(result: PNChannelGroupsAllChannelsResult) { + assertEquals(listOf("ch1", "ch2"), result.channels) + } + + override fun successfulResponseBody() = + """ + { + "status": 200, + "payload": { + "channels": [ + "ch1", + "ch2" + ], + "group": "cg1" + }, + "service": "channel-registry", + "error": false + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = + listOf( + """{"payload":{"channels":null,"group":null}}""", + """{"payload":{"channels":null}}""", + """{"payload":{}}""", + """{"payload":null}""", + ) + + override fun mappingBuilder(): MappingBuilder { + return get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/cg1")) + } + + override fun affectedChannelsAndGroups() = emptyList() to listOf("cg1") + + override fun optionalScenarioList() = + listOf( + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { + withBody( + """ + { + "payload": { + "channels": [], + "group": "cg1" + } + } + """.trimIndent(), + ) + } + additionalChecks = { result -> + assertFalse(result.isFailure) + assertTrue(result.getOrThrow().channels.isEmpty()) + } + }, + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { + withBody( + """ + { + "payload": { + "group": "cg1" + } + } + """.trimIndent(), + ) + } + pnError = PubNubError.PARSING_ERROR + result = com.pubnub.internal.suite.Result.FAIL + additionalChecks = { result -> + assertTrue(result.isFailure) + } + }, + ) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/DeleteChannelGroupTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/DeleteChannelGroupTestSuite.kt new file mode 100644 index 000000000..b30137a8b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/DeleteChannelGroupTestSuite.kt @@ -0,0 +1,44 @@ +package com.pubnub.internal.suite.channel_groups + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.channel_groups.DeleteChannelGroup +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsDeleteGroupResult + +class DeleteChannelGroupTestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNRemoveGroupOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): DeleteChannelGroup { + return pubnub.deleteChannelGroup( + channelGroup = "cg1", + ) + } + + override fun verifyResultExpectations(result: PNChannelGroupsDeleteGroupResult) { + } + + override fun successfulResponseBody() = + """ + { + "status": 200, + "message": "OK", + "service": "channel-registry", + "error": false + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder(): MappingBuilder { + return get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/cg1/remove")) + } + + override fun affectedChannelsAndGroups() = emptyList() to listOf("cg1") + + override fun voidResponse() = true +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/ListAllChannelGroupTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/ListAllChannelGroupTestSuite.kt new file mode 100644 index 000000000..ae63bd6d1 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/ListAllChannelGroupTestSuite.kt @@ -0,0 +1,58 @@ +package com.pubnub.internal.suite.channel_groups + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.channel_groups.ListAllChannelGroup +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsListAllResult +import com.pubnub.api.v2.callbacks.getOrThrow +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue + +class ListAllChannelGroupTestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNChannelGroupsOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): ListAllChannelGroup { + return pubnub.listAllChannelGroups() + } + + override fun verifyResultExpectations(result: PNChannelGroupsListAllResult) { + assertEquals(listOf("cg1", "cg2"), result.groups) + } + + override fun successfulResponseBody() = """{"payload":{"groups":["cg1","cg2"]}}""" + + override fun unsuccessfulResponseBodyList() = + listOf( + """{"payload":{"groups":null}}""", + """{"payload":{"groups":{}}}""", + """{"payload":{}}""", + """{"payload":null}""", + ) + + override fun mappingBuilder(): MappingBuilder { + return get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group")) + } + + override fun affectedChannelsAndGroups() = emptyList() to emptyList() + + override fun optionalScenarioList(): List> { + return listOf( + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { + withBody("""{"payload":{"groups":[]}}""") + } + additionalChecks = { result -> + assertFalse(result.isFailure) + assertTrue(result.getOrThrow().groups.isEmpty()) + } + result = com.pubnub.internal.suite.Result.SUCCESS + }, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/RemoveChannelChannelGroupTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/RemoveChannelChannelGroupTestSuite.kt new file mode 100644 index 000000000..7215254cf --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/channel_groups/RemoveChannelChannelGroupTestSuite.kt @@ -0,0 +1,51 @@ +package com.pubnub.internal.suite.channel_groups + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.channel_groups.RemoveChannelChannelGroup +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsRemoveChannelResult +import org.junit.Assert.assertTrue + +class RemoveChannelChannelGroupTestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNRemoveChannelsFromGroupOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): RemoveChannelChannelGroup { + return pubnub.removeChannelsFromChannelGroup( + channelGroup = "cg1", + channels = listOf("ch1", "ch2"), + ) + } + + override fun verifyResultExpectations(result: PNChannelGroupsRemoveChannelResult) {} + + override fun successfulResponseBody() = """{"payload":{"groups":["cg1","cg2"]}}""" + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder(): MappingBuilder { + return get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/cg1")) + .withQueryParam("remove", equalTo("ch1,ch2")) + } + + override fun affectedChannelsAndGroups() = listOf("ch1", "ch2") to listOf("cg1") + + override fun voidResponse() = true + + override fun optionalScenarioList(): List> { + return listOf( + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { withBody("").withStatus(400) } + result = com.pubnub.internal.suite.Result.FAIL + additionalChecks = { result -> + assertTrue(result.isFailure) + } + }, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/grant/GrantTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/grant/GrantTestSuite.kt new file mode 100644 index 000000000..fa07ef409 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/grant/GrantTestSuite.kt @@ -0,0 +1,71 @@ +package com.pubnub.internal.suite.grant + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.matching +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.access.Grant +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerGrantResult +import org.junit.Assert.assertEquals + +class GrantTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun onBefore() { + super.onBefore() + config.secretKey = "mySecretKey" + } + + override fun pnOperation() = PNOperationType.PNAccessManagerGrant + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.SEC + + override fun snippet(): Grant { + return pubnub.grant().apply { + } + } + + override fun verifyResultExpectations(result: PNAccessManagerGrantResult) { + assertEquals("mySubscribeKey", result.subscribeKey) + assertEquals(0, result.channels.size) + assertEquals(0, result.channelGroups.size) + assertEquals("subkey", result.level) + assertEquals(1, result.ttl) + } + + override fun successfulResponseBody() = + """ + { + "message": "Success", + "payload": { + "level": "subkey", + "subscribe_key": "mySubscribeKey", + "ttl": 1, + "r": 0, + "w": 0, + "m": 0, + "d": 0 + }, + "service": "Access Manager", + "status": 200 + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = + listOf( + """{"payload":{}}""", + """{"payload":null}""", + ) + + override fun mappingBuilder(): MappingBuilder { + return get(urlPathEqualTo("/v2/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("r", equalTo("0")) + .withQueryParam("w", equalTo("0")) + .withQueryParam("d", equalTo("0")) + .withQueryParam("m", equalTo("0")) + .withQueryParam("ttl", equalTo("-1")) + .withQueryParam("signature", matching(".*")) + } + + override fun affectedChannelsAndGroups() = emptyList() to emptyList() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/DeleteMessagesTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/DeleteMessagesTestSuite.kt new file mode 100644 index 000000000..f2b1d891e --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/DeleteMessagesTestSuite.kt @@ -0,0 +1,44 @@ +package com.pubnub.internal.suite.history + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.delete +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.DeleteMessages +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.history.PNDeleteMessagesResult + +class DeleteMessagesTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNDeleteMessagesOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): DeleteMessages = + pubnub.deleteMessages( + channels = listOf("ch1"), + ) + + override fun verifyResultExpectations(result: PNDeleteMessagesResult) { + } + + override fun successfulResponseBody() = + """ + { + "status": 200, + "error": false, + "error_message": "" + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder(): MappingBuilder { + return delete(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/channel/ch1")) + .withQueryParam("start", absent()) + .withQueryParam("end", absent()) + } + + override fun affectedChannelsAndGroups() = emptyList() to emptyList() + + override fun voidResponse() = true +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/counts/MessageCountsTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/counts/MessageCountsTestSuite.kt new file mode 100644 index 000000000..0b6a48872 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/counts/MessageCountsTestSuite.kt @@ -0,0 +1,48 @@ +package com.pubnub.internal.suite.history.counts + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.MessageCounts +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.history.PNMessageCountResult +import org.junit.Assert.assertEquals + +class MessageCountsTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNMessageCountOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): MessageCounts = + pubnub.messageCounts( + channels = listOf("ch1"), + channelsTimetoken = listOf(1588284000000), + ) + + override fun verifyResultExpectations(result: PNMessageCountResult) { + assertEquals(1, result.channels.keys.size) + assertEquals(5L, result.channels["ch1"]) + } + + override fun successfulResponseBody() = + """ + { + "status": 200, + "error": false, + "error_message": "", + "channels": { + "ch1": 5 + } + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder(): MappingBuilder { + return get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/message-counts/ch1")) + .withQueryParam("timetoken", equalTo("1588284000000")) + } + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v2/HistoryMetaTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v2/HistoryMetaTestSuite.kt new file mode 100644 index 000000000..b9625a7cd --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v2/HistoryMetaTestSuite.kt @@ -0,0 +1,101 @@ +package com.pubnub.internal.suite.history.v2 + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.History +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.history.PNHistoryResult +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue + +class HistoryMetaTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNHistoryOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): History = + pubnub.history( + channel = "ch1", + includeMeta = true, + includeTimetoken = true, + ) + + override fun verifyResultExpectations(result: PNHistoryResult) { + assertEquals(100L, result.startTimetoken) + assertEquals(200L, result.endTimetoken) + assertEquals(2, result.messages.size) + assertEquals("msg1", result.messages[0].entry.asString) + assertEquals("msg2", result.messages[1].entry.asString) + assertTrue(result.messages[0].meta!!.asString.isBlank()) + assertEquals( + mapOf("color" to "red"), + Gson().fromJson( + result.messages[1].meta!!.asJsonObject, + object : TypeToken?>() {}.type, + ), + ) + assertEquals(100L, result.messages[0].timetoken) + assertEquals(200L, result.messages[1].timetoken) + } + + override fun successfulResponseBody() = + """ + [ + [ + { + "message": "msg1", + "timetoken": 100, + "meta": "" + }, + { + "message": "msg2", + "timetoken": 200, + "meta": { + "color": "red" + } + } + ], + 100, + 200 + ] + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = + listOf( + "[]", + "[{}]", + ) + + override fun mappingBuilder(): MappingBuilder { + return get( + urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/ch1"), + ) + .withQueryParam("include_token", equalTo("true")) + .withQueryParam("count", equalTo("100")) + .withQueryParam("include_meta", equalTo("true")) + .withQueryParam("reverse", equalTo("false")) + } + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() + + override fun optionalScenarioList() = + listOf( + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { + withBody("""["First Element Not An Array",0,0]""") + } + additionalChecks = { result -> + assertTrue(result.isFailure) + assertEquals((result.exceptionOrNull() as? PubNubException)?.errorMessage, "History is disabled") + } + result = com.pubnub.internal.suite.Result.FAIL + pnError = PubNubError.HTTP_ERROR + }, + ) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v2/HistoryTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v2/HistoryTestSuite.kt new file mode 100644 index 000000000..507ff55be --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v2/HistoryTestSuite.kt @@ -0,0 +1,72 @@ +package com.pubnub.internal.suite.history.v2 + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.History +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.history.PNHistoryResult +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue + +class HistoryTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNHistoryOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): History = + pubnub.history( + channel = "ch1", + ) + + override fun verifyResultExpectations(result: PNHistoryResult) { + assertEquals(100L, result.startTimetoken) + assertEquals(200L, result.endTimetoken) + assertEquals(2, result.messages.size) + assertEquals("msg1", result.messages[0].entry.asString) + assertEquals("msg2", result.messages[1].entry.asString) + assertNull(result.messages[0].meta) + assertNull(result.messages[1].meta) + assertNull(result.messages[0].timetoken) + assertNull(result.messages[1].timetoken) + } + + override fun successfulResponseBody() = """[["msg1","msg2"],100,200]""" + + override fun unsuccessfulResponseBodyList() = + listOf( + "[]", + "[{}]", + ) + + override fun mappingBuilder(): MappingBuilder { + return get( + urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/ch1"), + ) + .withQueryParam("include_token", equalTo("false")) + .withQueryParam("count", equalTo("100")) + .withQueryParam("include_meta", equalTo("false")) + .withQueryParam("reverse", equalTo("false")) + } + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() + + override fun optionalScenarioList() = + listOf( + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { + withBody("""["First Element Not An Array",0,0]""") + } + additionalChecks = { result -> + assertTrue(result.isFailure) + assertEquals((result.exceptionOrNull() as? PubNubException)?.errorMessage, "History is disabled") + } + result = com.pubnub.internal.suite.Result.FAIL + pnError = PubNubError.HTTP_ERROR + }, + ) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v3/FetchMessagesMetaActionsTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v3/FetchMessagesMetaActionsTestSuite.kt new file mode 100644 index 000000000..07610a259 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v3/FetchMessagesMetaActionsTestSuite.kt @@ -0,0 +1,122 @@ +package com.pubnub.internal.suite.history.v3 + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.google.gson.JsonObject +import com.pubnub.api.PubNubError +import com.pubnub.api.endpoints.FetchMessages +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.history.PNFetchMessagesResult +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue + +class FetchMessagesMetaActionsTestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNFetchMessagesOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): FetchMessages = + pubnub.fetchMessages( + channels = listOf("ch1"), + includeMeta = true, + includeMessageActions = true, + ) + + override fun verifyResultExpectations(result: PNFetchMessagesResult) { + assertEquals(1, result.channels.size) + assertTrue(result.channels.containsKey("ch1")) + assertEquals("bar", result.channels["ch1"]!![0].message.asString) + assertEquals( + JsonObject().apply { addProperty("color", "red") }, + result.channels["ch1"]!![0].meta, + ) + assertEquals(100L, result.channels["ch1"]!![0].timetoken) + + val actions = result.channels["ch1"]!![0].actions + assertEquals(1, actions!!.keys.size) + assertEquals(1, actions["reaction"]!!["smile"]!!.size) + assertEquals("publishersUuid", actions["reaction"]!!["smile"]!![0].uuid) + assertEquals(200, actions["reaction"]!!["smile"]!![0].actionTimetoken) + } + + override fun successfulResponseBody() = + """ + { + "channels": { + "ch1": [ + { + "message": "bar", + "meta": { + "color": "red" + }, + "timetoken": 100, + "actions": { + "reaction": { + "smile": [ + { + "uuid": "publishersUuid", + "actionTimetoken": "200" + } + ] + } + } + } + ] + } + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = + listOf( + """ + { + "channels": { + "ch1": [ + { + "timetoken": 100 + } + ] + } + } + """.trimIndent(), + """ + { + "channels": { + "ch1": [ + { + "entry": "hello", + "timetoken": 100 + } + ], + "ch2": [], + "ch3": null + } + } + """.trimIndent(), + ) + + override fun mappingBuilder(): MappingBuilder { + return get(urlPathEqualTo("/v3/history-with-actions/sub-key/mySubscribeKey/channel/ch1")) + .withQueryParam("max", equalTo("25")) + .withQueryParam("include_meta", equalTo("true")) + } + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() + + override fun optionalScenarioList(): List> { + return listOf( + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { withBody("""{"channels":{"ch3":null}}""") } + result = com.pubnub.internal.suite.Result.FAIL + pnError = PubNubError.PARSING_ERROR + additionalChecks = { result -> + assertTrue(result.isFailure) +// assertEquals(PNStatusCategory.PNMalformedResponseCategory, status.category) + } + }, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v3/FetchMessagesTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v3/FetchMessagesTestSuite.kt new file mode 100644 index 000000000..7331c8319 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/history/v3/FetchMessagesTestSuite.kt @@ -0,0 +1,98 @@ +package com.pubnub.internal.suite.history.v3 + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.endpoints.FetchMessages +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.history.PNFetchMessagesResult +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue + +class FetchMessagesTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNFetchMessagesOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): FetchMessages = + pubnub.fetchMessages( + channels = listOf("ch1"), + ) + + override fun verifyResultExpectations(result: PNFetchMessagesResult) { + assertEquals(1, result.channels.size) + assertTrue(result.channels.containsKey("ch1")) + assertEquals("hello", result.channels["ch1"]!![0].message.asString) + assertEquals(100L, result.channels["ch1"]!![0].timetoken) + assertNull(result.channels["ch1"]!![0].meta) + } + + override fun successfulResponseBody() = + """ + { + "channels": { + "ch1": [ + { + "message": "hello", + "timetoken": 100 + } + ] + } + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = + listOf( + """ + { + "channels": { + "ch1": [ + { + "timetoken": 100 + } + ] + } + } + """.trimIndent(), + """ + { + "channels": { + "ch1": [ + { + "entry": "hello", + "timetoken": 100 + } + ], + "ch2": [], + "ch3": null + } + } + """.trimIndent(), + ) + + override fun mappingBuilder(): MappingBuilder { + return get(urlPathEqualTo("/v3/history/sub-key/mySubscribeKey/channel/ch1")) + .withQueryParam("max", equalTo("100")) + .withQueryParam("include_meta", absent()) + } + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() + + override fun optionalScenarioList(): List> { + return listOf( + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { withBody("""{"channels":{"ch3":null}}""") } + result = com.pubnub.internal.suite.Result.FAIL + pnError = PubNubError.PARSING_ERROR + additionalChecks = { result -> + assertTrue(result.isFailure) +// assertEquals(PNStatusCategory.PNMalformedResponseCategory, status.category) + } + }, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/AddMessageActionTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/AddMessageActionTestSuite.kt new file mode 100644 index 000000000..e465579c8 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/AddMessageActionTestSuite.kt @@ -0,0 +1,64 @@ +package com.pubnub.internal.suite.message_actions + +import com.github.tomakehurst.wiremock.client.WireMock.equalToJson +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.urlMatching +import com.pubnub.api.endpoints.message_actions.AddMessageAction +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.message_actions.PNAddMessageActionResult +import com.pubnub.api.models.consumer.message_actions.PNMessageAction +import org.junit.Assert.assertEquals + +class AddMessageActionTestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNAddMessageAction + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): AddMessageAction = + pubnub.addMessageAction( + channel = "ch1", + messageAction = + PNMessageAction( + type = "reaction", + value = "smiley", + messageTimetoken = 1000, + ), + ) + + override fun successfulResponseBody() = + """ + { + "status": 200, + "data": { + "messageTimetoken": "123", + "type": "reaction", + "uuid": "someUuid", + "value": "smiley", + "actionTimetoken": "1000" + } + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = + listOf( + """{"status":200,"data":null}""", + """{"status":200}""", + ) + + override fun verifyResultExpectations(result: PNAddMessageActionResult) { + assertEquals(123L, result.messageTimetoken) + assertEquals("reaction", result.type) + assertEquals("someUuid", result.uuid) + assertEquals("smiley", result.value) + assertEquals(1000L, result.actionTimetoken) + } + + override fun mappingBuilder() = + post(urlMatching("/v1/message-actions/mySubscribeKey/channel/ch1/message/1000.*")) + .withRequestBody( + equalToJson("""{"type":"reaction","value":"smiley"}"""), + ) + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/GetMessageActionsMultipleTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/GetMessageActionsMultipleTestSuite.kt new file mode 100644 index 000000000..0ebf3f6d7 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/GetMessageActionsMultipleTestSuite.kt @@ -0,0 +1,79 @@ +package com.pubnub.internal.suite.message_actions + +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.message_actions.GetMessageActions +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.PNBoundedPage +import com.pubnub.api.models.consumer.message_actions.PNGetMessageActionsResult +import org.junit.Assert.assertEquals + +class GetMessageActionsMultipleTestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNGetMessageActions + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): GetMessageActions = + pubnub.getMessageActions( + channel = "ch1", + page = + PNBoundedPage( + start = 991, + end = 969, + limit = 5, + ), + ) + + override fun successfulResponseBody() = + """ + { + "status": 200, + "data": [ + { + "messageTimetoken": "100", + "type": "reaction", + "uuid": "abc", + "value": "๐Ÿ˜€", + "actionTimetoken": "970" + }, + { + "messageTimetoken": "100", + "type": "reaction", + "uuid": "abc", + "value": "๐Ÿ˜ฌ", + "actionTimetoken": "980" + }, + { + "messageTimetoken": "200", + "type": "action", + "uuid": "xyz", + "value": "๐Ÿ˜€", + "actionTimetoken": "990" + } + ] + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = + listOf( + """{"status":200,"data":null}""", + """{"status":200}""", + ) + + override fun verifyResultExpectations(result: PNGetMessageActionsResult) { + assertEquals(3, result.actions.size) + assertEquals(2, result.actions.filter { it.uuid == "abc" }.size) + assertEquals(2, result.actions.filter { it.value == "\uD83D\uDE00" }.size) + assertEquals(2, result.actions.filter { it.messageTimetoken == 100L }.size) + } + + override fun mappingBuilder() = + get(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/ch1")) + .withQueryParam("start", equalTo("991")) + .withQueryParam("end", equalTo("969")) + .withQueryParam("limit", equalTo("5")) + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/GetMessageActionsTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/GetMessageActionsTestSuite.kt new file mode 100644 index 000000000..74f8477d5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/GetMessageActionsTestSuite.kt @@ -0,0 +1,62 @@ +package com.pubnub.internal.suite.message_actions + +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.message_actions.GetMessageActions +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.message_actions.PNGetMessageActionsResult +import org.junit.Assert.assertEquals + +class GetMessageActionsTestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNGetMessageActions + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): GetMessageActions = + pubnub.getMessageActions( + channel = "ch1", + ) + + override fun successfulResponseBody() = + """ + { + "status": 200, + "data": [ + { + "messageTimetoken": "100", + "type": "reaction", + "uuid": "abc", + "value": "smiley", + "actionTimetoken": "200" + } + ] + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = + listOf( + """{"status":200,"data":null}""", + """{"status":200}""", + ) + + override fun verifyResultExpectations(result: PNGetMessageActionsResult) { + assertEquals(1, result.actions.size) + result.actions[0].apply { + assertEquals(100L, messageTimetoken) + assertEquals("reaction", type) + assertEquals("abc", uuid) + assertEquals("smiley", value) + assertEquals(200L, actionTimetoken) + } + } + + override fun mappingBuilder() = + get(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/ch1")) + .withQueryParam("start", absent()) + .withQueryParam("end", absent()) + .withQueryParam("limit", absent()) + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/RemoveMessageActionsTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/RemoveMessageActionsTestSuite.kt new file mode 100644 index 000000000..de8263a9b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/message_actions/RemoveMessageActionsTestSuite.kt @@ -0,0 +1,41 @@ +package com.pubnub.internal.suite.message_actions + +import com.github.tomakehurst.wiremock.client.WireMock.delete +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.message_actions.RemoveMessageAction +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.message_actions.PNRemoveMessageActionResult + +class RemoveMessageActionsTestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNDeleteMessageAction + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): RemoveMessageAction { + return pubnub.removeMessageAction( + channel = "ch1", + messageTimetoken = 100, + actionTimetoken = 200, + ) + } + + override fun successfulResponseBody() = + """ + { + "status": 200, + "data": {} + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun verifyResultExpectations(result: PNRemoveMessageActionResult) { + } + + override fun mappingBuilder() = delete(urlPathEqualTo("/v1/message-actions/mySubscribeKey/channel/ch1/message/100/action/200")) + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() + + override fun voidResponse() = true +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/GetStateTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/GetStateTestSuite.kt new file mode 100644 index 000000000..4089ed183 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/GetStateTestSuite.kt @@ -0,0 +1,68 @@ +package com.pubnub.internal.suite.presence + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.google.gson.JsonNull +import com.google.gson.JsonObject +import com.pubnub.api.endpoints.presence.GetState +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.presence.PNGetStateResult +import com.pubnub.api.v2.callbacks.getOrThrow +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse + +class GetStateTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNGetState + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): GetState = + pubnub.getPresenceState( + channels = listOf("ch1"), + ) + + override fun verifyResultExpectations(result: PNGetStateResult) { + assertEquals(1, result.stateByUUID.keys.size) + assertEquals(JsonObject().apply { addProperty("text", "hello") }, result.stateByUUID["ch1"]) + } + + override fun successfulResponseBody() = + """ + { + "status": 200, + "message": "OK", + "payload": { + "text": "hello" + }, + "uuid": "myUUID", + "channel": "ch1", + "service": "Presence" + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder(): MappingBuilder = get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/uuid/myUUID")) + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() + + override fun optionalScenarioList(): List> { + return listOf( + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { withBody("""{"payload":{}}""") } + additionalChecks = { result -> + assertFalse(result.isFailure) + assertEquals(JsonObject(), result.getOrThrow().stateByUUID["ch1"]) + } + }, + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { withBody("""{"payload":null}""") } + additionalChecks = { result -> + assertFalse(result.isFailure) + assertEquals(JsonNull.INSTANCE, result.getOrThrow().stateByUUID["ch1"]) + } + }, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/HeartbeatTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/HeartbeatTestSuite.kt new file mode 100644 index 000000000..a84ecda1f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/HeartbeatTestSuite.kt @@ -0,0 +1,45 @@ +package com.pubnub.internal.suite.presence + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.enums.PNOperationType +import com.pubnub.internal.endpoints.presence.HeartbeatEndpoint +import org.junit.Assert.assertTrue + +class HeartbeatTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNHeartbeatOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): HeartbeatEndpoint { + return HeartbeatEndpoint( + pubnub = pubnub, + channels = listOf("ch1"), + ) + } + + override fun verifyResultExpectations(result: Boolean) { + assertTrue(result) + } + + override fun successfulResponseBody() = + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder(): MappingBuilder = + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .withQueryParam("heartbeat", equalTo(config.presenceTimeout.toString())) + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() + + override fun voidResponse() = true +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/HereNowTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/HereNowTestSuite.kt new file mode 100644 index 000000000..9381acd7f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/HereNowTestSuite.kt @@ -0,0 +1,56 @@ +package com.pubnub.internal.suite.presence + +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.presence.HereNow +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.presence.PNHereNowResult +import org.junit.Assert.assertEquals + +class HereNowTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNHereNowOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): HereNow = + pubnub.hereNow( + channels = listOf("ch1"), + ) + + override fun verifyResultExpectations(result: PNHereNowResult) { + assertEquals(1, result.totalChannels) + assertEquals(1, result.totalOccupancy) + assertEquals(1, result.channels.size) + assertEquals("user_1", result.channels["ch1"]!!.occupants[0].uuid) + } + + override fun successfulResponseBody() = + """ + { + "status": 200, + "message": "OK", + "occupancy": 1, + "uuids": [ + "user_1" + ], + "service": "Presence" + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = + listOf( + """{"occupancy": 0, "uuids": null}""", + """{"payload": {"channels": null, "total_channels": 0, "total_occupancy": 0}}""", + """{"payload": {}}""", + """{"payload": null}""", + ) + + override fun mappingBuilder() = + get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/ch1")) + .withQueryParam("state", absent()) + .withQueryParam("disable_uuids", absent()) + .withQueryParam("channel-group", absent()) + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/LeaveTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/LeaveTestSuite.kt new file mode 100644 index 000000000..2d2abf4a5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/LeaveTestSuite.kt @@ -0,0 +1,41 @@ +package com.pubnub.internal.suite.presence + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.enums.PNOperationType +import com.pubnub.internal.endpoints.presence.LeaveEndpoint +import org.junit.Assert.assertTrue + +class LeaveTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNUnsubscribeOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): LeaveEndpoint { + return LeaveEndpoint(pubnub).apply { + channels = listOf("ch1") + } + } + + override fun verifyResultExpectations(result: Boolean) { + assertTrue(result) + } + + override fun successfulResponseBody() = + """ + { + "status": 200, + "message": "OK", + "service": "Presence" + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder(): MappingBuilder = get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/leave")) + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() + + override fun voidResponse() = true +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/StateSetTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/StateSetTestSuite.kt new file mode 100644 index 000000000..c3d12802f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/StateSetTestSuite.kt @@ -0,0 +1,73 @@ +package com.pubnub.internal.suite.presence + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.google.gson.JsonObject +import com.pubnub.api.endpoints.presence.SetState +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.presence.PNSetStateResult +import com.pubnub.api.v2.callbacks.getOrThrow +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue + +class StateSetTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNSetStateOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): SetState = + pubnub.setPresenceState( + channels = listOf("ch1"), + state = mapOf("text" to "hello"), + ) + + override fun verifyResultExpectations(result: PNSetStateResult) { + assertEquals(JsonObject().apply { addProperty("text", "hello") }, result.state) + } + + override fun successfulResponseBody() = + """ + { + "status": 200, + "message": "OK", + "payload": { + "text": "hello" + }, + "service": "Presence" + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = + listOf( + """{"payload":null}""", + ) + + override fun mappingBuilder(): MappingBuilder = + get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/uuid/myUUID/data")) + .withQueryParam("state", equalTo("""{"text":"hello"}""")) + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() + + override fun optionalScenarioList(): List> { + return listOf( + com.pubnub.internal.suite.OptionalScenario().apply { + result = com.pubnub.internal.suite.Result.SUCCESS + responseBuilder = { withBody("""{"payload":{}}""") } + additionalChecks = { result -> + assertFalse(result.isFailure) + assertTrue(result.getOrThrow().state.asJsonObject.keySet().isEmpty()) + } + }, + com.pubnub.internal.suite.OptionalScenario().apply { + result = com.pubnub.internal.suite.Result.FAIL + responseBuilder = { withBody("""{"payload":null}""") } + additionalChecks = { result -> + assertTrue(result.isFailure) + } + }, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/WhereNowTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/WhereNowTestSuite.kt new file mode 100644 index 000000000..c229c1037 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/presence/WhereNowTestSuite.kt @@ -0,0 +1,49 @@ +package com.pubnub.internal.suite.presence + +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.presence.WhereNow +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.presence.PNWhereNowResult +import org.junit.Assert.assertEquals + +class WhereNowTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNWhereNowOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): WhereNow { + return pubnub.whereNow().apply { + } + } + + override fun verifyResultExpectations(result: PNWhereNowResult) { + assertEquals(1, result.channels.size) + assertEquals("ch1", result.channels[0]) + } + + override fun successfulResponseBody() = + """ + { + "status": 200, + "message": "OK", + "payload": { + "channels": [ + "ch1" + ] + }, + "service": "Presence" + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = + listOf( + """{"payload":{"channels":null}}""", + """{"payload": {}}""", + """{"payload": null}""", + ) + + override fun mappingBuilder() = get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + + override fun affectedChannelsAndGroups() = emptyList() to emptyList() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/PublishGetTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/PublishGetTestSuite.kt new file mode 100644 index 000000000..6e0d79d88 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/PublishGetTestSuite.kt @@ -0,0 +1,56 @@ +package com.pubnub.internal.suite.pubsub + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.google.gson.Gson +import com.pubnub.api.endpoints.pubsub.Publish +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.PNPublishResult +import org.junit.Assert.assertEquals +import java.net.URLEncoder +import java.nio.charset.StandardCharsets + +class PublishGetTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNPublishOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.PUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): Publish { + return pubnub.publish( + channel = "ch1", + message = "ch2", + ) + } + + override fun verifyResultExpectations(result: PNPublishResult) { + assertEquals(15883272000000000L, result.timetoken) + } + + override fun successfulResponseBody() = """[1,"Sent","15883272000000000"]""" + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder(): MappingBuilder { + return get( + urlPathEqualTo( + "/publish/myPublishKey/mySubscribeKey/0/ch1/0/%s".format( + URLEncoder.encode(Gson().toJson("ch2"), StandardCharsets.UTF_8.name()), + ), + ), + )!! + } + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() + + /*companion object { + @JvmStatic + fun predefined(): Stream? { + return Stream.of( + Arguments.of("ch1"), + Arguments.of(123), + Arguments.of(3.14) + ) + } + }*/ +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/PublishPostTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/PublishPostTestSuite.kt new file mode 100644 index 000000000..ddff0ec77 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/PublishPostTestSuite.kt @@ -0,0 +1,66 @@ +package com.pubnub.internal.suite.pubsub + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.equalToJson +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.google.gson.Gson +import com.pubnub.api.endpoints.pubsub.Publish +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.PNPublishResult +import org.json.JSONObject +import org.junit.Assert.assertEquals + +class PublishPostTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNPublishOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.PUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): Publish { + return pubnub.publish( + channel = "ch1", + message = + mapOf( + "name" to "john", + "age" to 30, + "private" to false, + ), + meta = + JSONObject().apply { + put("city", "sf") + }.toMap(), + usePost = true, + ) + } + + override fun verifyResultExpectations(result: PNPublishResult) { + assertEquals(15883272000000000L, result.timetoken) + } + + override fun successfulResponseBody() = """[1,"Sent","15883272000000000"]""" + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder(): MappingBuilder { + return post( + urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/ch1/0"), + ) + .withQueryParam( + "meta", + equalToJson("""{"city":"sf"}"""), + ) + .withRequestBody( + equalToJson( + Gson().toJson( + mapOf( + "name" to "john", + "age" to 30, + "private" to false, + ), + ), + ), + )!! + } + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/SignalTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/SignalTestSuite.kt new file mode 100644 index 000000000..a6d726d7c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/SignalTestSuite.kt @@ -0,0 +1,45 @@ +package com.pubnub.internal.suite.pubsub + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.google.gson.Gson +import com.pubnub.api.endpoints.pubsub.Signal +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.models.consumer.PNPublishResult +import org.junit.Assert.assertEquals +import java.net.URLEncoder +import java.nio.charset.StandardCharsets + +class SignalTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNSignalOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.PUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): Signal { + return pubnub.signal( + channel = "ch1", + message = "ch2", + ) + } + + override fun verifyResultExpectations(result: PNPublishResult) { + assertEquals(15883272000000000L, result.timetoken) + } + + override fun successfulResponseBody() = """[1,"Sent","15883272000000000"]""" + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder(): MappingBuilder { + return get( + urlPathEqualTo( + "/signal/myPublishKey/mySubscribeKey/0/ch1/0/%s".format( + URLEncoder.encode(Gson().toJson("ch2"), StandardCharsets.UTF_8.name()), + ), + ), + )!! + } + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/SubscribeTestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/SubscribeTestSuite.kt new file mode 100644 index 000000000..4b4f66151 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/pubsub/SubscribeTestSuite.kt @@ -0,0 +1,53 @@ +package com.pubnub.internal.suite.pubsub + +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.enums.PNOperationType +import com.pubnub.internal.endpoints.pubsub.SubscribeEndpoint +import com.pubnub.internal.models.server.SubscribeEnvelope +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue + +class SubscribeTestSuite : com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNSubscribeOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): SubscribeEndpoint { + return SubscribeEndpoint(pubnub).apply { + channels = listOf("ch1") + } + } + + override fun verifyResultExpectations(result: SubscribeEnvelope) { + assertEquals(100, result.metadata.timetoken) + assertEquals("1", result.metadata.region) + assertTrue(result.messages.isEmpty()) + } + + override fun successfulResponseBody() = + """ + { + "t": { + "t": "100", + "r": 1 + }, + "m": [] + } + """.trimIndent() + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder() = + get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch1/0")) + .withQueryParam("tt", absent()) + .withQueryParam("tr", absent()) + .withQueryParam("filter-expr", absent()) + .withQueryParam("state", absent()) + .withQueryParam("channel-group", absent()) + .withQueryParam("heartbeat", equalTo("300")) + + override fun affectedChannelsAndGroups() = listOf("ch1") to emptyList() +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/add/AddChannelsToPushV1TestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/add/AddChannelsToPushV1TestSuite.kt new file mode 100644 index 000000000..80b091f32 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/add/AddChannelsToPushV1TestSuite.kt @@ -0,0 +1,68 @@ +package com.pubnub.internal.suite.push.add + +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.endpoints.push.AddChannelsToPush +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.models.consumer.push.PNPushAddChannelResult +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue + +class AddChannelsToPushV1TestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNAddPushNotificationsOnChannelsOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): AddChannelsToPush { + return pubnub.addPushNotificationsOnChannels( + pushType = PNPushType.FCM, + channels = listOf("ch1", "ch2"), + deviceId = "12345", + ) + } + + override fun verifyResultExpectations(result: PNPushAddChannelResult) { + } + + override fun successfulResponseBody(): String { + return """[1, "Modified Channels"]""" + } + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder() = + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/12345")) + .withQueryParam("type", equalTo("gcm")) + .withQueryParam("add", equalTo("ch1,ch2")) + .withQueryParam("environment", absent()) + .withQueryParam("topic", absent()) + + override fun affectedChannelsAndGroups() = listOf("ch1", "ch2") to emptyList() + + override fun voidResponse() = true + + override fun optionalScenarioList(): List> { + return listOf( + com.pubnub.internal.suite.OptionalScenario().apply { + val body = + "{\"error\":\"Use of the mobile push notifications API requires Push Notifications" + + " which is not enabled for this subscribe key. Login to your PubNub Dashboard Account" + + " and enable Push Notifications. " + + "Contact support@pubnub.com if you require further assistance.\"}" + result = com.pubnub.internal.suite.Result.FAIL + responseBuilder = { withBody(body).withStatus(400) } + pnError = PubNubError.HTTP_ERROR + additionalChecks = { result -> + assertTrue(voidResponse()) + assertEquals(body, (result.exceptionOrNull() as? PubNubException)?.jso) + } + }, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/add/AddChannelsToPushV2TestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/add/AddChannelsToPushV2TestSuite.kt new file mode 100644 index 000000000..6c7d09d12 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/add/AddChannelsToPushV2TestSuite.kt @@ -0,0 +1,46 @@ +package com.pubnub.internal.suite.push.add + +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.push.AddChannelsToPush +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.models.consumer.push.PNPushAddChannelResult + +class AddChannelsToPushV2TestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNAddPushNotificationsOnChannelsOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): AddChannelsToPush { + return pubnub.addPushNotificationsOnChannels( + pushType = PNPushType.APNS2, + channels = listOf("ch1", "ch2"), + deviceId = "12345", + topic = "news", + ) + } + + override fun verifyResultExpectations(result: PNPushAddChannelResult) { + } + + override fun successfulResponseBody(): String { + return """[1, "Modified Channels"]""" + } + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder() = + get(urlPathEqualTo("/v2/push/sub-key/mySubscribeKey/devices-apns2/12345")) + .withQueryParam("type", absent()) + .withQueryParam("add", equalTo("ch1,ch2")) + .withQueryParam("environment", equalTo("development")) + .withQueryParam("topic", equalTo("news")) + + override fun affectedChannelsAndGroups() = listOf("ch1", "ch2") to emptyList() + + override fun voidResponse() = true +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/list/ListPushProvisionsV1TestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/list/ListPushProvisionsV1TestSuite.kt new file mode 100644 index 000000000..7701e1b1f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/list/ListPushProvisionsV1TestSuite.kt @@ -0,0 +1,63 @@ +package com.pubnub.internal.suite.push.list + +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.push.ListPushProvisions +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.models.consumer.push.PNPushListProvisionsResult +import com.pubnub.api.v2.callbacks.getOrThrow +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse + +class ListPushProvisionsV1TestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNPushNotificationEnabledChannelsOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): ListPushProvisions { + return pubnub.auditPushChannelProvisions( + pushType = PNPushType.FCM, + deviceId = "12345", + ) + } + + override fun verifyResultExpectations(result: PNPushListProvisionsResult) { + assertEquals(2, result.channels.size) + assertEquals("ch1", result.channels[0]) + assertEquals("ch2", result.channels[1]) + } + + override fun successfulResponseBody(): String { + return """["ch1", "ch2"]""" + } + + override fun unsuccessfulResponseBodyList() = + listOf( + """["ch1","ch2",{}]""", + ) + + override fun mappingBuilder() = + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/12345")) + .withQueryParam("type", equalTo("gcm")) + .withQueryParam("environment", absent()) + .withQueryParam("topic", absent()) + + override fun affectedChannelsAndGroups() = emptyList() to emptyList() + + override fun optionalScenarioList(): List> { + return listOf( + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { withBody("[]") } + result = com.pubnub.internal.suite.Result.SUCCESS + additionalChecks = { result -> + assertFalse(result.isFailure) + assertEquals(0, result.getOrThrow().channels.size) + } + }, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/list/ListPushProvisionsV2TestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/list/ListPushProvisionsV2TestSuite.kt new file mode 100644 index 000000000..2425f4374 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/list/ListPushProvisionsV2TestSuite.kt @@ -0,0 +1,64 @@ +package com.pubnub.internal.suite.push.list + +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.push.ListPushProvisions +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.models.consumer.push.PNPushListProvisionsResult +import com.pubnub.api.v2.callbacks.getOrThrow +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse + +class ListPushProvisionsV2TestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNPushNotificationEnabledChannelsOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): ListPushProvisions { + return pubnub.auditPushChannelProvisions( + pushType = PNPushType.APNS2, + deviceId = "12345", + topic = "news", + ) + } + + override fun verifyResultExpectations(result: PNPushListProvisionsResult) { + assertEquals(2, result.channels.size) + assertEquals("ch1", result.channels[0]) + assertEquals("ch2", result.channels[1]) + } + + override fun successfulResponseBody(): String { + return """["ch1", "ch2"]""" + } + + override fun unsuccessfulResponseBodyList() = + listOf( + """["ch1","ch2",{}]""", + ) + + override fun mappingBuilder() = + get(urlPathEqualTo("/v2/push/sub-key/mySubscribeKey/devices-apns2/12345")) + .withQueryParam("type", absent()) + .withQueryParam("environment", equalTo("development")) + .withQueryParam("topic", equalTo("news")) + + override fun affectedChannelsAndGroups() = emptyList() to emptyList() + + override fun optionalScenarioList(): List> { + return listOf( + com.pubnub.internal.suite.OptionalScenario().apply { + responseBuilder = { withBody("[]") } + result = com.pubnub.internal.suite.Result.SUCCESS + additionalChecks = { result -> + assertFalse(result.isFailure) + assertEquals(0, result.getOrThrow().channels.size) + } + }, + ) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveAllFromPushV1TestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveAllFromPushV1TestSuite.kt new file mode 100644 index 000000000..93b6b9b8b --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveAllFromPushV1TestSuite.kt @@ -0,0 +1,43 @@ +package com.pubnub.internal.suite.push.remove + +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.push.RemoveAllPushChannelsForDevice +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.models.consumer.push.PNPushRemoveAllChannelsResult + +class RemoveAllFromPushV1TestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNRemoveAllPushNotificationsOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): RemoveAllPushChannelsForDevice { + return pubnub.removeAllPushNotificationsFromDeviceWithPushToken( + pushType = PNPushType.FCM, + deviceId = "12345", + ) + } + + override fun verifyResultExpectations(result: PNPushRemoveAllChannelsResult) { + } + + override fun successfulResponseBody(): String { + return """[1, "Removed Device"]""" + } + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder() = + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/12345/remove")) + .withQueryParam("type", equalTo("gcm")) + .withQueryParam("environment", absent()) + .withQueryParam("topic", absent()) + + override fun affectedChannelsAndGroups() = emptyList() to emptyList() + + override fun voidResponse() = true +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveAllFromPushV2TestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveAllFromPushV2TestSuite.kt new file mode 100644 index 000000000..b73aad59c --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveAllFromPushV2TestSuite.kt @@ -0,0 +1,44 @@ +package com.pubnub.internal.suite.push.remove + +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.push.RemoveAllPushChannelsForDevice +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.models.consumer.push.PNPushRemoveAllChannelsResult + +class RemoveAllFromPushV2TestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNRemoveAllPushNotificationsOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): RemoveAllPushChannelsForDevice { + return pubnub.removeAllPushNotificationsFromDeviceWithPushToken( + pushType = PNPushType.APNS2, + deviceId = "12345", + topic = "news", + ) + } + + override fun verifyResultExpectations(result: PNPushRemoveAllChannelsResult) { + } + + override fun successfulResponseBody(): String { + return """[1, "Removed Device"]""" + } + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder() = + get(urlPathEqualTo("/v2/push/sub-key/mySubscribeKey/devices-apns2/12345/remove")) + .withQueryParam("type", absent()) + .withQueryParam("environment", equalTo("development")) + .withQueryParam("topic", equalTo("news")) + + override fun affectedChannelsAndGroups() = emptyList() to emptyList() + + override fun voidResponse() = true +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveChannelsFromPushV1TestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveChannelsFromPushV1TestSuite.kt new file mode 100644 index 000000000..ab9eb582e --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveChannelsFromPushV1TestSuite.kt @@ -0,0 +1,45 @@ +package com.pubnub.internal.suite.push.remove + +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.push.RemoveChannelsFromPush +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.models.consumer.push.PNPushRemoveChannelResult + +class RemoveChannelsFromPushV1TestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNRemovePushNotificationsFromChannelsOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): RemoveChannelsFromPush { + return pubnub.removePushNotificationsFromChannels( + pushType = PNPushType.FCM, + channels = listOf("ch1", "ch2"), + deviceId = "12345", + ) + } + + override fun verifyResultExpectations(result: PNPushRemoveChannelResult) { + } + + override fun successfulResponseBody(): String { + return """[1, "Modified Channels"]""" + } + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder() = + get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/12345")) + .withQueryParam("type", equalTo("gcm")) + .withQueryParam("remove", equalTo("ch1,ch2")) + .withQueryParam("environment", absent()) + .withQueryParam("topic", absent()) + + override fun affectedChannelsAndGroups() = listOf("ch1", "ch2") to emptyList() + + override fun voidResponse() = true +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveChannelsFromPushV2TestSuite.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveChannelsFromPushV2TestSuite.kt new file mode 100644 index 000000000..285a9afa9 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/suite/push/remove/RemoveChannelsFromPushV2TestSuite.kt @@ -0,0 +1,46 @@ +package com.pubnub.internal.suite.push.remove + +import com.github.tomakehurst.wiremock.client.WireMock.absent +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.pubnub.api.endpoints.push.RemoveChannelsFromPush +import com.pubnub.api.enums.PNOperationType +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.models.consumer.push.PNPushRemoveChannelResult + +class RemoveChannelsFromPushV2TestSuite : + com.pubnub.internal.suite.CoreEndpointTestSuite() { + override fun pnOperation() = PNOperationType.PNRemovePushNotificationsFromChannelsOperation + + override fun requiredKeys() = com.pubnub.internal.suite.SUB + com.pubnub.internal.suite.AUTH + + override fun snippet(): RemoveChannelsFromPush { + return pubnub.removePushNotificationsFromChannels( + pushType = PNPushType.APNS2, + channels = listOf("ch1", "ch2"), + deviceId = "12345", + topic = "news", + ) + } + + override fun verifyResultExpectations(result: PNPushRemoveChannelResult) { + } + + override fun successfulResponseBody(): String { + return """[1, "Modified Channels"]""" + } + + override fun unsuccessfulResponseBodyList() = emptyList() + + override fun mappingBuilder() = + get(urlPathEqualTo("/v2/push/sub-key/mySubscribeKey/devices-apns2/12345")) + .withQueryParam("type", absent()) + .withQueryParam("remove", equalTo("ch1,ch2")) + .withQueryParam("environment", equalTo("development")) + .withQueryParam("topic", equalTo("news")) + + override fun affectedChannelsAndGroups() = listOf("ch1", "ch2") to emptyList() + + override fun voidResponse() = true +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/utils/PolymorphicDeserializerTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/utils/PolymorphicDeserializerTest.kt new file mode 100644 index 000000000..2e5ce8d0f --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/utils/PolymorphicDeserializerTest.kt @@ -0,0 +1,81 @@ +package com.pubnub.internal.utils + +import com.google.gson.GsonBuilder +import com.google.gson.JsonDeserializer +import com.google.gson.JsonSyntaxException +import com.google.gson.annotations.JsonAdapter +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +class PolymorphicDeserializerTest { + private val gson = GsonBuilder().create() + private val expectedFieldValue = "1" + + @Test + fun deserializationHappyPath() { + val jsonString = """{"f": "$expectedFieldValue", "type": "1"}""" + val result = gson.fromJson(jsonString, I::class.java) + assertEquals(Impl1(expectedFieldValue), result) + } + + @Test + fun deserializeWithExtraFieldsInJson() { + val jsonString = """{"f": "$expectedFieldValue", "type": "2", "extra": 1}""" + val result = gson.fromJson(jsonString, I::class.java) + assertEquals(Impl2(expectedFieldValue), result) + } + + @Test + fun deserializeToDefaultClass() { + val gson = GsonBuilder().registerTypeAdapter(I::class.java, DeserializerWithDefaultClass).create() + val jsonString = """{"f": "$expectedFieldValue", "type": "3"}""" + val result = gson.fromJson(jsonString, I::class.java) + assertEquals(DefaultImpl(expectedFieldValue, "3"), result) + } + + @Test + fun deserializeUnknownTypeWithoutProvidingDefaultClass() { + val jsonString = """{"f": "$expectedFieldValue", "type": "3"}""" + try { + gson.fromJson(jsonString, I::class.java) + } catch (e: Exception) { + assertTrue(e is JsonSyntaxException) + } + } +} + +object DeserializerWithoutDefaultClass : JsonDeserializer by PolymorphicDeserializer.dispatchByFieldsValues( + fields = listOf("type"), + mappingFieldValuesToClass = + mapOf( + listOf("1") to Impl1::class.java, + listOf("2") to Impl2::class.java, + ), +) + +object DeserializerWithDefaultClass : JsonDeserializer by PolymorphicDeserializer.dispatchByFieldsValues( + fields = listOf("type"), + mappingFieldValuesToClass = + mapOf( + listOf("1") to Impl1::class.java, + listOf("2") to Impl2::class.java, + ), + defaultClass = DefaultImpl::class.java, +) + +@JsonAdapter(DeserializerWithoutDefaultClass::class) +interface I { + val type: String + val f: String +} + +data class Impl1(override val f: String) : I { + override val type: String = "1" +} + +data class Impl2(override val f: String) : I { + override val type: String = "2" +} + +data class DefaultImpl(override val f: String, override val type: String = "N/A") : I diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/utils/UnwrapSingleFieldTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/utils/UnwrapSingleFieldTest.kt new file mode 100644 index 000000000..ee7045aa3 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/utils/UnwrapSingleFieldTest.kt @@ -0,0 +1,58 @@ +package com.pubnub.internal.utils + +import com.google.gson.GsonBuilder +import com.google.gson.JsonSyntaxException +import com.google.gson.annotations.JsonAdapter +import org.junit.Assert +import org.junit.Assert.assertEquals +import org.junit.Test + +internal class UnwrapSingleFieldTest { + val gson = GsonBuilder().create() + + @Test + fun unwrapString() { + val expectedValue = "The things you own end up owning you." + val jsonString = """{"f": {"doesn't matter": "$expectedValue"}}""" + val result = gson.fromJson(jsonString, ClassWithString::class.java) + assertEquals(ClassWithString(expectedValue), result) + } + + @Test + fun unwrapInteger() { + val expectedValue = 42 + val jsonString = """{"f": {"doesn't matter": $expectedValue}}""" + val result = gson.fromJson(jsonString, ClassWithNumber::class.java) + assertEquals(ClassWithNumber(expectedValue), result) + } + + @Test + fun unwrapBoolean() { + val expectedValue = false + val jsonString = """{"f": {"doesn't matter": $expectedValue}}""" + val result = gson.fromJson(jsonString, ClassWithBoolean::class.java) + assertEquals(ClassWithBoolean(expectedValue), result) + } + + @Test + fun unwrappingFailsWhenMoreFieldsArePresent() { + val jsonString = """{"f": {"doesn't matter": "whatIPutHere", "oneTooMany": 17}}""" + try { + gson.fromJson(jsonString, ClassWithString::class.java) + } catch (e: Exception) { + Assert.assertTrue(e is JsonSyntaxException) + } + } +} + +data class ClassWithString( + @JsonAdapter(UnwrapSingleField::class) val f: String, +) + +data class ClassWithNumber( + @JsonAdapter(UnwrapSingleField::class) val f: Int, +) + +data class ClassWithBoolean( + @JsonAdapter(UnwrapSingleField::class) val f: Boolean, +) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/PNConfigurationImplTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/PNConfigurationImplTest.kt index c8f9ffff0..857ddd20e 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/PNConfigurationImplTest.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/PNConfigurationImplTest.kt @@ -1,12 +1,14 @@ package com.pubnub.internal.v2 +import com.pubnub.api.PubNubException import com.pubnub.api.UserId import com.pubnub.api.crypto.CryptoModule import com.pubnub.api.enums.PNHeartbeatNotificationOptions import com.pubnub.api.enums.PNLogVerbosity import com.pubnub.api.retry.RetryConfiguration import com.pubnub.api.v2.PNConfiguration -import com.pubnub.internal.BasePubNubImpl +import com.pubnub.api.v2.PNConfigurationOverride +import com.pubnub.internal.PubNubImpl import io.mockk.mockk import okhttp3.Authenticator import okhttp3.CertificatePinner @@ -22,16 +24,46 @@ import java.net.Proxy import javax.net.ssl.X509ExtendedTrustManager class PNConfigurationImplTest { + @Test(expected = PubNubException::class) + fun setUUIDToEmptyString() { + PNConfiguration.builder(UserId(""), "") + } + + @Test(expected = PubNubException::class) + fun resetUUIDToEmptyString() { + val config = PNConfiguration.builder(UserId(PubNubImpl.generateUUID()), "") + config.userId = UserId("") + } + + @Test + fun resetUUIDToNonEmptyString() { + val config = PNConfiguration.builder(UserId(PubNubImpl.generateUUID()), "") + val newUUID = PubNubImpl.generateUUID() + config.userId = UserId(newUUID) + + assertEquals(newUUID, config.userId.value) + } + + @Test + fun `create config override from existing config`() { + val config = PNConfiguration.builder(UserId(PubNubImpl.generateUUID()), "expectedSubscribe").build() + val override = PNConfigurationOverride.from(config).apply { + userId = UserId("override") + }.build() + assertEquals("override", override.userId.value) + assertEquals("expectedSubscribe", override.subscribeKey) + } + @Test fun testDefaultTimeoutValues() { - val config = PNConfiguration.builder(UserId(BasePubNubImpl.generateUUID()), "demo") + val config = PNConfiguration.builder(UserId(PubNubImpl.generateUUID()), "demo") assertEquals(300, config.presenceTimeout) assertEquals(0, config.heartbeatInterval) } @Test fun testCustomTimeoutValues1() { - val config = PNConfiguration.builder(UserId(BasePubNubImpl.generateUUID()), "demo") + val config = PNConfiguration.builder(UserId(PubNubImpl.generateUUID()), "demo") config.presenceTimeout = 100 assertEquals(100, config.presenceTimeout) assertEquals(49, config.heartbeatInterval) @@ -39,7 +71,7 @@ class PNConfigurationImplTest { @Test fun testCustomTimeoutValues2() { - val config = PNConfiguration.builder(UserId(BasePubNubImpl.generateUUID()), "demo") + val config = PNConfiguration.builder(UserId(PubNubImpl.generateUUID()), "demo") config.heartbeatInterval = 100 assertEquals(300, config.presenceTimeout) assertEquals(100, config.heartbeatInterval) @@ -47,7 +79,7 @@ class PNConfigurationImplTest { @Test fun testCustomTimeoutValues3() { - val config = PNConfiguration.builder(UserId(BasePubNubImpl.generateUUID()), "demo") + val config = PNConfiguration.builder(UserId(PubNubImpl.generateUUID()), "demo") config.heartbeatInterval = 40 config.presenceTimeout = 50 assertEquals(50, config.presenceTimeout) @@ -56,7 +88,7 @@ class PNConfigurationImplTest { @Test fun `build uses all values from Builder`() { - val expectedUserId = UserId(BasePubNubImpl.generateUUID()) + val expectedUserId = UserId(PubNubImpl.generateUUID()) val expectedCryptoModule = CryptoModule.createAesCbcCryptoModule("cipher") val expectedProxy = Proxy(Proxy.Type.HTTP, InetSocketAddress(80)) val expectedProxySelector = DefaultProxySelector() @@ -150,10 +182,10 @@ class PNConfigurationImplTest { } @Test - fun `builder has all default values from BasePNConfiguration`() { - val expectedUserId = UserId(BasePubNubImpl.generateUUID()) + fun `builder has all default values from PNConfiguration`() { + val expectedUserId = UserId(PubNubImpl.generateUUID()) val builder = PNConfiguration.builder(expectedUserId, "subKey") - val expectedDefaults = BasePNConfigurationImpl(expectedUserId) + val expectedDefaults = PNConfigurationImpl(expectedUserId) assertEquals(expectedUserId, builder.userId) assertEquals("subKey", builder.subscribeKey) @@ -197,7 +229,7 @@ class PNConfigurationImplTest { @Test fun `can reset userId and subscribeKey`() { - val expectedUserId = UserId(BasePubNubImpl.generateUUID()) + val expectedUserId = UserId(PubNubImpl.generateUUID()) val expectedSubKey = "expectedSubKey" val config = PNConfiguration.builder(UserId("aaa"), "subKey") { diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingEventListenerTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingEventListenerTest.kt deleted file mode 100644 index 3810b49b3..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingEventListenerTest.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.pubnub.internal.v2.callbacks - -import com.pubnub.api.v2.callbacks.EventListener -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test - -internal class DelegatingEventListenerTest { - @Test - fun testEquals() { - val eventListener = object : EventListener {} - val otherEventListener = object : EventListener {} - val delegating1 = DelegatingEventListener(eventListener) - val delegating2 = DelegatingEventListener(eventListener) - val otherDelegating = DelegatingEventListener(otherEventListener) - - Assertions.assertEquals(delegating1, delegating2) - Assertions.assertEquals(delegating2, delegating1) - Assertions.assertNotEquals(delegating1, otherDelegating) - Assertions.assertNotEquals(delegating2, otherDelegating) - Assertions.assertNotEquals(otherDelegating, delegating1) - Assertions.assertNotEquals(otherDelegating, delegating2) - } -} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingStatusListenerTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingStatusListenerTest.kt deleted file mode 100644 index dc5006a48..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingStatusListenerTest.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.pubnub.internal.v2.callbacks - -import com.pubnub.api.PubNub -import com.pubnub.api.models.consumer.PNStatus -import com.pubnub.api.v2.callbacks.StatusListener -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test - -internal class DelegatingStatusListenerTest { - @Test - fun testEquals() { - val statusListener = - object : StatusListener { - override fun status( - pubnub: PubNub, - status: PNStatus, - ) {} - } - val otherStatusListener = - object : StatusListener { - override fun status( - pubnub: PubNub, - status: PNStatus, - ) {} - } - val delegating1 = DelegatingStatusListener(statusListener) - val delegating2 = DelegatingStatusListener(statusListener) - val otherDelegating = DelegatingStatusListener(otherStatusListener) - - Assertions.assertEquals(delegating1, delegating2) - Assertions.assertEquals(delegating2, delegating1) - Assertions.assertNotEquals(delegating1, otherDelegating) - Assertions.assertNotEquals(delegating2, otherDelegating) - Assertions.assertNotEquals(otherDelegating, delegating1) - Assertions.assertNotEquals(otherDelegating, delegating2) - } -} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallbackTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallbackTest.kt deleted file mode 100644 index c0f0923cf..000000000 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/DelegatingSubscribeCallbackTest.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.pubnub.internal.v2.callbacks - -import com.pubnub.api.PubNub -import com.pubnub.api.callbacks.SubscribeCallback -import com.pubnub.api.models.consumer.PNStatus -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test - -internal class DelegatingSubscribeCallbackTest { - @Test - fun testEquals() { - val statusListener = - object : SubscribeCallback() { - override fun status( - pubnub: PubNub, - status: PNStatus, - ) {} - } - val otherStatusListener = - object : SubscribeCallback() { - override fun status( - pubnub: PubNub, - status: PNStatus, - ) {} - } - - val delegating1 = DelegatingSubscribeCallback(statusListener) - val delegating2 = DelegatingSubscribeCallback(statusListener) - val otherDelegating = DelegatingSubscribeCallback(otherStatusListener) - - Assertions.assertEquals(delegating1, delegating2) - Assertions.assertEquals(delegating2, delegating1) - Assertions.assertNotEquals(delegating1, otherDelegating) - Assertions.assertNotEquals(delegating2, otherDelegating) - Assertions.assertNotEquals(otherDelegating, delegating1) - Assertions.assertNotEquals(otherDelegating, delegating2) - } -} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImplTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImplTest.kt new file mode 100644 index 000000000..965bb1258 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/callbacks/EventEmitterImplTest.kt @@ -0,0 +1,346 @@ +package com.pubnub.internal.v2.callbacks + +import com.google.gson.JsonPrimitive +import com.pubnub.api.PubNub +import com.pubnub.api.UserId +import com.pubnub.api.models.consumer.files.PNDownloadableFile +import com.pubnub.api.models.consumer.message_actions.PNMessageAction +import com.pubnub.api.models.consumer.pubsub.BasePubSubResult +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult +import com.pubnub.api.models.consumer.pubsub.PNSignalResult +import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult +import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult +import com.pubnub.api.models.consumer.pubsub.objects.PNDeleteUUIDMetadataEventMessage +import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult +import com.pubnub.api.v2.callbacks.EventListener +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.managers.AnnouncementCallback +import com.pubnub.internal.managers.AnnouncementEnvelope +import com.pubnub.internal.v2.PNConfigurationImpl +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class EventEmitterImplTest { + lateinit var emitterImpl: EventEmitterImpl + val testPubNub = PubNubImpl(PNConfigurationImpl(UserId("aa"))) + val basePubSubResult = BasePubSubResult("a", null, null, null, null) + val message = JsonPrimitive(1) + + @BeforeEach + fun setUp() { + emitterImpl = EventEmitterImpl(AnnouncementCallback.Phase.SUBSCRIPTION) + } + + @Test + fun `can add listener`() { + val listener = object : EventListener { } + + emitterImpl.addListener(listener) + + assertTrue(emitterImpl.listeners.contains(listener)) + } + + @Test + fun `can remove listener`() { + val listener = object : EventListener { } + emitterImpl.addListener(listener) + assertTrue(emitterImpl.listeners.contains(listener)) + + emitterImpl.removeListener(listener) + + assertFalse(emitterImpl.listeners.contains(listener)) + } + + @Test + fun `can remove all listeners`() { + emitterImpl.addListener(object : EventListener { }) + emitterImpl.addListener(object : EventListener { }) + emitterImpl.addListener(object : EventListener { }) + assertTrue(emitterImpl.listeners.size == 3) + + emitterImpl.removeAllListeners() + + assertTrue(emitterImpl.listeners.isEmpty()) + } + + @Test + fun `message is delivered`() { + var success = false + emitterImpl.addListener( + object : EventListener { + override fun message( + pubnub: PubNub, + event: PNMessageResult, + ) { + success = true + } + }, + ) + emitterImpl.message(testPubNub, PNMessageResult(basePubSubResult, message)) + assertTrue(success) + } + + @Test + fun `message announcement is delivered`() { + var success = false + emitterImpl.addListener( + object : EventListener { + override fun message( + pubnub: PubNub, + event: PNMessageResult, + ) { + success = true + } + }, + ) + emitterImpl.message(testPubNub, AnnouncementEnvelope(PNMessageResult(basePubSubResult, message))) + assertTrue(success) + } + + @Test + fun `presence event is delivered`() { + var success = false + emitterImpl.addListener( + object : EventListener { + override fun presence( + pubnub: PubNub, + event: PNPresenceEventResult, + ) { + success = true + } + }, + ) + + emitterImpl.presence(testPubNub, PNPresenceEventResult(channel = "a")) + + assertTrue(success) + } + + @Test + fun `presence announcement is delivered`() { + var success = false + emitterImpl.addListener( + object : EventListener { + override fun presence( + pubnub: PubNub, + event: PNPresenceEventResult, + ) { + success = true + } + }, + ) + + emitterImpl.presence(testPubNub, AnnouncementEnvelope(PNPresenceEventResult(channel = "a"))) + + assertTrue(success) + } + + @Test + fun `signal is delivered`() { + var success = false + emitterImpl.addListener( + object : EventListener { + override fun signal( + pubnub: PubNub, + event: PNSignalResult, + ) { + success = true + } + }, + ) + + emitterImpl.signal(testPubNub, PNSignalResult(basePubSubResult, message)) + + assertTrue(success) + } + + @Test + fun `signal announcement is delivered`() { + var success = false + emitterImpl.addListener( + object : EventListener { + override fun signal( + pubnub: PubNub, + event: PNSignalResult, + ) { + success = true + } + }, + ) + + emitterImpl.signal(testPubNub, AnnouncementEnvelope(PNSignalResult(basePubSubResult, message))) + + assertTrue(success) + } + + @Test + fun `messageAction is delivered`() { + var success = false + emitterImpl.addListener( + object : EventListener { + override fun messageAction( + pubnub: PubNub, + event: PNMessageActionResult, + ) { + success = true + } + }, + ) + + emitterImpl.messageAction(testPubNub, PNMessageActionResult(basePubSubResult, "a", PNMessageAction())) + + assertTrue(success) + } + + @Test + fun `messageAction announcement is delivered`() { + var success = false + emitterImpl.addListener( + object : EventListener { + override fun messageAction( + pubnub: PubNub, + event: PNMessageActionResult, + ) { + success = true + } + }, + ) + + emitterImpl.messageAction(testPubNub, AnnouncementEnvelope(PNMessageActionResult(basePubSubResult, "a", PNMessageAction()))) + + assertTrue(success) + } + + @Test + fun `object is delivered`() { + var success = false + emitterImpl.addListener( + object : EventListener { + override fun objects( + pubnub: PubNub, + event: PNObjectEventResult, + ) { + success = true + } + }, + ) + + emitterImpl.objects(testPubNub, PNObjectEventResult(basePubSubResult, PNDeleteUUIDMetadataEventMessage("a", "b", "c", "d", "e"))) + + assertTrue(success) + } + + @Test + fun `objects announcement is delivered`() { + var success = false + emitterImpl.addListener( + object : EventListener { + override fun objects( + pubnub: PubNub, + event: PNObjectEventResult, + ) { + success = true + } + }, + ) + + emitterImpl.objects( + testPubNub, + AnnouncementEnvelope(PNObjectEventResult(basePubSubResult, PNDeleteUUIDMetadataEventMessage("a", "b", "c", "d", "e"))), + ) + + assertTrue(success) + } + + @Test + fun `file is delivered`() { + var success = false + emitterImpl.addListener( + object : EventListener { + override fun file( + pubnub: PubNub, + event: PNFileEventResult, + ) { + success = true + } + }, + ) + + emitterImpl.file(testPubNub, PNFileEventResult("a", null, null, null, PNDownloadableFile("a", "b", "c"), message)) + + assertTrue(success) + } + + @Test + fun `file announcement is delivered`() { + var success = false + emitterImpl.addListener( + object : EventListener { + override fun file( + pubnub: PubNub, + event: PNFileEventResult, + ) { + success = true + } + }, + ) + + emitterImpl.file( + testPubNub, + AnnouncementEnvelope(PNFileEventResult("a", null, null, null, PNDownloadableFile("a", "b", "c"), message)), + ) + + assertTrue(success) + } + + @Test + fun `when accepts is true then announce message`() { + var success = false + emitterImpl = + EventEmitterImpl(AnnouncementCallback.Phase.SUBSCRIPTION) { envelope -> + envelope.event.channel == "acceptedChannel" + } + emitterImpl.addListener( + object : EventListener { + override fun message( + pubnub: PubNub, + event: PNMessageResult, + ) { + success = true + } + }, + ) + emitterImpl.message( + testPubNub, + AnnouncementEnvelope(PNMessageResult(BasePubSubResult("acceptedChannel", null, null, null, null), message)), + ) + assertTrue(success) + } + + @Test + fun `when accepts is false then don't announce message`() { + var success = false + emitterImpl = + EventEmitterImpl(AnnouncementCallback.Phase.SUBSCRIPTION) { envelope -> + envelope.event.channel == "acceptedChannel" + } + emitterImpl.addListener( + object : EventListener { + override fun message( + pubnub: PubNub, + event: PNMessageResult, + ) { + success = true + } + }, + ) + emitterImpl.message( + testPubNub, + AnnouncementEnvelope(PNMessageResult(BasePubSubResult("anotherChannel", null, null, null, null), message)), + ) + assertFalse(success) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelGroupImplTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelGroupImplTest.kt new file mode 100644 index 000000000..c186572a1 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelGroupImplTest.kt @@ -0,0 +1,59 @@ +package com.pubnub.internal.v2.entities + +import com.pubnub.api.UserId +import com.pubnub.api.v2.subscriptions.SubscriptionOptions +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.v2.PNConfigurationImpl +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class BaseChannelGroupImplTest { + private lateinit var pn: PubNubImpl + private lateinit var channelGrp: ChannelGroupImpl + private val channelGroupname = "myChannelGroup" + + @BeforeEach + fun setUp() { + pn = PubNubImpl(PNConfigurationImpl(UserId("uuid"))) + channelGrp = + ChannelGroupImpl(pn, ChannelGroupName(channelGroupname)) + } + + @AfterEach + fun teardown() { + pn.destroy() + } + + @Test + fun `create subscription`() { + val subscription = channelGrp.subscription() + + Assertions.assertEquals(setOf(ChannelGroupName(channelGroupname)), subscription.channelGroups) + Assertions.assertTrue(subscription.channels.isEmpty()) + } + + @Test + fun `create subscription with presence`() { + val subscriptions = channelGrp.subscription(SubscriptionOptions.receivePresenceEvents()) + + Assertions.assertEquals( + setOf( + ChannelGroupName(channelGroupname), + ChannelGroupName(channelGroupname).withPresence, + ), + subscriptions.channelGroups, + ) + Assertions.assertTrue(subscriptions.channels.isEmpty()) + } + + @Test + fun `create ChannelGroupName-pnpres from ChannelGroupName`() { + val name = ChannelGroupName(channelGroupname) + + val nameWithPresence = name.withPresence + + Assertions.assertEquals(ChannelGroupName("$channelGroupname-pnpres"), nameWithPresence) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelImplTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelImplTest.kt new file mode 100644 index 000000000..d2caa6290 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/entities/BaseChannelImplTest.kt @@ -0,0 +1,58 @@ +package com.pubnub.internal.v2.entities + +import com.pubnub.api.UserId +import com.pubnub.api.v2.subscriptions.SubscriptionOptions +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.v2.PNConfigurationImpl +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class BaseChannelImplTest { + private lateinit var pn: PubNubImpl + private lateinit var channel: ChannelImpl + private val channelName = "myChannel" + + @BeforeEach + fun setUp() { + pn = PubNubImpl(PNConfigurationImpl(UserId("uuid"))) + channel = ChannelImpl(pn, ChannelName(channelName)) + } + + @AfterEach + fun teardown() { + pn.destroy() + } + + @Test + fun `create subscription`() { + val subscription = channel.subscription() + + Assertions.assertEquals(setOf(ChannelName(channelName)), subscription.channels) + Assertions.assertTrue(subscription.channelGroups.isEmpty()) + } + + @Test + fun `create subscription with presence`() { + val subscriptions = channel.subscription(SubscriptionOptions.receivePresenceEvents()) + + Assertions.assertEquals( + setOf( + ChannelName(channelName), + ChannelName(channelName).withPresence, + ), + subscriptions.channels, + ) + Assertions.assertTrue(subscriptions.channelGroups.isEmpty()) + } + + @Test + fun `create ChannelName-pnpres from ChannelName`() { + val name = ChannelName(channelName) + + val nameWithPresence = name.withPresence + + Assertions.assertEquals(ChannelName("$channelName-pnpres"), nameWithPresence) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionImplTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionImplTest.kt new file mode 100644 index 000000000..6015d88d2 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionImplTest.kt @@ -0,0 +1,135 @@ +package com.pubnub.internal.v2.subscriptions + +import com.google.gson.JsonNull +import com.pubnub.api.PubNub +import com.pubnub.api.UserId +import com.pubnub.api.models.consumer.pubsub.BasePubSubResult +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.v2.callbacks.EventListener +import com.pubnub.api.v2.subscriptions.SubscriptionOptions +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.v2.PNConfigurationImpl +import com.pubnub.internal.v2.entities.ChannelName +import com.pubnub.internal.v2.subscription.SubscriptionImpl +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class BaseSubscriptionImplTest { + private lateinit var pubnub: PubNubImpl + private lateinit var subscription: SubscriptionImpl + private val channelName = "myChannel" + + @BeforeEach + fun setUp() { + pubnub = PubNubImpl(PNConfigurationImpl(UserId("uuid"))) + subscription = SubscriptionImpl(pubnub, setOf(ChannelName(channelName))) + } + + @AfterEach + fun teardown() { + subscription.close() + pubnub.destroy() + } + + @Test + fun `subscribe adds channel to mix`() { + // when + subscription.subscribe() + + // then + assertEquals(listOf(channelName), pubnub.getSubscribedChannels()) + assertEquals(emptyList(), pubnub.getSubscribedChannelGroups()) + } + + @Test + fun `unsubscribe removes channel from mix`() { + // given + subscription.subscribe() + + // when + subscription.unsubscribe() + + // then + assertEquals(emptyList(), pubnub.getSubscribedChannels()) + assertEquals(emptyList(), pubnub.getSubscribedChannelGroups()) + } + + @Test + fun `close stops the subscription`() { + // given + subscription.subscribe() + subscription.addListener(object : EventListener { + override fun message(pubnub: PubNub, result: PNMessageResult) { + throw IllegalStateException("We should not get a message after close!") + } + }) + + // when + subscription.close() + + pubnub.listenerManager.announce( + PNMessageResult( + BasePubSubResult(channelName, null, null, null, null), + JsonNull.INSTANCE, + ), + ) + + // then + // no exception from listener + assertEquals(emptyList(), pubnub.getSubscribedChannels()) + assertEquals(emptyList(), pubnub.getSubscribedChannelGroups()) + } + + @Test + fun `subscriptions with filter doesn't deliver filtered messages`() { + // given + val subWithFilter = + SubscriptionImpl( + pubnub, + setOf( + ChannelName(channelName), + ), + options = + SubscriptionOptions.filter { + it !is PNMessageResult + }, + ) + + // when + subWithFilter.subscribe() + pubnub.listenerManager.announce( + PNMessageResult( + BasePubSubResult(channelName, null, null, null, null), + JsonNull.INSTANCE, + ), + ) + + // then + // no exception + } + + @Test + fun `subscription doesn't get events until subscribe is called`() { + // given + subscription.addListener( + object : EventListener { + override fun message(pubnub: PubNub, result: PNMessageResult) { + throw IllegalStateException("Message should not be received without subscribe call!") + } + } + ) + + // when + pubnub.listenerManager.announce( + PNMessageResult( + BasePubSubResult(channelName, null, 1000L, null, null), + JsonNull.INSTANCE, + ), + ) + + // then + // no exception + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionSetImplTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionSetImplTest.kt new file mode 100644 index 000000000..734f800ab --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/BaseSubscriptionSetImplTest.kt @@ -0,0 +1,121 @@ +package com.pubnub.internal.v2.subscriptions + +import com.google.gson.JsonNull +import com.pubnub.api.PubNub +import com.pubnub.api.UserId +import com.pubnub.api.models.consumer.pubsub.BasePubSubResult +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.v2.callbacks.EventListener +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.v2.PNConfigurationImpl +import com.pubnub.internal.v2.entities.ChannelName +import com.pubnub.internal.v2.subscription.SubscriptionImpl +import com.pubnub.internal.v2.subscription.SubscriptionSetImpl +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class BaseSubscriptionSetImplTest { + private lateinit var pubnub: PubNubImpl + private lateinit var subscriptionSet: SubscriptionSetImpl + private lateinit var anotherSubscription: SubscriptionImpl + private val channelName = "myChannel" + + @BeforeEach + fun setUp() { + pubnub = PubNubImpl(PNConfigurationImpl(UserId("uuid"))) + subscriptionSet = SubscriptionSetImpl(pubnub, setOf()) + + subscriptionSet.add( + SubscriptionImpl(pubnub, setOf(ChannelName(channelName))) + ) + + anotherSubscription = SubscriptionImpl(pubnub, setOf(ChannelName("anotherChannel"))) + } + + @AfterEach + fun teardown() { + subscriptionSet.close() + anotherSubscription.close() + pubnub.destroy() + } + + @Test + fun add() { + // given + + // when + subscriptionSet.add(anotherSubscription) + subscriptionSet.subscribe() + + // then + assertTrue(subscriptionSet.subscriptions.contains(anotherSubscription)) + assertEquals(setOf(channelName, "anotherChannel"), pubnub.getSubscribedChannels().toSet()) + } + + @Test + fun remove() { + // given + subscriptionSet.add(anotherSubscription) + + // when + subscriptionSet.remove(anotherSubscription) + subscriptionSet.subscribe() + + // then + assertFalse(subscriptionSet.subscriptions.contains(anotherSubscription)) + assertEquals(setOf(channelName), pubnub.getSubscribedChannels().toSet()) + } + + @Test + fun subscribe() { + // when + subscriptionSet.subscribe() + + // then + assertEquals(setOf(channelName), pubnub.getSubscribedChannels().toSet()) + } + + @Test + fun unsubscribe() { + // given + subscriptionSet.subscribe() + + // when + subscriptionSet.unsubscribe() + + // then + assertEquals(emptyList(), pubnub.getSubscribedChannels()) + } + + @Test + fun close() { + // given + subscriptionSet.subscribe() + subscriptionSet.addListener( + object : EventListener { + override fun message(pubnub: PubNub, result: PNMessageResult) { + throw IllegalStateException("We should not get a message after close!") + } + }, + ) + + // when + subscriptionSet.close() + + pubnub.listenerManager.announce( + PNMessageResult( + BasePubSubResult(channelName, null, null, null, null), + JsonNull.INSTANCE, + ), + ) + + // then + // no exception from listener + assertEquals(emptyList(), pubnub.getSubscribedChannels()) + assertEquals(emptyList(), pubnub.getSubscribedChannelGroups()) + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/SubscriptionSetImplTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/SubscriptionSetImplTest.kt index 3b6cdba68..6b54812a6 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/SubscriptionSetImplTest.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/v2/subscription/SubscriptionSetImplTest.kt @@ -2,7 +2,6 @@ package com.pubnub.internal.v2.subscription import com.pubnub.api.v2.subscriptions.EmptyOptions import com.pubnub.api.v2.subscriptions.SubscriptionOptions -import com.pubnub.internal.PubNubCore import com.pubnub.internal.PubNubImpl import com.pubnub.internal.managers.ListenerManager import com.pubnub.internal.v2.entities.ChannelGroupName @@ -17,7 +16,6 @@ import org.junit.jupiter.api.Test class SubscriptionSetImplTest { private lateinit var objectUnderTest: SubscriptionSetImpl - private val pubNubCore: PubNubCore = mockk() private val pubNubImpl: PubNubImpl = mockk() private val channel = setOf(ChannelName("Channel2")) @@ -26,10 +24,9 @@ class SubscriptionSetImplTest { @BeforeEach fun setUp() { val listenerManager = ListenerManager(pubNubImpl) - every { pubNubCore.listenerManager } returns listenerManager - every { pubNubImpl.pubNubCore } returns pubNubCore + every { pubNubImpl.listenerManager } returns listenerManager - objectUnderTest = SubscriptionSetImpl(pubNubCore, emptySet()) + objectUnderTest = SubscriptionSetImpl(pubNubImpl, emptySet()) } @Test diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessorTest.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessorTest.kt new file mode 100644 index 000000000..992c9a6a7 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/internal/workers/SubscribeMessageProcessorTest.kt @@ -0,0 +1,200 @@ +package com.pubnub.internal.workers + +import com.google.gson.Gson +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonNull +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.UserId +import com.pubnub.api.crypto.CryptoModule +import com.pubnub.api.models.consumer.pubsub.PNMessageResult +import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult +import com.pubnub.api.v2.PNConfiguration +import com.pubnub.internal.PubNubImpl +import com.pubnub.internal.crypto.encryptString +import com.pubnub.internal.managers.DuplicationManager +import com.pubnub.internal.models.server.SubscribeMessage +import com.pubnub.internal.v2.PNConfigurationImpl +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.isA +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.hamcrest.core.Is.`is` as iz + +@RunWith(Parameterized::class) +class SubscribeMessageProcessorTest( + private val messageJson: JsonElement, +) { + companion object { + @JvmStatic + @Parameterized.Parameters + fun data(): Collection { + return listOf( + JsonPrimitive("thisIsMessage"), + JsonObject().apply { add("test", JsonPrimitive("value")) }, + JsonArray().apply { + add(JsonPrimitive("array")) + add(JsonPrimitive("of")) + add(JsonPrimitive("elements")) + }, + JsonPrimitive(true), + JsonPrimitive(1337), + JsonNull.INSTANCE, + JsonObject().apply { + add( + "with", + JsonObject().apply { + add( + "array", + JsonArray().apply { + add(JsonPrimitive("array")) + add(JsonPrimitive("of")) + add(JsonPrimitive("elements")) + }, + ) + }, + ) + }, + ) + } + } + + @Test + fun testDifferentJsonMessages() { + val gson = Gson() + val configuration = config() + + val messageProcessor = messageProcessor(configuration) + + val result = + messageProcessor.processIncomingPayload( + gson.fromJson( + fileMessage(messageJson.toString()), + SubscribeMessage::class.java, + ), + ) + + assertThat(result, isA(PNFileEventResult::class.java)) + assertThat((result as PNFileEventResult).jsonMessage, iz(messageJson)) + } + + @Test + fun testProcessFileUnencryptedWithCrypto() { + val gson = Gson() + val configuration = config { cryptoModule = CryptoModule.createAesCbcCryptoModule("enigma", false) } + + val messageProcessor = messageProcessor(configuration) + + val result = + messageProcessor.processIncomingPayload( + gson.fromJson( + fileMessage(messageJson.toString()), + SubscribeMessage::class.java, + ), + ) + + assertThat(result, isA(PNFileEventResult::class.java)) + assertThat((result as PNFileEventResult).jsonMessage, iz(messageJson)) + assertThat( + (result as PNFileEventResult).error, + iz(PubNubError.CRYPTO_IS_CONFIGURED_BUT_MESSAGE_IS_NOT_ENCRYPTED), + ) + } + + @Test + fun testProcessMessageEncryptedWithCrypto() { + // given + val gson = Gson() + val config: PNConfiguration = config { cryptoModule = CryptoModule.createAesCbcCryptoModule("enigma", false) } + val subscribeMessageProcessor = messageProcessor(config) + val messageEncrypted = config.cryptoModule!!.encryptString(messageJson.toString()) + + // when + val result = + subscribeMessageProcessor.processIncomingPayload( + gson.fromJson( + message(JsonPrimitive(messageEncrypted)), + SubscribeMessage::class.java, + ), + ) + + // then + assertThat(result, isA(PNMessageResult::class.java)) + assertThat((result as PNMessageResult).message, iz(messageJson)) + } + + @Test + @Throws(PubNubException::class) + fun testProcessMessageUnencryptedWithCrypto() { + // given + val gson = Gson() + val config: PNConfiguration = config { cryptoModule = CryptoModule.createAesCbcCryptoModule("enigma", false) } + + val subscribeMessageProcessor = messageProcessor(config) + + // when + val result = + subscribeMessageProcessor.processIncomingPayload( + gson.fromJson( + message(messageJson), + SubscribeMessage::class.java, + ), + ) + + // then + assertThat(result, isA(PNMessageResult::class.java)) + assertThat((result as PNMessageResult).message, iz(messageJson)) + } + + @Test + fun testProcessMessageWithPnOtherEncryptedWithCrypto() { + // given + val gson = Gson() + val config: PNConfiguration = config { cryptoModule = CryptoModule.createAesCbcCryptoModule("enigma", false) } + + val subscribeMessageProcessor = messageProcessor(config) + val message = "Hello world." + val messageEncrypted = "bk8x+ZEg+Roq8ngUo7lfFg==" + val messageObject = JsonObject() + messageObject.add("something", messageJson) + messageObject.addProperty("pn_other", messageEncrypted) + val expectedObject = JsonObject() + expectedObject.add("something", messageJson) + expectedObject.addProperty("pn_other", message) + + // when + val result = + subscribeMessageProcessor.processIncomingPayload( + gson.fromJson( + message(messageObject), + SubscribeMessage::class.java, + ), + ) + + // then + assertThat(result, isA(PNMessageResult::class.java)) + assertThat((result as PNMessageResult).message, iz(expectedObject)) + } + + private fun config( + action: PNConfigurationImpl.Builder.() -> Unit = { + }, + ) = PNConfigurationImpl.Builder(userId = UserId("test"), "").apply(action).build() + + private fun messageProcessor(configuration: PNConfiguration) = + SubscribeMessageProcessor( + pubnub = PubNubImpl(configuration), + duplicationManager = DuplicationManager(configuration), + ) + + private fun message(messageJson: JsonElement): String { + return "{\"a\":\"2\",\"f\":0,\"i\":\"client-2bdc6006-1b48-45e4-9c09-9cc4c5ac5e8c\",\"s\":1,\"p\":{\"t\":\"17000393136828867\",\"r\":43},\"k\":\"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe\",\"c\":\"ch_cxnysctxlw\",\"d\":$messageJson,\"b\":\"ch_cxnysctxlw\"}" + } + + private fun fileMessage(messageJson: String) = + """{"a":"0","f":0,"e":4,"i":"client-52774e6f-2f4e-4915-aefd-e8bb75cd2e7d","p":{"t":"16632349939765880","r":43},"k":"sub-c-4b1dbfef-2fa9-495f-a316-2b634063083d","c":"ch_1663234993171_F4FC4F460F","u":"This is meta","d":{"message":$messageJson,"file":{"id":"30ce0095-3c50-4cdc-a626-bf402d233731","name":"fileNamech_1663234993171_F4FC4F460F.txt"}}}""" +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/CommonUtils.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/CommonUtils.kt new file mode 100644 index 000000000..b829d50e0 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/CommonUtils.kt @@ -0,0 +1,311 @@ +package com.pubnub.test + +import com.github.tomakehurst.wiremock.client.WireMock +import com.google.gson.Gson +import com.google.gson.JsonObject +import com.google.gson.reflect.TypeToken +import com.pubnub.api.PubNubError +import com.pubnub.api.PubNubException +import com.pubnub.api.models.consumer.PNPublishResult +import com.pubnub.internal.PubNubImpl +import okhttp3.logging.HttpLoggingInterceptor +import org.awaitility.Awaitility +import org.awaitility.Durations +import org.json.JSONObject +import org.junit.Assert.assertEquals +import org.junit.Assert.fail +import org.slf4j.Logger +import java.io.BufferedReader +import java.io.InputStreamReader +import java.util.Locale +import java.util.UUID +import java.util.concurrent.Callable +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean +import java.util.stream.Collectors + +object CommonUtils { + var defaultListenDuration = 5 + + internal fun observe( + success: AtomicBoolean, + seconds: Int, + ) { + Awaitility.await() + .atMost(seconds.toLong(), TimeUnit.SECONDS) + .untilTrue(success) + } + + fun assertPnException( + expectedPubNubError: PubNubError, + exception: Throwable?, + ) { + exception as PubNubException + assertEquals(expectedPubNubError, exception.pubnubError) + } + + fun emptyJson() = WireMock.aResponse().withBody("{}") + + fun failTest(message: String? = null) { + fail(message) + } + + fun getSpecialCharsMap(): List { + val resourceFileAsString = getResourceFileAsString("special_chars.json") + return Gson().fromJson( + resourceFileAsString, + object : TypeToken>() {}.type, + ) + } + + fun getResourceFileAsString(fileName: String?): String? { + val classLoader = ClassLoader.getSystemClassLoader() + classLoader.getResourceAsStream(fileName).use { inputStream -> + if (inputStream == null) { + return null + } + InputStreamReader(inputStream).use { isr -> + BufferedReader(isr).use { reader -> + return reader.lines().collect(Collectors.joining(System.lineSeparator())) + } + } + } + } + + class SpecialChar( + val name: String, + val regular: String, + val encoded: String, + ) + + fun retry(function: () -> Unit) { + val success = AtomicBoolean() + val block = { + try { + val executor = Executors.newSingleThreadExecutor() + val submit = + executor.submit( + Callable { + try { + function.invoke() + true + } catch (t: Throwable) { + false + } + }, + ) + success.set(submit.get()) + } catch (t: Throwable) { + success.set(false) + } + } + + Awaitility.await() + .atMost(defaultListenDuration.toLong(), TimeUnit.SECONDS) + .pollInterval(Durations.FIVE_HUNDRED_MILLISECONDS) + .until { + block.invoke() + success.get() + } + } + + fun randomValue(length: Int = 10): String { + return UUID.randomUUID().toString() + .replace("-", "") + .take(length) + .uppercase(Locale.getDefault()) + } + + fun randomChannel(): String { + return "ch_${System.currentTimeMillis()}_${randomValue()}" + } + + fun randomNumeric(length: Int = 10): String { + return generateSequence { (0..9).random() }.take(length).toList().shuffled() + .joinToString(separator = "") + } + + fun publishMixed( + pubnub: PubNubImpl, + count: Int, + channel: String, + ): List { + val list = mutableListOf() + repeat(count) { + val sync = + pubnub.publish( + channel = channel, + message = "${it}_msg", + meta = + when { + it % 2 == 0 -> generateMap() + it % 3 == 0 -> randomValue(4) + else -> null + }, + ).sync() + list.add(sync) + } + return list + } + + fun generatePayload(): JsonObject { + return JsonObject().apply { + addProperty("text", randomValue()) + addProperty("uncd", unicode(8)) + addProperty("info", randomValue()) + } + } + + fun generateMessage(pubnub: PubNubImpl): JsonObject { + return JsonObject().apply { + addProperty("publisher", pubnub.configuration.userId.value) + addProperty("text", randomValue()) + addProperty("uncd", unicode(8)) + } + } + + fun generatePayloadJSON(): JSONObject { + return JSONObject().apply { + put("text", randomValue()) + put("uncd", unicode(8)) + put("info", randomValue()) + } + } + + fun unicode(length: Int = 5): String { + val unicodeChars = "!?+-=" + return unicodeChars.toList().shuffled().take(length).joinToString(separator = "") + } + + fun emoji(): String { + return listOf( + "๐Ÿ˜€", + "๐Ÿ˜", + "๐Ÿ˜‚", + "๐Ÿคฃ", + "๐Ÿ˜ƒ", + "๐Ÿ˜„", + "๐Ÿ˜…", + "๐Ÿ˜†", + "๐Ÿ˜‰", + "๐Ÿ˜Š", + "๐Ÿ˜‹", + "๐Ÿ˜Ž", + "๐Ÿ˜", + "๐Ÿ˜˜", + "๐Ÿฅฐ", + "๐Ÿ˜—", + "๐Ÿ˜™", + "๐Ÿ˜š", + "โ˜บ๏ธ", + "๐Ÿ™‚", + "๐Ÿค—", + "๐Ÿคฉ", + "๐Ÿค”", + "๐Ÿคจ", + "๐Ÿ˜", + "๐Ÿ˜‘", + "๐Ÿ˜ถ", + "๐Ÿ™„", + "๐Ÿ˜", + "๐Ÿ˜ฃ", + "๐Ÿ˜ฅ", + "๐Ÿ˜ฎ", + "๐Ÿค", + "๐Ÿ˜ฏ", + "๐Ÿ˜ช", + "๐Ÿ˜ซ", + "๐Ÿ˜ด", + "๐Ÿ˜Œ", + "๐Ÿ˜›", + "๐Ÿ˜œ", + "๐Ÿ˜", + "๐Ÿคค", + "๐Ÿ˜’", + "๐Ÿ˜“", + "๐Ÿ˜”", + "๐Ÿ˜•", + "๐Ÿ™ƒ", + "๐Ÿค‘", + "๐Ÿ˜ฒ", + "โ˜น๏ธ", + "๐Ÿ™", + "๐Ÿ˜–", + "๐Ÿ˜ž", + "๐Ÿ˜Ÿ", + "๐Ÿ˜ค", + "๐Ÿ˜ข", + "๐Ÿ˜ญ", + "๐Ÿ˜ฆ", + "๐Ÿ˜ง", + "๐Ÿ˜จ", + "๐Ÿ˜ฉ", + "๐Ÿคฏ", + "๐Ÿ˜ฌ", + "๐Ÿ˜ฐ", + "๐Ÿ˜ฑ", + "๐Ÿฅต", + "๐Ÿฅถ", + "๐Ÿ˜ณ", + "๐Ÿคช", + "๐Ÿ˜ต", + "๐Ÿ˜ก", + "๐Ÿ˜ ", + "๐Ÿคฌ", + "๐Ÿ˜ท", + "๐Ÿค’", + "๐Ÿค•", + "๐Ÿคข", + "๐Ÿคฎ", + "๐Ÿคง", + "๐Ÿ˜‡", + "๐Ÿค ", + "๐Ÿคก", + "๐Ÿฅณ", + "๐Ÿฅด", + "๐Ÿฅบ", + "๐Ÿคฅ", + "๐Ÿคซ", + "๐Ÿคญ", + "๐Ÿง", + "๐Ÿค“", + "๐Ÿ˜ˆ", + "๐Ÿ‘ฟ", + "๐Ÿ‘น", + "๐Ÿ‘บ", + "๐Ÿ’€", + "๐Ÿ‘ป", + "๐Ÿ‘ฝ", + "๐Ÿค–", + "๐Ÿ’ฉ", + "๐Ÿ˜บ", + "๐Ÿ˜ธ", + "๐Ÿ˜น", + "๐Ÿ˜ป", + "๐Ÿ˜ผ", + "๐Ÿ˜ฝ", + "๐Ÿ™€", + "๐Ÿ˜ฟ", + "๐Ÿ˜พ", + ).shuffled().take(5).joinToString("") + } + + fun generateMap() = + mapOf( + "text" to randomValue(8), + "uncd" to unicode(), + "info" to randomValue(8), + ) + + fun createInterceptor(logger: Logger) = + HttpLoggingInterceptor( + object : HttpLoggingInterceptor.Logger { + override fun log(message: String) { + logger.debug(message) + } + }, + ).apply { + level = HttpLoggingInterceptor.Level.BODY + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/Extensions.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/Extensions.kt new file mode 100644 index 000000000..cc1f0e8a5 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/Extensions.kt @@ -0,0 +1,114 @@ +package com.pubnub.test + +import com.pubnub.api.Endpoint +import com.pubnub.api.v2.callbacks.Result +import com.pubnub.internal.EndpointCore +import org.awaitility.Awaitility +import org.awaitility.Durations +import org.awaitility.pollinterval.FibonacciPollInterval +import org.hamcrest.core.IsEqual +import java.util.UUID +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger + +fun Endpoint.await(function: (result: Result) -> Unit) { + val success = AtomicBoolean() + async { result -> + function.invoke(result) + success.set(true) + } + success.listen() +} + +fun String.encodedParamString(param: String): String { + return split("&") + .first { it.startsWith(param) } + .split("=")[1] +} + +fun AtomicBoolean.listen(function: () -> Boolean): AtomicBoolean { + Awaitility.await() + .atMost(Durations.FIVE_SECONDS) + .with() + .until { + function.invoke() + } + return this +} + +fun EndpointCore.asyncRetry(function: (result: Result) -> Unit) { + val hits = AtomicInteger(0) + + val block = { + hits.incrementAndGet() + val latch = CountDownLatch(1) + val success = AtomicBoolean() + queryParam += mapOf("key" to UUID.randomUUID().toString()) + async { result -> + try { + function.invoke(result) + success.set(true) + } catch (e: Throwable) { + success.set(false) + } + latch.countDown() + } + latch.await(2L, TimeUnit.SECONDS) + success.get() + } + + Awaitility.await() + .atMost(CommonUtils.defaultListenDuration.toLong(), TimeUnit.SECONDS) + .pollInterval(FibonacciPollInterval(TimeUnit.SECONDS)) + .until { + block.invoke() + } +} + +fun EndpointCore.retryForbidden( + onFail: (exception: Throwable) -> Unit, + function: (result: Result) -> Unit, +) { + val success = AtomicBoolean() + + // first run should return forbidden + async { result -> + result.onFailure { + println(it) + + TODO("check what exception is thrown for 403 and how to detect it") +// if (status.error && status.statusCode == 403) { //TODO check what exception is thrown for 403 and how to detect it +// onFail.invoke(status) +// success.set(false) +// } + } + } + + Awaitility.await() + .atMost(10L, TimeUnit.SECONDS) + .untilAtomic(success, IsEqual.equalTo(false)) + + Thread.sleep(2_000L) + + // retry and invoke callback + queryParam += mapOf("key" to UUID.randomUUID().toString()) + async { result -> + try { + function.invoke(result) + success.set(true) + } catch (e: Throwable) { + success.set(false) + } + } + + Awaitility.await() + .atMost(CommonUtils.defaultListenDuration.toLong(), TimeUnit.SECONDS) + .pollInterval(FibonacciPollInterval(TimeUnit.SECONDS)) + .untilAtomic(success, IsEqual.equalTo(true)) +} + +fun AtomicBoolean.listen(seconds: Int = CommonUtils.defaultListenDuration) { + CommonUtils.observe(this, seconds) +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/Keys.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/Keys.kt new file mode 100644 index 000000000..a31318d43 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/Keys.kt @@ -0,0 +1,24 @@ +package com.pubnub.test + +import org.aeonbits.owner.Config +import org.aeonbits.owner.ConfigFactory + +@Config.Sources("file:../../test.properties") +interface KeysConfig : Config { + @get:Config.Key("subKey") + val subKey: String + + @get:Config.Key("pubKey") + val pubKey: String + + @get:Config.Key("pamSubKey") + val pamSubKey: String + + @get:Config.Key("pamPubKey") + val pamPubKey: String + + @get:Config.Key("pamSecKey") + val pamSecKey: String +} + +val Keys: KeysConfig = ConfigFactory.create(KeysConfig::class.java, System.getenv()) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/SignatureUtils.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/SignatureUtils.kt new file mode 100644 index 000000000..9545c0ffc --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/kotlin/com/pubnub/test/SignatureUtils.kt @@ -0,0 +1,179 @@ +package com.pubnub.test + +import com.github.tomakehurst.wiremock.verification.LoggedRequest +import com.pubnub.api.v2.PNConfiguration +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.Request +import okio.Buffer +import org.apache.commons.codec.digest.HmacAlgorithms +import org.apache.commons.codec.digest.HmacUtils +import org.junit.Assert.assertEquals +import java.io.IOException +import java.net.URLEncoder +import java.nio.charset.Charset +import java.security.InvalidKeyException +import java.security.NoSuchAlgorithmException +import java.util.Locale +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec + +object SignatureUtils { + fun decomposeAndVerifySignature( + configuration: PNConfiguration, + request: LoggedRequest, + ) { + decomposeAndVerifySignature( + configuration = configuration, + url = request.absoluteUrl, + method = request.method.name, + body = request.bodyAsString, + ) + } + + fun decomposeAndVerifySignature( + configuration: PNConfiguration, + request: Request, + ) { + decomposeAndVerifySignature( + configuration = configuration, + url = request.url.toString(), + method = request.method, + body = requestBodyToString(request), + ) + } + + private fun decomposeAndVerifySignature( + configuration: PNConfiguration, + url: String, + method: String, + body: String = "", + ) { + val httpUrl = url.toHttpUrlOrNull() + println(httpUrl) + + val sortedQueryString = + httpUrl!!.run { + queryParameterNames + .filter { it != "signature" } + .mapNotNull { queryParameterName -> + queryParameterValues(queryParameterName).first()?.let { queryParameterName to pamEncode(it) } + } + .toMap() + .toSortedMap() + .map { "${it.key}=${it.value}" } + .joinToString("&") + } + + var v2Signature = true + + val input = + if (!(httpUrl.encodedPath.startsWith("/publish") && method.lowercase(Locale.getDefault()) == "post")) { + """ + ${method.uppercase(Locale.getDefault())} + ${configuration.publishKey} + ${httpUrl.encodedPath} + $sortedQueryString + $body + """.trimIndent() + } else { + v2Signature = false + """ + ${configuration.subscribeKey} + ${configuration.publishKey} + ${httpUrl.encodedPath} + $sortedQueryString + """.trimIndent() + } + + val actualSignature = httpUrl.queryParameter("signature") + val verifiedSignature = verifyViaKotlin(configuration.secretKey, input, v2Signature) + + val rebuiltSignature = + signSHA256(configuration.secretKey, input).run { + if (v2Signature) { + "v2.${this.trim('=')}" + } else { + this + } + } + + println("originalTimestamp:\t${httpUrl.queryParameter("timestamp")}") + println("signatureInput:\t$input") + println("actualSignature:\t$actualSignature") + println("rebuiltSignature:\t$rebuiltSignature") + println("verifiedSignature:\t$verifiedSignature") + + assertEquals(actualSignature, rebuiltSignature) + assertEquals(actualSignature, verifiedSignature) + } + + private fun signSHA256( + key: String, + data: String, + ): String { + val hmacData: ByteArray + val secretKey = SecretKeySpec(key.toByteArray(charset("UTF-8")), "HmacSHA256") + val sha256HMAC: Mac = + try { + Mac.getInstance("HmacSHA256") + } catch (e: NoSuchAlgorithmException) { + throw com.pubnub.internal.vendor.Crypto.newCryptoError(0, e) + } + try { + sha256HMAC.init(secretKey) + } catch (e: InvalidKeyException) { + throw com.pubnub.internal.vendor.Crypto.newCryptoError(0, e) + } + hmacData = sha256HMAC.doFinal(data.toByteArray(charset("UTF-8"))) + return String(com.pubnub.internal.vendor.Base64.encode(hmacData, 0), Charset.forName("UTF-8")) + .replace('+', '-') + .replace('/', '_') + .replace("\n", "") + } + + private fun verifyViaKotlin( + key: String, + input: String, + v2Signature: Boolean, + ): String { + val hmac = HmacUtils(HmacAlgorithms.HMAC_SHA_256, key.toByteArray()) + hmac.hmacHex(input.toByteArray()).lowercase(Locale.getDefault()) + val hmacResult = hmac.hmac(input) + val encoder = java.util.Base64.getUrlEncoder() + return if (v2Signature) { + "v2.${String(encoder.withoutPadding().encode(hmacResult))}" + } else { + String(encoder.encode(hmacResult)) + } + } + + private fun requestBodyToString(request: Request): String { + if (request.body == null) { + return "" + } + try { + val buffer = Buffer() + request.body!!.writeTo(buffer) + return buffer.readUtf8() + } catch (e: IOException) { + e.printStackTrace() + } + return "" + } + + private fun pamEncode( + stringToEncode: String, + alreadyPercentEncoded: Boolean = false, + ): String { + // !'()*~ + + return if (alreadyPercentEncoded) { + stringToEncode + } else { + URLEncoder.encode(stringToEncode, "UTF-8") + .replace("+", "%20") + }.run { + replace("*", "%2A") + } + } +} diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/entityTooLarge.xml b/pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/entityTooLarge.xml new file mode 100644 index 000000000..4621bdfb9 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/entityTooLarge.xml @@ -0,0 +1,9 @@ + + + EntityTooLarge + Your proposed upload exceeds the maximum allowed size + 5282 + 5120 + ES1J0M4J8Z1K9R1T + hADJBPzd5nX3X6t/jS/0NwFChBR2qUG/APFr2S6cJURmS/0XEszOVkPJ2KEssTMoN1xCN2+Uqhk= + diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/junit-platform.properties b/pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/junit-platform.properties new file mode 100644 index 000000000..a2c7d487d --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.execution.parallel.enabled=false \ No newline at end of file diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/logback.xml b/pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/logback.xml new file mode 100644 index 000000000..28af5fc95 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/logback.xml @@ -0,0 +1,18 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/special_chars.json b/pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/special_chars.json new file mode 100644 index 000000000..286418ca2 --- /dev/null +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/test/resources/special_chars.json @@ -0,0 +1,327 @@ +[ + { + "name": "space", + "regular": " ", + "encoded": "%20" + }, + { + "name": "double_quote", + "regular": "\"", + "encoded": "%22" + }, + { + "name": "single_quote", + "regular": "'", + "encoded": "%27" + }, + { + "name": "exclamation", + "regular": "!", + "encoded": "%21" + }, + { + "name": "right_double_quotation_mark", + "regular": "โ€", + "encoded": "%E2%80%9D" + }, + { + "name": "hash", + "regular": "#", + "encoded": "%23" + }, + { + "name": "dollar_sign", + "regular": "$", + "encoded": "%24" + }, + { + "name": "percent", + "regular": "%", + "encoded": "%25" + }, + { + "name": "ampersand", + "regular": "&", + "encoded": "%26" + }, + { + "name": "right_single_quotation_mark", + "regular": "โ€™", + "encoded": "%E2%80%99" + }, + { + "name": "left_parenthesis", + "regular": "(", + "encoded": "%28" + }, + { + "name": "right_parenthesis", + "regular": ")", + "encoded": "%29" + }, + { + "name": "asterisk", + "regular": "*", + "encoded": "*" + }, + { + "name": "plus", + "regular": "+", + "encoded": "%2B" + }, + { + "name": "comma", + "regular": ",", + "encoded": "%2C" + }, + { + "name": "minus", + "regular": "-", + "encoded": "-" + }, + { + "name": "full_stop", + "regular": ".", + "encoded": "." + }, + { + "name": "slash", + "regular": "/", + "encoded": "%2F" + }, + { + "name": "colon", + "regular": ":", + "encoded": "%3A" + }, + { + "name": "semicolon", + "regular": ";", + "encoded": "%3B" + }, + { + "name": "less_than", + "regular": "<", + "encoded": "%3C" + }, + { + "name": "equal_sign", + "regular": "=", + "encoded": "%3D" + }, + { + "name": "greater_than", + "regular": ">", + "encoded": "%3E" + }, + { + "name": "question_mark", + "regular": "?", + "encoded": "%3F" + }, + { + "name": "at_sign", + "regular": "@", + "encoded": "%40" + }, + { + "name": "left_bracket", + "regular": "[", + "encoded": "%5B" + }, + { + "name": "backslash", + "regular": "\\", + "encoded": "%5C" + }, + { + "name": "right_bracket", + "regular": "]", + "encoded": "%5D" + }, + { + "name": "caret", + "regular": "^", + "encoded": "%5E" + }, + { + "name": "underscore", + "regular": "_", + "encoded": "_" + }, + { + "name": "backtick", + "regular": "`", + "encoded": "%60" + }, + { + "name": "left_brace", + "regular": "{", + "encoded": "%7B" + }, + { + "name": "vertical_bar", + "regular": "|", + "encoded": "%7C" + }, + { + "name": "right_brace", + "regular": "}", + "encoded": "%7D" + }, + { + "name": "tilde", + "regular": "~", + "encoded": "%7E" + }, + { + "name": "inverted_exclamation_mark", + "regular": "ยก", + "encoded": "%C2%A1" + }, + { + "name": "cent_sign", + "regular": "ยข", + "encoded": "%C2%A2" + }, + { + "name": "pound_sign", + "regular": "ยฃ", + "encoded": "%C2%A3" + }, + { + "name": "currency_sign", + "regular": "ยค", + "encoded": "%C2%A4" + }, + { + "name": "yen_sign", + "regular": "ยฅ", + "encoded": "%C2%A5" + }, + { + "name": "broken_bar", + "regular": "ยฆ", + "encoded": "%C2%A6" + }, + { + "name": "section_sign", + "regular": "ยง", + "encoded": "%C2%A7" + }, + { + "name": "diaeresis", + "regular": "ยจ", + "encoded": "%C2%A8" + }, + { + "name": "copyright_sign", + "regular": "ยฉ", + "encoded": "%C2%A9" + }, + { + "name": "feminine_ordinal_indicator", + "regular": "ยช", + "encoded": "%C2%AA" + }, + { + "name": "left-pointing_double_angle_quotation_mark", + "regular": "ยซ", + "encoded": "%C2%AB" + }, + { + "name": "not_sign", + "regular": "ยฌ", + "encoded": "%C2%AC" + }, + { + "name": "registered_sign", + "regular": "ยฎ", + "encoded": "%C2%AE" + }, + { + "name": "macron", + "regular": "ยฏ", + "encoded": "%C2%AF" + }, + { + "name": "degree_sign", + "regular": "ยฐ", + "encoded": "%C2%B0" + }, + { + "name": "plus-minus_sign", + "regular": "ยฑ", + "encoded": "%C2%B1" + }, + { + "name": "superscript_two", + "regular": "ยฒ", + "encoded": "%C2%B2" + }, + { + "name": "superscript_three", + "regular": "ยณ", + "encoded": "%C2%B3" + }, + { + "name": "acute_accent", + "regular": "ยด", + "encoded": "%C2%B4" + }, + { + "name": "micro_sign", + "regular": "ยต", + "encoded": "%C2%B5" + }, + { + "name": "pilcrow_sign", + "regular": "ยถ", + "encoded": "%C2%B6" + }, + { + "name": "middle_dot", + "regular": "ยท", + "encoded": "%C2%B7" + }, + { + "name": "cedilla", + "regular": "ยธ", + "encoded": "%C2%B8" + }, + { + "name": "superscript_one", + "regular": "ยน", + "encoded": "%C2%B9" + }, + { + "name": "masculine_ordinal_indicator", + "regular": "ยบ", + "encoded": "%C2%BA" + }, + { + "name": "right-pointing_double_angle_quotation_mark", + "regular": "ยป", + "encoded": "%C2%BB" + }, + { + "name": "vulgar_fraction_one_quarter", + "regular": "ยผ", + "encoded": "%C2%BC" + }, + { + "name": "vulgar_fraction_one_half", + "regular": "ยฝ", + "encoded": "%C2%BD" + }, + { + "name": "vulgar_fraction_three_quarters", + "regular": "ยพ", + "encoded": "%C2%BE" + }, + { + "name": "inverted_question_mark", + "regular": "ยฟ", + "encoded": "%C2%BF" + } +] \ No newline at end of file diff --git a/pubnub-kotlin/pubnub-kotlin-test/build.gradle.kts b/pubnub-kotlin/pubnub-kotlin-test/build.gradle.kts index 287854eb3..4fc549729 100644 --- a/pubnub-kotlin/pubnub-kotlin-test/build.gradle.kts +++ b/pubnub-kotlin/pubnub-kotlin-test/build.gradle.kts @@ -37,7 +37,7 @@ kotlin { val commonMain by getting { dependencies { api(project(":pubnub-kotlin:pubnub-kotlin-api")) - api(project(":pubnub-core:pubnub-core-api")) +// api(project(":pubnub-core:pubnub-core-api")) api(kotlin("test")) api(libs.coroutines.test) } diff --git a/pubnub-kotlin/pubnub-kotlin-test/src/commonMain/kotlin/com.pubnub.test/FakePubNub.kt b/pubnub-kotlin/pubnub-kotlin-test/src/commonMain/kotlin/com.pubnub.test/FakePubNub.kt deleted file mode 100644 index 035902256..000000000 --- a/pubnub-kotlin/pubnub-kotlin-test/src/commonMain/kotlin/com.pubnub.test/FakePubNub.kt +++ /dev/null @@ -1,509 +0,0 @@ -package com.pubnub.test - -import com.pubnub.api.PubNub -import com.pubnub.api.callbacks.Listener -import com.pubnub.api.endpoints.DeleteMessages -import com.pubnub.api.endpoints.FetchMessages -import com.pubnub.api.endpoints.MessageCounts -import com.pubnub.api.endpoints.Time -import com.pubnub.api.endpoints.access.GrantToken -import com.pubnub.api.endpoints.access.RevokeToken -import com.pubnub.api.endpoints.channel_groups.AddChannelChannelGroup -import com.pubnub.api.endpoints.channel_groups.AllChannelsChannelGroup -import com.pubnub.api.endpoints.channel_groups.DeleteChannelGroup -import com.pubnub.api.endpoints.channel_groups.ListAllChannelGroup -import com.pubnub.api.endpoints.channel_groups.RemoveChannelChannelGroup -import com.pubnub.api.endpoints.files.DeleteFile -import com.pubnub.api.endpoints.files.DownloadFile -import com.pubnub.api.endpoints.files.GetFileUrl -import com.pubnub.api.endpoints.files.ListFiles -import com.pubnub.api.endpoints.files.PublishFileMessage -import com.pubnub.api.endpoints.files.SendFile -import com.pubnub.api.endpoints.message_actions.AddMessageAction -import com.pubnub.api.endpoints.message_actions.GetMessageActions -import com.pubnub.api.endpoints.message_actions.RemoveMessageAction -import com.pubnub.api.endpoints.objects.channel.GetAllChannelMetadata -import com.pubnub.api.endpoints.objects.channel.GetChannelMetadata -import com.pubnub.api.endpoints.objects.channel.RemoveChannelMetadata -import com.pubnub.api.endpoints.objects.channel.SetChannelMetadata -import com.pubnub.api.endpoints.objects.member.GetChannelMembers -import com.pubnub.api.endpoints.objects.member.ManageChannelMembers -import com.pubnub.api.endpoints.objects.membership.GetMemberships -import com.pubnub.api.endpoints.objects.membership.ManageMemberships -import com.pubnub.api.endpoints.objects.uuid.GetAllUUIDMetadata -import com.pubnub.api.endpoints.objects.uuid.GetUUIDMetadata -import com.pubnub.api.endpoints.objects.uuid.RemoveUUIDMetadata -import com.pubnub.api.endpoints.objects.uuid.SetUUIDMetadata -import com.pubnub.api.endpoints.presence.GetState -import com.pubnub.api.endpoints.presence.HereNow -import com.pubnub.api.endpoints.presence.SetState -import com.pubnub.api.endpoints.presence.WhereNow -import com.pubnub.api.endpoints.pubsub.Publish -import com.pubnub.api.endpoints.pubsub.Signal -import com.pubnub.api.endpoints.push.AddChannelsToPush -import com.pubnub.api.endpoints.push.ListPushProvisions -import com.pubnub.api.endpoints.push.RemoveAllPushChannelsForDevice -import com.pubnub.api.endpoints.push.RemoveChannelsFromPush -import com.pubnub.api.enums.PNPushEnvironment -import com.pubnub.api.enums.PNPushType -import com.pubnub.api.models.consumer.PNBoundedPage -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant -import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant -import com.pubnub.api.models.consumer.access_manager.v3.UUIDGrant -import com.pubnub.api.models.consumer.message_actions.PNMessageAction -import com.pubnub.api.models.consumer.objects.PNKey -import com.pubnub.api.models.consumer.objects.PNMemberKey -import com.pubnub.api.models.consumer.objects.PNMembershipKey -import com.pubnub.api.models.consumer.objects.PNPage -import com.pubnub.api.models.consumer.objects.PNSortKey -import com.pubnub.api.models.consumer.objects.member.MemberInput -import com.pubnub.api.models.consumer.objects.member.PNUUIDDetailsLevel -import com.pubnub.api.models.consumer.objects.membership.ChannelMembershipInput -import com.pubnub.api.models.consumer.objects.membership.PNChannelDetailsLevel -import com.pubnub.api.v2.PNConfiguration -import com.pubnub.api.v2.callbacks.EventListener -import com.pubnub.api.v2.callbacks.StatusListener -import com.pubnub.api.v2.entities.Channel -import com.pubnub.api.v2.entities.ChannelGroup -import com.pubnub.api.v2.entities.ChannelMetadata -import com.pubnub.api.v2.entities.UserMetadata -import com.pubnub.api.v2.subscriptions.Subscription -import com.pubnub.api.v2.subscriptions.SubscriptionOptions -import com.pubnub.api.v2.subscriptions.SubscriptionSet -import com.pubnub.kmp.CustomObject -import com.pubnub.kmp.Uploadable - -private fun notImplemented(): Nothing = TODO("Not implemented") - -abstract class FakePubNub( - override val configuration: PNConfiguration, - private val addListener: (EventListener) -> Unit = { notImplemented() }, -) : PubNub { - override fun addListener(listener: EventListener) = addListener.invoke(listener) - - override fun addListener(listener: StatusListener) { - TODO("Not yet implemented") - } - - override fun removeListener(listener: Listener) { - TODO("Not yet implemented") - } - - override fun removeAllListeners() { - TODO("Not yet implemented") - } - - override fun publish( - channel: String, - message: Any, - meta: Any?, - shouldStore: Boolean, - usePost: Boolean, - replicate: Boolean, - ttl: Int?, - ): Publish { - TODO("Not yet implemented") - } - - override fun fire(channel: String, message: Any, meta: Any?, usePost: Boolean, ttl: Int?): Publish { - TODO("Not yet implemented") - } - - override fun signal(channel: String, message: Any): Signal { - TODO("Not yet implemented") - } - - override fun getSubscribedChannels(): List { - TODO("Not yet implemented") - } - - override fun getSubscribedChannelGroups(): List { - TODO("Not yet implemented") - } - - override fun addPushNotificationsOnChannels( - pushType: PNPushType, - channels: List, - deviceId: String, - topic: String?, - environment: PNPushEnvironment, - ): AddChannelsToPush { - TODO("Not yet implemented") - } - - override fun auditPushChannelProvisions( - pushType: PNPushType, - deviceId: String, - topic: String?, - environment: PNPushEnvironment, - ): ListPushProvisions { - TODO("Not yet implemented") - } - - override fun removePushNotificationsFromChannels( - pushType: PNPushType, - channels: List, - deviceId: String, - topic: String?, - environment: PNPushEnvironment, - ): RemoveChannelsFromPush { - TODO("Not yet implemented") - } - - override fun removeAllPushNotificationsFromDeviceWithPushToken( - pushType: PNPushType, - deviceId: String, - topic: String?, - environment: PNPushEnvironment, - ): RemoveAllPushChannelsForDevice { - TODO("Not yet implemented") - } - - override fun fetchMessages( - channels: List, - page: PNBoundedPage, - includeUUID: Boolean, - includeMeta: Boolean, - includeMessageActions: Boolean, - includeMessageType: Boolean, - ): FetchMessages { - TODO("Not yet implemented") - } - - override fun deleteMessages(channels: List, start: Long?, end: Long?): DeleteMessages { - TODO("Not yet implemented") - } - - override fun messageCounts(channels: List, channelsTimetoken: List): MessageCounts { - TODO("Not yet implemented") - } - - override fun hereNow( - channels: List, - channelGroups: List, - includeState: Boolean, - includeUUIDs: Boolean, - ): HereNow { - TODO("Not yet implemented") - } - - override fun whereNow(uuid: String): WhereNow { - TODO("Not yet implemented") - } - - override fun setPresenceState(channels: List, channelGroups: List, state: Any): SetState { - TODO("Not yet implemented") - } - - override fun getPresenceState(channels: List, channelGroups: List, uuid: String): GetState { - TODO("Not yet implemented") - } - - override fun presence(channels: List, channelGroups: List, connected: Boolean) { - TODO("Not yet implemented") - } - - override fun addMessageAction(channel: String, messageAction: PNMessageAction): AddMessageAction { - TODO("Not yet implemented") - } - - override fun removeMessageAction( - channel: String, - messageTimetoken: Long, - actionTimetoken: Long, - ): RemoveMessageAction { - TODO("Not yet implemented") - } - - override fun getMessageActions(channel: String, page: PNBoundedPage): GetMessageActions { - TODO("Not yet implemented") - } - - override fun addChannelsToChannelGroup(channels: List, channelGroup: String): AddChannelChannelGroup { - TODO("Not yet implemented") - } - - override fun listChannelsForChannelGroup(channelGroup: String): AllChannelsChannelGroup { - TODO("Not yet implemented") - } - - override fun removeChannelsFromChannelGroup( - channels: List, - channelGroup: String, - ): RemoveChannelChannelGroup { - TODO("Not yet implemented") - } - - override fun listAllChannelGroups(): ListAllChannelGroup { - TODO("Not yet implemented") - } - - override fun deleteChannelGroup(channelGroup: String): DeleteChannelGroup { - TODO("Not yet implemented") - } - - override fun grantToken( - ttl: Int, - meta: CustomObject?, - authorizedUUID: String?, - channels: List, - channelGroups: List, - uuids: List, - ): GrantToken { - TODO("Not yet implemented") - } - - override fun revokeToken(token: String): RevokeToken { - TODO("Not yet implemented") - } - - override fun time(): Time { - TODO("Not yet implemented") - } - - override fun getAllChannelMetadata( - limit: Int?, - page: PNPage?, - filter: String?, - sort: Collection>, - includeCount: Boolean, - includeCustom: Boolean, - ): GetAllChannelMetadata { - TODO("Not yet implemented") - } - - override fun getChannelMetadata(channel: String, includeCustom: Boolean): GetChannelMetadata { - TODO("Not yet implemented") - } - - override fun setChannelMetadata( - channel: String, - name: String?, - description: String?, - custom: CustomObject?, - includeCustom: Boolean, - type: String?, - status: String?, - ): SetChannelMetadata { - TODO("Not yet implemented") - } - - override fun removeChannelMetadata(channel: String): RemoveChannelMetadata { - TODO("Not yet implemented") - } - - override fun getAllUUIDMetadata( - limit: Int?, - page: PNPage?, - filter: String?, - sort: Collection>, - includeCount: Boolean, - includeCustom: Boolean, - ): GetAllUUIDMetadata { - TODO("Not yet implemented") - } - - override fun getUUIDMetadata(uuid: String?, includeCustom: Boolean): GetUUIDMetadata { - TODO("Not yet implemented") - } - - override fun setUUIDMetadata( - uuid: String?, - name: String?, - externalId: String?, - profileUrl: String?, - email: String?, - custom: CustomObject?, - includeCustom: Boolean, - type: String?, - status: String?, - ): SetUUIDMetadata { - TODO("Not yet implemented") - } - - override fun removeUUIDMetadata(uuid: String?): RemoveUUIDMetadata { - TODO("Not yet implemented") - } - - override fun getMemberships( - uuid: String?, - limit: Int?, - page: PNPage?, - filter: String?, - sort: Collection>, - includeCount: Boolean, - includeCustom: Boolean, - includeChannelDetails: PNChannelDetailsLevel?, - includeType: Boolean, - ): GetMemberships { - TODO("Not yet implemented") - } - - override fun setMemberships( - channels: List, - uuid: String?, - limit: Int?, - page: PNPage?, - filter: String?, - sort: Collection>, - includeCount: Boolean, - includeCustom: Boolean, - includeChannelDetails: PNChannelDetailsLevel?, - includeType: Boolean, - ): ManageMemberships { - TODO("Not yet implemented") - } - - override fun removeMemberships( - channels: List, - uuid: String?, - limit: Int?, - page: PNPage?, - filter: String?, - sort: Collection>, - includeCount: Boolean, - includeCustom: Boolean, - includeChannelDetails: PNChannelDetailsLevel?, - includeType: Boolean, - ): ManageMemberships { - TODO("Not yet implemented") - } - - override fun getChannelMembers( - channel: String, - limit: Int?, - page: PNPage?, - filter: String?, - sort: Collection>, - includeCount: Boolean, - includeCustom: Boolean, - includeUUIDDetails: PNUUIDDetailsLevel?, - includeType: Boolean, - ): GetChannelMembers { - TODO("Not yet implemented") - } - - override fun setChannelMembers( - channel: String, - uuids: List, - limit: Int?, - page: PNPage?, - filter: String?, - sort: Collection>, - includeCount: Boolean, - includeCustom: Boolean, - includeUUIDDetails: PNUUIDDetailsLevel?, - includeType: Boolean, - ): ManageChannelMembers { - TODO("Not yet implemented") - } - - override fun removeChannelMembers( - channel: String, - uuids: List, - limit: Int?, - page: PNPage?, - filter: String?, - sort: Collection>, - includeCount: Boolean, - includeCustom: Boolean, - includeUUIDDetails: PNUUIDDetailsLevel?, - includeType: Boolean, - ): ManageChannelMembers { - TODO("Not yet implemented") - } - - override fun listFiles(channel: String, limit: Int?, next: PNPage.PNNext?): ListFiles { - TODO("Not yet implemented") - } - - override fun getFileUrl(channel: String, fileName: String, fileId: String): GetFileUrl { - TODO("Not yet implemented") - } - - override fun sendFile( - channel: String, - fileName: String, - inputStream: Uploadable, - message: Any?, - meta: Any?, - ttl: Int?, - shouldStore: Boolean?, - cipherKey: String?, - ): SendFile { - TODO("Not yet implemented") - } - - override fun downloadFile(channel: String, fileName: String, fileId: String, cipherKey: String?): DownloadFile { - TODO("Not yet implemented") - } - - override fun deleteFile(channel: String, fileName: String, fileId: String): DeleteFile { - TODO("Not yet implemented") - } - - override fun publishFileMessage( - channel: String, - fileName: String, - fileId: String, - message: Any?, - meta: Any?, - ttl: Int?, - shouldStore: Boolean?, - ): PublishFileMessage { - TODO("Not yet implemented") - } - - override fun subscribe( - channels: List, - channelGroups: List, - withPresence: Boolean, - withTimetoken: Long, - ) { - TODO("Not yet implemented") - } - - override fun unsubscribe(channels: List, channelGroups: List) { - TODO("Not yet implemented") - } - - override fun unsubscribeAll() { - TODO("Not yet implemented") - } - - override fun setToken(token: String?) { - TODO("Not yet implemented") - } - - override fun destroy() { - TODO("Not yet implemented") - } - - override fun channel(name: String): Channel { - TODO("Not yet implemented") - } - - override fun channelGroup(name: String): ChannelGroup { - TODO("Not yet implemented") - } - - override fun channelMetadata(id: String): ChannelMetadata { - TODO("Not yet implemented") - } - - override fun userMetadata(id: String): UserMetadata { - TODO("Not yet implemented") - } - - override fun subscriptionSetOf(subscriptions: Set): SubscriptionSet { - TODO("Not yet implemented") - } - - override fun subscriptionSetOf( - channels: Set, - channelGroups: Set, - options: SubscriptionOptions, - ): SubscriptionSet { - TODO("Not yet implemented") - } -} diff --git a/settings.gradle.kts b/settings.gradle.kts index 74449c59f..17cd29489 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,8 +13,8 @@ dependencyResolutionManagement { } rootProject.name = "pubnub" -include("pubnub-core:pubnub-core-api") -include("pubnub-core:pubnub-core-impl") +//include("pubnub-core:pubnub-core-api") +//include("pubnub-core:pubnub-core-impl") include("pubnub-kotlin") include("pubnub-kotlin:pubnub-kotlin-api") include("pubnub-kotlin:pubnub-kotlin-impl")