Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/additional space and message type fields #153

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,13 @@ jobs:
cp sdk-specifications/features/access/authorization-failure-reporting.feature tests/acceptance/pam
cp sdk-specifications/features/access/grant-token.feature tests/acceptance/pam
cp sdk-specifications/features/access/revoke-token.feature tests/acceptance/pam
cp sdk-specifications/features/files/file-upload-to-space.feature tests/acceptance/files
cp sdk-specifications/features/history/history-vsp.feature tests/acceptance/history

sudo pip3 install -r requirements-dev.txt
behave --junit tests/acceptance/pam
behave --junit tests/acceptance/files
behave --junit tests/acceptance/history
seba-aln marked this conversation as resolved.
Show resolved Hide resolved
- name: Expose acceptance tests reports
uses: actions/upload-artifact@v3
if: always()
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ _trial_temp
# jupyter dev notebook
PubNubTwisted.ipynb

# Feature files for acceptance testing.
tests/acceptance/*/*.feature

# GitHub Actions #
##################
.github/.release
20 changes: 16 additions & 4 deletions pubnub/endpoints/fetch_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ def __init__(self, pubnub):
self._count = None
self._include_meta = None
self._include_message_actions = None
self._include_message_type = None
self._include_message_type = True
self._include_space_id = None
self._include_uuid = None

def channels(self, channels):
Expand Down Expand Up @@ -76,8 +77,17 @@ def include_uuid(self, include_uuid):
self._include_uuid = include_uuid
return self

def include_space_id(self, include_space_id):
assert isinstance(include_space_id, bool)
self._include_space_id = include_space_id
seba-aln marked this conversation as resolved.
Show resolved Hide resolved
return self

def custom_params(self):
params = {'max': int(self._count)}
params = {
'max': int(self._count),
'include_type': 'true' if self._include_message_type else 'false',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'include_type': str(self._include_message_type).lower()

'include_message_type': 'true' if self._include_message_type else 'false',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'include_type': str(self._include_message_type).lower()

}

if self._start is not None:
params['start'] = str(self._start)
Expand All @@ -88,8 +98,8 @@ def custom_params(self):
if self._include_meta is not None:
params['include_meta'] = "true" if self._include_meta else "false"

if self._include_message_type is not None:
params['include_message_type'] = "true" if self._include_message_type else "false"
if self._include_space_id is not None:
params['include_space_id'] = "true" if self._include_space_id else "false"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

params['include_space_id'] = str(self. _include_space_id).lower()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space_id can be null and in this case the parameter should not be added, that's why there's if statement


if self.include_message_actions and self._include_uuid is not None:
params['include_uuid'] = "true" if self._include_uuid else "false"
Expand Down Expand Up @@ -154,6 +164,8 @@ def create_response(self, envelope): # pylint: disable=W0221
return PNFetchMessagesResult.from_json(
json_input=envelope,
include_message_actions=self._include_message_actions,
include_message_type=self._include_message_type,
include_space_id=self._include_space_id,
start_timetoken=self._start,
end_timetoken=self._end)

Expand Down
18 changes: 18 additions & 0 deletions pubnub/endpoints/file_operations/publish_file_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from pubnub import utils
from pubnub.models.consumer.file import PNPublishFileMessageResult
from pubnub.endpoints.mixins import TimeTokenOverrideMixin
from pubnub.models.consumer.message_type import PNMessageType
from typing import Union


class PublishFileMessage(FileOperationEndpoint, TimeTokenOverrideMixin):
Expand All @@ -20,6 +22,8 @@ def __init__(self, pubnub):
self._cipher_key = None
self._replicate = None
self._ptto = None
self._message_type = None
self._space_id = None

def meta(self, meta):
self._meta = meta
Expand Down Expand Up @@ -49,6 +53,14 @@ def file_name(self, file_name):
self._file_name = file_name
return self

def message_type(self, message_type: Union[PNMessageType, str]):
seba-aln marked this conversation as resolved.
Show resolved Hide resolved
self._message_type = message_type
return self

def space_id(self, space_id):
self._space_id = space_id
return self

def _encrypt_message(self, message):
if self._cipher_key or self._pubnub.config.cipher_key:
return self._pubnub.config.crypto.encrypt(
Expand Down Expand Up @@ -87,6 +99,12 @@ def custom_params(self):
"ttl": self._ttl,
"store": 1 if self._should_store else 0
})
if self._message_type:
params['type'] = str(self._message_type)

if self._space_id:
params['space-id'] = str(self._space_id)

return params

def is_auth_required(self):
Expand Down
26 changes: 23 additions & 3 deletions pubnub/endpoints/file_operations/send_file.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from pubnub.endpoints.file_operations.file_based_endpoint import FileOperationEndpoint

from pubnub.crypto import PubNubFileCrypto
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.file import PNSendFileResult
from pubnub.endpoints.file_operations.publish_file_message import PublishFileMessage
from pubnub.endpoints.file_operations.fetch_upload_details import FetchFileUploadS3Data
from pubnub.request_handlers.requests_handler import RequestsRequestHandler
from pubnub.endpoints.mixins import TimeTokenOverrideMixin
from pubnub.models.consumer.message_type import PNMessageType
from typing import Union


class SendFileNative(FileOperationEndpoint, TimeTokenOverrideMixin):
Expand All @@ -23,6 +24,8 @@ def __init__(self, pubnub):
self._file_object = None
self._replicate = None
self._ptto = None
self._message_type = None
self._space_id = None

def file_object(self, fd):
self._file_object = fd
Expand Down Expand Up @@ -69,7 +72,14 @@ def is_compressable(self):
return True

def custom_params(self):
return {}
params = {}
if self._message_type:
params['type'] = str(self._message_type)

if self._space_id:
params['space-id'] = self._space_id

return params

def validate_params(self):
self.validate_subscribe_key()
Expand Down Expand Up @@ -110,6 +120,14 @@ def cipher_key(self, cipher_key):
self._cipher_key = cipher_key
return self

def message_type(self, message_type: Union[PNMessageType, str]):
self._message_type = message_type
return self

def space_id(self, space_id):
self._space_id = space_id
return self

def create_response(self, envelope, data=None):
return PNSendFileResult(envelope, self._file_upload_envelope)

Expand Down Expand Up @@ -139,7 +157,9 @@ def sync(self):
ttl(self._ttl).\
replicate(self._replicate).\
ptto(self._ptto).\
cipher_key(self._cipher_key).sync()
cipher_key(self._cipher_key).\
message_type(self._message_type).\
space_id(self._space_id).sync()

response_envelope.result.timestamp = publish_file_response.result.timestamp
return response_envelope
Expand Down
18 changes: 18 additions & 0 deletions pubnub/endpoints/pubsub/publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from pubnub.models.consumer.pubsub import PNPublishResult
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.endpoints.mixins import TimeTokenOverrideMixin
from pubnub.models.consumer.message_type import PNMessageType
from typing import Union


class Publish(Endpoint, TimeTokenOverrideMixin):
Expand All @@ -15,7 +17,9 @@ class Publish(Endpoint, TimeTokenOverrideMixin):
def __init__(self, pubnub):
super(Publish, self).__init__(pubnub)
self._channel = None
self._space_id = None
self._message = None
self._message_type = None
self._should_store = None
self._use_post = None
self._meta = None
Expand All @@ -27,10 +31,18 @@ def channel(self, channel):
self._channel = str(channel)
return self

def space_id(self, space_id):
self._space_id = str(space_id)
return self

def message(self, message):
self._message = message
return self

def message_type(self, message_type: Union[PNMessageType, str]):
self._message_type = message_type
return self

def use_post(self, use_post):
self._use_post = bool(use_post)
return self
Expand Down Expand Up @@ -91,6 +103,12 @@ def custom_params(self):
if self.pubnub.config.auth_key is not None:
params["auth"] = utils.url_encode(self.pubnub.config.auth_key)

if self._message_type is not None:
params['type'] = str(self._message_type)

if self._space_id is not None:
params['space-id'] = str(self._space_id)

return params

def build_path(self):
Expand Down
22 changes: 21 additions & 1 deletion pubnub/endpoints/signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.signal import PNSignalResult
from pubnub.models.consumer.message_type import PNMessageType
from typing import Union


class Signal(Endpoint):
Expand All @@ -11,6 +13,8 @@ def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channel = None
self._message = None
self._space = None
self._message_type = None

def channel(self, channel):
self._channel = str(channel)
Expand All @@ -20,6 +24,14 @@ def message(self, message):
self._message = message
return self

def space_id(self, space):
self._space = str(space)
return self

def message_type(self, message_type: Union[PNMessageType, str]):
self._message_type = message_type
return self

def build_path(self):
stringified_message = utils.write_value_as_string(self._message)
msg = utils.url_encode(stringified_message)
Expand All @@ -29,7 +41,15 @@ def build_path(self):
)

def custom_params(self):
return {}
params = {}

if self._message_type is not None:
params['type'] = str(self._message_type)

if self._space is not None:
params['space-id'] = str(self._space)

return params

def http_method(self):
return HttpMethod.GET
Expand Down
55 changes: 34 additions & 21 deletions pubnub/models/consumer/history.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from pubnub.models.consumer.message_type import PNMessageType


class PNHistoryResult(object):
def __init__(self, messages, start_timetoken, end_timetoken):
self.messages = messages
Expand Down Expand Up @@ -63,27 +66,14 @@ def __str__(self):
return "Fetch messages result for range %d..%d" % (self.start_timetoken, self.end_timetoken)

@classmethod
def from_json(cls, json_input, include_message_actions=False, start_timetoken=None, end_timetoken=None):
def from_json(cls, json_input, include_message_actions=False, include_message_type=False, include_space_id=False,
start_timetoken=None, end_timetoken=None):
channels = {}

for key, entry in json_input['channels'].items():
channels[key] = []
for item in entry:
message = PNFetchMessageItem(item['message'], item['timetoken'])
if 'uuid' in item:
message.uuid = item['uuid']
if 'message_type' in item:
message.message_type = item['message_type']

if 'meta' in item:
message.meta = item['meta']

if include_message_actions:
if 'actions' in item:
message.actions = item['actions']
else:
message.actions = {}

message = PNFetchMessageItem(item, include_message_actions, include_message_type, include_space_id)
channels[key].append(message)

return PNFetchMessagesResult(
Expand All @@ -94,11 +84,34 @@ def from_json(cls, json_input, include_message_actions=False, start_timetoken=No


class PNFetchMessageItem(object):
def __init__(self, message, timetoken, meta=None, actions=None):
self.message = message
self.meta = meta
self.timetoken = timetoken
self.actions = actions
message = None
meta = None
timetoken = None
actions = None

def __init__(self, item, include_message_actions, include_message_type, include_space_id):
self.message = item['message']
self.timetoken = item['timetoken']

if 'uuid' in item:
self.uuid = item['uuid']

if 'meta' in item:
self.meta = item['meta']

if include_message_actions:
if 'actions' in item:
self.actions = item['actions']
else:
self.actions = {}

if include_message_type:
self.message_type = PNMessageType.from_response(
message_type=item['type'] if 'type' in item.keys() else None,
pn_message_type=item['message_type'])

if include_space_id:
self.space_id = item['space_id']

def __str__(self):
return "Fetch message item with tt: %s and content: %s" % (self.timetoken, self.message)
27 changes: 27 additions & 0 deletions pubnub/models/consumer/message_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class PNMessageType:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class look like a good example of enum class:
https://docs.python.org/3/library/enum.html

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well Enum is a closed set of values - this is more like a wrapper around two independent message types:

  • One is user-defined and can be any string (with some constrains on server side)
  • Second is our own internal message type, which is numeric
    In both cases we want to deliver to client a string. If he didn't defined his own message type, we return "mapped" version of our internal message type.
    Either way we also allow access to "raw" values using PNMessageType.message_type and PNMessageType.pn_message_type properties. And i guess enum doesn't have such possibilities

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every time we need to explain this I'm wondering if that's actually a good decision. Especially when you put it like that:

wrapper around two independent message types.

pn_message_type: str = None
message_type: str = None
_type_mapping = {
'None': 'pn_message',
'0': 'pn_message',
'1': 'pn_signal',
'2': 'pn_object',
'3': 'pn_message_action',
'4': 'pn_file',
}

def __init__(self, message_type: str = None) -> None:
self.message_type = message_type

def set_pn_message_type(self, pn_message_type: str):
self.pn_message_type = self._type_mapping[str(pn_message_type)]
return self

def from_response(message_type: str = None, pn_message_type: str = None):
return PNMessageType(message_type).set_pn_message_type(pn_message_type)

def __str__(self) -> str:
return self.message_type if self.message_type else str(self.pn_message_type)

def toJSON(self) -> str:
return str(self)
Loading