diff --git a/.gitignore b/.gitignore index cdc797df..16841382 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ __pycache__/ # Distribution / packaging .Python env/ +src/ build/ develop-eggs/ dist/ @@ -65,4 +66,10 @@ target/ # pyenv .python-version -.idea \ No newline at end of file +.idea + +# Twisted +_trial_temp + +# jupyter dev notebook +PubNubTwisted.ipynb diff --git a/.pubnub.yml b/.pubnub.yml new file mode 100644 index 00000000..a95b1f61 --- /dev/null +++ b/.pubnub.yml @@ -0,0 +1,104 @@ +name: python +version: 4.0.8 +schema: 1 +scm: github.com/pubnub/python +changelog: + - version: v4.0.8 + date: + changes: + - type: feature + text: Support log_verbosity in pnconfiguration to enable HTTP logging. + - version: v4.0.7 + date: + changes: + - type: bug + text: Handle interval presence messages gracefully if they do not contain a UUID. + - type: feature + text: Support custom cryptography module when using GAE + - type: improvement + text: designate the request thread as non-daemon to keep the SDK running. + - version: v4.0.6 + date: + changes: + - type: bug + text: Fix on state object type definition. + - version: v4.0.5 + date: + changes: + - type: improvement + text: new pubnub domain + - type: improvement + text: native demo app + - type: improvement + text: fixed HTTPAdapter config + - type: improvement + text: add a new Python 3.6.0 config to travis builds + - type: improvement + text: fix blocking Ctrl+C bug + - version: v4.0.4 + date: + changes: + - type: improvement + text: Add reconnection managers + - version: v4.0.3 + date: + changes: + - type: improvement + text: do not strip plus sign when encoding message. + - version: v4.0.2 + date: + changes: + - type: improvement + text: Adjusting maximum pool size for requests installations + - type: improvement + text: Adding Publsher UUID + - version: v4.0.1 + date: + changes: + - type: improvement + text: Fixing up packaging configuration for py3 + - version: v4.0.0 + date: + changes: + - type: improvement + text: Initial Release +features: + access: + - ACCESS-GRANT + channel-groups: + - CHANNEL-GROUPS-ADD-CHANNELS + - CHANNEL-GROUPS-REMOVE-CHANNELS + - CHANNEL-GROUPS-REMOVE-GROUPS + - CHANNEL-GROUPS-LIST-CHANNELS-IN-GROUP + push: + - PUSH-ADD-DEVICE-TO-CHANNELS + - PUSH-REMOVE-DEVICE-FROM-CHANNELS + - PUSH-LIST-CHANNELS-FROM-DEVICE + - PUSH-REMOVE-DEVICE + presence: + - PRESENCE-HERE-NOW + - PRESENCE-WHERE-NOW + - PRESENCE-SET-STATE + - PRESENCE-GET-STATE + - PRESENCE-HEARTBEAT + publish: + - PUBLISH-STORE-FLAG + - PUBLISH-RAW-JSON + - PUBLISH-WITH-METADATA + - PUBLISH-GET + - PUBLISH-ASYNC + storage: + - STORAGE-REVERSE + - STORAGE-INCLUDE-TIMETOKEN + - STORAGE-START-END + - STORAGE-COUNT + time: + - TIME-TIME + subscribe: + - SUBSCRIBE-CHANNELS + - SUBSCRIBE-CHANNEL-GROUPS + - SUBSCRIBE-PRESENCE-CHANNELS + - SUBSCRIBE-PRESENCE-CHANNELS-GROUPS + - SUBSCRIBE-WITH-TIMETOKEN + - SUBSCRIBE-WILDCARD + - SUBSCRIBE-PUBLISHER-UUID diff --git a/.travis.yml b/.travis.yml old mode 100755 new mode 100644 index 48b3e64f..65a3c41a --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,16 @@ language: python -sudo: false -addons: - apt: - packages: - - libssl-dev - - libffi-dev python: - - '2.6' - - '2.7' - - '3.2' - - '3.3' - - '3.4' - - '3.5' - -before_install: - - pip install -r dev-requirements.txt - - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then pip install twisted tornado pyopenssl; fi - + - "2.6" + - "2.7" + - "3.3" + - "3.4.4" + - "3.5" + - "3.6" + - "pypy" +sudo: false install: - python setup.py install - - -script: ./run-tests.sh + - bash scripts/install.sh +script: + - python scripts/run-tests.py +after_success: + - python-codacy-coverage -r coverage.xml diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index d912c8ca..00000000 --- a/CHANGELOG +++ /dev/null @@ -1,54 +0,0 @@ - -3.7.7 - 02-10-2015 - 3012d7e -. Adding .stop() method for base python async operations to exit the listener - -3.7.6 - 02-10-2015 - 3012d7e -. fixed issues in receiving gzipped response for twisted -. fix for non reporting of dns lookup failure -. fix in time method - -3.7.5 - 12-08-2015 - 61f1adc -. increased timeout to 15 sec - -3.7.4 - 11-17-15 - 8a00782 -. added state and here_now -. added presence heartbeat support - -3.7.2 - 6-25-15 - ad89de8 -. fix for decryption bug in history API -. module name changed to pubnub ( it was Pubnub earlier ). - Developers need to do from pubnub import Pubnub, instead of from Pubnub import Pubnub now -. fixed method arguments bug for presence API -. subscribe_sync removed -. fix for issue where error callback not invoked for presence -. added state support in subscribe and here now -. fix for grant API with python 3 -. added include_token option to history - -3.7.0 - 1-12-14 - f20e5e2 -. Channel Groups functionality -. Added Python Echo Server example -. Added missing timeout keyword arg -. Bringing versioning up to standard (3.7) - -3.5.3 - 10-21-14 - dc617ed -. Added patch to handle quick net calls in Azure environments -. Presence fixes -. added daemon flag - -3.5.2 - 6-25-14 - 1f98c4 -. Added pnsdk URL param to each request -. Added grant/revoke/audit examples to README -. Fixed erroneous "Connected" error condition in console -. Can now pass init vars via the CL on console -. Fixed UI issue of bracket color on console -. Enable subscribing to "-pnpres" channel on console - -3.5.1 - 6-17-14 -. Added subscribe_sync method -. renamed pres_uuid argument for Pubnub constructor to uuid - -3.5.0 - 6-16-14 -New version! Complete re-write! -. Async subscribe allows for MX, unsubscribe calls -. New method signatures -- be sure to check migration doc if upgrading diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..31635311 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,117 @@ + +## [v4.0.8](https://github.com/pubnub/python/tree/v4.0.8) + + + [Full Changelog](https://github.com/pubnub/python/compare/v4.0.7...v4.0.8) + +- 🌟Support log_verbosity in pnconfiguration to enable HTTP logging. + + + + +## [v4.0.7](https://github.com/pubnub/python/tree/v4.0.7) + + + [Full Changelog](https://github.com/pubnub/python/compare/v4.0.6...v4.0.7) + + + +- 🐛Handle interval presence messages gracefully if they do not contain a UUID. + +- 🌟Support custom cryptography module when using GAE + + + + +- ⭐designate the request thread as non-daemon to keep the SDK running. + + + +## [v4.0.6](https://github.com/pubnub/python/tree/v4.0.6) + + + [Full Changelog](https://github.com/pubnub/python/compare/v4.0.5...v4.0.6) + + + +- 🐛Fix on state object type definition. + + +## [v4.0.5](https://github.com/pubnub/python/tree/v4.0.5) + + + [Full Changelog](https://github.com/pubnub/python/compare/v4.0.4...v4.0.5) + + +- ⭐new pubnub domain + + + +- ⭐native demo app + + + +- ⭐fixed HTTPAdapter config + + + +- ⭐add a new Python 3.6.0 config to travis builds + + + +- ⭐fix blocking Ctrl+C bug + + + +## [v4.0.4](https://github.com/pubnub/python/tree/v4.0.4) + + + [Full Changelog](https://github.com/pubnub/python/compare/v4.0.3...v4.0.4) + + +- ⭐Add reconnection managers + + + +## [v4.0.3](https://github.com/pubnub/python/tree/v4.0.3) + + + [Full Changelog](https://github.com/pubnub/python/compare/v4.0.2...v4.0.3) + + +- ⭐do not strip plus sign when encoding message. + + + +## [v4.0.2](https://github.com/pubnub/python/tree/v4.0.2) + + + [Full Changelog](https://github.com/pubnub/python/compare/v4.0.1...v4.0.2) + + +- ⭐Adjusting maximum pool size for requests installations + + + +- ⭐Adding Publsher UUID + + + +## [v4.0.1](https://github.com/pubnub/python/tree/v4.0.1) + + + [Full Changelog](https://github.com/pubnub/python/compare/v4.0.0...v4.0.1) + + +- ⭐Fixing up packaging configuration for py3 + + + +## [v4.0.0](https://github.com/pubnub/python/tree/v4.0.0) + + + + +- ⭐Initial Release + + diff --git a/README.md b/README.md index 64eada3b..63f70ae9 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,20 @@ -# PubNub Python SDK - +# PubNub Python SDK (V4) [![Build Status](https://travis-ci.org/pubnub/python.svg?branch=master)](https://travis-ci.org/pubnub/python) -[![PyPI](https://img.shields.io/pypi/v/pubnub.svg)](http://github.com/pubnub/python) - -#### [PubNub](http://www.pubnub.com) Real-time Data Network -##### Clients for Python, including Twisted and Tornado - -## Communication -- If you **need help** or have a **general question**, contact -- If you **want to contribute**, please open a pull request against the `develop` branch. - -## Installation -``` -pip install pubnub -``` -Examples and instructions for the SDK are available in their accompanying README.md, migration.md and examples directories under their specific platform directories: - -[Standalone Python - Everyday python for your scripts and apps](python) +[![codecov](https://codecov.io/gh/pubnub/python/branch/master/graph/badge.svg)](https://codecov.io/gh/pubnub/python) +[![PyPI](https://img.shields.io/pypi/v/pubnub.svg)](https://pypi.python.org/pypi/pubnub/) +[![PyPI](https://img.shields.io/pypi/pyversions/pubnub.svg)](https://pypi.python.org/pypi/pubnub/) +[![Docs](https://img.shields.io/badge/docs-online-blue.svg)](https://www.pubnub.com/docs/python/pubnub-python-sdk-v4) -[Tornado - For use with the Python Tornado Framework](python-tornado) +The SDK supports Python 2.6, 2.7, 3.3, 3.4, 3.5 and pypy. -[Twisted - For use with the Python Twisted Framework](python-twisted) +## Documentation -## Migration and Reversion -If you need to revert to the previous version of PubNub, run this commandline: +Please review our documentation and examples on the [PubNub Website](https://www.pubnub.com/docs/python/pubnub-python-sdk-v4) -``` -pip install pubnub==3.3.5 -``` -Migration information for the SDK are available in their migration.md under their specific platform directories: +### Looking for Python V3 SDK? +please use the [master_3x](https://github.com/pubnub/python/tree/master_3x) branch -Migration docs for Python Standalone are [found here.](python/migration.md) - -Migration docs for Tornado are [found here.](python-tornado/migration.md) - -Migration docs for Twisted [found here.](python-twisted/migration.md) - -## Pubnub Console -Pubnub console is a command line app which allows you to do various -pubnub operations like publish, subscribe, getting history, here now, -presence etc from command line - -``` -pip install pubnub-console -``` +## Communication -## Contact support@pubnub.com for all questions +- If you **need help** or have a **general question**, contact diff --git a/VERSION b/VERSION deleted file mode 100644 index d2577d97..00000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -3.7.7 diff --git a/common/PubnubUnitTest.py b/common/PubnubUnitTest.py deleted file mode 100755 index 36ce4446..00000000 --- a/common/PubnubUnitTest.py +++ /dev/null @@ -1,34 +0,0 @@ - -class Suite(): - def __init__(self, pubnub, expected): - self.pubnub = pubnub - self.total = expected - self.passed = 0 - self.failed = 0 - self.started = False - - def test(self, condition, name, message=None, response=None): - - if condition: - self.passed += 1 - msg = "PASS : " + name - if message: - msg += ", " + message - if response: - msg += ", " + response - print(msg) - else: - self.failed += 1 - msg = "FAIL : " + name - if message: - msg += ", " + message - if response: - msg += ", " + response - print(msg) - - if self.total == self.failed + self.passed: - print("\n======== RESULT ========") - print("Total\t:\t", self.total) - print("Passed\t:\t", self.passed) - print("Failed\t:\t", self.failed) - self.pubnub.stop() diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100755 index ea54f1b3..00000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -flake8==2.5.2 -gevent==1.0.2 -nose==1.3.7 diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 0736a139..00000000 --- a/docs/Makefile +++ /dev/null @@ -1,177 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PubNub.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PubNub.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/PubNub" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PubNub" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle deleted file mode 100644 index 6acb7b9f..00000000 Binary files a/docs/build/doctrees/environment.pickle and /dev/null differ diff --git a/docs/build/doctrees/index.doctree b/docs/build/doctrees/index.doctree deleted file mode 100644 index 2d04154a..00000000 Binary files a/docs/build/doctrees/index.doctree and /dev/null differ diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo deleted file mode 100644 index 8797a77f..00000000 --- a/docs/build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 96d35ff6b48d08024d651739e716797e -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/_sources/index.txt b/docs/build/html/_sources/index.txt deleted file mode 100644 index 6b41e896..00000000 --- a/docs/build/html/_sources/index.txt +++ /dev/null @@ -1,35 +0,0 @@ -.. PubNub documentation master file, created by - sphinx-quickstart on Wed Jun 25 12:50:44 2014. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to PubNub's documentation! -================================== - -.. toctree:: - :maxdepth: 5 - -.. automodule:: Pubnub - -Pubnub ---------------------------------- -.. autoclass:: Pubnub - :members: publish, subscribe, subscribe_group, unsubscribe, unsubscribe_group, presence, presence_group, history, here_now, grant, audit, revoke, get_origin, set_origin, get_auth_key, set_auth_key, encrypt, decrypt, time, channel_group_list_namespaces, channel_group_remove_namespace, channel_group_list_groups, channel_group_list_channels, channel_groups_add_channel, channel_group_remove_channel, channel_group_remove_group - -PubnubTwisted ---------------------------------- -.. autoclass:: PubnubTwisted - :members: publish, subscribe, subscribe_group, unsubscribe, unsubscribe_group, presence, presence_group, history, here_now, grant, audit, revoke, get_origin, set_origin, get_auth_key, set_auth_key, encrypt, decrypt, time, channel_group_list_namespaces, channel_group_remove_namespace, channel_group_list_groups, channel_group_list_channels, channel_groups_add_channel, channel_group_remove_channel, channel_group_remove_group - - -PubnubTornado ---------------------------------- -.. autoclass:: PubnubTornado - :members: publish, subscribe, subscribe_group, unsubscribe, unsubscribe_group, presence, presence_group, history, here_now, grant, audit, revoke, get_origin, set_origin, get_auth_key, set_auth_key, encrypt, decrypt, time, channel_group_list_namespaces, channel_group_remove_namespace, channel_group_list_groups, channel_group_list_channels, channel_groups_add_channel, channel_group_remove_channel, channel_group_remove_group - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`search` - diff --git a/docs/build/html/_static/ajax-loader.gif b/docs/build/html/_static/ajax-loader.gif deleted file mode 100644 index 61faf8ca..00000000 Binary files a/docs/build/html/_static/ajax-loader.gif and /dev/null differ diff --git a/docs/build/html/_static/basic.css b/docs/build/html/_static/basic.css deleted file mode 100644 index 967e36ce..00000000 --- a/docs/build/html/_static/basic.css +++ /dev/null @@ -1,537 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox input[type="text"] { - width: 170px; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - width: 30px; -} - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li div.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable dl, table.indextable dd { - margin-top: 0; - margin-bottom: 0; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- general body styles --------------------------------------------------- */ - -a.headerlink { - visibility: hidden; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.field-list ul { - padding-left: 1em; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px 7px 0 7px; - background-color: #ffe; - width: 40%; - float: right; -} - -p.sidebar-title { - font-weight: bold; -} - -/* -- topics ---------------------------------------------------------------- */ - -div.topic { - border: 1px solid #ccc; - padding: 7px 7px 0 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -div.admonition dl { - margin-bottom: 0; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - border: 0; - border-collapse: collapse; -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -table.field-list td, table.field-list th { - border: 0 !important; -} - -table.footnote td, table.footnote th { - border: 0 !important; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -dl { - margin-bottom: 15px; -} - -dd p { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dt:target, .highlighted { - background-color: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.optional { - font-size: 1.3em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -td.linenos pre { - padding: 5px 0px; - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - margin-left: 0.5em; -} - -table.highlighttable td { - padding: 0 0.5em 0 0.5em; -} - -tt.descname { - background-color: transparent; - font-weight: bold; - font-size: 1.2em; -} - -tt.descclassname { - background-color: transparent; -} - -tt.xref, a tt { - background-color: transparent; - font-weight: bold; -} - -h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/docs/build/html/_static/comment-bright.png b/docs/build/html/_static/comment-bright.png deleted file mode 100644 index 551517b8..00000000 Binary files a/docs/build/html/_static/comment-bright.png and /dev/null differ diff --git a/docs/build/html/_static/comment-close.png b/docs/build/html/_static/comment-close.png deleted file mode 100644 index 09b54be4..00000000 Binary files a/docs/build/html/_static/comment-close.png and /dev/null differ diff --git a/docs/build/html/_static/comment.png b/docs/build/html/_static/comment.png deleted file mode 100644 index 92feb52b..00000000 Binary files a/docs/build/html/_static/comment.png and /dev/null differ diff --git a/docs/build/html/_static/default.css b/docs/build/html/_static/default.css deleted file mode 100644 index 5f1399ab..00000000 --- a/docs/build/html/_static/default.css +++ /dev/null @@ -1,256 +0,0 @@ -/* - * default.css_t - * ~~~~~~~~~~~~~ - * - * Sphinx stylesheet -- default theme. - * - * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: sans-serif; - font-size: 100%; - background-color: #11303d; - color: #000; - margin: 0; - padding: 0; -} - -div.document { - background-color: #1c4e63; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 230px; -} - -div.body { - background-color: #ffffff; - color: #000000; - padding: 0 20px 30px 20px; -} - -div.footer { - color: #ffffff; - width: 100%; - padding: 9px 0 9px 0; - text-align: center; - font-size: 75%; -} - -div.footer a { - color: #ffffff; - text-decoration: underline; -} - -div.related { - background-color: #133f52; - line-height: 30px; - color: #ffffff; -} - -div.related a { - color: #ffffff; -} - -div.sphinxsidebar { -} - -div.sphinxsidebar h3 { - font-family: 'Trebuchet MS', sans-serif; - color: #ffffff; - font-size: 1.4em; - font-weight: normal; - margin: 0; - padding: 0; -} - -div.sphinxsidebar h3 a { - color: #ffffff; -} - -div.sphinxsidebar h4 { - font-family: 'Trebuchet MS', sans-serif; - color: #ffffff; - font-size: 1.3em; - font-weight: normal; - margin: 5px 0 0 0; - padding: 0; -} - -div.sphinxsidebar p { - color: #ffffff; -} - -div.sphinxsidebar p.topless { - margin: 5px 10px 10px 10px; -} - -div.sphinxsidebar ul { - margin: 10px; - padding: 0; - color: #ffffff; -} - -div.sphinxsidebar a { - color: #98dbcc; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - - - -/* -- hyperlink styles ------------------------------------------------------ */ - -a { - color: #355f7c; - text-decoration: none; -} - -a:visited { - color: #355f7c; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - - - -/* -- body styles ----------------------------------------------------------- */ - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: 'Trebuchet MS', sans-serif; - background-color: #f2f2f2; - font-weight: normal; - color: #20435c; - border-bottom: 1px solid #ccc; - margin: 20px -20px 10px -20px; - padding: 3px 0 3px 10px; -} - -div.body h1 { margin-top: 0; font-size: 200%; } -div.body h2 { font-size: 160%; } -div.body h3 { font-size: 140%; } -div.body h4 { font-size: 120%; } -div.body h5 { font-size: 110%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #c60f0f; - font-size: 0.8em; - padding: 0 4px 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - background-color: #c60f0f; - color: white; -} - -div.body p, div.body dd, div.body li { - text-align: justify; - line-height: 130%; -} - -div.admonition p.admonition-title + p { - display: inline; -} - -div.admonition p { - margin-bottom: 5px; -} - -div.admonition pre { - margin-bottom: 5px; -} - -div.admonition ul, div.admonition ol { - margin-bottom: 5px; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre { - padding: 5px; - background-color: #eeffcc; - color: #333333; - line-height: 120%; - border: 1px solid #ac9; - border-left: none; - border-right: none; -} - -tt { - background-color: #ecf0f3; - padding: 0 1px 0 1px; - font-size: 0.95em; -} - -th { - background-color: #ede; -} - -.warning tt { - background: #efc2c2; -} - -.note tt { - background: #d6d6d6; -} - -.viewcode-back { - font-family: sans-serif; -} - -div.viewcode-block:target { - background-color: #f4debf; - border-top: 1px solid #ac9; - border-bottom: 1px solid #ac9; -} \ No newline at end of file diff --git a/docs/build/html/_static/doctools.js b/docs/build/html/_static/doctools.js deleted file mode 100644 index c5455c90..00000000 --- a/docs/build/html/_static/doctools.js +++ /dev/null @@ -1,238 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for all documentation. - * - * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/** - * select a different prefix for underscore - */ -$u = _.noConflict(); - -/** - * make the code below compatible with browsers without - * an installed firebug like debugger -if (!window.console || !console.firebug) { - var names = ["log", "debug", "info", "warn", "error", "assert", "dir", - "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", - "profile", "profileEnd"]; - window.console = {}; - for (var i = 0; i < names.length; ++i) - window.console[names[i]] = function() {}; -} - */ - -/** - * small helper function to urldecode strings - */ -jQuery.urldecode = function(x) { - return decodeURIComponent(x).replace(/\+/g, ' '); -}; - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s == 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -}; - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node) { - if (node.nodeType == 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { - var span = document.createElement("span"); - span.className = className; - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this); - }); - } - } - return this.each(function() { - highlight(this); - }); -}; - -/** - * Small JavaScript module for the documentation. - */ -var Documentation = { - - init : function() { - this.fixFirefoxAnchorBug(); - this.highlightSearchWords(); - this.initIndexTable(); - }, - - /** - * i18n support - */ - TRANSLATIONS : {}, - PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, - LOCALE : 'unknown', - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext : function(string) { - var translated = Documentation.TRANSLATIONS[string]; - if (typeof translated == 'undefined') - return string; - return (typeof translated == 'string') ? translated : translated[0]; - }, - - ngettext : function(singular, plural, n) { - var translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated == 'undefined') - return (n == 1) ? singular : plural; - return translated[Documentation.PLURALEXPR(n)]; - }, - - addTranslations : function(catalog) { - for (var key in catalog.messages) - this.TRANSLATIONS[key] = catalog.messages[key]; - this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); - this.LOCALE = catalog.locale; - }, - - /** - * add context elements like header anchor links - */ - addContextElements : function() { - $('div[id] > :header:first').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this headline')). - appendTo(this); - }); - $('dt[id]').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this definition')). - appendTo(this); - }); - }, - - /** - * workaround a firefox stupidity - */ - fixFirefoxAnchorBug : function() { - if (document.location.hash && $.browser.mozilla) - window.setTimeout(function() { - document.location.href += ''; - }, 10); - }, - - /** - * highlight the search words provided in the url in the text - */ - highlightSearchWords : function() { - var params = $.getQueryParameters(); - var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; - if (terms.length) { - var body = $('div.body'); - if (!body.length) { - body = $('body'); - } - window.setTimeout(function() { - $.each(terms, function() { - body.highlightText(this.toLowerCase(), 'highlighted'); - }); - }, 10); - $('') - .appendTo($('#searchbox')); - } - }, - - /** - * init the domain index toggle buttons - */ - initIndexTable : function() { - var togglers = $('img.toggler').click(function() { - var src = $(this).attr('src'); - var idnum = $(this).attr('id').substr(7); - $('tr.cg-' + idnum).toggle(); - if (src.substr(-9) == 'minus.png') - $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); - else - $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); - }).css('display', ''); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { - togglers.click(); - } - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords : function() { - $('#searchbox .highlight-link').fadeOut(300); - $('span.highlighted').removeClass('highlighted'); - }, - - /** - * make the url absolute - */ - makeURL : function(relativeURL) { - return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; - }, - - /** - * get the current relative url - */ - getCurrentURL : function() { - var path = document.location.pathname; - var parts = path.split(/\//); - $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { - if (this == '..') - parts.pop(); - }); - var url = parts.join('/'); - return path.substring(url.lastIndexOf('/') + 1, path.length - 1); - } -}; - -// quick alias for translations -_ = Documentation.gettext; - -$(document).ready(function() { - Documentation.init(); -}); diff --git a/docs/build/html/_static/down-pressed.png b/docs/build/html/_static/down-pressed.png deleted file mode 100644 index 6f7ad782..00000000 Binary files a/docs/build/html/_static/down-pressed.png and /dev/null differ diff --git a/docs/build/html/_static/down.png b/docs/build/html/_static/down.png deleted file mode 100644 index 3003a887..00000000 Binary files a/docs/build/html/_static/down.png and /dev/null differ diff --git a/docs/build/html/_static/file.png b/docs/build/html/_static/file.png deleted file mode 100644 index d18082e3..00000000 Binary files a/docs/build/html/_static/file.png and /dev/null differ diff --git a/docs/build/html/_static/jquery.js b/docs/build/html/_static/jquery.js deleted file mode 100644 index 83589daa..00000000 --- a/docs/build/html/_static/jquery.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v1.8.3 jquery.com | jquery.org/license */ -(function(e,t){function _(e){var t=M[e]={};return v.each(e.split(y),function(e,n){t[n]=!0}),t}function H(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(P,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:+r+""===r?+r:D.test(r)?v.parseJSON(r):r}catch(s){}v.data(e,n,r)}else r=t}return r}function B(e){var t;for(t in e){if(t==="data"&&v.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function et(){return!1}function tt(){return!0}function ut(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function at(e,t){do e=e[t];while(e&&e.nodeType!==1);return e}function ft(e,t,n){t=t||0;if(v.isFunction(t))return v.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return v.grep(e,function(e,r){return e===t===n});if(typeof t=="string"){var r=v.grep(e,function(e){return e.nodeType===1});if(it.test(t))return v.filter(t,r,!n);t=v.filter(t,r)}return v.grep(e,function(e,r){return v.inArray(e,t)>=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write(""),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t
a",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="
t
",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="
",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;ti.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="
",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="

",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t0)for(i=r;i=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*\s*$/g,Nt={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X
","
"]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1>");try{for(;r1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]===""&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("
").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window); \ No newline at end of file diff --git a/docs/build/html/_static/minus.png b/docs/build/html/_static/minus.png deleted file mode 100644 index da1c5620..00000000 Binary files a/docs/build/html/_static/minus.png and /dev/null differ diff --git a/docs/build/html/_static/plus.png b/docs/build/html/_static/plus.png deleted file mode 100644 index b3cb3742..00000000 Binary files a/docs/build/html/_static/plus.png and /dev/null differ diff --git a/docs/build/html/_static/pygments.css b/docs/build/html/_static/pygments.css deleted file mode 100644 index d79caa15..00000000 --- a/docs/build/html/_static/pygments.css +++ /dev/null @@ -1,62 +0,0 @@ -.highlight .hll { background-color: #ffffcc } -.highlight { background: #eeffcc; } -.highlight .c { color: #408090; font-style: italic } /* Comment */ -.highlight .err { border: 1px solid #FF0000 } /* Error */ -.highlight .k { color: #007020; font-weight: bold } /* Keyword */ -.highlight .o { color: #666666 } /* Operator */ -.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #007020 } /* Comment.Preproc */ -.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ -.highlight .gd { color: #A00000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #FF0000 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #00A000 } /* Generic.Inserted */ -.highlight .go { color: #333333 } /* Generic.Output */ -.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #0044DD } /* Generic.Traceback */ -.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #007020 } /* Keyword.Pseudo */ -.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #902000 } /* Keyword.Type */ -.highlight .m { color: #208050 } /* Literal.Number */ -.highlight .s { color: #4070a0 } /* Literal.String */ -.highlight .na { color: #4070a0 } /* Name.Attribute */ -.highlight .nb { color: #007020 } /* Name.Builtin */ -.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ -.highlight .no { color: #60add5 } /* Name.Constant */ -.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ -.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #007020 } /* Name.Exception */ -.highlight .nf { color: #06287e } /* Name.Function */ -.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ -.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #bb60d5 } /* Name.Variable */ -.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mf { color: #208050 } /* Literal.Number.Float */ -.highlight .mh { color: #208050 } /* Literal.Number.Hex */ -.highlight .mi { color: #208050 } /* Literal.Number.Integer */ -.highlight .mo { color: #208050 } /* Literal.Number.Oct */ -.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ -.highlight .sc { color: #4070a0 } /* Literal.String.Char */ -.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #4070a0 } /* Literal.String.Double */ -.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ -.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ -.highlight .sx { color: #c65d09 } /* Literal.String.Other */ -.highlight .sr { color: #235388 } /* Literal.String.Regex */ -.highlight .s1 { color: #4070a0 } /* Literal.String.Single */ -.highlight .ss { color: #517918 } /* Literal.String.Symbol */ -.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ -.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ -.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ -.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/build/html/_static/searchtools.js b/docs/build/html/_static/searchtools.js deleted file mode 100644 index 6e1f06bd..00000000 --- a/docs/build/html/_static/searchtools.js +++ /dev/null @@ -1,622 +0,0 @@ -/* - * searchtools.js_t - * ~~~~~~~~~~~~~~~~ - * - * Sphinx JavaScript utilties for the full-text search. - * - * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - - -/** - * Porter Stemmer - */ -var Stemmer = function() { - - var step2list = { - ational: 'ate', - tional: 'tion', - enci: 'ence', - anci: 'ance', - izer: 'ize', - bli: 'ble', - alli: 'al', - entli: 'ent', - eli: 'e', - ousli: 'ous', - ization: 'ize', - ation: 'ate', - ator: 'ate', - alism: 'al', - iveness: 'ive', - fulness: 'ful', - ousness: 'ous', - aliti: 'al', - iviti: 'ive', - biliti: 'ble', - logi: 'log' - }; - - var step3list = { - icate: 'ic', - ative: '', - alize: 'al', - iciti: 'ic', - ical: 'ic', - ful: '', - ness: '' - }; - - var c = "[^aeiou]"; // consonant - var v = "[aeiouy]"; // vowel - var C = c + "[^aeiouy]*"; // consonant sequence - var V = v + "[aeiou]*"; // vowel sequence - - var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - - - -/** - * Simple result scoring code. - */ -var Scorer = { - // Implement the following function to further tweak the score for each result - // The function takes a result array [filename, title, anchor, descr, score] - // and returns the new score. - /* - score: function(result) { - return result[4]; - }, - */ - - // query matches the full name of an object - objNameMatch: 11, - // or matches in the last dotted part of the object name - objPartialMatch: 6, - // Additive scores depending on the priority of the object - objPrio: {0: 15, // used to be importantResults - 1: 5, // used to be objectResults - 2: -5}, // used to be unimportantResults - // Used when the priority is not in the mapping. - objPrioDefault: 0, - - // query found in title - title: 15, - // query found in terms - term: 5 -}; - - -/** - * Search Module - */ -var Search = { - - _index : null, - _queued_query : null, - _pulse_status : -1, - - init : function() { - var params = $.getQueryParameters(); - if (params.q) { - var query = params.q[0]; - $('input[name="q"]')[0].value = query; - this.performSearch(query); - } - }, - - loadIndex : function(url) { - $.ajax({type: "GET", url: url, data: null, - dataType: "script", cache: true, - complete: function(jqxhr, textstatus) { - if (textstatus != "success") { - document.getElementById("searchindexloader").src = url; - } - }}); - }, - - setIndex : function(index) { - var q; - this._index = index; - if ((q = this._queued_query) !== null) { - this._queued_query = null; - Search.query(q); - } - }, - - hasIndex : function() { - return this._index !== null; - }, - - deferQuery : function(query) { - this._queued_query = query; - }, - - stopPulse : function() { - this._pulse_status = 0; - }, - - startPulse : function() { - if (this._pulse_status >= 0) - return; - function pulse() { - var i; - Search._pulse_status = (Search._pulse_status + 1) % 4; - var dotString = ''; - for (i = 0; i < Search._pulse_status; i++) - dotString += '.'; - Search.dots.text(dotString); - if (Search._pulse_status > -1) - window.setTimeout(pulse, 500); - } - pulse(); - }, - - /** - * perform a search for something (or wait until index is loaded) - */ - performSearch : function(query) { - // create the required interface elements - this.out = $('#search-results'); - this.title = $('

' + _('Searching') + '

').appendTo(this.out); - this.dots = $('').appendTo(this.title); - this.status = $('

').appendTo(this.out); - this.output = $('
'); - } - // Prettify the comment rating. - comment.pretty_rating = comment.rating + ' point' + - (comment.rating == 1 ? '' : 's'); - // Make a class (for displaying not yet moderated comments differently) - comment.css_class = comment.displayed ? '' : ' moderate'; - // Create a div for this comment. - var context = $.extend({}, opts, comment); - var div = $(renderTemplate(commentTemplate, context)); - - // If the user has voted on this comment, highlight the correct arrow. - if (comment.vote) { - var direction = (comment.vote == 1) ? 'u' : 'd'; - div.find('#' + direction + 'v' + comment.id).hide(); - div.find('#' + direction + 'u' + comment.id).show(); - } - - if (opts.moderator || comment.text != '[deleted]') { - div.find('a.reply').show(); - if (comment.proposal_diff) - div.find('#sp' + comment.id).show(); - if (opts.moderator && !comment.displayed) - div.find('#cm' + comment.id).show(); - if (opts.moderator || (opts.username == comment.username)) - div.find('#dc' + comment.id).show(); - } - return div; - } - - /** - * A simple template renderer. Placeholders such as <%id%> are replaced - * by context['id'] with items being escaped. Placeholders such as <#id#> - * are not escaped. - */ - function renderTemplate(template, context) { - var esc = $(document.createElement('div')); - - function handle(ph, escape) { - var cur = context; - $.each(ph.split('.'), function() { - cur = cur[this]; - }); - return escape ? esc.text(cur || "").html() : cur; - } - - return template.replace(/<([%#])([\w\.]*)\1>/g, function() { - return handle(arguments[2], arguments[1] == '%' ? true : false); - }); - } - - /** Flash an error message briefly. */ - function showError(message) { - $(document.createElement('div')).attr({'class': 'popup-error'}) - .append($(document.createElement('div')) - .attr({'class': 'error-message'}).text(message)) - .appendTo('body') - .fadeIn("slow") - .delay(2000) - .fadeOut("slow"); - } - - /** Add a link the user uses to open the comments popup. */ - $.fn.comment = function() { - return this.each(function() { - var id = $(this).attr('id').substring(1); - var count = COMMENT_METADATA[id]; - var title = count + ' comment' + (count == 1 ? '' : 's'); - var image = count > 0 ? opts.commentBrightImage : opts.commentImage; - var addcls = count == 0 ? ' nocomment' : ''; - $(this) - .append( - $(document.createElement('a')).attr({ - href: '#', - 'class': 'sphinx-comment-open' + addcls, - id: 'ao' + id - }) - .append($(document.createElement('img')).attr({ - src: image, - alt: 'comment', - title: title - })) - .click(function(event) { - event.preventDefault(); - show($(this).attr('id').substring(2)); - }) - ) - .append( - $(document.createElement('a')).attr({ - href: '#', - 'class': 'sphinx-comment-close hidden', - id: 'ah' + id - }) - .append($(document.createElement('img')).attr({ - src: opts.closeCommentImage, - alt: 'close', - title: 'close' - })) - .click(function(event) { - event.preventDefault(); - hide($(this).attr('id').substring(2)); - }) - ); - }); - }; - - var opts = { - processVoteURL: '/_process_vote', - addCommentURL: '/_add_comment', - getCommentsURL: '/_get_comments', - acceptCommentURL: '/_accept_comment', - deleteCommentURL: '/_delete_comment', - commentImage: '/static/_static/comment.png', - closeCommentImage: '/static/_static/comment-close.png', - loadingImage: '/static/_static/ajax-loader.gif', - commentBrightImage: '/static/_static/comment-bright.png', - upArrow: '/static/_static/up.png', - downArrow: '/static/_static/down.png', - upArrowPressed: '/static/_static/up-pressed.png', - downArrowPressed: '/static/_static/down-pressed.png', - voting: false, - moderator: false - }; - - if (typeof COMMENT_OPTIONS != "undefined") { - opts = jQuery.extend(opts, COMMENT_OPTIONS); - } - - var popupTemplate = '\ -
\ -

\ - Sort by:\ - best rated\ - newest\ - oldest\ -

\ -
Comments
\ -
\ - loading comments...
\ -
    \ -
    \ -

    Add a comment\ - (markup):

    \ -
    \ - reStructured text markup: *emph*, **strong**, \ - ``code``, \ - code blocks: :: and an indented block after blank line
    \ -
    \ - \ -

    \ - \ - Propose a change ▹\ - \ - \ - Propose a change ▿\ - \ -

    \ - \ - \ - \ - \ - \ -
    \ -
    '; - - var commentTemplate = '\ -
    \ -
    \ -
    \ - \ - \ - \ - \ - \ - \ -
    \ -
    \ - \ - \ - \ - \ - \ - \ -
    \ -
    \ -
    \ -

    \ - <%username%>\ - <%pretty_rating%>\ - <%time.delta%>\ -

    \ -
    <#text#>
    \ -

    \ - \ - reply ▿\ - proposal ▹\ - proposal ▿\ - \ - \ -

    \ -
    \
    -<#proposal_diff#>\
    -        
    \ -
      \ -
      \ -
      \ -
      \ - '; - - var replyTemplate = '\ -
    • \ -
      \ -
      \ - \ - \ - \ - \ - \ - \ -
      \ -
    • '; - - $(document).ready(function() { - init(); - }); -})(jQuery); - -$(document).ready(function() { - // add comment anchors for all paragraphs that are commentable - $('.sphinx-has-comment').comment(); - - // highlight search words in search results - $("div.context").each(function() { - var params = $.getQueryParameters(); - var terms = (params.q) ? params.q[0].split(/\s+/) : []; - var result = $(this); - $.each(terms, function() { - result.highlightText(this.toLowerCase(), 'highlighted'); - }); - }); - - // directly open comment window if requested - var anchor = document.location.hash; - if (anchor.substring(0, 9) == '#comment-') { - $('#ao' + anchor.substring(9)).click(); - document.location.hash = '#s' + anchor.substring(9); - } -}); diff --git a/docs/build/html/genindex.html b/docs/build/html/genindex.html deleted file mode 100644 index 2f3a278b..00000000 --- a/docs/build/html/genindex.html +++ /dev/null @@ -1,358 +0,0 @@ - - - - - - - - - Index — PubNub 3.7.6 documentation - - - - - - - - - - - - - -
      -
      -
      -
      - - -

      Index

      - -
      - A - | D - | E - | G - | H - | P - | R - | S - | T - | U - -
      -

      A

      -
      - -
      - -
      audit() (Pubnub.Pubnub method) -
      - -
      - -
      (Pubnub.PubnubTornado method) -
      - - -
      (Pubnub.PubnubTwisted method) -
      - -
      -
      - -

      D

      - - -
      - -
      decrypt() (Pubnub.Pubnub method) -
      - -
      - -
      (Pubnub.PubnubTornado method) -
      - - -
      (Pubnub.PubnubTwisted method) -
      - -
      -
      - -

      E

      - - -
      - -
      encrypt() (Pubnub.Pubnub method) -
      - -
      - -
      (Pubnub.PubnubTornado method) -
      - - -
      (Pubnub.PubnubTwisted method) -
      - -
      -
      - -

      G

      - - -
      - -
      grant() (Pubnub.Pubnub method) -
      - -
      - -
      (Pubnub.PubnubTornado method) -
      - - -
      (Pubnub.PubnubTwisted method) -
      - -
      -
      - -

      H

      - - - -
      - -
      here_now() (Pubnub.Pubnub method) -
      - -
      - -
      (Pubnub.PubnubTornado method) -
      - - -
      (Pubnub.PubnubTwisted method) -
      - -
      -
      - -
      history() (Pubnub.Pubnub method) -
      - -
      - -
      (Pubnub.PubnubTornado method) -
      - - -
      (Pubnub.PubnubTwisted method) -
      - -
      -
      - -

      P

      - - - -
      - -
      presence() (Pubnub.Pubnub method) -
      - -
      - -
      (Pubnub.PubnubTornado method) -
      - - -
      (Pubnub.PubnubTwisted method) -
      - -
      - -
      publish() (Pubnub.Pubnub method) -
      - -
      - -
      (Pubnub.PubnubTornado method) -
      - - -
      (Pubnub.PubnubTwisted method) -
      - -
      - -
      Pubnub (class in Pubnub) -
      - -
      - -
      (module) -
      - -
      -
      - -
      PubnubTornado (class in Pubnub) -
      - - -
      PubnubTwisted (class in Pubnub) -
      - -
      - -

      R

      - - -
      - -
      revoke() (Pubnub.Pubnub method) -
      - -
      - -
      (Pubnub.PubnubTornado method) -
      - - -
      (Pubnub.PubnubTwisted method) -
      - -
      -
      - -

      S

      - - -
      - -
      subscribe() (Pubnub.Pubnub method) -
      - -
      - -
      (Pubnub.PubnubTornado method) -
      - - -
      (Pubnub.PubnubTwisted method) -
      - -
      -
      - -

      T

      - - -
      - -
      time() (Pubnub.Pubnub method) -
      - -
      - -
      (Pubnub.PubnubTornado method) -
      - - -
      (Pubnub.PubnubTwisted method) -
      - -
      -
      - -

      U

      - - -
      - -
      unsubscribe() (Pubnub.Pubnub method) -
      - -
      - -
      (Pubnub.PubnubTornado method) -
      - - -
      (Pubnub.PubnubTwisted method) -
      - -
      -
      - - - - - - -
      -
      - - - - - -
      -
      -
      - - - - - \ No newline at end of file diff --git a/docs/build/html/index.html b/docs/build/html/index.html deleted file mode 100644 index 5b29831f..00000000 --- a/docs/build/html/index.html +++ /dev/null @@ -1,1555 +0,0 @@ - - - - - - - - Welcome to PubNub’s documentation! — PubNub 3.7.6 documentation - - - - - - - - - - - - - -
      -
      -
      -
      - -
      -

      Welcome to PubNub’s documentation!

      -
      -
        -
      -
      -
      -

      Pubnub

      -
      -
      -class Pubnub.Pubnub(publish_key, subscribe_key, secret_key=None, cipher_key=None, auth_key=None, ssl_on=False, origin='pubsub.pubnub.com', uuid=None, pooling=True, daemon=False, pres_uuid=None)
      -
      -
      -audit(channel=None, auth_key=None, callback=None, error=None)
      -

      Method for fetching permissions from pubnub servers.

      -

      This method provides a mechanism to reveal existing PubNub Access Manager attributes -for any combination of subscribe_key, channel and auth_key.

      -
      -
      Args:
      -
      -
      channel: (string) (optional)
      -
      Specifies channel name to return PAM -attributes optionally in combination with auth_key. -If channel is not specified, results for all channels -associated with subscribe_key are returned. -If auth_key is not specified, it is possible to return -results for a comma separated list of channels.
      -
      auth_key: (string) (optional)
      -
      Specifies the auth_key to return PAM attributes for. -If only a single channel is specified, it is possible to return -results for a comma separated list of auth_keys.
      -
      callback: (function) (optional)
      -
      A callback method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      error: (function) (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Returns a dict in sync mode i.e. when callback argument is not given -The dict returned contains values with keys ‘message’ and ‘payload’

      -

      Sample Response -{

      -
      -

      “message”:”Success”, -“payload”:{

      -
      -
      -
      “channels”:{
      -
      -
      “my_channel”:{
      -
      “auths”:{“my_ro_authkey”:{“r”:1,”w”:0}, -“my_rw_authkey”:{“r”:0,”w”:1}, -“my_admin_authkey”:{“r”:1,”w”:1}
      -
      -

      }

      -
      -
      -

      }

      -
      -

      },

      -
      -

      }

      -
      -
      -

      Usage:

      -
      -
      pubnub.audit (‘my_channel’); # Sync Mode
      -
      - -
      -
      -decrypt(message)
      -

      Method for decrypting data.

      -

      This method takes ciphertext as input and returns decrypted data. -This need not be called directly as enncryption/decryption is -taken care of transparently by Pubnub class if cipher key is -provided at time of initializing pubnub object

      -
      -
      Args:
      -
      message: Message to be decrypted.
      -
      Returns:
      -
      Returns decrypted message if cipher key is set
      -
      -
      - -
      -
      -encrypt(message)
      -

      Method for encrypting data.

      -

      This method takes plaintext as input and returns encrypted data. -This need not be called directly as enncryption/decryption is -taken care of transparently by Pubnub class if cipher key is -provided at time of initializing pubnub object

      -
      -
      Args:
      -
      message: Message to be encrypted.
      -
      Returns:
      -
      Returns encrypted message if cipher key is set
      -
      -
      - -
      -
      -grant(channel=None, auth_key=False, read=True, write=True, ttl=5, callback=None, error=None)
      -

      Method for granting permissions.

      -

      This function establishes subscribe and/or write permissions for -PubNub Access Manager (PAM) by setting the read or write attribute -to true. A grant with read or write set to false (or not included) -will revoke any previous grants with read or write set to true.

      -
      -
      Permissions can be applied to any one of three levels:
      -
        -
      1. Application level privileges are based on subscribe_key applying to all associated channels.
      2. -
      3. Channel level privileges are based on a combination of subscribe_key and channel name.
      4. -
      5. User level privileges are based on the combination of subscribe_key, channel and auth_key.
      6. -
      -
      -
      Args:
      -
      -
      channel: (string) (optional)
      -
      Specifies channel name to grant permissions to. -If channel is not specified, the grant applies to all -channels associated with the subscribe_key. If auth_key -is not specified, it is possible to grant permissions to -multiple channels simultaneously by specifying the channels -as a comma separated list.
      -
      auth_key: (string) (optional)
      -
      Specifies auth_key to grant permissions to. -It is possible to specify multiple auth_keys as comma -separated list in combination with a single channel name. -If auth_key is provided as the special-case value “null” -(or included in a comma-separated list, eg. “null,null,abc”), -a new auth_key will be generated and returned for each “null” value.
      -
      read: (boolean) (default: True)
      -
      Read permissions are granted by setting to True. -Read permissions are removed by setting to False.
      -
      write: (boolean) (default: True)
      -
      Write permissions are granted by setting to true. -Write permissions are removed by setting to false.
      -
      ttl: (int) (default: 1440 i.e 24 hrs)
      -
      Time in minutes for which granted permissions are valid. -Max is 525600 , Min is 1. -Setting ttl to 0 will apply the grant indefinitely.
      -
      callback: (function) (optional)
      -
      A callback method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      error: (function) (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Returns a dict in sync mode i.e. when callback argument is not given -The dict returned contains values with keys ‘message’ and ‘payload’

      -

      Sample Response: -{

      -
      -

      “message”:”Success”, -“payload”:{

      -
      -

      “ttl”:5, -“auths”:{

      -
      -
      “my_ro_authkey”:{“r”:1,”w”:0}
      -

      }, -“subscribe_key”:”my_subkey”, -“level”:”user”, -“channel”:”my_channel”

      -
      -

      }

      -
      -

      }

      -
      -
      -
      - -
      -
      -here_now(channel, callback=None, error=None)
      -

      Get here now data.

      -

      You can obtain information about the current state of a channel including -a list of unique user-ids currently subscribed to the channel and the total -occupancy count of the channel by calling the here_now() function in your -application.

      -
      -
      Args:
      -
      -
      channel: (string) (optional)
      -
      Specifies the channel name to return occupancy results. -If channel is not provided, here_now will return data for all channels.
      -
      callback: (optional)
      -
      A callback method should be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      error: (optional)
      -
      Optional variable. An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Sync Mode: list -Async Mode: None

      -

      Response Format:

      -

      The here_now() method returns a list of uuid s currently subscribed to the channel.

      -

      uuids:[“String”,”String”, ... ,”String”] - List of UUIDs currently subscribed to the channel.

      -

      occupancy: Number - Total current occupancy of the channel.

      -

      Example Response: -{

      -
      -

      occupancy: 4, -uuids: [

      -
      -
      ‘123123234t234f34fq3dq’, -‘143r34f34t34fq34q34q3’, -‘23f34d3f4rq34r34rq23q’, -‘w34tcw45t45tcw435tww3’,
      -

      ]

      -
      -

      }

      -
      -
      -
      - -
      -
      -history(channel, count=100, reverse=False, start=None, end=None, callback=None, error=None)
      -

      This method fetches historical messages of a channel.

      -

      PubNub Storage/Playback Service provides real-time access to an unlimited -history for all messages published to PubNub. Stored messages are replicated -across multiple availability zones in several geographical data center -locations. Stored messages can be encrypted with AES-256 message encryption -ensuring that they are not readable while stored on PubNub’s network.

      -

      It is possible to control how messages are returned and in what order, -for example you can:

      -
      -

      Return messages in the order newest to oldest (default behavior).

      -

      Return messages in the order oldest to newest by setting reverse to true.

      -

      Page through results by providing a start or end time token.

      -

      Retrieve a “slice” of the time line by providing both a start and end time token.

      -

      Limit the number of messages to a specific quantity using the count parameter.

      -
      -
      -
      Args:
      -
      -
      channel: (string)
      -
      Specifies channel to return history messages from
      -
      count: (int) (default: 100)
      -
      Specifies the number of historical messages to return
      -
      callback: (optional)
      -
      A callback method should be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      error: (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Returns a list in sync mode i.e. when callback argument is not given

      -
      -
      Sample Response:
      -
      [[“Pub1”,”Pub2”,”Pub3”,”Pub4”,”Pub5”],13406746729185766,13406746845892666]
      -
      -
      -
      -
      - -
      -
      -presence(channel, callback, error=None)
      -

      Subscribe to presence data on a channel.

      -
      -
      Only works in async mode
      -
      -
      Args:
      -

      channel: Channel name ( string ) on which to publish message -callback: A callback method should be passed to the method.

      -
      -
      If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      error: Optional variable. An error method can be passed to the method.
      -
      If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -
      None
      -
      -
      - -
      -
      -publish(channel, message, callback=None, error=None)
      -

      Publishes data on a channel.

      -

      The publish() method is used to send a message to all subscribers of a channel. -To publish a message you must first specify a valid publish_key at initialization. -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 ssl to True during initialization.

      -

      Published messages can also be encrypted with AES-256 simply by specifying a cipher_key -during initialization.

      -
      -
      Args:
      -
      -
      channel: (string)
      -
      Specifies channel name to publish messages to.
      -
      message: (string/int/double/dict/list)
      -
      Message to be published
      -
      callback: (optional)
      -
      A callback method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      error: (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      -
      -
      Returns:
      -

      Sync Mode : list -Async Mode : None

      -

      The function returns the following formatted response:

      -
      -
      [ Number, “Status”, “Time Token”]
      -

      The output below demonstrates the response to a successful call:

      -
      -
      [1,”Sent”,”13769558699541401”]
      -
      -
      -
      - -
      -
      -revoke(channel=None, auth_key=None, ttl=1, callback=None, error=None)
      -

      Method for revoking permissions.

      -
      -
      Args:
      -
      -
      channel: (string) (optional)
      -
      Specifies channel name to revoke permissions to. -If channel is not specified, the revoke applies to all -channels associated with the subscribe_key. If auth_key -is not specified, it is possible to grant permissions to -multiple channels simultaneously by specifying the channels -as a comma separated list.
      -
      auth_key: (string) (optional)
      -
      Specifies auth_key to revoke permissions to. -It is possible to specify multiple auth_keys as comma -separated list in combination with a single channel name. -If auth_key is provided as the special-case value “null” -(or included in a comma-separated list, eg. “null,null,abc”), -a new auth_key will be generated and returned for each “null” value.
      -
      ttl: (int) (default: 1440 i.e 24 hrs)
      -
      Time in minutes for which granted permissions are valid. -Max is 525600 , Min is 1. -Setting ttl to 0 will apply the grant indefinitely.
      -
      callback: (function) (optional)
      -
      A callback method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      error: (function) (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Returns a dict in sync mode i.e. when callback argument is not given -The dict returned contains values with keys ‘message’ and ‘payload’

      -

      Sample Response: -{

      -
      -

      “message”:”Success”, -“payload”:{

      -
      -

      “ttl”:5, -“auths”:{

      -
      -
      “my_authkey”:{“r”:0,”w”:0}
      -

      }, -“subscribe_key”:”my_subkey”, -“level”:”user”, -“channel”:”my_channel”

      -
      -

      }

      -
      -

      }

      -
      -
      -
      - -
      -
      -subscribe(channels, callback, error=None, connect=None, disconnect=None, reconnect=None, sync=False)
      -

      Subscribe to data on a channel.

      -

      This function 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 subscribe_key at -initialization.

      -

      Only works in async mode

      -
      -
      Args:
      -
      -
      channel: (string/list)
      -
      Specifies the channel to subscribe to. It is possible to specify -multiple channels as a comma separated list or andarray.
      -
      callback: (function)
      -
      This callback is called on receiving a message from the channel.
      -
      error: (function) (optional)
      -
      This callback is called on an error event
      -
      connect: (function) (optional)
      -
      This callback is called on a successful connection to the PubNub cloud
      -
      disconnect: (function) (optional)
      -
      This callback is called on client disconnect from the PubNub cloud
      -
      reconnect: (function) (optional)
      -
      This callback is called on successfully re-connecting to the PubNub cloud
      -
      -
      -
      Returns:
      -
      None
      -
      -
      - -
      -
      -time(callback=None)
      -

      This function will return a 17 digit precision Unix epoch.

      -

      Args:

      -
      -
      -
      callback: (optional)
      -
      A callback method should be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      -
      Returns:
      -

      Returns a 17 digit number in sync mode i.e. when callback argument is not given

      -
      -
      Sample:
      -
      13769501243685161
      -
      -
      -
      -
      - -
      -
      -unsubscribe(channel)
      -
      -
      Subscribe to presence data on a channel.
      -
      Only works in async mode
      -
      Args:
      -

      channel: Channel name ( string ) on which to publish message -message: Message to be published ( String / int / double / dict / list ). -callback: A callback method should be passed to the method.

      -
      -
      If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      error: Optional variable. An error method can be passed to the method.
      -
      If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -
      Returns a list in sync mode i.e. when callback argument is not given
      -
      -
      - -
      - -
      -
      -

      PubnubTwisted

      -
      -
      -class Pubnub.PubnubTwisted(publish_key, subscribe_key, secret_key=None, cipher_key=None, auth_key=None, ssl_on=False, origin='pubsub.pubnub.com')
      -
      -
      -audit(channel=None, auth_key=None, callback=None, error=None)
      -

      Method for fetching permissions from pubnub servers.

      -

      This method provides a mechanism to reveal existing PubNub Access Manager attributes -for any combination of subscribe_key, channel and auth_key.

      -
      -
      Args:
      -
      -
      channel: (string) (optional)
      -
      Specifies channel name to return PAM -attributes optionally in combination with auth_key. -If channel is not specified, results for all channels -associated with subscribe_key are returned. -If auth_key is not specified, it is possible to return -results for a comma separated list of channels.
      -
      auth_key: (string) (optional)
      -
      Specifies the auth_key to return PAM attributes for. -If only a single channel is specified, it is possible to return -results for a comma separated list of auth_keys.
      -
      callback: (function) (optional)
      -
      A callback method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      error: (function) (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Returns a dict in sync mode i.e. when callback argument is not given -The dict returned contains values with keys ‘message’ and ‘payload’

      -

      Sample Response -{

      -
      -

      “message”:”Success”, -“payload”:{

      -
      -
      -
      “channels”:{
      -
      -
      “my_channel”:{
      -
      “auths”:{“my_ro_authkey”:{“r”:1,”w”:0}, -“my_rw_authkey”:{“r”:0,”w”:1}, -“my_admin_authkey”:{“r”:1,”w”:1}
      -
      -

      }

      -
      -
      -

      }

      -
      -

      },

      -
      -

      }

      -
      -
      -

      Usage:

      -
      -
      pubnub.audit (‘my_channel’); # Sync Mode
      -
      - -
      -
      -decrypt(message)
      -

      Method for decrypting data.

      -

      This method takes ciphertext as input and returns decrypted data. -This need not be called directly as enncryption/decryption is -taken care of transparently by Pubnub class if cipher key is -provided at time of initializing pubnub object

      -
      -
      Args:
      -
      message: Message to be decrypted.
      -
      Returns:
      -
      Returns decrypted message if cipher key is set
      -
      -
      - -
      -
      -encrypt(message)
      -

      Method for encrypting data.

      -

      This method takes plaintext as input and returns encrypted data. -This need not be called directly as enncryption/decryption is -taken care of transparently by Pubnub class if cipher key is -provided at time of initializing pubnub object

      -
      -
      Args:
      -
      message: Message to be encrypted.
      -
      Returns:
      -
      Returns encrypted message if cipher key is set
      -
      -
      - -
      -
      -grant(channel=None, auth_key=False, read=True, write=True, ttl=5, callback=None, error=None)
      -

      Method for granting permissions.

      -

      This function establishes subscribe and/or write permissions for -PubNub Access Manager (PAM) by setting the read or write attribute -to true. A grant with read or write set to false (or not included) -will revoke any previous grants with read or write set to true.

      -
      -
      Permissions can be applied to any one of three levels:
      -
        -
      1. Application level privileges are based on subscribe_key applying to all associated channels.
      2. -
      3. Channel level privileges are based on a combination of subscribe_key and channel name.
      4. -
      5. User level privileges are based on the combination of subscribe_key, channel and auth_key.
      6. -
      -
      -
      Args:
      -
      -
      channel: (string) (optional)
      -
      Specifies channel name to grant permissions to. -If channel is not specified, the grant applies to all -channels associated with the subscribe_key. If auth_key -is not specified, it is possible to grant permissions to -multiple channels simultaneously by specifying the channels -as a comma separated list.
      -
      auth_key: (string) (optional)
      -
      Specifies auth_key to grant permissions to. -It is possible to specify multiple auth_keys as comma -separated list in combination with a single channel name. -If auth_key is provided as the special-case value “null” -(or included in a comma-separated list, eg. “null,null,abc”), -a new auth_key will be generated and returned for each “null” value.
      -
      read: (boolean) (default: True)
      -
      Read permissions are granted by setting to True. -Read permissions are removed by setting to False.
      -
      write: (boolean) (default: True)
      -
      Write permissions are granted by setting to true. -Write permissions are removed by setting to false.
      -
      ttl: (int) (default: 1440 i.e 24 hrs)
      -
      Time in minutes for which granted permissions are valid. -Max is 525600 , Min is 1. -Setting ttl to 0 will apply the grant indefinitely.
      -
      callback: (function) (optional)
      -
      A callback method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      error: (function) (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Returns a dict in sync mode i.e. when callback argument is not given -The dict returned contains values with keys ‘message’ and ‘payload’

      -

      Sample Response: -{

      -
      -

      “message”:”Success”, -“payload”:{

      -
      -

      “ttl”:5, -“auths”:{

      -
      -
      “my_ro_authkey”:{“r”:1,”w”:0}
      -

      }, -“subscribe_key”:”my_subkey”, -“level”:”user”, -“channel”:”my_channel”

      -
      -

      }

      -
      -

      }

      -
      -
      -
      - -
      -
      -here_now(channel, callback=None, error=None)
      -

      Get here now data.

      -

      You can obtain information about the current state of a channel including -a list of unique user-ids currently subscribed to the channel and the total -occupancy count of the channel by calling the here_now() function in your -application.

      -
      -
      Args:
      -
      -
      channel: (string) (optional)
      -
      Specifies the channel name to return occupancy results. -If channel is not provided, here_now will return data for all channels.
      -
      callback: (optional)
      -
      A callback method should be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      error: (optional)
      -
      Optional variable. An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Sync Mode: list -Async Mode: None

      -

      Response Format:

      -

      The here_now() method returns a list of uuid s currently subscribed to the channel.

      -

      uuids:[“String”,”String”, ... ,”String”] - List of UUIDs currently subscribed to the channel.

      -

      occupancy: Number - Total current occupancy of the channel.

      -

      Example Response: -{

      -
      -

      occupancy: 4, -uuids: [

      -
      -
      ‘123123234t234f34fq3dq’, -‘143r34f34t34fq34q34q3’, -‘23f34d3f4rq34r34rq23q’, -‘w34tcw45t45tcw435tww3’,
      -

      ]

      -
      -

      }

      -
      -
      -
      - -
      -
      -history(channel, count=100, reverse=False, start=None, end=None, callback=None, error=None)
      -

      This method fetches historical messages of a channel.

      -

      PubNub Storage/Playback Service provides real-time access to an unlimited -history for all messages published to PubNub. Stored messages are replicated -across multiple availability zones in several geographical data center -locations. Stored messages can be encrypted with AES-256 message encryption -ensuring that they are not readable while stored on PubNub’s network.

      -

      It is possible to control how messages are returned and in what order, -for example you can:

      -
      -

      Return messages in the order newest to oldest (default behavior).

      -

      Return messages in the order oldest to newest by setting reverse to true.

      -

      Page through results by providing a start or end time token.

      -

      Retrieve a “slice” of the time line by providing both a start and end time token.

      -

      Limit the number of messages to a specific quantity using the count parameter.

      -
      -
      -
      Args:
      -
      -
      channel: (string)
      -
      Specifies channel to return history messages from
      -
      count: (int) (default: 100)
      -
      Specifies the number of historical messages to return
      -
      callback: (optional)
      -
      A callback method should be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      error: (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Returns a list in sync mode i.e. when callback argument is not given

      -
      -
      Sample Response:
      -
      [[“Pub1”,”Pub2”,”Pub3”,”Pub4”,”Pub5”],13406746729185766,13406746845892666]
      -
      -
      -
      -
      - -
      -
      -presence(channel, callback, error=None)
      -

      Subscribe to presence data on a channel.

      -
      -
      Only works in async mode
      -
      -
      Args:
      -

      channel: Channel name ( string ) on which to publish message -callback: A callback method should be passed to the method.

      -
      -
      If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      error: Optional variable. An error method can be passed to the method.
      -
      If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -
      None
      -
      -
      - -
      -
      -publish(channel, message, callback=None, error=None)
      -

      Publishes data on a channel.

      -

      The publish() method is used to send a message to all subscribers of a channel. -To publish a message you must first specify a valid publish_key at initialization. -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 ssl to True during initialization.

      -

      Published messages can also be encrypted with AES-256 simply by specifying a cipher_key -during initialization.

      -
      -
      Args:
      -
      -
      channel: (string)
      -
      Specifies channel name to publish messages to.
      -
      message: (string/int/double/dict/list)
      -
      Message to be published
      -
      callback: (optional)
      -
      A callback method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      error: (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      -
      -
      Returns:
      -

      Sync Mode : list -Async Mode : None

      -

      The function returns the following formatted response:

      -
      -
      [ Number, “Status”, “Time Token”]
      -

      The output below demonstrates the response to a successful call:

      -
      -
      [1,”Sent”,”13769558699541401”]
      -
      -
      -
      - -
      -
      -revoke(channel=None, auth_key=None, ttl=1, callback=None, error=None)
      -

      Method for revoking permissions.

      -
      -
      Args:
      -
      -
      channel: (string) (optional)
      -
      Specifies channel name to revoke permissions to. -If channel is not specified, the revoke applies to all -channels associated with the subscribe_key. If auth_key -is not specified, it is possible to grant permissions to -multiple channels simultaneously by specifying the channels -as a comma separated list.
      -
      auth_key: (string) (optional)
      -
      Specifies auth_key to revoke permissions to. -It is possible to specify multiple auth_keys as comma -separated list in combination with a single channel name. -If auth_key is provided as the special-case value “null” -(or included in a comma-separated list, eg. “null,null,abc”), -a new auth_key will be generated and returned for each “null” value.
      -
      ttl: (int) (default: 1440 i.e 24 hrs)
      -
      Time in minutes for which granted permissions are valid. -Max is 525600 , Min is 1. -Setting ttl to 0 will apply the grant indefinitely.
      -
      callback: (function) (optional)
      -
      A callback method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      error: (function) (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Returns a dict in sync mode i.e. when callback argument is not given -The dict returned contains values with keys ‘message’ and ‘payload’

      -

      Sample Response: -{

      -
      -

      “message”:”Success”, -“payload”:{

      -
      -

      “ttl”:5, -“auths”:{

      -
      -
      “my_authkey”:{“r”:0,”w”:0}
      -

      }, -“subscribe_key”:”my_subkey”, -“level”:”user”, -“channel”:”my_channel”

      -
      -

      }

      -
      -

      }

      -
      -
      -
      - -
      -
      -subscribe(channels, callback, error=None, connect=None, disconnect=None, reconnect=None, sync=False)
      -

      Subscribe to data on a channel.

      -

      This function 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 subscribe_key at -initialization.

      -

      Only works in async mode

      -
      -
      Args:
      -
      -
      channel: (string/list)
      -
      Specifies the channel to subscribe to. It is possible to specify -multiple channels as a comma separated list or andarray.
      -
      callback: (function)
      -
      This callback is called on receiving a message from the channel.
      -
      error: (function) (optional)
      -
      This callback is called on an error event
      -
      connect: (function) (optional)
      -
      This callback is called on a successful connection to the PubNub cloud
      -
      disconnect: (function) (optional)
      -
      This callback is called on client disconnect from the PubNub cloud
      -
      reconnect: (function) (optional)
      -
      This callback is called on successfully re-connecting to the PubNub cloud
      -
      -
      -
      Returns:
      -
      None
      -
      -
      - -
      -
      -time(callback=None)
      -

      This function will return a 17 digit precision Unix epoch.

      -

      Args:

      -
      -
      -
      callback: (optional)
      -
      A callback method should be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      -
      Returns:
      -

      Returns a 17 digit number in sync mode i.e. when callback argument is not given

      -
      -
      Sample:
      -
      13769501243685161
      -
      -
      -
      -
      - -
      -
      -unsubscribe(channel)
      -
      -
      Subscribe to presence data on a channel.
      -
      Only works in async mode
      -
      Args:
      -

      channel: Channel name ( string ) on which to publish message -message: Message to be published ( String / int / double / dict / list ). -callback: A callback method should be passed to the method.

      -
      -
      If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      error: Optional variable. An error method can be passed to the method.
      -
      If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -
      Returns a list in sync mode i.e. when callback argument is not given
      -
      -
      - -
      - -
      -
      -

      PubnubTornado

      -
      -
      -class Pubnub.PubnubTornado(publish_key, subscribe_key, secret_key=False, cipher_key=False, auth_key=False, ssl_on=False, origin='pubsub.pubnub.com')
      -
      -
      -audit(channel=None, auth_key=None, callback=None, error=None)
      -

      Method for fetching permissions from pubnub servers.

      -

      This method provides a mechanism to reveal existing PubNub Access Manager attributes -for any combination of subscribe_key, channel and auth_key.

      -
      -
      Args:
      -
      -
      channel: (string) (optional)
      -
      Specifies channel name to return PAM -attributes optionally in combination with auth_key. -If channel is not specified, results for all channels -associated with subscribe_key are returned. -If auth_key is not specified, it is possible to return -results for a comma separated list of channels.
      -
      auth_key: (string) (optional)
      -
      Specifies the auth_key to return PAM attributes for. -If only a single channel is specified, it is possible to return -results for a comma separated list of auth_keys.
      -
      callback: (function) (optional)
      -
      A callback method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      error: (function) (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Returns a dict in sync mode i.e. when callback argument is not given -The dict returned contains values with keys ‘message’ and ‘payload’

      -

      Sample Response -{

      -
      -

      “message”:”Success”, -“payload”:{

      -
      -
      -
      “channels”:{
      -
      -
      “my_channel”:{
      -
      “auths”:{“my_ro_authkey”:{“r”:1,”w”:0}, -“my_rw_authkey”:{“r”:0,”w”:1}, -“my_admin_authkey”:{“r”:1,”w”:1}
      -
      -

      }

      -
      -
      -

      }

      -
      -

      },

      -
      -

      }

      -
      -
      -

      Usage:

      -
      -
      pubnub.audit (‘my_channel’); # Sync Mode
      -
      - -
      -
      -decrypt(message)
      -

      Method for decrypting data.

      -

      This method takes ciphertext as input and returns decrypted data. -This need not be called directly as enncryption/decryption is -taken care of transparently by Pubnub class if cipher key is -provided at time of initializing pubnub object

      -
      -
      Args:
      -
      message: Message to be decrypted.
      -
      Returns:
      -
      Returns decrypted message if cipher key is set
      -
      -
      - -
      -
      -encrypt(message)
      -

      Method for encrypting data.

      -

      This method takes plaintext as input and returns encrypted data. -This need not be called directly as enncryption/decryption is -taken care of transparently by Pubnub class if cipher key is -provided at time of initializing pubnub object

      -
      -
      Args:
      -
      message: Message to be encrypted.
      -
      Returns:
      -
      Returns encrypted message if cipher key is set
      -
      -
      - -
      -
      -grant(channel=None, auth_key=False, read=True, write=True, ttl=5, callback=None, error=None)
      -

      Method for granting permissions.

      -

      This function establishes subscribe and/or write permissions for -PubNub Access Manager (PAM) by setting the read or write attribute -to true. A grant with read or write set to false (or not included) -will revoke any previous grants with read or write set to true.

      -
      -
      Permissions can be applied to any one of three levels:
      -
        -
      1. Application level privileges are based on subscribe_key applying to all associated channels.
      2. -
      3. Channel level privileges are based on a combination of subscribe_key and channel name.
      4. -
      5. User level privileges are based on the combination of subscribe_key, channel and auth_key.
      6. -
      -
      -
      Args:
      -
      -
      channel: (string) (optional)
      -
      Specifies channel name to grant permissions to. -If channel is not specified, the grant applies to all -channels associated with the subscribe_key. If auth_key -is not specified, it is possible to grant permissions to -multiple channels simultaneously by specifying the channels -as a comma separated list.
      -
      auth_key: (string) (optional)
      -
      Specifies auth_key to grant permissions to. -It is possible to specify multiple auth_keys as comma -separated list in combination with a single channel name. -If auth_key is provided as the special-case value “null” -(or included in a comma-separated list, eg. “null,null,abc”), -a new auth_key will be generated and returned for each “null” value.
      -
      read: (boolean) (default: True)
      -
      Read permissions are granted by setting to True. -Read permissions are removed by setting to False.
      -
      write: (boolean) (default: True)
      -
      Write permissions are granted by setting to true. -Write permissions are removed by setting to false.
      -
      ttl: (int) (default: 1440 i.e 24 hrs)
      -
      Time in minutes for which granted permissions are valid. -Max is 525600 , Min is 1. -Setting ttl to 0 will apply the grant indefinitely.
      -
      callback: (function) (optional)
      -
      A callback method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      error: (function) (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Returns a dict in sync mode i.e. when callback argument is not given -The dict returned contains values with keys ‘message’ and ‘payload’

      -

      Sample Response: -{

      -
      -

      “message”:”Success”, -“payload”:{

      -
      -

      “ttl”:5, -“auths”:{

      -
      -
      “my_ro_authkey”:{“r”:1,”w”:0}
      -

      }, -“subscribe_key”:”my_subkey”, -“level”:”user”, -“channel”:”my_channel”

      -
      -

      }

      -
      -

      }

      -
      -
      -
      - -
      -
      -here_now(channel, callback=None, error=None)
      -

      Get here now data.

      -

      You can obtain information about the current state of a channel including -a list of unique user-ids currently subscribed to the channel and the total -occupancy count of the channel by calling the here_now() function in your -application.

      -
      -
      Args:
      -
      -
      channel: (string) (optional)
      -
      Specifies the channel name to return occupancy results. -If channel is not provided, here_now will return data for all channels.
      -
      callback: (optional)
      -
      A callback method should be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      error: (optional)
      -
      Optional variable. An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Sync Mode: list -Async Mode: None

      -

      Response Format:

      -

      The here_now() method returns a list of uuid s currently subscribed to the channel.

      -

      uuids:[“String”,”String”, ... ,”String”] - List of UUIDs currently subscribed to the channel.

      -

      occupancy: Number - Total current occupancy of the channel.

      -

      Example Response: -{

      -
      -

      occupancy: 4, -uuids: [

      -
      -
      ‘123123234t234f34fq3dq’, -‘143r34f34t34fq34q34q3’, -‘23f34d3f4rq34r34rq23q’, -‘w34tcw45t45tcw435tww3’,
      -

      ]

      -
      -

      }

      -
      -
      -
      - -
      -
      -history(channel, count=100, reverse=False, start=None, end=None, callback=None, error=None)
      -

      This method fetches historical messages of a channel.

      -

      PubNub Storage/Playback Service provides real-time access to an unlimited -history for all messages published to PubNub. Stored messages are replicated -across multiple availability zones in several geographical data center -locations. Stored messages can be encrypted with AES-256 message encryption -ensuring that they are not readable while stored on PubNub’s network.

      -

      It is possible to control how messages are returned and in what order, -for example you can:

      -
      -

      Return messages in the order newest to oldest (default behavior).

      -

      Return messages in the order oldest to newest by setting reverse to true.

      -

      Page through results by providing a start or end time token.

      -

      Retrieve a “slice” of the time line by providing both a start and end time token.

      -

      Limit the number of messages to a specific quantity using the count parameter.

      -
      -
      -
      Args:
      -
      -
      channel: (string)
      -
      Specifies channel to return history messages from
      -
      count: (int) (default: 100)
      -
      Specifies the number of historical messages to return
      -
      callback: (optional)
      -
      A callback method should be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      error: (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Returns a list in sync mode i.e. when callback argument is not given

      -
      -
      Sample Response:
      -
      [[“Pub1”,”Pub2”,”Pub3”,”Pub4”,”Pub5”],13406746729185766,13406746845892666]
      -
      -
      -
      -
      - -
      -
      -presence(channel, callback, error=None)
      -

      Subscribe to presence data on a channel.

      -
      -
      Only works in async mode
      -
      -
      Args:
      -

      channel: Channel name ( string ) on which to publish message -callback: A callback method should be passed to the method.

      -
      -
      If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      error: Optional variable. An error method can be passed to the method.
      -
      If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -
      None
      -
      -
      - -
      -
      -publish(channel, message, callback=None, error=None)
      -

      Publishes data on a channel.

      -

      The publish() method is used to send a message to all subscribers of a channel. -To publish a message you must first specify a valid publish_key at initialization. -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 ssl to True during initialization.

      -

      Published messages can also be encrypted with AES-256 simply by specifying a cipher_key -during initialization.

      -
      -
      Args:
      -
      -
      channel: (string)
      -
      Specifies channel name to publish messages to.
      -
      message: (string/int/double/dict/list)
      -
      Message to be published
      -
      callback: (optional)
      -
      A callback method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      error: (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      -
      -
      Returns:
      -

      Sync Mode : list -Async Mode : None

      -

      The function returns the following formatted response:

      -
      -
      [ Number, “Status”, “Time Token”]
      -

      The output below demonstrates the response to a successful call:

      -
      -
      [1,”Sent”,”13769558699541401”]
      -
      -
      -
      - -
      -
      -revoke(channel=None, auth_key=None, ttl=1, callback=None, error=None)
      -

      Method for revoking permissions.

      -
      -
      Args:
      -
      -
      channel: (string) (optional)
      -
      Specifies channel name to revoke permissions to. -If channel is not specified, the revoke applies to all -channels associated with the subscribe_key. If auth_key -is not specified, it is possible to grant permissions to -multiple channels simultaneously by specifying the channels -as a comma separated list.
      -
      auth_key: (string) (optional)
      -
      Specifies auth_key to revoke permissions to. -It is possible to specify multiple auth_keys as comma -separated list in combination with a single channel name. -If auth_key is provided as the special-case value “null” -(or included in a comma-separated list, eg. “null,null,abc”), -a new auth_key will be generated and returned for each “null” value.
      -
      ttl: (int) (default: 1440 i.e 24 hrs)
      -
      Time in minutes for which granted permissions are valid. -Max is 525600 , Min is 1. -Setting ttl to 0 will apply the grant indefinitely.
      -
      callback: (function) (optional)
      -
      A callback method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado
      -
      error: (function) (optional)
      -
      An error method can be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -

      Returns a dict in sync mode i.e. when callback argument is not given -The dict returned contains values with keys ‘message’ and ‘payload’

      -

      Sample Response: -{

      -
      -

      “message”:”Success”, -“payload”:{

      -
      -

      “ttl”:5, -“auths”:{

      -
      -
      “my_authkey”:{“r”:0,”w”:0}
      -

      }, -“subscribe_key”:”my_subkey”, -“level”:”user”, -“channel”:”my_channel”

      -
      -

      }

      -
      -

      }

      -
      -
      -
      - -
      -
      -subscribe(channels, callback, error=None, connect=None, disconnect=None, reconnect=None, sync=False)
      -

      Subscribe to data on a channel.

      -

      This function 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 subscribe_key at -initialization.

      -

      Only works in async mode

      -
      -
      Args:
      -
      -
      channel: (string/list)
      -
      Specifies the channel to subscribe to. It is possible to specify -multiple channels as a comma separated list or andarray.
      -
      callback: (function)
      -
      This callback is called on receiving a message from the channel.
      -
      error: (function) (optional)
      -
      This callback is called on an error event
      -
      connect: (function) (optional)
      -
      This callback is called on a successful connection to the PubNub cloud
      -
      disconnect: (function) (optional)
      -
      This callback is called on client disconnect from the PubNub cloud
      -
      reconnect: (function) (optional)
      -
      This callback is called on successfully re-connecting to the PubNub cloud
      -
      -
      -
      Returns:
      -
      None
      -
      -
      - -
      -
      -time(callback=None)
      -

      This function will return a 17 digit precision Unix epoch.

      -

      Args:

      -
      -
      -
      callback: (optional)
      -
      A callback method should be passed to the method. -If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      -
      Returns:
      -

      Returns a 17 digit number in sync mode i.e. when callback argument is not given

      -
      -
      Sample:
      -
      13769501243685161
      -
      -
      -
      -
      - -
      -
      -unsubscribe(channel)
      -
      -
      Subscribe to presence data on a channel.
      -
      Only works in async mode
      -
      Args:
      -

      channel: Channel name ( string ) on which to publish message -message: Message to be published ( String / int / double / dict / list ). -callback: A callback method should be passed to the method.

      -
      -
      If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      error: Optional variable. An error method can be passed to the method.
      -
      If set, the api works in async mode. -Required argument when working with twisted or tornado .
      -
      -
      -
      Returns:
      -
      Returns a list in sync mode i.e. when callback argument is not given
      -
      -
      - -
      - -
      -
      -
      -

      Indices and tables

      - -
      - - -
      -
      -
      -
      -
      -

      Table Of Contents

      - - -

      This Page

      - - - -
      -
      -
      -
      - - - - \ No newline at end of file diff --git a/docs/build/html/objects.inv b/docs/build/html/objects.inv deleted file mode 100644 index 645f0de5..00000000 Binary files a/docs/build/html/objects.inv and /dev/null differ diff --git a/docs/build/html/py-modindex.html b/docs/build/html/py-modindex.html deleted file mode 100644 index da78366d..00000000 --- a/docs/build/html/py-modindex.html +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - Python Module Index — PubNub 3.7.6 documentation - - - - - - - - - - - - - - - - - - -
      -
      -
      -
      - - -

      Python Module Index

      - -
      - p -
      - - - - - - - -
       
      - p
      - Pubnub -
      - - -
      -
      -
      -
      -
      - - -
      -
      -
      -
      - - - - \ No newline at end of file diff --git a/docs/build/html/search.html b/docs/build/html/search.html deleted file mode 100644 index 6d5ac37e..00000000 --- a/docs/build/html/search.html +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - Search — PubNub 3.7.6 documentation - - - - - - - - - - - - - - - - - - - -
      -
      -
      -
      - -

      Search

      -
      - -

      - Please activate JavaScript to enable the search - functionality. -

      -
      -

      - From here you can search these documents. Enter your search - words into the box below and click "search". Note that the search - function will automatically search for all of the words. Pages - containing fewer words won't appear in the result list. -

      -
      - - - -
      - -
      - -
      - -
      -
      -
      -
      -
      -
      -
      -
      -
      - - - - \ No newline at end of file diff --git a/docs/build/html/searchindex.js b/docs/build/html/searchindex.js deleted file mode 100644 index 0bba8eda..00000000 --- a/docs/build/html/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({envversion:42,terms:{"23f34d3f4rq34r34rq23q":0,all:0,newest:0,my_rw_authkei:0,obtain:0,tcp:0,ssl_on:0,reconnect:0,ttl:0,follow:0,twist:0,simultan:0,callback:0,cipher:0,paramet:0,access:0,onli:0,locat:0,uuid:0,zone:0,how:0,here_now:0,readabl:0,publish_kei:0,send:0,should:0,pub2:0,valid:0,dict:0,appli:0,input:0,sent:0,pubsub:0,real:0,applic:0,digit:0,"return":0,string:0,thei:0,transpar:0,fals:0,ciphertext:0,auth:0,ssl:0,mechan:0,now:0,requir:0,my_channel:0,reveal:0,daemon:0,name:0,specif:0,level:0,revers:0,list:0,geograph:0,privileg:0,server:0,separ:0,provid:0,token:0,api:0,mode:0,contain:0,comma:0,servic:0,set:0,specifi:0,permiss:0,my_admin_authkei:0,multipl:0,sync:0,my_ro_authkei:0,sampl:0,result:0,pass:0,pres_uuid:0,successfulli:0,event:0,special:0,page:0,variabl:0,index:0,what:0,oldest:0,network:0,channel:0,subscribe_kei:0,"while":0,publish:0,cipher_kei:0,current:0,method:0,state:0,pool:0,enncrypt:0,"new":0,get:0,across:0,attribut:0,object:0,kei:0,gener:0,each:0,usag:0,here:0,plaintext:0,base:0,async:0,disconnect:0,my_authkei:0,secret_kei:0,valu:0,care:0,both:0,about:0,output:0,socket:0,success:0,through:0,manag:0,precis:0,"123123234t234f34fq3dq":0,auth_kei:0,my_subkei:0,com:0,first:0,origin:0,arg:0,simpli:0,directli:0,revok:0,slice:0,transit:0,number:0,unix:0,"boolean":0,uniqu:0,ensur:0,total:0,storag:0,your:0,cloud:0,min:0,given:0,from:0,associ:0,doubl:0,three:0,messag:0,avail:0,start:0,call:0,includ:0,subscrib:0,taken:0,unsubscrib:0,store:0,listen:0,"function":0,option:0,presenc:0,search:0,andarrai:0,ani:0,line:0,"true":0,must:0,count:0,replic:0,none:0,retriev:0,possibl:0,"default":0,remov:0,work:0,histor:0,histori:0,below:0,limit:0,can:0,behavior:0,error:0,minut:0,initi:0,fetch:0,connect:0,control:0,payload:0,exampl:0,creat:0,"int":0,dure:0,respons:0,decrypt:0,argument:0,pub3:0,"case":0,pub1:0,exist:0,pub5:0,pub4:0,need:0,w34tcw45t45tcw435tww3:0,"null":0,sever:0,occup:0,end:0,open:0,grant:0,receiv:0,format:0,when:0,write:0,also:0,epoch:0,playback:0,take:0,which:0,indefinit:0,you:0,unlimit:0,singl:0,begin:0,thi:0,tornado:0,max:0,previou:0,"143r34f34t34fq34q34q3":0,statu:0,abc:0,user:0,establish:0,eavesdropp:0,encrypt:0,data:0,"class":0,demonstr:0,audit:0,appropri:0,center:0,read:0,secur:0,quantiti:0,caus:0,inform:0,client:0,combin:0,potenti:0,time:0,pam:0,order:0},objtypes:{"0":"py:module","1":"py:method","2":"py:class"},objnames:{"0":["py","module","Python module"],"1":["py","method","Python method"],"2":["py","class","Python class"]},filenames:["index"],titles:["Welcome to PubNub’s documentation!"],objects:{"":{Pubnub:[0,0,0,"-"]},"Pubnub.PubnubTornado":{audit:[0,1,1,""],revoke:[0,1,1,""],grant:[0,1,1,""],here_now:[0,1,1,""],decrypt:[0,1,1,""],publish:[0,1,1,""],presence:[0,1,1,""],subscribe:[0,1,1,""],unsubscribe:[0,1,1,""],time:[0,1,1,""],encrypt:[0,1,1,""],history:[0,1,1,""]},"Pubnub.PubnubTwisted":{audit:[0,1,1,""],revoke:[0,1,1,""],grant:[0,1,1,""],here_now:[0,1,1,""],presence:[0,1,1,""],decrypt:[0,1,1,""],publish:[0,1,1,""],subscribe:[0,1,1,""],unsubscribe:[0,1,1,""],time:[0,1,1,""],encrypt:[0,1,1,""],history:[0,1,1,""]},Pubnub:{PubnubTornado:[0,2,1,""],Pubnub:[0,2,1,""],PubnubTwisted:[0,2,1,""]},"Pubnub.Pubnub":{audit:[0,1,1,""],revoke:[0,1,1,""],here_now:[0,1,1,""],grant:[0,1,1,""],decrypt:[0,1,1,""],publish:[0,1,1,""],presence:[0,1,1,""],subscribe:[0,1,1,""],unsubscribe:[0,1,1,""],time:[0,1,1,""],encrypt:[0,1,1,""],history:[0,1,1,""]}},titleterms:{welcom:0,pubnub:0,indic:0,pubnubtwist:0,tabl:0,pubnubtornado:0,document:0}}) \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index 8c17afad..00000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,261 +0,0 @@ -# -*- coding: utf-8 -*- -# -# PubNub documentation build configuration file, created by -# sphinx-quickstart on Wed Jun 25 12:50:44 2014. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys -import os -sys.path.insert(0, os.path.abspath('../..')) - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'PubNub' -copyright = u'2014, PubNub Inc.' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '3.7.6' -# The full version, including alpha/beta/rc tags. -release = '3.7.6' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -#html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'PubNubdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'PubNub.tex', u'PubNub Documentation', - u'PubNub Inc.', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'pubnub', u'PubNub Documentation', - [u'PubNub Inc.'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'PubNub', u'PubNub Documentation', - u'PubNub Inc.', 'PubNub', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False diff --git a/docs/source/index.rst b/docs/source/index.rst deleted file mode 100644 index 6b41e896..00000000 --- a/docs/source/index.rst +++ /dev/null @@ -1,35 +0,0 @@ -.. PubNub documentation master file, created by - sphinx-quickstart on Wed Jun 25 12:50:44 2014. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to PubNub's documentation! -================================== - -.. toctree:: - :maxdepth: 5 - -.. automodule:: Pubnub - -Pubnub ---------------------------------- -.. autoclass:: Pubnub - :members: publish, subscribe, subscribe_group, unsubscribe, unsubscribe_group, presence, presence_group, history, here_now, grant, audit, revoke, get_origin, set_origin, get_auth_key, set_auth_key, encrypt, decrypt, time, channel_group_list_namespaces, channel_group_remove_namespace, channel_group_list_groups, channel_group_list_channels, channel_groups_add_channel, channel_group_remove_channel, channel_group_remove_group - -PubnubTwisted ---------------------------------- -.. autoclass:: PubnubTwisted - :members: publish, subscribe, subscribe_group, unsubscribe, unsubscribe_group, presence, presence_group, history, here_now, grant, audit, revoke, get_origin, set_origin, get_auth_key, set_auth_key, encrypt, decrypt, time, channel_group_list_namespaces, channel_group_remove_namespace, channel_group_list_groups, channel_group_list_channels, channel_groups_add_channel, channel_group_remove_channel, channel_group_remove_group - - -PubnubTornado ---------------------------------- -.. autoclass:: PubnubTornado - :members: publish, subscribe, subscribe_group, unsubscribe, unsubscribe_group, presence, presence_group, history, here_now, grant, audit, revoke, get_origin, set_origin, get_auth_key, set_auth_key, encrypt, decrypt, time, channel_group_list_namespaces, channel_group_remove_namespace, channel_group_list_groups, channel_group_list_channels, channel_groups_add_channel, channel_group_remove_channel, channel_group_remove_group - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`search` - diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 00000000..d1f2589f --- /dev/null +++ b/examples/__init__.py @@ -0,0 +1,7 @@ +from pubnub.pnconfiguration import PNConfiguration + +pnconf = PNConfiguration() + +pnconf.subscribe_key = "demo" +pnconf.publish_key = "demo" +pnconf.enable_subscribe = False diff --git a/examples/asyncio/__init__.py b/examples/asyncio/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/asyncio/http/__init__.py b/examples/asyncio/http/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/asyncio/http/app.py b/examples/asyncio/http/app.py new file mode 100644 index 00000000..0fa40890 --- /dev/null +++ b/examples/asyncio/http/app.py @@ -0,0 +1,168 @@ +import asyncio +import json + +import sys +import os + +import aiohttp_cors as aiohttp_cors +from aiohttp import web + +from pubnub import utils +from pubnub.callbacks import SubscribeCallback +from pubnub.enums import PNStatusCategory, PNOperationType +from pubnub.pubnub_asyncio import PubNubAsyncio + +d = os.path.dirname +PUBNUB_ROOT = d(d(d(os.path.dirname(os.path.abspath(__file__))))) +APP_ROOT = d(os.path.abspath(__file__)) +sys.path.append(PUBNUB_ROOT) + + +from pubnub.exceptions import PubNubException +from pubnub.pnconfiguration import PNConfiguration + +pnconf = PNConfiguration() +pnconf.subscribe_key = "sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe" +pnconf.publish_key = "pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52" +pnconf.uuid = "pubnub-demo-api-python-backend" +DEFAULT_CHANNEL = "pubnub_demo_api_python_channel" +EVENTS_CHANNEL = "pubnub_demo_api_python_events" +APP_KEY = utils.uuid() + +loop = asyncio.get_event_loop() +pubnub = PubNubAsyncio(pnconf) + + +def publish_sync(): + return _not_implemented_error({ + "error": "Sync publish not implemented" + }) + + +def app_key_handler(): + return _ok({ + 'app_key': APP_KEY + }) + + +def list_channels_handler(): + return _ok({ + "subscribed_channels": pubnub.get_subscribed_channels() + }) + + +def add_channel_handler(request): + channel = request.GET['channel'] + + if channel is None: + return _internal_server_error({ + "error": "Channel missing" + }) + + try: + pubnub.subscribe().channels(channel).execute() + return _ok({ + "subscribed_channels": pubnub.get_subscribed_channels() + }) + except PubNubException as e: + return _internal_server_error({ + "message": str(e) + }) + + +def remove_channel_handler(request): + channel = request.GET['channel'] + + if channel is None: + return _internal_server_error({ + "error": "Channel missing" + }) + + try: + pubnub.unsubscribe().channels(channel).execute() + return _ok({ + "subscribed_channels": pubnub.get_subscribed_channels() + }) + except PubNubException as e: + return _internal_server_error({ + "message": str(e) + }) + + +def _ok(body): + return _prepare_response(body) + + +def _not_implemented_error(body): + return web.HTTPNotImplemented(body=json.dumps(body).encode('utf-8'), content_type='application/json') + + +def _internal_server_error(body): + return web.HTTPInternalServerError(body=json.dumps(body).encode('utf-8'), content_type='application/json') + + +def _prepare_response(body): + return web.Response(body=json.dumps(body).encode('utf-8'), content_type='application/json') + + +def init_events_transmitter(): + """ + Method transmits status events to the specific channel + :return: + """ + class StatusListener(SubscribeCallback): + def status(self, pubnub, status): + event = "unknown" + + if status.operation == PNOperationType.PNSubscribeOperation \ + and status.category == PNStatusCategory.PNConnectedCategory: + event = "Connect" + elif status.operation == PNOperationType.PNUnsubscribeOperation \ + and status.category == PNStatusCategory.PNAcknowledgmentCategory: + event = "Unsubscribe" + + asyncio.ensure_future(pubnub.publish().channel('status-' + APP_KEY).message({ + "event": event + }).future(), loop=loop) + + def presence(self, pubnub, presence): + pass + + def message(self, pubnub, message): + pass + + listener = StatusListener() + pubnub.add_listener(listener) + + +async def make_app(loop): + app = web.Application() + # (r"/listen", ListenHandler), + + cors = aiohttp_cors.setup(app, defaults={ + "*": aiohttp_cors.ResourceOptions( + allow_credentials=True, + expose_headers="*", + allow_headers="*", + ) + }) + + app.router.add_route('GET', '/app_key', app_key_handler) + app.router.add_route('GET', '/subscription/add', add_channel_handler) + app.router.add_route('GET', '/subscription/remove', remove_channel_handler) + app.router.add_route('GET', '/subscription/list', list_channels_handler) + app.router.add_route('GET', '/publish/sync', publish_sync) + app.router.add_route('GET', '/publish/async', publish_sync) + app.router.add_route('GET', '/publish/async2', publish_sync) + + for route in list(app.router.routes()): + cors.add(route) + + srv = await loop.create_server(app.make_handler(), '0.0.0.0', 8080) + return srv + + +if __name__ == "__main__": + init_events_transmitter() + loop.run_until_complete(make_app(loop)) + loop.run_forever() diff --git a/examples/asyncio/http/requirements.txt b/examples/asyncio/http/requirements.txt new file mode 100644 index 00000000..8d6dd462 --- /dev/null +++ b/examples/asyncio/http/requirements.txt @@ -0,0 +1,3 @@ +aiohttp +aiohttp_cors + diff --git a/examples/logger.py b/examples/logger.py new file mode 100644 index 00000000..6f68aee9 --- /dev/null +++ b/examples/logger.py @@ -0,0 +1,18 @@ +import logging +import sys + +sys.path.append("../") + +import pubnub +from examples import pnconf +from pubnub.pubnub import PubNub + +# Default log-level is ERROR, to override it use pubnub.set_stream_logger helper: +pubnub.set_stream_logger('pubnub', logging.DEBUG, stream=sys.stdout) + +pubnub = PubNub(pnconf) + +pubnub.publish() \ + .channel("logging") \ + .message("hello") \ + .sync() diff --git a/examples/native_threads/__init__.py b/examples/native_threads/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/native_threads/check.py b/examples/native_threads/check.py new file mode 100644 index 00000000..5ce999d7 --- /dev/null +++ b/examples/native_threads/check.py @@ -0,0 +1,59 @@ +from pubnub.callbacks import SubscribeCallback +from pubnub.enums import PNStatusCategory +from pubnub.pnconfiguration import PNConfiguration +from pubnub.pubnub import PubNub + +pnconfig = PNConfiguration() + +pnconfig.subscribe_key = 'sub-c-a41be4e8-b620-11e5-a916-0619f8945a4f' +pnconfig.publish_key = 'pub-c-b525a8c0-3301-432e-a37b-d8fec5583788' +pnconfig.subscribe_key = 'demo' +pnconfig.publish_key = 'demo' + +pubnub = PubNub(pnconfig) + + +def my_publish_callback(envelope, status): + # Check whether request successfully completed or not + if not status.is_error(): + pass # Message successfully published to specified channel. + else: + pass # Handle message publish error. Check 'category' property to find out possible issue + + +# because of which request did fail. +# Request can be resent using: [status retry]; + + +class MySubscribeCallback(SubscribeCallback): + def presence(self, pubnub, presence): + pass # handle incoming presence data + + def status(self, pubnub, status): + print("Status category", status.category) + if status.category == PNStatusCategory.PNUnexpectedDisconnectCategory: + pass # This event happens when radio / connectivity is lost + + elif status.category == PNStatusCategory.PNConnectedCategory: + # Connect event. You can do stuff like publish, and know you'll get it. + # Or just use the connected event to confirm you are subscribed for + # UI / internal notifications, etc + pubnub.publish().channel("someChannel").message("Hi...").async(my_publish_callback) + elif status.category == PNStatusCategory.PNReconnectedCategory: + pass + # Happens as part of our regular operation. This event happens when + # radio / connectivity is lost, then regained. + elif status.category == PNStatusCategory.PNDecryptionErrorCategory: + pass + # Handle message decryption error. Probably client configured to + + # encrypt messages and on live data feed it received plain text. + + def message(self, pubnub, message): + # Handle new message stored in message.message + print(message) + pubnub.unsubscribe().channels("someChannel").execute() + + +pubnub.add_listener(MySubscribeCallback()) +pubnub.subscribe().channels('someChannel').execute() diff --git a/examples/native_threads/custom_crypto.py b/examples/native_threads/custom_crypto.py new file mode 100644 index 00000000..c29b180f --- /dev/null +++ b/examples/native_threads/custom_crypto.py @@ -0,0 +1,31 @@ +# PubNub custom crypto library usage example +import logging +import os +import sys + +d = os.path.dirname +PUBNUB_ROOT = d(d(os.path.dirname(os.path.abspath(__file__)))) +sys.path.append(PUBNUB_ROOT) + +import pubnub + +pubnub.set_stream_logger('pubnub', logging.DEBUG, stream=sys.stdout) + +from examples import pnconf +from pubnub.pubnub import PubNub +from pubnub.crypto_legacy import PubNubCryptoLegacy + +crypto = PubNubCryptoLegacy() + +pnconf.enable_subscribe = False +pnconf.cipher_key = 'blah' +pnconf.crypto_instance = crypto +pubnub = PubNub(pnconf) + + +envelope = pubnub.publish() \ + .channel("blah") \ + .message("hey") \ + .sync() + +print(envelope.result) diff --git a/examples/native_threads/http/__init__.py b/examples/native_threads/http/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/native_threads/http/app.py b/examples/native_threads/http/app.py new file mode 100644 index 00000000..08529116 --- /dev/null +++ b/examples/native_threads/http/app.py @@ -0,0 +1,125 @@ +import logging +import os +import sys +import time + +from flask import Flask, jsonify +from flask import request + +d = os.path.dirname +PUBNUB_ROOT = d(d(d(os.path.dirname(os.path.abspath(__file__))))) +sys.path.append(PUBNUB_ROOT) + +import pubnub as pn +from pubnub import utils +from pubnub.exceptions import PubNubException +from pubnub.pnconfiguration import PNConfiguration +from pubnub.pubnub import PubNub + + +pn.set_stream_logger('pubnub', logging.DEBUG) +logger = logging.getLogger("myapp") + +app = Flask(__name__) + +pnconfig = PNConfiguration() +pnconfig.subscribe_request_timeout = 10 +pnconfig.subscribe_key = "sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe" +pnconfig.publish_key = "pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52" +pnconfig.uuid = "pubnub-demo-api-python-backend" +DEFAULT_CHANNEL = "pubnub_demo_api_python_channel" +EVENTS_CHANNEL = "pubnub_demo_api_python_events" +APP_KEY = utils.uuid() + +pubnub = PubNub(pnconfig) +logger.info("SDK Version: %s", pubnub.SDK_VERSION) + + +@app.route("/app_key") +def app_key(): + return { + 'app_key': APP_KEY + } + + +@app.route("/subscription/add") +def subscription_add(): + channel = request.args.get('channel') + + if channel is None: + return jsonify({ + "error": "Channel missing" + }), 500 + + pubnub.subscribe().channels(channel).execute() + return jsonify({ + 'subscribed_channels': pubnub.get_subscribed_channels() + }) + + +@app.route("/subscription/remove") +def subscription_remove(): + channel = request.args.get('channel') + + if channel is None: + return jsonify({ + "error": "Channel missing" + }), 500 + + pubnub.unsubscribe().channels(channel).execute() + + return jsonify({ + 'subscribed_channels': pubnub.get_subscribed_channels() + }) + + +@app.route("/subscription/list") +def subscription_list(): + return jsonify({ + 'subscribed_channels': pubnub.get_subscribed_channels() + }) + + +@app.route('/publish/sync') +def publish_sync(): + channel = request.args.get('channel') + + if channel is None: + return jsonify({ + "error": "Channel missing" + }), 500 + + try: + envelope = pubnub.publish().channel(channel).message("hello from yield-based publish").sync() + return jsonify({ + "original_response": str(envelope.status.original_response) + }) + except PubNubException as e: + return jsonify({ + "message": str(e) + }), 500 + + +@app.route('/publish/async') +def publish_async(): + channel = request.args.get('channel') + + if channel is None: + return jsonify({ + "error": "Channel missing" + }), 500 + + def stub(res, state): + pass + + pubnub.publish().channel(channel).message("hello from yield-based publish")\ + .async(stub) + + return jsonify({ + "message": "Publish task scheduled" + }) + + +if __name__ == '__main__': + app.run(host='0.0.0.0') + time.sleep(100) diff --git a/examples/native_threads/publish.py b/examples/native_threads/publish.py new file mode 100644 index 00000000..84e1e150 --- /dev/null +++ b/examples/native_threads/publish.py @@ -0,0 +1,31 @@ +# PubNub HereNow usage example +import logging +import os +import sys + +d = os.path.dirname +PUBNUB_ROOT = d(d(os.path.dirname(os.path.abspath(__file__)))) +sys.path.append(PUBNUB_ROOT) + +import pubnub +from examples import pnconf +from pubnub.pubnub import PubNub, NonSubscribeListener + + +pubnub.set_stream_logger('pubnub', logging.DEBUG, stream=sys.stdout) + +pubnub = PubNub(pnconf) + + +listener = NonSubscribeListener() + +pubnub.publish() \ + .channel("blah") \ + .message("hey") \ + .async(listener.callback) + +result = listener.await_result_and_reset(5) +# FIX: returns None +print(result) + +pubnub.stop() diff --git a/examples/tornado/__init__.py b/examples/tornado/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/tornado/http/Procfile b/examples/tornado/http/Procfile new file mode 100644 index 00000000..2e35818f --- /dev/null +++ b/examples/tornado/http/Procfile @@ -0,0 +1 @@ +web: python app.py diff --git a/examples/tornado/http/__init__.py b/examples/tornado/http/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/tornado/http/app.py b/examples/tornado/http/app.py new file mode 100644 index 00000000..6e8ce398 --- /dev/null +++ b/examples/tornado/http/app.py @@ -0,0 +1,260 @@ +import json +import tornado.ioloop +import tornado.web +import tornado.gen +import sys +import os + +from pubnub import utils +from pubnub.enums import PNStatusCategory, PNOperationType + +d = os.path.dirname +PUBNUB_ROOT = d(d(d(os.path.dirname(os.path.abspath(__file__))))) +APP_ROOT = d(os.path.abspath(__file__)) +sys.path.append(PUBNUB_ROOT) + + +from pubnub.pubnub_tornado import SubscribeListener, TornadoEnvelope +from pubnub.exceptions import PubNubException +from pubnub.pnconfiguration import PNConfiguration +from pubnub.pubnub_tornado import PubNubTornado, PubNubTornadoException +from pubnub.pubnub_tornado import SubscribeCallback +from pubnub.models.consumer.pubsub import PNPublishResult + +pnconf = PNConfiguration() +pnconf.subscribe_key = "sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe" +pnconf.publish_key = "pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52" +pnconf.uuid = "pubnub-demo-api-python-backend" +DEFAULT_CHANNEL = "pubnub_demo_api_python_channel" +EVENTS_CHANNEL = "pubnub_demo_api_python_events" +APP_KEY = utils.uuid() + +pubnub = PubNubTornado(pnconf) + + +class SyncPublishHandler(tornado.web.RequestHandler): + @tornado.gen.coroutine + def get(self): + return self.send_error(501, message={ + "error": "Sync publish not implemented" + }) + + +class AsyncPublishHandler(tornado.web.RequestHandler): + @tornado.gen.coroutine + def get(self): + channel = self.get_argument('channel') + if channel is None: + return self.send_error(500, message={ + "error": "Channel missing" + }) + + try: + envelope = yield pubnub.publish().channel(channel).message("hello from yield-based publish").future() + self.write(json.dumps({ + "original_response": str(envelope.status.original_response) + })) + except PubNubTornadoException as e: + self.send_error(500, message={ + "message": str(e) + }) + + +class AsyncPublishHandler2(tornado.web.RequestHandler): + def data_received(self, chunk): + pass + + @tornado.web.asynchronous + def get(self): + channel = self.get_argument('channel') + if channel is None: + return self.send_error(500, message={ + "error": "Channel missing" + }) + + pubnub.publish().channel(channel).message("hello from callback-based publish")\ + .future().add_done_callback(self.callback) + + def callback(self, future): + if future.exception() is not None: + self.send_error(500, message={ + "message": str(str(future.exception())) + }) + else: + envelope = future.result() + self.write(json.dumps({ + "original_response": str(envelope.status.original_response) + })) + + self.finish() + + +class AppKeyHandler(tornado.web.RequestHandler): + def data_received(self, chunk): + pass + + @tornado.gen.coroutine + def get(self): + self.set_header('Content-Type', 'application/json') + + self.write(json.dumps({ + "app_key": APP_KEY + })) + + +class ListenHandler(tornado.web.RequestHandler): + """ + Long-polling request + """ + def data_received(self, chunk): + pass + + @tornado.gen.coroutine + def get(self): + self.set_header('Content-Type', 'application/json') + + channel = self.get_argument('channel') + if channel is None: + return self.send_error(500, message={ + "error": "Channel missing" + }) + + listener = SubscribeListener() + pubnub.add_listener(listener) + + try: + res = yield listener.wait_for_message_on(channel) + self.write(json.dumps({"message": res.message})) + except PubNubException as e: + self.send_error(500, message={ + "message": str(e) + }) + finally: + pubnub.remove_listener(listener) + + +class ListChannelHandler(tornado.web.RequestHandler): + def data_received(self, chunk): + pass + + @tornado.gen.coroutine + def get(self): + self.set_header('Content-Type', 'application/json') + + self.write(json.dumps({ + "subscribed_channels": pubnub.get_subscribed_channels() + })) + + +class AddChannelHandler(tornado.web.RequestHandler): + def data_received(self, chunk): + pass + + @tornado.gen.coroutine + def get(self): + self.set_header('Content-Type', 'application/json') + + channel = self.get_argument('channel') + if channel is None: + return self.send_error(500, message={ + "error": "Channel missing" + }) + + try: + pubnub.subscribe().channels(channel).execute() + self.write(json.dumps({ + "subscribed_channels": pubnub.get_subscribed_channels() + })) + except PubNubException as e: + self.send_error(500, message={ + "message": str(e) + }) + + +class RemoveChannelHandler(tornado.web.RequestHandler): + def data_received(self, chunk): + pass + + @tornado.gen.coroutine + def get(self): + self.set_header('Content-Type', 'application/json') + + channel = self.get_argument('channel') + if channel is None: + return self.send_error(500, message={ + "error": "Channel missing" + }) + + try: + pubnub.unsubscribe().channels(channel).execute() + self.write(json.dumps({ + "subscribed_channels": pubnub.get_subscribed_channels() + })) + except PubNubException as e: + self.send_error(500, message={ + "message": str(e) + }) + + +def init_events_transmitter(): + """ + Method transmits status events to the specific channel + :return: + """ + class StatusListener(SubscribeCallback): + def status(self, pubnub, status): + def callback(future): + envelope = future.result() + assert isinstance(envelope, TornadoEnvelope) + + result = envelope.result + assert isinstance(result, PNPublishResult) + + print(result) + + event = "unknown" + + if status.operation == PNOperationType.PNSubscribeOperation \ + and status.category == PNStatusCategory.PNConnectedCategory: + event = "Connect" + elif status.operation == PNOperationType.PNUnsubscribeOperation \ + and status.category == PNStatusCategory.PNAcknowledgmentCategory: + event = "Unsubscribe" + + tornado.ioloop.IOLoop.current().add_future( + pubnub.publish().channel('status-' + APP_KEY).message({ + "event": event + }).future(), + callback + ) + + def presence(self, pubnub, presence): + pass + + def message(self, pubnub, message): + pass + + listener = StatusListener() + pubnub.add_listener(listener) + + +def make_app(): + return tornado.web.Application([ + (r"/listen", ListenHandler), + (r"/app_key", AppKeyHandler), + (r"/publish/sync", SyncPublishHandler), + (r"/publish/async", AsyncPublishHandler), + (r"/publish/async2", AsyncPublishHandler2), + (r"/subscription/list", ListChannelHandler), + (r"/subscription/add", AddChannelHandler), + (r"/subscription/remove", RemoveChannelHandler), + ], + static_path=os.path.join(APP_ROOT, "static"), + template_path=os.path.join(APP_ROOT, "templates"),) + + +if __name__ == "__main__": + init_events_transmitter() + app = make_app() + app.listen(8888) + tornado.ioloop.IOLoop.current().start() diff --git a/examples/tornado/http/requirements.txt b/examples/tornado/http/requirements.txt new file mode 100644 index 00000000..de466fb5 --- /dev/null +++ b/examples/tornado/http/requirements.txt @@ -0,0 +1,2 @@ +tornado +pubnub diff --git a/examples/twisted/__init__.py b/examples/twisted/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/twisted/basic_usage.py b/examples/twisted/basic_usage.py new file mode 100644 index 00000000..61fdcb71 --- /dev/null +++ b/examples/twisted/basic_usage.py @@ -0,0 +1,61 @@ +from pubnub.enums import PNStatusCategory +from pubnub.pubnub_twisted import PubNubTwisted as PubNub +from pubnub.pnconfiguration import PNConfiguration +from twisted.internet import reactor +from pubnub.callbacks import SubscribeCallback + + +def main(): + pnconf = PNConfiguration() + pnconf.subscribe_key = 'demo' + pnconf.publish_key = 'demo' + + pubnub = PubNub(pnconf) + + def my_publish_callback(result, status): + # Check whether request successfully completed or not + if not status.is_error(): + envelope = result # NOQA:W292 + pass # Message successfully published to specified channel. + else: + pass # Handle message publish error. Check 'category' property to find out possible issue + # because of which request did fail. + # Request can be resent using: [status retry]; + + class MySubscribeCallback(SubscribeCallback): + def presence(self, pubnub, presence): + pass # handle incoming presence data + + def status(self, pubnub, status): + if status.category == PNStatusCategory.PNUnexpectedDisconnectCategory: + pass # This event happens when radio / connectivity is lost + + elif status.category == PNStatusCategory.PNConnectedCategory: + # Connect event. You can do stuff like publish, and know you'll get it. + # Or just use the connected event to confirm you are subscribed for + # UI / internal notifications, etc + pubnub.publish().channel("awesome_channel").message("Hello!").async(my_publish_callback), + + elif status.category == PNStatusCategory.PNReconnectedCategory: + pass + # Happens as part of our regular operation. This event happens when + # radio / connectivity is lost, then regained. + elif status.category == PNStatusCategory.PNDecryptionErrorCategory: + pass + # Handle message decryption error. Probably client configured to + # encrypt messages and on live data feed it received plain text. + + def message(self, pubnub, message): + # Handle new message stored in message.message + pass + + pubnub.add_listener(MySubscribeCallback()) + pubnub.subscribe().channels('awesome_channel').execute() + + reactor.callLater(30, pubnub.stop) # stop reactor loop after 30 seconds + + pubnub.start() + + +if __name__ == '__main__': + main() diff --git a/examples/twisted/subscribe.py b/examples/twisted/subscribe.py new file mode 100644 index 00000000..0ad81f43 --- /dev/null +++ b/examples/twisted/subscribe.py @@ -0,0 +1,38 @@ +# PubNub HereNow usage example +import sys + +import time + + +sys.path.append("../../") + +from pubnub.callbacks import SubscribeCallback +from pubnub.pnconfiguration import PNConfiguration +from pubnub.pubnub_twisted import PubNubTwisted + +pnconf = PNConfiguration() +pnconf.publish_key = "demo" +pnconf.subscribe_key = "demo" +pnconf.enable_subscribe = True + +pubnub = PubNubTwisted(pnconf) + + +class MyListener(SubscribeCallback): + def status(self, pubnub, status): + print("status changed: %s" % status) + + def message(self, pubnub, message): + print("new message: %s" % message) + + def presence(self, pubnub, presence): + pass + + +my_listener = MyListener() + + +pubnub.add_listener(my_listener) + +pubnub.subscribe().channels('my_channel').execute() +time.sleep(60) diff --git a/pubnub.py b/pubnub.py deleted file mode 100755 index 0fcd651b..00000000 --- a/pubnub.py +++ /dev/null @@ -1,2927 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2016 Stephen Blum -# http://www.pubnub.com/ - -# ----------------------------------- -# PubNub 3.7.6 Real-time Push Cloud API -# ----------------------------------- - - -try: - import json -except ImportError: - import simplejson as json - -import copy -import hashlib -import hmac -import random -import sys -import time -import uuid as uuid_lib -from Crypto.Cipher import AES -from base64 import encodestring, decodestring -from base64 import urlsafe_b64encode - -try: - from hashlib import sha256 - digestmod = sha256 -except ImportError: - import Crypto.Hash.SHA256 as digestmod - sha256 = digestmod.new - - -# vanilla python imports -try: - from urllib.parse import quote -except ImportError: - from urllib2 import quote -try: - import urllib.request -except ImportError: - import urllib2 - -try: - import requests - from requests.adapters import HTTPAdapter -except ImportError: - pass - -#import urllib -import socket -import threading - -try: - import urllib3.HTTPConnection - default_socket_options = urllib3.HTTPConnection.default_socket_options -except: - default_socket_options = [] - -default_socket_options += [ - # Enable TCP keepalive - (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) -] - -if sys.platform.startswith("linux"): - default_socket_options += [ - # Send first keepalive packet 200 seconds after last data packet - (socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 200), - # Resend keepalive packets every second, when unanswered - (socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1), - # Close the socket after 5 unanswered keepalive packets - (socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5) - ] -elif sys.platform.startswith("darwin"): - # From /usr/include/netinet/tcp.h - - # idle time used when SO_KEEPALIVE is enabled - socket.TCP_KEEPALIVE = socket.TCP_KEEPALIVE \ - if hasattr(socket, 'TCP_KEEPALIVE') \ - else 0x10 - - # interval between keepalives - socket.TCP_KEEPINTVL = socket.TCP_KEEPINTVL \ - if hasattr(socket, 'TCP_KEEPINTVL') \ - else 0x101 - - # number of keepalives before close - socket.TCP_KEEPCNT = socket.TCP_KEEPCNT \ - if hasattr(socket, 'TCP_KEEPCNT') \ - else 0x102 - - default_socket_options += [ - # Send first keepalive packet 200 seconds after last data packet - (socket.IPPROTO_TCP, socket.TCP_KEEPALIVE, 200), - # Resend keepalive packets every second, when unanswered - (socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1), - # Close the socket after 5 unanswered keepalive packets - (socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5) - ] -""" -# The Windows code is currently untested -elif sys.platform.startswith("win"): - import struct - from urllib3.connectionpool import HTTPConnectionPool, HTTPSConnectionPool - - def patch_socket_keepalive(conn): - conn.sock.ioctl(socket.SIO_KEEPALIVE_VALS, ( - # Enable TCP keepalive - 1, - # Send first keepalive packet 200 seconds after last data packet - 200, - # Resend keepalive packets every second, when unanswered - 1 - )) - - class PubnubHTTPConnectionPool(HTTPConnectionPool): - def _validate_conn(self, conn): - super(PubnubHTTPConnectionPool, self)._validate_conn(conn) - - class PubnubHTTPSConnectionPool(HTTPSConnectionPool): - def _validate_conn(self, conn): - super(PubnubHTTPSConnectionPool, self)._validate_conn(conn) - - import urllib3.poolmanager - urllib3.poolmanager.pool_classes_by_scheme = { - 'http' : PubnubHTTPConnectionPool, - 'https' : PubnubHTTPSConnectionPool - } -""" - -################################## - - -# Tornado imports and globals -try: - import tornado.httpclient - import tornado.ioloop - from tornado.stack_context import ExceptionStackContext - ioloop = tornado.ioloop.IOLoop.instance() -except ImportError: - pass - -####################################### - - -# Twisted imports and globals -try: - from twisted.internet import reactor - from twisted.internet.defer import Deferred - from twisted.internet.protocol import Protocol - from twisted.web.client import Agent, ContentDecoderAgent - from twisted.web.client import RedirectAgent, GzipDecoder - from twisted.web.client import HTTPConnectionPool - from twisted.web.http_headers import Headers - from twisted.internet.ssl import ClientContextFactory - import twisted - - pnconn_pool = HTTPConnectionPool(reactor, persistent=True) - pnconn_pool.maxPersistentPerHost = 100000 - pnconn_pool.cachedConnectionTimeout = 15 - pnconn_pool.retryAutomatically = True - - class WebClientContextFactory(ClientContextFactory): - def getContext(self, hostname, port): - return ClientContextFactory.getContext(self) - - class PubNubPamResponse(Protocol): - def __init__(self, finished): - self.finished = finished - - def dataReceived(self, bytes): - self.finished.callback(bytes) - - class PubNubResponse(Protocol): - def __init__(self, finished): - self.finished = finished - self.data = "" - - def dataReceived(self, bytes): - self.data += bytes - - def connectionLost(self, reason): - self.finished.callback(self.data) - - -except ImportError: - pass - -####################################### - - -def get_data_for_user(data): - try: - if 'message' in data and 'payload' in data: - return {'message': data['message'], 'payload': data['payload']} - else: - return data - except TypeError: - return data - - -class PubnubCrypto2(): - - def pad(self, msg, block_size=16): - - padding = block_size - (len(msg) % block_size) - return msg + chr(padding) * padding - - def depad(self, msg): - - return msg[0:-ord(msg[-1])] - - def getSecret(self, key): - - return hashlib.sha256(key).hexdigest() - - def encrypt(self, key, msg): - secret = self.getSecret(key) - Initial16bytes = '0123456789012345' - cipher = AES.new(secret[0:32], AES.MODE_CBC, Initial16bytes) - enc = encodestring(cipher.encrypt(self.pad(msg))) - return enc - - def decrypt(self, key, msg): - - try: - secret = self.getSecret(key) - Initial16bytes = '0123456789012345' - cipher = AES.new(secret[0:32], AES.MODE_CBC, Initial16bytes) - plain = self.depad(cipher.decrypt(decodestring(msg))) - except: - return msg - try: - return json.loads(plain) - except SyntaxError: - return plain - - -class PubnubCrypto3(): - - def pad(self, msg, block_size=16): - - padding = block_size - (len(msg) % block_size) - return msg + (chr(padding) * padding).encode('utf-8') - - def depad(self, msg): - - return msg[0:-ord(msg[-1])] - - def getSecret(self, key): - - return hashlib.sha256(key.encode("utf-8")).hexdigest() - - def encrypt(self, key, msg): - - secret = self.getSecret(key) - Initial16bytes = '0123456789012345' - cipher = AES.new(secret[0:32], AES.MODE_CBC, Initial16bytes) - return encodestring( - cipher.encrypt(self.pad(msg.encode('utf-8')))).decode('utf-8') - - def decrypt(self, key, msg): - - secret = self.getSecret(key) - Initial16bytes = '0123456789012345' - cipher = AES.new(secret[0:32], AES.MODE_CBC, Initial16bytes) - return (cipher.decrypt( - decodestring(msg.encode('utf-8')))).decode('utf-8') - - -class PubnubBase(object): - def __init__( - self, - publish_key, - subscribe_key, - secret_key=False, - cipher_key=False, - auth_key=None, - ssl_on=False, - origin='pubsub.pubnub.com', - uuid=None - ): - """Pubnub Class - - Provides methods to communicate with Pubnub cloud - - Attributes: - publish_key: Publish Key - subscribe_key: Subscribe Key - secret_key: Secret Key - cipher_key: Cipher Key - auth_key: Auth Key (used with Pubnub Access Manager i.e. PAM) - ssl: SSL enabled ? - origin: Origin - """ - - self.origin = origin - self.version = '3.7.6' - self.limit = 1800 - self.publish_key = publish_key - self.subscribe_key = subscribe_key - self.secret_key = secret_key - self.cipher_key = cipher_key - self.ssl = ssl_on - self.auth_key = auth_key - self.STATE = {} - self.http_debug = None - - if self.ssl: - self.origin = 'https://' + self.origin - else: - self.origin = 'http://' + self.origin - - self.uuid = uuid or str(uuid_lib.uuid4()) - - if type(sys.version_info) is tuple: - self.python_version = 2 - self.pc = PubnubCrypto2() - else: - if sys.version_info.major == 2: - self.python_version = 2 - self.pc = PubnubCrypto2() - else: - self.python_version = 3 - self.pc = PubnubCrypto3() - - if not isinstance(self.uuid, str): - raise AttributeError("uuid must be a string") - - def set_http_debug(self, func=None): - self.http_debug = func - - def _pam_sign(self, msg): - - sign = urlsafe_b64encode(hmac.new( - self.secret_key.encode("utf-8"), - msg.encode("utf-8"), - sha256 - ).digest()) - return quote(sign, safe="") - - def set_u(self, u=False): - self.u = u - - def _pam_auth(self, query, apicode=0, callback=None, error=None): - - if 'timestamp' not in query: - query['timestamp'] = int(time.time()) - - ## Global Grant? - if 'auth' in query and not query['auth']: - del query['auth'] - - if 'channel' in query and not query['channel']: - del query['channel'] - - if 'channel-group' in query and not query['channel-group']: - del query['channel-group'] - - params = "&".join([ - x + "=" + quote( - str(query[x]), safe="" - ) for x in sorted(query) - ]) - sign_input = "{subkey}\n{pubkey}\n{apitype}\n{params}".format( - subkey=self.subscribe_key, - pubkey=self.publish_key, - apitype="audit" if (apicode) else "grant", - params=params - ) - query['signature'] = self._pam_sign(sign_input) - - return self._request({"urlcomponents": [ - 'v1', 'auth', "audit" if (apicode) else "grant", - 'sub-key', - self.subscribe_key - ], 'urlparams': query}, - self._return_wrapped_callback(callback), - self._return_wrapped_callback(error), - encoder_map={'signature': self._encode_pam}) - - def get_origin(self): - return self.origin - - def set_auth_key(self, auth_key): - self.auth_key = auth_key - - def get_auth_key(self): - return self.auth_key - - def grant(self, channel=None, channel_group=None, auth_key=False, - read=False, write=False, manage=False, ttl=5, callback=None, - error=None): - """Method for granting permissions. - - This function establishes subscribe and/or write permissions for - PubNub Access Manager (PAM) by setting the read or write attribute - to true. A grant with read or write set to false (or not included) - will revoke any previous grants with read or write set to true. - - Permissions can be applied to any one of three levels: - 1. Application level privileges are based on subscribe_key applying - to all associated channels. - 2. Channel level privileges are based on a combination of - subscribe_key and channel name. - 3. User level privileges are based on the combination of - subscribe_key, channel and auth_key. - - Args: - channel: (string) (optional) - Specifies channel name to grant permissions to. - If channel/channel_group is not specified, the grant - applies to all channels associated with the - subscribe_key. If auth_key is not specified, it is - possible to grant permissions to multiple channels - simultaneously by specifying the channels - as a comma separated list. - channel_group: (string) (optional) - Specifies channel group name to grant permissions to. - If channel/channel_group is not specified, the grant - applies to all channels associated with the - subscribe_key. If auth_key is not specified, it is - possible to grant permissions to multiple channel - groups simultaneously by specifying the channel groups - as a comma separated list. - - auth_key: (string) (optional) - Specifies auth_key to grant permissions to. - It is possible to specify multiple auth_keys as comma - separated list in combination with a single channel - name. If auth_key is provided as the special-case - value "null" (or included in a comma-separated list, - eg. "null,null,abc"), a new auth_key will be generated - and returned for each "null" value. - - read: (boolean) (default: True) - Read permissions are granted by setting to True. - Read permissions are removed by setting to False. - - write: (boolean) (default: True) - Write permissions are granted by setting to true. - Write permissions are removed by setting to false. - manage: (boolean) (default: True) - Manage permissions are granted by setting to true. - Manage permissions are removed by setting to false. - - ttl: (int) (default: 1440 i.e 24 hrs) - Time in minutes for which granted permissions are - valid. Max is 525600 , Min is 1. - Setting ttl to 0 will apply the grant indefinitely. - - callback: (function) (optional) - A callback method can be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or tornado - - error: (function) (optional) - An error method can be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or tornado - - Returns: - Returns a dict in sync mode i.e. when callback argument is not - given - The dict returned contains values with keys 'message' and 'payload' - - Sample Response: - { - "message":"Success", - "payload":{ - "ttl":5, - "auths":{ - "my_ro_authkey":{"r":1,"w":0} - }, - "subscribe_key":"my_subkey", - "level":"user", - "channel":"my_channel" - } - } - """ - - return self._pam_auth({ - 'channel': channel, - 'channel-group': channel_group, - 'auth': auth_key, - 'r': read and 1 or 0, - 'w': write and 1 or 0, - 'm': manage and 1 or 0, - 'ttl': ttl, - 'pnsdk': self.pnsdk - }, callback=callback, error=error) - - def revoke(self, channel=None, channel_group=None, auth_key=None, ttl=1, - callback=None, error=None): - """Method for revoking permissions. - - Args: - channel: (string) (optional) - Specifies channel name to revoke permissions to. - If channel/channel_group is not specified, the revoke - applies to all channels associated with the - subscribe_key. If auth_key is not specified, it is - possible to grant permissions to multiple channels - simultaneously by specifying the channels as a comma - separated list. - - channel_group: (string) (optional) - Specifies channel group name to revoke permissions to. - If channel/channel_group is not specified, the grant - applies to all channels associated with the - subscribe_key. If auth_key is not specified, it is - possible to revoke permissions to multiple channel - groups simultaneously by specifying the channel groups - as a comma separated list. - - auth_key: (string) (optional) - Specifies auth_key to revoke permissions to. - It is possible to specify multiple auth_keys as comma - separated list in combination with a single channel - name. If auth_key is provided as the special-case - value "null" (or included in a comma-separated list, - eg. "null,null,abc"), a new auth_key will be generated - and returned for each "null" value. - - ttl: (int) (default: 1440 i.e 24 hrs) - Time in minutes for which granted permissions are - valid. - Max is 525600 , Min is 1. - Setting ttl to 0 will apply the grant indefinitely. - - callback: (function) (optional) - A callback method can be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - error: (function) (optional) - An error method can be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Returns a dict in sync mode i.e. when callback argument is not - given. - The dict returned contains values with keys 'message' and 'payload' - - Sample Response: - { - "message":"Success", - "payload":{ - "ttl":5, - "auths":{ - "my_authkey":{"r":0,"w":0} - }, - "subscribe_key":"my_subkey", - "level":"user", - "channel":"my_channel" - } - } - - """ - - return self._pam_auth({ - 'channel': channel, - 'channel-group': channel_group, - 'auth': auth_key, - 'r': 0, - 'w': 0, - 'ttl': ttl, - 'pnsdk': self.pnsdk - }, callback=callback, error=error) - - def audit(self, channel=None, channel_group=None, auth_key=None, - callback=None, error=None): - """Method for fetching permissions from pubnub servers. - - This method provides a mechanism to reveal existing PubNub Access - Manager attributes for any combination of subscribe_key, channel - and auth_key. - - Args: - channel: (string) (optional) - Specifies channel name to return PAM - attributes optionally in combination with auth_key. - If channel/channel_group is not specified, results - for all channels associated with subscribe_key are - returned. If auth_key is not specified, it is possible - to return results for a comma separated list of - channels. - channel_group: (string) (optional) - Specifies channel group name to return PAM - attributes optionally in combination with auth_key. - If channel/channel_group is not specified, results - for all channels associated with subscribe_key are - returned. If auth_key is not specified, it is possible - to return results for a comma separated list of - channels. - - auth_key: (string) (optional) - Specifies the auth_key to return PAM attributes for. - If only a single channel is specified, it is possible - to return results for a comma separated list of - auth_keys. - - callback: (function) (optional) - A callback method can be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - error: (function) (optional) - An error method can be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Returns a dict in sync mode i.e. when callback argument is not - given - The dict returned contains values with keys 'message' and 'payload' - - Sample Response - { - "message":"Success", - "payload":{ - "channels":{ - "my_channel":{ - "auths":{"my_ro_authkey":{"r":1,"w":0}, - "my_rw_authkey":{"r":0,"w":1}, - "my_admin_authkey":{"r":1,"w":1} - } - } - }, - } - - Usage: - - pubnub.audit ('my_channel'); # Sync Mode - - """ - - return self._pam_auth({ - 'channel': channel, - 'channel-group': channel_group, - 'auth': auth_key, - 'pnsdk': self.pnsdk - }, 1, callback=callback, error=error) - - def encrypt(self, message): - """Method for encrypting data. - - This method takes plaintext as input and returns encrypted data. - This need not be called directly as enncryption/decryption is - taken care of transparently by Pubnub class if cipher key is - provided at time of initializing pubnub object - - Args: - message: Message to be encrypted. - - Returns: - Returns encrypted message if cipher key is set - """ - if self.cipher_key: - message = json.dumps(self.pc.encrypt( - self.cipher_key, json.dumps(message)).replace('\n', '')) - else: - message = json.dumps(message) - - return message - - def decrypt(self, message): - """Method for decrypting data. - - This method takes ciphertext as input and returns decrypted data. - This need not be called directly as enncryption/decryption is - taken care of transparently by Pubnub class if cipher key is - provided at time of initializing pubnub object - - Args: - message: Message to be decrypted. - - Returns: - Returns decrypted message if cipher key is set - """ - if self.cipher_key: - message = self.pc.decrypt(self.cipher_key, message) - - return message - - def _return_wrapped_callback(self, callback=None): - def _new_format_callback(response): - if self.http_debug is not None: - self.http_debug(response) - if 'payload' in response: - if (callback is not None): - callback_data = dict() - callback_data['payload'] = response['payload'] - - if 'message' in response: - callback_data['message'] = response['message'] - - if (callback is not None): - callback(callback_data) - else: - if (callback is not None): - callback(response) - if (callback is not None): - return _new_format_callback - else: - return None - - def leave_channel(self, channel, callback=None, error=None): - ## Send leave - return self._request({"urlcomponents": [ - 'v2', 'presence', - 'sub_key', - self.subscribe_key, - 'channel', - channel, - 'leave' - ], 'urlparams': - {'auth': self.auth_key, 'pnsdk': self.pnsdk, "uuid": self.uuid, }}, - callback=self._return_wrapped_callback(callback), - error=self._return_wrapped_callback(error)) - - def leave_group(self, channel_group, callback=None, error=None): - ## Send leave - return self._request({"urlcomponents": [ - 'v2', 'presence', - 'sub_key', - self.subscribe_key, - 'channel', - ',', - 'leave' - ], 'urlparams': - {'auth': self.auth_key, 'pnsdk': self.pnsdk, - 'channel-group': channel_group, - "uuid": self.uuid, }}, - callback=self._return_wrapped_callback(callback), - error=self._return_wrapped_callback(error)) - - def publish(self, channel, message, callback=None, error=None): - """Publishes data on a channel. - - The publish() method is used to send a message to all subscribers of - a channel. To publish a message you must first specify a valid - publish_key at initialization. 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 ssl to - True during initialization. - - Published messages can also be encrypted with AES-256 simply by - specifying a cipher_key during initialization. - - Args: - channel: (string) - Specifies channel name to publish messages to. - message: (string/int/double/dict/list) - Message to be published - callback: (optional) - A callback method can be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - error: (optional) - An error method can be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Sync Mode : list - Async Mode : None - - The function returns the following formatted response: - - [ Number, "Status", "Time Token"] - - The output below demonstrates the response to a successful call: - - [1,"Sent","13769558699541401"] - - """ - - message = self.encrypt(message) - - ## Send Message - return self._request({"urlcomponents": [ - 'publish', - self.publish_key, - self.subscribe_key, - '0', - channel, - '0', - message - ], 'urlparams': {'auth': self.auth_key, 'pnsdk': self.pnsdk}}, - callback=self._return_wrapped_callback(callback), - error=self._return_wrapped_callback(error)) - - def presence(self, channel, callback, error=None, connect=None, - disconnect=None, reconnect=None): - """Subscribe to presence events on a channel. - - Only works in async mode - - Args: - channel: Channel name ( string ) on which to listen for events - callback: A callback method should be passed as parameter. - If passed, the api works in async mode. - Required argument when working with twisted or tornado. - error: Optional variable. - An error method can be passed as - parameter. If set, the api works in async mode. - - Returns: - None - """ - return self.subscribe(channel + '-pnpres', callback=callback, - error=error, connect=connect, - disconnect=disconnect, - reconnect=reconnect) - - def presence_group(self, channel_group, callback, error=None, - connect=None, disconnect=None, reconnect=None): - """Subscribe to presence events on a channel group. - - Only works in async mode - - Args: - channel_group: Channel group name ( string ) - callback: A callback method should be passed to the method. - If passed, the api works in async mode. - Required argument when working with twisted or tornado. - error: Optional variable. An error method can be passed as - parameter. - If passed, the api works in async mode. - - Returns: - None - """ - return self.subscribe_group(channel_group + '-pnpres', - callback=callback, error=error, - connect=connect, - disconnect=disconnect, - reconnect=reconnect) - - def state(self, channel=None, channel_group=None, uuid=None, state=None, - callback=None, error=None): - """Get/Set state data. - - The state API is used to set key/value pairs specific to a subscriber - uuid. - State information is supplied as a dict of key/value pairs. - - - Args: - state: (string) (optional) - Specifies the channel name to return occupancy - results. If channel is not provided, here_now will - return data for all channels. - - uuid: (string) (optional) - The subscriber uuid to set state for or get current - state from. - Default is current uuid. - - channel: (string) (optional) - Specifies the channel for which state is to be - set/get. - - channel_group: (string) (optional) - Specifies the channel_group for which state is to - be set/get. - - callback: (optional) - A callback method should be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - error: (optional) - Optional variable. An error method can be passed to - the method. If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Sync Mode: Object - Async Mode: None - - Response Format: - - The state API returns a JSON object containing key value pairs. - - Example Response: - { - first : "Robert", - last : "Plant", - age : 59, - region : "UK" - } - """ - data = {'auth': self.auth_key, 'pnsdk': self.pnsdk} - - try: - if (channel and self.subscriptions[channel] and - self.subscriptions[channel].subscribed and - state is not None): - self.STATE[channel] = state - except KeyError: - pass - - if channel_group and state is not None: - try: - if (self.subscription_groups[channel_group] and - self.subscription_groups[channel_group].subscribed): - self.STATE[channel_group] = state - except KeyError: - pass - - data['channel-group'] = channel_group - - if channel is None or len(channel) >= 0: - channel = ',' - - if uuid is None: - uuid = self.uuid - - if state is not None: - data['state'] = json.dumps(state) - urlcomponents = [ - 'v2', 'presence', - 'sub-key', self.subscribe_key, - 'channel', channel, - 'uuid', uuid, - 'data' - ] - else: - urlcomponents = [ - 'v2', 'presence', - 'sub-key', self.subscribe_key, - 'channel', channel, - 'uuid', uuid - ] - - ## Get Presence Here Now - return self._request({"urlcomponents": urlcomponents, - 'urlparams': data}, - callback=self._return_wrapped_callback(callback), - error=self._return_wrapped_callback(error)) - - def where_now(self, uuid=None, callback=None, error=None): - """Get where now data. - - You can obtain information about the current list of a channels to - which a uuid is subscribed to by calling the where_now() function - in your application. - - - Args: - - uuid: (optional) - Specifies the uuid to return channel list for. - Default is current uuid. - - callback: (optional) - A callback method should be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - error: (optional) - Optional variable. An error method can be passed - to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Sync Mode: list - Async Mode: None - - Response Format: - - The where_now() method returns a list of channels to which - uuid is currently subscribed. - - channels:["String","String", ... ,"String"] - List of Channels - uuid is currently subscribed to. - - Example Response: - { - "channels": - [ - "lobby", - "game01", - "chat" - ] - } - """ - - urlcomponents = [ - 'v2', 'presence', - 'sub_key', self.subscribe_key, - 'uuid' - ] - - if (uuid is not None and len(uuid) > 0): - urlcomponents.append(uuid) - else: - urlcomponents.append(self.uuid) - - data = {'auth': self.auth_key, 'pnsdk': self.pnsdk} - - ## Get Presence Where Now - return self._request({"urlcomponents": urlcomponents, - 'urlparams': data}, - callback=self._return_wrapped_callback(callback), - error=self._return_wrapped_callback(error)) - - def here_now(self, channel, uuids=True, state=False, - callback=None, error=None): - """Get here now data. - - You can obtain information about the current state of a channel - including a list of unique user-ids currently subscribed to the - channel and the total occupancy count of the channel by calling - the here_now() function in your application. - - - Args: - channel: (string) (optional) - Specifies the channel name to return occupancy - results. If channel is not provided, here_now will - return data for all channels. - - callback: (optional) - A callback method should be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - error: (optional) - Optional variable. An error method can be passed - to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado . - - Returns: - Sync Mode: list - Async Mode: None - - Response Format: - - The here_now() method returns a list of uuid s currently - subscribed to the channel. - - uuids:["String","String", ... ,"String"] - List of UUIDs currently - subscribed to the channel. - - occupancy: Number - Total current occupancy of the channel. - - Example Response: - { - occupancy: 4, - uuids: [ - '123123234t234f34fq3dq', - '143r34f34t34fq34q34q3', - '23f34d3f4rq34r34rq23q', - 'w34tcw45t45tcw435tww3', - ] - } - """ - - urlcomponents = [ - 'v2', 'presence', - 'sub_key', self.subscribe_key - ] - - if (channel is not None and len(channel) > 0): - urlcomponents.append('channel') - urlcomponents.append(channel) - - data = {'auth': self.auth_key, 'pnsdk': self.pnsdk} - - if state is True: - data['state'] = '1' - - if uuids is False: - data['disable_uuids'] = '1' - - ## Get Presence Here Now - return self._request({"urlcomponents": urlcomponents, - 'urlparams': data}, - callback=self._return_wrapped_callback(callback), - error=self._return_wrapped_callback(error)) - - def history(self, channel, count=100, reverse=False, - start=None, end=None, include_token=False, callback=None, - error=None): - """This method fetches historical messages of a channel. - - PubNub Storage/Playback Service provides real-time access to an - unlimited history for all messages published to PubNub. Stored - messages are replicated across multiple availability zones in several - geographical data center locations. Stored messages can be encrypted - with AES-256 message encryption ensuring that they are not readable - while stored on PubNub's network. - - It is possible to control how messages are returned and in what order, - for example you can: - - Return messages in the order newest to oldest (default behavior). - - Return messages in the order oldest to newest by setting reverse - to true. - - Page through results by providing a start or end time token. - - Retrieve a "slice" of the time line by providing both a start - and end time token. - - Limit the number of messages to a specific quantity using - the count parameter. - - - - Args: - channel: (string) - Specifies channel to return history messages from - - count: (int) (default: 100) - Specifies the number of historical messages to return - - callback: (optional) - A callback method should be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - error: (optional) - An error method can be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Returns a list in sync mode i.e. when callback argument is not - given - - Sample Response: - [["Pub1","Pub2","Pub3","Pub4","Pub5"], - 13406746729185766,13406746845892666] - """ - - def _get_decrypted_history(resp): - try: - if (resp is not None and isinstance(resp, (list)) and - resp[1] is not None and self.cipher_key): - msgs = resp[0] - for i in range(0, len(msgs)): - msgs[i] = self.decrypt(msgs[i]) - except KeyError: - pass - return resp - - def _history_callback(resp): - if callback is not None: - callback(_get_decrypted_history(resp)) - - if callback is None: - history_cb = None - else: - history_cb = _history_callback - - params = dict() - - params['count'] = count - params['reverse'] = reverse - params['start'] = start - params['end'] = end - params['auth'] = self.auth_key - params['pnsdk'] = self.pnsdk - params['include_token'] = 'true' if include_token else 'false' - - # Get History - return _get_decrypted_history(self._request({'urlcomponents': [ - 'v2', - 'history', - 'sub-key', - self.subscribe_key, - 'channel', - channel, - ], 'urlparams': params}, - callback=self._return_wrapped_callback(history_cb), - error=self._return_wrapped_callback(error))) - - def time(self, callback=None): - """This function will return a 17 digit precision Unix epoch. - - Args: - - callback: (optional) - A callback method should be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Returns a 17 digit number in sync mode i.e. when callback - argument is not given - - Sample: - 13769501243685161 - """ - - time = self._request({'urlcomponents': [ - 'time', - '0' - ]}, callback) - return time - - def _encode(self, request): - return [ - "".join([' ~`!@#$%^&*()+=[]\\{}|;\':",./<>?'.find(ch) > -1 and - hex(ord(ch)).replace('0x', '%').upper() or - ch for ch in list(bit) - ]) for bit in request] - - def _encode_param(self, val): - return "".join([' ~`!@#$%^&*()+=[]\\{}|;\':",./<>?'.find(ch) > -1 and - hex(ord(ch)).replace('0x', '%').upper() or - ch for ch in list(val)]) - - def _encode_pam(self, val): - return val - - def getUrl(self, request, encoder_map=None): - - if self.u is True and "urlparams" in request: - request['urlparams']['u'] = str(random.randint(1, 100000000000)) - ## Build URL - url = self.origin + '/' + "/".join([ - "".join([' ~`!@#$%^&*()+=[]\\{}|;\':",./<>?'.find(ch) > -1 and - hex(ord(ch)).replace('0x', '%').upper() or - ch for ch in list(bit) - ]) for bit in request["urlcomponents"]]) - - if ("urlparams" in request): - url = url + '?' + "&".join([x + "=" + - (self._encode_param(str(y)) if encoder_map is None or x not in encoder_map else encoder_map[x](str(y))) - for x, y in request["urlparams"].items() if y is not None and len(str(y)) > 0]) - if self.http_debug is not None: - self.http_debug(url) - return url - - def _channel_registry(self, url=None, params=None, callback=None, - error=None): - - if (params is None): - params = dict() - - urlcomponents = ['v1', 'channel-registration', 'sub-key', - self.subscribe_key] - - if (url is not None): - urlcomponents += url - - params['auth'] = self.auth_key - params['pnsdk'] = self.pnsdk - - # Get History - return self._request({'urlcomponents': urlcomponents, - 'urlparams': params}, - callback=self._return_wrapped_callback(callback), - error=self._return_wrapped_callback(error)) - - def _channel_group(self, channel_group=None, channels=None, cloak=None, - mode='add', callback=None, error=None): - params = dict() - url = [] - namespace = None - - if channel_group is not None and len(channel_group) > 0: - ns_ch_a = channel_group.split(':') - - if len(ns_ch_a) > 1: - namespace = None if ns_ch_a[0] == '*' else ns_ch_a[0] - channel_group = ns_ch_a[1] - else: - channel_group = ns_ch_a[0] - - if namespace is not None: - url.append('namespace') - url.append(self._encode(namespace)) - - url.append('channel-group') - - if channel_group is not None and channel_group != '*': - url.append(channel_group) - - if channels is not None: - if (type(channels) is list): - channels = ','.join(channels) - params[mode] = channels - # params['cloak'] = 'true' if CLOAK is True else 'false' - else: - if mode == 'remove': - url.append('remove') - - return self._channel_registry(url=url, params=params, - callback=callback, error=error) - - def channel_group_list_namespaces(self, callback=None, error=None): - """Get list of namespaces. - - You can obtain list of namespaces for the subscribe key associated with - PubNub object using this method. - - - Args: - callback: (optional) - A callback method should be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - error: (optional) - Optional variable. An error method can be passed - to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Sync Mode: dict - channel_group_list_namespaces method returns a dict which - contains list of namespaces in payload field - { - u'status': 200, - u'payload': { - u'sub_key': u'demo', - u'namespaces': [u'dev', u'foo'] - }, - u'service': u'channel-registry', - u'error': False - } - - Async Mode: None (callback gets the response as parameter) - - Response Format: - - The callback passed to channel_group_list_namespaces gets the a - dict containing list of namespaces under payload field - - { - u'payload': { - u'sub_key': u'demo', - u'namespaces': [u'dev', u'foo'] - } - } - - namespaces is the list of namespaces for the given subscribe key - - - """ - - url = ['namespace'] - return self._channel_registry(url=url, callback=callback, error=error) - - def channel_group_remove_namespace(self, namespace, callback=None, - error=None): - """Remove a namespace. - - A namespace can be deleted using this method. - - - Args: - namespace: (string) namespace to be deleted - callback: (optional) - A callback method should be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - error: (optional) - Optional variable. An error method can be passed to - the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Sync Mode: dict - channel_group_remove_namespace method returns a dict indicating - status of the request - - { - u'status': 200, - u'message': 'OK', - u'service': u'channel-registry', - u'error': False - } - - Async Mode: None ( callback gets the response as parameter ) - - Response Format: - - The callback passed to channel_group_list_namespaces gets the a - dict indicating status of the request - - { - u'status': 200, - u'message': 'OK', - u'service': u'channel-registry', - u'error': False - } - - """ - url = ['namespace', self._encode(namespace), 'remove'] - return self._channel_registry(url=url, callback=callback, error=error) - - def channel_group_list_groups(self, namespace=None, callback=None, - error=None): - """Get list of groups. - - Using this method, list of groups for the subscribe key associated - with PubNub object, can be obtained. If namespace is provided, groups - within the namespace only are listed - - Args: - namespace: (string) (optional) namespace - callback: (optional) - A callback method should be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - error: (optional) - Optional variable. An error method can be passed to - the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Sync Mode: dict - channel_group_list_groups method returns a dict which contains - list of groups in payload field - { - u'status': 200, - u'payload': {"namespace": "dev", "groups": ["abcd"]}, - u'service': u'channel-registry', - u'error': False - } - - Async Mode: None ( callback gets the response as parameter ) - - Response Format: - - The callback passed to channel_group_list_namespaces gets the a - dict containing list of groups under payload field - - { - u'payload': {"namespace": "dev", "groups": ["abcd"]} - } - - - - """ - - if namespace is not None and len(namespace) > 0: - channel_group = namespace + ':*' - else: - channel_group = '*:*' - - return self._channel_group(channel_group=channel_group, - callback=callback, error=error) - - def channel_group_list_channels(self, channel_group, - callback=None, error=None): - """Get list of channels for a group. - - Using this method, list of channels for a group, can be obtained. - - Args: - channel_group: (string) (optional) - Channel Group name. It can also contain namespace. - If namespace is also specified, then the parameter - will be in format namespace:channel_group - - callback: (optional) - A callback method should be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - error: (optional) - Optional variable. An error method can be passed to the - method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Sync Mode: dict - channel_group_list_channels method returns a dict which contains - list of channels in payload field - { - u'status': 200, - u'payload': {"channels": ["hi"], "group": "abcd"}, - u'service': u'channel-registry', - u'error': False - } - - Async Mode: None ( callback gets the response as parameter ) - - Response Format: - - The callback passed to channel_group_list_channels gets the a - dict containing list of channels under payload field - - { - u'payload': {"channels": ["hi"], "group": "abcd"} - } - - - """ - return self._channel_group(channel_group=channel_group, - callback=callback, error=error) - - def channel_group_add_channel(self, channel_group, channel, - callback=None, error=None): - """Add a channel to group. - - A channel can be added to group using this method. - - - Args: - channel_group: (string) - Channel Group name. It can also contain namespace. - If namespace is also specified, then the parameter - will be in format namespace:channel_group - channel: (string) - Can be a channel name, a list of channel names, - or a comma separated list of channel names - callback: (optional) - A callback method should be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - error: (optional) - Optional variable. An error method can be passed to - the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Sync Mode: dict - channel_group_add_channel method returns a dict indicating - status of the request - - { - u'status': 200, - u'message': 'OK', - u'service': u'channel-registry', - u'error': False - } - - Async Mode: None ( callback gets the response as parameter ) - - Response Format: - - The callback passed to channel_group_add_channel gets the a - dict indicating status of the request - - { - u'status': 200, - u'message': 'OK', - u'service': u'channel-registry', - u'error': False - } - - """ - - return self._channel_group(channel_group=channel_group, - channels=channel, mode='add', - callback=callback, error=error) - - def channel_group_remove_channel(self, channel_group, channel, - callback=None, error=None): - """Remove channel. - - A channel can be removed from a group method. - - - Args: - channel_group: (string) - Channel Group name. It can also contain namespace. - If namespace is also specified, then the parameter - will be in format namespace:channel_group - channel: (string) - Can be a channel name, a list of channel names, - or a comma separated list of channel names - callback: (optional) - A callback method should be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - error: (optional) - Optional variable. An error method can be passed - to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Sync Mode: dict - channel_group_remove_channel method returns a dict indicating - status of the request - - { - u'status': 200, - u'message': 'OK', - u'service': u'channel-registry', - u'error': False - } - - Async Mode: None ( callback gets the response as parameter ) - - Response Format: - - The callback passed to channel_group_remove_channel gets the - a dict indicating status of the request - - { - u'status': 200, - u'message': 'OK', - u'service': u'channel-registry', - u'error': False - } - - """ - - return self._channel_group(channel_group=channel_group, - channels=channel, mode='remove', - callback=callback, error=error) - - def channel_group_remove_group(self, channel_group, - callback=None, error=None): - """Remove channel group. - - A channel group can be removed using this method. - - - Args: - channel_group: (string) - Channel Group name. It can also contain namespace. - If namespace is also specified, then the parameter - will be in format namespace:channel_group - callback: (optional) - A callback method should be passed to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - error: (optional) - Optional variable. An error method can be passed - to the method. - If set, the api works in async mode. - Required argument when working with twisted or - tornado. - - Returns: - Sync Mode: dict - channel_group_remove_group method returns a dict indicating - status of the request - - { - u'status': 200, - u'message': 'OK', - u'service': u'channel-registry', - u'error': False - } - - Async Mode: None ( callback gets the response as parameter ) - - Response Format: - - The callback passed to channel_group_remove_group gets the a - dict indicating status of the request - - { - u'status': 200, - u'message': 'OK', - u'service': u'channel-registry', - u'error': False - } - - """ - - return self._channel_group(channel_group=channel_group, - mode='remove', callback=callback, - error=error) - - -class EmptyLock(): - - def __init__(self): - pass - - def __enter__(self): - pass - - def __exit__(self, a, b, c): - pass - -empty_lock = EmptyLock() - - -class PubnubCoreAsync(PubnubBase): - - def start(self): - pass - - def stop(self): - self._reset_offline() - - def nop(self): - pass - - def __init__( - self, - publish_key, - subscribe_key, - secret_key=None, - cipher_key=None, - auth_key=None, - ssl_on=False, - origin='pubsub.pubnub.com', - uuid=None, - _tt_lock=empty_lock, - _channel_list_lock=empty_lock, - _channel_group_list_lock=empty_lock - ): - - super(PubnubCoreAsync, self).__init__( - publish_key=publish_key, - subscribe_key=subscribe_key, - secret_key=secret_key, - cipher_key=cipher_key, - auth_key=auth_key, - ssl_on=ssl_on, - origin=origin, - uuid=uuid - ) - - self.subscriptions = {} - self.subscription_groups = {} - self.timetoken = 0 - self.last_timetoken = 0 - self.accept_encoding = 'gzip' - self.SUB_RECEIVER = None - self._connect = None - self._tt_lock = _tt_lock - self._channel_list_lock = _channel_list_lock - self._channel_group_list_lock = _channel_group_list_lock - self._connect = lambda: None - self.u = None - self.heartbeat = 0 - self.heartbeat_interval = 0 - self.heartbeat_running = False - self.heartbeat_stop_flag = False - self.abort_heartbeat = self.nop - self.heartbeat_callback = self.nop - self.heartbeat_error = self.nop - - def get_channel_list(self, channels, nopresence=False): - channel = '' - first = True - with self._channel_list_lock: - for ch in channels: - if nopresence is True and ch.find("-pnpres") >= 0: - continue - if not channels[ch]['subscribed']: - continue - if not first: - channel += ',' - else: - first = False - channel += ch - return channel - - def get_channel_group_list(self, channel_groups, nopresence=False): - channel_group = '' - first = True - with self._channel_group_list_lock: - for ch in channel_groups: - if nopresence is True and ch.find("-pnpres") >= 0: - continue - if not channel_groups[ch]['subscribed']: - continue - if not first: - channel_group += ',' - else: - first = False - channel_group += ch - return channel_group - - def get_channel_array(self, nopresence=False): - """Get List of currently subscribed channels - - Returns: - Returns a list containing names of channels subscribed - - Sample return value: - ["a","b","c] - """ - channels = self.subscriptions - channel = [] - with self._channel_list_lock: - for ch in channels: - if nopresence is True and ch.find("-pnpres") >= 0: - continue - if not channels[ch]['subscribed']: - continue - channel.append(ch) - return channel - - def get_channel_group_array(self, nopresence=False): - """Get List of currently subscribed channel groups - - Returns: - Returns a list containing names of channel groups subscribed - - Sample return value: - ["a","b","c] - """ - channel_groups = self.subscription_groups - channel_group = [] - with self._channel_group_list_lock: - for ch in channel_groups: - if nopresence is True and ch.find("-pnpres") >= 0: - continue - if not channel_groups[ch]['subscribed']: - continue - channel_group.append(ch) - return channel_group - - def each(l, func): - if func is None: - return - for i in l: - func(i) - - def restart_heartbeat(self): - self.stop_heartbeat() - self.start_heartbeat() - - def stop_heartbeat(self): - self.abort_heartbeat() - self.heartbeat_running = False - self.heartbeat_stop_flag = False - - def start_heartbeat(self): - if self.heartbeat_running is True: - return - self._presence_heartbeat() - - def _presence_heartbeat(self): - if (self.heartbeat_interval is None or self.heartbeat_interval > 500 or - self.heartbeat_interval < 1): - self.heartbeat_stop_flag = True - - if (len(self.get_channel_list(self.subscriptions, True)) == 0 and - len(self.get_channel_group_list(self.subscription_groups, True)) == 0): - self.heartbeat_stop_flag = True - - if self.heartbeat_stop_flag is True: - self.heartbeat_running = False - self.heartbeat_stop_flag = False - return - - def _callback(resp): - if self.heartbeat_callback is not None: - self.heartbeat_callback(resp) - self.abort_heartbeat = self.timeout( - self.heartbeat_interval, self._presence_heartbeat) - - def _error(resp): - if self.heartbeat_error is not None: - self.heartbeat_error(resp) - self.abort_heartbeat = self.timeout( - self.heartbeat_interval, self._presence_heartbeat) - - self.heartbeat_running = True - self.presence_heartbeat(_callback, _error) - - def set_heartbeat(self, heartbeat, callback=None, error=None): - self.heartbeat = heartbeat - self.heartbeat_interval = (self.heartbeat / 2) - 1 - if self.heartbeat == 2: - self.heartbeat_interval = 1 - self.restart_heartbeat() - with self._tt_lock: - self.last_timetoken = self.timetoken if self.timetoken != 0 \ - else self.last_timetoken - self.timetoken = 0 - self._connect() - self.heartbeat_callback = callback - self.heartbeat_error = error - - def get_heartbeat(self): - return self.heartbeat - - def set_heartbeat_interval(self, heartbeat_interval): - self.heartbeat_interval = heartbeat_interval - self.start_heartbeat() - - def get_heartbeat_interval(self): - return self.heartbeat_interval - - def presence_heartbeat(self, callback=None, error=None): - - data = {'auth': self.auth_key, 'pnsdk': self.pnsdk, - 'uuid': self.uuid} - - st = json.dumps(self.STATE) - - if len(st) > 2: - data['state'] = st - - channels = self.get_channel_list(self.subscriptions, True) - channel_groups = self.get_channel_group_list( - self.subscription_groups, True) - - if channels is None: - channels = ',' - - if channel_groups is not None and len(channel_groups) > 0: - data['channel-group'] = channel_groups - - if self.heartbeat > 0 and self.heartbeat < 320: - data['heartbeat'] = self.heartbeat - - ## Send Heartbeat - return self._request({"urlcomponents": [ - 'v2', 'presence', 'sub-key', - self.subscribe_key, - 'channel', - channels, - 'heartbeat' - ], 'urlparams': data}, - callback=self._return_wrapped_callback(callback), - error=self._return_wrapped_callback(error)) - - def subscribe(self, channels, callback, state=None, error=None, - connect=None, disconnect=None, reconnect=None, - presence=None, sync=False): - """Subscribe to data on a channel. - - This function 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 subscribe_key at initialization. - - Only works in async mode - - Args: - channel: (string/list) - Specifies the channel to subscribe to. It is possible - to specify multiple channels as a comma separated list - or array. - - callback: (function) - This callback is called on receiving a message from - the channel. - - state: (dict) - State to be set. - - error: (function) (optional) - This callback is called on an error event - - connect: (function) (optional) - This callback is called on a successful connection to - the PubNub cloud - - disconnect: (function) (optional) - This callback is called on client disconnect from the - PubNub cloud - - reconnect: (function) (optional) - This callback is called on successfully re-connecting - to the PubNub cloud - - Returns: - None - """ - - return self._subscribe( - channels=channels, callback=callback, state=state, error=error, - connect=connect, disconnect=disconnect, reconnect=reconnect, - presence=presence) - - def subscribe_group(self, channel_groups, callback, error=None, - connect=None, disconnect=None, reconnect=None, - sync=False): - """Subscribe to data on a channel group. - - This function 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 group the client must - send the appropriate subscribe_key at initialization. - - Only works in async mode - - Args: - channel_groups: (string/list) - Specifies the channel groups to subscribe to. It is - possible to specify multiple channel groups as a comma - separated list or array. - - callback: (function) - This callback is called on receiving a message from - the channel. - - error: (function) (optional) - This callback is called on an error event - - connect: (function) (optional) - This callback is called on a successful connection to - the PubNub cloud - - disconnect: (function) (optional) - This callback is called on client disconnect from the - PubNub cloud - - reconnect: (function) (optional) - This callback is called on successfully re-connecting - to the PubNub cloud - - Returns: - None - """ - - return self._subscribe( - channel_groups=channel_groups, callback=callback, error=error, - connect=connect, disconnect=disconnect, reconnect=reconnect) - - def _subscribe( - self, channels=None, channel_groups=None, state=None, callback=None, - error=None, connect=None, disconnect=None, reconnect=None, - presence=None): - - with self._tt_lock: - self.last_timetoken = self.timetoken if self.timetoken != 0 \ - else self.last_timetoken - self.timetoken = 0 - - def _invoke(func, msg=None, channel=None, real_channel=None): - if func is not None: - if (msg is not None and channel is not None and - real_channel is not None): - try: - func(get_data_for_user(msg), channel, real_channel) - except: - func(get_data_for_user(msg), channel) - elif msg is not None and channel is not None: - func(get_data_for_user(msg), channel) - elif msg is not None: - func(get_data_for_user(msg)) - else: - func() - - def _invoke_connect(): - if self._channel_list_lock: - with self._channel_list_lock: - x = copy.copy(self.subscriptions) - for ch in x: - chobj = x[ch] - if chobj['connected'] is False: - chobj['connected'] = True - chobj['disconnected'] = False - _invoke(chobj['connect'], chobj['name']) - else: - if chobj['disconnected'] is True: - chobj['disconnected'] = False - _invoke(chobj['reconnect'], chobj['name']) - - if self._channel_group_list_lock: - with self._channel_group_list_lock: - for ch in self.subscription_groups: - chobj = self.subscription_groups[ch] - if chobj['connected'] is False: - chobj['connected'] = True - chobj['disconnected'] = False - _invoke(chobj['connect'], chobj['name']) - else: - if chobj['disconnected'] is True: - chobj['disconnected'] = False - _invoke(chobj['reconnect'], chobj['name']) - - def _invoke_disconnect(): - if self._channel_list_lock: - with self._channel_list_lock: - for ch in self.subscriptions: - chobj = self.subscriptions[ch] - if chobj['connected'] is True: - if chobj['disconnected'] is False: - chobj['disconnected'] = True - _invoke(chobj['disconnect'], chobj['name']) - if self._channel_group_list_lock: - with self._channel_group_list_lock: - for ch in self.subscription_groups: - chobj = self.subscription_groups[ch] - if chobj['connected'] is True: - if chobj['disconnected'] is False: - chobj['disconnected'] = True - _invoke(chobj['disconnect'], chobj['name']) - - def _invoke_error(channel_list=None, error=None): - if channel_list is None: - for ch in self.subscriptions: - chobj = self.subscriptions[ch] - try: - _invoke(chobj['error'], error, ch) - except TypeError: - _invoke(chobj['error'], error) - else: - for ch in channel_list: - chobj = self.subscriptions[ch] - try: - _invoke(chobj['error'], error, ch) - except TypeError: - _invoke(chobj['error'], error) - - def _get_channel(): - for ch in self.subscriptions: - chobj = self.subscriptions[ch] - if chobj['subscribed'] is True: - return chobj - - if channels is not None: - channels = channels if isinstance( - channels, list) else channels.split(",") - for channel in channels: - ## New Channel? - if len(channel) > 0 and \ - (channel not in self.subscriptions or - self.subscriptions[channel]['subscribed'] is False): - with self._channel_list_lock: - self.subscriptions[channel] = { - 'name': channel, - 'first': False, - 'connected': False, - 'disconnected': True, - 'subscribed': True, - 'callback': callback, - 'connect': connect, - 'disconnect': disconnect, - 'reconnect': reconnect, - 'error': error, - 'presence': presence - } - if state is not None: - if channel in self.STATE: - self.STATE[channel] = state[channel] - else: - self.STATE[channel] = state - - if channel_groups is not None: - channel_groups = channel_groups if isinstance( - channel_groups, list) else channel_groups.split(",") - - for channel_group in channel_groups: - ## New Channel? - if (len(channel_group) > 0 and - (channel_group not in self.subscription_groups or - self.subscription_groups[channel_group]['subscribed'] - is False)): - with self._channel_group_list_lock: - self.subscription_groups[channel_group] = { - 'name': channel_group, - 'first': False, - 'connected': False, - 'disconnected': True, - 'subscribed': True, - 'callback': callback, - 'connect': connect, - 'disconnect': disconnect, - 'reconnect': reconnect, - 'error': error, - 'presence': presence - } - - ''' - ## return if already connected to channel - if channel in self.subscriptions and \ - 'connected' in self.subscriptions[channel] and \ - self.subscriptions[channel]['connected'] is True: - _invoke(error, "Already Connected") - return - ''' - - self.restart_heartbeat() - - ## SUBSCRIPTION RECURSION - def _connect(): - - self._reset_offline() - - def error_callback(response): - ## ERROR ? - if not response or \ - ('message' in response and - response['message'] == 'Forbidden'): - _invoke_error(channel_list=response['payload'][ - 'channels'], error=response['message']) - self.timeout(1, _connect) - return - if 'message' in response: - _invoke_error(error=response['message']) - else: - _invoke_disconnect() - self.timetoken = 0 - self.timeout(1, _connect) - - def sub_callback(response): - ## ERROR ? - if not response or \ - ('message' in response and - response['message'] == 'Forbidden'): - _invoke_error(channel_list=response['payload'][ - 'channels'], error=response['message']) - _connect() - return - - _invoke_connect() - - with self._tt_lock: - self.timetoken = \ - self.last_timetoken if self.timetoken == 0 and \ - self.last_timetoken != 0 else response[1] - - if len(response) > 3: - channel_list = response[2].split(',') - channel_list_2 = response[3].split(',') - response_list = response[0] - for ch in enumerate(channel_list): - if (ch[1] in self.subscription_groups or - ch[1] in self.subscriptions): - try: - chobj = self.subscription_groups[ch[1]] - except KeyError: - chobj = self.subscriptions[ch[1]] - - if ('-pnpres' in channel_list_2[ch[0]]): - cb = chobj['presence'] - else: - cb = chobj['callback'] - _invoke(cb, - self.decrypt(response_list[ch[0]]), - chobj['name'].split('-pnpres')[0], - channel_list_2[ch[0]].split - ('-pnpres')[0]) - elif len(response) > 2: - channel_list = response[2].split(',') - response_list = response[0] - for ch in enumerate(channel_list): - if ch[1] in self.subscriptions: - chobj = self.subscriptions[ch[1]] - _invoke(chobj['callback'], - self.decrypt(response_list[ch[0]]), - chobj['name'].split('-pnpres')[0]) - else: - response_list = response[0] - chobj = _get_channel() - for r in response_list: - if chobj: - _invoke(chobj['callback'], self.decrypt(r), - chobj['name'].split('-pnpres')[0]) - - _connect() - - channel_list = self.get_channel_list(self.subscriptions) - channel_group_list = self.get_channel_group_list( - self.subscription_groups) - - if len(channel_list) <= 0 and len(channel_group_list) <= 0: - return - - if len(channel_list) <= 0: - channel_list = ',' - - data = {"uuid": self.uuid, "auth": self.auth_key, - 'pnsdk': self.pnsdk, 'channel-group': channel_group_list} - - st = json.dumps(self.STATE) - - if len(st) > 2: - data['state'] = quote(st, safe="") - - if self.heartbeat > 0: - data["heartbeat"] = self.heartbeat - - ## CONNECT TO PUBNUB SUBSCRIBE SERVERS - #try: - self.SUB_RECEIVER = self._request({"urlcomponents": [ - 'subscribe', - self.subscribe_key, - channel_list, - '0', - str(self.timetoken) - ], "urlparams": data}, - sub_callback, - error_callback, - single=True, timeout=320) - ''' - except Exception as e: - self.timeout(1, _connect) - return - ''' - - self._connect = _connect - - ## BEGIN SUBSCRIPTION (LISTEN FOR MESSAGES) - _connect() - - def _reset_offline(self): - if self.SUB_RECEIVER is not None: - self.SUB_RECEIVER() - self.SUB_RECEIVER = None - - def CONNECT(self): - self._reset_offline() - self._connect() - - def unsubscribe(self, channel): - """Unsubscribe from channel . - Only works in async mode - - Args: - channel: Channel name ( string ) - """ - if channel in self.subscriptions is False: - return False - - ## DISCONNECT - with self._channel_list_lock: - if channel in self.subscriptions: - self.subscriptions[channel]['connected'] = 0 - self.subscriptions[channel]['subscribed'] = False - self.subscriptions[channel]['timetoken'] = 0 - self.subscriptions[channel]['first'] = False - self.leave_channel(channel=channel) - - # remove channel from STATE - self.STATE.pop(channel, None) - - self.CONNECT() - - def unsubscribe_group(self, channel_group): - """Unsubscribe from channel group. - Only works in async mode - - Args: - channel_group: Channel group name ( string ) - """ - if channel_group in self.subscription_groups is False: - return False - - ## DISCONNECT - with self._channel_group_list_lock: - if channel_group in self.subscription_groups: - self.subscription_groups[channel_group]['connected'] = 0 - self.subscription_groups[channel_group]['subscribed'] = False - self.subscription_groups[channel_group]['timetoken'] = 0 - self.subscription_groups[channel_group]['first'] = False - self.leave_group(channel_group=channel_group) - self.CONNECT() - - -class PubnubCore(PubnubCoreAsync): - def __init__( - self, - publish_key, - subscribe_key, - secret_key=None, - cipher_key=None, - auth_key=None, - ssl_on=False, - origin='pubsub.pubnub.com', - uuid=None, - _tt_lock=None, - _channel_list_lock=None, - _channel_group_list_lock=None - - ): - super(PubnubCore, self).__init__( - publish_key=publish_key, - subscribe_key=subscribe_key, - secret_key=secret_key, - cipher_key=cipher_key, - auth_key=auth_key, - ssl_on=ssl_on, - origin=origin, - uuid=uuid, - _tt_lock=_tt_lock, - _channel_list_lock=_channel_list_lock, - _channel_group_list_lock=_channel_group_list_lock - ) - - self.subscriptions = {} - self.timetoken = 0 - self.accept_encoding = 'gzip' - - -class Timer: - def __init__(self, timeout, func, daemon=False, *argv): - self.timeout = timeout - self.func = func - self.argv = argv - self.stop = False - self.thread = None - self.daemon = daemon - - def cancel(self): - self.stop = True - self.func = None - - def run(self): - time.sleep(self.timeout) - if self.func is not None: - if self.argv is None and len(self.argv) == 0: - self.func() - else: - self.func(*(self.argv)) - - def start(self): - self.thread = threading.Thread(target=self.run) - self.thread.daemon = self.daemon - self.thread.start() - - -class HTTPClient: - def __init__(self, pubnub, url, urllib_func=None, - callback=None, error=None, id=None, timeout=15): - self.url = url - self.id = id - self.callback = callback - self.error = error - self.stop = False - self._urllib_func = urllib_func - self.timeout = timeout - self.pubnub = pubnub - - def cancel(self): - self.stop = True - self.callback = None - self.error = None - - def run(self): - - def _invoke(func, data): - if func is not None: - func(get_data_for_user(data)) - - if self._urllib_func is None: - return - - resp = self._urllib_func(self.url, timeout=self.timeout) - data = resp[0] - code = resp[1] - - if self.stop is True: - return - if self.callback is None: - with self.pubnub.latest_sub_callback_lock: - if self.pubnub.latest_sub_callback['id'] != self.id: - return - else: - if (self.pubnub.latest_sub_callback['callback'] - is not None): - self.pubnub.latest_sub_callback['id'] = 0 - try: - data = json.loads(data) - except ValueError: - _invoke(self.pubnub.latest_sub_callback['error'], - {'error': 'json decoding error'}) - return - if code != 200: - _invoke(self.pubnub.latest_sub_callback[ - 'error'], data) - else: - _invoke(self.pubnub.latest_sub_callback[ - 'callback'], data) - else: - try: - data = json.loads(data) - except ValueError: - _invoke(self.error, {'error': 'json decoding error'}) - return - - if code != 200: - _invoke(self.error, data) - else: - _invoke(self.callback, data) - - -def _urllib_request_2(url, timeout=15): - try: - resp = urllib2.urlopen(url, timeout=timeout) - except urllib2.HTTPError as http_error: - resp = http_error - except urllib2.URLError as error: - msg = {"message": str(error.reason)} - return (json.dumps(msg), 0) - - return (resp.read(), resp.code) - - -class PubnubHTTPAdapter(HTTPAdapter): - def init_poolmanager(self, *args, **kwargs): - kwargs.setdefault('socket_options', default_socket_options) - - super(PubnubHTTPAdapter, self).init_poolmanager(*args, **kwargs) - -s = requests.Session() -#s.mount('http://', PubnubHTTPAdapter(max_retries=1)) -#s.mount('https://', PubnubHTTPAdapter(max_retries=1)) -#s.mount('http://pubsub.pubnub.com', HTTPAdapter(max_retries=1)) -#s.mount('https://pubsub.pubnub.com', HTTPAdapter(max_retries=1)) - - -def _requests_request(url, timeout=15): - try: - resp = s.get(url, timeout=timeout) - except requests.exceptions.HTTPError as http_error: - resp = http_error - except requests.exceptions.ConnectionError as error: - msg = str(error) - return (json.dumps(msg), 0) - except requests.exceptions.Timeout as error: - msg = str(error) - return (json.dumps(msg), 0) - return (resp.text, resp.status_code) - - -def _urllib_request_3(url, timeout=15): - try: - resp = urllib.request.urlopen(url, timeout=timeout) - except (urllib.request.HTTPError, urllib.request.URLError) as http_error: - resp = http_error - r = resp.read().decode("utf-8") - return (r, resp.code) - -_urllib_request = None - - -# Pubnub - -class Pubnub(PubnubCore): - def __init__( - self, - publish_key, - subscribe_key, - secret_key=None, - cipher_key=None, - auth_key=None, - ssl_on=False, - origin='pubsub.pubnub.com', - uuid=None, - pooling=True, - daemon=False, - pres_uuid=None, - azure=False - ): - super(Pubnub, self).__init__( - publish_key=publish_key, - subscribe_key=subscribe_key, - secret_key=secret_key, - cipher_key=cipher_key, - auth_key=auth_key, - ssl_on=ssl_on, - origin=origin, - uuid=uuid or pres_uuid, - _tt_lock=threading.RLock(), - _channel_list_lock=threading.RLock(), - _channel_group_list_lock=threading.RLock() - ) - global _urllib_request - if self.python_version == 2: - _urllib_request = _urllib_request_2 - else: - _urllib_request = _urllib_request_3 - - if pooling is True: - _urllib_request = _requests_request - - self.latest_sub_callback_lock = threading.RLock() - self.latest_sub_callback = {'id': None, 'callback': None} - self.pnsdk = 'PubNub-Python' + '/' + self.version - self.daemon = daemon - - if azure is False: - s.mount('http://pubsub.pubnub.com', HTTPAdapter(max_retries=1)) - s.mount('https://pubsub.pubnub.com', HTTPAdapter(max_retries=1)) - else: - s.mount('http://', PubnubHTTPAdapter(max_retries=1)) - s.mount('https://', PubnubHTTPAdapter(max_retries=1)) - - def timeout(self, interval, func1, *argv): - timer = Timer(interval, func1, False, *argv) - timer.start() - - return timer.cancel - - def _request_async(self, url, callback=None, error=None, single=False, - timeout=15): - global _urllib_request - - if single is True: - id = time.time() - client = HTTPClient(self, url=url, urllib_func=_urllib_request, - callback=None, error=None, id=id, - timeout=timeout) - with self.latest_sub_callback_lock: - self.latest_sub_callback['id'] = id - self.latest_sub_callback['callback'] = callback - self.latest_sub_callback['error'] = error - else: - client = HTTPClient(self, url=url, urllib_func=_urllib_request, - callback=callback, error=error, - timeout=timeout) - - thread = threading.Thread(target=client.run) - thread.daemon = self.daemon - thread.start() - - def abort(): - client.cancel() - return abort - - def _request_sync(self, url, timeout=15): - global _urllib_request - - ## Send Request Expecting JSONP Response - response = _urllib_request(url, timeout=timeout) - try: - resp_json = json.loads(response[0]) - except ValueError: - return [0, "JSON Error"] - - if (response[1] != 200 and 'message' in resp_json and - 'payload' in resp_json): - return {'message': resp_json['message'], - 'payload': resp_json['payload']} - - if response[1] == 0: - return [0, resp_json] - - return resp_json - - def _request(self, request, callback=None, error=None, single=False, - timeout=15, encoder_map=None): - - url = self.getUrl(request, encoder_map) - - if callback is None: - return get_data_for_user(self._request_sync(url, - timeout=timeout)) - else: - return self._request_async(url, callback, error, - single=single, timeout=timeout) - -# Pubnub Twisted - - -class PubnubTwisted(PubnubCoreAsync): - - def start(self): - reactor.run() - - def stop(self): - reactor.stop() - - def timeout(self, delay, callback, *args): - def cb(): - if callback is not None: - callback(*args) - - timeout = reactor.callLater(delay, cb) - - def cancel(): - if timeout.active(): - timeout.cancel() - - return cancel - - def __init__( - self, - publish_key, - subscribe_key, - secret_key=None, - cipher_key=None, - auth_key=None, - ssl_on=False, - origin='pubsub.pubnub.com', - uuid=None - ): - super(PubnubTwisted, self).__init__( - publish_key=publish_key, - subscribe_key=subscribe_key, - secret_key=secret_key, - cipher_key=cipher_key, - auth_key=auth_key, - ssl_on=ssl_on, - origin=origin, - uuid=uuid - ) - self.headers = {} - self.headers['User-Agent'] = ['Python-Twisted'] - self.headers['V'] = [self.version] - self.pnsdk = 'PubNub-Python-' + 'Twisted' + '/' + self.version - - def _request(self, request, callback=None, error=None, - single=False, timeout=15, encoder_map=None): - global pnconn_pool - - def _invoke(func, data): - if func is not None: - func(get_data_for_user(data)) - - ## Build URL - - url = self.getUrl(request, encoder_map) - - agent = ContentDecoderAgent(RedirectAgent(Agent( - reactor, - contextFactory=WebClientContextFactory(), - pool=self.ssl and None or pnconn_pool - )), [('gzip', GzipDecoder)]) - - try: - request = agent.request( - 'GET', url, Headers(self.headers), None) - except TypeError: - request = agent.request( - 'GET', url.encode(), Headers(self.headers), None) - - if single is True: - id = time.time() - self.id = id - - def received(response): - if not isinstance(response, twisted.web._newclient.Response) and \ - not isinstance(response, twisted.web.client.GzipDecoder): - if response is None: - return - message = "not found" - try: - message = response.getErrorMessage() - except: - pass - _invoke(error, {"message": message}) - return - - finished = Deferred() - if response.code in [401, 403]: - response.deliverBody(PubNubPamResponse(finished)) - else: - response.deliverBody(PubNubResponse(finished)) - - return finished - - def complete(data): - if data is None: - return - - if single is True: - if id != self.id: - return None - try: - data = json.loads(data) - except ValueError: - try: - data = json.loads(data.decode("utf-8")) - except ValueError: - _invoke(error, {'error': 'json decode error'}) - - if 'error' in data and 'status' in data and 'status' != 200: - _invoke(error, data) - else: - _invoke(callback, data) - - def abort(): - pass - - request.addErrback(received) - request.addCallback(received) - request.addCallback(complete) - - return abort - - -# PubnubTornado -class PubnubTornado(PubnubCoreAsync): - - def stop(self): - ioloop.stop() - - def start(self): - ioloop.start() - - def timeout(self, delay, callback, *args): - handle = None - - def cancel(): - ioloop.remove_timeout(handle) - - def cb(): - if callback is not None: - callback(*args) - - handle = ioloop.add_timeout(time.time() + float(delay), cb) - - return cancel - - def __init__( - self, - publish_key, - subscribe_key, - secret_key=False, - cipher_key=False, - auth_key=False, - ssl_on=False, - origin='pubsub.pubnub.com', - uuid=None - ): - super(PubnubTornado, self).__init__( - publish_key=publish_key, - subscribe_key=subscribe_key, - secret_key=secret_key, - cipher_key=cipher_key, - auth_key=auth_key, - ssl_on=ssl_on, - origin=origin, - uuid=uuid - ) - self.headers = {} - self.headers['User-Agent'] = 'Python-Tornado' - self.headers['Accept-Encoding'] = self.accept_encoding - self.headers['V'] = self.version - self.http = tornado.httpclient.AsyncHTTPClient(max_clients=1000) - self.id = None - self.pnsdk = 'PubNub-Python-' + 'Tornado' + '/' + self.version - - def _request(self, request, callback=None, error=None, - single=False, timeout=15, connect_timeout=5, - encoder_map=None): - - def _invoke(func, data): - if func is not None: - func(get_data_for_user(data)) - - url = self.getUrl(request, encoder_map) - request = tornado.httpclient.HTTPRequest( - url, 'GET', - self.headers, - connect_timeout=connect_timeout, - request_timeout=timeout) - if single is True: - id = time.time() - self.id = id - - def responseCallback(response): - if single is True: - if not id == self.id: - return None - - body = response._get_body() - - if body is None: - return - - def handle_exc(*args): - return True - if response.error is not None: - with ExceptionStackContext(handle_exc): - if response.code in [403, 401]: - response.rethrow() - else: - _invoke(error, {"message": response.reason}) - return - - try: - data = json.loads(body) - except TypeError: - try: - data = json.loads(body.decode("utf-8")) - except ValueError: - _invoke(error, {'error': 'json decode error'}) - - if 'error' in data and 'status' in data and 'status' != 200: - _invoke(error, data) - else: - _invoke(callback, data) - - self.http.fetch( - request=request, - callback=responseCallback - ) - - def abort(): - pass - - return abort diff --git a/pubnub/__init__.py b/pubnub/__init__.py new file mode 100644 index 00000000..eeeaadb9 --- /dev/null +++ b/pubnub/__init__.py @@ -0,0 +1,17 @@ +import logging +import os + +PUBNUB_ROOT = os.path.dirname(os.path.abspath(__file__)) + + +def set_stream_logger(name='pubnub', level=logging.ERROR, format_string=None, stream=None): + if format_string is None: + format_string = "%(asctime)s %(name)s [%(levelname)s] %(message)s" + + logger = logging.getLogger(name) + logger.setLevel(level) + handler = logging.StreamHandler(stream) + handler.setLevel(level) + formatter = logging.Formatter(format_string) + handler.setFormatter(formatter) + logger.addHandler(handler) diff --git a/pubnub/builders.py b/pubnub/builders.py new file mode 100644 index 00000000..d4a58e06 --- /dev/null +++ b/pubnub/builders.py @@ -0,0 +1,68 @@ +from abc import ABCMeta, abstractmethod +from .dtos import SubscribeOperation, UnsubscribeOperation +from . import utils + + +class PubSubBuilder(object): + __metaclass__ = ABCMeta + + def __init__(self, subscription_manager): + self._subscription_manager = subscription_manager + self._channel_subscriptions = [] + self._channel_group_subscriptions = [] + + # TODO: make the 'channel' alias + def channels(self, channels_list): + utils.extend_list(self._channel_subscriptions, channels_list) + + return self + + def channel_groups(self, channel_groups_list): + utils.extend_list(self._channel_group_subscriptions, channel_groups_list) + + return self + + @abstractmethod + def execute(self): + pass + + +class SubscribeBuilder(PubSubBuilder): + def __init__(self, subscription_manager): + super(SubscribeBuilder, self).__init__(subscription_manager) + self._presence_enabled = False + self._timetoken = 0 + + def with_presence(self): + self._presence_enabled = True + return self + + def with_timetoken(self, timetoken): + self._timetoken = timetoken + return self + + def channel_subscriptions(self): + return self._channel_subscriptions + + def channel_group_subscriptions(self): + return self._channel_group_subscriptions + + def execute(self): + subscribe_operation = SubscribeOperation( + channels=self._channel_subscriptions, + channel_groups=self._channel_group_subscriptions, + timetoken=self._timetoken, + presence_enabled=self._presence_enabled + ) + + self._subscription_manager.adapt_subscribe_builder(subscribe_operation) + + +class UnsubscribeBuilder(PubSubBuilder): + def execute(self): + unsubscribe_operation = UnsubscribeOperation( + channels=self._channel_subscriptions, + channel_groups=self._channel_group_subscriptions + ) + + self._subscription_manager.adapt_unsubscribe_builder(unsubscribe_operation) diff --git a/pubnub/callbacks.py b/pubnub/callbacks.py new file mode 100644 index 00000000..f68153c5 --- /dev/null +++ b/pubnub/callbacks.py @@ -0,0 +1,29 @@ +from abc import ABCMeta, abstractmethod + + +class PNCallback(object): + @abstractmethod + def on_response(self, x, status): + pass + + +class SubscribeCallback(object): + __metaclass__ = ABCMeta + + @abstractmethod + def status(self, pubnub, status): + pass + + @abstractmethod + def message(self, pubnub, message): + pass + + @abstractmethod + def presence(self, pubnub, presence): + pass + + +class ReconnectionCallback(object): + @abstractmethod + def on_reconnect(self): + pass diff --git a/pubnub/crypto.py b/pubnub/crypto.py new file mode 100644 index 00000000..4c9b3def --- /dev/null +++ b/pubnub/crypto.py @@ -0,0 +1,69 @@ +import hashlib +import json +import sys + +from .crypto_core import PubNubCrypto +from Cryptodome.Cipher import AES + +Initial16bytes = '0123456789012345' + +if sys.version_info > (3, 0): + v = 3 +else: + v = 2 + +try: + from base64 import decodebytes, encodebytes +except ImportError: + from base64 import decodestring, encodestring + +try: + from hashlib import sha256 + digestmod = sha256 +except ImportError: + import Cryptodome.Hash.SHA256 as digestmod + sha256 = digestmod.new + + +class PubNubCryptodome(PubNubCrypto): + def encrypt(self, key, msg): + secret = self.get_secret(key) + + if v == 3: + cipher = AES.new(bytes(secret[0:32], 'utf-8'), AES.MODE_CBC, bytes(Initial16bytes, 'utf-8')) + return encodebytes(cipher.encrypt(self.pad(msg.encode('utf-8')))).decode('utf-8').replace("\n", "") + else: + cipher = AES.new(secret[0:32], AES.MODE_CBC, Initial16bytes) + return encodestring(cipher.encrypt(self.pad(msg))).replace("\n", "") + + def decrypt(self, key, msg): + secret = self.get_secret(key) + + if v == 3: + cipher = AES.new(bytes(secret[0:32], 'utf-8'), AES.MODE_CBC, bytes(Initial16bytes, 'utf-8')) + plain = self.depad((cipher.decrypt(decodebytes(msg.encode('utf-8')))).decode('utf-8')) + else: + cipher = AES.new(secret[0:32], AES.MODE_CBC, Initial16bytes) + plain = self.depad(cipher.decrypt(decodestring(msg))) + + try: + return json.loads(plain) + except Exception: + return plain + + def pad(self, msg, block_size=16): + padding = block_size - (len(msg) % block_size) + + if v == 3: + return msg + (chr(padding) * padding).encode('utf-8') + else: + return msg + chr(padding) * padding + + def depad(self, msg): + return msg[0:-ord(msg[-1])] + + def get_secret(self, key): + if v == 3: + return hashlib.sha256(key.encode("utf-8")).hexdigest() + else: + return hashlib.sha256(key).hexdigest() diff --git a/pubnub/crypto_core.py b/pubnub/crypto_core.py new file mode 100644 index 00000000..d050f2cb --- /dev/null +++ b/pubnub/crypto_core.py @@ -0,0 +1,11 @@ +from abc import abstractmethod + + +class PubNubCrypto: + @abstractmethod + def encrypt(self, key, msg): + pass + + @abstractmethod + def decrypt(self, key, msg): + pass diff --git a/pubnub/crypto_legacy.py b/pubnub/crypto_legacy.py new file mode 100644 index 00000000..37091677 --- /dev/null +++ b/pubnub/crypto_legacy.py @@ -0,0 +1,82 @@ +import hashlib +import json +import sys + +from .crypto_core import PubNubCrypto +from Crypto.Cipher import AES + +Initial16bytes = '0123456789012345' + +if sys.version_info > (3, 0): + v = 3 +else: + v = 2 + +try: + from base64 import decodebytes, encodebytes +except ImportError: + from base64 import decodestring, encodestring + +try: + from hashlib import sha256 + digestmod = sha256 +except ImportError: + import Crypto.Hash.SHA256 as digestmod + sha256 = digestmod.new + + +class PubNubCryptoLegacy(PubNubCrypto): + """ + Provides a crypto utils using a legacy pycrypto library. + Useful for GAE standard environment, which doesn't support Cryptodome yet. + + To use it you should explicitly assign it while configuring PNConfiguration() object: + + from pubnub.crypto_legacy import PubNubCryptoLegacy + + config = PNConfiguration() + config.crypto_instance = PubNubCryptoLegacy() + pubnub = PubNub(config) + """ + + def encrypt(self, key, msg): + secret = self.get_secret(key) + + if v == 3: + cipher = AES.new(bytes(secret[0:32], 'utf-8'), AES.MODE_CBC, bytes(Initial16bytes, 'utf-8')) + return encodebytes(cipher.encrypt(self.pad(msg.encode('utf-8')))).decode('utf-8').replace("\n", "") + else: + cipher = AES.new(secret[0:32], AES.MODE_CBC, Initial16bytes) + return encodestring(cipher.encrypt(self.pad(msg))).replace("\n", "") + + def decrypt(self, key, msg): + secret = self.get_secret(key) + + if v == 3: + cipher = AES.new(bytes(secret[0:32], 'utf-8'), AES.MODE_CBC, bytes(Initial16bytes, 'utf-8')) + plain = self.depad((cipher.decrypt(decodebytes(msg.encode('utf-8')))).decode('utf-8')) + else: + cipher = AES.new(secret[0:32], AES.MODE_CBC, Initial16bytes) + plain = self.depad(cipher.decrypt(decodestring(msg))) + + try: + return json.loads(plain) + except Exception: + return plain + + def pad(self, msg, block_size=16): + padding = block_size - (len(msg) % block_size) + + if v == 3: + return msg + (chr(padding) * padding).encode('utf-8') + else: + return msg + chr(padding) * padding + + def depad(self, msg): + return msg[0:-ord(msg[-1])] + + def get_secret(self, key): + if v == 3: + return hashlib.sha256(key.encode("utf-8")).hexdigest() + else: + return hashlib.sha256(key).hexdigest() diff --git a/pubnub/dtos.py b/pubnub/dtos.py new file mode 100644 index 00000000..18c418d5 --- /dev/null +++ b/pubnub/dtos.py @@ -0,0 +1,30 @@ +import six + + +class SubscribeOperation(object): + def __init__(self, channels=None, channel_groups=None, presence_enabled=None, timetoken=None): + assert isinstance(channels, (list, tuple)) + assert isinstance(channel_groups, (list, tuple)) + assert isinstance(presence_enabled, bool) + assert isinstance(timetoken, six.integer_types) + + self.channels = channels + self.channel_groups = channel_groups + self.presence_enabled = presence_enabled + self.timetoken = timetoken + + +class UnsubscribeOperation(object): + def __init__(self, channels=None, channel_groups=None): + assert isinstance(channels, (list, tuple)) + assert isinstance(channel_groups, (list, tuple)) + + self.channels = channels + self.channel_groups = channel_groups + + +class StateOperation(object): + def __init__(self, channels=None, channel_groups=None, state=None): + self.channels = channels + self.channel_groups = channel_groups + self.state = state diff --git a/pubnub/endpoints/__init__.py b/pubnub/endpoints/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pubnub/endpoints/access/__init__.py b/pubnub/endpoints/access/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pubnub/endpoints/access/audit.py b/pubnub/endpoints/access/audit.py new file mode 100644 index 00000000..935958b0 --- /dev/null +++ b/pubnub/endpoints/access/audit.py @@ -0,0 +1,80 @@ +from pubnub import utils +from pubnub.endpoints.endpoint import Endpoint +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.access_manager import PNAccessManagerAuditResult + + +class Audit(Endpoint): + AUDIT_PATH = "/v1/auth/audit/sub-key/%s" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._auth_keys = [] + self._channels = [] + self._groups = [] + self._read = None + self._write = None + self._manage = None + self._ttl = None + + self._sort_params = True + + def auth_keys(self, auth_keys): + utils.extend_list(self._auth_keys, auth_keys) + return self + + def channels(self, channels): + utils.extend_list(self._channels, channels) + return self + + def channel_groups(self, channel_groups): + utils.extend_list(self._groups, channel_groups) + return self + + def custom_params(self): + params = {} + + if len(self._auth_keys) > 0: + params['auth'] = utils.join_items_and_encode(self._auth_keys) + + if len(self._channels) > 0: + params['channel'] = utils.join_items_and_encode(self._channels) + + if len(self._groups) > 0: + params['channel-group'] = utils.join_items_and_encode(self._groups) + + return params + + def build_path(self): + return Audit.AUDIT_PATH % self.pubnub.config.subscribe_key + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + self.validate_secret_key() + + def create_response(self, envelope): + return PNAccessManagerAuditResult.from_json(envelope['payload']) + + def is_auth_required(self): + return False + + def affected_channels(self): + return self._channels + + def affected_channels_groups(self): + return self._groups + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNAccessManagerAudit + + def name(self): + return "Grant" diff --git a/pubnub/endpoints/access/grant.py b/pubnub/endpoints/access/grant.py new file mode 100644 index 00000000..64dbff1a --- /dev/null +++ b/pubnub/endpoints/access/grant.py @@ -0,0 +1,112 @@ +from pubnub import utils +from pubnub.endpoints.endpoint import Endpoint +from pubnub.errors import PNERR_PAM_NO_FLAGS +from pubnub.exceptions import PubNubException +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.access_manager import PNAccessManagerGrantResult + + +class Grant(Endpoint): + GRANT_PATH = "/v1/auth/grant/sub-key/%s" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._auth_keys = [] + self._channels = [] + self._groups = [] + self._read = None + self._write = None + self._manage = None + self._ttl = None + + self._sort_params = True + + def auth_keys(self, auth_keys): + utils.extend_list(self._auth_keys, auth_keys) + return self + + def channels(self, channels): + utils.extend_list(self._channels, channels) + return self + + def channel_groups(self, channel_groups): + utils.extend_list(self._groups, channel_groups) + return self + + def read(self, flag): + self._read = flag + return self + + def write(self, flag): + self._write = flag + return self + + def manage(self, flag): + self._manage = flag + return self + + def ttl(self, ttl): + self._ttl = ttl + return self + + def custom_params(self): + params = {} + + if self._read is not None: + params['r'] = '1' if self._read is True else '0' + if self._write is not None: + params['w'] = '1' if self._write is True else '0' + if self._manage is not None: + params['m'] = '1' if self._manage is True else '0' + + if len(self._auth_keys) > 0: + params['auth'] = utils.join_items_and_encode(self._auth_keys) + + if len(self._channels) > 0: + params['channel'] = utils.join_items(self._channels) + + if len(self._groups) > 0: + params['channel-group'] = utils.join_items(self._groups) + + if self._ttl is not None: + params['ttl'] = str(int(self._ttl)) + + return params + + def build_path(self): + return Grant.GRANT_PATH % self.pubnub.config.subscribe_key + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + self.validate_secret_key() + # self.validate_channels_and_groups() + + if self._write is None and self._read is None and self._manage is None: + raise PubNubException(pn_error=PNERR_PAM_NO_FLAGS) + + def create_response(self, envelope): + return PNAccessManagerGrantResult.from_json(envelope['payload']) + + def is_auth_required(self): + return False + + def affected_channels(self): + return self._channels + + def affected_channels_groups(self): + return self._groups + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNAccessManagerGrant + + def name(self): + return "Grant" diff --git a/pubnub/endpoints/access/revoke.py b/pubnub/endpoints/access/revoke.py new file mode 100644 index 00000000..65313b58 --- /dev/null +++ b/pubnub/endpoints/access/revoke.py @@ -0,0 +1,27 @@ +from pubnub.endpoints.access.grant import Grant +from pubnub.enums import PNOperationType + + +class Revoke(Grant): + def __init__(self, pubnub): + Grant.__init__(self, pubnub) + self._read = False + self._write = False + self._manage = False + + self._sort_params = True + + def read(self, flag): + raise NotImplementedError + + def write(self, flag): + raise NotImplementedError + + def manage(self, flag): + raise NotImplementedError + + def operation_type(self): + return PNOperationType.PNAccessManagerRevoke + + def name(self): + return "Revoke" diff --git a/pubnub/endpoints/channel_groups/__init__.py b/pubnub/endpoints/channel_groups/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pubnub/endpoints/channel_groups/add_channel_to_channel_group.py b/pubnub/endpoints/channel_groups/add_channel_to_channel_group.py new file mode 100644 index 00000000..897802e5 --- /dev/null +++ b/pubnub/endpoints/channel_groups/add_channel_to_channel_group.py @@ -0,0 +1,69 @@ +import six + +from pubnub import utils +from pubnub.endpoints.endpoint import Endpoint +from pubnub.errors import PNERR_CHANNELS_MISSING, PNERR_GROUP_MISSING +from pubnub.exceptions import PubNubException +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.channel_group import PNChannelGroupsAddChannelResult + + +class AddChannelToChannelGroup(Endpoint): + # /v1/channel-registration/sub-key//channel-group/?add=ch1,ch2 + ADD_PATH = "/v1/channel-registration/sub-key/%s/channel-group/%s" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._channels = [] + self._channel_group = None + + def channels(self, channels): + if isinstance(channels, (list, tuple)): + self._channels.extend(channels) + else: + self._channels.extend(utils.split_items(channels)) + + return self + + def channel_group(self, channel_group): + self._channel_group = channel_group + + return self + + def custom_params(self): + return {'add': utils.join_items(self._channels)} + + def build_path(self): + return AddChannelToChannelGroup.ADD_PATH % ( + self.pubnub.config.subscribe_key, self._channel_group) + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + if len(self._channels) == 0: + raise PubNubException(pn_error=PNERR_CHANNELS_MISSING) + + if not isinstance(self._channel_group, six.string_types)\ + or len(self._channel_group) == 0: + raise PubNubException(pn_error=PNERR_GROUP_MISSING) + + def is_auth_required(self): + return True + + def create_response(self, envelope): + return PNChannelGroupsAddChannelResult() + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNAddChannelsToGroupOperation + + def name(self): + return "AddChannelToChannelGroup" diff --git a/pubnub/endpoints/channel_groups/list_channels_in_channel_group.py b/pubnub/endpoints/channel_groups/list_channels_in_channel_group.py new file mode 100644 index 00000000..37aaa8c1 --- /dev/null +++ b/pubnub/endpoints/channel_groups/list_channels_in_channel_group.py @@ -0,0 +1,59 @@ +import six + +from pubnub.endpoints.endpoint import Endpoint +from pubnub.errors import PNERR_GROUP_MISSING +from pubnub.exceptions import PubNubException +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.channel_group import PNChannelGroupsListResult + + +class ListChannelsInChannelGroup(Endpoint): + # /v1/channel-registration/sub-key//channel-group/ + LIST_PATH = "/v1/channel-registration/sub-key/%s/channel-group/%s" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._channel_group = None + + def channel_group(self, channel_group): + self._channel_group = channel_group + + return self + + def custom_params(self): + return {} + + def build_path(self): + return ListChannelsInChannelGroup.LIST_PATH % ( + self.pubnub.config.subscribe_key, self._channel_group) + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + if not isinstance(self._channel_group, six.string_types)\ + or len(self._channel_group) == 0: + raise PubNubException(pn_error=PNERR_GROUP_MISSING) + + def create_response(self, envelope): + if 'payload' in envelope and 'channels' in envelope['payload']: + return PNChannelGroupsListResult(envelope['payload']['channels']) + else: + return PNChannelGroupsListResult([]) + + def is_auth_required(self): + return True + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNChannelsForGroupOperation + + def name(self): + return "ListChannelsInChannelGroup" diff --git a/pubnub/endpoints/channel_groups/remove_channel_from_channel_group.py b/pubnub/endpoints/channel_groups/remove_channel_from_channel_group.py new file mode 100644 index 00000000..621daa48 --- /dev/null +++ b/pubnub/endpoints/channel_groups/remove_channel_from_channel_group.py @@ -0,0 +1,69 @@ +import six + +from pubnub import utils +from pubnub.endpoints.endpoint import Endpoint +from pubnub.errors import PNERR_CHANNELS_MISSING, PNERR_GROUP_MISSING +from pubnub.exceptions import PubNubException +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.channel_group import PNChannelGroupsRemoveChannelResult + + +class RemoveChannelFromChannelGroup(Endpoint): + # /v1/channel-registration/sub-key//channel-group/?remove=ch1,ch2 + REMOVE_PATH = "/v1/channel-registration/sub-key/%s/channel-group/%s" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._channels = [] + self._channel_group = None + + def channels(self, channels): + if isinstance(channels, (list, tuple)): + self._channels.extend(channels) + else: + self._channels.extend(utils.split_items(channels)) + + return self + + def channel_group(self, channel_group): + self._channel_group = channel_group + + return self + + def custom_params(self): + return {'remove': utils.join_items(self._channels)} + + def build_path(self): + return RemoveChannelFromChannelGroup.REMOVE_PATH % ( + self.pubnub.config.subscribe_key, self._channel_group) + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + if len(self._channels) == 0: + raise PubNubException(pn_error=PNERR_CHANNELS_MISSING) + + if not isinstance(self._channel_group, six.string_types)\ + or len(self._channel_group) == 0: + raise PubNubException(pn_error=PNERR_GROUP_MISSING) + + def is_auth_required(self): + return True + + def create_response(self, envelope): + return PNChannelGroupsRemoveChannelResult() + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNRemoveChannelsFromGroupOperation + + def name(self): + return "RemoveChannelToChannelGroup" diff --git a/pubnub/endpoints/channel_groups/remove_channel_group.py b/pubnub/endpoints/channel_groups/remove_channel_group.py new file mode 100644 index 00000000..ab61ca7f --- /dev/null +++ b/pubnub/endpoints/channel_groups/remove_channel_group.py @@ -0,0 +1,56 @@ +import six + +from pubnub.endpoints.endpoint import Endpoint +from pubnub.errors import PNERR_GROUP_MISSING +from pubnub.exceptions import PubNubException +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.channel_group import PNChannelGroupsRemoveGroupResult + + +class RemoveChannelGroup(Endpoint): + # /v1/channel-registration/sub-key//channel-group//remove + REMOVE_PATH = "/v1/channel-registration/sub-key/%s/channel-group/%s/remove" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._channel_group = None + + def channel_group(self, channel_group): + self._channel_group = channel_group + + return self + + def custom_params(self): + return {} + + def build_path(self): + return RemoveChannelGroup.REMOVE_PATH % ( + self.pubnub.config.subscribe_key, self._channel_group) + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + if not isinstance(self._channel_group, six.string_types)\ + or len(self._channel_group) == 0: + raise PubNubException(pn_error=PNERR_GROUP_MISSING) + + def is_auth_required(self): + return True + + def create_response(self, envelope): + return PNChannelGroupsRemoveGroupResult() + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNRemoveGroupOperation + + def name(self): + return "RemoveChannelGroup" diff --git a/pubnub/endpoints/endpoint.py b/pubnub/endpoints/endpoint.py new file mode 100644 index 00000000..748e7608 --- /dev/null +++ b/pubnub/endpoints/endpoint.py @@ -0,0 +1,246 @@ +from abc import ABCMeta, abstractmethod + +from pubnub import utils +from pubnub.enums import PNStatusCategory, PNOperationType +from pubnub.errors import PNERR_SUBSCRIBE_KEY_MISSING, PNERR_PUBLISH_KEY_MISSING, PNERR_CHANNEL_OR_GROUP_MISSING, \ + PNERR_SECRET_KEY_MISSING, PNERR_CHANNEL_MISSING +from pubnub.exceptions import PubNubException +from pubnub.models.consumer.common import PNStatus +from pubnub.models.consumer.pn_error_data import PNErrorData +from ..structures import RequestOptions, ResponseInfo + + +class Endpoint(object): + SERVER_RESPONSE_SUCCESS = 200 + SERVER_RESPONSE_FORBIDDEN = 403 + SERVER_RESPONSE_BAD_REQUEST = 400 + + __metaclass__ = ABCMeta + + def __init__(self, pubnub): + self.pubnub = pubnub + self._cancellation_event = None + self._sort_params = False + + def cancellation_event(self, event): + self._cancellation_event = event + return self + + @abstractmethod + def build_path(self): + pass + + @abstractmethod + def custom_params(self): + raise NotImplementedError + + def build_data(self): + return None + + @abstractmethod + def http_method(self): + pass + + @abstractmethod + def validate_params(self): + pass + + @abstractmethod + def create_response(self, endpoint): + pass + + @abstractmethod + def operation_type(self): + raise NotImplementedError + + @abstractmethod + def name(self): + pass + + @abstractmethod + def request_timeout(self): + pass + + @abstractmethod + def connect_timeout(self): + pass + + def is_auth_required(self): + raise NotImplementedError + + def affected_channels(self): + return None + + def affected_channels_groups(self): + return None + + def options(self): + return RequestOptions( + path=self.build_path(), + params_callback=self.build_params_callback(), + method=self.http_method(), + request_timeout=self.request_timeout(), + connect_timeout=self.connect_timeout(), + create_response=self.create_response, + create_status=self.create_status, + create_exception=self.create_exception, + operation_type=self.operation_type(), + data=self.build_data(), + sort_arguments=self._sort_params) + + def sync(self): + self.validate_params() + + envelope = self.pubnub.request_sync(self.options()) + + if envelope.status.is_error(): + raise envelope.status.error_data.exception + + return envelope + + def async(self, callback): + try: + self.validate_params() + options = self.options() + except PubNubException as e: + callback(None, self.create_status(PNStatusCategory.PNBadRequestCategory, None, None, e)) + return + + def callback_wrapper(envelope): + callback(envelope.result, envelope.status) + + return self.pubnub.request_async(endpoint_name=self.name(), + endpoint_call_options=options, + callback=callback_wrapper, + # REVIEW: include self._cancellation_event into options? + cancellation_event=self._cancellation_event) + + def result(self): + def handler(): + self.validate_params() + return self.options() + + return self.pubnub.request_result(options_func=handler, + cancellation_event=self._cancellation_event) + + def future(self): + def handler(): + self.validate_params() + return self.options() + + return self.pubnub.request_future(options_func=handler, + cancellation_event=self._cancellation_event) + + def deferred(self): + def handler(): + self.validate_params() + return self.options() + + return self.pubnub.request_deferred(options_func=handler, + cancellation_event=self._cancellation_event) + + def build_params_callback(self): + def callback(params_to_merge): + operation_type = self.operation_type() + custom_params = self.custom_params() + custom_params.update(params_to_merge) + + custom_params['pnsdk'] = self.pubnub.sdk_name + custom_params['uuid'] = self.pubnub.uuid + + if self.is_auth_required() and self.pubnub.config.auth_key is not None: + custom_params['auth'] = self.pubnub.config.auth_key + + if self.pubnub.config.secret_key is not None: + custom_params['timestamp'] = str(self.pubnub.timestamp()) + signed_input = (self.pubnub.config.subscribe_key + "\n" + self.pubnub.config.publish_key + "\n") + + if operation_type == PNOperationType.PNAccessManagerAudit: + signed_input += 'audit\n' + elif operation_type == PNOperationType.PNAccessManagerGrant or \ + operation_type == PNOperationType.PNAccessManagerRevoke: + signed_input += 'grant\n' + else: + signed_input += self.build_path() + "\n" + + signed_input += utils.prepare_pam_arguments(custom_params) + signature = utils.sign_sha256(self.pubnub.config.secret_key, signed_input) + + custom_params['signature'] = signature + + # REVIEW: add encoder map to not hardcode encoding here + if operation_type == PNOperationType.PNPublishOperation and 'meta' in custom_params: + custom_params['meta'] = utils.url_encode(custom_params['meta']) + if operation_type == PNOperationType.PNSetStateOperation and 'state' in custom_params: + custom_params['state'] = utils.url_encode(custom_params['state']) + + # reassign since pnsdk should be signed unencoded + custom_params['pnsdk'] = utils.url_encode(self.pubnub.sdk_name) + + return custom_params + + return callback + + def validate_subscribe_key(self): + if self.pubnub.config.subscribe_key is None or len(self.pubnub.config.subscribe_key) == 0: + raise PubNubException(pn_error=PNERR_SUBSCRIBE_KEY_MISSING) + + def validate_secret_key(self): + if self.pubnub.config.secret_key is None or len(self.pubnub.config.secret_key) == 0: + raise PubNubException(pn_error=PNERR_SECRET_KEY_MISSING) + + def validate_channel(self): + if self._channel is None or len(self._channel) is 0: + raise PubNubException(pn_error=PNERR_CHANNEL_MISSING) + + def validate_channels_and_groups(self): + if len(self._channels) == 0 and len(self._groups) == 0: + raise PubNubException(pn_error=PNERR_CHANNEL_OR_GROUP_MISSING) + + def validate_publish_key(self): + if self.pubnub.config.publish_key is None or len(self.pubnub.config.publish_key) == 0: + raise PubNubException(pn_error=PNERR_PUBLISH_KEY_MISSING) + + def create_status(self, category, response, response_info, exception): + if response_info is not None: + assert isinstance(response_info, ResponseInfo) + + pn_status = PNStatus() + + if response is None or exception is not None: + pn_status.error = True + + if response is not None: + pn_status.original_response = response + + if exception is not None: + pn_status.error_data = PNErrorData(str(exception), exception) + + if response_info is not None: + pn_status.status_code = response_info.status_code + pn_status.tls_enabled = response_info.tls_enabled + pn_status.origin = response_info.origin + pn_status.uuid = response_info.uuid + pn_status.auth_key = response_info.auth_key + pn_status.client_request = response_info.client_request + + pn_status.operation = self.operation_type() + pn_status.category = category + pn_status.affected_channels = self.affected_channels() + pn_status.affected_channels_groups = self.affected_channels_groups() + + return pn_status + + """ Used by asyncio and tornado clients to build exceptions + + The only difference with create_status() method is that a status + is wrapped with an exception and also contains this exception inside + as 'status.error_data.exception' + """ + + def create_exception(self, category, response, response_info, exception): + status = self.create_status(category, response, response_info, exception) + + exception.status = status + + return exception diff --git a/pubnub/endpoints/history.py b/pubnub/endpoints/history.py new file mode 100644 index 00000000..a42baec2 --- /dev/null +++ b/pubnub/endpoints/history.py @@ -0,0 +1,106 @@ +import six + +from pubnub import utils +from pubnub.endpoints.endpoint import Endpoint +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.history import PNHistoryResult + + +class History(Endpoint): + HISTORY_PATH = "/v2/history/sub-key/%s/channel/%s" + MAX_COUNT = 100 + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._channel = None + self._start = None + self._end = None + self._reverse = None + self._count = None + self._include_timetoken = None + + def channel(self, channel): + self._channel = channel + return self + + def start(self, start): + assert isinstance(start, six.integer_types) + self._start = start + return self + + def end(self, end): + assert isinstance(end, six.integer_types) + self._end = end + return self + + def reverse(self, reverse): + assert isinstance(reverse, bool) + self._reverse = reverse + return self + + def count(self, count): + assert isinstance(count, six.integer_types) + self._count = count + return self + + def include_timetoken(self, include_timetoken): + assert isinstance(include_timetoken, bool) + self._include_timetoken = include_timetoken + return self + + def custom_params(self): + params = {} + + if self._start is not None: + params['start'] = str(self._start) + + if self._end is not None: + params['end'] = str(self._end) + + if self._count is not None and 0 < self._count <= History.MAX_COUNT: + params['count'] = str(self._count) + else: + params['count'] = '100' + + if self._reverse is not None: + params['reverse'] = "true" if self._reverse else "false" + + if self._include_timetoken is not None: + params['include_token'] = "true" if self._include_timetoken else "false" + + return params + + def build_path(self): + return History.HISTORY_PATH % ( + self.pubnub.config.subscribe_key, + utils.url_encode(self._channel) + ) + + def http_method(self): + return HttpMethod.GET + + def is_auth_required(self): + return True + + def validate_params(self): + self.validate_subscribe_key() + self.validate_channel() + + def create_response(self, envelope): + return PNHistoryResult.from_json( + json_input=envelope, + crypto=self.pubnub.config.crypto, + include_tt_option=self._include_timetoken, + cipher=self.pubnub.config.cipher_key) + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNHistoryOperation + + def name(self): + return "History" diff --git a/pubnub/endpoints/presence/__init__.py b/pubnub/endpoints/presence/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pubnub/endpoints/presence/get_state.py b/pubnub/endpoints/presence/get_state.py new file mode 100644 index 00000000..5b2ba111 --- /dev/null +++ b/pubnub/endpoints/presence/get_state.py @@ -0,0 +1,74 @@ +from pubnub import utils +from pubnub.endpoints.endpoint import Endpoint +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.presence import PNGetStateResult + + +class GetState(Endpoint): + # /v2/presence/sub-key//channel//uuid//data?state= + GET_STATE_PATH = "/v2/presence/sub-key/%s/channel/%s/uuid/%s" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._channels = [] + self._groups = [] + + def channels(self, channels): + utils.extend_list(self._channels, channels) + return self + + def channel_groups(self, channel_groups): + utils.extend_list(self._groups, channel_groups) + return self + + def custom_params(self): + params = {} + + if len(self._groups) > 0: + params['channel-group'] = utils.join_items(self._groups) + + return params + + def build_path(self): + return GetState.GET_STATE_PATH % ( + self.pubnub.config.subscribe_key, + utils.join_channels(self._channels), + self.pubnub.uuid + ) + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + self.validate_channels_and_groups() + + def create_response(self, envelope): + if len(self._channels) == 1 and len(self._groups) == 0: + channels = {self._channels[0]: envelope['payload']} + else: + channels = envelope['payload']['channels'] + + return PNGetStateResult(channels) + + def is_auth_required(self): + return True + + def affected_channels(self): + return self._channels + + def affected_channels_groups(self): + return self._groups + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNGetState + + def name(self): + return "GetState" diff --git a/pubnub/endpoints/presence/heartbeat.py b/pubnub/endpoints/presence/heartbeat.py new file mode 100644 index 00000000..20ea60e8 --- /dev/null +++ b/pubnub/endpoints/presence/heartbeat.py @@ -0,0 +1,79 @@ +from pubnub import utils +from pubnub.endpoints.endpoint import Endpoint +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.errors import PNERR_CHANNEL_OR_GROUP_MISSING +from pubnub.exceptions import PubNubException + + +class Heartbeat(Endpoint): + # /v2/presence/sub-key//channel//heartbeat?uuid= + HEARTBEAT_PATH = "/v2/presence/sub-key/%s/channel/%s/heartbeat" + + def __init__(self, pubnub): + super(Heartbeat, self).__init__(pubnub) + self._channels = [] + self._groups = [] + self._state = None + + def channels(self, channels): + utils.extend_list(self._channels, channels) + + return self + + def channel_groups(self, channel_groups): + utils.extend_list(self._groups, channel_groups) + + return self + + def state(self, state): + self._state = state + + return self + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + if len(self._channels) == 0 and len(self._groups) == 0: + raise PubNubException(pn_error=PNERR_CHANNEL_OR_GROUP_MISSING) + + def build_path(self): + channels = utils.join_channels(self._channels) + return Heartbeat.HEARTBEAT_PATH % (self.pubnub.config.subscribe_key, channels) + + def custom_params(self): + params = {'heartbeat': str(self.pubnub.config.presence_timeout)} + + if len(self._groups) > 0: + params['channel-group'] = utils.join_items(self._groups) + + if self._state is not None and len(self._state) > 0: + params['state'] = utils.url_write(self._state) + + return params + + def create_response(self, envelope): + return True + + def is_auth_required(self): + return True + + def affected_channels(self): + return None + + def affected_channels_groups(self): + return None + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNHeartbeatOperation + + def name(self): + return "Heartbeat" diff --git a/pubnub/endpoints/presence/here_now.py b/pubnub/endpoints/presence/here_now.py new file mode 100644 index 00000000..83afe6e6 --- /dev/null +++ b/pubnub/endpoints/presence/here_now.py @@ -0,0 +1,77 @@ +from pubnub import utils +from pubnub.endpoints.endpoint import Endpoint +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.presence import PNHereNowResult + + +class HereNow(Endpoint): + HERE_NOW_PATH = "/v2/presence/sub-key/%s/channel/%s" + HERE_NOW_GLOBAL_PATH = "/v2/presence/sub-key/%s" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._channels = [] + self._channel_groups = [] + self._include_state = False + self._include_uuids = True + + def channels(self, channels): + utils.extend_list(self._channels, channels) + return self + + def channel_groups(self, channel_groups): + utils.extend_list(self._channel_groups, channel_groups) + return self + + def include_state(self, should_include_state): + self._include_state = should_include_state + return self + + def include_uuids(self, include_uuids): + self._include_uuids = include_uuids + return self + + def custom_params(self): + params = {} + + if len(self._channel_groups) > 0: + params['channel-group'] = utils.join_items_and_encode(self._channel_groups) + + if self._include_state: + params['state'] = "1" + + if not self._include_uuids: + params['disable_uuids'] = "1" + + return params + + def build_path(self): + if len(self._channels) == 0 and len(self._channel_groups) == 0: + return HereNow.HERE_NOW_GLOBAL_PATH % self.pubnub.config.subscribe_key + else: + return HereNow.HERE_NOW_PATH % (self.pubnub.config.subscribe_key, + utils.join_channels(self._channels)) + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + def is_auth_required(self): + return True + + def create_response(self, envelope): + return PNHereNowResult.from_json(envelope, self._channels) + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNHereNowOperation + + def name(self): + return "HereNow" diff --git a/pubnub/endpoints/presence/leave.py b/pubnub/endpoints/presence/leave.py new file mode 100644 index 00000000..8dfe20a0 --- /dev/null +++ b/pubnub/endpoints/presence/leave.py @@ -0,0 +1,75 @@ +from pubnub import utils +from pubnub.endpoints.endpoint import Endpoint +from pubnub.errors import PNERR_CHANNEL_OR_GROUP_MISSING +from pubnub.exceptions import PubNubException +from pubnub.enums import HttpMethod, PNOperationType + + +class Leave(Endpoint): + # /v2/presence/sub-key//channel//leave?uuid= + LEAVE_PATH = "/v2/presence/sub-key/%s/channel/%s/leave" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._channels = [] + self._groups = [] + + def channels(self, channels): + if isinstance(channels, (list, tuple)): + self._channels.extend(channels) + else: + self._channels.extend(utils.split_items(channels)) + + return self + + def channel_groups(self, channel_groups): + if isinstance(channel_groups, (list, tuple)): + self._groups.extend(channel_groups) + else: + self._groups.extend(utils.split_items(channel_groups)) + + return self + + def custom_params(self): + params = {} + + if len(self._groups) > 0: + params['channel-group'] = utils.join_items(self._groups) + + return params + + def build_path(self): + return Leave.LEAVE_PATH % (self.pubnub.config.subscribe_key, utils.join_channels(self._channels)) + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + if len(self._channels) == 0 and len(self._groups) == 0: + raise PubNubException(pn_error=PNERR_CHANNEL_OR_GROUP_MISSING) + + def create_response(self, envelope): + return envelope + + def is_auth_required(self): + return True + + def affected_channels(self): + return self._channels + + def affected_channels_groups(self): + return self._groups + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNUnsubscribeOperation + + def name(self): + return "Leave" diff --git a/pubnub/endpoints/presence/set_state.py b/pubnub/endpoints/presence/set_state.py new file mode 100644 index 00000000..be8b9b56 --- /dev/null +++ b/pubnub/endpoints/presence/set_state.py @@ -0,0 +1,93 @@ +from pubnub import utils +from pubnub.dtos import StateOperation +from pubnub.endpoints.endpoint import Endpoint +from pubnub.errors import PNERR_STATE_MISSING, PNERR_STATE_SETTER_FOR_GROUPS_NOT_SUPPORTED_YET +from pubnub.exceptions import PubNubException +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.presence import PNSetStateResult + + +class SetState(Endpoint): + # /v2/presence/sub-key//channel//uuid//data?state= + SET_STATE_PATH = "/v2/presence/sub-key/%s/channel/%s/uuid/%s/data" + + def __init__(self, pubnub, subscription_manager=None): + Endpoint.__init__(self, pubnub) + self._subscription_manager = subscription_manager + self._channels = [] + self._groups = [] + self._state = None + + def channels(self, channels): + utils.extend_list(self._channels, channels) + return self + + def channel_groups(self, channel_groups): + utils.extend_list(self._groups, channel_groups) + return self + + def state(self, state): + self._state = state + return self + + def custom_params(self): + if self._subscription_manager is not None: + self._subscription_manager.adapt_state_builder(StateOperation( + channels=self._channels, + channel_groups=self._groups, + state=self._state + )) + + params = {'state': utils.write_value_as_string(self._state)} + + if len(self._groups) > 0: + params['channel-group'] = utils.join_items_and_encode(self._groups) + + return params + + def build_path(self): + return SetState.SET_STATE_PATH % ( + self.pubnub.config.subscribe_key, + utils.join_channels(self._channels), + self.pubnub.uuid + ) + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + self.validate_channels_and_groups() + + if len(self._channels) == 0 and len(self._groups) > 0: + raise PubNubException(pn_error=PNERR_STATE_SETTER_FOR_GROUPS_NOT_SUPPORTED_YET) + + if self._state is None or not isinstance(self._state, dict): + raise PubNubException(pn_error=PNERR_STATE_MISSING) + + def create_response(self, envelope): + if 'status' in envelope and envelope['status'] is 200: + return PNSetStateResult(envelope['payload']) + else: + return envelope + + def is_auth_required(self): + return True + + def affected_channels(self): + return self._channels + + def affected_channels_groups(self): + return self._groups + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNSetStateOperation + + def name(self): + return "SetState" diff --git a/pubnub/endpoints/presence/where_now.py b/pubnub/endpoints/presence/where_now.py new file mode 100644 index 00000000..5de93874 --- /dev/null +++ b/pubnub/endpoints/presence/where_now.py @@ -0,0 +1,53 @@ +import six + +from pubnub.endpoints.endpoint import Endpoint +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.errors import PNERR_UUID_MISSING +from pubnub.exceptions import PubNubException +from pubnub.models.consumer.presence import PNWhereNowResult + + +class WhereNow(Endpoint): + # /v2/presence/sub-key//uuid/ + WHERE_NOW_PATH = "/v2/presence/sub-key/%s/uuid/%s" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._uuid = pubnub.config.uuid + + def uuid(self, uuid): + self._uuid = uuid + return self + + def custom_params(self): + return {} + + def build_path(self): + return WhereNow.WHERE_NOW_PATH % (self.pubnub.config.subscribe_key, self._uuid) + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + if self._uuid is None or not isinstance(self._uuid, six.string_types): + raise PubNubException(pn_error=PNERR_UUID_MISSING) + + def is_auth_required(self): + return True + + def create_response(self, envelope): + return PNWhereNowResult.from_json(envelope) + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNWhereNowOperation + + def name(self): + return "WhereNow" diff --git a/pubnub/endpoints/pubsub/__init__.py b/pubnub/endpoints/pubsub/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pubnub/endpoints/pubsub/publish.py b/pubnub/endpoints/pubsub/publish.py new file mode 100644 index 00000000..0b74223f --- /dev/null +++ b/pubnub/endpoints/pubsub/publish.py @@ -0,0 +1,146 @@ +from pubnub import utils +from pubnub.endpoints.endpoint import Endpoint +from pubnub.errors import PNERR_MESSAGE_MISSING +from pubnub.exceptions import PubNubException +from pubnub.models.consumer.pubsub import PNPublishResult +from pubnub.enums import HttpMethod, PNOperationType + + +class Publish(Endpoint): + # /publish//////[?argument(s)] + PUBLISH_GET_PATH = "/publish/%s/%s/0/%s/%s/%s" + PUBLISH_POST_PATH = "/publish/%s/%s/0/%s/%s" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._channel = None + self._message = None + self._should_store = None + self._replicate = None + self._use_post = None + self._meta = None + + def channel(self, channel): + self._channel = str(channel) + return self + + def message(self, message): + self._message = message + return self + + def use_post(self, use_post): + self._use_post = bool(use_post) + return self + + def replicate(self, replicate): + self._replicate = bool(replicate) + return self + + def should_store(self, should_store): + self._should_store = bool(should_store) + return self + + def meta(self, meta): + self._meta = meta + return self + + def build_data(self): + if self._use_post is True: + cipher = self.pubnub.config.cipher_key + if cipher is not None: + return '"' + self.pubnub.config.crypto.encrypt(cipher, utils.write_value_as_string(self._message)) + '"' + else: + return utils.write_value_as_string(self._message) + else: + return None + + def custom_params(self): + params = {} + + if self._meta is not None: + params['meta'] = utils.write_value_as_string(self._meta) + + if self._should_store is not None: + if self._should_store: + params["store"] = "1" + else: + params["store"] = "0" + + if self._replicate is not None: + if self._replicate: + params["replicate"] = "1" + else: + params["replicate"] = "0" + # REVIEW: should auth key be assigned here? + if self.pubnub.config.auth_key is not None: + params["auth"] = utils.url_encode(self.pubnub.config.auth_key) + + return params + + def build_path(self): + if self._use_post: + return Publish.PUBLISH_POST_PATH % (self.pubnub.config.publish_key, + self.pubnub.config.subscribe_key, + self._channel, 0) + else: + cipher = self.pubnub.config.cipher_key + stringified_message = utils.write_value_as_string(self._message) + + if cipher is not None: + stringified_message = '"' + self.pubnub.config.crypto.encrypt(cipher, stringified_message) + '"' + + stringified_message = utils.url_encode(stringified_message) + + return Publish.PUBLISH_GET_PATH % (self.pubnub.config.publish_key, + self.pubnub.config.subscribe_key, + self._channel, 0, stringified_message) + + def http_method(self): + if self._use_post is True: + return HttpMethod.POST + else: + return HttpMethod.GET + + def validate_params(self): + self.validate_channel() + + if self._message is None: + raise PubNubException(pn_error=PNERR_MESSAGE_MISSING) + + self.validate_subscribe_key() + self.validate_publish_key() + + def create_response(self, envelope): + """ + :param envelope: an already serialized json response + :return: + """ + if envelope is None: + return None + + timetoken = int(envelope[2]) + + res = PNPublishResult(envelope, timetoken) + + return res + + def is_auth_required(self): + return True + + def affected_channels(self): + return None + + def affected_channels_groups(self): + return None + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNPublishOperation + + def name(self): + return "Publish" diff --git a/pubnub/endpoints/pubsub/subscribe.py b/pubnub/endpoints/pubsub/subscribe.py new file mode 100644 index 00000000..b00f4adb --- /dev/null +++ b/pubnub/endpoints/pubsub/subscribe.py @@ -0,0 +1,102 @@ +from pubnub import utils +from pubnub.endpoints.endpoint import Endpoint +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.errors import PNERR_CHANNEL_OR_GROUP_MISSING +from pubnub.exceptions import PubNubException + + +class Subscribe(Endpoint): + # /subscribe//// + SUBSCRIBE_PATH = "/v2/subscribe/%s/%s/0" + + def __init__(self, pubnub): + super(Subscribe, self).__init__(pubnub) + self._channels = [] + self._groups = [] + + self._region = None + self._filter_expression = None + self._timetoken = None + self._with_presence = None + + def channels(self, channels): + utils.extend_list(self._channels, channels) + + return self + + def channel_groups(self, groups): + utils.extend_list(self._groups, groups) + + return self + + def timetoken(self, timetoken): + self._timetoken = timetoken + + return self + + def filter_expression(self, expr): + self._filter_expression = expr + + return self + + def region(self, region): + self._region = region + + return self + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + if len(self._channels) == 0 and len(self._groups) == 0: + raise PubNubException(pn_error=PNERR_CHANNEL_OR_GROUP_MISSING) + + def build_path(self): + channels = utils.join_channels(self._channels) + return Subscribe.SUBSCRIBE_PATH % (self.pubnub.config.subscribe_key, channels) + + def custom_params(self): + params = {} + + if len(self._groups) > 0: + params['channel-group'] = utils.join_items_and_encode(self._groups) + + if self._filter_expression is not None and len(self._filter_expression) > 0: + params['filter-expr'] = utils.url_encode(self._filter_expression) + + if self._timetoken is not None: + params['tt'] = str(self._timetoken) + + if self._region is not None: + params['tr'] = self._region + + if not self.pubnub.config.heartbeat_default_values: + params['heartbeat'] = self.pubnub.config.presence_timeout + + return params + + def create_response(self, envelope): + return envelope + + def is_auth_required(self): + return True + + def affected_channels(self): + return None + + def affected_channels_groups(self): + return None + + def request_timeout(self): + return self.pubnub.config.subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNSubscribeOperation + + def name(self): + return "Subscribe" diff --git a/pubnub/endpoints/push/__init__.py b/pubnub/endpoints/push/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pubnub/endpoints/push/add_channels_to_push.py b/pubnub/endpoints/push/add_channels_to_push.py new file mode 100644 index 00000000..e9764b0a --- /dev/null +++ b/pubnub/endpoints/push/add_channels_to_push.py @@ -0,0 +1,76 @@ +import six + +from pubnub.endpoints.endpoint import Endpoint +from pubnub.errors import PNERR_CHANNEL_MISSING, PNERR_PUSH_DEVICE_MISSING, PNERROR_PUSH_TYPE_MISSING +from pubnub.exceptions import PubNubException +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.push import PNPushAddChannelResult +from pubnub import utils + + +class AddChannelsToPush(Endpoint): + # v1/push/sub-key/{subKey}/devices/{pushToken} + ADD_PATH = "v1/push/sub-key/%s/devices/%s" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._channels = None + self._device_id = None + self._push_type = None + + def channels(self, channels): + self._channels = channels + return self + + def device_id(self, device_id): + self._device_id = device_id + return self + + def push_type(self, push_type): + self._push_type = push_type + return self + + def custom_params(self): + params = {} + + params['add'] = utils.join_items(self._channels) + params['type'] = utils.push_type_to_string(self._push_type) + + return params + + def build_path(self): + return AddChannelsToPush.ADD_PATH % ( + self.pubnub.config.subscribe_key, self._device_id) + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + if not isinstance(self._channels, list) or len(self._channels) == 0: + raise PubNubException(pn_error=PNERR_CHANNEL_MISSING) + + if not isinstance(self._device_id, six.string_types) or len(self._device_id) == 0: + raise PubNubException(pn_error=PNERR_PUSH_DEVICE_MISSING) + + if self._push_type is None or len(self._push_type) == 0: + raise PubNubException(pn_error=PNERROR_PUSH_TYPE_MISSING) + + def create_response(self, envelope): + return PNPushAddChannelResult() + + def is_auth_required(self): + return True + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNAddPushNotificationsOnChannelsOperation + + def name(self): + return "AddChannelsToPush" diff --git a/pubnub/endpoints/push/list_push_provisions.py b/pubnub/endpoints/push/list_push_provisions.py new file mode 100644 index 00000000..ec8acd34 --- /dev/null +++ b/pubnub/endpoints/push/list_push_provisions.py @@ -0,0 +1,70 @@ +import six + +from pubnub.endpoints.endpoint import Endpoint +from pubnub.errors import PNERR_PUSH_DEVICE_MISSING, PNERROR_PUSH_TYPE_MISSING +from pubnub.exceptions import PubNubException +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.push import PNPushListProvisionsResult +from pubnub import utils + + +class ListPushProvisions(Endpoint): + # v1/push/sub-key/{subKey}/devices/{pushToken} + LIST_PATH = "v1/push/sub-key/%s/devices/%s" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._device_id = None + self._push_type = None + + def device_id(self, device_id): + self._device_id = device_id + return self + + def push_type(self, push_type): + self._push_type = push_type + return self + + def custom_params(self): + params = {} + + params['type'] = utils.push_type_to_string(self._push_type) + + return params + + def build_path(self): + return ListPushProvisions.LIST_PATH % ( + self.pubnub.config.subscribe_key, self._device_id) + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + if not isinstance(self._device_id, six.string_types) or len(self._device_id) == 0: + raise PubNubException(pn_error=PNERR_PUSH_DEVICE_MISSING) + + if self._push_type is None or len(self._push_type) == 0: + raise PubNubException(pn_error=PNERROR_PUSH_TYPE_MISSING) + + def create_response(self, envelope): + if envelope is not None and isinstance(envelope, list): + return PNPushListProvisionsResult(envelope['payload']['channels']) + else: + return PNPushListProvisionsResult([]) + + def is_auth_required(self): + return True + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNPushNotificationEnabledChannelsOperation + + def name(self): + return "ListPushProvisions" diff --git a/pubnub/endpoints/push/remove_channels_from_push.py b/pubnub/endpoints/push/remove_channels_from_push.py new file mode 100644 index 00000000..1610614d --- /dev/null +++ b/pubnub/endpoints/push/remove_channels_from_push.py @@ -0,0 +1,73 @@ +import six + +from pubnub.endpoints.endpoint import Endpoint +from pubnub.errors import PNERR_CHANNEL_MISSING, PNERR_PUSH_DEVICE_MISSING, PNERROR_PUSH_TYPE_MISSING +from pubnub.exceptions import PubNubException +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.push import PNPushRemoveChannelResult +from pubnub import utils + + +class RemoveChannelsFromPush(Endpoint): + # v1/push/sub-key/{subKey}/devices/{pushToken} + REMOVE_PATH = "v1/push/sub-key/%s/devices/%s" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._channels = None + self._device_id = None + self._push_type = None + + def channels(self, channels): + self._channels = channels + return self + + def device_id(self, device_id): + self._device_id = device_id + return self + + def push_type(self, push_type): + self._push_type = push_type + return self + + def custom_params(self): + params = {'remove': utils.join_items(self._channels), 'type': utils.push_type_to_string(self._push_type)} + + return params + + def build_path(self): + return RemoveChannelsFromPush.REMOVE_PATH % ( + self.pubnub.config.subscribe_key, self._device_id) + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + if not isinstance(self._channels, list) or len(self._channels) == 0: + raise PubNubException(pn_error=PNERR_CHANNEL_MISSING) + + if not isinstance(self._device_id, six.string_types) or len(self._device_id) == 0: + raise PubNubException(pn_error=PNERR_PUSH_DEVICE_MISSING) + + if self._push_type is None or len(self._push_type) == 0: + raise PubNubException(pn_error=PNERROR_PUSH_TYPE_MISSING) + + def create_response(self, envelope): + return PNPushRemoveChannelResult() + + def is_auth_required(self): + return True + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNRemovePushNotificationsFromChannelsOperation + + def name(self): + return "RemoveChannelsFromPush" diff --git a/pubnub/endpoints/push/remove_device.py b/pubnub/endpoints/push/remove_device.py new file mode 100644 index 00000000..a258a0b3 --- /dev/null +++ b/pubnub/endpoints/push/remove_device.py @@ -0,0 +1,67 @@ +import six + +from pubnub.endpoints.endpoint import Endpoint +from pubnub.errors import PNERR_PUSH_DEVICE_MISSING, PNERROR_PUSH_TYPE_MISSING +from pubnub.exceptions import PubNubException +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.push import PNPushRemoveAllChannelsResult +from pubnub import utils + + +class RemoveDeviceFromPush(Endpoint): + # v1/push/sub-key/{subKey}/devices/{pushToken}/remove + REMOVE_PATH = "v1/push/sub-key/%s/devices/%s/remove" + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._device_id = None + self._push_type = None + + def device_id(self, device_id): + self._device_id = device_id + return self + + def push_type(self, push_type): + self._push_type = push_type + return self + + def custom_params(self): + params = {} + + params['type'] = utils.push_type_to_string(self._push_type) + + return params + + def build_path(self): + return RemoveDeviceFromPush.REMOVE_PATH % ( + self.pubnub.config.subscribe_key, self._device_id) + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + self.validate_subscribe_key() + + if not isinstance(self._device_id, six.string_types) or len(self._device_id) == 0: + raise PubNubException(pn_error=PNERR_PUSH_DEVICE_MISSING) + + if self._push_type is None or len(self._push_type) == 0: + raise PubNubException(pn_error=PNERROR_PUSH_TYPE_MISSING) + + def create_response(self, envelope): + return PNPushRemoveAllChannelsResult() + + def is_auth_required(self): + return True + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNRemoveAllPushNotificationsOperation + + def name(self): + return "RemoveDeviceFromPush" diff --git a/pubnub/endpoints/time.py b/pubnub/endpoints/time.py new file mode 100644 index 00000000..3199ef32 --- /dev/null +++ b/pubnub/endpoints/time.py @@ -0,0 +1,37 @@ +from pubnub.endpoints.endpoint import Endpoint +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.models.consumer.time import PNTimeResponse + + +class Time(Endpoint): + TIME_PATH = "/time/0" + + def custom_params(self): + return {} + + def build_path(self): + return Time.TIME_PATH + + def http_method(self): + return HttpMethod.GET + + def validate_params(self): + pass + + def is_auth_required(self): + return False + + def create_response(self, envelope): + return PNTimeResponse(envelope) + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNTimeOperation + + def name(self): + return "Time" diff --git a/pubnub/enums.py b/pubnub/enums.py new file mode 100644 index 00000000..df7917ca --- /dev/null +++ b/pubnub/enums.py @@ -0,0 +1,75 @@ +class HttpMethod(object): + GET = 1 + POST = 2 + + @classmethod + def string(cls, method): + if method == cls.GET: + return "GET" + elif method == cls.POST: + return "POST" + + +class PNStatusCategory(object): + PNUnknownCategory = 1 + PNAcknowledgmentCategory = 2 + PNAccessDeniedCategory = 3 + PNTimeoutCategory = 4 + PNNetworkIssuesCategory = 5 + PNConnectedCategory = 6 + PNReconnectedCategory = 7 + PNDisconnectedCategory = 8 + PNUnexpectedDisconnectCategory = 9 + PNCancelledCategory = 10 + PNBadRequestCategory = 11 + PNMalformedFilterExpressionCategory = 12 + PNMalformedResponseCategory = 13 + PNDecryptionErrorCategory = 14 + PNTLSConnectionFailedCategory = 15 + PNTLSUntrustedCertificateCategory = 16 + PNInternalExceptionCategory = 17 + + +class PNOperationType(object): + PNSubscribeOperation = 1 + PNUnsubscribeOperation = 2 + PNPublishOperation = 3 + PNHistoryOperation = 4 + PNWhereNowOperation = 5 + + PNHeartbeatOperation = 6 + PNSetStateOperation = 7 + PNAddChannelsToGroupOperation = 8 + PNRemoveChannelsFromGroupOperation = 9 + PNChannelGroupsOperation = 10 + PNRemoveGroupOperation = 11 + PNChannelsForGroupOperation = 12 + PNPushNotificationEnabledChannelsOperation = 13 + PNAddPushNotificationsOnChannelsOperation = 14 + PNRemovePushNotificationsFromChannelsOperation = 15 + PNRemoveAllPushNotificationsOperation = 16 + PNTimeOperation = 17 + + PNHereNowOperation = 18 + PNGetState = 19 + PNAccessManagerAudit = 20 + PNAccessManagerGrant = 21 + PNAccessManagerRevoke = 22 + + +class PNHeartbeatNotificationOptions(object): + NONE = 1 + FAILURES = 2 + ALL = 3 + + +class PNReconnectionPolicy(object): + NONE = 1 + LINEAR = 2 + EXPONENTIAL = 3 + + +class PNPushType(object): + APNS = 1 + MPNS = 2 + GCM = 3 diff --git a/pubnub/errors.py b/pubnub/errors.py new file mode 100644 index 00000000..fb677338 --- /dev/null +++ b/pubnub/errors.py @@ -0,0 +1,30 @@ +PNERR_CLIENT_TIMEOUT = "Client Timeout" +PNERR__TIMEOUT = "Timeout Occurred" +PNERR_REQUEST_CANCELLED = "HTTP Client Error" +# TODO: clarify to not confuse with 4xx and 5xx http erros +PNERR_HTTP_ERROR = "HTTP Error" +PNERR_CONNECTION_ERROR = "Connection Error" +PNERR_TOO_MANY_REDIRECTS_ERROR = "Too many redirects" +# For 5xx server responses +PNERR_SERVER_ERROR = "HTTP Server Error" +# For 4xx server responses +PNERR_CLIENT_ERROR = "HTTP Client Error" +PNERR_UNKNOWN_ERROR = "Unknown Error" +PNERR_CHANNEL_MISSING = "Channel missing" +PNERR_CHANNELS_MISSING = "Channels missing" +PNERR_GROUP_MISSING = "Channel group missing" +PNERR_MESSAGE_MISSING = "Message missing" +PNERR_SUBSCRIBE_KEY_MISSING = "Subscribe key not configured" +PNERR_SECRET_KEY_MISSING = "Secret key is not configured" +PNERR_PUBLISH_KEY_MISSING = "Publish key not configured" +PNERR_PUBLISH_META_WRONG_TYPE = "Publish meta should be dict" +PNERR_DEFERRED_NOT_IMPLEMENTED = "Deferred endpoint call is not implemented by this platform" +PNERR_JSON_DECODING_FAILED = "JSON decoding failed" +PNERR_JSON_NOT_SERIALIZABLE = "Trying to publish not JSON serializable object" +PNERR_CHANNEL_OR_GROUP_MISSING = "Channel or group missing" +PNERR_STATE_MISSING = "State missing or not a dict" +PNERR_UUID_MISSING = "uuid missing or not a string" +PNERR_STATE_SETTER_FOR_GROUPS_NOT_SUPPORTED_YET = "State setter for channel groups is not supported yet" +PNERR_PUSH_DEVICE_MISSING = "Device ID is missing for push operation" +PNERROR_PUSH_TYPE_MISSING = "Push Type is missing" +PNERR_PAM_NO_FLAGS = "At least one flag should be specified" diff --git a/pubnub/exceptions.py b/pubnub/exceptions.py new file mode 100644 index 00000000..bbfebe07 --- /dev/null +++ b/pubnub/exceptions.py @@ -0,0 +1,20 @@ +class PubNubException(Exception): + def __init__(self, errormsg="", status_code=0, pn_error=None, status=None): + self._errormsg = errormsg + self._status_code = status_code + self._pn_error = pn_error + self.status = status + + if len(str(errormsg)) > 0 and int(status_code) > 0: + msg = str(pn_error) + " (" + str(status_code) + "): " + str(errormsg) + elif len(str(errormsg)) > 0: + msg = str(pn_error) + ": " + str(errormsg) + else: + msg = str(pn_error) + + super(PubNubException, self).__init__(msg) + + @property + def _status(self): + raise DeprecationWarning + return self.status diff --git a/pubnub/managers.py b/pubnub/managers.py new file mode 100644 index 00000000..39a5cdf0 --- /dev/null +++ b/pubnub/managers.py @@ -0,0 +1,344 @@ +import logging +from abc import abstractmethod, ABCMeta + +import math + +from . import utils +from .enums import PNStatusCategory, PNReconnectionPolicy +from .models.consumer.common import PNStatus +from .models.server.subscribe import SubscribeEnvelope +from .dtos import SubscribeOperation, UnsubscribeOperation +from .callbacks import SubscribeCallback, ReconnectionCallback +from .models.subscription_item import SubscriptionItem + +logger = logging.getLogger("pubnub") + + +class PublishSequenceManager(object): + def __init__(self, provided_max_sequence): + self.max_sequence = provided_max_sequence + self.next_sequence = 0 + + @abstractmethod + def get_next_sequence(self): + if self.max_sequence == self.next_sequence: + self.next_sequence = 1 + else: + self.next_sequence += 1 + return self.next_sequence + + +class BasePathManager(object): + MAX_SUBDOMAIN = 20 + DEFAULT_SUBDOMAIN = "pubsub" + DEFAULT_BASE_PATH = "pubnub.com" + + def __init__(self, initial_config): + self.config = initial_config + self._current_subdomain = 1 + + def get_base_path(self): + if self.config.origin is not None: + return self.config.origin + # TODO: should CacheBusting be used? + elif False: + constructed_url = ("ps%s.%s" % (self._current_subdomain, BasePathManager.DEFAULT_BASE_PATH)) + + if self._current_subdomain == BasePathManager.MAX_SUBDOMAIN: + self._current_subdomain = 1 + else: + self._current_subdomain += 1 + + return constructed_url + else: + return "%s.%s" % (BasePathManager.DEFAULT_SUBDOMAIN, BasePathManager.DEFAULT_BASE_PATH) + + +class ReconnectionManager(object): + INTERVAL = 3 + MINEXPONENTIALBACKOFF = 1 + MAXEXPONENTIALBACKOFF = 32 + + def __init__(self, pubnub): + self._pubnub = pubnub + self._callback = None + self._timer = None + self._timer_interval = None + self._connection_errors = 1 + + def set_reconnection_listener(self, reconnection_callback): + assert isinstance(reconnection_callback, ReconnectionCallback) + self._callback = reconnection_callback + + def _recalculate_interval(self): + if self._pubnub.config.reconnect_policy == PNReconnectionPolicy.EXPONENTIAL: + self._timer_interval = int(math.pow(2, self._connection_errors) - 1) + if self._timer_interval > self.MAXEXPONENTIALBACKOFF: + self._timer_interval = self.MINEXPONENTIALBACKOFF + self._connection_errors = 1 + logger.debug("timerInterval > MAXEXPONENTIALBACKOFF at: %s" % utils.datetime_now()) + elif self._timer_interval < 1: + self._timer_interval = self.MINEXPONENTIALBACKOFF + logger.debug("timerInterval = %d at: %s" % (self._timer_interval, utils.datetime_now())) + else: + self._timer_interval = self.INTERVAL + + @abstractmethod + def start_polling(self): + pass + + def _stop_heartbeat_timer(self): + if self._timer is not None: + self._timer.stop() + self._timer = None + + +class StateManager(object): + def __init__(self): + self._channels = {} + self._groups = {} + self._presence_channels = {} + self._presence_groups = {} + + def is_empty(self): + return len(self._channels) == 0 and len(self._groups) == 0 and\ + len(self._presence_channels) == 0 and len(self._presence_groups) == 0 + + def subscribed_to_the_only_channel(self): + return len(self._channels) == 1 and len(self._groups) == 0 and\ + len(self._presence_channels) == 0 and len(self._presence_groups) == 0 + + def prepare_channel_list(self, include_presence): + return StateManager._prepare_membership_list( + self._channels, self._presence_channels, include_presence) + + def prepare_channel_group_list(self, include_presence): + return StateManager._prepare_membership_list( + self._groups, self._presence_groups, include_presence) + + def adapt_subscribe_builder(self, subscribe_operation): + for channel in subscribe_operation.channels: + self._channels[channel] = SubscriptionItem(name=channel) + + if subscribe_operation.presence_enabled: + self._presence_channels[channel] = SubscriptionItem(name=channel) + + for group in subscribe_operation.channel_groups: + self._groups[group] = SubscriptionItem(name=group) + + if subscribe_operation.presence_enabled: + self._presence_groups[group] = SubscriptionItem(name=group) + + def adapt_unsubscribe_builder(self, unsubscribe_operation): + for channel in unsubscribe_operation.channels: + self._channels.pop(channel, None) + if channel in self._presence_channels: + self._presence_channels.pop(channel, None) + + for group in unsubscribe_operation.channel_groups: + self._groups.pop(group) + if group in self._presence_groups: + self._presence_groups.pop(group) + + def adapt_state_builder(self, state_operation): + for channel in state_operation.channels: + subscribed_channel = self._channels.get(channel) + + if subscribed_channel is not None: + subscribed_channel.state = state_operation.state + + for group in state_operation.channel_groups: + subscribed_group = self._channels.get(group) + + if subscribed_group is not None: + subscribed_group.state = state_operation.state + + def state_payload(self): + state = {} + + for channel in self._channels.values(): + if channel.state is not None: + state[channel.name] = channel.state + + for group in self._groups.values(): + if group.state is not None: + state[group.name] = group.state + + return state + + @staticmethod + def _prepare_membership_list(data_storage, presence_storage, include_presence): + response = [] + + for item in data_storage.values(): + response.append(item.name) + + if include_presence: + for item in presence_storage.values(): + response.append(item.name + "-pnpres") + + return response + + +class ListenerManager(object): + def __init__(self, pubnub_instance): + self._pubnub = pubnub_instance + self._listeners = [] + + def add_listener(self, listener): + assert isinstance(listener, SubscribeCallback) + self._listeners.append(listener) + + def remove_listener(self, listener): + assert isinstance(listener, SubscribeCallback) + self._listeners.remove(listener) + + def announce_status(self, status): + for callback in self._listeners: + callback.status(self._pubnub, status) + + def announce_message(self, message): + for callback in self._listeners: + callback.message(self._pubnub, message) + + def announce_presence(self, presence): + for callback in self._listeners: + callback.presence(self._pubnub, presence) + + +class SubscriptionManager(object): + __metaclass__ = ABCMeta + + HEARTBEAT_INTERVAL_MULTIPLIER = 1000 + + def __init__(self, pubnub_instance): + self._pubnub = pubnub_instance + self._subscription_status_announced = False + + self._subscription_state = StateManager() + self._listener_manager = ListenerManager(self._pubnub) + self._timetoken = int(0) + self._region = None + + self._should_stop = False + + self._subscribe_request_task = None + self._heartbeat_call = None + + @abstractmethod + def _start_worker(self): + pass + + @abstractmethod + def _set_consumer_event(self): + pass + + @abstractmethod + def _message_queue_put(self, message): + pass + + @abstractmethod + def _start_subscribe_loop(self): + pass + + @abstractmethod + def _stop_subscribe_loop(self): + pass + + @abstractmethod + def _stop_heartbeat_timer(self): + pass + + @abstractmethod + def _perform_heartbeat_loop(self): + pass + + @abstractmethod + def _send_leave(self, unsubscribe_operation): + pass + + def add_listener(self, listener): + self._listener_manager.add_listener(listener) + + def remove_listener(self, listener): + self._listener_manager.remove_listener(listener) + + def get_subscribed_channels(self): + return self._subscription_state.prepare_channel_list(False) + + def get_subscribed_channel_groups(self): + return self._subscription_state.prepare_channel_group_list(False) + + def unsubscribe_all(self): + self.adapt_unsubscribe_builder(UnsubscribeOperation( + channels=self._subscription_state.prepare_channel_list(False), + channel_groups=self._subscription_state.prepare_channel_group_list(False) + )) + + def adapt_subscribe_builder(self, subscribe_operation): + assert isinstance(subscribe_operation, SubscribeOperation) + self._subscription_state.adapt_subscribe_builder(subscribe_operation) + self._subscription_status_announced = False + + if subscribe_operation.timetoken is not None: + self._timetoken = subscribe_operation.timetoken + + self.reconnect() + + def adapt_unsubscribe_builder(self, unsubscribe_operation): + assert isinstance(unsubscribe_operation, UnsubscribeOperation) + + self._subscription_state.adapt_unsubscribe_builder(unsubscribe_operation) + + self._send_leave(unsubscribe_operation) + + if self._subscription_state.is_empty(): + self._region = None + self._timetoken = 0 + self.reconnect() + + def adapt_state_builder(self, state_operation): + self._subscription_state.adapt_state_builder(state_operation) + self.reconnect() + + @abstractmethod + def reconnect(self): + pass + + def stop(self): + self._should_stop = True + self._stop_subscribe_loop() + self._stop_heartbeat_timer() + self._set_consumer_event() + + def _handle_endpoint_call(self, raw_result, status): + assert isinstance(status, PNStatus) + + if not self._subscription_status_announced: + pn_status = PNStatus() + pn_status.category = PNStatusCategory.PNConnectedCategory + pn_status.status_code = status.status_code + pn_status.auth_key = status.auth_key + pn_status.operation = status.operation + pn_status.client_request = status.client_request + pn_status.origin = status.origin + pn_status.tls_enabled = status.tls_enabled + + self._subscription_status_announced = True + self._listener_manager.announce_status(pn_status) + + result = SubscribeEnvelope.from_json(raw_result) + only_channel = self._subscription_state.subscribed_to_the_only_channel() + if result.messages is not None and len(result.messages) > 0: + for message in result.messages: + if only_channel: + message.only_channel_subscription = True + self._message_queue_put(message) + + # REVIEW: is int compatible with long for Python 2 + self._timetoken = int(result.metadata.timetoken) + self._region = int(result.metadata.region) + + # TODO: make abstract + def _register_heartbeat_timer(self): + self._stop_heartbeat_timer() diff --git a/pubnub/models/__init__.py b/pubnub/models/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pubnub/models/consumer/__init__.py b/pubnub/models/consumer/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pubnub/models/consumer/access_manager.py b/pubnub/models/consumer/access_manager.py new file mode 100644 index 00000000..470cfb1f --- /dev/null +++ b/pubnub/models/consumer/access_manager.py @@ -0,0 +1,153 @@ +import six +""" +Possible responses of PAM request +""" + + +class _PAMResult(object): + def __init__(self, level, subscribe_key, channels, groups, ttl=None, r=None, w=None, m=None): + self.level = level + self.subscribe_key = subscribe_key + self.channels = channels + self.groups = groups + self.ttl = ttl + self.read_enabled = r + self.write_enabled = w + self.manage_enabled = m + + @classmethod + def from_json(cls, json_input): + constructed_channels = {} + constructed_groups = {} + r, w, m, ttl = fetch_permissions(json_input) + + if 'channel' in json_input: + channel_name = json_input['channel'] + constructed_auth_keys = {} + for auth_key_name, value in six.iteritems(json_input['auths']): + constructed_auth_keys[auth_key_name] = PNAccessManagerKeyData.from_json(value) + + constructed_channels[channel_name] = PNAccessManagerChannelData( + name=channel_name, + auth_keys=constructed_auth_keys, + ttl=ttl + ) + + if 'channel-group' in json_input: + if isinstance(json_input['channel-group'], six.string_types): + group_name = json_input['channel-group'] + constructed_auth_keys = {} + for auth_key_name, value in six.iteritems(json_input['auths']): + constructed_auth_keys[auth_key_name] = PNAccessManagerKeyData.from_json(value) + constructed_groups[group_name] = PNAccessManagerChannelGroupData( + name=group_name, + auth_keys=constructed_auth_keys, + ttl=ttl + ) + + if 'channel-groups' in json_input: + if isinstance(json_input['channel-groups'], six.string_types): + group_name = json_input['channel-groups'] + constructed_auth_keys = {} + for auth_key_name, value in six.iteritems(json_input['auths']): + constructed_auth_keys[auth_key_name] = PNAccessManagerKeyData.from_json(value) + constructed_groups[group_name] = PNAccessManagerChannelGroupData( + name=group_name, + auth_keys=constructed_auth_keys, + ttl=ttl + ) + if isinstance(json_input['channel-groups'], dict): + for group_name, value in six.iteritems(json_input['channel-groups']): + constructed_groups[group_name] = \ + PNAccessManagerChannelGroupData.from_json(group_name, value) + + if 'channels' in json_input: + for channel_name, value in six.iteritems(json_input['channels']): + constructed_channels[channel_name] = \ + PNAccessManagerChannelData.from_json(channel_name, value) + + return cls( + level=json_input['level'], + subscribe_key=json_input['subscribe_key'], + channels=constructed_channels, + groups=constructed_groups, + r=r, + w=w, + m=m, + ttl=ttl, + ) + + +class PNAccessManagerAuditResult(_PAMResult): + def __str__(self): + return "Current permissions are valid for %d minutes: read %s, write %s, manage: %s" % \ + (self.ttl or 0, self.read_enabled, self.write_enabled, self.manage_enabled) + + +class PNAccessManagerGrantResult(_PAMResult): + def __str__(self): + return "New permissions are set for %d minutes: read %s, write %s, manage: %s" % \ + (self.ttl or 0, self.read_enabled, self.write_enabled, self.manage_enabled) + + +class _PAMEntityData(object): + def __init__(self, name, auth_keys=None, r=None, w=None, m=None, ttl=None): + self.name = name + self.auth_keys = auth_keys + self.read_enabled = r + self.write_enabled = w + self.manage_enabled = m + self.ttl = ttl + + @classmethod + def from_json(cls, name, json_input): + r, w, m, ttl = fetch_permissions(json_input) + constructed_auth_keys = {} + + if 'auths' in json_input: + for auth_key, value in json_input['auths'].items(): + constructed_auth_keys[auth_key] = PNAccessManagerKeyData.from_json(value) + + return cls(name, constructed_auth_keys, r, w, m) + + +class PNAccessManagerChannelData(_PAMEntityData): + pass + + +class PNAccessManagerChannelGroupData(_PAMEntityData): + pass + + +class PNAccessManagerKeyData(object): + def __init__(self, r, w, m, ttl=None): + self.read_enabled = r + self.write_enabled = w + self.manage_enabled = m + self.ttl = ttl + + @classmethod + def from_json(cls, json_input): + r, w, m, ttl = fetch_permissions(json_input) + return PNAccessManagerKeyData(r, w, m, ttl) + + +def fetch_permissions(json_input): + r = None + w = None + m = None + ttl = None + + if 'r' in json_input: + r = json_input['r'] == 1 + + if 'w' in json_input: + w = json_input['w'] == 1 + + if 'm' in json_input: + m = json_input['m'] == 1 + + if 'ttl' in json_input: + ttl = json_input['ttl'] + + return r, w, m, ttl diff --git a/pubnub/models/consumer/channel_group.py b/pubnub/models/consumer/channel_group.py new file mode 100644 index 00000000..f4e23e82 --- /dev/null +++ b/pubnub/models/consumer/channel_group.py @@ -0,0 +1,21 @@ +class PNChannelGroupsAddChannelResult(object): + def __str__(self): + return "Channel successfully added" + + +class PNChannelGroupsRemoveChannelResult(object): + def __str__(self): + return "Channel successfully removed" + + +class PNChannelGroupsRemoveGroupResult(object): + def __str__(self): + return "Group successfully removed" + + +class PNChannelGroupsListResult(object): + def __init__(self, channels): + self.channels = channels + + def __str__(self): + return "Group contains following channels: %s" % ", ".join(self.channels) diff --git a/pubnub/models/consumer/common.py b/pubnub/models/consumer/common.py new file mode 100644 index 00000000..71ef301d --- /dev/null +++ b/pubnub/models/consumer/common.py @@ -0,0 +1,22 @@ +class PNStatus: + def __init__(self): + self.category = None + self.error_data = None + self.error = None + + self.status_code = None + self.operation = None + + self.tls_enabled = None + + self.uuid = None + self.auth_key = None + self.origin = None + self.client_request = None + self.original_response = None + + self.affected_channels = None + self.affected_groups = None + + def is_error(self): + return self.error is not None diff --git a/pubnub/models/consumer/history.py b/pubnub/models/consumer/history.py new file mode 100644 index 00000000..148cdb32 --- /dev/null +++ b/pubnub/models/consumer/history.py @@ -0,0 +1,47 @@ + +class PNHistoryResult(object): + def __init__(self, messages, start_timetoken, end_timetoken): + self.messages = messages + self.start_timetoken = start_timetoken + self.end_timetoken = end_timetoken + + def __str__(self): + return "History result for range %d..%d" % (self.start_timetoken, self.end_timetoken) + + @classmethod + def from_json(cls, json_input, crypto, include_tt_option=False, cipher=None): + start_timetoken = json_input[1] + end_timetoken = json_input[2] + + raw_items = json_input[0] + messages = [] + + for item in raw_items: + if isinstance(item, dict) and 'timetoken' in item and 'message' in item and include_tt_option: + message = PNHistoryItemResult(item['message'], crypto, item['timetoken']) + else: + message = PNHistoryItemResult(item, crypto) + + if cipher is not None: + message.decrypt(cipher) + + messages.append(message) + + return PNHistoryResult( + messages=messages, + start_timetoken=start_timetoken, + end_timetoken=end_timetoken + ) + + +class PNHistoryItemResult(object): + def __init__(self, entry, crypto, timetoken=None): + self.timetoken = timetoken + self.entry = entry + self.crypto = crypto + + def __str__(self): + return "History item with tt: %s and content: %s" % (self.timetoken, self.entry) + + def decrypt(self, cipher_key): + self.entry = self.crypto.decrypt(cipher_key, self.entry) diff --git a/pubnub/models/consumer/pn_error_data.py b/pubnub/models/consumer/pn_error_data.py new file mode 100644 index 00000000..e7946d1d --- /dev/null +++ b/pubnub/models/consumer/pn_error_data.py @@ -0,0 +1,7 @@ +class PNErrorData(): + def __init__(self, information, exception): + assert isinstance(information, str) + assert isinstance(exception, Exception) + + self.information = information + self.exception = exception diff --git a/pubnub/models/consumer/presence.py b/pubnub/models/consumer/presence.py new file mode 100644 index 00000000..5abb0d61 --- /dev/null +++ b/pubnub/models/consumer/presence.py @@ -0,0 +1,151 @@ +import six + + +class PNHereNowResult(object): + def __init__(self, total_channels, total_occupancy, channels): + assert isinstance(total_channels, six.integer_types) + assert isinstance(total_occupancy, six.integer_types) + + self.total_channels = total_channels + self.total_occupancy = total_occupancy + self.channels = channels + + def __str__(self): + return "HereNow Result total occupancy: %d, total channels: %d" % (self.total_occupancy, self.total_channels) + + @classmethod + def from_json(cls, envelope, channel_names): + # multiple + if 'payload' in envelope and isinstance(envelope['payload'], dict): + json_input = envelope['payload'] + + channels = [] + if len(json_input['channels']) > 0: + for channel_name, raw_data in json_input['channels'].items(): + channels.append(PNHereNowChannelData.from_json(channel_name, raw_data)) + return PNHereNowResult( + total_channels=int(json_input['total_channels']), + total_occupancy=int(json_input['total_occupancy']), + channels=channels) + elif len(channel_names) == 1: + return PNHereNowResult( + total_channels=int(1), + total_occupancy=int(json_input['total_occupancy']), + channels=[PNHereNowChannelData(channel_names[0], 0, [])] + ) + else: + return PNHereNowResult( + total_channels=int(json_input['total_channels']), + total_occupancy=int(json_input['total_occupancy']), + channels={} + ) + # empty + elif 'occupancy' in envelope and int(envelope['occupancy']) == 0: + return PNHereNowResult( + total_channels=int(1), + total_occupancy=int(envelope['occupancy']), + channels=[PNHereNowChannelData(channel_names[0], 0, [])] + ) + # single + elif 'uuids' in envelope and isinstance(envelope['uuids'], list): + occupants = [] + for user in envelope['uuids']: + if isinstance(user, six.string_types): + occupants.append(PNHereNowOccupantsData(user, None)) + else: + state = user['state'] if 'state' in user else None + occupants.append(PNHereNowOccupantsData(user['uuid'], state)) + + return PNHereNowResult( + total_channels=1, + total_occupancy=int(envelope['occupancy']), + channels=[ + PNHereNowChannelData( + channel_name=channel_names[0], + occupancy=envelope['occupancy'], + occupants=occupants + ) + ]) + else: + return PNHereNowResult( + total_channels=1, + total_occupancy=int(envelope['occupancy']), + channels=[ + PNHereNowChannelData( + channel_name=channel_names[0], + occupancy=envelope['occupancy'], + occupants=[] + ) + ]) + + +class PNHereNowChannelData(object): + def __init__(self, channel_name, occupancy, occupants): + self.channel_name = channel_name + self.occupancy = occupancy + self.occupants = occupants + + def __str__(self): + return "HereNow Channel Data for channel '%s': occupancy: %d, occupants: %d" \ + % (self.channel_name, self.occupancy, self.occupants) + + @classmethod + def from_json(cls, name, json_input): + if 'uuids' in json_input: + occupants = [] + for user in json_input['uuids']: + if isinstance(user, dict) and len(user) > 0: + if 'state' in user: + occupants.append(PNHereNowOccupantsData(user['uuid'], user['state'])) + else: + occupants.append(PNHereNowOccupantsData(user['uuid'], None)) + else: + occupants.append(PNHereNowOccupantsData(user, None)) + else: + occupants = None + + return PNHereNowChannelData( + channel_name=name, + occupancy=int(json_input['occupancy']), + occupants=occupants + ) + + +class PNHereNowOccupantsData(object): + def __init__(self, uuid, state): + self.uuid = uuid + self.state = state + + def __str__(self): + return "HereNow Occupants Data for '%s': %s" % (self.uuid, self.state) + + +class PNWhereNowResult(object): + def __init__(self, channels): + assert isinstance(channels, (list, tuple)) + self.channels = channels + + def __str__(self): + return "User is currently subscribed to %s" % ", ".join(self.channels) + + @classmethod + def from_json(cls, json_input): + return PNWhereNowResult(json_input['payload']['channels']) + + +class PNSetStateResult(object): + def __init__(self, state): + assert isinstance(state, dict) + self.state = state + + def __str__(self): + return "New state %s successfully set" % self.state + + +class PNGetStateResult(object): + def __init__(self, channels): + assert isinstance(channels, dict) + self.channels = channels + + def __str__(self): + return "Current state is %s" % self.channels diff --git a/pubnub/models/consumer/pubsub.py b/pubnub/models/consumer/pubsub.py new file mode 100644 index 00000000..a5f911e6 --- /dev/null +++ b/pubnub/models/consumer/pubsub.py @@ -0,0 +1,77 @@ +import six + + +class PNMessageResult(object): + def __init__(self, message, subscription, channel, timetoken, user_metadata=None, publisher=None): + assert message is not None + + if subscription is not None: + assert isinstance(subscription, six.string_types) + + if channel is not None: + assert isinstance(channel, six.string_types) + + if publisher is not None: + assert isinstance(publisher, six.string_types) + + assert isinstance(timetoken, six.integer_types) + + if user_metadata is not None: + assert isinstance(user_metadata, object) + + self.message = message + # DEPRECATED: subscribed_channel and actual_channel properties are deprecated + # self.subscribed_channel = subscribed_channel <= now known as subscription + # self.actual_channel = actual_channel <= now known as channel + + self.channel = channel + self.subscription = subscription + + self.timetoken = timetoken + self.user_metadata = user_metadata + self.publisher = publisher + + +class PNPresenceEventResult(object): + def __init__(self, event, uuid, timestamp, occupancy, subscription, channel, + timetoken, state, user_metadata=None): + + assert isinstance(event, six.string_types) + assert isinstance(timestamp, six.integer_types) + assert isinstance(occupancy, six.integer_types) + assert isinstance(channel, six.string_types) + assert isinstance(timetoken, six.integer_types) + + if user_metadata is not None: + assert isinstance(user_metadata, object) + + if state is not None: + assert isinstance(state, dict) + + self.event = event + self.uuid = uuid + self.timestamp = timestamp + self.occupancy = occupancy + self.state = state + + # DEPRECATED: subscribed_channel and actual_channel properties are deprecated + # self.subscribed_channel = subscribed_channel <= now known as subscription + # self.actual_channel = actual_channel <= now known as channel + self.subscription = subscription + self.channel = channel + + self.timetoken = timetoken + self.user_metadata = user_metadata + + +class PNPublishResult(object): + def __init__(self, envelope, timetoken): + """ + Representation of publish server response + + :param timetoken: of publish operation + """ + self.timetoken = timetoken + + def __str__(self): + return "Publish success with timetoken %s" % self.timetoken diff --git a/pubnub/models/consumer/push.py b/pubnub/models/consumer/push.py new file mode 100644 index 00000000..08a17ad1 --- /dev/null +++ b/pubnub/models/consumer/push.py @@ -0,0 +1,23 @@ + +class PNPushAddChannelResult(object): + def __str__(self): + return "Channel successfully added" + + +class PNPushRemoveChannelResult(object): + def __str__(self): + return "Channel successfully removed" + + +class PNPushRemoveAllChannelsResult(object): + def __str__(self): + return "All channels successfully removed" + + +class PNPushListProvisionsResult(object): + def __init__(self, channels): + self.channels = channels + + def __str__(self): + return "Push notification enabled on following channels: %s" % \ + ", ".join(self.channels) diff --git a/pubnub/models/consumer/time.py b/pubnub/models/consumer/time.py new file mode 100644 index 00000000..b9a117e4 --- /dev/null +++ b/pubnub/models/consumer/time.py @@ -0,0 +1,20 @@ +from datetime import date + + +class PNTimeResponse(object): + MULTIPLIER = 10000000 + + def __init__(self, server_response): + assert isinstance(server_response, list) + self.server_response = server_response + self.value_as_string = str(server_response[0]) + self.value_as_int = server_response[0] + + def __int__(self): + return self.value_as_int + + def __str__(self): + return self.value_as_string + + def date_time(self): + return date.fromtimestamp(self.value_as_int / PNTimeResponse.MULTIPLIER) diff --git a/pubnub/models/server/__init__.py b/pubnub/models/server/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pubnub/models/server/subscribe.py b/pubnub/models/server/subscribe.py new file mode 100644 index 00000000..ef4295a8 --- /dev/null +++ b/pubnub/models/server/subscribe.py @@ -0,0 +1,112 @@ +import six + + +class SubscribeEnvelope: + def __init__(self, messages=None, metadata=None): + assert isinstance(messages, (list, None)) + assert isinstance(metadata, (SubscribeMetadata, None)) + + self.messages = messages + self.metadata = metadata + + @classmethod + def from_json(cls, json_input): + messages = [] + + for raw_message in json_input['m']: + messages.append(SubscribeMessage.from_json(raw_message)) + + metadata = SubscribeMetadata.from_json(json_input['t']) + return SubscribeEnvelope(messages, metadata) + + +class SubscribeMessage: + def __init__(self): + self.shard = None + self.subscription_match = None + self.channel = None + self.payload = None + self.flags = None + self.issuing_client_id = None + self.subscribe_key = None + self.origination_timetoken = None + self.publish_metadata = None + self.only_channel_subscription = False + + @classmethod + def from_json(cls, json_input): + message = SubscribeMessage() + message.shard = json_input['a'] + if 'b' in json_input: + message.subscription_match = json_input['b'] + message.channel = json_input['c'] + message.payload = json_input['d'] + message.flags = json_input['f'] + if 'i' in json_input: + message.issuing_client_id = json_input['i'] + message.subscribe_key = json_input['k'] + if 'o' in json_input: + message.origination_timetoken = json_input['o'] + message.publish_metadata = PublishMetadata.from_json(json_input['p']) + + return message + + +class SubscribeMetadata: + def __init__(self, timetoken=None, region=None): + self.timetoken = timetoken + self.region = region + + @classmethod + def from_json(cls, json_input): + assert isinstance(json_input, dict) + assert 'r' in json_input + assert 't' in json_input + + return SubscribeMetadata(json_input['t'], json_input['r']) + + +class PresenceEnvelope: + def __init__(self, action, uuid, occupancy, timestamp, data=None): + assert isinstance(action, six.string_types) + assert isinstance(occupancy, six.integer_types) + assert isinstance(timestamp, six.integer_types) + if data is not None: + assert isinstance(data, dict) + + self.action = action + self.uuid = uuid + self.occupancy = occupancy + self.timestamp = timestamp + self.data = data + + @classmethod + def extract_value(cls, json, key): + if key in json: + return json[key] + else: + return None + + @classmethod + def from_json_payload(cls, json): + + return PresenceEnvelope( + action=cls.extract_value(json, 'action'), + uuid=cls.extract_value(json, 'uuid'), + occupancy=cls.extract_value(json, 'occupancy'), + timestamp=cls.extract_value(json, 'timestamp'), + data=cls.extract_value(json, 'data') + ) + + +class PublishMetadata: + def __init__(self, publish_timetoken=None, region=None): + self.publish_timetoken = publish_timetoken + self.region = region + + @classmethod + def from_json(cls, json_input): + assert 'r' in json_input + assert 't' in json_input + + return PublishMetadata(int(json_input['t']), int(json_input['r'])) diff --git a/pubnub/models/subscription_item.py b/pubnub/models/subscription_item.py new file mode 100644 index 00000000..4b2bde92 --- /dev/null +++ b/pubnub/models/subscription_item.py @@ -0,0 +1,7 @@ +class SubscriptionItem(object): + def __init__(self, name=None, state=None): + self.name = name + self.state = state + + def __str__(self): + return self.name diff --git a/pubnub/pnconfiguration.py b/pubnub/pnconfiguration.py new file mode 100644 index 00000000..e7c2af7f --- /dev/null +++ b/pubnub/pnconfiguration.py @@ -0,0 +1,83 @@ +from .enums import PNHeartbeatNotificationOptions, PNReconnectionPolicy +from . import utils + + +class PNConfiguration(object): + DEFAULT_PRESENCE_TIMEOUT = 300 + DEFAULT_HEARTBEAT_INTERVAL = 280 + + def __init__(self): + # TODO: add validation + self.uuid = None + self.origin = "ps.pndsn.com" + self.ssl = False + self.non_subscribe_request_timeout = 10 + self.subscribe_request_timeout = 310 + self.connect_timeout = 5 + self.subscribe_key = None + self.publish_key = None + self.secret_key = None + self.cipher_key = None + self.auth_key = None + self.filter_expression = None + self.enable_subscribe = True + self.crypto_instance = None + self.log_verbosity = False + self.heartbeat_notification_options = PNHeartbeatNotificationOptions.FAILURES + self.reconnect_policy = PNReconnectionPolicy.NONE + + self.heartbeat_default_values = True + self._presence_timeout = PNConfiguration.DEFAULT_PRESENCE_TIMEOUT + self._heartbeat_interval = PNConfiguration.DEFAULT_HEARTBEAT_INTERVAL + + def validate(self): + assert self.uuid is None or isinstance(self.uuid, str) + + if self.uuid is None: + self.uuid = utils.uuid() + + def scheme(self): + if self.ssl: + return "https" + else: + return "http" + + def scheme_extended(self): + return self.scheme() + "://" + + def scheme_and_host(self): + return self.scheme_extended() + self.origin + + def set_presence_timeout_with_custom_interval(self, timeout, interval): + self.heartbeat_default_values = False + self._presence_timeout = timeout + self._heartbeat_interval = interval + + def set_presence_timeout(self, timeout): + self.set_presence_timeout_with_custom_interval(timeout, (timeout / 2) - 1) + + @property + def crypto(self): + if self.crypto_instance is None: + self._init_cryptodome() + + return self.crypto_instance + + def _init_cryptodome(self): + from .crypto import PubNubCryptodome + self.crypto_instance = PubNubCryptodome() + + @property + def port(self): + return 443 if self.ssl == "https" else 80 + + @property + def presence_timeout(self): + return self._presence_timeout + + @property + def heartbeat_interval(self): + return self._heartbeat_interval + + # TODO: set log level + # TODO: set log level diff --git a/pubnub/pubnub.py b/pubnub/pubnub.py new file mode 100644 index 00000000..d17008f0 --- /dev/null +++ b/pubnub/pubnub.py @@ -0,0 +1,440 @@ +import copy +import logging +import threading + +from threading import Event +from six.moves.queue import Queue, Empty + +from . import utils +from .request_handlers.base import BaseRequestHandler +from .request_handlers.requests_handler import RequestsRequestHandler +from .callbacks import SubscribeCallback, ReconnectionCallback +from .endpoints.presence.heartbeat import Heartbeat +from .endpoints.presence.leave import Leave +from .endpoints.pubsub.subscribe import Subscribe +from .enums import PNStatusCategory, PNHeartbeatNotificationOptions, PNOperationType, PNReconnectionPolicy +from .managers import SubscriptionManager, PublishSequenceManager, ReconnectionManager +from .models.consumer.common import PNStatus +from .pnconfiguration import PNConfiguration +from .pubnub_core import PubNubCore +from .structures import PlatformOptions +from .workers import SubscribeMessageWorker + +logger = logging.getLogger("pubnub") + + +class PubNub(PubNubCore): + """PubNub Python API""" + + def __init__(self, config): + assert isinstance(config, PNConfiguration) + + self._request_handler = RequestsRequestHandler(self) + PubNubCore.__init__(self, config) + + if self.config.enable_subscribe: + self._subscription_manager = NativeSubscriptionManager(self) + + self._publish_sequence_manager = PublishSequenceManager(PubNubCore.MAX_SEQUENCE) + + def sdk_platform(self): + return "" + + def set_request_handler(self, handler): + assert isinstance(handler, BaseRequestHandler) + self._request_handler = handler + + def request_sync(self, endpoint_call_options): + platform_options = PlatformOptions(self.headers, self.config) + + self.merge_in_params(endpoint_call_options) + + if self.config.log_verbosity: + print(endpoint_call_options) + + return self._request_handler.sync_request(platform_options, endpoint_call_options) + + def request_async(self, endpoint_name, endpoint_call_options, callback, cancellation_event): + platform_options = PlatformOptions(self.headers, self.config) + + self.merge_in_params(endpoint_call_options) + + if self.config.log_verbosity: + print(endpoint_call_options) + + return self._request_handler.async_request(endpoint_name, platform_options, endpoint_call_options, + callback, cancellation_event) + + def merge_in_params(self, options): + + params_to_merge_in = {} + + if options.operation_type == PNOperationType.PNPublishOperation: + params_to_merge_in['seqn'] = self._publish_sequence_manager.get_next_sequence() + + options.merge_params_in(params_to_merge_in) + + def stop(self): + if self._subscription_manager is not None: + self._subscription_manager.stop() + else: + raise Exception("Subscription manager is not enabled for this instance") + + def request_deferred(self, options_func): + raise NotImplementedError + + def request_future(self, *args, **kwargs): + raise NotImplementedError + + +class NativeReconnectionManager(ReconnectionManager): + def __init__(self, pubnub): + super(NativeReconnectionManager, self).__init__(pubnub) + + def _register_heartbeat_timer(self): + self.stop_heartbeat_timer() + + self._recalculate_interval() + + self._timer = threading.Timer(self._timer_interval, self._call_time) + self._timer.setDaemon(True) + self._timer.start() + + def _call_time(self): + self._pubnub.time().async(self._call_time_callback) + + def _call_time_callback(self, resp, status): + if not status.is_error(): + self._connection_errors = 1 + self.stop_heartbeat_timer() + self._callback.on_reconnect() + logger.debug("reconnection manager stop due success time endpoint call: %s" % utils.datetime_now()) + elif self._pubnub.config.reconnect_policy == PNReconnectionPolicy.EXPONENTIAL: + logger.debug("reconnect interval increment at: %s" % utils.datetime_now()) + self.stop_heartbeat_timer() + self._connection_errors += 1 + self._register_heartbeat_timer() + elif self._pubnub.config.reconnect_policy == PNReconnectionPolicy.LINEAR: + self.stop_heartbeat_timer() + self._connection_errors += 1 + self._register_heartbeat_timer() + + def start_polling(self): + if self._pubnub.config.reconnect_policy == PNReconnectionPolicy.NONE: + logger.warn("reconnection policy is disabled, please handle reconnection manually.") + return + + logger.debug("reconnection manager start at: %s" % utils.datetime_now()) + + self._register_heartbeat_timer() + + def stop_heartbeat_timer(self): + if self._timer is not None: + self._timer.cancel() + + +class NativePublishSequenceManager(PublishSequenceManager): + def __init__(self, provided_max_sequence): + super(NativePublishSequenceManager, self).__init__(provided_max_sequence) + self._lock = threading.Lock() + + def get_next_sequence(self): + with self._lock: + if self.max_sequence == self.next_sequence: + self.next_sequence = 1 + else: + self.next_sequence += 1 + + return self.next_sequence + + +class NativeSubscriptionManager(SubscriptionManager): + def __init__(self, pubnub_instance): + subscription_manager = self + + self._message_queue = Queue() + self._consumer_event = threading.Event() + self._subscribe_call = None + self._heartbeat_periodic_callback = None + self._reconnection_manager = NativeReconnectionManager(pubnub_instance) + + super(NativeSubscriptionManager, self).__init__(pubnub_instance) + self._start_worker() + + class NativeReconnectionCallback(ReconnectionCallback): + def on_reconnect(self): + subscription_manager.reconnect() + + pn_status = PNStatus() + pn_status.category = PNStatusCategory.PNReconnectedCategory + pn_status.error = False + + subscription_manager._subscription_status_announced = True + subscription_manager._listener_manager.announce_status(pn_status) + + self._reconnection_listener = NativeReconnectionCallback() + self._reconnection_manager.set_reconnection_listener(self._reconnection_listener) + + def _send_leave(self, unsubscribe_operation): + def leave_callback(result, status): + self._listener_manager.announce_status(status) + + Leave(self._pubnub) \ + .channels(unsubscribe_operation.channels) \ + .channel_groups(unsubscribe_operation.channel_groups).async(leave_callback) + + def _register_heartbeat_timer(self): + super(NativeSubscriptionManager, self)._register_heartbeat_timer() + + self._perform_heartbeat_loop() + + self._heartbeat_periodic_callback = NativePeriodicCallback( + self._perform_heartbeat_loop, + self._pubnub.config.heartbeat_interval) + + if not self._should_stop: + self._heartbeat_periodic_callback.start() + + def _perform_heartbeat_loop(self): + if self._heartbeat_call is not None: + # TODO: cancel call + pass + + state_payload = self._subscription_state.state_payload() + presence_channels = self._subscription_state.prepare_channel_list(False) + presence_groups = self._subscription_state.prepare_channel_group_list(False) + + if len(presence_channels) == 0 and len(presence_groups) == 0: + return + + def heartbeat_callback(raw_result, status): + heartbeat_verbosity = self._pubnub.config.heartbeat_notification_options + if status.is_error: + if heartbeat_verbosity == PNHeartbeatNotificationOptions.ALL or \ + heartbeat_verbosity == PNHeartbeatNotificationOptions.ALL: + self._listener_manager.announce_stateus(status) + else: + if heartbeat_verbosity == PNHeartbeatNotificationOptions.ALL: + self._listener_manager.announce_stateus(status) + + try: + (Heartbeat(self._pubnub) + .channels(presence_channels) + .channel_groups(presence_groups) + .state(state_payload) + .async(heartbeat_callback)) + except Exception as e: + logger.error("Heartbeat request failed: %s" % e) + + def _stop_heartbeat_timer(self): + if self._heartbeat_periodic_callback is not None: + self._heartbeat_periodic_callback.stop() + + def _set_consumer_event(self): + self._consumer_event.set() + self._message_queue_put(None) + + def _message_queue_put(self, message): + self._message_queue.put(message) + + def reconnect(self): + self._should_stop = False + self._start_subscribe_loop() + self._register_heartbeat_timer() + + def disconnect(self): + self._should_stop = True + self._stop_heartbeat_timer() + self._stop_subscribe_loop() + + def _start_worker(self): + consumer = NativeSubscribeMessageWorker(self._pubnub, self._listener_manager, + self._message_queue, self._consumer_event) + self._consumer_thread = threading.Thread(target=consumer.run, + name="SubscribeMessageWorker") + self._consumer_thread.setDaemon(True) + self._consumer_thread.start() + + def _start_subscribe_loop(self): + self._stop_subscribe_loop() + + combined_channels = self._subscription_state.prepare_channel_list(True) + combined_groups = self._subscription_state.prepare_channel_group_list(True) + + if len(combined_channels) == 0 and len(combined_groups) == 0: + return + + def callback(raw_result, status): + """ SubscribeEndpoint callback""" + if status.is_error(): + if status is not None and status.category == PNStatusCategory.PNCancelledCategory: + return + + if status.category is PNStatusCategory.PNTimeoutCategory and not self._should_stop: + self._start_subscribe_loop() + return + + logger.error("Exception in subscribe loop: %s" % str(status.error_data.exception)) + + if status is not None and status.category == PNStatusCategory.PNAccessDeniedCategory: + status.operation = PNOperationType.PNUnsubscribeOperation + + self._listener_manager.announce_status(status) + + self._reconnection_manager.start_polling() + self.disconnect() + else: + self._handle_endpoint_call(raw_result, status) + self._start_subscribe_loop() + + try: + self._subscribe_call = Subscribe(self._pubnub) \ + .channels(combined_channels).channel_groups(combined_groups) \ + .timetoken(self._timetoken).region(self._region) \ + .filter_expression(self._pubnub.config.filter_expression) \ + .async(callback) + except Exception as e: + logger.error("Subscribe request failed: %s" % e) + + def _stop_subscribe_loop(self): + sc = self._subscribe_call + + if sc is not None and not sc.is_executed and not sc.is_canceled: + sc.cancel() + + +class NativePeriodicCallback(object): + def __init__(self, callback, callback_time): + self._callback = callback + self._callback_time = callback_time + self._running = False + self._timeout = None + + def start(self): + self._running = True + self._schedule_next() + + def stop(self): + self._running = False + if self._timeout is not None: + self._timeout.cancel() + self._timeout = None + + def _run(self): + if not self._running: + return + try: + self._callback() + except Exception: + # TODO: handle the exception + pass + finally: + self._schedule_next() + + def _schedule_next(self): + self._timeout = threading.Timer(self._callback_time, self._run) + self._timeout.setDaemon(True) + self._timeout.start() + + +class NativeSubscribeMessageWorker(SubscribeMessageWorker): + def _take_message(self): + while not self._event.isSet(): + try: + # TODO: get rid of 1s timeout + msg = self._queue.get(True, 1) + if msg is not None: + self._process_incoming_payload(msg) + self._queue.task_done() + except Empty: + continue + except Exception as e: + # TODO: move to finally + self._queue.task_done() + self._event.set() + logger.error("take message interrupted: %s" % str(e)) + raise + + +class SubscribeListener(SubscribeCallback): + def __init__(self): + self.connected = False + self.connected_event = Event() + self.disconnected_event = Event() + self.presence_queue = Queue() + self.message_queue = Queue() + + def status(self, pubnub, status): + if utils.is_subscribed_event(status) and not self.connected_event.is_set(): + self.connected_event.set() + elif utils.is_unsubscribed_event(status) and not self.disconnected_event.is_set(): + self.disconnected_event.set() + + def message(self, pubnub, message): + self.message_queue.put(message) + + def presence(self, pubnub, presence): + self.presence_queue.put(presence) + + def wait_for_connect(self): + if not self.connected_event.is_set(): + self.connected_event.wait() + else: + raise Exception("the instance is already connected") + + def wait_for_disconnect(self): + if not self.disconnected_event.is_set(): + self.disconnected_event.wait() + else: + raise Exception("the instance is already disconnected") + + def wait_for_message_on(self, *channel_names): + channel_names = list(channel_names) + while True: + env = self.message_queue.get() + self.message_queue.task_done() + if env.channel in channel_names: + return env + else: + continue + + def wait_for_presence_on(self, *channel_names): + channel_names = list(channel_names) + while True: + env = self.presence_queue.get() + self.presence_queue.task_done() + if env.channel in channel_names: + return env + else: + continue + + +class NonSubscribeListener(object): + def __init__(self): + self.result = None + self.status = None + self.done_event = Event() + + def callback(self, result, status): + self.result = result + self.status = status + self.done_event.set() + + def await(self, timeout=5): + """ Returns False if a timeout happened, otherwise True""" + return self.done_event.wait(timeout) + + def await_result(self, timeout=5): + self.await(timeout) + return self.result + + def await_result_and_reset(self, timeout=5): + self.await(timeout) + cp = copy.copy(self.result) + self.reset() + return cp + + def reset(self): + self.result = None + self.status = None + self.done_event.clear() diff --git a/pubnub/pubnub_asyncio.py b/pubnub/pubnub_asyncio.py new file mode 100644 index 00000000..980294d8 --- /dev/null +++ b/pubnub/pubnub_asyncio.py @@ -0,0 +1,652 @@ +import logging +import json +import asyncio +import aiohttp +import math +import six + +from asyncio import Event, Queue, Semaphore + +from pubnub.models.consumer.common import PNStatus +from .endpoints.presence.heartbeat import Heartbeat +from .endpoints.presence.leave import Leave +from .endpoints.pubsub.subscribe import Subscribe +from .pubnub_core import PubNubCore +from .workers import SubscribeMessageWorker +from .managers import SubscriptionManager, PublishSequenceManager, ReconnectionManager +from . import utils +from .structures import ResponseInfo, RequestOptions +from .enums import PNStatusCategory, PNHeartbeatNotificationOptions, PNOperationType, PNReconnectionPolicy +from .callbacks import SubscribeCallback, ReconnectionCallback +from .errors import PNERR_SERVER_ERROR, PNERR_CLIENT_ERROR, PNERR_JSON_DECODING_FAILED, PNERR_REQUEST_CANCELLED, \ + PNERR_CLIENT_TIMEOUT +from .exceptions import PubNubException + +logger = logging.getLogger("pubnub") + + +class PubNubAsyncio(PubNubCore): + """ + PubNub Python SDK for asyncio framework + """ + + def __init__(self, config, custom_event_loop=None): + super(PubNubAsyncio, self).__init__(config) + self.event_loop = custom_event_loop or asyncio.get_event_loop() + + self._connector = None + self._session = None + + self.set_connector(aiohttp.TCPConnector(conn_timeout=config.connect_timeout, verify_ssl=True)) + + if self.config.enable_subscribe: + self._subscription_manager = AsyncioSubscriptionManager(self) + + self._publish_sequence_manager = AsyncioPublishSequenceManager(self.event_loop, + PubNubCore.MAX_SEQUENCE) + + def set_connector(self, cn): + if self._session is not None and self._session.closed: + self._session.close() + + self._connector = cn + self._session = aiohttp.ClientSession(loop=self.event_loop, connector=self._connector) + + def stop(self): + self._session.close() + if self._subscription_manager is not None: + self._subscription_manager.stop() + + def sdk_platform(self): + return "-Asyncio" + + def request_sync(self, *args): + raise NotImplementedError + + def request_deferred(self, *args): + raise NotImplementedError + + @asyncio.coroutine + def request_result(self, options_func, cancellation_event): + envelope = yield from self._request_helper(options_func, cancellation_event) + return envelope.result + + @asyncio.coroutine + def request_future(self, options_func, cancellation_event): + try: + res = yield from self._request_helper(options_func, cancellation_event) + return res + except PubNubException as e: + return PubNubAsyncioException( + result=None, + status=e.status + ) + except asyncio.TimeoutError: + return PubNubAsyncioException( + result=None, + status=options_func().create_status(PNStatusCategory.PNTimeoutCategory, + None, + None, + exception=PubNubException( + pn_error=PNERR_CLIENT_TIMEOUT + )) + ) + except asyncio.CancelledError: + return PubNubAsyncioException( + result=None, + status=options_func().create_status(PNStatusCategory.PNCancelledCategory, + None, + None, + exception=PubNubException( + pn_error=PNERR_REQUEST_CANCELLED + )) + ) + except Exception as e: + return PubNubAsyncioException( + result=None, + status=options_func().create_status(PNStatusCategory.PNUnknownCategory, + None, + None, + e) + ) + + @asyncio.coroutine + def _request_helper(self, options_func, cancellation_event): + """ + Query string should be provided as a manually serialized and encoded string. + + :param options_func: + :param cancellation_event: + :return: + """ + if cancellation_event is not None: + assert isinstance(cancellation_event, Event) + + options = options_func() + assert isinstance(options, RequestOptions) + + create_response = options.create_response + create_status = options.create_status + create_exception = options.create_exception + + params_to_merge_in = {} + + if options.operation_type == PNOperationType.PNPublishOperation: + params_to_merge_in['seqn'] = yield from self._publish_sequence_manager.get_next_sequence() + + options.merge_params_in(params_to_merge_in) + + url = utils.build_url(self.config.scheme(), self.base_origin, options.path) + log_url = utils.build_url(self.config.scheme(), self.base_origin, + options.path, options.query_string) + logger.debug("%s %s %s" % (options.method_string, log_url, options.data)) + + try: + response = yield from asyncio.wait_for( + self._session.request(options.method_string, url, + params=options.query_string, + headers=self.headers, + data=options.data if options.data is not None else None), + options.request_timeout) + except (asyncio.TimeoutError, asyncio.CancelledError): + raise + except Exception as e: + logger.error("session.request exception: %s" % str(e)) + raise + + body = yield from response.text() + + if cancellation_event is not None and cancellation_event.is_set(): + return + + response_info = None + status_category = PNStatusCategory.PNUnknownCategory + + if response is not None: + request_url = six.moves.urllib.parse.urlparse(response.url) + query = six.moves.urllib.parse.parse_qs(request_url.query) + uuid = None + auth_key = None + + if 'uuid' in query and len(query['uuid']) > 0: + uuid = query['uuid'][0] + + if 'auth_key' in query and len(query['auth_key']) > 0: + auth_key = query['auth_key'][0] + + response_info = ResponseInfo( + status_code=response.status, + tls_enabled='https' == request_url.scheme, + origin=request_url.hostname, + uuid=uuid, + auth_key=auth_key, + client_request=None + ) + + if body is not None and len(body) > 0: + try: + data = json.loads(body) + except ValueError: + if response.status == 599 and len(body) > 0: + data = body + else: + raise + except TypeError: + try: + data = json.loads(body.decode("utf-8")) + except ValueError: + raise create_exception(category=status_category, + response=response, + response_info=response_info, + exception=PubNubException( + pn_error=PNERR_JSON_DECODING_FAILED, + errormsg='json decode error', + ) + ) + else: + data = "N/A" + + logger.debug(data) + + if response.status != 200: + if response.status >= 500: + err = PNERR_SERVER_ERROR + else: + err = PNERR_CLIENT_ERROR + + if response.status == 403: + status_category = PNStatusCategory.PNAccessDeniedCategory + + if response.status == 400: + status_category = PNStatusCategory.PNBadRequestCategory + + raise create_exception(category=status_category, + response=data, + response_info=response_info, + exception=PubNubException( + errormsg=data, + pn_error=err, + status_code=response.status + ) + ) + else: + return AsyncioEnvelope( + result=create_response(data), + status=create_status( + PNStatusCategory.PNAcknowledgmentCategory, + data, + response_info, + None) + ) + + +class AsyncioReconnectionManager(ReconnectionManager): + def __init__(self, pubnub): + self._task = None + super(AsyncioReconnectionManager, self).__init__(pubnub) + + @asyncio.coroutine + def _register_heartbeat_timer(self): + while True: + self._recalculate_interval() + + yield from asyncio.sleep(self._timer_interval) + + logger.debug("reconnect loop at: %s" % utils.datetime_now()) + + try: + yield from self._pubnub.time().future() + self._connection_errors = 1 + self._callback.on_reconnect() + break + except Exception: + if self._pubnub.config.reconnect_policy == PNReconnectionPolicy.EXPONENTIAL: + logger.debug("reconnect interval increment at: %s" % utils.datetime_now()) + self._connection_errors += 1 + + def start_polling(self): + if self._pubnub.config.reconnect_policy == PNReconnectionPolicy.NONE: + logger.warn("reconnection policy is disabled, please handle reconnection manually.") + return + + self._task = asyncio.ensure_future(self._register_heartbeat_timer()) + + def stop_polling(self): + if self._task is not None and not self._task.cancelled(): + self._task.cancel() + + +class AsyncioPublishSequenceManager(PublishSequenceManager): + def __init__(self, ioloop, provided_max_sequence): + super(AsyncioPublishSequenceManager, self).__init__(provided_max_sequence) + self._lock = asyncio.Lock() + self._event_loop = ioloop + + @asyncio.coroutine + def get_next_sequence(self): + with (yield from self._lock): + if self.max_sequence == self.next_sequence: + self.next_sequence = 1 + else: + self.next_sequence += 1 + + return self.next_sequence + + +class AsyncioSubscriptionManager(SubscriptionManager): + def __init__(self, pubnub_instance): + subscription_manager = self + + self._message_worker = None + self._message_queue = Queue() + self._subscription_lock = Semaphore(1) + self._subscribe_loop_task = None + self._heartbeat_periodic_callback = None + self._reconnection_manager = AsyncioReconnectionManager(pubnub_instance) + + super(AsyncioSubscriptionManager, self).__init__(pubnub_instance) + self._start_worker() + + class AsyncioReconnectionCallback(ReconnectionCallback): + def on_reconnect(self): + subscription_manager.reconnect() + + pn_status = PNStatus() + pn_status.category = PNStatusCategory.PNReconnectedCategory + pn_status.error = False + + subscription_manager._subscription_status_announced = True + subscription_manager._listener_manager.announce_status(pn_status) + + self._reconnection_listener = AsyncioReconnectionCallback() + self._reconnection_manager.set_reconnection_listener(self._reconnection_listener) + + def _set_consumer_event(self): + if not self._message_worker.cancelled(): + self._message_worker.cancel() + + def _message_queue_put(self, message): + self._message_queue.put_nowait(message) + + def _start_worker(self): + consumer = AsyncioSubscribeMessageWorker(self._pubnub, + self._listener_manager, + self._message_queue, None) + self._message_worker = asyncio.ensure_future(consumer.run(), + loop=self._pubnub.event_loop) + + def reconnect(self): + # TODO: method is synchronized in Java + self._should_stop = False + self._subscribe_loop_task = asyncio.ensure_future(self._start_subscribe_loop()) + self._register_heartbeat_timer() + + def disconnect(self): + # TODO: method is synchronized in Java + self._should_stop = True + self._stop_heartbeat_timer() + self._stop_subscribe_loop() + + def stop(self): + super(AsyncioSubscriptionManager, self).stop() + self._reconnection_manager.stop_polling() + if self._subscribe_loop_task is not None and not self._subscribe_loop_task.cancelled(): + self._subscribe_loop_task.cancel() + + @asyncio.coroutine + def _start_subscribe_loop(self): + self._stop_subscribe_loop() + + yield from self._subscription_lock.acquire() + + combined_channels = self._subscription_state.prepare_channel_list(True) + combined_groups = self._subscription_state.prepare_channel_group_list(True) + + if len(combined_channels) == 0 and len(combined_groups) == 0: + self._subscription_lock.release() + return + + self._subscribe_request_task = asyncio.ensure_future(Subscribe(self._pubnub) + .channels(combined_channels) + .channel_groups(combined_groups) + .timetoken(self._timetoken).region(self._region) + .filter_expression(self._pubnub.config.filter_expression) + .future()) + + e = yield from self._subscribe_request_task + + if self._subscribe_request_task.cancelled(): + self._subscription_lock.release() + return + + if e.is_error(): + if e.status is not None and e.status.category == PNStatusCategory.PNCancelledCategory: + self._subscription_lock.release() + return + + if e.status is not None and e.status.category == PNStatusCategory.PNTimeoutCategory: + self._pubnub.event_loop.call_soon(self._start_subscribe_loop) + self._subscription_lock.release() + return + + logger.error("Exception in subscribe loop: %s" % str(e)) + + if e.status is not None and e.status.category == PNStatusCategory.PNAccessDeniedCategory: + e.status.operation = PNOperationType.PNUnsubscribeOperation + + # TODO: raise error + self._listener_manager.announce_status(e.status) + + self._reconnection_manager.start_polling() + self._subscription_lock.release() + self.disconnect() + return + else: + self._handle_endpoint_call(e.result, e.status) + self._subscription_lock.release() + self._subscribe_loop_task = asyncio.ensure_future(self._start_subscribe_loop()) + + self._subscription_lock.release() + + def _stop_subscribe_loop(self): + if self._subscribe_request_task is not None and not self._subscribe_request_task.cancelled(): + self._subscribe_request_task.cancel() + + def _stop_heartbeat_timer(self): + if self._heartbeat_periodic_callback is not None: + self._heartbeat_periodic_callback.stop() + + def _register_heartbeat_timer(self): + super(AsyncioSubscriptionManager, self)._register_heartbeat_timer() + + self._heartbeat_periodic_callback = AsyncioPeriodicCallback( + self._perform_heartbeat_loop, + self._pubnub.config.heartbeat_interval * 1000, + self._pubnub.event_loop) + if not self._should_stop: + self._heartbeat_periodic_callback.start() + + @asyncio.coroutine + def _perform_heartbeat_loop(self): + if self._heartbeat_call is not None: + # TODO: cancel call + pass + + cancellation_event = Event() + state_payload = self._subscription_state.state_payload() + presence_channels = self._subscription_state.prepare_channel_list(False) + presence_groups = self._subscription_state.prepare_channel_group_list(False) + + if len(presence_channels) == 0 and len(presence_groups) == 0: + return + + try: + heartbeat_call = (Heartbeat(self._pubnub) + .channels(presence_channels) + .channel_groups(presence_groups) + .state(state_payload) + .cancellation_event(cancellation_event) + .future()) + + envelope = yield from heartbeat_call + + heartbeat_verbosity = self._pubnub.config.heartbeat_notification_options + if envelope.status.is_error: + if heartbeat_verbosity == PNHeartbeatNotificationOptions.ALL or \ + heartbeat_verbosity == PNHeartbeatNotificationOptions.ALL: + self._listener_manager.announce_stateus(envelope.status) + else: + if heartbeat_verbosity == PNHeartbeatNotificationOptions.ALL: + self._listener_manager.announce_stateus(envelope.status) + + except PubNubAsyncioException as e: + pass + # TODO: check correctness + # if e.status is not None and e.status.category == PNStatusCategory.PNTimeoutCategory: + # self._start_subscribe_loop() + # else: + # self._listener_manager.announce_status(e.status) + finally: + cancellation_event.set() + + def _send_leave(self, unsubscribe_operation): + asyncio.ensure_future(self._send_leave_helper(unsubscribe_operation)) + + @asyncio.coroutine + def _send_leave_helper(self, unsubscribe_operation): + envelope = yield from Leave(self._pubnub) \ + .channels(unsubscribe_operation.channels) \ + .channel_groups(unsubscribe_operation.channel_groups).future() + + self._listener_manager.announce_status(envelope.status) + + +class AsyncioSubscribeMessageWorker(SubscribeMessageWorker): + @asyncio.coroutine + def run(self): + yield from self._take_message() + + @asyncio.coroutine + def _take_message(self): + while True: + try: + msg = yield from self._queue.get() + if msg is not None: + self._process_incoming_payload(msg) + self._queue.task_done() + except asyncio.CancelledError: + logger.debug("Message Worker cancelled") + break + except Exception as e: + logger.error("take message interrupted: %s" % str(e)) + raise + + +class AsyncioPeriodicCallback(object): + def __init__(self, callback, callback_time, event_loop): + self._callback = callback + self._callback_time = callback_time + self._event_loop = event_loop + self._next_timeout = None + self._running = False + self._timeout = None + + def start(self): + self._running = True + self._next_timeout = self._event_loop.time() + self._schedule_next() + + def stop(self): + self._running = False + if self._timeout is not None: + self._timeout.cancel() + self._timeout = None + + def _run(self): + if not self._running: + return + try: + asyncio.ensure_future(self._callback()) + except Exception: + raise + finally: + self._schedule_next() + + def _schedule_next(self): + current_time = self._event_loop.time() + + if self._next_timeout <= current_time: + callback_time_sec = self._callback_time / 1000.0 + self._next_timeout += (math.floor( + (current_time - self._next_timeout) / callback_time_sec) + 1) * callback_time_sec + + self._timeout = self._event_loop.call_at(self._next_timeout, self._run) + + +class AsyncioEnvelope(object): + def __init__(self, result, status): + self.result = result + self.status = status + + @staticmethod + def is_error(): + return False + + +class PubNubAsyncioException(Exception): + def __init__(self, result, status): + self.result = result + self.status = status + + def __str__(self): + return str(self.status.error_data.exception) + + @staticmethod + def is_error(): + return True + + def value(self): + return self.status.error_data.exception + + +class SubscribeListener(SubscribeCallback): + def __init__(self): + self.connected = False + self.connected_event = Event() + self.disconnected_event = Event() + self.presence_queue = Queue() + self.message_queue = Queue() + self.error_queue = Queue() + + def status(self, pubnub, status): + if utils.is_subscribed_event(status) and not self.connected_event.is_set(): + self.connected_event.set() + elif utils.is_unsubscribed_event(status) and not self.disconnected_event.is_set(): + self.disconnected_event.set() + elif status.is_error(): + self.error_queue.put_nowait(status.error_data.exception) + + def message(self, pubnub, message): + self.message_queue.put_nowait(message) + + def presence(self, pubnub, presence): + self.presence_queue.put_nowait(presence) + + @asyncio.coroutine + def _wait_for(self, coro): + scc_task = asyncio.ensure_future(coro) + err_task = asyncio.ensure_future(self.error_queue.get()) + + yield from asyncio.wait([ + scc_task, + err_task + ], return_when=asyncio.FIRST_COMPLETED) + + if err_task.done() and not scc_task.done(): + if not scc_task.cancelled(): + scc_task.cancel() + raise err_task.result() + else: + if not err_task.cancelled(): + err_task.cancel() + return scc_task.result() + + @asyncio.coroutine + def wait_for_connect(self): + if not self.connected_event.is_set(): + yield from self._wait_for(self.connected_event.wait()) + else: + raise Exception("instance is already connected") + + @asyncio.coroutine + def wait_for_disconnect(self): + if not self.disconnected_event.is_set(): + yield from self._wait_for(self.disconnected_event.wait()) + else: + raise Exception("instance is already disconnected") + + @asyncio.coroutine + def wait_for_message_on(self, *channel_names): + channel_names = list(channel_names) + while True: + try: + env = yield from self._wait_for(self.message_queue.get()) + if env.channel in channel_names: + return env + else: + continue + finally: + self.message_queue.task_done() + + @asyncio.coroutine + def wait_for_presence_on(self, *channel_names): + channel_names = list(channel_names) + while True: + try: + env = yield from self._wait_for(self.presence_queue.get()) + if env.channel in channel_names: + return env + else: + continue + finally: + self.presence_queue.task_done() diff --git a/pubnub/pubnub_core.py b/pubnub/pubnub_core.py new file mode 100644 index 00000000..bb35e173 --- /dev/null +++ b/pubnub/pubnub_core.py @@ -0,0 +1,166 @@ +import logging +import time + +from abc import ABCMeta, abstractmethod + +from .managers import BasePathManager +from .builders import SubscribeBuilder +from .builders import UnsubscribeBuilder +from .endpoints.time import Time +from .endpoints.history import History +from .endpoints.access.audit import Audit +from .endpoints.access.grant import Grant +from .endpoints.access.revoke import Revoke +from .endpoints.channel_groups.add_channel_to_channel_group import AddChannelToChannelGroup +from .endpoints.channel_groups.list_channels_in_channel_group import ListChannelsInChannelGroup +from .endpoints.channel_groups.remove_channel_from_channel_group import RemoveChannelFromChannelGroup +from .endpoints.channel_groups.remove_channel_group import RemoveChannelGroup +from .endpoints.presence.get_state import GetState +from .endpoints.presence.heartbeat import Heartbeat +from .endpoints.presence.set_state import SetState +from .endpoints.pubsub.publish import Publish +from .endpoints.presence.here_now import HereNow +from .endpoints.presence.where_now import WhereNow + +from .endpoints.push.add_channels_to_push import AddChannelsToPush +from .endpoints.push.remove_channels_from_push import RemoveChannelsFromPush +from .endpoints.push.remove_device import RemoveDeviceFromPush +from .endpoints.push.list_push_provisions import ListPushProvisions + +logger = logging.getLogger("pubnub") + + +class PubNubCore: + """A base class for PubNub Python API implementations""" + SDK_VERSION = "4.0.8" + SDK_NAME = "PubNub-Python" + + TIMESTAMP_DIVIDER = 1000 + MAX_SEQUENCE = 65535 + + __metaclass__ = ABCMeta + + def __init__(self, config): + self.config = config + self.config.validate() + self.headers = { + 'User-Agent': self.sdk_name + } + + self._subscription_manager = None + self._publish_sequence_manager = None + self._base_path_manager = BasePathManager(config) + + @property + def base_origin(self): + return self._base_path_manager.get_base_path() + + @property + def sdk_name(self): + return "%s%s/%s" % (PubNubCore.SDK_NAME, self.sdk_platform(), PubNubCore.SDK_VERSION) + + @abstractmethod + def sdk_platform(self): + pass + + @property + def uuid(self): + return self.config.uuid + + def add_listener(self, listener): + self._validate_subscribe_manager_enabled() + + return self._subscription_manager.add_listener(listener) + + def remove_listener(self, listener): + self._validate_subscribe_manager_enabled() + + return self._subscription_manager.remove_listener(listener) + + def get_subscribed_channels(self): + self._validate_subscribe_manager_enabled() + + return self._subscription_manager.get_subscribed_channels() + + def get_subscribed_channel_groups(self): + self._validate_subscribe_manager_enabled() + + return self._subscription_manager.get_subscribed_channel_groups() + + def add_channel_to_channel_group(self): + return AddChannelToChannelGroup(self) + + def remove_channel_from_channel_group(self): + return RemoveChannelFromChannelGroup(self) + + def list_channels_in_channel_group(self): + return ListChannelsInChannelGroup(self) + + def remove_channel_group(self): + return RemoveChannelGroup(self) + + def subscribe(self): + return SubscribeBuilder(self._subscription_manager) + + def unsubscribe(self): + return UnsubscribeBuilder(self._subscription_manager) + + def unsubscribe_all(self): + return self._subscription_manager.unsubscribe_all() + + def reconnect(self): + return self._subscription_manager.reconnect() + + def heartbeat(self): + return Heartbeat(self) + + def set_state(self): + return SetState(self, self._subscription_manager) + + def get_state(self): + return GetState(self) + + def here_now(self): + return HereNow(self) + + def where_now(self): + return WhereNow(self) + + def publish(self): + return Publish(self) + + def grant(self): + return Grant(self) + + def revoke(self): + return Revoke(self) + + def audit(self): + return Audit(self) + + # Push Related methods + def list_push_channels(self): + return ListPushProvisions(self) + + def add_channels_to_push(self): + return AddChannelsToPush(self) + + def remove_channels_from_push(self): + return RemoveChannelsFromPush(self) + + def remove_device_from_push(self): + return RemoveDeviceFromPush(self) + + def history(self): + return History(self) + + def time(self): + return Time(self) + + @staticmethod + def timestamp(): + return int(time.time()) + + def _validate_subscribe_manager_enabled(self): + if self._subscription_manager is None: + raise Exception("Subscription manager is not enabled for this instance") diff --git a/pubnub/pubnub_tornado.py b/pubnub/pubnub_tornado.py new file mode 100644 index 00000000..a4c0a675 --- /dev/null +++ b/pubnub/pubnub_tornado.py @@ -0,0 +1,659 @@ +import json +import logging +import time +import datetime + +import math +import six +import tornado.gen +import tornado.httpclient +import tornado.ioloop +from tornado import gen + +from tornado import ioloop +from tornado import stack_context +from tornado.concurrent import Future +from tornado.ioloop import PeriodicCallback +from tornado.locks import Event, Semaphore, Lock +from tornado.queues import Queue +from tornado.simple_httpclient import SimpleAsyncHTTPClient + +from . import utils +from .models.consumer.common import PNStatus +from .callbacks import SubscribeCallback, ReconnectionCallback +from .endpoints.presence.leave import Leave +from .endpoints.pubsub.subscribe import Subscribe +from .enums import PNStatusCategory, PNHeartbeatNotificationOptions, PNOperationType, PNReconnectionPolicy +from .errors import PNERR_SERVER_ERROR, PNERR_CLIENT_ERROR, PNERR_JSON_DECODING_FAILED, PNERR_CLIENT_TIMEOUT, \ + PNERR_CONNECTION_ERROR +from .exceptions import PubNubException +from .managers import SubscriptionManager, PublishSequenceManager, ReconnectionManager +from .pubnub_core import PubNubCore +from .structures import ResponseInfo +from .workers import SubscribeMessageWorker + +logger = logging.getLogger("pubnub") + +tornado.httpclient.AsyncHTTPClient.configure(SimpleAsyncHTTPClient) + + +class PubNubTornado(PubNubCore): + MAX_CLIENTS = 1000 + + def stop(self): + self.ioloop.stop() + + def start(self): + self.ioloop.start() + + def timeout(self, delay, callback, *args): + handle = None + + def cancel(): + self.ioloop.remove_timeout(handle) + + def cb(): + if callback is not None: + callback(*args) + + handle = self.ioloop.add_timeout(time.time() + float(delay), cb) + + return cancel + + def sdk_platform(self): + return "-Tornado" + + def __init__(self, config, custom_ioloop=None): + super(PubNubTornado, self).__init__(config) + self.ioloop = custom_ioloop or ioloop.IOLoop.instance() + + if self.config.enable_subscribe: + self._subscription_manager = TornadoSubscriptionManager(self) + + self._publish_sequence_manager = TornadoPublishSequenceManager(PubNubCore.MAX_SEQUENCE) + + self.http = tornado.httpclient.AsyncHTTPClient(max_clients=PubNubTornado.MAX_CLIENTS) + self.id = None + + self.headers = { + 'User-Agent': self.sdk_name, + 'Accept-Encoding': 'utf-8' + } + + def request_sync(self, *args): + raise NotImplementedError + + def request_async(self, *args): + raise NotImplementedError + + def request_deferred(self, *args): + raise NotImplementedError + + @tornado.gen.coroutine + def request_result(self, options_func, cancellation_event): + try: + envelope = yield self._request_helper(options_func, cancellation_event) + raise tornado.gen.Return(envelope.result) + except PubNubTornadoException as e: + raise e.status.error_data.exception + + @tornado.gen.coroutine + def request_future(self, options_func, cancellation_event): + try: + e = yield self._request_helper(options_func, cancellation_event) + except PubNubTornadoException as ex: + e = ex + except Exception as ex: + e = PubNubTornadoException( + result=None, + status=options_func().create_status(PNStatusCategory.PNUnknownCategory, + None, + None, + ex) + ) + + raise tornado.gen.Return(e) + + # REFACTOR: quickly adjusted to fit the new result() and future() endpoints + def _request_helper(self, options_func, cancellation_event): + if cancellation_event is not None: + assert isinstance(cancellation_event, Event) + + options = options_func() + + create_response = options.create_response + create_status_response = options.create_status + + params_to_merge_in = {} + + if options.operation_type == PNOperationType.PNPublishOperation: + params_to_merge_in['seqn'] = self._publish_sequence_manager.get_next_sequence() + + options.merge_params_in(params_to_merge_in) + + future = Future() + + url = utils.build_url(self.config.scheme(), self.base_origin, + options.path, options.query_string) + logger.debug("%s %s %s" % (options.method_string, url, options.data)) + + request = tornado.httpclient.HTTPRequest( + url=url, + method=options.method_string, + headers=self.headers, + body=options.data if options.data is not None else None, + connect_timeout=options.connect_timeout, + request_timeout=options.request_timeout) + + def response_callback(response): + if cancellation_event is not None and cancellation_event.is_set(): + return + + body = response.body + response_info = None + status_category = PNStatusCategory.PNUnknownCategory + + if response is not None: + request_url = six.moves.urllib.parse.urlparse(response.effective_url) + query = six.moves.urllib.parse.parse_qs(request_url.query) + uuid = None + auth_key = None + + if 'uuid' in query and len(query['uuid']) > 0: + uuid = query['uuid'][0] + + if 'auth_key' in query and len(query['auth_key']) > 0: + auth_key = query['auth_key'][0] + + response_info = ResponseInfo( + status_code=response.code, + tls_enabled='https' == request_url.scheme, + origin=request_url.hostname, + uuid=uuid, + auth_key=auth_key, + client_request=response.request + ) + + if body is not None and len(body) > 0: + try: + data = json.loads(body) + except (ValueError, TypeError): + try: + data = json.loads(body.decode("utf-8")) + except ValueError: + tornado_result = PubNubTornadoException( + create_response(None), + create_status_response(status_category, response, response_info, PubNubException( + pn_error=PNERR_JSON_DECODING_FAILED, + errormsg='json decode error') + ) + ) + future.set_exception(tornado_result) + return + else: + data = "N/A" + + logger.debug(data) + + if response.error is not None: + if response.code >= 500: + err = PNERR_SERVER_ERROR + data = str(response.error) + else: + err = PNERR_CLIENT_ERROR + + e = PubNubException( + errormsg=data, + pn_error=err, + status_code=response.code, + ) + + if response.code == 403: + status_category = PNStatusCategory.PNAccessDeniedCategory + + if response.code == 400: + status_category = PNStatusCategory.PNBadRequestCategory + + if response.code == 599: + if 'HTTP 599: Timeout during request' == data: + status_category = PNStatusCategory.PNTimeoutCategory + e = PubNubException( + pn_error=PNERR_CLIENT_TIMEOUT, + errormsg=str(e) + ) + elif 'HTTP 599: Stream closed' == data or\ + 'Name or service not known' in data or\ + 'Temporary failure in name resolution' in data: + status_category = PNStatusCategory.PNNetworkIssuesCategory + e = PubNubException( + pn_error=PNERR_CONNECTION_ERROR, + errormsg=str(e) + ) + # TODO: add check for other status codes + + future.set_exception(PubNubTornadoException( + result=data, + status=create_status_response(status_category, data, response_info, e) + )) + else: + future.set_result(TornadoEnvelope( + result=create_response(data), + status=create_status_response( + PNStatusCategory.PNAcknowledgmentCategory, + data, + response_info, + None) + )) + + self.http.fetch( + request=request, + callback=response_callback + ) + + return future + + +class TornadoReconnectionManager(ReconnectionManager): + def __init__(self, pubnub): + self._cancelled_event = Event() + super(TornadoReconnectionManager, self).__init__(pubnub) + + @gen.coroutine + def _register_heartbeat_timer(self): + self._cancelled_event.clear() + + while not self._cancelled_event.is_set(): + if self._pubnub.config.reconnect_policy == PNReconnectionPolicy.EXPONENTIAL: + self._timer_interval = int(math.pow(2, self._connection_errors) - 1) + if self._timer_interval > self.MAXEXPONENTIALBACKOFF: + self._timer_interval = self.MINEXPONENTIALBACKOFF + self._connection_errors = 1 + logger.debug("timerInterval > MAXEXPONENTIALBACKOFF at: %s" % utils.datetime_now()) + elif self._timer_interval < 1: + self._timer_interval = self.MINEXPONENTIALBACKOFF + logger.debug("timerInterval = %d at: %s" % (self._timer_interval, utils.datetime_now())) + else: + self._timer_interval = self.INTERVAL + + # >>> Wait given interval or cancel + sleeper = tornado.gen.sleep(self._timer_interval) + canceller = self._cancelled_event.wait() + + wi = tornado.gen.WaitIterator(canceller, sleeper) + + while not wi.done(): + try: + future = wi.next() + yield future + except Exception as e: + # TODO: verify the error will not be eaten + logger.error(e) + raise + else: + if wi.current_future == sleeper: + break + elif wi.current_future == canceller: + return + else: + raise Exception("unknown future raised") + + logger.debug("reconnect loop at: %s" % utils.datetime_now()) + + # >>> Attempt to request /time/0 endpoint + try: + yield self._pubnub.time().result() + self._connection_errors = 1 + self._callback.on_reconnect() + logger.debug("reconnection manager stop due success time endpoint call: %s" % utils.datetime_now()) + break + except Exception: + if self._pubnub.config.reconnect_policy == PNReconnectionPolicy.EXPONENTIAL: + logger.debug("reconnect interval increment at: %s" % utils.datetime_now()) + self._connection_errors += 1 + + def start_polling(self): + if self._pubnub.config.reconnect_policy == PNReconnectionPolicy.NONE: + logger.warn("reconnection policy is disabled, please handle reconnection manually.") + return + + self._pubnub.ioloop.spawn_callback(self._register_heartbeat_timer) + + def stop_polling(self): + if self._cancelled_event is not None and not self._cancelled_event.is_set(): + self._cancelled_event.set() + + +class TornadoPublishSequenceManager(PublishSequenceManager): + def __init__(self, provided_max_sequence): + super(TornadoPublishSequenceManager, self).__init__(provided_max_sequence) + self._lock = Lock() + self._ioloop = ioloop + + def get_next_sequence(self): + if self.max_sequence == self.next_sequence: + self.next_sequence = 1 + else: + self.next_sequence += 1 + + return self.next_sequence + + +class TornadoSubscribeMessageWorker(SubscribeMessageWorker): + @tornado.gen.coroutine + def run(self): + self._take_message() + + @tornado.gen.coroutine + def _take_message(self): + i = 0 + while not self._event.is_set(): + try: + msg = yield self._queue.get(datetime.timedelta(seconds=1)) + if msg is not None: + self._process_incoming_payload(msg) + self._queue.task_done() + except tornado.gen.TimeoutError: + i += 1 + continue + + +class TornadoSubscriptionManager(SubscriptionManager): + def __init__(self, pubnub_instance): + + subscription_manager = self + + self._message_queue = Queue() + self._consumer_event = Event() + self._cancellation_event = Event() + self._subscription_lock = Semaphore(1) + # self._current_request_key_object = None + self._heartbeat_periodic_callback = None + self._reconnection_manager = TornadoReconnectionManager(pubnub_instance) + + super(TornadoSubscriptionManager, self).__init__(pubnub_instance) + self._start_worker() + + class TornadoReconnectionCallback(ReconnectionCallback): + def on_reconnect(self): + subscription_manager.reconnect() + + pn_status = PNStatus() + pn_status.category = PNStatusCategory.PNReconnectedCategory + pn_status.error = False + + subscription_manager._subscription_status_announced = True + subscription_manager._listener_manager.announce_status(pn_status) + + self._reconnection_listener = TornadoReconnectionCallback() + self._reconnection_manager.set_reconnection_listener(self._reconnection_listener) + + def _set_consumer_event(self): + self._consumer_event.set() + + def _message_queue_put(self, message): + self._message_queue.put(message) + + def _start_worker(self): + self._consumer = TornadoSubscribeMessageWorker(self._pubnub, + self._listener_manager, + self._message_queue, + self._consumer_event) + run = stack_context.wrap(self._consumer.run) + self._pubnub.ioloop.spawn_callback(run) + + def reconnect(self): + self._should_stop = False + self._pubnub.ioloop.spawn_callback(self._start_subscribe_loop) + # self._register_heartbeat_timer() + + def disconnect(self): + self._should_stop = True + self._stop_heartbeat_timer() + self._stop_subscribe_loop() + + @tornado.gen.coroutine + def _start_subscribe_loop(self): + self._stop_subscribe_loop() + + yield self._subscription_lock.acquire() + + self._cancellation_event.clear() + + combined_channels = self._subscription_state.prepare_channel_list(True) + combined_groups = self._subscription_state.prepare_channel_group_list(True) + + if len(combined_channels) == 0 and len(combined_groups) == 0: + return + + envelope_future = Subscribe(self._pubnub) \ + .channels(combined_channels).channel_groups(combined_groups) \ + .timetoken(self._timetoken).region(self._region) \ + .filter_expression(self._pubnub.config.filter_expression) \ + .cancellation_event(self._cancellation_event) \ + .future() + + canceller_future = self._cancellation_event.wait() + + wi = tornado.gen.WaitIterator(envelope_future, canceller_future) + + # iterates 2 times: one for result one for cancelled + while not wi.done(): + try: + result = yield wi.next() + except Exception as e: + # TODO: verify the error will not be eaten + logger.error(e) + raise + else: + if wi.current_future == envelope_future: + e = result + elif wi.current_future == canceller_future: + return + else: + raise Exception("Unexpected future resolved: %s" % str(wi.current_future)) + + if e.is_error(): + # 599 error doesn't works - tornado use this status code + # for a wide range of errors, for ex: + # HTTP Server Error (599): [Errno -2] Name or service not known + if e.status is not None and e.status.category == PNStatusCategory.PNTimeoutCategory: + self._pubnub.ioloop.spawn_callback(self._start_subscribe_loop) + return + + logger.error("Exception in subscribe loop: %s" % str(e)) + + if e.status is not None and e.status.category == PNStatusCategory.PNAccessDeniedCategory: + e.status.operation = PNOperationType.PNUnsubscribeOperation + + self._listener_manager.announce_status(e.status) + + self._reconnection_manager.start_polling() + self.disconnect() + return + else: + self._handle_endpoint_call(e.result, e.status) + + self._pubnub.ioloop.spawn_callback(self._start_subscribe_loop) + + finally: + self._cancellation_event.set() + yield tornado.gen.moment + self._subscription_lock.release() + self._cancellation_event.clear() + break + + def _stop_subscribe_loop(self): + if self._cancellation_event is not None and not self._cancellation_event.is_set(): + self._cancellation_event.set() + + def _stop_heartbeat_timer(self): + if self._heartbeat_periodic_callback is not None: + self._heartbeat_periodic_callback.stop() + + def _register_heartbeat_timer(self): + super(TornadoSubscriptionManager, self)._register_heartbeat_timer() + + self._heartbeat_periodic_callback = PeriodicCallback( + stack_context.wrap(self._perform_heartbeat_loop), + self._pubnub.config.heartbeat_interval * + TornadoSubscriptionManager.HEARTBEAT_INTERVAL_MULTIPLIER, + self._pubnub.ioloop) + self._heartbeat_periodic_callback.start() + + @tornado.gen.coroutine + def _perform_heartbeat_loop(self): + if self._heartbeat_call is not None: + # TODO: cancel call + pass + + cancellation_event = Event() + state_payload = self._subscription_state.state_payload() + presence_channels = self._subscription_state.prepare_channel_list(False) + presence_groups = self._subscription_state.prepare_channel_group_list(False) + + if len(presence_channels) == 0 and len(presence_groups) == 0: + return + + try: + envelope = yield self._pubnub.heartbeat() \ + .channels(presence_channels) \ + .channel_groups(presence_groups) \ + .state(state_payload) \ + .cancellation_event(cancellation_event) \ + .future() + + heartbeat_verbosity = self._pubnub.config.heartbeat_notification_options + if envelope.status.is_error: + if heartbeat_verbosity == PNHeartbeatNotificationOptions.ALL or \ + heartbeat_verbosity == PNHeartbeatNotificationOptions.ALL: + self._listener_manager.announce_stateus(envelope.status) + else: + if heartbeat_verbosity == PNHeartbeatNotificationOptions.ALL: + self._listener_manager.announce_stateus(envelope.status) + + except PubNubTornadoException: + pass + # TODO: check correctness + # if e.status is not None and e.status.category == PNStatusCategory.PNTimeoutCategory: + # self._start_subscribe_loop() + # else: + # self._listener_manager.announce_status(e.status) + except Exception as e: + print(e) + finally: + cancellation_event.set() + + @tornado.gen.coroutine + def _send_leave(self, unsubscribe_operation): + envelope = yield Leave(self._pubnub) \ + .channels(unsubscribe_operation.channels) \ + .channel_groups(unsubscribe_operation.channel_groups).future() + self._listener_manager.announce_status(envelope.status) + + +class TornadoEnvelope(object): + def __init__(self, result, status): + self.result = result + self.status = status + + @staticmethod + def is_error(): + return False + + +class PubNubTornadoException(Exception): + def __init__(self, result, status): + self.result = result + self.status = status + + def __str__(self): + return str(self.status.error_data.exception) + + @staticmethod + def is_error(): + return True + + def value(self): + return self.status.error_data.exception + + +class SubscribeListener(SubscribeCallback): + def __init__(self): + self.connected = False + self.connected_event = Event() + self.disconnected_event = Event() + self.presence_queue = Queue() + self.message_queue = Queue() + self.error_queue = Queue() + + def status(self, pubnub, status): + if utils.is_subscribed_event(status) and not self.connected_event.is_set(): + self.connected_event.set() + elif utils.is_unsubscribed_event(status) and not self.disconnected_event.is_set(): + self.disconnected_event.set() + elif status.is_error(): + self.error_queue.put_nowait(status.error_data.exception) + + def message(self, pubnub, message): + self.message_queue.put(message) + + def presence(self, pubnub, presence): + self.presence_queue.put(presence) + + @tornado.gen.coroutine + def _wait_for(self, coro): + error = self.error_queue.get() + wi = tornado.gen.WaitIterator(coro, error) + + while not wi.done(): + result = yield wi.next() + + if wi.current_future == coro: + raise gen.Return(result) + elif wi.current_future == error: + raise result + else: + raise Exception("Unexpected future resolved: %s" % str(wi.current_future)) + + @tornado.gen.coroutine + def wait_for_connect(self): + if not self.connected_event.is_set(): + yield self._wait_for(self.connected_event.wait()) + else: + raise Exception("instance is already connected") + + @tornado.gen.coroutine + def wait_for_disconnect(self): + if not self.disconnected_event.is_set(): + yield self._wait_for(self.disconnected_event.wait()) + else: + raise Exception("instance is already disconnected") + + @tornado.gen.coroutine + def wait_for_message_on(self, *channel_names): + channel_names = list(channel_names) + while True: + try: + env = yield self._wait_for(self.message_queue.get()) + if env.channel in channel_names: + raise tornado.gen.Return(env) + else: + continue + finally: + self.message_queue.task_done() + + @tornado.gen.coroutine + def wait_for_presence_on(self, *channel_names): + channel_names = list(channel_names) + while True: + try: + try: + env = yield self._wait_for(self.presence_queue.get()) + except: + break + if env.channel in channel_names: + raise tornado.gen.Return(env) + else: + continue + finally: + self.presence_queue.task_done() diff --git a/pubnub/pubnub_twisted.py b/pubnub/pubnub_twisted.py new file mode 100644 index 00000000..b01c09f7 --- /dev/null +++ b/pubnub/pubnub_twisted.py @@ -0,0 +1,409 @@ +import json +import logging +import time + +from urlparse import urlparse, parse_qs +from StringIO import StringIO + +from twisted.internet import reactor as _reactor +from twisted.internet.task import LoopingCall +from twisted.internet.defer import Deferred, DeferredQueue +from twisted.internet.protocol import Protocol +from twisted.internet.error import ConnectingCancelledError +from twisted.web.client import Agent +from twisted.web.client import HTTPConnectionPool +from twisted.web.http_headers import Headers +from twisted.web.client import FileBodyProducer + +from . import utils +from .workers import SubscribeMessageWorker +from .pubnub_core import PubNubCore +from .managers import SubscriptionManager, PublishSequenceManager +from .enums import PNStatusCategory, PNHeartbeatNotificationOptions, PNOperationType +from .errors import PNERR_CLIENT_ERROR, PNERR_CONNECTION_ERROR, \ + PNERR_SERVER_ERROR, PNERR_JSON_DECODING_FAILED +from .exceptions import PubNubException +from .structures import ResponseInfo + +from .endpoints.pubsub.subscribe import Subscribe +from .endpoints.presence.leave import Leave +from .endpoints.presence.heartbeat import Heartbeat + +logger = logging.getLogger("pubnub") + + +class PubNubResponse(Protocol): + def __init__(self, finished, code): + self.finished = finished + self.code = code + + def dataReceived(self, body): + self.finished.callback(TwistedResponse(body, self.code)) + + +class TwistedSubscribeMessageWorker(SubscribeMessageWorker): + def run(self): + self._take_message() + + def _take_message(self): + self._queue.get().addCallback(self.send_message_to_processing) + + def send_message_to_processing(self, message): + if message is not None: + self._pubnub.reactor.callInThread(self._process_incoming_payload, message) + + +class TwistedSubscriptionManager(SubscriptionManager): + def __init__(self, pubnub_instance): + self._message_queue = DeferredQueue() + self.worker_loop = None + self._heartbeat_loop = None + self._heartbeat_call = None + self.clock = pubnub_instance.clock + super(TwistedSubscriptionManager, self).__init__(pubnub_instance) + + def _announce_status(self, status): + self._listener_manager.announce_status(status) + + def _start_worker(self): + consumer = TwistedSubscribeMessageWorker(self._pubnub, self._listener_manager, self._message_queue, None) + looping_call = LoopingCall(consumer.run) + + if self.clock is not None: + looping_call.clock = self.clock + + self.worker_loop = looping_call.start(0.1, False) + + def _set_consumer_event(self): + raise NotImplementedError + + def _message_queue_put(self, message): + self._message_queue.put(message) + + def _start_subscribe_loop(self): + self._stop_subscribe_loop() + + combined_channels = self._subscription_state.prepare_channel_list(True) + combined_groups = self._subscription_state.prepare_channel_group_list(True) + + if len(combined_channels) == 0 and len(combined_groups) == 0: + return + + def continue_subscribe_loop(envelope): + try: + self._handle_endpoint_call(envelope.raw_result, envelope.status) + except Exception as ex: + return ex + self._start_subscribe_loop() + + def manage_failure(failure): + if failure.type == PubNubTwistedException: + self._announce_status(failure.value.status) + if failure.value.status.category in (PNStatusCategory.PNDisconnectedCategory, + PNStatusCategory.PNUnexpectedDisconnectCategory, + PNStatusCategory.PNCancelledCategory, + PNStatusCategory.PNBadRequestCategory, + PNStatusCategory.PNMalformedFilterExpressionCategory): + time.sleep(30) # TODO: SET VALUE ACCORDING TO DOCS + self._start_subscribe_loop() + else: + return failure + + try: + self._subscribe_request_task = Subscribe(self._pubnub) \ + .channels(combined_channels) \ + .channel_groups(combined_groups) \ + .timetoken(self._timetoken) \ + .region(self._region) \ + .filter_expression(self._pubnub.config.filter_expression) \ + .deferred() \ + .addCallbacks(continue_subscribe_loop, manage_failure) + + except Exception as ex: + raise ex + + def _stop_subscribe_loop(self): + if self._subscribe_request_task is not None and not self._subscribe_request_task.called: + self._subscribe_request_task.cancel() + + def _stop_heartbeat_timer(self): + if self._heartbeat_call is not None: + self._heartbeat_call.cancel() + + if self._heartbeat_loop is not None: + self._heartbeat_loop.cancel() + + def _register_heartbeat_timer(self): + super(TwistedSubscriptionManager, self)._register_heartbeat_timer() + self._heartbeat_loop = LoopingCall(self._perform_heartbeat_loop) + interval = self._pubnub.config.heartbeat_interval / 2 - 1 + self._heartbeat_loop.start(interval, True) + + def _perform_heartbeat_loop(self): + def heartbeat_callback(_, status): + heartbeat_verbosity = self._pubnub.config.heartbeat_notification_options + if heartbeat_verbosity == PNHeartbeatNotificationOptions.ALL or ( + status.is_error() is True and heartbeat_verbosity == PNHeartbeatNotificationOptions.FAILURES): + self._listener_manager.announce_status(status) + + if self._heartbeat_call is not None: + self._heartbeat_call.cancel() + + state_payload = self._subscription_state.state_payload() + channels = self._subscription_state.prepare_channel_list(False) + channel_groups = self._subscription_state.prepare_channel_group_list(False) + + self._heartbeat_call = Heartbeat(self._pubnub) \ + .channels(channels) \ + .channel_groups(channel_groups) \ + .state(state_payload) \ + .async(heartbeat_callback) + + def _send_leave(self, unsubscribe_operation): + def announce_leave_status(response, status): + self._listener_manager.announce_status(status) + + Leave(self._pubnub) \ + .channels(unsubscribe_operation.channels) \ + .channel_groups(unsubscribe_operation.channel_groups) \ + .async(announce_leave_status) + + def reconnect(self): + # TODO: REVIEW + self._start_subscribe_loop() + self._register_heartbeat_timer() + + +class PubNubTwisted(PubNubCore): + """PubNub Python API for Twisted framework""" + + def sdk_platform(self): + return "-Twisted" + + def __init__(self, config, pool=None, reactor=None, clock=None): + super(PubNubTwisted, self).__init__(config) + + self.clock = clock + self._publish_sequence_manager = PublishSequenceManager(PubNubCore.MAX_SEQUENCE) + + if self.config.enable_subscribe: + self._subscription_manager = TwistedSubscriptionManager(self) + + self.disconnected_times = 0 + + if reactor is None: + self.reactor = _reactor + else: + self.reactor = reactor + + if pool is None: + self.pnconn_pool = HTTPConnectionPool(self.reactor, persistent=True) + self.pnconn_pool.maxPersistentPerHost = 3 + self.pnconn_pool.cachedConnectionTimeout = self.config.subscribe_request_timeout + self.pnconn_pool.retryAutomatically = False + else: + self.pnconn_pool = pool + + self.headers = { + 'User-Agent': [self.sdk_name], + } + + def start(self, skip_reactor=False): + if self._subscription_manager is not None: + self._subscription_manager._start_worker() + if not skip_reactor: + self.reactor.run() + + def stop(self): + self.reactor.stop() + + def add_listener(self, listener): + if self._subscription_manager is not None: + self._subscription_manager.add_listener(listener) + else: + raise Exception("Subscription manager is not enabled for this instance") + + def request_async(self, endpoint_name, endpoint_call_options, callback, cancellation_event): + def async_request(endpoint_call_options, cancellation_event, callback): + def manage_failures(failure): + # Cancelled + if failure.type == ConnectingCancelledError: + return + elif failure.type == PubNubTwistedException: + callback(failure.value) + else: + return failure + + def options_func(): + return endpoint_call_options + + request = self.request_deferred(options_func, cancellation_event) + request.addCallbacks(callback, manage_failures) + + self.reactor.callLater(0, async_request, endpoint_call_options, cancellation_event, callback) + + return + + # REVIEW: cancellation_event doesn't used inside function + def request_deferred(self, options_func, cancellation_event): + options = options_func() + reactor = self.reactor + pnconn_pool = self.pnconn_pool + headers = self.headers + params_to_merge_in = {} + + if options.operation_type == PNOperationType.PNPublishOperation: + params_to_merge_in['seqn'] = self._publish_sequence_manager.get_next_sequence() + + options.merge_params_in(params_to_merge_in) + + create_response = options.create_response + create_status_response = options.create_status + + url = utils.build_url(self.config.scheme(), self.base_origin, + options.path, options.query_string) + + logger.debug("%s %s %s" % (options.method_string, url, options.data)) + + def handler(): + agent = Agent(reactor, pool=pnconn_pool) + + if options.data is not None: + body = FileBodyProducer(StringIO(options.data)) + else: + body = None + request = agent.request( + options.method_string, + url, + Headers(headers), + body) + + def received(response): + finished = Deferred() + response.deliverBody(PubNubResponse(finished, response.code)) + return finished + + def success(response, req_url, request): + parsed_url = urlparse(req_url) + query = parse_qs(parsed_url.query) + uuid = None + auth_key = None + + if 'uuid' in query and len(query['uuid']) > 0: + uuid = query['uuid'][0] + + if 'auth_key' in query and len(query['auth_key']) > 0: + auth_key = query['auth_key'][0] + + response_body = response.body + code = response.code + d = Deferred() + + response_info = ResponseInfo( + status_code=response.code, + tls_enabled='https' == parsed_url.scheme, + origin=parsed_url.netloc, + uuid=uuid, + auth_key=auth_key, + client_request=request + ) + + if code != 200: + if code == 403: + status_category = PNStatusCategory.PNAccessDeniedCategory + elif code == 400: + status_category = PNStatusCategory.PNBadRequestCategory + else: + status_category = self + + if code >= 500: + error = PNERR_SERVER_ERROR + else: + error = PNERR_CLIENT_ERROR + else: + error = None + status_category = PNStatusCategory.PNAcknowledgmentCategory + + try: + data = json.loads(response_body) + except ValueError: + try: + data = json.loads(response_body.decode("utf-8")) + except ValueError: + raise PubNubTwistedException( + result=create_response(None), + status=create_status_response( + status_category, + response_info, + PubNubException( + pn_error=PNERR_JSON_DECODING_FAILED, + errormsg='json decode error' + ) + ) + ) + + if error: + raise PubNubTwistedException( + result=data, + status=create_status_response(status_category, data, response_info, + PubNubException( + errormsg=data, + pn_error=error, + status_code=response.code + ))) + + envelope = TwistedEnvelope( + create_response(data), + create_status_response( + status_category, + response, + response_info, + error), + data + ) + d.callback(envelope) + return d + + def failed(failure): + raise PubNubTwistedException( + result=None, + status=create_status_response(PNStatusCategory.PNTLSConnectionFailedCategory, + None, + None, + PubNubException( + errormsg=str(failure), + pn_error=PNERR_CONNECTION_ERROR, + status_code=0 + ))) + request.addErrback(failed) + request.addCallback(received) + request.addCallback(success, url, request) + + return request + + return handler() + + def disconnected(self): + return self.disconnected_times > 0 + + +class TwistedEnvelope(object): + def __init__(self, result, status, raw_result=None): + self.result = result + self.status = status + self.raw_result = raw_result + + +class TwistedResponse(object): + def __init__(self, body, code): + self.body = body + self.code = code + + +class PubNubTwistedException(Exception): + def __init__(self, result, status): + self.result = result + self.status = status + + def __str__(self): + return str(self.status.error_data.exception) diff --git a/pubnub/request_handlers/__init__.py b/pubnub/request_handlers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pubnub/request_handlers/base.py b/pubnub/request_handlers/base.py new file mode 100644 index 00000000..fb90342e --- /dev/null +++ b/pubnub/request_handlers/base.py @@ -0,0 +1,9 @@ +from abc import abstractmethod, ABCMeta + + +class BaseRequestHandler(object): + __metaclass__ = ABCMeta + + @abstractmethod + def sync_request(self, platform_options, endpoint_call_options): + pass diff --git a/pubnub/request_handlers/requests_handler.py b/pubnub/request_handlers/requests_handler.py new file mode 100644 index 00000000..84d80818 --- /dev/null +++ b/pubnub/request_handlers/requests_handler.py @@ -0,0 +1,268 @@ +import logging +import threading +import requests +import six + +from requests import Session +from requests.adapters import HTTPAdapter + +from pubnub import utils +from pubnub.enums import PNStatusCategory +from pubnub.errors import PNERR_CLIENT_ERROR, PNERR_UNKNOWN_ERROR, PNERR_TOO_MANY_REDIRECTS_ERROR,\ + PNERR_CLIENT_TIMEOUT, PNERR_HTTP_ERROR, PNERR_CONNECTION_ERROR +from pubnub.errors import PNERR_SERVER_ERROR +from pubnub.exceptions import PubNubException +from pubnub.request_handlers.base import BaseRequestHandler +from pubnub.structures import RequestOptions, PlatformOptions, ResponseInfo, Envelope + +logger = logging.getLogger("pubnub") + + +class RequestsRequestHandler(BaseRequestHandler): + """ PubNub Python SDK Native requests handler based on `requests` HTTP library. """ + ENDPOINT_THREAD_COUNTER = 0 + + def __init__(self, pubnub): + self.session = Session() + + self.session.mount('http://ps.pndsn.com', HTTPAdapter(max_retries=1, pool_maxsize=500)) + self.session.mount('https://ps.pndsn.com', HTTPAdapter(max_retries=1, pool_maxsize=500)) + self.session.mount('http://ps.pndsn.com/v2/subscribe', HTTPAdapter(pool_maxsize=500)) + self.session.mount('https://ps.pndsn.com/v2/subscribe', HTTPAdapter(pool_maxsize=500)) + + self.pubnub = pubnub + + def sync_request(self, platform_options, endpoint_call_options): + return self._build_envelope(platform_options, endpoint_call_options) + + def async_request(self, endpoint_name, platform_options, endpoint_call_options, callback, cancellation_event): + call = Call() + + if cancellation_event is None: + cancellation_event = threading.Event() + + def callback_to_invoke_in_another_thread(): + try: + envelope = self._build_envelope(platform_options, endpoint_call_options) + if cancellation_event is not None and cancellation_event.isSet(): + # Since there are no way to affect on ongoing request it's response will + # be just ignored on cancel call + return + + callback(envelope) + except PubNubException as e: + logger.error("Async request PubNubException. %s" % str(e)) + callback(Envelope( + result=None, + status=endpoint_call_options.create_status( + category=PNStatusCategory.PNBadRequestCategory, + response=None, + response_info=None, + exception=e))) + except Exception as e: + logger.error("Async request Exception. %s" % str(e)) + callback(Envelope( + result=None, + status=endpoint_call_options.create_status( + category=PNStatusCategory.PNInternalExceptionCategory, + response=None, + response_info=None, + exception=e))) + finally: + call.executed_cb() + + client = AsyncHTTPClient(callback_to_invoke_in_another_thread) + + thread = threading.Thread( + target=client.run, + name="EndpointThread-%s-%d" % (endpoint_name, ++RequestsRequestHandler.ENDPOINT_THREAD_COUNTER) + ) + thread.setDaemon(False) + thread.start() + + call.thread = thread + call.cancellation_event = cancellation_event + + return call + + def _build_envelope(self, p_options, e_options): + """ A wrapper for _invoke_url to separate request logic """ + + status_category = PNStatusCategory.PNUnknownCategory + response_info = None + + try: + res = self._invoke_request(p_options, e_options, self.pubnub.base_origin) + except PubNubException as e: + if e._pn_error is PNERR_CONNECTION_ERROR: + status_category = PNStatusCategory.PNUnexpectedDisconnectCategory + elif e._pn_error is PNERR_CLIENT_TIMEOUT: + status_category = PNStatusCategory.PNTimeoutCategory + + return Envelope( + result=None, + status=e_options.create_status( + category=status_category, + response=None, + response_info=response_info, + exception=e)) + + if res is not None: + url = six.moves.urllib.parse.urlparse(res.url) + query = six.moves.urllib.parse.parse_qs(url.query) + uuid = None + auth_key = None + + if 'uuid' in query and len(query['uuid']) > 0: + uuid = query['uuid'][0] + + if 'auth_key' in query and len(query['auth_key']) > 0: + auth_key = query['auth_key'][0] + + response_info = ResponseInfo( + status_code=res.status_code, + tls_enabled='https' == url.scheme, + origin=url.hostname, + uuid=uuid, + auth_key=auth_key, + client_request=res.request + ) + + if res.status_code != requests.codes.ok: + if res.status_code == 403: + status_category = PNStatusCategory.PNAccessDeniedCategory + + if res.status_code == 400: + status_category = PNStatusCategory.PNBadRequestCategory + + if res.text is None: + text = "N/A" + else: + text = res.text + + if res.status_code >= 500: + err = PNERR_SERVER_ERROR + else: + err = PNERR_CLIENT_ERROR + + return Envelope( + result=None, + status=e_options.create_status( + category=status_category, + response=res.json(), + response_info=response_info, + exception=PubNubException( + pn_error=err, + errormsg=text, + status_code=res.status_code + ))) + else: + return Envelope( + result=e_options.create_response(res.json()), + status=e_options.create_status( + category=PNStatusCategory.PNAcknowledgmentCategory, + response=res.json(), + response_info=response_info, + exception=None)) + + def _invoke_request(self, p_options, e_options, base_origin): + assert isinstance(p_options, PlatformOptions) + assert isinstance(e_options, RequestOptions) + + url = p_options.pn_config.scheme() + "://" + base_origin + e_options.path + + args = { + "method": e_options.method_string, + 'headers': p_options.headers, + "url": url, + 'params': e_options.query_string, + 'timeout': (e_options.connect_timeout, e_options.request_timeout) + } + + if e_options.is_post(): + args['data'] = e_options.data + logger.debug("%s %s %s" % ( + e_options.method_string, + utils.build_url( + p_options.pn_config.scheme(), + base_origin, + e_options.path, + e_options.query_string), e_options.data)) + else: + logger.debug("%s %s" % ( + e_options.method_string, + utils.build_url( + p_options.pn_config.scheme(), + base_origin, + e_options.path, + e_options.query_string))) + + try: + res = self.session.request(**args) + logger.debug("GOT %s" % res.text) + except requests.exceptions.ConnectionError as e: + raise PubNubException( + pn_error=PNERR_CONNECTION_ERROR, + errormsg=str(e) + ) + except requests.exceptions.HTTPError as e: + raise PubNubException( + pn_error=PNERR_HTTP_ERROR, + errormsg=str(e) + ) + except requests.exceptions.Timeout as e: + raise PubNubException( + pn_error=PNERR_CLIENT_TIMEOUT, + errormsg=str(e) + ) + except requests.exceptions.TooManyRedirects as e: + raise PubNubException( + pn_error=PNERR_TOO_MANY_REDIRECTS_ERROR, + errormsg=str(e) + ) + except Exception as e: + raise PubNubException( + pn_error=PNERR_UNKNOWN_ERROR, + errormsg=str(e) + ) + + return res + + +class AsyncHTTPClient: + """A wrapper for threaded calls""" + + def __init__(self, callback_to_invoke): + self._callback_to_invoke = callback_to_invoke + + def run(self): + self._callback_to_invoke() + + +class Call(object): + """ + A platform dependent representation of async PubNub method call + """ + + def __init__(self): + self.thread = None + self.cancellation_event = None + self.is_executed = False + self.is_canceled = False + + def cancel(self): + """ + Set Event flag to stop thread on timeout. This will not stop thread immediately, it will stopped + only after ongoing request will be finished + :return: nothing + """ + if self.cancellation_event is not None: + self.cancellation_event.set() + self.is_canceled = True + + def join(self): + if isinstance(self.thread, threading.Thread): + self.thread.join() + + def executed_cb(self): + self.is_executed = True diff --git a/pubnub/request_handlers/urllib2_handler.py b/pubnub/request_handlers/urllib2_handler.py new file mode 100644 index 00000000..a808b950 --- /dev/null +++ b/pubnub/request_handlers/urllib2_handler.py @@ -0,0 +1,259 @@ +import json +import logging +import socket +import threading + +import six +from six.moves import urllib + +from pubnub import utils +from pubnub.enums import PNStatusCategory +from pubnub.errors import PNERR_CLIENT_ERROR, PNERR_UNKNOWN_ERROR, PNERR_CLIENT_TIMEOUT, \ + PNERR_HTTP_ERROR, PNERR_CONNECTION_ERROR +from pubnub.errors import PNERR_SERVER_ERROR +from pubnub.exceptions import PubNubException +from pubnub.request_handlers.base import BaseRequestHandler +from pubnub.structures import RequestOptions, PlatformOptions, ResponseInfo, Envelope + +logger = logging.getLogger("pubnub") + + +class Urllib2RequestHandler(BaseRequestHandler): + """ + PubNub Python SDK Native requests handler based on `urllib2/urllib` native HTTP library. + + Do not use this helper since it's doesnt finished yet. Treat it as an example how to write a custom + handler for PubNub SDK + """ + ENDPOINT_THREAD_COUNTER = 0 + + def __init__(self, pubnub): + self.pubnub = pubnub + + def sync_request(self, platform_options, endpoint_call_options): + return self._build_envelope(platform_options, endpoint_call_options) + + def async_request(self, endpoint_name, platform_options, endpoint_call_options, callback, cancellation_event): + call = Call() + + def callback_to_invoke_in_another_thread(): + try: + envelope = self._build_envelope(platform_options, endpoint_call_options) + if cancellation_event is not None and cancellation_event.isSet(): + # Since there are no way to affect on ongoing request it's response will + # be just ignored on cancel call + return + + callback(envelope) + except PubNubException as e: + logger.error("Async request PubNubException. %s" % str(e)) + callback(Envelope( + result=None, + status=endpoint_call_options.create_status( + category=PNStatusCategory.PNBadRequestCategory, + response=None, + response_info=None, + exception=e))) + except Exception as e: + logger.error("Async request Exception. %s" % str(e)) + callback(Envelope( + result=None, + status=endpoint_call_options.create_status( + category=PNStatusCategory.PNInternalExceptionCategory, + response=None, + response_info=None, + exception=e))) + finally: + call.executed_cb() + + client = AsyncHTTPClient(callback_to_invoke_in_another_thread) + + thread = threading.Thread( + target=client.run, + name="EndpointThread-%s-%d" % (endpoint_name, ++Urllib2RequestHandler.ENDPOINT_THREAD_COUNTER) + ) + thread.setDaemon(True) + thread.start() + + call.thread = thread + call.cancellation_event = cancellation_event + + return call + + def _build_envelope(self, p_options, e_options): + """ A wrapper for _invoke_url to separate request logic """ + + status_category = PNStatusCategory.PNUnknownCategory + response_info = None + + try: + res = self._invoke_request(p_options, e_options, self.pubnub.base_origin) + except PubNubException as e: + if e._pn_error is PNERR_CONNECTION_ERROR: + status_category = PNStatusCategory.PNUnexpectedDisconnectCategory + elif e._pn_error is PNERR_CLIENT_TIMEOUT: + status_category = PNStatusCategory.PNTimeoutCategory + + return Envelope( + result=None, + status=e_options.create_status( + category=status_category, + response=None, + response_info=response_info, + exception=e)) + + if res is not None: + url = six.moves.urllib.parse.urlparse(res.url) + query = six.moves.urllib.parse.parse_qs(url.query) + uuid = None + auth_key = None + + if 'uuid' in query and len(query['uuid']) > 0: + uuid = query['uuid'][0] + + if 'auth_key' in query and len(query['auth_key']) > 0: + auth_key = query['auth_key'][0] + + response_info = ResponseInfo( + status_code=res.code, + tls_enabled='https' == url.scheme, + origin=url.hostname, + uuid=uuid, + auth_key=auth_key, + client_request=None + ) + + decoded_text = res.read().decode('utf-8') + decoded_json = json.loads(decoded_text) + logger.debug("GOT %s" % decoded_text) + + if res.code != 200: + if res.code == 403: + status_category = PNStatusCategory.PNAccessDeniedCategory + + if res.code == 400: + status_category = PNStatusCategory.PNBadRequestCategory + + if decoded_json is None: + text = "N/A" + else: + text = decoded_json + + if res.status_code >= 500: + err = PNERR_SERVER_ERROR + else: + err = PNERR_CLIENT_ERROR + + return Envelope( + result=e_options.create_response(decoded_json), + status=e_options.create_status( + category=status_category, + response=decoded_json, + response_info=response_info, + exception=PubNubException( + pn_error=err, + errormsg=text, + status_code=res.status_code + ))) + else: + + return Envelope( + result=e_options.create_response(decoded_json), + status=e_options.create_status( + category=PNStatusCategory.PNAcknowledgmentCategory, + response=decoded_json, + response_info=response_info, + exception=None)) + + @staticmethod + def _invoke_request(p_options, e_options, base_origin): + assert isinstance(p_options, PlatformOptions) + assert isinstance(e_options, RequestOptions) + + url = utils.build_url(p_options.pn_config.scheme(), base_origin, + e_options.path, e_options.query_string) + + args = { + "method": e_options.method_string, + 'headers': p_options.headers, + "url": url, + 'params': e_options.query_string, + 'timeout': (e_options.connect_timeout, e_options.request_timeout) + } + + if e_options.is_post(): + args['data'] = e_options.data + logger.debug("%s %s %s" % (e_options.method_string, url, e_options.data)) + else: + logger.debug("%s %s" % (e_options.method_string, url)) + + try: + req = urllib.request.Request(url, e_options.data, p_options.headers) + res = urllib.request.urlopen(req) + except urllib.error.URLError as e: + # For Python 2.6 + if isinstance(e.reason, socket.timeout): + raise PubNubException( + pn_error=PNERR_CLIENT_TIMEOUT, + errormsg=str(e) + ) + else: + # TODO: wrap + raise + + except urllib.error.HTTPError as e: + raise PubNubException( + pn_error=PNERR_HTTP_ERROR, + errormsg=str(e) + ) + except socket.timeout as e: + raise PubNubException( + pn_error=PNERR_CLIENT_TIMEOUT, + errormsg=str(e) + ) + except Exception as e: + raise PubNubException( + pn_error=PNERR_UNKNOWN_ERROR, + errormsg=str(e) + ) + + return res + + +class AsyncHTTPClient: + """A wrapper for threaded calls""" + + def __init__(self, callback_to_invoke): + self._callback_to_invoke = callback_to_invoke + + def run(self): + self._callback_to_invoke() + + +class Call(object): + """ + A platform dependent representation of async PubNub method call + """ + + def __init__(self): + self.thread = None + self.cancellation_event = None + self.is_executed = False + self.is_canceled = False + + def cancel(self): + """ + Set Event flag to stop thread on timeout. This will not stop thread immediately, it will stopped + only after ongoing request will be finished + :return: nothing + """ + if self.cancellation_event is not None: + self.cancellation_event.set() + self.is_canceled = True + + def join(self): + if isinstance(self.thread, threading.Thread): + self.thread.join() + + def executed_cb(self): + self.is_executed = True diff --git a/pubnub/structures.py b/pubnub/structures.py new file mode 100644 index 00000000..d0a14a08 --- /dev/null +++ b/pubnub/structures.py @@ -0,0 +1,81 @@ +import six + +from .enums import HttpMethod + + +class RequestOptions(object): + def __init__(self, path, params_callback, method, request_timeout, connect_timeout, create_response, + create_status, create_exception, operation_type, data=None, sort_arguments=False): + assert len(path) > 0 + assert callable(params_callback) + assert isinstance(method, six.integer_types) + assert isinstance(request_timeout, six.integer_types) + assert isinstance(connect_timeout, six.integer_types) + assert method is HttpMethod.GET or method is HttpMethod.POST + + self.params = None + self.path = path + self.params_callback = params_callback + self._method = method + self.request_timeout = request_timeout + self.connect_timeout = connect_timeout + # TODO: rename 'data' => 'body' + self.data = data + self.body = data + self.sort_params = sort_arguments + + self.create_response = create_response + self.create_status = create_status + self.create_exception = create_exception + self.operation_type = operation_type + + def merge_params_in(self, params_to_merge_in): + self.params = self.params_callback(params_to_merge_in) + + @property + def method_string(self): + return HttpMethod.string(self._method) + + def is_post(self): + return self._method is HttpMethod.POST + + def query_list(self): + """ All query keys and values should be already encoded inside a build_params() method""" + s = [] + + for k, v in self.params.items(): + s.append(str(k) + "=" + str(v)) + + if self.sort_params: + return sorted(s) + else: + return s + + @property + def query_string(self): + return str('&'.join(self.query_list())) + + def __str__(self): + return "path: {0}, qs: {1}".format(self.path, self.query_string) + + +class PlatformOptions(object): + def __init__(self, headers, pn_config): + self.headers = headers + self.pn_config = pn_config + + +class ResponseInfo(object): + def __init__(self, status_code, tls_enabled, origin, uuid, auth_key, client_request): + self.status_code = status_code + self.tls_enabled = tls_enabled + self.origin = origin + self.uuid = uuid + self.auth_key = auth_key + self.client_request = client_request + + +class Envelope(object): + def __init__(self, result, status): + self.result = result + self.status = status diff --git a/pubnub/utils.py b/pubnub/utils.py new file mode 100644 index 00000000..d098e37d --- /dev/null +++ b/pubnub/utils.py @@ -0,0 +1,175 @@ +import datetime +import hmac +import json +import uuid as u +import threading + +try: + from hashlib import sha256 + + digestmod = sha256 +except ImportError: + import Crypto.Hash.SHA256 as digestmod + + sha256 = digestmod.new + +import six + +from .enums import PNStatusCategory, PNOperationType, PNPushType +from .models.consumer.common import PNStatus +from .errors import PNERR_JSON_NOT_SERIALIZABLE +from .exceptions import PubNubException + + +def get_data_for_user(data): + try: + if 'message' in data and 'payload' in data: + return {'message': data['message'], 'payload': data['payload']} + else: + return data + except TypeError: + return data + + +def write_value_as_string(data): + try: + if isinstance(data, six.string_types): + return "\"%s\"" % data + else: + return json.dumps(data) + except TypeError: + raise PubNubException( + pn_error=PNERR_JSON_NOT_SERIALIZABLE + ) + + +def url_encode(data): + return six.moves.urllib.parse.quote(data, safe="").replace("+", "%2B") + + +def url_write(data): + """ Just wraps url_encode(write_value_as_string()) """ + return url_encode(write_value_as_string(data)) + + +def uuid(): + return str(u.uuid4()) + + +def split_items(items_string): + if len(items_string) is 0: + return [] + else: + return items_string.split(",") + + +def join_items(items_list): + return ",".join(items_list) + + +def join_items_and_encode(items_list): + return ",".join(url_encode(x) for x in items_list) + + +def join_channels(items_list): + if len(items_list) == 0: + return "," + else: + return join_items_and_encode(items_list) + + +def extend_list(existing_items, new_items): + if isinstance(new_items, six.string_types): + existing_items.extend(split_items(new_items)) + else: + existing_items.extend(new_items) + + +def build_url(scheme, origin, path, params={}): + return six.moves.urllib.parse.urlunsplit((scheme, origin, path, params, '')) + + +def synchronized(func): + func.__lock__ = threading.Lock() + + def synced_func(*args, **kws): + with func.__lock__: + return func(*args, **kws) + + return synced_func + + +def is_subscribed_event(status): + assert isinstance(status, PNStatus) + return status.category == PNStatusCategory.PNConnectedCategory + + +def is_unsubscribed_event(status): + assert isinstance(status, PNStatus) + return status.category == PNStatusCategory.PNAcknowledgmentCategory \ + and status.operation == PNOperationType.PNUnsubscribeOperation + + +def prepare_pam_arguments(unsorted_params): + sorted_keys = sorted(unsorted_params) + stringified_arguments = "" + i = 0 + + for key in sorted_keys: + if i != 0: + stringified_arguments += "&" + + stringified_arguments += (key + "=" + pam_encode(str(unsorted_params[key]))) + i += 1 + + return stringified_arguments + + +def pam_encode(s_url): + # !'()*~ + encoded = url_encode(s_url) + if encoded is not None: + encoded = (encoded.replace("*", "%2A") + .replace("!", "%21") + .replace("'", "%27") + .replace("(", "%28") + .replace(")", "%29") + .replace("[", "%5B") + .replace("]", "%5D") + .replace("~", "%7E")) + + return encoded + + +def sign_sha256(secret, sign_input): + from base64 import urlsafe_b64encode + + sign = urlsafe_b64encode(hmac.new( + secret.encode("utf-8"), + sign_input.encode("utf-8"), + sha256 + ).digest()) + + return sign.decode("utf-8") + + +def push_type_to_string(push_type): + if push_type == PNPushType.APNS: + return "apns" + elif push_type == PNPushType.GCM: + return "gcm" + elif push_type == PNPushType.MPNS: + return "mpns" + else: + return "" + + +def strip_right(text, suffix): + if not text.endswith(suffix): + return text + + return text[:len(text) - len(suffix)] + + +def datetime_now(): + return datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y") diff --git a/pubnub/workers.py b/pubnub/workers.py new file mode 100644 index 00000000..9b5f0058 --- /dev/null +++ b/pubnub/workers.py @@ -0,0 +1,84 @@ +import logging + +from abc import abstractmethod +from .utils import strip_right +from .models.consumer.pubsub import PNPresenceEventResult, PNMessageResult +from .models.server.subscribe import SubscribeMessage, PresenceEnvelope + +logger = logging.getLogger("pubnub") + + +class SubscribeMessageWorker(object): + def __init__(self, pubnub_instance, listener_manager_instance, queue_instance, event): + # assert isinstance(pubnub_instnace, PubNubCore) + # assert isinstance(listener_manager_instance, ListenerManager) + # assert isinstance(queue_instance, utils.Queue) + + self._pubnub = pubnub_instance + self._listener_manager = listener_manager_instance + self._queue = queue_instance + self._is_running = None + self._event = event + + def run(self): + self._take_message() + + @abstractmethod + def _take_message(self): + pass + + def _process_message(self, message_input): + if self._pubnub.config.cipher_key is None: + return message_input + else: + return self._pubnub.config.crypto.decrypt(self._pubnub.config.cipher_key, message_input) + + def _process_incoming_payload(self, message): + assert isinstance(message, SubscribeMessage) + + channel = message.channel + subscription_match = message.subscription_match + publish_meta_data = message.publish_metadata + + if channel is not None and channel == subscription_match: + subscription_match = None + + if "-pnpres" in message.channel: + presence_payload = PresenceEnvelope.from_json_payload(message.payload) + + stripped_presence_channel = None + stripped_presence_subscription = None + + if channel is not None: + stripped_presence_channel = strip_right(channel, "-pnpres") + + if subscription_match is not None: + stripped_presence_subscription = strip_right(subscription_match, "-pnpres") + + pn_presence_event_result = PNPresenceEventResult( + event=presence_payload.action, + channel=stripped_presence_channel, + subscription=stripped_presence_subscription, + timetoken=publish_meta_data.publish_timetoken, + occupancy=presence_payload.occupancy, + uuid=presence_payload.uuid, + timestamp=presence_payload.timestamp, + state=presence_payload.data + ) + self._listener_manager.announce_presence(pn_presence_event_result) + else: + extracted_message = self._process_message(message.payload) + publisher = message.issuing_client_id + + if extracted_message is None: + logger.debug("unable to parse payload on #processIncomingMessages") + + pn_message_result = PNMessageResult( + message=extracted_message, + channel=channel, + subscription=subscription_match, + timetoken=publish_meta_data.publish_timetoken, + publisher=publisher + ) + + self._listener_manager.announce_message(pn_message_result) diff --git a/python-tornado/LICENSE b/python-tornado/LICENSE deleted file mode 100644 index 3efa3922..00000000 --- a/python-tornado/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -PubNub Real-time Cloud-Hosted Push API and Push Notification Client Frameworks -Copyright (c) 2013 PubNub Inc. -http://www.pubnub.com/ -http://www.pubnub.com/terms - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -PubNub Real-time Cloud-Hosted Push API and Push Notification Client Frameworks -Copyright (c) 2013 PubNub Inc. -http://www.pubnub.com/ -http://www.pubnub.com/terms diff --git a/python-tornado/README.md b/python-tornado/README.md deleted file mode 100644 index eaa2fe02..00000000 --- a/python-tornado/README.md +++ /dev/null @@ -1,137 +0,0 @@ -## Contact support@pubnub.com for all questions - -#### [PubNub](http://www.pubnub.com) Real-time Data Network -##### Tornado Client - -## IO Event Loop -Be sure to eventually start the event loop or PubNub won't run! - -``` -pubnub.start() -``` - -#### Import -``` -from pubnub import PubnubTornado as Pubnub -``` - -#### Init -``` -pubnub = Pubnub(publish_key="demo", subscribe_key="demo", ssl_on=False) -``` - -#### Publish Example -``` -channel = 'hello_world' -message = 'Hello World !!!' - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.publish(channel, message, callback=callback, error=callback) -``` - -#### Subscribe Example -``` -channel = 'hello_world' - -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect(message): - print("CONNECTED") - - -def reconnect(message): - print("RECONNECTED") - - -def disconnect(message): - print("DISCONNECTED") - - -pubnub.subscribe(channel, callback=callback, error=callback, - connect=connect, reconnect=reconnect, disconnect=disconnect) -``` - -#### History Example -``` -def callback(message): - print(message) - -pubnub.history(channel, count=2, callback=callback, error=callback) -``` - -#### Here Now Example -``` -def callback(message): - print(message) - -pubnub.here_now(channel, callback=callback, error=callback) -``` - -#### Presence Example -``` -channel = 'hello_world' - -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - -pubnub.presence(channel, callback=callback, error=callback) -``` - -#### Unsubscribe Example -``` -pubnub.unsubscribe(channel='hello_world') -``` - -#### Grant Example -``` -authkey = "abcd" - -def callback(message): - print(message) - -pubnub.grant(channel, authkey, True, True, callback=callback, error=callback) - -``` - -#### Audit Example -``` -authkey = "abcd" - -def callback(message): - print(message) - -pubnub.audit(channel, authkey, callback=callback, error=callback) -``` - -#### Revoke Example -``` -authkey = "abcd" - -def callback(message): - print(message) - -pubnub.revoke(channel, authkey, callback=callback, error=callback) -``` - - -#### IO Event Loop start -``` -pubnub.start() -``` - -## Contact support@pubnub.com for all questions diff --git a/python-tornado/examples/audit.py b/python-tornado/examples/audit.py deleted file mode 100755 index 2e7fa5b2..00000000 --- a/python-tornado/examples/audit.py +++ /dev/null @@ -1,35 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTornado as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'pam' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'pam' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'pam' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' -authkey = "abcd" - - -def callback(message): - print(message) - pubnub.stop() - - -pubnub.audit(channel, auth_key=authkey, callback=callback, error=callback) - -pubnub.start() diff --git a/python-tornado/examples/grant.py b/python-tornado/examples/grant.py deleted file mode 100755 index 52c7acd4..00000000 --- a/python-tornado/examples/grant.py +++ /dev/null @@ -1,35 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTornado as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'pam' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'pam' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'pam' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' -authkey = "abcd" - - -def callback(message): - print(message) - pubnub.stop() - - -pubnub.grant(channel, authkey, True, True, callback=callback, error=callback) - -pubnub.start() diff --git a/python-tornado/examples/here-now.py b/python-tornado/examples/here-now.py deleted file mode 100755 index 9d3e0288..00000000 --- a/python-tornado/examples/here-now.py +++ /dev/null @@ -1,36 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTornado as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' - - -# Asynchronous usage - -def callback(message): - print(message) - pubnub.stop() - - -pubnub.here_now(channel, callback=callback, error=callback) - -pubnub.start() diff --git a/python-tornado/examples/history.py b/python-tornado/examples/history.py deleted file mode 100755 index 3c7bbe4f..00000000 --- a/python-tornado/examples/history.py +++ /dev/null @@ -1,37 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTornado as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'a' - - -# Asynchronous usage - - -def callback(message): - print(message) - pubnub.stop() - - -pubnub.history(channel, count=2, callback=callback, error=callback) - -pubnub.start() diff --git a/python-tornado/examples/presence_group.py b/python-tornado/examples/presence_group.py deleted file mode 100755 index 2384b500..00000000 --- a/python-tornado/examples/presence_group.py +++ /dev/null @@ -1,67 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTornado as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or 'abcd' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) - -channel = 'ab' - - -# Asynchronous usage - -def callback_abc(message, channel, real_channel): - print(str(message) + ' , ' + channel + ', ' + real_channel) - # pubnub.unsubscribe_group(channel_group='abc') - # pubnub.stop() - - -def callback_d(message, channel): - print(str(message) + ' , ' + channel) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect_abc(message): - print("CONNECTED " + str(message)) - - -def connect_d(message): - print("CONNECTED " + str(message)) - pubnub.unsubscribe(channel='d') - - -def reconnect(message): - print("RECONNECTED " + str(message)) - - -def disconnect(message): - print("DISCONNECTED " + str(message)) - - -print(pubnub.channel_group_add_channel(channel_group='abc', channel="bn")) - -pubnub.presence_group(channel_group='abc', callback=callback_abc, error=error) - -pubnub.presence(channel='d', callback=callback_d, error=error) - -pubnub.start() diff --git a/python-tornado/examples/publish.py b/python-tornado/examples/publish.py deleted file mode 100755 index 5cc87575..00000000 --- a/python-tornado/examples/publish.py +++ /dev/null @@ -1,36 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTornado as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' -message = 'Hello World !!!' - - -# Asynchronous usage -def callback(message): - print(message) - pubnub.stop() - - -pubnub.publish(channel, message, callback=callback, error=callback) - -pubnub.start() diff --git a/python-tornado/examples/revoke.py b/python-tornado/examples/revoke.py deleted file mode 100755 index 1231782b..00000000 --- a/python-tornado/examples/revoke.py +++ /dev/null @@ -1,35 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTornado as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'pam' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'pam' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'pam' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' -authkey = "abcd" - - -def callback(message): - print(message) - pubnub.stop() - - -pubnub.revoke(channel, authkey, callback=callback, error=callback) - -pubnub.start() diff --git a/python-tornado/examples/subscribe.py b/python-tornado/examples/subscribe.py deleted file mode 100755 index 597db110..00000000 --- a/python-tornado/examples/subscribe.py +++ /dev/null @@ -1,52 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTornado as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) - -channel = 'a' - - -# Asynchronous usage -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect(message): - print("CONNECTED") - - -def reconnect(message): - print("RECONNECTED") - - -def disconnect(message): - print("DISCONNECTED") - - -pubnub.subscribe(channel, callback=callback, error=callback, - connect=connect, reconnect=reconnect, disconnect=disconnect) - -pubnub.start() diff --git a/python-tornado/examples/subscribe_group.py b/python-tornado/examples/subscribe_group.py deleted file mode 100755 index 4f923535..00000000 --- a/python-tornado/examples/subscribe_group.py +++ /dev/null @@ -1,69 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTornado as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or 'abcd' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) - -channel = 'ab' - - -# Asynchronous usage - -def callback_abc(message, channel, real_channel): - print(str(message) + ' , ' + channel + ', ' + real_channel) - pubnub.unsubscribe_group(channel_group='abc') - pubnub.stop() - - -def callback_d(message, channel): - print(str(message) + ' , ' + channel) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect_abc(message): - print("CONNECTED " + str(message)) - - -def connect_d(message): - print("CONNECTED " + str(message)) - pubnub.unsubscribe(channel='d') - - -def reconnect(message): - print("RECONNECTED " + str(message)) - - -def disconnect(message): - print("DISCONNECTED " + str(message)) - - -print(pubnub.channel_group_add_channel(channel_group='abc', channel="b")) - -pubnub.subscribe_group(channel_groups='abc', callback=callback_abc, error=error, - connect=connect_abc, reconnect=reconnect, disconnect=disconnect) - -pubnub.subscribe(channels='d', callback=callback_d, error=error, - connect=connect_d, reconnect=reconnect, disconnect=disconnect) - -pubnub.start() diff --git a/python-tornado/migration.md b/python-tornado/migration.md deleted file mode 100644 index 6a1abf38..00000000 --- a/python-tornado/migration.md +++ /dev/null @@ -1,205 +0,0 @@ -## Contact support@pubnub.com for all questions - -#### [PubNub](http://www.pubnub.com) Real-time Data Network -##### Tornado Migration - -#### Import - -``` -# Pre 3.5: -from pubnub import Pubnub - -# New in 3.5+ -from pubnub import PubnubTornado as Pubnub - -``` - - -#### Init - -``` - -# Pre 3.5: -pubnub = Pubnub( - "demo", ## PUBLISH_KEY - "demo", ## SUBSCRIBE_KEY - False ## SSL_ON? -) - -# New in 3.5+ -pubnub = Pubnub(publish_key="demo", subscribe_key="demo", ssl_on=False) - -``` - -#### PUBLISH - -``` -channel = 'hello_world' -message = 'Hello World !!!' - -# Pre 3.5: -def callback(messages): - print(messages) - -pubnub.publish( { - 'channel' : channel, - 'message' : message, - 'callback' : callback -}) - -# New in 3.5+ - -def callback(message): - print(message) - -pubnub.publish(channel, message, callback=callback, error=callback) - -``` - - -#### SUBSCRIBE - -``` - -# Listen for Messages - -channel = 'hello_world' - -# Pre 3.5: -def connected() : - print('CONNECTED') - -def message_received(message): - print(message) - -pubnub.subscribe({ - 'channel' : channel, - 'connect' : connected, - 'callback' : message_received -}) - -# New in 3.5+ - -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect(message): - print("CONNECTED") - - -def reconnect(message): - print("RECONNECTED") - - -def disconnect(message): - print("DISCONNECTED") - - -pubnub.subscribe(channel, callback=callback, error=callback, - connect=connect, reconnect=reconnect, disconnect=disconnect) -``` - -#### Unsubscribe -Once subscribed, you can easily, gracefully, unsubscribe: - -``` -# Pre 3.5: -pubnub.unsubscribe({ - 'channel' : 'hello_world' -}) - -# New in 3.5+ - -pubnub.unsubscribe(channel='hello_world') -``` - -#### PRESENCE - -``` - -# Pre 3.5: -# - -# New in 3.5+ - -# Listen for Presence Event Messages - -channel = 'hello_world' - -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - -pubnub.presence(channel, callback=callback, error=callback) -``` - -#### HERE_NOW - -``` - -channel = 'hello_world' - -# Pre 3.5: -def callback(messages): - print(messages) - -pubnub.here_now( { - 'channel' : channel, - 'callback' : callback -}) - - -# New in 3.5+ - -# Get info on who is here right now! - - -def callback(message): - print(message) - -pubnub.here_now(channel, callback=callback, error=callback) -``` - -#### HISTORY - -``` -channel = 'hello_world' - -# Pre 3.5: -def history_complete(messages): - print(messages) - -pubnub.history( { - 'channel' : channel, - 'limit' : 2, - 'callback' : history_complete -}) - - -# New in 3.5+ - -def callback(message): - print(message) - -pubnub.history(channel, count=2, callback=callback, error=callback) -``` - -#### IO Event Loop - -``` - -# Pre 3.5: -tornado.ioloop.IOLoop.instance().start() - -# New in 3.5+ -pubnub.start() -``` -## Contact support@pubnub.com for all questions diff --git a/python-tornado/tests/test_grant_async.py b/python-tornado/tests/test_grant_async.py deleted file mode 100755 index bb6121c2..00000000 --- a/python-tornado/tests/test_grant_async.py +++ /dev/null @@ -1,354 +0,0 @@ -import time - -from pubnub import PubnubTornado as Pubnub - -subkey = "sub-c-9aeec0d4-cdf4-11e5-bcee-0619f8945a4f" -pubkey = "pub-c-b3fdf8fc-4516-4ab2-8836-6fb22ba7870d" -secretkey = "sec-c-ZDQwNTUwMDctZDViYi00MzhlLTg2NTctYjViZDcwNTA5Zjhj" -pubnub = Pubnub(pubkey, subkey) -pubnub_pam = Pubnub(pubkey, subkey, secretkey) - - -# Grant permission read true, write true, on channel ( Async Mode ) -def test_1(): - def _callback(resp, ch=None): - assert resp == { - 'message': 'Success', - 'payload': {'auths': {'abcd': {'r': 1, 'w': 1, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': 'abcd', 'ttl': 1} - } - - def _error(response): - assert False - - pubnub_pam.grant(channel="abcd", auth_key="abcd", read=True, write=True, ttl=1, callback=_callback, error=_error) - - -# Grant permission read false, write false, on channel ( Async Mode ) -def test_2(): - def _callback(resp, ch=None): - assert resp == { - 'message': 'Success', - 'payload': {'auths': {'abcd': {'r': 0, 'w': 0, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': 'abcd', 'ttl': 1} - } - - def _error(response): - print("error") - print(response) - assert False - - pubnub_pam.grant(channel="abcd", auth_key="abcd", read=False, write=False, ttl=1, callback=_callback, error=_error) - - -# Grant permission read True, write false, on channel ( Async Mode ) -def test_3(): - def _callback(resp, ch=None): - assert resp == { - 'message': 'Success', - 'payload': {'auths': {'abcd': {'r': 1, 'w': 0, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': 'abcd', 'ttl': 1} - } - - def _error(response): - assert False - - pubnub_pam.grant(channel="abcd", auth_key="abcd", read=True, write=False, ttl=1, callback=_callback, error=_error) - - -# Grant permission read False, write True, on channel ( Async Mode ) -def test_4(): - def _callback(resp, ch=None): - assert resp == { - 'message': 'Success', - 'payload': {'auths': {'abcd': {'r': 0, 'w': 1, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': 'abcd', 'ttl': 1} - } - - def _error(response): - assert False - - pubnub_pam.grant(channel="abcd", auth_key="abcd", read=False, write=True, ttl=1, callback=_callback, error=_error) - - -# Grant permission read False, write True, on channel ( Async Mode ), TTL 10 -def test_5(): - def _callback(resp, ch=None): - assert resp == { - 'message': 'Success', - 'payload': {'auths': {'abcd': {'r': 0, 'w': 1, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': 'abcd', 'ttl': 10} - } - - def _error(response): - assert False - - pubnub_pam.grant(channel="abcd", auth_key="abcd", read=False, write=True, ttl=10, callback=_callback, error=_error) - - -# Grant permission read False, write True, without channel ( Async Mode ), TTL 10 -def test_6(): - def _callback(resp, ch=None): - assert resp == { - 'message': 'Success', - 'payload': {'r': 0, 'w': 1, 'm': 0, - 'subscribe_key': subkey, - 'level': 'subkey', 'ttl': 10} - } - - def _error(response): - print(response) - assert False - - pubnub_pam.grant(read=False, write=True, ttl=10, callback=_callback, error=_error) - - -# Grant permission read False, write False, without channel ( Async Mode ) -def test_7(): - def _callback(resp, ch=None): - assert resp == { - 'message': 'Success', - 'payload': {'r': 0, 'w': 0, 'm': 0, - 'subscribe_key': subkey, - 'level': 'subkey', 'ttl': 1} - } - - def _error(response): - assert False - - pubnub_pam.grant(read=False, write=False, callback=_callback, error=_error) - - -# Complete flow , try publish on forbidden channel, grant permission to subkey and try again. ( Sync Mode) - -def test_8(): - channel = "test_8-" + str(time.time()) - message = "Hello World" - auth_key = "auth-" + channel - pubnub.set_auth_key(auth_key) - - def _cb2(resp): - assert resp == { - 'message': 'Success', - 'payload': {'auths': {auth_key: {'r': 1, 'w': 1, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': channel, 'ttl': 10} - } - - def _cb3(resp, ch=None): - assert resp[0] == 1 - - def _err3(resp): - print(resp) - assert False - - time.sleep(7) - pubnub.publish(channel=channel, message=message, callback=_cb3, error=_err3) - - def _err2(): - assert False - - pubnub_pam.grant(channel=channel, read=True, write=True, auth_key=auth_key, ttl=10, callback=_cb2, error=_err2) - - -# Complete flow , try publish on forbidden channel, grant permission to authkey and try again. -# then revoke and try again -def test_9(): - channel = "test_9-" + str(time.time()) - message = "Hello World" - auth_key = "auth-" + channel - pubnub.set_auth_key(auth_key) - - def _cb2(resp): - assert resp == { - 'message': 'Success', - 'payload': {'auths': {auth_key: {'r': 1, 'w': 1, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': channel, 'ttl': 10} - } - - def _cb3(resp, ch=None): - assert resp[0] == 1 - - def _cb4(resp, ch=None): - assert resp == { - 'message': 'Success', - 'payload': {'auths': {auth_key: {'r': 0, 'w': 0, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': channel, 'ttl': 1} - } - - def _cb5(resp, ch=None): - print(resp) - assert False - - def _err5(resp): - assert resp['message'] == 'Forbidden' - assert resp['payload'] == {'channels': [channel]} - - time.sleep(5) - pubnub.publish(channel=channel, message=message, callback=_cb5, error=_err5) - - def _err4(resp): - assert False - - pubnub_pam.revoke(channel=channel, auth_key=auth_key, callback=_cb4, error=_err4) - - def _err3(resp): - assert False - - time.sleep(7) - pubnub.publish(channel=channel, message=message, callback=_cb3, error=_err3) - - def _err2(resp): - assert False - - pubnub_pam.grant(channel=channel, read=True, write=True, auth_key=auth_key, ttl=10, callback=_cb2, error=_err2) - - -# Complete flow , try publish on forbidden channel, grant permission channel level for subkey and try again. -# then revoke and try again -def test_10(): - channel = "test_10-" + str(time.time()) - message = "Hello World" - auth_key = "auth-" + channel - pubnub_pam.set_auth_key(auth_key) - - def _cb2(resp, ch=None): - assert resp == { - 'message': 'Success', - 'payload': {'channels': {channel: {'r': 1, 'w': 1, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'channel', 'ttl': 10} - } - - def _cb3(resp, ch=None): - assert resp[0] == 1 - - def _cb4(resp, ch=None): - assert resp == { - 'message': 'Success', - 'payload': {'channels': {channel: {'r': 0, 'w': 0, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'channel', 'ttl': 1} - } - - def _cb5(resp, ch=None): - assert False - - def _err5(resp): - assert resp['message'] == 'Forbidden' - assert resp['payload'] == {'channels': [channel]} - - time.sleep(5) - pubnub.publish(channel=channel, message=message, callback=_cb5, error=_err5) - - def _err4(resp): - assert False - - pubnub_pam.revoke(channel=channel, callback=_cb4, error=_err4) - - def _err3(resp): - assert False - - time.sleep(5) - pubnub.publish(channel=channel, message=message, callback=_cb3, error=_err3) - - def _err2(resp): - assert False - - pubnub_pam.grant(channel=channel, read=True, write=True, ttl=10, callback=_cb2, error=_err2) - - -# Complete flow , try publish on forbidden channel, grant permission subkey level for subkey and try again. -# then revoke and try again -def test_11(): - channel = "test_11-" + str(time.time()) - message = "Hello World" - auth_key = "auth-" + channel - pubnub.set_auth_key(auth_key) - - def _cb2(resp, ch=None): - assert resp == { - 'message': 'Success', - 'payload': {'r': 1, 'w': 1, 'm': 0, - 'subscribe_key': subkey, - 'level': 'subkey', 'ttl': 10} - } - - def _cb3(resp, ch=None): - assert resp[0] == 1 - - def _cb4(resp, ch=None): - assert resp == { - 'message': 'Success', - 'payload': {'r': 0, 'w': 0, 'm': 0, - 'subscribe_key': subkey, - 'level': 'subkey', 'ttl': 1} - } - - def _cb5(resp, ch=None): - assert False - - def _err5(resp): - assert resp['message'] == 'Forbidden' - assert resp['payload'] == {'channels': [channel]} - - time.sleep(5) - pubnub.publish(channel=channel, message=message, callback=_cb5, error=_err5) - - def _err4(resp): - assert False - - pubnub_pam.revoke(callback=_cb4, error=_err4) - - def _err3(resp): - assert False - - time.sleep(5) - pubnub.publish(channel=channel, message=message, callback=_cb3, error=_err3) - - def _err2(resp): - assert False - - pubnub_pam.grant(read=True, write=True, ttl=10, callback=_cb2, error=_err2) - - -x = 5 - - -def run_test(t): - global x - x += 5 - i = (x / 5) - 1 - - def _print(): - print('Running test ' + str(i)) - - pubnub.timeout(x, _print) - pubnub.timeout(x + 1, t) - - -def stop(): - pubnub.stop() - - -run_test(test_1) -run_test(test_2) -run_test(test_3) -run_test(test_4) -run_test(test_5) -run_test(test_6) -run_test(test_7) -run_test(test_8) -run_test(test_9) -run_test(test_10) -run_test(test_11) -run_test(stop) - -pubnub_pam.start() diff --git a/python-tornado/tests/test_publish_async.py b/python-tornado/tests/test_publish_async.py deleted file mode 100755 index b8ddcdce..00000000 --- a/python-tornado/tests/test_publish_async.py +++ /dev/null @@ -1,333 +0,0 @@ -import time -# from twisted.trial import unittest - -from pubnub import PubnubTornado as Pubnub - -pubkey = "pub-c-37d3c709-c35e-487a-8b33-2314d9b62b28" -subkey = "sub-c-cd0b6288-cdf5-11e5-bcee-0619f8945a4f" -pubnub = Pubnub(pubkey, subkey) -pubnub_enc = Pubnub(pubkey, subkey, cipher_key="enigma") - - -# class PublishTests(unittest.TestCase): -def test_1(): - channel = "test_1-" + str(time.time()) - message = "I am a string" - - def _cb(resp, ch=None): - assert resp == message - pubnub.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive array -def test_2(): - channel = "test_2-" + str(time.time()) - message = [1, 2] - - def _cb(resp, ch=None): - assert resp == message - pubnub.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - print(resp) - assert False - - pubnub.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive json object -def test_3(): - channel = "test_2-" + str(time.time()) - message = {"a": "b"} - - def _cb(resp, ch=None): - assert resp == message - pubnub.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive number -def test_4(): - channel = "test_2-" + str(time.time()) - message = 100 - - def _cb(resp, ch=None): - assert resp == message - pubnub.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive number string -def test_5(): - channel = "test_5-" + str(time.time()) - message = "100" - - def _cb(resp, ch=None): - assert resp == message - pubnub.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive string (Encryption enabled) -def test_6(): - channel = "test_6-" + str(time.time()) - message = "I am a string" - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive array (Encryption enabled) -def test_7(): - channel = "test_7-" + str(time.time()) - message = [1, 2] - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive json object (Encryption enabled) -def test_8(): - channel = "test_8-" + str(time.time()) - message = {"a": "b"} - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive number (Encryption enabled) -def test_9(): - channel = "test_9-" + str(time.time()) - message = 100 - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive number string (Encryption enabled) -def test_10(): - channel = "test_10-" + str(time.time()) - message = "100" - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive object string (Encryption enabled) -def test_11(): - channel = "test_11-" + str(time.time()) - message = '{"a" : "b"}' - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive array string (Encryption enabled) -def test_12(): - channel = "test_12-" + str(time.time()) - message = '[1,2]' - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -x = 5 - - -def run_test(t): - global x - x += 5 - i = (x / 5) - 1 - - def _print(): - print('Running test ' + str(i)) - - pubnub.timeout(x, _print) - pubnub.timeout(x + 1, t) - - -def stop(): - pubnub.stop() - - -run_test(test_1) -run_test(test_2) -run_test(test_3) -run_test(test_4) -run_test(test_5) -run_test(test_6) -run_test(test_7) -run_test(test_8) -run_test(test_9) -run_test(test_10) -run_test(test_11) -run_test(stop) - -pubnub_enc.start() diff --git a/python-twisted/LICENSE b/python-twisted/LICENSE deleted file mode 100644 index 3efa3922..00000000 --- a/python-twisted/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -PubNub Real-time Cloud-Hosted Push API and Push Notification Client Frameworks -Copyright (c) 2013 PubNub Inc. -http://www.pubnub.com/ -http://www.pubnub.com/terms - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -PubNub Real-time Cloud-Hosted Push API and Push Notification Client Frameworks -Copyright (c) 2013 PubNub Inc. -http://www.pubnub.com/ -http://www.pubnub.com/terms diff --git a/python-twisted/README.md b/python-twisted/README.md deleted file mode 100644 index 0677eff3..00000000 --- a/python-twisted/README.md +++ /dev/null @@ -1,137 +0,0 @@ -## Contact support@pubnub.com for all questions - -#### [PubNub](http://www.pubnub.com) Real-time Data Network -##### Twisted Client - -## IO Event Loop -Be sure to eventually start the event loop or PubNub won't run! - -``` -pubnub.start() -``` - -#### Import -``` -from pubnub import PubnubTwisted as Pubnub -``` - -#### Init -``` -pubnub = Pubnub(publish_key="demo", subscribe_key="demo", ssl_on=False) -``` - -#### Publish Example -``` -channel = 'hello_world' -message = 'Hello World !!!' - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.publish(channel, message, callback=callback, error=callback) -``` - -#### Subscribe Example -``` -channel = 'hello_world' - -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect(message): - print("CONNECTED") - - -def reconnect(message): - print("RECONNECTED") - - -def disconnect(message): - print("DISCONNECTED") - - -pubnub.subscribe(channel, callback=callback, error=callback, - connect=connect, reconnect=reconnect, disconnect=disconnect) -``` - -#### History Example -``` -def callback(message): - print(message) - -pubnub.history(channel, count=2, callback=callback, error=callback) -``` - -#### Here Now Example -``` -def callback(message): - print(message) - -pubnub.here_now(channel, callback=callback, error=callback) -``` - -#### Presence Example -``` -channel = 'hello_world' - -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - -pubnub.presence(channel, callback=callback, error=callback) -``` - -#### Unsubscribe Example -``` -pubnub.unsubscribe(channel='hello_world') -``` - -#### Grant Example -``` -authkey = "abcd" - -def callback(message): - print(message) - -pubnub.grant(channel, authkey, True, True, callback=callback, error=callback) - -``` - -#### Audit Example -``` -authkey = "abcd" - -def callback(message): - print(message) - -pubnub.audit(channel, authkey, callback=callback, error=callback) -``` - -#### Revoke Example -``` -authkey = "abcd" - -def callback(message): - print(message) - -pubnub.revoke(channel, authkey, callback=callback, error=callback) -``` - - -#### IO Event Loop start -``` -pubnub.start() -``` - -## Contact support@pubnub.com for all questions diff --git a/python-twisted/examples/audit.py b/python-twisted/examples/audit.py deleted file mode 100755 index 2f2b08a2..00000000 --- a/python-twisted/examples/audit.py +++ /dev/null @@ -1,35 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTwisted as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'pam' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'pam' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'pam' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' -authkey = "abcd" - - -def callback(message): - print(message) - pubnub.stop() - - -pubnub.audit(channel, auth_key=authkey, callback=callback, error=callback) - -pubnub.start() diff --git a/python-twisted/examples/echo-client.py b/python-twisted/examples/echo-client.py deleted file mode 100755 index 510fee18..00000000 --- a/python-twisted/examples/echo-client.py +++ /dev/null @@ -1,99 +0,0 @@ -# www.pubnub.com - PubNub - Data Stream Network -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Import Libs -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -from pubnub import PubnubTwisted as Pubnub - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Configuration -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -publish_key = 'pub-c-f6a20151-db8d-45af-ba42-def0edaa459f' -subscribe_key = 'sub-c-b5ff3208-7f64-11e4-b601-02ee2ddab7fe' -server_channel = 'echo-server' -client_channel = 'echo-channel' - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Create PubNub Instance -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -pubnub = Pubnub( - publish_key=publish_key, - subscribe_key=subscribe_key, - ssl_on=True -) - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Error Log -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def error_log(data): - print(data) - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Access Log -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def access_log(data): - print(data) - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Respond -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def request(): - pubnub.publish( - server_channel, - {'response': client_channel, 'body': "Hello"}, - callback=access_log, - error=error_log - ) - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Request Received -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def onResponse(data, channel): - print("Channel: %s | Req: %s" % (channel, data)) - request() - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Ready to Receive Requests -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def onReady(message): - print("Ready to Receive Requests on '%s'" % server_channel) - request() - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Network Recovered -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def onReconnect(message): - print("RECONNECTED") - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Network Failed -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def onDisconnect(message): - print("DISCONNECTED") - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Start Echo Server -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -pubnub.subscribe( - client_channel, - callback=onResponse, - error=error_log, - connect=onReady, - reconnect=onReconnect, - disconnect=onDisconnect -) - -pubnub.start() diff --git a/python-twisted/examples/echo-server.py b/python-twisted/examples/echo-server.py deleted file mode 100755 index 9485f4d6..00000000 --- a/python-twisted/examples/echo-server.py +++ /dev/null @@ -1,104 +0,0 @@ -# www.pubnub.com - PubNub - Data Stream Network -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Import Libs -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -from pubnub import PubnubTwisted as Pubnub - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Configuration -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -publish_key = 'pub-c-f6a20151-db8d-45af-ba42-def0edaa459f' -subscribe_key = 'sub-c-b5ff3208-7f64-11e4-b601-02ee2ddab7fe' -server_channel = 'echo-server' - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Create PubNub Instance -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -pubnub = Pubnub( - publish_key=publish_key, - subscribe_key=subscribe_key, - ssl_on=True -) - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Error Log -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def error_log(data): - print(data) - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Access Log -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def access_log(data): - print(data) - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Respond -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def respond(channel, body): - pubnub.publish( - channel, - body, - callback=access_log, - error=error_log - ) - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Request Received -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def onRequest(request, channel): - response_channel = request['response'] - response_body = request['body'] - - print("Channel: %s | Req: %s" % (channel, request)) - - respond( - channel=response_channel, - body=response_body - ) - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Ready to Receive Requests -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def onReady(message): - print("Ready to Receive Requests on '%s'" % server_channel) - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Network Recovered -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def onReconnect(message): - print("RECONNECTED") - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Network Failed -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -def onDisconnect(message): - print("DISCONNECTED") - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Start Echo Server -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -pubnub.subscribe( - server_channel, - callback=onRequest, - error=error_log, - connect=onReady, - reconnect=onReconnect, - disconnect=onDisconnect -) - -pubnub.start() diff --git a/python-twisted/examples/grant.py b/python-twisted/examples/grant.py deleted file mode 100755 index f300c35f..00000000 --- a/python-twisted/examples/grant.py +++ /dev/null @@ -1,35 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTwisted as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'pam' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'pam' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'pam' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' -authkey = "abcd" - - -def callback(message): - print(message) - pubnub.stop() - - -pubnub.grant(channel, authkey, True, True, callback=callback, error=callback) - -pubnub.start() diff --git a/python-twisted/examples/here-now.py b/python-twisted/examples/here-now.py deleted file mode 100755 index 76a8ea8a..00000000 --- a/python-twisted/examples/here-now.py +++ /dev/null @@ -1,35 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTwisted as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' - - -# Asynchronous usage -def callback(message): - print(message) - pubnub.stop() - - -pubnub.here_now(channel, callback=callback, error=callback) - -pubnub.start() diff --git a/python-twisted/examples/history.py b/python-twisted/examples/history.py deleted file mode 100755 index 12ce9d39..00000000 --- a/python-twisted/examples/history.py +++ /dev/null @@ -1,35 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTwisted as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'a' - - -# Asynchronous usage -def callback(message): - print(message) - pubnub.stop() - - -pubnub.history(channel, count=2, callback=callback, error=callback) - -pubnub.start() diff --git a/python-twisted/examples/publish.py b/python-twisted/examples/publish.py deleted file mode 100755 index e618a197..00000000 --- a/python-twisted/examples/publish.py +++ /dev/null @@ -1,36 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTwisted as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' -message = 'Hello World !!!' - - -# Asynchronous usage -def callback(message): - print(message) - pubnub.stop() - - -pubnub.publish(channel, message, callback=callback, error=callback) - -pubnub.start() diff --git a/python-twisted/examples/revoke.py b/python-twisted/examples/revoke.py deleted file mode 100755 index e8008566..00000000 --- a/python-twisted/examples/revoke.py +++ /dev/null @@ -1,35 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTwisted as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'pam' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'pam' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'pam' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' -authkey = "abcd" - - -def callback(message): - print(message) - pubnub.stop() - - -pubnub.revoke(channel, authkey, callback=callback, error=callback) - -pubnub.start() diff --git a/python-twisted/examples/subscribe.py b/python-twisted/examples/subscribe.py deleted file mode 100755 index d5b9b77f..00000000 --- a/python-twisted/examples/subscribe.py +++ /dev/null @@ -1,52 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import PubnubTwisted as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) - -channel = 'a' - - -# Asynchronous usage -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect(message): - print("CONNECTED") - - -def reconnect(message): - print("RECONNECTED") - - -def disconnect(message): - print("DISCONNECTED") - - -pubnub.subscribe(channel, callback=callback, error=callback, - connect=connect, reconnect=reconnect, disconnect=disconnect) - -pubnub.start() diff --git a/python-twisted/examples/subscribe_group.py b/python-twisted/examples/subscribe_group.py deleted file mode 100755 index f3909e5f..00000000 --- a/python-twisted/examples/subscribe_group.py +++ /dev/null @@ -1,54 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or 'abcd' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on, daemon=False) - -channel = 'ab' - - -# Asynchronous usage -def callback(message, channel): - print(str(message) + ' , ' + channel) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect(message): - print("CONNECTED " + str(message)) - - -def reconnect(message): - print("RECONNECTED " + str(message)) - - -def disconnect(message): - print("DISCONNECTED " + str(message)) - - -print(pubnub.channel_group_add_channel(channel_group='abc', channel="a")) - -pubnub.subscribe_group(channel_groups='abc', callback=callback, error=callback, - connect=connect, reconnect=reconnect, disconnect=disconnect) - -pubnub.start() diff --git a/python-twisted/migration.md b/python-twisted/migration.md deleted file mode 100644 index 924fa7c3..00000000 --- a/python-twisted/migration.md +++ /dev/null @@ -1,205 +0,0 @@ -## Contact support@pubnub.com for all questions - -#### [PubNub](http://www.pubnub.com) Real-time Data Network -##### Twisted Migration - -#### Import - -``` -# Pre 3.5: -from pubnub import Pubnub - -# New in 3.5+ -from pubnub import PubnubTwisted as Pubnub - -``` - - -#### Init - -``` - -# Pre 3.5: -pubnub = Pubnub( - "demo", ## PUBLISH_KEY - "demo", ## SUBSCRIBE_KEY - False ## SSL_ON? -) - -# New in 3.5+ -pubnub = Pubnub(publish_key="demo", subscribe_key="demo", ssl_on=False) - -``` - -#### PUBLISH - -``` -channel = 'hello_world' -message = 'Hello World !!!' - -# Pre 3.5: -def callback(messages): - print(messages) - -pubnub.publish( { - 'channel' : channel, - 'message' : message, - 'callback' : callback -}) - -# New in 3.5+ - -def callback(message): - print(message) - -pubnub.publish(channel, message, callback=callback, error=callback) - -``` - - -#### SUBSCRIBE - -``` - -# Listen for Messages - -channel = 'hello_world' - -# Pre 3.5: -def connected() : - print('CONNECTED') - -def message_received(message): - print(message) - -pubnub.subscribe({ - 'channel' : channel, - 'connect' : connected, - 'callback' : message_received -}) - -# New in 3.5+ - -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect(message): - print("CONNECTED") - - -def reconnect(message): - print("RECONNECTED") - - -def disconnect(message): - print("DISCONNECTED") - - -pubnub.subscribe(channel, callback=callback, error=callback, - connect=connect, reconnect=reconnect, disconnect=disconnect) -``` - -#### Unsubscribe -Once subscribed, you can easily, gracefully, unsubscribe: - -``` -# Pre 3.5: -pubnub.unsubscribe({ - 'channel' : 'hello_world' -}) - -# New in 3.5+ - -pubnub.unsubscribe(channel='hello_world') -``` - -#### PRESENCE - -``` - -# Pre 3.5: -# - -# New in 3.5+ - -# Listen for Presence Event Messages - -channel = 'hello_world' - -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - -pubnub.presence(channel, callback=callback, error=callback) -``` - -#### HERE_NOW - -``` - -channel = 'hello_world' - -# Pre 3.5: -def callback(messages): - print(messages) - -pubnub.here_now( { - 'channel' : channel, - 'callback' : callback -}) - - -# New in 3.5+ - -# Get info on who is here right now! - - -def callback(message): - print(message) - -pubnub.here_now(channel, callback=callback, error=callback) -``` - -#### HISTORY - -``` -channel = 'hello_world' - -# Pre 3.5: -def history_complete(messages): - print(messages) - -pubnub.history( { - 'channel' : channel, - 'limit' : 2, - 'callback' : history_complete -}) - - -# New in 3.5+ - -def callback(message): - print(message) - -pubnub.history(channel, count=2, callback=callback, error=callback) -``` - -#### IO Event Loop - -``` - -# Pre 3.5: -reactor.run() - -# New in 3.5+ -pubnub.start() -``` -## Contact support@pubnub.com for all questions diff --git a/python-twisted/tests/benchmark.py b/python-twisted/tests/benchmark.py deleted file mode 100755 index a715d666..00000000 --- a/python-twisted/tests/benchmark.py +++ /dev/null @@ -1,90 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - -import datetime -import sys - -from pubnub import PubnubTwisted as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or 'demo' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False -origin = len(sys.argv) > 6 and sys.argv[6] or 'pubsub.pubnub.com' - -# ----------------------------------------------------------------------- -# Initiat Class -# ----------------------------------------------------------------------- -pubnub = Pubnub( - publish_key, - subscribe_key, - secret_key=secret_key, - cipher_key=cipher_key, - ssl_on=ssl_on, - origin=origin -) -crazy = ' ~`!@#$%^&*( 顶顅 Ȓ)+=[]\\{}|;\':",./<>?abcd' - - -# ----------------------------------------------------------------------- -# BENCHMARK -# ----------------------------------------------------------------------- -def success(msg): - print(msg) - - -def connected(msg): - pubnub.publish(crazy, {'Info': 'Connected!'}, error=error, callback=success) - - -def error(err): - print(err) - -trips = {'last': None, 'current': None, 'max': 0, 'avg': 0} - - -def received(message): - current_trip = trips['current'] = str(datetime.datetime.now())[0:19] - last_trip = trips['last'] = str( - datetime.datetime.now() - datetime.timedelta(seconds=1) - )[0:19] - - # New Trip Span (1 Second) - if current_trip not in trips: - trips[current_trip] = 0 - - # Average - if last_trip in trips: - trips['avg'] = (trips['avg'] + trips[last_trip]) / 2 - - # Increment Trip Counter - trips[current_trip] = trips[current_trip] + 1 - - # Update Max - if trips[current_trip] > trips['max']: - trips['max'] = trips[current_trip] - - print(message) - - pubnub.publish(crazy, current_trip + - " Trip: " + - str(trips[current_trip]) + - " MAX: " + - str(trips['max']) + - "/sec " + - " AVG: " + - str(trips['avg']) + - "/sec", error=error) - - -pubnub.subscribe(crazy, received, connect=connected, error=error) - -# ----------------------------------------------------------------------- -# IO Event Loop -# ----------------------------------------------------------------------- -pubnub.start() diff --git a/python-twisted/tests/delivery.py b/python-twisted/tests/delivery.py deleted file mode 100755 index 457c6a93..00000000 --- a/python-twisted/tests/delivery.py +++ /dev/null @@ -1,168 +0,0 @@ -## www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -## PubNub Real-time Push APIs and Notifications Framework -## Copyright (c) 2010 Stephen Blum -## http://www.pubnub.com/ - -## ----------------------------------- -## PubNub 3.1 Real-time Push Cloud API -## ----------------------------------- - -import sys -import datetime -import time - -from pubnub import PubnubTwisted as Pubnub - -## ----------------------------------------------------------------------- -## Configuration -## ----------------------------------------------------------------------- -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or 'demo' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False -origin = len(sys.argv) > 6 and sys.argv[6] or 'pubsub.pubnub.com' -origin = '184.72.9.220' - -## ----------------------------------------------------------------------- -## Analytics -## ----------------------------------------------------------------------- -analytics = { - 'publishes': 0, # Total Send Requests - 'received': 0, # Total Received Messages (Deliveries) - 'queued': 0, # Total Unreceived Queue (UnDeliveries) - 'successful_publishes': 0, # Confirmed Successful Publish Request - 'failed_publishes': 0, # Confirmed UNSuccessful Publish Request - 'failed_deliveries': 0, # (successful_publishes - received) - 'deliverability': 0 # Percentage Delivery -} - -trips = { - 'last': None, - 'current': None, - 'max': 0, - 'avg': 0 -} - -## ----------------------------------------------------------------------- -## Initiat Class -## ----------------------------------------------------------------------- -channel = 'deliverability-' + str(time.time()) -pubnub = Pubnub( - publish_key, - subscribe_key, - secret_key=secret_key, - cipher_key=cipher_key, - ssl_on=ssl_on, - origin=origin -) - -## ----------------------------------------------------------------------- -## BENCHMARK -## ----------------------------------------------------------------------- - - -def publish_sent(info=None): - if info and info[0]: - analytics['successful_publishes'] += 1 - else: - analytics['failed_publishes'] += 1 - - analytics['publishes'] += 1 - analytics['queued'] += 1 - - pubnub.timeout(send, 0.1) - - -def send(): - if analytics['queued'] > 100: - analytics['queued'] -= 10 - return pubnub.timeout(send, 10) - - pubnub.publish({ - 'channel': channel, - 'callback': publish_sent, - 'message': "1234567890" - }) - - -def received(message): - analytics['queued'] -= 1 - analytics['received'] += 1 - current_trip = trips['current'] = str(datetime.datetime.now())[0:19] - last_trip = trips['last'] = str( - datetime.datetime.now() - datetime.timedelta(seconds=1) - )[0:19] - - ## New Trip Span (1 Second) - if current_trip not in trips: - trips[current_trip] = 0 - - ## Average - if last_trip in trips: - trips['avg'] = (trips['avg'] + trips[last_trip]) / 2 - - ## Increment Trip Counter - trips[current_trip] = trips[current_trip] + 1 - - ## Update Max - if trips[current_trip] > trips['max']: - trips['max'] = trips[current_trip] - - -def show_status(): - ## Update Failed Deliveries - analytics['failed_deliveries'] = \ - analytics['successful_publishes'] \ - - analytics['received'] - - ## Update Deliverability - analytics['deliverability'] = ( - float(analytics['received']) / - float(analytics['successful_publishes'] or 1.0) - ) * 100.0 - - ## Print Display - print(( - "max:%(max)03d/sec " + - "avg:%(avg)03d/sec " + - "pubs:%(publishes)05d " + - "received:%(received)05d " + - "spub:%(successful_publishes)05d " + - "fpub:%(failed_publishes)05d " + - "failed:%(failed_deliveries)05d " + - "queued:%(queued)03d " + - "delivery:%(deliverability)03f%% " + - "" - ) % { - 'max': trips['max'], - 'avg': trips['avg'], - 'publishes': analytics['publishes'], - 'received': analytics['received'], - 'successful_publishes': analytics['successful_publishes'], - 'failed_publishes': analytics['failed_publishes'], - 'failed_deliveries': analytics['failed_deliveries'], - 'publishes': analytics['publishes'], - 'deliverability': analytics['deliverability'], - 'queued': analytics['queued'] - }) - pubnub.timeout(show_status, 1) - - -def connected(): - show_status() - pubnub.timeout(send, 1) - -print("Connected: %s\n" % origin) -pubnub.subscribe({ - 'channel': channel, - 'connect': connected, - 'callback': received -}) - -## ----------------------------------------------------------------------- -## IO Event Loop -## ----------------------------------------------------------------------- -pubnub.start() diff --git a/python-twisted/tests/subscribe-test.py b/python-twisted/tests/subscribe-test.py deleted file mode 100755 index fd33ff7a..00000000 --- a/python-twisted/tests/subscribe-test.py +++ /dev/null @@ -1,156 +0,0 @@ -## www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -## PubNub Real-time Push APIs and Notifications Framework -## Copyright (c) 2010 Stephen Blum -## http://www.pubnub.com/ - -## ----------------------------------- -## PubNub 3.1 Real-time Push Cloud API -## ----------------------------------- - -import sys -from pubnub import PubnubTwisted as Pubnub -from functools import partial -from threading import current_thread -import threading -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or None -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -## ----------------------------------------------------------------------- -## Initiate Pubnub State -## ----------------------------------------------------------------------- -#pubnub = Pubnub( publish_key, subscribe_key, secret_key, cipher_key, ssl_on ) -pubnub = Pubnub(publish_key, subscribe_key, secret_key, ssl_on) -crazy = 'hello_world' - -current = -1 - -errors = 0 -received = 0 - -## ----------------------------------------------------------------------- -## Subscribe Example -## ----------------------------------------------------------------------- - - -def message_received(message): - print(message) - - -def check_received(message): - global current - global errors - global received - print(message) - print(current) - if message <= current: - print('ERROR') - #sys.exit() - errors += 1 - else: - received += 1 - print('active thread count : ', threading.activeCount()) - print('errors = ', errors) - print(current_thread().getName(), ' , ', 'received = ', received) - - if received != message: - print('********** MISSED **************** ', message - received) - current = message - - -def connected_test(ch): - print('Connected', ch) - - -def connected(ch): - pass - - -''' -pubnub.subscribe({ - 'channel' : 'abcd1', - 'connect' : connected, - 'callback' : message_received -}) -''' - - -def cb1(): - pubnub.subscribe({ - 'channel': 'efgh1', - 'connect': connected, - 'callback': message_received - }) - - -def cb2(): - pubnub.subscribe({ - 'channel': 'dsm-test', - 'connect': connected_test, - 'callback': check_received - }) - - -def cb3(): - pubnub.unsubscribe({'channel': 'efgh1'}) - - -def cb4(): - pubnub.unsubscribe({'channel': 'abcd1'}) - - -def subscribe(channel): - pubnub.subscribe({ - 'channel': channel, - 'connect': connected, - 'callback': message_received - }) - - -print(threading.activeCount()) - - -pubnub.timeout(15, cb1) - -pubnub.timeout(30, cb2) - - -pubnub.timeout(45, cb3) - -pubnub.timeout(60, cb4) - -#''' -for x in range(1, 1000): - #print x - def y(t): - subscribe('channel-' + str(t)) - - def z(t): - pubnub.unsubscribe({'channel': 'channel-' + str(t)}) - - pubnub.timeout(x + 5, partial(y, x)) - pubnub.timeout(x + 25, partial(z, x)) - x += 10 -#''' - -''' -for x in range(1,1000): - def cb(r): print r , ' : ', threading.activeCount() - def y(t): - pubnub.publish({ - 'message' : t, - 'callback' : cb, - 'channel' : 'dsm-test' - }) - - - pubnub.timeout(x + 1, partial(y,x)) - x += 1 -''' - - -pubnub.start() diff --git a/python-twisted/tests/test_grant_async.py b/python-twisted/tests/test_grant_async.py deleted file mode 100755 index 7fe66cbe..00000000 --- a/python-twisted/tests/test_grant_async.py +++ /dev/null @@ -1,365 +0,0 @@ -import time - -from pubnub import PubnubTwisted as Pubnub - -subkey = "sub-c-9aeec0d4-cdf4-11e5-bcee-0619f8945a4f" -pubkey = "pub-c-b3fdf8fc-4516-4ab2-8836-6fb22ba7870d" -secretkey = "sec-c-ZDQwNTUwMDctZDViYi00MzhlLTg2NTctYjViZDcwNTA5Zjhj" -pubnub = Pubnub(pubkey, subkey) -pubnub_pam = Pubnub(pubkey, subkey, secretkey) - - -# Grant permission read true, write true, on channel ( Async Mode ) -def test_1(): - def _callback(resp, ch=None): - assert resp == { - 'message': u'Success', - 'payload': {u'auths': {u'abcd': {u'r': 1, u'w': 1}}, - u'subscribe_key': subkey, - u'level': u'user', u'channel': u'abcd', u'ttl': 1} - } - - def _error(response): - assert False - - pubnub_pam.grant(channel="abcd", auth_key="abcd", read=True, write=True, ttl=1, callback=_callback, error=_error) - - -# Grant permission read false, write false, on channel ( Async Mode ) -def test_2(): - def _callback(resp, ch=None): - assert resp == { - 'message': u'Success', - 'payload': {u'auths': {u'abcd': {u'r': 0, u'm': 0, u'w': 0}}, - u'subscribe_key': subkey, - u'level': u'user', u'channel': u'abcd', u'ttl': 1} - } - - def _error(response): - print("error") - print(response) - assert False - - pubnub_pam.grant(channel="abcd", auth_key="abcd", read=False, write=False, ttl=1, callback=_callback, error=_error) - - -# Grant permission read True, write false, on channel ( Async Mode ) -def test_3(): - def _callback(resp, ch=None): - assert resp == { - 'message': u'Success', - 'payload': {u'auths': {u'abcd': {u'r': 1, u'm': 0, u'w': 0}}, - u'subscribe_key': subkey, - u'level': u'user', u'channel': u'abcd', u'ttl': 1} - } - - def _error(response): - assert False - - pubnub_pam.grant(channel="abcd", auth_key="abcd", read=True, write=False, ttl=1, callback=_callback, error=_error) - - -# Grant permission read False, write True, on channel ( Async Mode ) -def test_4(): - def _callback(resp, ch=None): - assert resp == { - 'message': u'Success', - 'payload': {u'auths': {u'abcd': {u'r': 0, u'm': 0, u'w': 1}}, - u'subscribe_key': subkey, - u'level': u'user', u'channel': u'abcd', u'ttl': 1} - } - - def _error(response): - assert False - - pubnub_pam.grant(channel="abcd", auth_key="abcd", read=False, write=True, ttl=1, callback=_callback, error=_error) - - -# Grant permission read False, write True, on channel ( Async Mode ), TTL 10 -def test_5(): - def _callback(resp, ch=None): - assert resp == { - 'message': u'Success', - 'payload': {u'auths': {u'abcd': {u'r': 0, u'm': 0, u'w': 1}}, - u'subscribe_key': subkey, - u'level': u'user', u'channel': u'abcd', u'ttl': 10} - } - - def _error(response): - assert False - - pubnub_pam.grant(channel="abcd", auth_key="abcd", read=False, write=True, ttl=10, callback=_callback, error=_error) - - -# Grant permission read False, write True, without channel ( Async Mode ), TTL 10 -def test_6(): - def _callback(resp, ch=None): - assert resp == { - 'message': u'Success', - 'payload': {u'r': 0, u'm': 0, u'w': 1, - u'subscribe_key': subkey, - u'level': u'subkey', u'ttl': 10} - } - - def _error(response): - print(response) - assert False - - pubnub_pam.grant(read=False, write=True, ttl=10, callback=_callback, error=_error) - - -# Grant permission read False, write False, without channel ( Async Mode ) -def test_7(): - def _callback(resp, ch=None): - assert resp == { - 'message': u'Success', - 'payload': {u'r': 0, u'm': 0, u'w': 0, - u'subscribe_key': subkey, - u'level': u'subkey', u'ttl': 1} - } - - def _error(response): - assert False - - pubnub_pam.grant(read=False, write=False, callback=_callback, error=_error) - - -# Complete flow , try publish on forbidden channel, grant permission to subkey and try again. ( Sync Mode) - -def test_8(): - channel = "test_8-" + str(time.time()) - message = "Hello World" - auth_key = "auth-" + channel - pubnub.set_auth_key(auth_key) - - def _cb2(resp, ch=None): - assert resp == { - 'message': u'Success', - 'payload': {u'auths': {auth_key: {u'r': 1, u'm': 0, u'w': 1}}, - u'subscribe_key': subkey, - u'level': u'user', u'channel': channel, u'ttl': 10} - } - - def _cb3(resp, ch=None): - assert resp[0] == 1 - - def _err3(resp): - print(resp) - assert False - - time.sleep(7) - pubnub.publish(channel=channel, message=message, callback=_cb3, error=_err3) - - def _err2(resp): - assert False - - pubnub_pam.grant(channel=channel, read=True, write=True, auth_key=auth_key, ttl=10, callback=_cb2, error=_err2) - - -# Complete flow , try publish on forbidden channel, grant permission to authkey and try again. -# then revoke and try again -def test_9(): - channel = "test_9-" + str(time.time()) - message = "Hello World" - auth_key = "auth-" + channel - pubnub.set_auth_key(auth_key) - - def _cb2(resp, ch=None): - assert resp == { - 'message': u'Success', - 'payload': {u'auths': {auth_key: {u'r': 1, u'm': 0, u'w': 1}}, - u'subscribe_key': subkey, - u'level': u'user', u'channel': channel, u'ttl': 10} - } - - def _cb3(resp, ch=None): - assert resp[0] == 1 - - def _cb4(resp, ch=None): - assert resp == { - 'message': u'Success', - 'payload': {u'auths': {auth_key: {u'r': 0, u'm': 0, u'w': 0}}, - u'subscribe_key': subkey, - u'level': u'user', u'channel': channel, u'ttl': 1} - } - - def _cb5(resp, ch=None): - print(resp) - assert False - - def _err5(resp): - assert resp['message'] == 'Forbidden' - assert resp['payload'] == {u'channels': [channel]} - - time.sleep(5) - pubnub.publish(channel=channel, message=message, callback=_cb5, error=_err5) - - def _err4(resp): - assert False - - pubnub_pam.revoke(channel=channel, auth_key=auth_key, callback=_cb4, error=_err4) - - def _err3(resp): - assert False - - time.sleep(7) - pubnub.publish(channel=channel, message=message, callback=_cb3, error=_err3) - - def _err2(resp): - assert False - - pubnub_pam.grant(channel=channel, read=True, write=True, auth_key=auth_key, ttl=10, callback=_cb2, error=_err2) - - -# Complete flow , try publish on forbidden channel, grant permission channel level for subkey and try again. -# then revoke and try again -def test_10(): - channel = "test_10-" + str(time.time()) - message = "Hello World" - auth_key = "auth-" + channel - pubnub.set_auth_key(auth_key) - - def _cb1(resp, ch=None): - assert False - - def _err1(resp): - assert resp['message'] == 'Forbidden' - assert resp['payload'] == {u'channels': [channel]} - - def _cb2(resp, ch=None): - assert resp == { - 'message': u'Success', - 'payload': {u'channels': {channel: {u'r': 1, u'm': 0, u'w': 1}}, - u'subscribe_key': subkey, - u'level': u'channel', u'ttl': 10} - } - - def _cb3(resp, ch=None): - assert resp[0] == 1 - - def _cb4(resp, ch=None): - assert resp == { - 'message': u'Success', - 'payload': {u'channels': {channel: {u'r': 0, u'm': 0, u'w': 0}}, - u'subscribe_key': subkey, - u'level': u'channel', u'ttl': 1} - } - - def _cb5(resp, ch=None): - print(resp) - assert False - - def _err5(resp): - assert resp['message'] == 'Forbidden' - assert resp['payload'] == {u'channels': [channel]} - - time.sleep(5) - pubnub.publish(channel=channel, message=message, callback=_cb5, error=_err5) - - def _err4(resp): - assert False - - pubnub_pam.revoke(channel=channel, callback=_cb4, error=_err4) - - def _err3(resp): - assert False - - time.sleep(5) - pubnub.publish(channel=channel, message=message, callback=_cb3, error=_err3) - - def _err2(resp): - assert False - - pubnub_pam.grant(channel=channel, read=True, write=True, ttl=10, callback=_cb2, error=_err2) - - pubnub.publish(channel=channel, message=message, callback=_cb1, error=_err1) - - -# Complete flow , try publish on forbidden channel, grant permission subkey level for subkey and try again. -# then revoke and try again -def test_11(): - channel = "test_11-" + str(time.time()) - message = "Hello World" - auth_key = "auth-" + channel - pubnub.set_auth_key(auth_key) - - def _cb2(resp, ch=None): - print(resp) - assert resp == { - 'message': u'Success', - 'payload': {u'r': 1, u'm': 0, u'w': 1, - u'subscribe_key': subkey, - u'level': u'subkey', u'ttl': 10} - } - - def _cb3(resp, ch=None): - assert resp[0] == 1 - - def _cb4(resp, ch=None): - assert resp == { - 'message': u'Success', - 'payload': {u'r': 0, u'm': 0, u'w': 0, - u'subscribe_key': subkey, - u'level': u'subkey', u'ttl': 1} - } - - def _cb5(resp, ch=None): - assert False - - def _err5(resp): - assert resp['message'] == 'Forbidden' - assert resp['payload'] == {u'channels': [channel]} - - time.sleep(5) - pubnub.publish(channel=channel, message=message, callback=_cb5, error=_err5) - - def _err4(resp): - assert False - - pubnub_pam.revoke(callback=_cb4, error=_err4) - - def _err3(resp): - assert False - - time.sleep(5) - pubnub.publish(channel=channel, message=message, callback=_cb3, error=_err3) - - def _err2(resp): - assert False - - pubnub_pam.grant(read=True, write=True, ttl=10, callback=_cb2, error=_err2) - - -x = 5 - - -def run_test(t): - global x - x += 5 - i = (x / 5) - 1 - - def _print(): - print('Running test ' + str(i)) - - pubnub.timeout(x, _print) - pubnub.timeout(x + 1, t) - - -def stop(): - pubnub.stop() - - -run_test(test_1) -run_test(test_2) -run_test(test_3) -run_test(test_4) -run_test(test_5) -run_test(test_6) -run_test(test_7) -run_test(test_8) -run_test(test_9) -run_test(test_10) -run_test(test_11) -run_test(stop) - -pubnub_pam.start() diff --git a/python-twisted/tests/test_publish_async.py b/python-twisted/tests/test_publish_async.py deleted file mode 100755 index a188cfbd..00000000 --- a/python-twisted/tests/test_publish_async.py +++ /dev/null @@ -1,333 +0,0 @@ -import time -# from twisted.trial import unittest - -from pubnub import PubnubTwisted as Pubnub - -pubkey = "pub-c-37d3c709-c35e-487a-8b33-2314d9b62b28" -subkey = "sub-c-cd0b6288-cdf5-11e5-bcee-0619f8945a4f" -pubnub = Pubnub(pubkey, subkey) -pubnub_enc = Pubnub(pubkey, subkey, cipher_key="enigma") - - -# class PublishTests(unittest.TestCase): -def test_1(): - channel = "test_1-" + str(time.time()) - message = "I am a string" - - def _cb(resp, ch=None): - assert resp == message - pubnub.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive array -def test_2(): - channel = "test_2-" + str(time.time()) - message = [1, 2] - - def _cb(resp, ch=None): - assert resp == message - pubnub.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - print(resp) - assert False - - pubnub.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive json object -def test_3(): - channel = "test_2-" + str(time.time()) - message = {"a": "b"} - - def _cb(resp, ch=None): - assert resp == message - pubnub.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive number -def test_4(): - channel = "test_2-" + str(time.time()) - message = 100 - - def _cb(resp, ch=None): - assert resp == message - pubnub.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive number string -def test_5(): - channel = "test_5-" + str(time.time()) - message = "100" - - def _cb(resp, ch=None): - assert resp == message - pubnub.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive string (Encryption enabled) -def test_6(): - channel = "test_6-" + str(time.time()) - message = "I am a string" - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive array (Encryption enabled) -def test_7(): - channel = "test_7-" + str(time.time()) - message = [1, 2] - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive json object (Encryption enabled) -def test_8(): - channel = "test_8-" + str(time.time()) - message = {"a": "b"} - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive number (Encryption enabled) -def test_9(): - channel = "test_9-" + str(time.time()) - message = 100 - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive number string (Encryption enabled) -def test_10(): - channel = "test_10-" + str(time.time()) - message = "100" - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive object string (Encryption enabled) -def test_11(): - channel = "test_11-" + str(time.time()) - message = '{"a" : "b"}' - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -# Publish and receive array string (Encryption enabled) -def test_12(): - channel = "test_12-" + str(time.time()) - message = '[1,2]' - - def _cb(resp, ch=None): - assert resp == message - pubnub_enc.unsubscribe(channel) - - def _connect(resp): - def _cb1(resp, ch=None): - assert resp[0] == 1 - - def _err1(resp): - assert False - - pubnub_enc.publish(channel, message, callback=_cb1, error=_err1) - - def _error(resp): - assert False - - pubnub_enc.subscribe(channel, callback=_cb, connect=_connect, error=_error) - - -x = 5 - - -def run_test(t): - global x - x += 5 - i = (x / 5) - 1 - - def _print(): - print('Running test ' + str(i)) - - pubnub.timeout(x, _print) - pubnub.timeout(x + 1, t) - - -def stop(): - pubnub.stop() - - -run_test(test_1) -run_test(test_2) -run_test(test_3) -run_test(test_4) -run_test(test_5) -run_test(test_6) -run_test(test_7) -run_test(test_8) -run_test(test_9) -run_test(test_10) -run_test(test_11) -run_test(stop) - -pubnub_enc.start() diff --git a/python-twisted/tests/unit-test-full.py b/python-twisted/tests/unit-test-full.py deleted file mode 100644 index 2335ef6e..00000000 --- a/python-twisted/tests/unit-test-full.py +++ /dev/null @@ -1,230 +0,0 @@ -## www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -## PubNub Real-time Push APIs and Notifications Framework -## Copyright (c) 2010 Stephen Blum -## http://www.pubnub.com/ - -## TODO Tests -## -## - wait 20 minutes, send a message, receive and success. -## - -## - -## -## - -## ----------------------------------- -## PubNub 3.1 Real-time Push Cloud API -## ----------------------------------- - -import sys -from pubnub import PubnubTwisted as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or None -cipher_key = len(sys.argv) > 4 and sys.argv[4] or None -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -## ----------------------------------------------------------------------- -## Command Line Options Supplied PubNub -## ----------------------------------------------------------------------- -pubnub_user_supplied_options = Pubnub( - publish_key, # OPTIONAL (supply None to disable) - subscribe_key, # REQUIRED - secret_key, # OPTIONAL (supply None to disable) - cipher_key, # OPTIONAL (supply None to disable) - ssl_on # OPTIONAL (supply None to disable) -) - -## ----------------------------------------------------------------------- -## High Security PubNub -## ----------------------------------------------------------------------- -pubnub_high_security = Pubnub( - ## Publish Key - 'pub-c-a30c030e-9f9c-408d-be89-d70b336ca7a0', - - ## Subscribe Key - 'sub-c-387c90f3-c018-11e1-98c9-a5220e0555fd', - - ## Secret Key - 'sec-c-MTliNDE0NTAtYjY4Ni00MDRkLTllYTItNDhiZGE0N2JlYzBl', - - ## Cipher Key - 'YWxzamRmbVjFaa05HVnGFqZHM3NXRBS73jxmhVMkjiwVVXV1d5UrXR1JLSkZFRr' + - 'WVd4emFtUm1iR0TFpUZvbiBoYXMgYmVlbxWkhNaF3uUi8kM0YkJTEVlZYVFjBYi' + - 'jFkWFIxSkxTa1pGUjd874hjklaTFpUwRVuIFNob3VsZCB5UwRkxUR1J6YVhlQWa' + - 'V1ZkNGVH32mDkdho3pqtRnRVbTFpUjBaeGUgYXNrZWQtZFoKjda40ZWlyYWl1eX' + - 'U4RkNtdmNub2l1dHE2TTA1jd84jkdJTbFJXYkZwWlZtRnKkWVrSRhhWbFpZVmFz' + - 'c2RkZmTFpUpGa1dGSXhTa3hUYTFwR1Vpkm9yIGluZm9ybWFNfdsWQdSiiYXNWVX' + - 'RSblJWYlRGcFVqQmFlRmRyYUU0MFpXbHlZV2wxZVhVNFJrTnR51YjJsMWRIRTJU' + - 'W91ciBpbmZvcm1hdGliBzdWJtaXR0ZWQb3UZSBhIHJlc3BvbnNlLCB3ZWxsIHJl' + - 'VEExWdHVybiB0am0aW9uIb24gYXMgd2UgcG9zc2libHkgY2FuLuhcFe24ldWVns' + - 'dSaTFpU3hVUjFKNllWaFdhRmxZUWpCaQo34gcmVxdWlGFzIHNveqQl83snBfVl3', - - ## 2048bit SSL ON - ENABLED TRUE - True -) - -## ----------------------------------------------------------------------- -## Channel | Message Test Data (UTF-8) -## ----------------------------------------------------------------------- -crazy = ' ~`â¦â§!@#$%^&*(顶顅Ȓ)+=[]\\{}|;\':",./<>?abcd' -many_channels = [str(x) + '-many_channel_test' for x in range(10)] -runthroughs = 0 -planned_tests = 2 -delivery_retries = 0 -max_retries = 10 - -## ----------------------------------------------------------------------- -## Unit Test Function -## ----------------------------------------------------------------------- - - -def test(trial, name): - if trial: - print('PASS: ' + name) - else: - print('- FAIL - ' + name) - - -def test_pubnub(pubnub): - global runthroughs, planned_tests, delivery_retries, max_retries - - ## ----------------------------------------------------------------------- - ## Many Channels - ## ----------------------------------------------------------------------- - def phase2(): - status = { - 'sent': 0, - 'received': 0, - 'connections': 0 - } - - def received(message, chan): - global runthroughs - - test(status['received'] <= status['sent'], 'many sends') - status['received'] += 1 - pubnub.unsubscribe({'channel': chan}) - if status['received'] == len(many_channels): - runthroughs += 1 - if runthroughs == planned_tests: - pubnub.stop() - - def publish_complete(info, chan): - global delivery_retries, max_retries - status['sent'] += 1 - test(info, 'publish complete') - test(info and len(info) > 2, 'publish response') - if not info[0]: - delivery_retries += 1 - if max_retries > delivery_retries: - sendit(chan) - - def sendit(chan): - tchan = chan - pubnub.publish({ - 'channel': chan, - 'message': "Hello World", - 'callback': (lambda msg: publish_complete(msg, tchan)) - }) - - def connected(chan): - status['connections'] += 1 - sendit(chan) - - def delivered(info): - if info and info[0]: - status['sent'] += 1 - - def subscribe(chan): - pubnub.subscribe({ - 'channel': chan, - 'connect': (lambda: connected(chan + '')), - 'callback': (lambda msg: received(msg, chan)) - }) - - ## Subscribe All Channels - for chan in many_channels: - subscribe(chan) - - ## ----------------------------------------------------------------------- - ## Time Example - ## ----------------------------------------------------------------------- - def time_complete(timetoken): - test(timetoken, 'timetoken fetch') - test(isinstance(timetoken, int), 'timetoken int type') - - pubnub.time({'callback': time_complete}) - - ## ----------------------------------------------------------------------- - ## Publish Example - ## ----------------------------------------------------------------------- - def publish_complete(info): - test(info, 'publish complete') - test(info and len(info) > 2, 'publish response') - - pubnub.history({ - 'channel': crazy, - 'limit': 10, - 'callback': history_complete - }) - - ## ----------------------------------------------------------------------- - ## History Example - ## ----------------------------------------------------------------------- - def history_complete(messages): - test(messages and len(messages) > 0, 'history') - test(messages, 'history') - - pubnub.publish({ - 'channel': crazy, - 'message': "Hello World", - 'callback': publish_complete - }) - - ## ----------------------------------------------------------------------- - ## Subscribe Example - ## ----------------------------------------------------------------------- - def message_received(message): - test(message, 'message received') - pubnub.unsubscribe({'channel': crazy}) - - def done(): - pubnub.unsubscribe({'channel': crazy}) - pubnub.publish({ - 'channel': crazy, - 'message': "Hello World", - 'callback': (lambda x: x) - }) - - def dumpster(message): - test(0, 'never see this') - - pubnub.subscribe({ - 'channel': crazy, - 'connect': done, - 'callback': dumpster - }) - - def connected(): - pubnub.publish({ - 'channel': crazy, - 'message': {'Info': 'Connected!'} - }) - - pubnub.subscribe({ - 'channel': crazy, - 'connect': connected, - 'callback': message_received - }) - - phase2() - -## ----------------------------------------------------------------------- -## Run Tests -## ----------------------------------------------------------------------- -test_pubnub(pubnub_user_supplied_options) -test_pubnub(pubnub_high_security) -pubnub_high_security.start() diff --git a/python/LICENSE b/python/LICENSE deleted file mode 100644 index 3efa3922..00000000 --- a/python/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -PubNub Real-time Cloud-Hosted Push API and Push Notification Client Frameworks -Copyright (c) 2013 PubNub Inc. -http://www.pubnub.com/ -http://www.pubnub.com/terms - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -PubNub Real-time Cloud-Hosted Push API and Push Notification Client Frameworks -Copyright (c) 2013 PubNub Inc. -http://www.pubnub.com/ -http://www.pubnub.com/terms diff --git a/python/README.md b/python/README.md deleted file mode 100644 index 2fce1bae..00000000 --- a/python/README.md +++ /dev/null @@ -1,400 +0,0 @@ -## Contact support@pubnub.com for all questions - -#### [PubNub](http://www.pubnub.com) Real-time Data Network -##### Standalone Python Client - -#### Init - -``` -pubnub = Pubnub(publish_key="demo", subscribe_key="demo", ssl_on=False) - -``` - -#### PUBLISH - -``` -channel = 'hello_world' -message = 'Hello World !!!' - -# Synchronous usage -print pubnub.publish(channel=channel, message=message) - -# Asynchronous usage - -def callback(message): - print(message) - -pubnub.publish(channel=channel, message=message, callback=callback, error=callback) - -``` - - -#### SUBSCRIBE - -``` -# Listen for Messages - -channel = 'hello_world' - -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect(message): - print("CONNECTED") - - -def reconnect(message): - print("RECONNECTED") - - -def disconnect(message): - print("DISCONNECTED") - - -pubnub.subscribe(channels=channel, callback=callback, error=error, - connect=connect, reconnect=reconnect, disconnect=disconnect) -``` - -#### SUBSCRIBE to group - -``` -# Listen for Messages - -channel_group = 'group1' - -def callback(message, channel_group, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect(message): - print("CONNECTED") - - -def reconnect(message): - print("RECONNECTED") - - -def disconnect(message): - print("DISCONNECTED") - - -pubnub.subscribe_group(channel_groups=channel_group, callback=callback, error=error, - connect=connect, reconnect=reconnect, disconnect=disconnect) -``` - -#### SUBSCRIBE Synchronous ( compatible with pre-3.5 ) -Runs in tight loop if callback return True. -Loop ends when callback return False -``` -# Listen for Messages - -channel = 'hello_world' - -def callback(message): - print(message) - return True - -pubnub.subscribe_sync(channel=channel, callback=callback) -``` - - -#### UNSUBSCRIBE - -``` -# Listen for Messages - -channel = 'hello_world' - -pubnub.unsubscribe(channel=channel) -``` - - -#### PRESENCE - -``` -# Listen for Presence Event Messages - -channel = 'hello_world' - -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - - -pubnub.presence(channel=channel, callback=callback, error=error) -``` - -#### PRESENCE channel group - -``` -# Listen for Presence Event Messages - -channel_group = 'group1' - -def callback(message, channel_group, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - - -pubnub.presence_group(channel_group=channel_group, callback=callback, error=error) -``` - -#### HERE_NOW - -``` -# Get info on who is here right now! - -channel = 'hello_world' - -# Synchronous usage -print pubnub.here_now(channel=channel) - - -# Asynchronous usage - -def callback(message): - print(message) - -pubnub.here_now(channel=channel, callback=callback, error=callback) -``` - -#### HISTORY - -``` -# Synchronous usage - -print pubnub.history(channel=channel, count=2) - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.history(channel=channel, count=2, callback=callback, error=callback) -``` - -#### HISTORY (including timetokens) - -``` -# Synchronous usage - -print pubnub.history(channel, count=2, include_token=True) - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.history(channel, count=2, include_token=True, callback=callback, error=callback) -``` - - -#### GRANT - -``` -authkey = "abcd" - -# Synchronous usage -print pubnub.grant(channel=channel, auth_key=authkey, read=True, write=True) - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.grant(channel=channel, auth_key=authkey, read=True, write=True, callback=callback, error=callback) -``` - -#### AUDIT - -``` -channel = "hello_world" -authkey = "abcd" - -# Synchronous usage -print pubnub.audit(channel=channel, auth_key=authkey) - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.audit(channel=channel, auth_key=authkey, callback=callback, error=callback) -``` - -#### REVOKE - -``` -channel = "hello_world" -authkey = "abcd" - -# Synchronous usage -print pubnub.revoke(channel=channel, auth_key=authkey) - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.revoke(channel=channel, auth_key=authkey, callback=callback, error=callback) -``` - -### CHANNEL GROUP METHODS - -#### List Groups - -``` -# Synchronous usage - -print pubnub.channel_group_list_groups(namespace='aaa') - -# Asynchronous usage - -def callback(message): - print(message) - -pubnub.channel_group_list_groups(namespace='aaa', callback=callback, error=callback) - -``` - -#### List Channels - -``` -# Synchronous usage - -print pubnub.channel_group_list_channels(channel_group='dev:abcd') - -# Asynchronous usage - -def callback(message): - print(message) - -pubnub.channel_group_list_channels(channel_group='dev:abcd', callback=callback, error=callback) - -``` - -#### Add Channel - -``` -# Synchronous usage - -print pubnub.channel_group_add_channel(channel_group='dev:abcd', channel="hi") - -# Asynchronous usage - -def callback(message): - print(message) - -pubnub.channel_group_add_channel(channel_group='dev:abcd', channel="hi", callback=callback, error=callback) - -``` - - -#### Remove Channel - -``` -# Synchronous usage - -print pubnub.channel_group_remove_channel(channel_group='dev:abcd', channel="hi") - -# Asynchronous usage - -def callback(message): - print(message) - -pubnub.channel_group_remove_channel(channel_group='dev:abcd', channel="hi", callback=callback, error=callback) - -``` - - -#### List Channels - -``` -# Synchronous usage - -print pubnub.channel_group_list_channels(channel_group='dev:abcd') - -# Asynchronous usage - -def callback(message): - print(message) - -pubnub.channel_group_add_channel(channel_group='dev:abcd', callback=callback, error=callback) - -``` - -#### Grant - - -``` -# Synchronous usage - -print pubnub.grant(channel_group='dev:abcd', read=True, write=True, manage=True, auth_key="abcd") - -# Asynchronous usage - -def callback(message): - print(message) - -pubnub.grant(channel_group='dev:abcd', read=True, write=True, manage=True, auth_key="abcd", callback=callback, error=callback) - -``` - -#### Revoke - -``` -# Synchronous usage - -print pubnub.revoke(channel_group='dev:abcd', auth_key="abcd") - -# Asynchronous usage - -def callback(message): - print(message) - -pubnub.revoke(channel_group='dev:abcd', auth_key="abcd", callback=callback, error=callback) - -``` - - -#### Audit - -``` -# Synchronous usage - -print pubnub.audit(channel_group='dev:abcd') - -# Asynchronous usage - -def callback(message): - print(message) - -pubnub.audit(channel_group='dev:abcd', callback=callback, error=callback) - -``` - - - -## Contact support@pubnub.com for all questions diff --git a/python/examples/audit.py b/python/examples/audit.py deleted file mode 100755 index b20e9ce3..00000000 --- a/python/examples/audit.py +++ /dev/null @@ -1,35 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys -from pubnub import Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'pam' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'pam' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'pam' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' -authkey = "abcd" - -# Synchronous usage -print(pubnub.audit(channel, authkey)) - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.audit(channel, authkey, callback=callback, error=callback) diff --git a/python/examples/config b/python/examples/config deleted file mode 100644 index f35134ff..00000000 --- a/python/examples/config +++ /dev/null @@ -1,9 +0,0 @@ -sessionname pubnub-console -screen -t output tail -f ./pubnub-console.log -split -focus down -screen -t console python console.py "set_output_file" -split -v -focus down -screen -t help vi -R ./console-help.txt -focus up diff --git a/python/examples/config_osx b/python/examples/config_osx deleted file mode 100644 index cdb186cb..00000000 --- a/python/examples/config_osx +++ /dev/null @@ -1,8 +0,0 @@ -sessionname pubnub-console -screen -t help vi -R ./console-help.txt -split -focus down -screen -t output tail -f ./pubnub-console.log -split -focus down -screen -t console python console.py "set_output_file" diff --git a/python/examples/console-help.txt b/python/examples/console-help.txt deleted file mode 100644 index abe92c81..00000000 --- a/python/examples/console-help.txt +++ /dev/null @@ -1,37 +0,0 @@ -********************** HELP ****************************** - -TO EXIT : -Ctrl-A \ followed by y ( on linux ) -Ctrl-A Ctrl-\ followed by y ( on mac osx ) - -TO MOVE BETWEEN PANES . Ctrl-A TAB - -********************************************************** - -PUBLISH - -Usage: publish [options] arg - -Options: - -h, --help show this help message and exit - -c CHANNEL, --channel=CHANNEL - Channel on which to publish - -Examples: - (1) publish -c hello_world hi how r u - (2) pub -c hello_world [1,2] - - - -SUBSCRIBE - -Usage: subscribe [options] arg - -Options: - -h, --help show this help message and exit - -c CHANNEL, --channel=CHANNEL - Channel for subscribe - -Examples: - (1) subscribe -c hello_world - (2) sub -c hello_world \ No newline at end of file diff --git a/python/examples/console.py b/python/examples/console.py deleted file mode 100755 index 5b391bfd..00000000 --- a/python/examples/console.py +++ /dev/null @@ -1,719 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import atexit -import json -import os -import pygments -import readline -import sys -import threading -from cmd2 import Cmd, make_option, options -from datetime import datetime -from pygments.formatters import TerminalFormatter -from pygments.lexers import JsonLexer - -from pubnub import Pubnub - -lexer = JsonLexer() -formatter = TerminalFormatter() - - -def highlight(msg): - return pygments.highlight(msg, lexer, formatter) - - -historyPath = os.path.expanduser("~/.pubnub_console_history") - - -def save_history(historyPath=historyPath): - import readline - readline.write_history_file(historyPath) - - -if os.path.exists(historyPath): - readline.read_history_file(historyPath) - -atexit.register(save_history) - -of = sys.stdout - -color = Cmd() - -stop = None - -full_date = False - - -def stop_2(th): - th._Thread__stop() - - -def stop_3(th): - th._stop() - - -def print_console_2(of, message): - print >> of, message - - -def print_console_3(of, message): - of.write(message) - of.write("\n") - - -print_console = None - -if type(sys.version_info) is tuple: - print_console = print_console_2 - stop = stop_2 -else: - if sys.version_info.major == 2: - print_console = print_console_2 - stop = stop_2 - else: - print_console = print_console_3 - stop = stop_3 - - -def get_date(): - if full_date is True: - return color.colorize(datetime.now().strftime( - '%Y-%m-%d %H:%M:%S'), "magenta") - else: - return color.colorize(datetime.now().strftime( - '%H:%M:%S'), "magenta") - - -def print_ok_normal(msg, channel=None): - if msg is None: - return - chstr = "[" + color.colorize(get_date(), "magenta") + "] " - chstr += "[" + color.colorize("Channel : " + channel if channel is not None else "", "cyan") + "] " - try: - print_console(of, (chstr + color.colorize(str(msg), "green"))) - except UnicodeEncodeError: - print_console(of, (msg)) - - of.flush() - - -def print_error_normal(msg, channel=None): - if msg is None: - return - chstr = "[" + color.colorize(get_date(), "magenta") + "] " - chstr += "[" + color.colorize("Channel : " + channel if channel is not None else "", "cyan") + "] " - try: - print_console(of, (chstr + color.colorize(color.colorize( - str(msg), "red"), "bold"))) - except UnicodeEncodeError: - print_console(of, (msg)) - of.flush() - - -def print_ok_pretty(msg, channel=None): - if msg is None: - return - chstr = "[" + color.colorize(get_date(), "magenta") + "] " - chstr += "[" + color.colorize("Channel : " + channel if channel is not None else "", "cyan") + "] " - try: - print_console(of, (chstr + highlight(json.dumps(msg, indent=2)))) - except UnicodeEncodeError: - print_console(of, (msg)) - - of.flush() - - -def print_error_pretty(msg, channel=None): - if msg is None: - return - chstr = "[" + color.colorize(get_date(), "magenta") + "] " - chstr += "[" + color.colorize("Channel : " + channel if channel is not None else "", "cyan") + "] " - try: - print_console(of, (chstr + color.colorize(color.colorize( - "ERROR: ", "red"), "bold") + highlight(json.dumps(msg, indent=2)))) - except UnicodeEncodeError: - print_console(of, (msg)) - of.flush() - - -print_ok = print_ok_pretty -print_error = print_error_pretty - - -class DefaultPubnub(object): - def handlerFunctionClosure(self, name): - def handlerFunction(*args, **kwargs): - print_error("Pubnub not initialized." + - "Use init command to initialize") - - return handlerFunction - - def __getattr__(self, name): - return self.handlerFunctionClosure(name) - - -pubnub = DefaultPubnub() - - -def kill_all_threads(): - for thread in threading.enumerate(): - if thread.isAlive(): - stop(thread) - - -def get_input(message, t=None): - while True: - try: - try: - command = raw_input(message) - except NameError: - command = input(message) - except KeyboardInterrupt: - return None - - command = command.strip() - - if command is None or len(command) == 0: - raise ValueError - - if t is not None and t == bool: - valid = ["True", "true", "1", 1, "y", "Y", "yes", "Yes", "YES"] - if command in valid: - return True - else: - return False - if t is not None: - command = t(command) - else: - command = eval("'" + command + "'") - - return command - except ValueError: - print_error("Invalid input : " + command) - - -def _publish_command_handler(channel, message, async=False): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - - print_ok(pubnub.publish(channel, message, - _callback if async is True else None, - _error if async is True else None)) - - -def _subscribe_command_handler(channel): - def _callback(r, ch): - print_ok(r, ch) - - def _error(r, ch=None): - print_error(r, ch if ch is not None else channel) - - def _disconnect(r): - print_ok("DISCONNECTED", r) - - def _reconnect(r): - print_ok("RECONNECTED", r) - - def _connect(r): - print_ok("CONNECTED", r) - - pubnub.subscribe(channel, _callback, _error, connect=_connect, - disconnect=_disconnect, reconnect=_reconnect) - - -def _unsubscribe_command_handler(channels): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - - unsub_list = [] - current_channel_list = pubnub.get_channel_array() - - for channel in channels: - pubnub.unsubscribe(channel) - if (channel in current_channel_list): - unsub_list.append(channel) - - # pubnub.unsubscribe(channel + '-pnpres') - # if (channel + '-pnpres' in current_channel_list): - # unsub_list.append(channel + ' (Presence)') - - if len(unsub_list) > 0: - print_ok('Unsubscribed from : ' + str(unsub_list)) - else: - print_error('Not subscribed to : ' + str(channels)) - - -def _grant_command_handler(channel, auth_key, read, write, ttl, async=False): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - - print_ok(pubnub.grant(channel, auth_key, - read, write, ttl, - _callback if async is True else None, - _error if async is True else None)) - - -def _revoke_command_handler(channel, auth_key, ttl, async=False): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - - print_ok(pubnub.revoke(channel, auth_key, ttl, - _callback if async is True else None, - _error if async is True else None)) - - -def _audit_command_handler(channel, auth_key, async=False): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - - print_ok(pubnub.audit(channel, auth_key, - _callback if async is True else None, - _error if async is True else None)) - - -def _history_command_handler(channel, count, async=False): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - - print_ok(pubnub.history(channel, count, - _callback if async is True else None, - _error if async is True else None)) - - -def _here_now_command_handler(channel, async=False): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - - print_ok(pubnub.here_now(channel, _callback if async is True else None, - _error if async is True else None)) - - -def get_channel_array(): - channels = pubnub.get_channel_array() - - for channel in channels: - if "-pnpres" in channel: - chname = channel.split("-pnpres")[0] - if chname not in channels: - continue - i = channels.index(chname) - channels[i] = channels[i] + color.colorize("(P)", "blue") - channels.remove(channel) - return channels - - -class DevConsole(Cmd): - def __init__(self): - Cmd.__init__(self) - global pubnub - self.intro = "For Help type ? or help . " + \ - "To quit/exit type exit" + "\n" + \ - "Commands can also be provided on command line while starting console (in quotes) ex. " + \ - "pubnub-console 'init -p demo -s demo'" - self.default_channel = None - self.async = False - pubnub = Pubnub("demo", "demo") - self.channel_truncation = 3 - self.prompt = self.get_prompt() - self.publish_key = "demo" - self.subscribe_key = "demo" - self.origin = "pubsub.pubnub.com" - self.auth_key = None - self.cipher_key = None - self.secret_key = "demo" - self.ssl = False - self.uuid = None - self.disable_pretty = False - - def get_channel_origin(self): - cho = "" - channels = get_channel_array() - sl = self.channel_truncation - if len(channels) > int(sl) and int(sl) > 0: - cho += ",".join(channels[:int(sl)]) + " " + str( - len(channels) - int(sl)) + " more..." - else: - cho += ",".join(channels) - - if len(channels) > 0: - cho = color.colorize(cho, "bold") + "@" - - origin = pubnub.get_origin().split("://")[1] - origin += color.colorize(" (SSL)", "green") if pubnub.get_origin( - ).split("://")[0] == "https" else "" - return " [" + cho + color.colorize(origin, "blue") + "] > " - - def get_prompt(self): - prompt = "[" + get_date() + "]" - - if self.default_channel is not None and len(self.default_channel) > 0: - prompt += " [default channel: " + color.colorize( - self.default_channel, "bold") + "]" - - prompt += self.get_channel_origin() - return prompt - - def precmd(self, line): - self.prompt = self.get_prompt() - return line - - # def emptyline(self): - # self.prompt = self.get_prompt() - - def cmdloop_with_keyboard_interrupt(self): - try: - self.cmdloop() - except KeyboardInterrupt: - pass - sys.stdout.write('\n') - kill_all_threads() - - @options([make_option('-p', '--publish-key', action="store", - default=None, help="Publish Key"), - make_option('-s', '--subscribe-key', action="store", - default=None, help="Subscribe Key"), - make_option('-k', '--secret-key', action="store", - default=None, help="cipher Key"), - make_option('-c', '--cipher-key', action="store", - default=None, help="Secret Key"), - make_option('-a', '--auth-key', action="store", - default=None, help="Auth Key"), - make_option('--ssl-on', dest='ssl', action='store_true', - default=False, help="SSL Enabled ?"), - make_option('-o', '--origin', action="store", - default=None, help="Origin"), - make_option('-u', '--uuid', action="store", - default=None, help="UUID"), - make_option('--disable-pretty-print', dest='disable_pretty', action='store_true', - default=False, help="Disable Pretty Print ?") - ]) - def do_init(self, command, opts): - global pubnub - global print_ok - global print_error - global print_ok_normal - global print_error_normal - global print_error_pretty - global print_ok_pretty - - self.publish_key = opts.publish_key if opts.publish_key is not None else self.publish_key - self.subscribe_key = opts.subscribe_key if opts.subscribe_key is not None else self.subscribe_key - self.secret_key = opts.secret_key if opts.secret_key is not None else self.secret_key - self.cipher_key = opts.cipher_key if opts.cipher_key is not None else self.cipher_key - self.auth_key = opts.auth_key if opts.auth_key is not None else self.auth_key - self.origin = opts.origin if opts.origin is not None else self.origin - self.uuid = opts.uuid if opts.uuid is not None else self.uuid - self.ssl = opts.ssl if opts.ssl is not None else self.ssl - self.disable_pretty = opts.disable_pretty if opts.disable_pretty is not None else self.disable_pretty - - pubnub = Pubnub(self.publish_key, - self.subscribe_key, - self.secret_key, - self.cipher_key, - self.auth_key, - self.ssl, - self.origin, - self.uuid) - self.prompt = self.get_prompt() - - if opts.disable_pretty is True: - print_ok = print_ok_normal - print_error = print_error_normal - else: - print_ok = print_ok_pretty - print_error = print_error_pretty - - def do_set_sync(self, command): - """unset_async - Unset Async mode""" - self.async = False - - def do_set_async(self, command): - """set_async - Set Async mode""" - self.async = True - - @options([make_option('-n', '--count', action="store", - default=3, help="Number of channels on prompt") - ]) - def do_set_channel_truncation(self, command, opts): - """set_channel_truncation - Set Channel Truncation""" - - self.channel_truncation = opts.count - - self.prompt = self.get_prompt() - - def do_unset_channel_truncation(self, command): - """unset_channel_truncation - Unset Channel Truncation""" - self.channel_truncation = 0 - self.prompt = self.get_prompt() - - def do_set_full_date(self, command): - global full_date - """do_set_full_date - Set Full Date""" - full_date = True - self.prompt = self.get_prompt() - - def do_unset_full_date(self, command): - global full_date - """do_unset_full_date - Unset Full Date""" - full_date = False - self.prompt = self.get_prompt() - - @options([make_option('-c', '--channel', - action="store", help="Default Channel") - ]) - def do_set_default_channel(self, command, opts): - - if opts.channel is None: - print_error("Missing channel") - return - self.default_channel = opts.channel - self.prompt = self.get_prompt() - - @options([make_option('-f', '--file', action="store", - default="./pubnub-console.log", help="Output file") - ]) - def do_set_output_file(self, command, opts): - global of - try: - of = open(opts.file, 'w+') - except IOError as e: - print_error("Could not set output file. " + e.reason) - - @options([make_option('-c', '--channel', action="store", - help="Channel for here now data") - ]) - def do_here_now(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - if opts.channel is None: - print_error("Missing channel") - return - - _here_now_command_handler(opts.channel, async=self.async) - - @options([make_option('-c', '--channel', action="store", - help="Channel for history data"), - make_option('-n', '--count', action="store", - default=5, help="Number of messages") - ]) - def do_get_history(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - if opts.channel is None: - print_error("Missing channel") - return - - _history_command_handler(opts.channel, opts.count, async=self.async) - - @options([make_option('-c', '--channel', action="store", - help="Channel on which to publish") - ]) - def do_publish(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - if opts.channel is None: - print_error("Missing channel") - return - - if command is None: - print_error("Missing message") - return - - try: - message = json.loads(str(command)) - except ValueError: - message = str(command) - - _publish_command_handler(opts.channel, message, async=self.async) - - @options([make_option('-c', '--channel', action="store", - help="Channel on which to grant"), - make_option('-a', '--auth-key', dest="auth_key", - action="store", - help="Auth Key"), - make_option('-r', '--read-enabled', dest='read', - action='store_true', - default=False, help="Read ?"), - make_option('-w', '--write-enabled', dest='write', - action='store_true', - default=False, help="Write ?"), - make_option('-t', '--ttl', action="store", - default=5, help="TTL"), - make_option('-p', '--presence', action="store_true", - dest="presence", - default=False, help="Grant on presence channel ?") - ]) - def do_grant(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - - opts.auth_key = pubnub.auth_key \ - if opts.auth_key is None else opts.auth_key - - _grant_command_handler(opts.channel, opts.auth_key, - opts.read, opts.write, - opts.ttl, async=self.async) - - if opts.presence is True: - _grant_command_handler(opts.channel + '-pnpres', opts.auth_key, - opts.read, opts.write, - opts.ttl, async=self.async) - - @options([make_option('-c', '--channel', action="store", - help="Channel on which to revoke"), - make_option('-a', '--auth-key', dest="auth_key", action="store", - help="Auth Key"), - make_option('-t', '--ttl', action="store", - default=5, help="TTL"), - make_option('-p', '--presence', action="store_true", - dest="presence", - default=False, help="Revoke on presence channel ?") - ]) - def do_revoke(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - - opts.auth_key = pubnub.auth_key \ - if opts.auth_key is None else opts.auth_key - - _revoke_command_handler( - opts.channel, opts.auth_key, opts.ttl, async=self.async) - - if opts.presence is True: - _revoke_command_handler( - opts.channel + '-pnpres', opts.auth_key, opts.ttl, async=self.async) - - @options([make_option('-c', '--channel', action="store", - help="Channel on which to revoke"), - make_option('-a', '--auth-key', dest="auth_key", action="store", - help="Auth Key") - ]) - def do_audit(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - - opts.auth_key = pubnub.auth_key \ - if opts.auth_key is None else opts.auth_key - - _audit_command_handler(opts.channel, opts.auth_key, async=self.async) - - @options([make_option('-c', '--channel', action="store", - help="Channel for unsubscribe"), - make_option('-a', '--all', action="store_true", dest="all", - default=False, help="Unsubscribe from all channels"), - make_option('-p', '--presence', action="store_true", - dest="presence", - default=False, help="Unsubscribe from presence events ?") - ]) - def do_unsubscribe(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - - if (opts.all is True): - opts.channel = [] - chs = pubnub.get_channel_array() - for ch in chs: - if '-pnpres' not in ch: - opts.channel.append(ch) - elif opts.presence is True: - opts.channel.append(ch) - - if opts.channel is None: - print_error("Missing channel") - return - - if not isinstance(opts.channel, list): - ch = [] - ch.append(opts.channel) - opts.channel = ch - - channels = [] - if opts.presence is True and opts.all is False: - for c in opts.channel: - if '-pnpres' not in c: - channels.append(c + '-pnpres') - - for c in opts.channel: - channels.append(c) - - _unsubscribe_command_handler(channels) - self.prompt = self.get_prompt() - - @options([make_option('-c', '--channel', action="store", - help="Channel for subscribe"), - make_option('-g', '--get-channel-list', action="store_true", - dest="get", - default=False, help="Get susbcribed channel list"), - make_option('-p', '--presence', action="store_true", - dest="presence", - default=False, help="Presence events ?") - ]) - def do_subscribe(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - if opts is None: - print_error("Missing argument") - return - - if opts.channel is not None: - _subscribe_command_handler(opts.channel) - - if opts.presence is True: - _subscribe_command_handler(opts.channel + '-pnpres') - - if opts.get is True: - print_ok(get_channel_array()) - self.prompt = self.get_prompt() - - def do_exit(self, args): - kill_all_threads() - return -1 - - # def do_EOF(self, args): - # kill_all_threads() - # return self.do_exit(args) - - # def handler(self, signum, frame): - # kill_all_threads() - - -def main(): - app = DevConsole() - app.cmdloop_with_keyboard_interrupt() - - -if __name__ == "__main__": - main() diff --git a/python/examples/cr.py b/python/examples/cr.py deleted file mode 100755 index 0c106935..00000000 --- a/python/examples/cr.py +++ /dev/null @@ -1,52 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys -from pubnub import Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'pam' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'pam' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'pam' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on, auth_key="abcd") -channel = 'hello_world' - - -def callback(message): - print(message) - -print(pubnub.revoke(channel_group='dev:abcd', auth_key="abcd")) -print(pubnub.audit(channel_group="dev:abcd")) -print(pubnub.grant(channel_group='dev:abcd', read=True, write=True, manage=True, auth_key="abcd")) -print(pubnub.channel_group_list_namespaces()) -print(pubnub.channel_group_list_groups(namespace='aaa')) -print(pubnub.channel_group_list_groups(namespace='foo')) -print(pubnub.channel_group_list_channels(channel_group='dev:abcd')) -print(pubnub.channel_group_add_channel(channel_group='dev:abcd', channel="hi")) -print(pubnub.channel_group_list_channels(channel_group='dev:abcd')) -print(pubnub.channel_group_remove_channel(channel_group='dev:abcd', channel="hi")) -print(pubnub.channel_group_list_channels(channel_group='dev:abcd')) - - -pubnub.revoke(channel_group='dev:abcd', auth_key="abcd", callback=callback, error=callback) -pubnub.audit(channel_group="dev:abcd", callback=callback, error=callback) -pubnub.grant(channel_group='dev:abcd', read=True, write=True, manage=True, auth_key="abcd", callback=callback, error=callback) -pubnub.channel_group_list_namespaces(callback=callback, error=callback) -pubnub.channel_group_list_groups(namespace='aaa', callback=callback, error=callback) -pubnub.channel_group_list_groups(namespace='foo', callback=callback, error=callback) -pubnub.channel_group_list_channels(channel_group='dev:abcd', callback=callback, error=callback) -pubnub.channel_group_add_channel(channel_group='dev:abcd', channel="hi", callback=callback, error=callback) -pubnub.channel_group_list_channels(channel_group='dev:abcd', callback=callback, error=callback) -pubnub.channel_group_remove_channel(channel_group='dev:abcd', channel="hi", callback=callback, error=callback) -pubnub.channel_group_list_channels(channel_group='dev:abcd', callback=callback, error=callback) diff --git a/python/examples/dev-console.py b/python/examples/dev-console.py deleted file mode 100755 index 4c4bf5c4..00000000 --- a/python/examples/dev-console.py +++ /dev/null @@ -1,273 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - -from pubnub import Pubnub -from optparse import OptionParser -from datetime import datetime -import threading - -parser = OptionParser() - -parser.add_option("--publish-key", - dest="publish_key", default="demo", - help="Publish Key ( default : 'demo' )") - -parser.add_option("--subscribe-key", - dest="subscribe_key", default="demo", - help="Subscribe Key ( default : 'demo' )") - -parser.add_option("--secret-key", - dest="secret_key", default="demo", - help="Secret Key ( default : 'demo' )") - -parser.add_option("--cipher-key", - dest="cipher_key", default="", - help="Cipher Key") - -parser.add_option("--auth-key", - dest="auth_key", default=None, - help="Auth Key") - -parser.add_option("--origin", - dest="origin", default="pubsub.pubnub.com", - help="Origin ( default: pubsub.pubnub.com )") - -parser.add_option("--ssl-on", - action="store_false", dest="ssl", default=False, - help="SSL") - -parser.add_option("--uuid", - dest="uuid", default=None, - help="UUID") - -(options, args) = parser.parse_args() - -print(options) - -pubnub = Pubnub(options.publish_key, - options.subscribe_key, - options.secret_key, - options.cipher_key, - options.auth_key, - options.ssl, - options.origin, - options.uuid) - - -class color(object): - PURPLE = '\033[95m' - CYAN = '\033[96m' - DARKCYAN = '\033[36m' - BLUE = '\033[94m' - GREEN = '\033[92m' - YELLOW = '\033[93m' - RED = '\033[91m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' - END = '\033[0m' - - -def print_ok(msg, channel=None): - chstr = color.PURPLE + "[" + datetime.now().strftime( - '%Y-%m-%d %H:%M:%S') + "] " + color.END - chstr += color.CYAN + "[Channel : " + channel + \ - "] " if channel is not None else "" + color.END - try: - print(chstr + color.GREEN + str(msg) + color.END) - except UnicodeEncodeError: - print(msg) - - -def print_error(msg, channel=None): - chstr = color.PURPLE + "[" + datetime.now().strftime( - '%Y-%m-%d %H:%M:%S') + "] " + color.END - chstr += color.CYAN + "[Channel : " + channel + \ - "] " if channel is not None else "" + color.END - try: - print(chstr + color.RED + color.BOLD + str(msg) + color.END) - except UnicodeEncodeError: - print(msg) - - -def kill_all_threads(): - for thread in threading.enumerate(): - if thread.isAlive(): - thread._Thread__stop() - - -def get_input(message, t=None): - while True: - try: - try: - command = raw_input(message) - except NameError: - command = input(message) - except KeyboardInterrupt: - return None - - command = command.strip() - - if command is None or len(command) == 0: - raise ValueError - - if t is not None and t == bool: - valid = ["True", "true", "1", 1, "y", "Y", "yes", "Yes", "YES"] - if command in valid: - return True - else: - return False - if t is not None: - command = t(command) - else: - command = eval("'" + command + "'") - - return command - except ValueError: - print_error("Invalid input : " + command) - - -def _publish_command_handler(): - - channel = get_input("[PUBLISH] Enter Channel Name ", str) - if channel is None: - return - while True: - message = get_input("[PUBLISH] Enter Message \ - ( QUIT or CTRL-C for exit from publish mode ) ") - if message == 'QUIT' or message == 'quit' or message is None: - return - - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - pubnub.publish(channel, message, _callback, _error) - - -def _subscribe_command_handler(): - channel = get_input("[SUBSCRIBE] Enter Channel Name ", str) - - def _callback(r): - print_ok(r, channel) - - def _error(r): - print_error(r, channel) - pubnub.subscribe(channel, _callback, _error) - - -def _unsubscribe_command_handler(): - channel = get_input("[UNSUBSCRIBE] Enter Channel Name ", str) - - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - pubnub.unsubscribe(channel) - - -def _grant_command_handler(): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - channel = get_input("[GRANT] Enter Channel Name ", str) - auth_key = get_input("[GRANT] Enter Auth Key ", str) - ttl = get_input("[GRANT] Enter ttl ", int) - read = get_input("[GRANT] Read ? ", bool) - write = get_input("[GRANT] Write ? ", bool) - pubnub.grant(channel, auth_key, read, write, ttl, _callback) - - -def _revoke_command_handler(): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - channel = get_input("[REVOKE] Enter Channel Name ", str) - auth_key = get_input("[REVOKE] Enter Auth Key ", str) - ttl = get_input("[REVOKE] Enter ttl ", int) - - pubnub.revoke(channel, auth_key, ttl, _callback) - - -def _audit_command_handler(): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - channel = get_input("[AUDIT] Enter Channel Name ", str) - auth_key = get_input("[AUDIT] Enter Auth Key ", str) - pubnub.audit(channel, auth_key, _callback) - - -def _history_command_handler(): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - channel = get_input("[HISTORY] Enter Channel Name ", str) - count = get_input("[HISTORY] Enter Count ", int) - - pubnub.history(channel, count, callback=_callback, error=_error) - - -def _here_now_command_handler(): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - channel = get_input("[HERE NOW] Enter Channel Name ", str) - - pubnub.here_now(channel, callback=_callback, error=_error) - - -commands = [] -commands.append({"command": "publish", "handler": _publish_command_handler}) -commands.append( - {"command": "subscribe", "handler": _subscribe_command_handler}) -commands.append( - {"command": "unsubscribe", "handler": _unsubscribe_command_handler}) -commands.append( - {"command": "here_now", "handler": _here_now_command_handler}) -commands.append({"command": "history", "handler": _history_command_handler}) -commands.append({"command": "grant", "handler": _grant_command_handler}) -commands.append({"command": "revoke", "handler": _revoke_command_handler}) -commands.append({"command": "audit", "handler": _audit_command_handler}) - -# last command is quit. add new commands before this line -commands.append({"command": "QUIT"}) - - -def get_help(): - help = "" - help += "Channels currently subscribed to : " - help += str(pubnub.get_channel_array()) - help += "\n" - for i, v in enumerate(commands): - help += "Enter " + str(i) + " for " + v['command'] + "\n" - return help - - -while True: - command = get_input(color.BLUE + get_help(), int) - if command == len(commands) - 1 or command is None: - kill_all_threads() - break - if command >= len(commands): - print_error("Invalid input " + str(command)) - continue - - commands[command]['handler']() - -# pubnub.start() diff --git a/python/examples/grant.py b/python/examples/grant.py deleted file mode 100755 index 0fad10fd..00000000 --- a/python/examples/grant.py +++ /dev/null @@ -1,35 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys -from pubnub import Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'pam' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'pam' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'pam' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' -authkey = "abcd" - -# Synchronous usage -print(pubnub.grant(channel, authkey, True, True)) - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.grant(channel, authkey, True, True, callback=callback, error=callback) diff --git a/python/examples/heartbeat.py b/python/examples/heartbeat.py deleted file mode 100755 index a58646a6..00000000 --- a/python/examples/heartbeat.py +++ /dev/null @@ -1,106 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - -import gevent.monkey -import random -import sys -from datetime import datetime -from pubnub import Pubnub as Pubnub - -gevent.monkey.patch_all() - -# from pubnub import PubnubTornado as Pubnub -# from pubnub import PubnubTwisted as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'ds' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'ds' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'ds' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on, uuid="test-" + str(random.random())) - - -def x(url): - print(url) - - -# pubnub.set_http_debug(x) - -channel = 'ab,cd,ef' -channel_group = "abg,cdg" - - -def set_heartbeat(*argv): - print("Change Heartbeat to ", argv[0]) - pubnub.set_heartbeat(argv[0], argv[1], argv[2]) - - -def set_heartbeat_interval(*argv): - pubnub.set_heartbeat_interval(argv[0]) - - -# pubnub.timeout(0, set_heartbeat, 8) - -# Asynchronous usage -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect(message): - print("CONNECTED") - - -def reconnect(message): - print("RECONNECTED") - - -def disconnect(message): - print("DISCONNECTED") - - -pubnub.channel_group_add_channel("abg", "abcd") -pubnub.channel_group_add_channel("cdg", "efgh") - -pubnub.subscribe(channels=channel, callback=callback, error=callback, - connect=connect, reconnect=reconnect, disconnect=disconnect) - -pubnub.subscribe_group(channel_groups=channel_group, callback=callback, error=callback, - connect=connect, reconnect=reconnect, disconnect=disconnect) - - -def cb(resp): - print(datetime.now().strftime('%H:%M:%S'), resp) - - -def err(resp): - print(datetime.now().strftime('%H:%M:%S'), resp) - - -pubnub.timeout(5, set_heartbeat, 120, cb, err) -pubnub.timeout(90, set_heartbeat, 60, cb, err) -pubnub.timeout(180, set_heartbeat, 30, cb, err) -pubnub.timeout(240, set_heartbeat, 15, cb, err) -pubnub.timeout(300, set_heartbeat, 8, cb, err) -# pubnub.timeout(360, pubnub.stop_heartbeat) - - -''' -import time -while True: - time.sleep(10) -''' - -pubnub.start() diff --git a/python/examples/here-now.py b/python/examples/here-now.py deleted file mode 100755 index 0eb89942..00000000 --- a/python/examples/here-now.py +++ /dev/null @@ -1,35 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys -from pubnub import Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' - - -# Synchronous usage -print(pubnub.here_now(channel)) - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.here_now(channel, callback=callback, error=callback) diff --git a/python/examples/history.py b/python/examples/history.py deleted file mode 100755 index 8691a4b8..00000000 --- a/python/examples/history.py +++ /dev/null @@ -1,47 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys -from pubnub import Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'a' - -# Synchronous usage - -print(pubnub.history(channel, count=2)) - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.history(channel, count=2, callback=callback, error=callback) - -# Synchronous usage - -print(pubnub.history(channel, count=2, include_token=True)) - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.history(channel, count=2, include_token=True, callback=callback, error=callback) diff --git a/python/examples/pam_demo/demo.py b/python/examples/pam_demo/demo.py deleted file mode 100755 index 1c68787a..00000000 --- a/python/examples/pam_demo/demo.py +++ /dev/null @@ -1,126 +0,0 @@ -from gevent.monkey import patch_all -from pubnub import Pubnub -import random -import json - -patch_all() - -rand = str(random.randint(1, 99999999)) - - -def get_unique(s): - return 'str-' + rand + '-' + s - - -# public channel -# This is the channel all clients announce themselves on -- or more generally speaking, a channel you expect the client -# to "check-in" on to announce his state - -# channel_public = get_unique("channel_public") -channel_public = "channel_public" - -# server auth key -# Only the server has/knows about this auth token. It will be used to grant read on the "check-in" Presence channel - -# server_auth_token = get_unique("server_auth_token") -server_auth_token = "server_auth_token" - -# client auth key -# only clients will use this authey -- it does not provide presence channel read access - -# client_auth_token = get_unique("client_auth_token") -client_auth_token = "client_auth_token" - -# each client must have a unique id -- a UUID, for presence information/state to bind to - -# client uuid -client_uuid = get_unique("client_uuid") - -# server uuid -server_uuid = get_unique("server_uuid") - -# For the demo, we'll implement a SERVER called server, who is the authoritative 'admin' entity in the system -# We'll also implement a CLIENT called client, who is an arbitrary hardware device member of the network - -# Please swap out the default 'pam' demo keys with your own PAM-enabled keys - -# init server object -server = Pubnub(publish_key="pam", subscribe_key="pam", secret_key="pam", auth_key=server_auth_token, uuid=server_uuid) - -# init client object -client = Pubnub(publish_key="pam", subscribe_key="pam", auth_key=client_auth_token, uuid=client_uuid) - -# To access a Presence channel with PAM, its format is CHANNELNAME-pnpres - -# Grant permission to server auth keys -# grant r/w to public, and r/w public Presence (public-pnpres) - -print(server.grant(channel=channel_public, auth_key=server_auth_token, read=True, write=True)) -print(server.grant(channel=channel_public + '-pnpres', auth_key=server_auth_token, read=True, write=True)) - -# Grant permission to client auth keys -# grant r/w to public, and w-only access to public Presence (public-pnpres) -print(server.grant(channel=channel_public, auth_key=client_auth_token, read=True, write=False)) -print(server.grant(channel=channel_public + '-pnpres', auth_key=client_auth_token, read=False, write=False)) - - -# Now, we'll run it to watch it work as advertised... - -# Define some simple callabcks for the Server and Client - -def _server_message_callback(message, channel): - print("Server heard: " + json.dumps(message)) - - -def _client_message_callback(message, channel): - print("Client heard: " + json.dumps(message)) - - -def _client_error_callback(error, channel): - print("Client Error: " + error + " on channel " + channel) - print("TTL on grant expired, or token was invalid, or revoked." - " Client will now unsubscribe from this unauthorized channel.") - client.unsubscribe(channel=channel) - - -def _server_error_callback(error, channel): - print("Server Error: " + error + " on channel " + channel) - print("TTL on grant expired, or token was revoked. Server will now unsubscribe from this unauthorized channel.") - server.unsubscribe(channel=channel) - - -def _server_presence_callback(message, channel): - print(message) - if 'action' in message: - if message['action'] == 'join' and message['uuid'] == client_uuid: - print("Server can see that client with UUID " + message['uuid'] + " has a state of " + json.dumps( - message['data'])) - - -def _client_presence_callback(message, channel): - print(message) - if 'action' in message: - if message['action'] == 'join' and message['uuid'] == client_uuid: - print("Client can see that client with UUID " + message['uuid'] + " has a state of " + json.dumps( - message['data'])) - - -# server subscribes to public channel -server.subscribe(channels=channel_public, callback=_server_message_callback, error=_server_error_callback) - -# server subscribes to presence events on public channel -# presence() is a convienence method that subscribes to channel-pnpres with special logic for handling -# presence-event formatted messages - -# uncomment out to see server able to read on presence channel -server.presence(channel=channel_public, callback=_server_presence_callback, error=_server_error_callback) - -# now if the client tried to subscribe on the presence channel, and therefore, get state info -# he is explicitly denied! - -# uncomment out to see client not able to read on presence channel -client.presence(channel=channel_public, callback=_client_presence_callback, error=_client_error_callback) - -# client subscribes to public channel -client.subscribe(channels=channel_public, state={"myKey": get_unique("foo")}, callback=_client_message_callback, - error=_client_error_callback) diff --git a/python/examples/presence.py b/python/examples/presence.py deleted file mode 100755 index b55bb0be..00000000 --- a/python/examples/presence.py +++ /dev/null @@ -1,32 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys -from pubnub import Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on, daemon=False) - -channel = 'b' - - -# Asynchronous usage -def callback(message, channel): - print(message) - -pubnub.presence(channel, callback=callback) diff --git a/python/examples/publish.py b/python/examples/publish.py deleted file mode 100755 index a645ad67..00000000 --- a/python/examples/publish.py +++ /dev/null @@ -1,36 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys -from pubnub import Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on, pooling=False) -channel = 'hello_world' -message = 'Hello World !!!' - - -# Synchronous usage -print(pubnub.publish(channel, message)) - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.publish(channel, message, callback=callback, error=callback) diff --git a/python/examples/pubnub-console/pubnub-console b/python/examples/pubnub-console/pubnub-console deleted file mode 100644 index a1915edf..00000000 --- a/python/examples/pubnub-console/pubnub-console +++ /dev/null @@ -1,724 +0,0 @@ -## www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -## PubNub Real-time Push APIs and Notifications Framework -## Copyright (c) 2010 Stephen Blum -## http://www.pubnub.com/ - - -import sys -from pubnub import Pubnub -import threading -from datetime import datetime - -from cmd2 import Cmd, make_option, options, Cmd2TestCase -import optparse -import json - -import atexit -import os -import readline -import rlcompleter - -import pygments -from pygments.lexers import JsonLexer -from pygments.formatters import TerminalFormatter - -lexer = JsonLexer() -formatter = TerminalFormatter() -def highlight(msg): - return pygments.highlight(msg, lexer, formatter) - - - -historyPath = os.path.expanduser("~/.pubnub_console_history") - - -def save_history(historyPath=historyPath): - import readline - readline.write_history_file(historyPath) - -if os.path.exists(historyPath): - readline.read_history_file(historyPath) - -atexit.register(save_history) - -of = sys.stdout - -color = Cmd() - -stop = None - -full_date = False - - -def stop_2(th): - th._Thread__stop() - - -def stop_3(th): - th._stop() - - -def print_console_2(of, message): - print >>of, message - - -def print_console_3(of, message): - of.write(message) - of.write("\n") - -print_console = None - -if type(sys.version_info) is tuple: - print_console = print_console_2 - stop = stop_2 -else: - if sys.version_info.major == 2: - print_console = print_console_2 - stop = stop_2 - else: - print_console = print_console_3 - stop = stop_3 - - -def get_date(): - if full_date is True: - return color.colorize(datetime.now().strftime( - '%Y-%m-%d %H:%M:%S'), "magenta") - else: - return color.colorize(datetime.now().strftime( - '%H:%M:%S'), "magenta") - -def print_ok_normal(msg, channel=None): - if msg is None: - return - chstr = "[" + color.colorize(get_date(), "magenta") + "] " - chstr += "[" + color.colorize("Channel : " + channel if channel is not None else "", "cyan") + "] " - try: - print_console(of, (chstr + color.colorize(str(msg), "green"))) - except UnicodeEncodeError as e: - print_console(of, (msg)) - - of.flush() - - -def print_error_normal(msg, channel=None): - if msg is None: - return - chstr = "[" + color.colorize(get_date(), "magenta") + "] " - chstr += "[" + color.colorize("Channel : " + channel if channel is not None else "", "cyan") + "] " - try: - print_console(of, (chstr + color.colorize(color.colorize( - str(msg), "red"), "bold"))) - except UnicodeEncodeError as e: - print_console(of, (msg)) - of.flush() - -def print_ok_pretty(msg, channel=None): - if msg is None: - return - chstr = "[" + color.colorize(get_date(), "magenta") + "] " - chstr += "[" + color.colorize("Channel : " + channel if channel is not None else "", "cyan") + "] " - try: - print_console(of, (chstr + highlight(json.dumps(msg, indent=2)))) - except UnicodeEncodeError as e: - print_console(of, (msg)) - - of.flush() - - -def print_error_pretty(msg, channel=None): - if msg is None: - return - chstr = "[" + color.colorize(get_date(), "magenta") + "] " - chstr += "[" + color.colorize("Channel : " + channel if channel is not None else "", "cyan") + "] " - try: - print_console(of, (chstr + color.colorize(color.colorize( - "ERROR: ", "red"), "bold") + - highlight(json.dumps(msg, indent=2)))) - except UnicodeEncodeError as e: - print_console(of, (msg)) - of.flush() - -print_ok = print_ok_pretty -print_error = print_error_pretty - - -class DefaultPubnub(object): - def handlerFunctionClosure(self, name): - def handlerFunction(*args, **kwargs): - print_error("Pubnub not initialized." + - "Use init command to initialize") - return handlerFunction - - def __getattr__(self, name): - return self.handlerFunctionClosure(name) - -pubnub = DefaultPubnub() - - -def kill_all_threads(): - for thread in threading.enumerate(): - if thread.isAlive(): - stop(thread) - - -def get_input(message, t=None): - while True: - try: - try: - command = raw_input(message) - except NameError: - command = input(message) - except KeyboardInterrupt: - return None - - command = command.strip() - - if command is None or len(command) == 0: - raise ValueError - - if t is not None and t == bool: - valid = ["True", "true", "1", 1, "y", "Y", "yes", "Yes", "YES"] - if command in valid: - return True - else: - return False - if t is not None: - command = t(command) - else: - command = eval("'" + command + "'") - - return command - except ValueError: - print_error("Invalid input : " + command) - - -def _publish_command_handler(channel, message, async=False): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - print_ok(pubnub.publish(channel, message, - _callback if async is True else None, - _error if async is True else None)) - - -def _subscribe_command_handler(channel): - - def _callback(r, ch): - print_ok(r, ch) - - def _error(r, ch=None): - print_error(r, ch if ch is not None else channel) - - def _disconnect(r): - print_ok("DISCONNECTED", r) - - def _reconnect(r): - print_ok("RECONNECTED", r) - - def _connect(r): - print_ok("CONNECTED", r) - - pubnub.subscribe(channel, _callback, _error, connect=_connect, - disconnect=_disconnect, reconnect=_reconnect) - - -def _unsubscribe_command_handler(channels): - - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - - unsub_list = [] - current_channel_list = pubnub.get_channel_array() - - for channel in channels: - pubnub.unsubscribe(channel) - if (channel in current_channel_list): - unsub_list.append(channel) - - #pubnub.unsubscribe(channel + '-pnpres') - #if (channel + '-pnpres' in current_channel_list): - # unsub_list.append(channel + ' (Presence)') - - if len(unsub_list) > 0: - print_ok('Unsubscribed from : ' + str(unsub_list)) - else: - print_error('Not subscribed to : ' + str(channels)) - - -def _grant_command_handler(channel, auth_key, read, write, ttl, async=False): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - - print_ok(pubnub.grant(channel, auth_key, - read, write, ttl, - _callback if async is True else None, - _error if async is True else None)) - - -def _revoke_command_handler(channel, auth_key, ttl, async=False): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - - print_ok(pubnub.revoke(channel, auth_key, ttl, - _callback if async is True else None, - _error if async is True else None)) - - -def _audit_command_handler(channel, auth_key, async=False): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - - print_ok(pubnub.audit(channel, auth_key, - _callback if async is True else None, - _error if async is True else None)) - - -def _history_command_handler(channel, count, async=False): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - - print_ok(pubnub.history(channel, count, - _callback if async is True else None, - _error if async is True else None)) - - -def _here_now_command_handler(channel, async=False): - def _callback(r): - print_ok(r) - - def _error(r): - print_error(r) - - print_ok(pubnub.here_now(channel, _callback if async is True else None, - _error if async is True else None)) - - -def kill_all_threads(): - for thread in threading.enumerate(): - if thread.isAlive(): - stop(thread) - - -def get_channel_array(): - channels = pubnub.get_channel_array() - - for channel in channels: - if "-pnpres" in channel: - chname = channel.split("-pnpres")[0] - if chname not in channels: - continue - i = channels.index(chname) - channels[i] = channels[i] + color.colorize("(P)", "blue") - channels.remove(channel) - return channels - - -class DevConsole(Cmd): - def __init__(self): - Cmd.__init__(self) - global pubnub - self.intro = "For Help type ? or help . " + \ - "To quit/exit type exit" + "\n" + \ - "Commands can also be provided on command line while starting console (in quotes) ex. " + \ - "pubnub-console 'init -p demo -s demo'" - self.default_channel = None - self.async = False - pubnub = Pubnub("demo", "demo") - self.channel_truncation = 3 - self.prompt = self.get_prompt() - self.publish_key = "demo" - self.subscribe_key = "demo" - self.origin = "pubsub.pubnub.com" - self.auth_key = None - self.cipher_key = None - self.secret_key = "demo" - self.ssl = False - self.uuid = None - self.disable_pretty = False - - def get_channel_origin(self): - cho = "" - channels = get_channel_array() - channels_str = ",".join(channels) - sl = self.channel_truncation - if len(channels) > int(sl) and int(sl) > 0: - cho += ",".join(channels[:int(sl)]) + " " + str( - len(channels) - int(sl)) + " more..." - else: - cho += ",".join(channels) - - if len(channels) > 0: - cho = color.colorize(cho, "bold") + "@" - - origin = pubnub.get_origin().split("://")[1] - origin += color.colorize(" (SSL)", "green") if pubnub.get_origin( - ).split("://")[0] == "https" else "" - return " [" + cho + color.colorize(origin, "blue") + "] > " - - def get_prompt(self): - prompt = "[" + get_date() + "]" - - if self.default_channel is not None and len(self.default_channel) > 0: - prompt += " [default channel: " + color.colorize( - self.default_channel, "bold") + "]" - - prompt += self.get_channel_origin() - return prompt - - def precmd(self, line): - self.prompt = self.get_prompt() - return line - - #def emptyline(self): - # self.prompt = self.get_prompt() - - def cmdloop_with_keyboard_interrupt(self): - try: - self.cmdloop() - except KeyboardInterrupt as e: - pass - sys.stdout.write('\n') - kill_all_threads() - - @options([make_option('-p', '--publish-key', action="store", - default=None, help="Publish Key"), - make_option('-s', '--subscribe-key', action="store", - default=None, help="Subscribe Key"), - make_option('-k', '--secret-key', action="store", - default=None, help="cipher Key"), - make_option('-c', '--cipher-key', action="store", - default=None, help="Secret Key"), - make_option('-a', '--auth-key', action="store", - default=None, help="Auth Key"), - make_option('--ssl-on', dest='ssl', action='store_true', - default=False, help="SSL Enabled ?"), - make_option('-o', '--origin', action="store", - default=None, help="Origin"), - make_option('-u', '--uuid', action="store", - default=None, help="UUID"), - make_option('--disable-pretty-print', dest='disable_pretty', action='store_true', - default=False, help="Disable Pretty Print ?") - ]) - def do_init(self, command, opts): - global pubnub - global print_ok - global print_error - global print_ok_normal - global print_error_normal - global print_error_pretty - global print_ok_pretty - - self.publish_key = opts.publish_key if opts.publish_key is not None else self.publish_key - self.subscribe_key = opts.subscribe_key if opts.subscribe_key is not None else self.subscribe_key - self.secret_key = opts.secret_key if opts.secret_key is not None else self.secret_key - self.cipher_key = opts.cipher_key if opts.cipher_key is not None else self.cipher_key - self.auth_key = opts.auth_key if opts.auth_key is not None else self.auth_key - self.origin = opts.origin if opts.origin is not None else self.origin - self.uuid = opts.uuid if opts.uuid is not None else self.uuid - self.ssl = opts.ssl if opts.ssl is not None else self.ssl - self.disable_pretty = opts.disable_pretty if opts.disable_pretty is not None else self.disable_pretty - - pubnub = Pubnub(self.publish_key, - self.subscribe_key, - self.secret_key, - self.cipher_key, - self.auth_key, - self.ssl, - self.origin, - self.uuid) - self.prompt = self.get_prompt() - - if opts.disable_pretty is True: - print_ok = print_ok_normal - print_error = print_error_normal - else: - print_ok = print_ok_pretty - print_error = print_error_pretty - - def do_set_sync(self, command): - """unset_async - Unset Async mode""" - self.async = False - - def do_set_async(self, command): - """set_async - Set Async mode""" - self.async = True - - @options([make_option('-n', '--count', action="store", - default=3, help="Number of channels on prompt") - ]) - def do_set_channel_truncation(self, command, opts): - """set_channel_truncation - Set Channel Truncation""" - - self.channel_truncation = opts.count - - self.prompt = self.get_prompt() - - def do_unset_channel_truncation(self, command): - """unset_channel_truncation - Unset Channel Truncation""" - self.channel_truncation = 0 - self.prompt = self.get_prompt() - - def do_set_full_date(self, command): - global full_date - """do_set_full_date - Set Full Date""" - full_date = True - self.prompt = self.get_prompt() - - def do_unset_full_date(self, command): - global full_date - """do_unset_full_date - Unset Full Date""" - full_date = False - self.prompt = self.get_prompt() - - @options([make_option('-c', '--channel', - action="store", help="Default Channel") - ]) - def do_set_default_channel(self, command, opts): - - if opts.channel is None: - print_error("Missing channel") - return - self.default_channel = opts.channel - self.prompt = self.get_prompt() - - @options([make_option('-f', '--file', action="store", - default="./pubnub-console.log", help="Output file") - ]) - def do_set_output_file(self, command, opts): - global of - try: - of = file(opts.file, 'w+') - except IOError as e: - print_error("Could not set output file. " + e.reason) - - @options([make_option('-c', '--channel', action="store", - help="Channel for here now data") - ]) - def do_here_now(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - if opts.channel is None: - print_error("Missing channel") - return - - _here_now_command_handler(opts.channel, async=self.async) - - @options([make_option('-c', '--channel', action="store", - help="Channel for history data"), - make_option('-n', '--count', action="store", - default=5, help="Number of messages") - ]) - def do_get_history(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - if opts.channel is None: - print_error("Missing channel") - return - - _history_command_handler(opts.channel, opts.count, async=self.async) - - @options([make_option('-c', '--channel', action="store", - help="Channel on which to publish") - ]) - def do_publish(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - if opts.channel is None: - print_error("Missing channel") - return - - if command is None: - print_error("Missing message") - return - - try: - message = json.loads(str(command)) - except ValueError as ve: - message = str(command) - - _publish_command_handler(opts.channel, message, async=self.async) - - @options([make_option('-c', '--channel', action="store", - help="Channel on which to grant"), - make_option('-a', '--auth-key', dest="auth_key", - action="store", - help="Auth Key"), - make_option('-r', '--read-enabled', dest='read', - action='store_true', - default=False, help="Read ?"), - make_option('-w', '--write-enabled', dest='write', - action='store_true', - default=False, help="Write ?"), - make_option('-t', '--ttl', action="store", - default=5, help="TTL"), - make_option('-p', '--presence', action="store_true", - dest="presence", - default=False, help="Grant on presence channel ?") - ]) - def do_grant(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - - opts.auth_key = pubnub.auth_key \ - if opts.auth_key is None else opts.auth_key - - _grant_command_handler(opts.channel, opts.auth_key, - opts.read, opts.write, - opts.ttl, async=self.async) - - if opts.presence is True: - _grant_command_handler(opts.channel + '-pnpres', opts.auth_key, - opts.read, opts.write, - opts.ttl, async=self.async) - - @options([make_option('-c', '--channel', action="store", - help="Channel on which to revoke"), - make_option('-a', '--auth-key', dest="auth_key", action="store", - help="Auth Key"), - make_option('-t', '--ttl', action="store", - default=5, help="TTL"), - make_option('-p', '--presence', action="store_true", - dest="presence", - default=False, help="Revoke on presence channel ?") - ]) - def do_revoke(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - - opts.auth_key = pubnub.auth_key \ - if opts.auth_key is None else opts.auth_key - - _revoke_command_handler( - opts.channel, opts.auth_key, opts.ttl, async=self.async) - - if opts.presence is True: - _revoke_command_handler( - opts.channel + '-pnpres', opts.auth_key, - opts.ttl, async=self.async) - - @options([make_option('-c', '--channel', action="store", - help="Channel on which to revoke"), - make_option('-a', '--auth-key', dest="auth_key", action="store", - help="Auth Key") - ]) - def do_audit(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - - opts.auth_key = pubnub.auth_key \ - if opts.auth_key is None else opts.auth_key - - _audit_command_handler(opts.channel, opts.auth_key, async=self.async) - - @options([make_option('-c', '--channel', action="store", - help="Channel for unsubscribe"), - make_option('-a', '--all', action="store_true", dest="all", - default=False, help="Unsubscribe from all channels"), - make_option('-p', '--presence', action="store_true", - dest="presence", - default=False, help="Unsubscribe from presence events ?") - ]) - def do_unsubscribe(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - - if (opts.all is True): - opts.channel = [] - chs = pubnub.get_channel_array() - for ch in chs: - if '-pnpres' not in ch: - opts.channel.append(ch) - elif opts.presence is True: - opts.channel.append(ch) - - if opts.channel is None: - print_error("Missing channel") - return - - if not isinstance(opts.channel, list): - ch = [] - ch.append(opts.channel) - opts.channel = ch - - channels = [] - if opts.presence is True and opts.all is False: - for c in opts.channel: - if '-pnpres' not in c: - channels.append(c + '-pnpres') - - for c in opts.channel: - channels.append(c) - - _unsubscribe_command_handler(channels) - self.prompt = self.get_prompt() - - @options([make_option('-c', '--channel', action="store", - help="Channel for subscribe"), - make_option('-g', '--get-channel-list', action="store_true", - dest="get", - default=False, help="Get susbcribed channel list"), - make_option('-p', '--presence', action="store_true", - dest="presence", - default=False, help="Presence events ?") - ]) - def do_subscribe(self, command, opts): - opts.channel = self.default_channel \ - if opts.channel is None else opts.channel - if opts is None: - print_error("Missing argument") - return - - if opts.channel is not None: - _subscribe_command_handler(opts.channel) - - if opts.presence is True: - _subscribe_command_handler(opts.channel + '-pnpres') - - if opts.get is True: - print_ok(get_channel_array()) - self.prompt = self.get_prompt() - - def do_exit(self, args): - kill_all_threads() - return -1 - - #def do_EOF(self, args): - # kill_all_threads() - # return self.do_exit(args) - - #def handler(self, signum, frame): - # kill_all_threads() - - -def main(): - app = DevConsole() - app.cmdloop_with_keyboard_interrupt() - -if __name__ == "__main__": - main() diff --git a/python/examples/pubnub-console/setup.py b/python/examples/pubnub-console/setup.py deleted file mode 100644 index 5ee5fd9e..00000000 --- a/python/examples/pubnub-console/setup.py +++ /dev/null @@ -1,27 +0,0 @@ -from setuptools import setup - -setup( - name='pubnub-console', - version='3.5.2', - description='PubNub Developer Console', - author='Stephen Blum', - author_email='support@pubnub.com', - url='http://pubnub.com', - scripts=['pubnub-console'], - license='MIT', - classifiers=( - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Programming Language :: Python', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Topic :: Internet :: WWW/HTTP', - 'Topic :: Software Development :: Libraries :: Python Modules', - ), - install_requires=[ - 'pubnub>=3.5.2', - 'cmd2>=0.6.7', - 'pygments >= 1.6' - ], - zip_safe=False, -) diff --git a/python/examples/requirements.pip b/python/examples/requirements.pip deleted file mode 100644 index 738c6067..00000000 --- a/python/examples/requirements.pip +++ /dev/null @@ -1,3 +0,0 @@ -pycrypto==2.6.1 -cmd2==0.6.7 -requests==2.2.1 diff --git a/python/examples/revoke.py b/python/examples/revoke.py deleted file mode 100755 index 23bc110f..00000000 --- a/python/examples/revoke.py +++ /dev/null @@ -1,35 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys -from pubnub import Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'pam' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'pam' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'pam' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) -channel = 'hello_world' -authkey = "abcd" - -# Synchronous usage -print(pubnub.revoke(channel, authkey)) - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.revoke(channel, authkey, callback=callback, error=callback) diff --git a/python/examples/start-console.sh b/python/examples/start-console.sh deleted file mode 100755 index a928cb33..00000000 --- a/python/examples/start-console.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -#!/bin/bash -e - -BASEDIR=. - -if [ ! -d "$BASEDIR/ve" ]; then - virtualenv -q $BASEDIR/ve --no-site-packages - $BASEDIR/ve/bin/activate - echo "Virtualenv created." -fi - -chmod 755 $BASEDIR/ve/bin/activate -$BASEDIR/ve/bin/activate - -if [ ! -f "$BASEDIR/ve/updated" -o $BASEDIR/requirements.pip -nt $BASEDIR/ve/updated ]; then - pip install -r $BASEDIR/requirements.pip -E $BASEDIR/ve - touch $BASEDIR/ve/updated - echo "Requirements installed." -fi - - - -if ! type "screen" > /dev/null; then - echo "[ERROR] Screen is not installed. Please install screen to use this utility ." - exit -fi -rm ./pubnub-console.log -touch ./pubnub-console.log -export PYTHONPATH=../.. -screen -X -S pubnub-console quit 2>&1 > /dev/null -OS="`uname`" -case $OS in - [dD]'arwin') - screen -c config_osx - ;; - *) screen -c config ;; -esac diff --git a/python/examples/state.py b/python/examples/state.py deleted file mode 100755 index f1c92c65..00000000 --- a/python/examples/state.py +++ /dev/null @@ -1,123 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys -import time -from gevent import monkey -from pubnub import Pubnub - -monkey.patch_all() - - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) - - -def log(a): - print(a) - - -pubnub.set_http_debug(log) - -# Synchronous usage -print(pubnub.state(channel='abcd', uuid='33c72389-1110-4312-9444-4dd24ade1d57', state={'a': 'b'})) - - -# Asynchronous usage - - -def callback(message): - print(message) - - -pubnub.state(channel='abcd', uuid='33c72389-1110-4312-9444-4dd24ade1d57', state={'a': 'b'}, callback=callback, - error=callback) - -time.sleep(5) - -# Synchronous usage -print(pubnub.state(channel='abcd', uuid='33c72389-1110-4312-9444-4dd24ade1d57')) - - -# Asynchronous usage - - -def callback(message): - print(message) - - -pubnub.state(channel='abcd', uuid='33c72389-1110-4312-9444-4dd24ade1d57', callback=callback, error=callback) - -time.sleep(5) - -# Synchronous usage -print(pubnub.state(channel='abcd')) - - -# Asynchronous usage - - -def callback(message): - print(message) - - -pubnub.state(channel='abcd', callback=callback, error=callback) - -time.sleep(5) - -# Synchronous usage -print(pubnub.state(channel='abcd', state={'a': 'b'})) - - -# Asynchronous usage - - -def callback(message): - print(message) - - -pubnub.state(channel='abcd', state={'a': 'b'}, callback=callback, error=callback) - -time.sleep(5) - -# Synchronous usage -print(pubnub.state(channel='abcd')) - - -# Asynchronous usage - - -def callback(message): - print(message) - - -pubnub.state(channel='abcd', callback=callback, error=callback) - -time.sleep(5) - -# Synchronous usage -print(pubnub.state(channel='abcd')) - - -# Asynchronous usage - - -def callback(message): - print(message) - - -pubnub.state(channel='abcd', callback=callback, error=callback) diff --git a/python/examples/subscribe.py b/python/examples/subscribe.py deleted file mode 100755 index 8ee00998..00000000 --- a/python/examples/subscribe.py +++ /dev/null @@ -1,53 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys -import time -from pubnub import Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on, daemon=True) - -channel = 'a' - - -# Asynchronous usage -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect(message): - print("CONNECTED") - - -def reconnect(message): - print("RECONNECTED") - - -def disconnect(message): - print("DISCONNECTED") - - -pubnub.subscribe(channels=channel, callback=callback, error=callback, - connect=connect, reconnect=reconnect, disconnect=disconnect) - -while True: - time.sleep(10) diff --git a/python/examples/subscribe_group.py b/python/examples/subscribe_group.py deleted file mode 100755 index b6228f66..00000000 --- a/python/examples/subscribe_group.py +++ /dev/null @@ -1,69 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys - -from pubnub import Pubnub as Pubnub - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or 'abcd' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) - -channel = 'ab' - - -# Asynchronous usage - -def callback_abc(message, channel, real_channel): - print(str(message) + ' , ' + channel + ', ' + real_channel) - pubnub.unsubscribe_group(channel_group='abc') - # pubnub.stop() - - -def callback_d(message, channel): - print(str(message) + ' , ' + channel) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect_abc(message): - print("CONNECTED " + str(message)) - - -def connect_d(message): - print("CONNECTED " + str(message)) - pubnub.unsubscribe(channel='d') - - -def reconnect(message): - print("RECONNECTED " + str(message)) - - -def disconnect(message): - print("DISCONNECTED " + str(message)) - - -print(pubnub.channel_group_add_channel(channel_group='abc', channel="b")) - -pubnub.subscribe_group(channel_groups='abc', callback=callback_abc, error=error, - connect=connect_abc, reconnect=reconnect, disconnect=disconnect) - -pubnub.subscribe(channels='d', callback=callback_d, error=error, - connect=connect_d, reconnect=reconnect, disconnect=disconnect) - -pubnub.start() diff --git a/python/examples/where-now.py b/python/examples/where-now.py deleted file mode 100755 index dd18a67e..00000000 --- a/python/examples/where-now.py +++ /dev/null @@ -1,38 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import sys -from gevent import monkey -from pubnub import Pubnub - -monkey.patch_all() - -publish_key = len(sys.argv) > 1 and sys.argv[1] or 'demo' -subscribe_key = len(sys.argv) > 2 and sys.argv[2] or 'demo' -secret_key = len(sys.argv) > 3 and sys.argv[3] or 'demo' -cipher_key = len(sys.argv) > 4 and sys.argv[4] or '' -ssl_on = len(sys.argv) > 5 and bool(sys.argv[5]) or False - -# ----------------------------------------------------------------------- -# Initiate Pubnub State -# ----------------------------------------------------------------------- -pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key, - secret_key=secret_key, cipher_key=cipher_key, ssl_on=ssl_on) - -# Synchronous usage -print(pubnub.where_now(uuid='33c72389-1110-4312-9444-4dd24ade1d57')) - - -# Asynchronous usage - - -def callback(message): - print(message) - - -pubnub.where_now(uuid='33c72389-1110-4312-9444-4dd24ade1d57', callback=callback, error=callback) diff --git a/python/examples/wildcard_subscribe.py b/python/examples/wildcard_subscribe.py deleted file mode 100755 index b3e542ee..00000000 --- a/python/examples/wildcard_subscribe.py +++ /dev/null @@ -1,96 +0,0 @@ -from gevent import monkey -from pubnub import Pubnub - -monkey.patch_all() - - -pubnub = Pubnub(publish_key="ds", subscribe_key="ds", - secret_key="ds", ssl_on=False) - - -# Wildcard Subscribe without presence - -def a(): - channel_wc = "a.*" - channel = "a.b" - - def callback(message1, channel1, real_channel=None): - print(channel1 + " : " + real_channel + " : " + str(message1)) - - def error(message): - print("ERROR : " + str(message)) - - def connect(channel1=None): - print("Connect on " + channel1) - print(pubnub.publish(channel=channel, message="a")) - - def disconnect(channel1=None): - print("Disconnect on " + channel1) - - def reconnect(channel1=None): - print("Reconnect on " + channel1) - - pubnub.subscribe(channels=channel_wc, callback=callback, error=callback, - connect=connect, disconnect=disconnect, reconnect=reconnect) - - -# Wildcard Subscribe with presence - -def b(): - channel_wc = "b.*" - channel = "b.c" - - def callback(message1, channel1, real_channel=None): - print(channel1 + " : " + real_channel + " : " + str(message1)) - - def error(message): - print("ERROR : " + str(message)) - - def presence(message1, channel1, real_channel=None): - print(channel1 + " : " + real_channel + " : " + str(message1)) - - def connect(channel1=None): - print("Connect on " + channel1) - print(pubnub.publish(channel=channel, message="b")) - - def disconnect(channel1=None): - print("Disconnect on " + channel1) - - def reconnect(channel1=None): - print("Reconnect on " + channel1) - - pubnub.subscribe(channels=channel_wc, callback=callback, error=callback, - connect=connect, disconnect=disconnect, reconnect=reconnect, presence=presence) - - -# Wildcard Subscribe and unsubscribe - -def c(): - channel_wc = "c.*" - channel = "c.d" - - def callback(message1, channel1, real_channel=None): - print(channel1 + " : " + real_channel + " : " + str(message1)) - pubnub.unsubscribe(channel="c.*") - print(pubnub.publish(channel=channel, message="c1")) - - def error(message): - print("ERROR : " + str(message)) - - def connect(channel1=None): - print("Connect on " + channel1) - print(pubnub.publish(channel=channel, message="c")) - - def disconnect(channel1=None): - print("Disconnect on " + channel1) - - def reconnect(channel1=None): - print("Reconnect on " + channel1) - - pubnub.subscribe(channels=channel_wc, callback=callback, error=callback, - connect=connect, disconnect=disconnect, reconnect=reconnect) - - -a() -b() -c() diff --git a/python/migration.md b/python/migration.md deleted file mode 100644 index 61cf203a..00000000 --- a/python/migration.md +++ /dev/null @@ -1,205 +0,0 @@ -## Contact support@pubnub.com for all questions - -#### [PubNub](http://www.pubnub.com) Real-time Data Network -##### Standalone Python Migration - -#### Init - -``` - -# Pre 3.5: -pubnub = Pubnub( - "demo", ## PUBLISH_KEY - "demo", ## SUBSCRIBE_KEY - False ## SSL_ON? -) - -# New in 3.5+ -pubnub = Pubnub(publish_key="demo", subscribe_key="demo", ssl_on=False) - -``` - -#### PUBLISH - -``` -channel = 'hello_world' -message = 'Hello World !!!' - -# Pre 3.5: -info = pubnub.publish({ - 'channel' : channel, - 'message' : message -}) -print(info) - -# New in 3.5+ - -# Synchronous usage -print pubnub.publish(channel='hello_world', message='Hello World !!!') - -# Asynchronous usage - -def callback(message): - print(message) - -pubnub.publish(channel, message, callback=callback, error=callback) - -``` - - -#### SUBSCRIBE -Pre 3.5.x, subscribe was blocking and would only be terminated via a false return from the callback. In our latest version of the SDK, subscribe is asyncronous, and because of this, usage is a bit different, but the experience is more like our other async SDKs. - -``` - -# Listen for Messages - -channel = 'hello_world' - -# Pre 3.5: -# Listen for Messages *BLOCKING* -def receive(message) : - print(message) - return True - -pubnub.subscribe({ - 'channel' : channel, - 'callback' : receive -}) - - -# New in 3.5+ - -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - -def connect(message): - print("CONNECTED") - - -def reconnect(message): - print("RECONNECTED") - - -def disconnect(message): - print("DISCONNECTED") - - -pubnub.subscribe(channel, callback=callback, error=callback, - connect=connect, reconnect=reconnect, disconnect=disconnect) -``` - -#### Unsubscribe -Once subscribed, you can easily, gracefully, unsubscribe: - -``` -# Pre 3.5: -# - -# New in 3.5+ - -pubnub.unsubscribe(channel='hello_world') -``` - -#### PRESENCE - -``` - - -channel = 'hello_world' - -# Pre 3.5: -def pres_event(message) : - print(message) - return True - -pubnub.presence({ - 'channel' : channel, - 'callback' : receive -}) - - -# New in 3.5+ - -# Listen for Presence Event Messages - -def callback(message, channel): - print(message) - - -def error(message): - print("ERROR : " + str(message)) - - - -pubnub.presence(channel, callback=callback, error=callback) -``` - -#### HERE_NOW - -``` - -# Pre 3.5: -# Get info on who is here right now! - -here_now = pubnub.here_now({ - 'channel' : 'hello_world', -}) - -print(here_now['occupancy']) -print(here_now['uuids']) - - -# New in 3.5+ - -# Get info on who is here right now! - -channel = 'hello_world' - -# Synchronous usage -print pubnub.here_now(channel) - - -# Asynchronous usage - -def callback(message): - print(message) - -pubnub.here_now(channel, callback=callback, error=callback) -``` - -#### HISTORY - -``` - -# Pre 3.5: -# Load Previously Published Messages -history = pubnub.detailedHistory({ - 'channel' : 'my_channel', - 'end' : my_end_time_token, # Optional - 'start' : my_start_time_token, # Optional - 'count' : num_of_msgs_to_return # Optional, default is 100 -}) -print(history) - -# New in 3.5+ - -# Synchronous usage - -print pubnub.history(channel, count=2) - -# Asynchronous usage - - -def callback(message): - print(message) - -pubnub.history(channel, count=2, callback=callback, error=callback) -``` - -## Contact support@pubnub.com for all questions diff --git a/python/tests/test_cg.py b/python/tests/test_cg.py deleted file mode 100755 index 154611f5..00000000 --- a/python/tests/test_cg.py +++ /dev/null @@ -1,96 +0,0 @@ -from pubnub import Pubnub -import random - -pubnub = Pubnub("demo", "demo") -pubnub.set_u(True) - - -def rand_str(s): - return str(s) + '-' + str(random.randint(1, 100000000000)) - - -def test_1(): - channel = rand_str('channel') - channel2 = rand_str('channel') - channel_group = rand_str('group') - channel_group2 = rand_str('group') - namespace = rand_str('ns') - - resp = pubnub.channel_group_add_channel(channel_group=namespace + ':' + channel_group, channel=channel) - assert resp['status'] == 200 - assert resp['message'] == 'OK' - assert resp['error'] == False - - resp = pubnub.channel_group_add_channel(channel_group=namespace + ':' + channel_group, channel=channel2) - assert resp['status'] == 200 - assert resp['message'] == 'OK' - assert resp['error'] == False - - resp = pubnub.channel_group_add_channel(channel_group=namespace + ':' + channel_group2, channel=channel) - assert resp['status'] == 200 - assert resp['message'] == 'OK' - assert resp['error'] == False - - resp = pubnub.channel_group_add_channel(channel_group=namespace + ':' + channel_group2, channel=channel2) - assert resp['status'] == 200 - assert resp['message'] == 'OK' - assert resp['error'] == False - - resp = pubnub.channel_group_list_channels(channel_group=namespace + ':' + channel_group) - assert channel in resp['payload']['channels'] - assert channel2 in resp['payload']['channels'] - assert len(resp['payload']['channels']) == 2 - - resp = pubnub.channel_group_remove_channel(channel_group=namespace + ':' + channel_group, channel=channel2) - print(resp) - assert resp['status'] == 200 - assert resp['message'] == 'OK' - assert resp['error'] == False - - resp = pubnub.channel_group_list_channels(channel_group=namespace + ':' + channel_group) - print(resp) - assert channel in resp['payload']['channels'] - assert len(resp['payload']['channels']) == 1 - - resp = pubnub.channel_group_list_channels(channel_group=namespace + ':' + channel_group2) - assert channel in resp['payload']['channels'] - assert channel2 in resp['payload']['channels'] - assert len(resp['payload']['channels']) == 2 - - resp = pubnub.channel_group_remove_channel(channel_group=namespace + ':' + channel_group2, channel=channel2) - print(resp) - assert resp['status'] == 200 - assert resp['message'] == 'OK' - assert resp['error'] == False - - resp = pubnub.channel_group_list_channels(channel_group=namespace + ':' + channel_group2) - print(resp) - assert channel in resp['payload']['channels'] - assert len(resp['payload']['channels']) == 1 - - resp = pubnub.channel_group_list_groups(namespace=namespace) - assert channel_group in resp['payload']['groups'] - assert channel_group2 in resp['payload']['groups'] - assert len(resp['payload']['groups']) == 2 - - resp = pubnub.channel_group_remove_group(channel_group=namespace + ':' + channel_group2) - print(resp) - assert resp['status'] == 200 - assert resp['message'] == 'OK' - assert resp['error'] == False - - resp = pubnub.channel_group_list_groups(namespace=namespace) - assert channel_group in resp['payload']['groups'] - assert len(resp['payload']['groups']) == 1 - - resp = pubnub.channel_group_list_namespaces() - assert namespace in resp['payload']['namespaces'] - - resp = pubnub.channel_group_remove_namespace(namespace=namespace) - print(resp) - assert resp['status'] == 200 - assert resp['message'] == 'OK' - assert resp['error'] == False - - resp = pubnub.channel_group_list_namespaces() - assert namespace not in resp['payload']['namespaces'] diff --git a/python/tests/test_grant.py b/python/tests/test_grant.py deleted file mode 100755 index 1789d603..00000000 --- a/python/tests/test_grant.py +++ /dev/null @@ -1,90 +0,0 @@ -from pubnub import Pubnub -import time - -subkey = "sub-c-9aeec0d4-cdf4-11e5-bcee-0619f8945a4f" -pubkey = "pub-c-b3fdf8fc-4516-4ab2-8836-6fb22ba7870d" -secretkey = "sec-c-ZDQwNTUwMDctZDViYi00MzhlLTg2NTctYjViZDcwNTA5Zjhj" -pubnub = Pubnub(pubkey, subkey) -pubnub_pam = Pubnub(pubkey, subkey, secretkey) -pam_timeout = 10 - - -# Grant permission read true, write true, on channel ( Sync Mode ) -def test_1(): - resp = pubnub_pam.grant(channel="abcd", auth_key="abcd", read=True, - write=True, ttl=1) - print(resp) - assert resp['message'] == 'Success' - assert resp['payload'] == { - 'auths': {'abcd': {'r': 1, 'w': 1, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': 'abcd', 'ttl': 1 - } - - -# Grant permission read false, write false, on channel ( Sync Mode ) -def test_2(): - resp = pubnub_pam.grant(channel="abcd", auth_key="abcd", read=False, - write=False, ttl=1) - assert resp['message'] == 'Success' - assert resp['payload'] == { - 'auths': {'abcd': {'r': 0, 'w': 0, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': 'abcd', 'ttl': 1 - } - - -# Grant permission read True, write false, on channel ( Sync Mode ) -def test_3(): - resp = pubnub_pam.grant(channel="abcd", auth_key="abcd", read=True, - write=False, ttl=1) - assert resp['message'] == 'Success' - assert resp['payload'] == { - 'auths': {'abcd': {'r': 1, 'w': 0, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': 'abcd', 'ttl': 1 - } - - -# Grant permission read False, write True, on channel ( Sync Mode ) -def test_4(): - resp = pubnub_pam.grant(channel="abcd", auth_key="abcd", read=True, - write=False, ttl=1) - assert resp['message'] == 'Success' - assert resp['payload'] == { - 'auths': {'abcd': {'r': 1, 'w': 0, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': 'abcd', 'ttl': 1 - } - - -# Grant permission read False, write True, on channel ( Sync Mode ), TTL 10 -def test_5(): - resp = pubnub_pam.grant(channel="abcd", auth_key="abcd", read=True, - write=False, ttl=10) - assert resp['message'] == 'Success' - assert resp['payload'] == { - 'auths': {'abcd': {'r': 1, 'w': 0, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': 'abcd', 'ttl': 10 - } - - -# Complete flow , try publish on forbidden channel, grant permission to -# auth key and try again. ( Sync Mode) -def test_8(): - channel = "test_8-" + str(time.time()) - message = "Hello World" - auth_key = "auth-" + channel - pubnub.set_auth_key(auth_key) - resp = pubnub_pam.grant(channel=channel, read=True, write=True, - auth_key=auth_key, ttl=10) - assert resp == { - 'message': 'Success', - 'payload': {'auths': {auth_key: {'r': 1, 'w': 1, 'm': 0}}, - 'subscribe_key': subkey, - 'level': 'user', 'channel': channel, 'ttl': 10} - } - time.sleep(pam_timeout) - resp = pubnub.publish(channel=channel, message=message) - assert resp[0] == 1 diff --git a/python/tests/test_history.py b/python/tests/test_history.py deleted file mode 100755 index 64ee7f00..00000000 --- a/python/tests/test_history.py +++ /dev/null @@ -1,65 +0,0 @@ -from pubnub import Pubnub -import time -import random -from nose.tools import with_setup - - -pubnub = Pubnub("ds", "ds") -pubnub_enc = Pubnub(publish_key="ds", subscribe_key="ds", cipher_key="enigma") -pubnub_pam = Pubnub(publish_key="pam", subscribe_key="pam", secret_key="pam") - - -def rand(msg): - return "rand-" + str(random.random()) + "-" + msg - -channel = rand("channel") -channel_enc = rand("channel_enc") -channel_pam = rand("channel_pam") - -messages = [] - - -def setup_func(): - pubnub_pam.grant(channel=channel_pam, read=True, write=True, ttl=144000) - - for i in range(0, 20): - msg = rand("message-" + str(i)) - messages.append(msg) - print(pubnub.publish(channel=channel, message=msg)) - # Fails with Python 3 - # print(pubnub_enc.publish(channel=channel_enc, message=msg)) - print(pubnub_pam.publish(channel=channel_pam, message=msg)) - - -@with_setup(setup_func) -def test_1(): - time.sleep(3) - hresp = pubnub.history(channel=channel, count=20) - # Fails with Python 3 - # hresp2 = pubnub_enc.history(channel=channel_enc, count=20) - hresp3 = pubnub_pam.history(channel=channel_pam, count=20) - hresp4 = pubnub_pam.history(channel=channel_pam + "no_rw", count=20) - assert hresp[0] == messages - # Fails with Python 3 - # assert hresp2[0] == messages - assert hresp3[0] == messages - assert hresp4['message'] == 'Forbidden' - assert channel_pam + "no_rw" in hresp4['payload']['channels'] - - -def test_2(): - time.sleep(3) - hresp = pubnub.history(channel=channel, count=20, include_token=True) - # Fails with Python 3 - # hresp2 = pubnub_enc.history(channel=channel_enc, count=20, include_token=True) - hresp3 = pubnub_pam.history(channel=channel_pam, count=20, include_token=True) - hresp4 = pubnub_pam.history(channel=channel_pam + "no_rw", count=20, include_token=True) - assert len(hresp[0]) == len(messages) - assert hresp[0][0]['timetoken'] - # Fails with Python 3 - # assert len(hresp2[0]) == len(messages) - # assert hresp2[0][0]['timetoken'] - assert len(hresp3[0]) == len(messages) - assert hresp3[0][0]['timetoken'] - assert hresp4['message'] == 'Forbidden' - assert channel_pam + "no_rw" in hresp4['payload']['channels'] diff --git a/python/tests/test_wildcard.py b/python/tests/test_wildcard.py deleted file mode 100755 index b563975e..00000000 --- a/python/tests/test_wildcard.py +++ /dev/null @@ -1,360 +0,0 @@ -# www.pubnub.com - PubNub Real-time push service in the cloud. -# coding=utf8 - -# PubNub Real-time Push APIs and Notifications Framework -# Copyright (c) 2010 Stephen Blum -# http://www.pubnub.com/ - - -import time -import random -from pubnub import Pubnub -from inspect import currentframe, getouterframes -from gevent import monkey - -monkey.patch_all() - - -def get_line(): - # print getouterframes(currentframe())[3] - return getouterframes(currentframe())[2][2] - - -DELAY = 5 - -RESULTS = {} - - -def check_tests(): - for i in RESULTS: - test = RESULTS[i] - if test['done'] is False: - if test['expect'] == test['passed']: - green(i + " PASSED " + str(test['passed'])) - else: - red(i + " FAILED " + str(test['failed'])) - test['done'] = True - - -def init(conf, name, expect): - # check_tests() - print("\n\n") - RESULTS[name + conf] = {'passed': 0, 'failed': 0, 'expect': 0, 'done': False, 'conf': conf} - RESULTS[name + conf]['expect'] = expect - - -def get_random(): - return str(random.randint(1, 99999)) - - -def red(name): - print('\033[1;31m' + name + '\033[1;m') - - -def green(name): - print('\033[1;92m' + name + '\033[1;m') - - -def test(cond, desc=None, test_name=None, conf=None): - if (cond): - green("[" + test_name + " " + str(conf) + " ""][" + str(get_line()) + "] PASS : " + str(desc)) - RESULTS[test_name + conf]['passed'] = RESULTS[test_name + str(conf)]['passed'] + 1 - else: - red("[" + test_name + " " + str(conf) + " ""][" + str(get_line()) + "] FAIL : " + str(desc)) - RESULTS[test_name + conf]['failed'] = RESULTS[test_name + str(conf)]['failed'] + 1 - # exit(0) - - -def run_tests(tests): - for test in tests: - if len(test) > 4: - test[1](test[2], test[3], test[0], test[4]) - else: - test[1](test[2], test[3], test[0]) - time.sleep(DELAY) - check_tests() - - -def test_1(pubnub, pubnub2, conf=None, msg=None): - init(conf, "test_1", 6) - r = get_random() - channel_wc = r + "-" + "test_1-ab.*" - message = msg if msg is not None else (r + "-" + "test_1_hi") - channel = r + "-" + "test_1-ab.d" - - # Asynchronous usage - def callback(message1, channel1, real_channel=None): - # print(str(message1) + ' : ' + str(channel1) + ' : ' + str(real_channel)) - test(message1 == str(message, 'utf-8'), "message received", "test_1", conf) - test(channel1 == channel_wc, "Channel == wildcard channel", "test_1", conf) - test(real_channel == channel, "Real channel == publish channel", "test_1", conf) - - def error(message): - print("ERROR : " + str(message)) - - def connect(channel1=None): - test(True, "Connect Called", "test_1", conf) - test(channel1 == channel_wc, "Channel param in Connect same as wildcard", "test_1", conf) - - def _callback(message): - test(message[0] == 1, "Publish Successful", "test_1", conf) - - def _error(message): - test(False, "Publish Successful", "test_1", conf) - - pubnub.publish(channel=channel, message=message, callback=_callback, error=_error) - - pubnub.subscribe(channels=channel_wc, callback=callback, error=callback, connect=connect) - - -def test_2(pubnub, pubnub2, conf=None, msg=None): - init(conf, "test_2", 8) - r = get_random() - channel_wc = r + "-" + "test_2-ab.*" - message = msg if msg is not None else (r + "-" + "test_2_hi") - channel = r + "-" + "test_2-ab.d" - - # Asynchronous usage - def callback(message1, channel1, real_channel=None): - # print(str(message1) + ' : ' + str(channel1) + ' : ' + str(real_channel)) - test(message1 == str(message, 'utf-8'), "message received", "test_2", conf) - test(channel1 == channel_wc, "Channel == wildcard channel", "test_2", conf) - test(real_channel == channel, "Real channel == publish channel", "test_2", conf) - - def error(message): - print("ERROR : " + str(message)) - - def presence(message, channel1, real_channel=None): - if (pubnub.uuid == message['uuid']): - test(channel_wc == real_channel, "On pubnub subscribe presence event as wildcard as real channel", "test_2", - conf) - elif (pubnub2.uuid == message['uuid']): - test(channel == real_channel, "On pubnub2 subscribe presence event as channel as real channel", "test_2", - conf) - else: - test(False, "Wrong presence event", "test_2", conf) - - def connect(channel1=None): - # print(channel1) - test(True, "Connect Called", "test_2", conf) - test(channel1 == channel_wc, "Channel param in Connect same as wildcard", "test_2", conf) - - def _callback(message): - test(message[0] == 1, "Publish Successful", "test_2", conf) - - def _error(message): - test(False, "Publish Successful", "test_2", conf) - - pubnub.publish(channel=channel, message=message, callback=_callback, error=_error) - - def _callback(message, channel1): - pass - - def _error(message): - test(False, "Error in subscribe", "test_2", conf) - - pubnub2.subscribe(channels=channel, callback=_callback, error=_error) - - pubnub.subscribe(channels=channel_wc, callback=callback, error=callback, connect=connect, presence=presence) - - -def test_3(pubnub, pubnub2, conf=None, msg=None): - init(conf, "test_3", 6) - - r = get_random() - channel_wc = r + "-" + "test_3-ab.*" - message = msg if msg is not None else (r + "-" + "test_3_hi") - channel = r + "-" + "test_3-ab.d" - - # Asynchronous usage - def callback(message1, channel1, real_channel=None): - if 'action' in message1: - test(False, "Presence event received without presence callback", "test_3", conf) - return - - test(message1 == str(message, 'utf-8'), "message received", "test_3", conf) - test(channel1 == channel_wc, "Channel == wildcard channel", "test_3", conf) - test(real_channel == channel, "Real channel == publish channel", "test_3", conf) - - def error(message): - print("ERROR : " + str(message)) - - def connect(channel1=None): - # print(channel1) - test(True, "Connect Called", "test_3", conf) - test(channel1 == channel_wc, "Channel param in Connect same as wildcard", "test_3", conf) - - def _callback(message): - test(message[0] == 1, "Publish Successful", "test_3", conf) - - def _error(message): - test(False, "Publish Successful", "test_3", conf) - - pubnub.publish(channel=channel, message=message, callback=_callback, error=_error) - - def _callback(message, channel1): - pass - - def _error(message): - test(False, "Error in subscribe", "test_3", conf) - - pubnub2.subscribe(channels=channel, callback=_callback, error=_error) - - pubnub.subscribe(channels=channel_wc, callback=callback, error=callback, connect=connect) - - -def test_4(pubnub, pubnub2, conf=None, msg=None): - init(conf, "test_4", 18) - - r = get_random() - channel_wc = r + "-" + "test_4-ab.*" - channel_group = r + "-" + "test_4_group" - channel_g = r + "-" + "test_4_group_channel" - message = msg if msg is not None else (r + "-" + "test_4_hi") - channel = r + "-" + "test_4-ab.d" - channel_n = r + "-" + "test_4-a" - - def _callback(resp): - # Asynchronous usage - def callback_wc(message1, channel1, real_channel=None): - test(message1 == str(message, 'utf-8'), "message received", "test_4", conf) - test(channel1 == channel_wc, "Channel == wildcard channel", "test_4", conf) - test(real_channel == channel, "Real channel == publish channel", "test_4", conf) - - def callback_group(message1, channel1, real_channel=None): - test(message1 == str(message, 'utf-8'), "message received", "test_4", conf) - test(channel1 == channel_group, "Channel == group", "test_4", conf) - test(real_channel == channel_g, "Real channel == publish channel", "test_4", conf) - - def callback_n(message1, channel1, real_channel=None): - test(message1 == str(message, 'utf-8'), "message received", "test_4", conf) - test(channel1 == channel_n, "Channel == channel", "test_4", conf) - test(real_channel == channel_n, "Real channel == publish channel", "test_4", conf) - - def error(message): - print("ERROR : " + str(message)) - - def connect_wc(channel1=None): - test(True, "Connect Called", "test_4", conf) - test(channel1 == channel_wc, "Channel param in Connect same as wildcard", "test_4", conf) - - def _callback(message): - test(message[0] == 1, "Publish Successful", "test_4", conf) - - def _error(message): - test(False, "Publish Successful", "test_4", conf) - - pubnub.publish(channel=channel, message=message, callback=_callback, error=_error) - - def connect_group(channel1=None): - # print(channel1) - test(True, "Connect Called", "test_4", conf) - test(channel1 == channel_group, "Channel param in Connect same as channel_group", "test_4", conf) - - def _callback(message): - test(message[0] == 1, "Publish Successful", "test_4", conf) - - def _error(message): - test(False, "Publish Successful", "test_4", conf) - - pubnub.publish(channel=channel_g, message=message, callback=_callback, error=_error) - - def connect_n(channel1=None): - # print(channel1) - test(True, "Connect Called", "test_4", conf) - test(channel1 == channel_n, "Channel param in Connect same as channel_n", "test_4", conf) - - def _callback(message): - test(message[0] == 1, "Publish Successful", "test_4", conf) - - def _error(message): - test(False, "Publish Successful", "test_4", conf) - - pubnub.publish(channel=channel_n, message=message, callback=_callback, error=_error) - - pubnub.subscribe(channels=channel_wc, callback=callback_wc, error=error, connect=connect_wc) - - pubnub.subscribe(channels=channel_n, callback=callback_n, error=error, connect=connect_n) - - pubnub.subscribe_group(channel_groups=[channel_group], callback=callback_group, error=error, - connect=connect_group) - - def _error(message): - test(False, "Channel Group Creation failed") - - pubnub.channel_group_add_channel(channel_group=channel_group, channel=channel_g, callback=_callback, error=_error) - - -pubnub = Pubnub(publish_key="ds", subscribe_key="ds", - secret_key="ds", ssl_on=False) - -pubnub2 = Pubnub(publish_key="ds", subscribe_key="ds", - secret_key="ds", ssl_on=False) - -pubnub_ssl = Pubnub(publish_key="ds", subscribe_key="ds", - secret_key="ds", ssl_on=True) - -pubnub_ssl_2 = Pubnub(publish_key="ds", subscribe_key="ds", - secret_key="ds", ssl_on=True) - -pubnub_enc = Pubnub(publish_key="ds", subscribe_key="ds", - secret_key="ds", cipher_key="enigma", ssl_on=False) - -pubnub_enc_2 = Pubnub(publish_key="ds", subscribe_key="ds", - secret_key="ds", cipher_key="enigma", ssl_on=False, daemon=False) - -pubnub_enc_ssl = Pubnub(publish_key="ds", subscribe_key="ds", - secret_key="ds", cipher_key="enigma", ssl_on=False) - -pubnub_enc_ssl_2 = Pubnub(publish_key="ds", subscribe_key="ds", - secret_key="ds", cipher_key="enigma", ssl_on=False, daemon=False) - -# run_tests([ ("[EMOJI][ENC]", test_4, pubnub_enc, pubnub_enc_2, "😀")]) - - -run_tests([ - ("", test_1, pubnub, pubnub2), - ("[SSL]", test_1, pubnub_ssl, pubnub_ssl_2), - ("[ENC]", test_1, pubnub_enc, pubnub_enc_2), - ("[SSL][ENC]", test_1, pubnub_enc_ssl, pubnub_enc_ssl_2), - ("", test_2, pubnub, pubnub2), - ("[SSL]", test_2, pubnub_ssl, pubnub_ssl_2), - ("[ENC]", test_2, pubnub_enc, pubnub_enc_2), - ("[SSL][ENC]", test_2, pubnub_enc_ssl, pubnub_enc_ssl_2), - ("", test_3, pubnub, pubnub2), - ("[SSL]", test_3, pubnub_ssl, pubnub_ssl_2), - ("[ENC]", test_3, pubnub_enc, pubnub_enc_2), - ("[SSL][ENC]", test_3, pubnub_enc_ssl, pubnub_enc_ssl_2), - ("", test_3, pubnub, pubnub2), - ("[SSL]", test_3, pubnub_ssl, pubnub_ssl_2), - ("[ENC]", test_3, pubnub_enc, pubnub_enc_2), - ("[SSL][ENC]", test_3, pubnub_enc_ssl, pubnub_enc_ssl_2), - ("", test_4, pubnub, pubnub2), - ("[SSL]", test_4, pubnub_ssl, pubnub_ssl_2), - ("[ENC]", test_4, pubnub_enc, pubnub_enc_2), - ("[SSL][ENC]", test_4, pubnub_enc_ssl, pubnub_enc_ssl_2), - ("[EMOJI]", test_1, pubnub, pubnub2, "😀"), - ("[SSL][EMOJI]", test_1, pubnub_ssl, pubnub_ssl_2, "😀"), - ("[ENC][EMOJI]", test_1, pubnub_enc, pubnub_enc_2, "😀"), - ("[SSL][ENC][EMOJI]", test_1, pubnub_enc_ssl, pubnub_enc_ssl_2, "😀"), - ("[EMOJI]", test_2, pubnub, pubnub2, "😀"), - ("[SSL][EMOJI]", test_2, pubnub_ssl, pubnub_ssl_2, "😀"), - ("[ENC][EMOJI]", test_2, pubnub_enc, pubnub_enc_2, "😀"), - ("[SSL][ENC][EMOJI]", test_2, pubnub_enc_ssl, pubnub_enc_ssl_2, "😀"), - ("[EMOJI]", test_3, pubnub, pubnub2, "😀"), - ("[SSL][EMOJI]", test_3, pubnub_ssl, pubnub_ssl_2, "😀"), - ("[ENC][EMOJI]", test_3, pubnub_enc, pubnub_enc_2, "😀"), - ("[SSL][ENC][EMOJI]", test_3, pubnub_enc_ssl, pubnub_enc_ssl_2, "😀"), - ("[EMOJI]", test_3, pubnub, pubnub2, "😀"), - ("[SSL][EMOJI]", test_3, pubnub_ssl, pubnub_ssl_2, "😀"), - ("[ENC][EMOJI]", test_3, pubnub_enc, pubnub_enc_2, "😀"), - ("[SSL][ENC][EMOJI]", test_3, pubnub_enc_ssl, pubnub_enc_ssl_2, "😀"), - ("[EMOJI]", test_3, pubnub, pubnub2, "😀"), - ("[SSL][EMOJI]", test_3, pubnub_ssl, pubnub_ssl_2, "😀"), - ("[ENC][EMOJI]", test_3, pubnub_enc, pubnub_enc_2, "😀"), - ("[SSL][ENC][EMOJI]", test_3, pubnub_enc_ssl, pubnub_enc_ssl_2, "😀"), - ("[EMOJI]", test_4, pubnub, pubnub2, "😀"), - ("[SSL][EMOJI]", test_4, pubnub_ssl, pubnub_ssl_2, "😀"), - ("[ENC][EMOJI]", test_4, pubnub_enc, pubnub_enc_2, "😀"), - ("[SSL][ENC][EMOJI]", test_4, pubnub_enc_ssl, pubnub_enc_ssl_2, "😀") -]) diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 00000000..901ba7eb --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,8 @@ +pytest +pytest-cov +codacy-coverage +pycryptodomex +pytest-benchmark +twisted +flake8 +-e git://github.com/pubnub/vcrpy@twisted#egg=vcrpy diff --git a/requirements-pypy-dev.txt b/requirements-pypy-dev.txt new file mode 100644 index 00000000..716f2ab7 --- /dev/null +++ b/requirements-pypy-dev.txt @@ -0,0 +1 @@ +tornado \ No newline at end of file diff --git a/requirements26-dev.txt b/requirements26-dev.txt new file mode 100644 index 00000000..6eb6461c --- /dev/null +++ b/requirements26-dev.txt @@ -0,0 +1 @@ +mock==2.0.0 \ No newline at end of file diff --git a/requirements27-dev.txt b/requirements27-dev.txt new file mode 100644 index 00000000..189e222c --- /dev/null +++ b/requirements27-dev.txt @@ -0,0 +1,2 @@ +tornado +pyopenssl \ No newline at end of file diff --git a/requirements33-dev.txt b/requirements33-dev.txt new file mode 100644 index 00000000..716f2ab7 --- /dev/null +++ b/requirements33-dev.txt @@ -0,0 +1 @@ +tornado \ No newline at end of file diff --git a/requirements34-dev.txt b/requirements34-dev.txt new file mode 100644 index 00000000..c680ba33 --- /dev/null +++ b/requirements34-dev.txt @@ -0,0 +1,3 @@ +pytest-asyncio +tornado +aiohttp \ No newline at end of file diff --git a/requirements35-dev.txt b/requirements35-dev.txt new file mode 100644 index 00000000..c680ba33 --- /dev/null +++ b/requirements35-dev.txt @@ -0,0 +1,3 @@ +pytest-asyncio +tornado +aiohttp \ No newline at end of file diff --git a/requirements36-dev.txt b/requirements36-dev.txt new file mode 100644 index 00000000..c680ba33 --- /dev/null +++ b/requirements36-dev.txt @@ -0,0 +1,3 @@ +pytest-asyncio +tornado +aiohttp \ No newline at end of file diff --git a/run-tests.sh b/run-tests.sh deleted file mode 100755 index 18034fba..00000000 --- a/run-tests.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -set -ev - -nosetests python/tests/test_cg.py -nosetests python/tests/test_grant.py -nosetests python/tests/test_history.py - -if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then - python python-twisted/tests/test_publish_async.py - python python-twisted/tests/test_grant_async.py - python python-tornado/tests/test_grant_async.py - flake8 --ignore=E501,E265,E266,E712 python-twisted/ -fi - -flake8 --ignore=E501,E265,E266,E712 python/ python-tornado/ pubnub.py diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 00000000..eee25dd9 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +pip install -r requirements-dev.txt +if [[ $TRAVIS_PYTHON_VERSION == 2.6 ]]; then pip install -r requirements26-dev.txt; fi +if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then pip install -r requirements27-dev.txt; fi +if [[ $TRAVIS_PYTHON_VERSION == 3.3 ]]; then pip install -r requirements33-dev.txt; fi +if [[ $TRAVIS_PYTHON_VERSION == 3.4.4 ]]; then pip install -r requirements34-dev.txt; fi +if [[ $TRAVIS_PYTHON_VERSION == 3.5 ]]; then pip install -r requirements35-dev.txt; fi +if [[ $TRAVIS_PYTHON_VERSION == 3.6 ]]; then pip install -r requirements36-dev.txt; fi +if [[ $TRAVIS_PYTHON_VERSION == "nightly" ]]; then pip install -r requirements36-dev.txt; fi +if [[ $TRAVIS_PYTHON_VERSION == "pypy" ]]; then pip install -r requirements-pypy-dev.txt; fi diff --git a/scripts/run-tests.py b/scripts/run-tests.py new file mode 100755 index 00000000..cbde55e0 --- /dev/null +++ b/scripts/run-tests.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# Don't run tests from the root repo dir. +# We want to ensure we're importing from the installed +# binary package not from the CWD. + +import os +import sys +from subprocess import check_call + +_dname = os.path.dirname + +REPO_ROOT = _dname(_dname(os.path.abspath(__file__))) +os.chdir(os.path.join(REPO_ROOT)) + +try: + version = str(sys.version_info.major) + "." + str(sys.version_info.minor) +except: + version = str(sys.version_info[0]) + "." + str(sys.version_info[1]) + +tcmn = 'py.test tests --cov-report=xml --cov=./pubnub --ignore=tests/manual/ ' +fcmn = 'flake8 --exclude=scripts/,src/,.cache,.git,.idea,.tox,._trial_temp/' + + +print("Version is", version) + + +def run(command): + return check_call(command, shell=True) + +if version.startswith('2.6'): + run( + '%s--ignore=tests/integrational/tornado/ --ignore=tests/integrational/twisted/ --ignore=tests/integrational/asyncio/ --ignore=tests/integrational/python_v35/' % tcmn) # noqa: E501 +elif version.startswith('2.7') or version.startswith('anaconda2'): + run("%s,*asyncio*,*python_v35*,examples/" % fcmn) + run('%s --ignore=tests/integrational/asyncio/ --ignore=tests/integrational/twisted/ --ignore=tests/integrational/python_v35/' % tcmn) +elif version.startswith('3.3'): + run("%s,*asyncio*,*python_v35*" % fcmn) + run('%s--ignore=tests/integrational/asyncio/ --ignore=tests/integrational/twisted/ --ignore=tests/integrational/python_v35/' % tcmn) +elif version.startswith('3.4'): + run("%s,*python_v35*,examples" % fcmn) + run('%s--ignore=tests/integrational/python_v35/ --ignore=tests/integrational/twisted/ --ignore=tests/integrational/asyncio/' % tcmn) +elif version.startswith('3.5'): + run(fcmn) + run('%s--ignore=tests/integrational/twisted/ --ignore=tests/integrational/asyncio/' % tcmn) +elif version.startswith('3.6') or version == 'nightly': + run(fcmn) + run('%s--ignore=tests/integrational/twisted/ --ignore=tests/integrational/asyncio/' % tcmn) +elif version.startswith('pypy'): + run("%s,*asyncio*,*python_v35*,examples" % fcmn) + run('%s--ignore=tests/integrational/asyncio/ --ignore=tests/integrational/twisted/ --ignore=tests/integrational/python_v35/' % tcmn) +else: + raise Exception("Version %s is not supported by this script runner" % version) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..c43582b6 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,6 @@ +[tool:pytest] +norecursedirs = benchmarks + +[flake8] +max-line-length = 120 +ignore=E402 \ No newline at end of file diff --git a/setup.py b/setup.py old mode 100755 new mode 100644 index 295969e5..a573296b --- a/setup.py +++ b/setup.py @@ -2,12 +2,12 @@ setup( name='pubnub', - version='3.7.7', + version='4.0.8', description='PubNub Real-time push service in the cloud', author='PubNub', author_email='support@pubnub.com', url='http://pubnub.com', - py_modules=['pubnub'], + packages=find_packages(exclude=("examples*", 'tests*')), license='MIT', classifiers=( 'Development Status :: 5 - Production/Stable', @@ -19,8 +19,9 @@ 'Topic :: Software Development :: Libraries :: Python Modules', ), install_requires=[ - 'pycrypto>=2.6.1', - 'requests>=2.4.0' + 'pycryptodomex>=3.3', + 'requests>=2.4', + 'six>=1.10' ], zip_safe=False, ) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/fixtures/wild/domain_redirect.yaml b/tests/fixtures/wild/domain_redirect.yaml new file mode 100644 index 00000000..e72a51a3 --- /dev/null +++ b/tests/fixtures/wild/domain_redirect.yaml @@ -0,0 +1,210 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [vcrpy-test] + method: GET + uri: http://seomoz.org/ + response: + body: {string: !!python/unicode ''} + headers: + connection: [Keep-Alive] + content-length: ['0'] + location: ['https://moz.com/'] + server: [BigIP] + set-cookie: ['visid_incap_157525=R/YNzv8NT3iSCATJ5aSyYsDBAFgAAAAAQUIPAAAAAAA4woOieqT1uc4Mav7tDwqX; + expires=Sat, 14 Oct 2017 08:29:36 GMT; path=/; Domain=.seomoz.org', incap_ses_163_157525=+X45MiBU9S4P/Q5jAhhDAsDBAFgAAAAAolJy4rjGd1/X5X1Hm9JecQ==; + path=/; Domain=.seomoz.org] + x-cdn: [Incapsula] + x-iinfo: [8-15965355-15965356 NNNN CT(38 -1 0) RT(1476444608757 1) q(0 0 0 0) + r(1 1) U11] + status: {code: 301, message: Moved Permanently} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [vcrpy-test] + method: GET + uri: https://moz.com/ + response: + body: + string: !!binary | + H4sIAAAAAAAAA+1963bcNrLufz0FpmdW5OwR2bw22bIkj6w4o8zYcRI7zs7KydJik+huWmyyTbJb + l4zX2q9xXu88yfmqQLLJvkiyrfzbtkWBQAEoFOoGoEAf/eWb12dvf/3hhZiWs+Rk7+gvmvZbPBZJ + Kb57IYa/n4gjKhBhEhTFcS9Lolj2kPmX32QaxePfNW1VZ8J1TOP3E8qpayZBOjnuyVT7+Q1X1LRu + 5akMopM9IY7KuEzkyavs9lC8efFavMnG5VWQywPxNsuSQgRpJH6SRbbIQ1mIcZaL57IsZS5eBfml + LON0ctRXTewdJXF6KXKZHPeCBCBpUMqeKG/mEhnzeRKHQRlnaT8vir9fzxIUUb3j3k9v3ghLN3pi + msvxcW9alvPDfn8sZTTPZVHoM9mfZbejJJv0RB/dzGQZiDSYoeoyllfzLC97IszSUqblce8qjsrp + cSSXcSg1fjkQcRqXcZBoRRigQ5Oawchb7USyCPN4Tui1mnoehJcyEqMbNBAtijK/0RKQDWMWUYC6 + RJtyKkHrfCKLEhVnswW6uhHZmIhZiCxlgDlmQ5YHAlQWo0WcRIUombrlNCjFLLiU6GGULdDerCar + kEFxo4s3ZZCX4gb0F+NcSlHmGAhqR8HNX5gcNIcrwodBmqUgdNKmZgFygoI68FNV9tpzVUxBwHBR + ijik0XerRaE/jdJidhvMlzM9TLJFNM5BHx3j6Y+DJdXR8eh1pr8obxJZTKUsP6k98Losi76qTRj3 + Mf/zLC3ipdTDong2lrYjA69mqlJel33k98RFGKV4zxeSB9ghCbGe1MpsEU41QlcDV4ES86yQUU8U + 8a2EhLnetet9DrLxLMDc16QgpLUqrbmePk8nzzzbcQPX/UzEPOvasx4ZMc9ixALXHw5HwWciZprO + NX4eGTW0yLj5o0gOA+NzcXOAm/PouDkKt6HhyoEX1aLU1SR5NsrKoqVE0iyL5rWk0h8FHU6DHB0e + 9xblWPOrca6KCWVNfljEy+Pef2s/n2pnGCPU5yiRrba/e3Eso4k8CKd5NqsU285mzlQt7S1kp9UG + CxGZjKcNTttRUuMrr2JS/4dFXLZb+QdU20aNeZ7NZV7erGoFYQgtV17EUauu6Q5c0zOMO+qPR4dz + zEu3nu/4Q8seWI57d80gmsVpe0pMA38cxzfcoe8M76q9lHk8vtGWZntCLTPJ4uU/z93+r7+eTkbT + y/+Of5xNZf+Xq+uF178d3/5wPvn27Yf3iXF8R9OTLJuAk4mOGndTmcdWT+OL6TvHSa9+CMtvPvxq + T+c3qWt9Gy1gmaN/jy8X8b/OXd//8cezNz/f0dGsWAZJDIMldaM9jsHpwDdO7YHtDp1vvzlznlvD + bwzTObNcwx3YZ99UTXbsLcRJ5rnMW80s0iIYS22RJz0Shw1PhuRDGVdR5OFKGKGx3xeVEMKASrJO + /eB9cN1P4lHBHOkW03jZt3VPt1bvOiZTf1/0To76qlnyhFqezd4uQ7RuM9Z0g5WVt+F44S0/3F4V + 68phkmSjIEmDZd/ULWBTmagmm5GiVoFMX7lWe0ejLLqpPbgp5FPQQ4MrIa/FbJIihXkv4WCsrJyW + BDD1pVC/tEu4K3GUFSHmsseUjOKliCN4OXkApVI1nmZahqbGSXbVU1zA7h2cNAJtkGzgqfNZEKdi + 1SYPCH5QXMBXuTmEE4EOj4rlRMBTS4vGK7u6utKvbD3LJ30LQtQHBMHdzEZZIsgbe55dH/cMYQjT + x78eY8CDJQ06yrJLgCt/scmpHUhxBA03FajwyhzoliuMc9MKNVDcFoZm6TZyB4aj2cLUXcs+86sc + W3gMIgZUQ/fcM93zURt/qwRlvjOdEBnDgcuZlLV6nA9CdOMA1KKOTUiKStFjiu5CQ2MAKtGaEk5N + HTxCarjqSWs60FTHhBLIobKBq8mQaoi3rwbCpBYY81DTTRvD0KhxHcZQJSzVCJCg4jq/KroT96WJ + Mg/NDmxNdzwPGDkGkkjxAJC4JWqjHpMHzXO/VduMF+ZA9x1PEAkGqDW0uS6qvrMVVveQhockqIpq + mcfj3PbghUOImXe28xBatZw1HoJkFrLLRJy1jYts3TGFab2kdizdHdZPIGuJqpQeinpcZhr8tHgu + 63KD4Y1VC4waFX3uIDKS/UIp/O5YWiWrIYVxHiZShGhyoBusxqHZ4bDmsGZAoQ0wvA/AtLZBtOiG + MZqe8M5NH2w3NHmCDRc851uORjl4+LbHyXNOnlGRkjjfHTDthuYSFDP9M4MoTb6TT9xJZPNt6Abn + 3F66iQtUXM2d4peTaOAg/FvauuGfkfpANeEDXJhDMQQP4ve5OUhc4S41EkvTC5ndDW4U+A1Ugnp/ + 5+NxBqw8yraAgi88UQ3t9pWH3wPiDcKJ0u9M69x6Z01NB3ifU7F/C0pAYKeavUQ1/yXkhHABYYyl + Zk8hiAOXEbArDDyNh7+izrvhuWUt/QdwiYlqgzUuiWRSBl324KwWq2fJzQSLzHkWpyU5vrbu+oMD + zLBlDIR/APUIzWiBUZ060zzAtHhDC6WQ3iFo6lY5n8fKEvyasH1q41nnbpFK0BSUBscMMR2stShl + CNB9EBIdTcOuCvF3ahkhZxGkpvI1653DTGmSKkOWZtFfqFKMEDMG+vt24ugeunJIX4V4QO96+sD3 + oP9YV3kDShVQyqRUXZWlUZ6qqXHNlxamERQ0ziE0HkpM29LAlyZYiHgDhfZSA296CdQJmgGfWail + ipzbGenWgasNdM/xXgIfXzcGzOkYEaqhiOpZDOAwwzlgNs+0E7TqDl1U8CwbGthCi+YQWtkdWirh + g/HRE2qaIAgI9ZnKCB4RbdYkazNY5W6fQQiBB2FxEyL8uXfmVpPoCk7ABiztepLJ2tXl3tJ0t0yy + 6W6ZZJIxe3s+d7t0b2cadAHE1pvakNptDRtLGwYOKCyBLQlHonk0sUI9SYcrTe7Q7JBJcN4Np9a7 + hxBzm4szzRbr5omztpknl90Pf6oBsaVGit2dwm7COKo8qDBnaEHDWhbbYJtNsDFkU05azhxA4wyI + bQ1yOyyDnAbws+cAgKyy7730dOJ0MsMDDzR0Bz7pJ8NXSagGXxgJOVADcJJpOIBzfN11wLcOCwqQ + 8AS3xd2gF/Rv2x53TDj4lJoSZha0qI9JISvhTmkkA304hKJ2nJdDyJ9FnpvnsJeDH0LCIXw5hS6J + n3Xb8ARVOyfnYgkhMKklRZ8BzeE7kqnP5PWZTBfd2aGcbTzuCJscIhckdTBLGlyBcxOYuy7NAmXi + aRb8m4qnmCMuFKbgGsK8fcWexqM0Y5lf3MznUWwu0zBe0w0qbxvVbN0eYvawMA+RAp/44A7gNwSz + D8hvcBziYvA1cSoYdwhGhTdvMbdaL03oRIC6hn2GpGkSV5Mu9eF9kIn0wP7QgGATi0bloj2XEceA + l5p5ziaNFLjjU4vuS8bIYpSIMfHLpqfnk772SOAhFZACtpI2KWOPH+qVHhbpDx3iAffVtdrVTAyP + rD0GuapocYbHhfAgIInUGAys6yeEkslDJUM9JB8Z8ma4PkkZ+cbgbNOHiNkWuen4cUEgm1Y9JDmG + RSkYGFe50+QvebrtkWCS7wHDRwbOYBG3fQcSTthgCgyP/mEYpkWiTqRPqHcsJlxgOWOLQ7Pjk3A6 + fvVDg7DJFVIuFcyWZ3j08In1oGXwQ7WA7sBVriGAaaqG6sdik+pC71h482wv8Wkq6TEQLUpSBnl6 + Dq2AwAc0MjJ1DisHi6tSG5+plOd5Fi3Csljj4ip3q5vtKP/YYf/YXvegN0q31TXbxSut76mlFubK + ZS4B64HvhF1otnrVbF7ickoViQpM2OBhgiIy+ZbP9sBlxW7SEk53C3oR1Uv9o9WZWv2i1S/mgzTp + NpoWMsjDaZeiKm+bqfNpvQvJJLPm2vx0zngF5gleJtAqGBww4N8evDtY8YFGCwfNo79vDHKtaI3h + FZzw6C+8gwEtN4ijiV/ARQ4JJY3LtV9ynwp71T/8fihkIh/WGDa8T8KEpByGlFL4eWNjKe8z6Tym + KPIEQwhK1fC+qODp9wNICOPdVq20r5xkk6yhX52xhXgelPfgpW/BJgobDgKkmIUUMuLTmgfmnXwj + yLlFrqM9OHMdfUAuEJ4D4Qx15V9DXIlUqMfrcRJaE5TDMsg2SfgG9js0T/SjTRcDeoeEEELtwmmG + mwx44iLT8xKNPBMfDqwNFesJWkB5ZwMwKcszUcqxSLbRAE8IOPfcWJrDKW0BeCG5NRBxy6FOMRSL + HHYyEliEJUNiWqy1X9Jy0YIDYAxDVnQYiAMV5vuYSVKM0PYmuQVo40wNBmqW7LYaIVI8ZKhY+EA2 + r+gdzJjt2ZSgJT4N0yYVQ7shBqjogOXOB8OXLq0Up0RpcohIC4HWsBqWxnrZH0Kt+b4DT+TcMyGT + 1oC0HK16B8Ra3gCMSpiZTLWBX6WBNG3u2Ox6gjosCUNyvJpSquzTXhbk2xJcuUpTcaFxLU5rq+w1 + /qNdOTyjeNns77V2/kZBXu8SrpXQnm4Qp7Iu34RQLFsVojjYccq4AqHd3+Wks/PYsH4LCGBwksU1 + bd0eqjb/uhNYDXCFRT9o8FWDPkqDDtp41WgXW+13LpJq+EncBopLORN1ojEOYkWLHYPtr6zLD41F + qTA66ifxAzqjs+0HdMRH4CfP8fzUDoIR7Srf3wPD9U5O6ddGH0d9otxRH9Q86c5csk5sLB7zoM0o + XeRq29Ge053U5fM4VeMil8Ui4eO17kibWII3my1vciDZKrFhyDpVdvHj7hprTNlhy/Y8bSEHOpHE + AyFtjt8JiI4V5oVMZFgGdBz4ICpyTVAx2026l9z4V+IsS1O0/Uk0VBv7D6fgFvgN+lGf8yBtdzqN + IzoiySdSi7KrtHdS4RzM5k9rvNEOan3pRIit3d1ZGWpAVL+V5mBlmyzk2oSJB/J91QwlmjmiABIo + md5JlfiEoWVhkGziV8oq95Px4wb7dO5Efk4Hw5dUpHDk5MOxVFExa/i0ke41x+j3oMctNVh9S6Ez + HNDUO1mlt+DVHvk6ASdxus6zwVaVO1qUZVbX2Ek+bu4lFH+cdhDpkkgp3Sq9zaxlo7ijBNZNNsv4 + Xfa4K8lqg+QBlnk79IZprpwQ9UKWY6+Tv9NLifLgSuZilWwpvyguiDEiOm2904XZcF+oCW0sg3KR + c/VtBKuKNTq+vd9irhQr/P4yz9LJmkK5rLXUmxevQR0GOWlN+PM8CyKZqhAvisC7iuH3r8LA8k7w + XZAkoriM8UzkUibFYdXKiOLP5nkQljFAD5qANVEs8qW8EZXhPBBXchSnQV58lY6K+dMgjVRiluVS + r8c6yk+2JOftgU1yNMpyygOCyxAtgzSUahgr7BnX4vCoP38gJVmtaE0DvS3UZE3WCj8MTjYtRSHn + QR6UGfjgP12LcE//VVzDnRhUkTSP1vNIQheAZwttsiCTU2ZakYUUsDiTURxsweANF4tXVNzy0+4T + KsX/3Emxg/s3JvafshQFBSHKiFnzsCXTnzSiNd9DDeTtVIrnFfB+If5J0KLMlLAE9/TS4pgdTSte + YfkjbjzDrMn8/oa3oP8Qvtg1mIpflKR1GLczWV03Ws0VhZRW6rlSgwUabS3hWpZi12iasNT+h7ad + 2KWtfmR36rTRVU0N4oQgLYAEdFGezTi0lYz8Wd1Bo+i3rU7uxXMqk/lDEDwHnDhfjDYxVIp2ml0R + 1clqVW4Suw5fhlxDxIdg2BCkck1fLDH9xSa+lcuqVL5rGP8WWUo+aKVAQegvQ1otGO/Hl5aUW9D7 + SQZRM8kEwxHPv2YLvO9CrF4kdlWRCoqSOYVF1VFbyyAXF8BBHIs/uAmZLg9Fr/L0KDDigLOnWVEW + hxVME8wEyM8K3aoanVG8+8a2RVUWtIqCNEhuYFQLvQuUFXKzAYpX5rUhe599wPQr8PHV/eCAqcHz + AM7WffAEpGGdHV7C3VH1Lq+ie6tdypurLI/gRY3HcQiv4KYeUkpr7ftHBW8ccLAUNKNV3RDKKbm3 + KkNhzVGUVbVgHm+p1Cx9UNzQbwvcOEuSDOroKgMTKzg2BltAlZFQMB+CLQBtJVkNSWntrbBc0u8B + 7iMBk9u1lIpHP1ZMJMuA2dTc2/v4dO/JeJEyUz/5mqGI+a/A+Vdw1LKrp1VOjJw1QFXAUhJl4QIO + d6mHOVxU+SKR9PZkXwnU/tdPlYSkOkU6An6fYx3fB8uggmgAguImDQFBAfNNZpFT1v5nBkRyD+sR + ke+Lqk8aQ9EewkSWFf7F85u3weT7YCZXI/nN+F3VK3Q4UwD6PoukHqeFzMvnEn6wfDJJD0TBQ/5I + j3gsnlzpf6tpFj9RRQIusmzR8SohmuvQsnC6VRf12zbSC6rw1Vd4Pqmo2zRd9ftx7+PXlLEKRmVl + WGu7rn81yq7h2CVxJEjJBznkiKKiq12CIIlpAcMuyhbnrFnZwLbN3a07tJM8jnasBMMMzkWWVM3D + lVPbGiXshtQ+LMi9y4vuSm5q1pUZlro1xCjD0nZmrLY6ydL+QEJIS4Vfyfl/rUzYu7iIsTSFROnQ + /ma3aaduOkjKqk27d/K6bfzIX4sLulOjbg7JSGc7VFQXlPjaTCHiUl2UQR9Op4/5iurU/GBz6a5s + 5mr7tjNcLSwDSGekEtWKfp7HQO5G3MiEY23fyK6LsbGOn7dmo7sUXvnQd3rtWD7IpHY+RTHD+o9U + floouRAjaP9JTjeHNGjyPIBBn0xL0s03GyvgFQepTQM0myTBHAJCnBEr4m/jwu50ER/0Tmz3wDCM + v4sQy8xsRi5h5W8dKDc3TsNkQVelDlczs86r4j0qU3y96q5pi3f8i839xHg26caQf8KFDoq97g+H + WPhgfIVeLCfPotD2DbpjglEd95qy9dtEj41ERjIPVcA4OL45GDhRhcPrqujPRiEPbrN8HBdTRQd3 + bI8GboVDU/ZnIxEEKC0qKvihEzkVBqdc8Kd3H0U2dz4y3OEolHXnyP7TOaDSi9T7OLClSZGfigFU + yW4ENh1r+tV2rzmw/z6JV7qhkGzu2tCkU7bBbhHeUcBL5K2WiMwN7V0sZlqWSm0aJOMdhombogdd + ginp5mKl5caJvNZk2jZonYp5dqUVEgsrrbySyRKmoK26xMrWtlQknUppEcyLsrhKh/bqyw8tQJ6t + Q7HIkyf722bw8kpqM+j8RNIkwvVq95LLOfyzQ5FmVbJTOs+KmKh+2NKxR1O7o2Hht2nX1yt1TtjS + /iJtNdXIwoWHplTGDV7m/BrOVcm3SSaj4InlDg7E6mHoNnCcg+dJIwvTmF8/7Z38W60ExIvreQKf + Kq+2q8BN9iZWvROAAXeYZVoSVnVh8mD3eV34SpY51kniNYphDt7CTsI3OIcJ1ZsW52sbL51TjmZl + Iit8GotcmV/wa5ZGZID52KKxzgEWMtf1rruiSA+rmRB+w+Vx72ISfNDni2L65Lf9C14u8XJ8/wC+ + Lubygi/k0JtqgFJw9C8wxRdqivd//xrEUhdxFamICN9nV80OzrwWxJ2cOqbLPTWjfoFR7zI5y4jK + mcVRVJ0DHE2tuuuuz9ackE2t9mz8Ir/6qz18Cpdq1Fx43nmtWTn4G/sU3ZvO4itMUzF9Snsa5CYw + f/DNP1BOF//KoKJiVaF930HQBndxSRvd1fZ0RLe49b3GlVr36jCoFS+ttmi+aPKbZi7eA081+TXG + 5N82mzs0/Ztzv8vP+3MUJO/MVhnkxeWSqCnvUpqPwYrKAf4iZuS9uh2suJSimFP3dLwoAhHJEOat + ZpkSmIVTIA92VLsLzF/TANUCkWTkoopiSncKxbe0Txmn4KhZQGv0Zn+W+6n2aHkRA9eVD0YoHYSh + LFjV0YRfptlVQvdsQY1CHhAOuazqp5kYqe8hgPNDyWc0lWwQTm9evFacLKHlmMe3MrNyqVec3Dqt + /xJORgMXikCVCnvxutqQfFMF6gUnCj317JxlsBFqrybq04xOjTWUN05vvgR9bqwzALWdv0L+IZq3 + 6yNsdQsqQWTHgC3LJ/O1VfO1gM1kyl1N45JWU2WjdmV0KJojceaOf/IVYPHqRjxfgNvAckoe9u7Z + 0u3PbmDtVA2SXQxIavR1CD4/r/21L3ZTscDGHIIHCppLzGqKMtqy0ZikWHbHMmcnNjRsT4Zh5cR+ + wngb32sWXKuPdRwKy2LvpEVdu9ki2PCL2QRvkaV7znfuJ+JO10NNbO17PAaHx2lBYlZcoD3F52+h + XuFPgXy53HQyatX+gB2FysM/qXfdt3yNJYn+/r6gW4h7tOfV+wfbguuyV+17gmoFlO0soDu/vDPa + +wc1QuWv80mQxrdBs1nfo1viPYEi+hwA59C9cLFjq73HkX1i1VNkSRnPwzyElA3WOXQEsxSpA7k+ + bxBQWxfkGtNXGVSDcHBzZqv6vGCF7A8ZbGVyWgGoTdoa/oxck/yGwH6GZYcLBG+vlOtgzMnkXwAO + SqgsoQu6ED/BuoAWKP/ltC6ac8dnWcRoDH3TMOuiAqZMljVOKDVNOEsWROUUpuqv8J6aLebemFQV + fGJA/UTC9C2W6JBPNW7MT6zoTIkyO6SztH90aF1gak6ZMr+pzusZmSeLQldfI2Dh+DvMxKyavhYY + 3fwew8RR6BbDbYGpPvOwqxiMXgYwJrMa4GL6YVs3dPd9MVLYLAqZ98FN5z9ug6TjKyjvtN7Ch326 + 4Z4B+jsPe56RZxNj5tWZ0iZjKHNy2j5y6pXk+jYycF844rMPx39UORj97GPdyoeFzG+0OJ0vuKmc + PsRBe6/8LYVWhZ7aSG5tIh/1aRnfWnjXd/61cZbxqnF9I4BSqqx16b8C7kS3KJg6jmoj+IWLtQKc + ld5sXdqvR9lsdVq3xArvVsf3RBruiBcW/xsh/78R8p8UId/lsnvCdWuhqt7XYjOgdtZOS9pBjXcx + uwqx5iPEIFTOISXuiACsmt+Ig+XF2q5TjU+IUf+Te+RD3JPTH767v8MHtkwqs6CzmGUQ3pCzhNdq + XfODyny0rtRkvc9GBe0BjB5AtAc2rGJdKJLlziZVNMVOFRtm85ucfMfOpJzVuZB2cyC+IqinfDQj + vktDXYhTeJcMUFBcoMyXMtJ3sP6mW1l71ptGg1ZUFCoFD1wLcqy96tjXv64MyZ2xp9X3DlpStx54 + ugWsJcj1YuB+/FqY0Uf+aM3/NlNTsRFSWtUeB+XWmNL1SzPr5XnzUZ571cga+1ThOrxsr+KV1yN0 + NoKpH2Jv79l0bbaFf5JFs9L+wh43wj94UKeLKC7r6H0qe4SetkTg8BTzN6EefUSdEJwT+OWX4i29 + rcL7Hm2eCpnxypRj/2qWwMvOfjq64/M4byPU9nH4T10dqG/R1HsB4qXaaCgUXzwC/VQ/vGCfB3OZ + gwPjklerWpjIIF1ABZ9VOeJM5TxWt9UlIY04hHZPsHQqMzotUzT9SWWLb1X2ow2Wt6xgGmkLWPX0 + bZaLF03un8ktDZ+sPhj7WByjAhnrMMRHoNaOAOWTenv4//3P/60DeN92o5E/v89OHO4qRLSJt/1y + nZTDENG268kvWX5ZTLN57Re9rUoeQ8EGadTnb27OZBrJiPcHiXi8iYblcJBCY0EZrkD4WgN/wDKN + 5W5eX1112X7KDdPcV8a4gtrxWUE9m5fxLL6VyQ1j/L7ou75lWoZvmGtfDtzRyIO+UltFvNHyNEsi + NPzMtMcjR7qdXcpWZ3ur7tQA/vZkny6Z7H+tB/M5KPVkv1nrk6Oz2h1tf5bvab32rwP+0Ar7RQXa + oUC2J/v3Be9hJacbWpDMpwEWm/Web+VbjYvqSJvJ3+Be00l9V3otspC2AV4RfyxyOjX6GT4lRfV+ + fKpys3QcT3TeOUH2b793siNJQ6cPinRKsxmEY5FTHOMfPfpI5kW1l9k75I8pUmu9gx5/FZR3HQ97 + FYf+n/7qO4s1hNrr6eFVcii2Slf2Vb2E2WKepRch79NRBu080QdHD9NFklSvfOmmzgnoOC7hj5Jy + hWIxar4iXWWCAnt/e0LjoU9Qg6Z7bXbYW9uW3aSqilq8+KCwBi2a5H/+w6QCxLbQUpnI2QMjRgm0 + iv180sCTMeHTKpCozOiw8vi43gvriWerbTFMCiapvYdLB2W9r8XfRU//sCBdQOsLlkN+JQFsul2P + Q+XMKnp1gxp1kGwRgmCfGkpKlXbGklK3BwyiokY5ohOJmthqVx8Zf3wIwrA87M212WDxyy+L4ff2 + hRb8q3eQBCO6jNW7GM91rqOf1zz6EfU6AaJHadZSP80pTb8/j69lsk40zuyvdahP4vEz1efxli7X + 1YZQekOomJHjHn1qXdL6j778J9QH0ynFpzc/Uv9hAB+9z5e9sw0VsItZa26dBB+IU+lXxaVCdA5H + oD9P1YeA6RDk51PN9D1j4A01k44/tkB/k9F2KE1sFZ1BpNkBe0on0y9pXzgHMHHWHXCnaTjNWnBC + 7PG/deAzjkx8FxCoe7BPCo6/o7x/sL+MoZ24DUt1pP6ttRFE0XeTFMwW/STH1dksjSPLJzSOjT43 + 4FuDvg94P4NGJZVZL+nqXtbr8cLlB3AM3eBVABX+e9sUSxWKHjxEsQBsdxg6FW4JQQ9qNVTFoO+T + 0rlDJT1bBasXRbIPHbS/+krtPqmg/epcQ1vd4iCZQk9fEpl+V1R60ESlKyXSVvf3yU//v+g/eDj7 + 5vTt6W/iv/p8QUbhf1FFySjDAmyHQ8uwHGfoPu1AMZde0P3DWdFcLqhocFEGk6qoUymXzXH9RZYm + N82cAJ3ffz8hRFq6684h1IqMTmZU60FEmoyupDaHJ0HUX41m/UPOK22z7avEKkT5KV37JbW5VYd1 + w+P4rXKblHprkGwQ1OGDZItRIvkclx2kCk8Si3KaZ4vJdIVyv6F9/9kySBby2PiK1hDHr7//SiF/ + bBxVfPhn9nGi9HPlH29R0+SpLdDJFla7S705zQn1RTzj/4AjJt+yc4p9qAAON+Pk9h6r6c0orEdr + uhsW82jNroerPFRqqptwlzFbTfpV+3btW0N/7AG1t/FMZouylb33B2uwqKXBDsSY3u5XZAdK8e3W + 4cUdKrzoanC8V/eHoIxtPZgFt1kaXCmxv4x1WoNZnmf2R7MhK18x3qlEC4wAvX/cOxAm5mf9gk1n + xUbH10FJ4V16+/A5C9nR3EXxB09NeVWW1GKjr5TJjLHISpz3o2S/M80bjt1dGqq7pusop827h+2x + xX0olLB8Vl6TMThmPL6aU/Ktgqr/04wvwKDfh2nPHthVS/uszY1aKutBlI1kVKpj/7FvRHJku5b0 + h34QWn5ojGzfNYYyMv1Q+lJalmmasl+AKZMEbszLeKR5o9AbRf5gaFljLxqY3sDwPcfxAzkwLdPx + hnY0NsbSWjcod0/vRdMHr06fc5TRk86c9mmFzhdY+T9q+v+4tME0uWkAAA== + headers: + accept-ranges: [bytes] + age: ['19'] + cache-control: ['no-cache, must-revalidate, s-maxage=3600'] + connection: [keep-alive] + content-encoding: [gzip] + content-length: ['8536'] + content-type: [text/html; charset=utf-8] + date: ['Fri, 14 Oct 2016 11:30:10 GMT'] + expires: ['Fri, 15 Oct 2004 12:00:00 GMT'] + server: [openresty] + server-name: [dalmozwww04.dal.moz.com] + set-cookie: ['visid_incap_133232=6hHJoJ60Qim/2AB9nERqEcHBAFgAAAAAQUIPAAAAAADNJNx7kcw7rFGPLfDjzdJd; + expires=Sat, 14 Oct 2017 08:44:17 GMT; path=/; Domain=.moz.com', incap_ses_305_133232=y0fjbIgnjC2XrzDQM5Q7BMHBAFgAAAAANwcfxib6ZSdGjoUT2YMLiA==; + path=/; Domain=.moz.com] + strict-transport-security: [max-age=31536000] + vary: [Accept-Encoding] + via: [1.1 varnish] + x-cdn: [Incapsula] + x-frame-options: [SAMEORIGIN] + x-iinfo: [0-4137914-4137198 PNNN RT(1476444608836 301) q(0 0 0 0) r(0 0) U5] + x-varnish: [462974108 462973962] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/functional/push/__init__.py b/tests/functional/push/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/functional/push/test_add_channels_to_push.py b/tests/functional/push/test_add_channels_to_push.py new file mode 100644 index 00000000..6b3a67d7 --- /dev/null +++ b/tests/functional/push/test_add_channels_to_push.py @@ -0,0 +1,71 @@ +import unittest + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub + +import pubnub.enums + +from pubnub.endpoints.push.add_channels_to_push import AddChannelsToPush +from tests.helper import pnconf, sdk_name + + +class TestAddChannelsFromPush(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + uuid=None + ) + + self.pubnub.uuid = "UUID_AddChannelsTest" + self.add_channels = AddChannelsToPush(self.pubnub) + + def test_push_add_single_channel(self): + self.add_channels.channels(['ch']).push_type(pubnub.enums.PNPushType.APNS).device_id("coolDevice") + + params = (pnconf.subscribe_key, "coolDevice") + self.assertEquals(self.add_channels.build_path(), AddChannelsToPush.ADD_PATH % params) + + self.assertEqual(self.add_channels.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'type': 'apns', + 'add': 'ch' + }) + + self.assertEqual(self.add_channels._channels, ['ch']) + + def test_push_add_multiple_channels(self): + self.add_channels.channels(['ch1', 'ch2']).push_type(pubnub.enums.PNPushType.MPNS).device_id("coolDevice") + + params = (pnconf.subscribe_key, "coolDevice") + self.assertEquals(self.add_channels.build_path(), AddChannelsToPush.ADD_PATH % params) + + self.assertEqual(self.add_channels.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'type': 'mpns', + 'add': 'ch1,ch2' + }) + + self.assertEqual(self.add_channels._channels, ['ch1', 'ch2']) + + def test_push_add_google(self): + self.add_channels.channels(['ch1', 'ch2', 'ch3']).push_type(pubnub.enums.PNPushType.GCM).device_id("coolDevice") + + params = (pnconf.subscribe_key, "coolDevice") + self.assertEquals(self.add_channels.build_path(), AddChannelsToPush.ADD_PATH % params) + + self.assertEqual(self.add_channels.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'type': 'gcm', + 'add': 'ch1,ch2,ch3' + }) + + self.assertEqual(self.add_channels._channels, ['ch1', 'ch2', 'ch3']) diff --git a/tests/functional/push/test_list_push_provisions.py b/tests/functional/push/test_list_push_provisions.py new file mode 100644 index 00000000..207641d3 --- /dev/null +++ b/tests/functional/push/test_list_push_provisions.py @@ -0,0 +1,63 @@ +import unittest + +from pubnub.endpoints.push.list_push_provisions import ListPushProvisions +from pubnub.enums import PNPushType + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub +from tests.helper import pnconf, sdk_name + + +class TestListPushProvisions(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + uuid=None + ) + self.pubnub.uuid = "UUID_ListChannelsInCGTest" + self.list_push = ListPushProvisions(self.pubnub) + + def test_list_channel_group_apns(self): + self.list_push.push_type(PNPushType.APNS).device_id('coolDevice') + + self.assertEquals(self.list_push.build_path(), + ListPushProvisions.LIST_PATH % ( + pnconf.subscribe_key, "coolDevice")) + + self.assertEqual(self.list_push.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'type': 'apns' + }) + + def test_list_channel_group_gcm(self): + self.list_push.push_type(PNPushType.GCM).device_id('coolDevice') + + self.assertEquals(self.list_push.build_path(), + ListPushProvisions.LIST_PATH % ( + pnconf.subscribe_key, "coolDevice")) + + self.assertEqual(self.list_push.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'type': 'gcm' + }) + + def test_list_channel_group_mpns(self): + self.list_push.push_type(PNPushType.MPNS).device_id('coolDevice') + + self.assertEquals(self.list_push.build_path(), + ListPushProvisions.LIST_PATH % ( + pnconf.subscribe_key, "coolDevice")) + + self.assertEqual(self.list_push.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'type': 'mpns' + }) diff --git a/tests/functional/push/test_remove_channels_from_push.py b/tests/functional/push/test_remove_channels_from_push.py new file mode 100644 index 00000000..16ecd442 --- /dev/null +++ b/tests/functional/push/test_remove_channels_from_push.py @@ -0,0 +1,72 @@ +import unittest + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub + +import pubnub.enums + +from pubnub.endpoints.push.remove_channels_from_push import RemoveChannelsFromPush +from tests.helper import pnconf, sdk_name + + +class TestRemoveChannelsFromPush(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + uuid=None + ) + + self.pubnub.uuid = "UUID_RemoveChannelsTest" + self.remove_channels = RemoveChannelsFromPush(self.pubnub) + + def test_push_remove_single_channel(self): + self.remove_channels.channels(['ch']).push_type(pubnub.enums.PNPushType.APNS).device_id("coolDevice") + + params = (pnconf.subscribe_key, "coolDevice") + self.assertEquals(self.remove_channels.build_path(), RemoveChannelsFromPush.REMOVE_PATH % params) + + self.assertEqual(self.remove_channels.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'type': 'apns', + 'remove': 'ch' + }) + + self.assertEqual(self.remove_channels._channels, ['ch']) + + def test_push_remove_multiple_channels(self): + self.remove_channels.channels(['ch1', 'ch2']).push_type(pubnub.enums.PNPushType.MPNS).device_id("coolDevice") + + params = (pnconf.subscribe_key, "coolDevice") + self.assertEquals(self.remove_channels.build_path(), RemoveChannelsFromPush.REMOVE_PATH % params) + + self.assertEqual(self.remove_channels.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'type': 'mpns', + 'remove': 'ch1,ch2' + }) + + self.assertEqual(self.remove_channels._channels, ['ch1', 'ch2']) + + def test_push_remove_google(self): + self.remove_channels.channels(['ch1', 'ch2', 'ch3']).push_type(pubnub.enums.PNPushType.GCM)\ + .device_id("coolDevice") + + params = (pnconf.subscribe_key, "coolDevice") + self.assertEquals(self.remove_channels.build_path(), RemoveChannelsFromPush.REMOVE_PATH % params) + + self.assertEqual(self.remove_channels.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'type': 'gcm', + 'remove': 'ch1,ch2,ch3' + }) + + self.assertEqual(self.remove_channels._channels, ['ch1', 'ch2', 'ch3']) diff --git a/tests/functional/push/test_remove_device_from_push.py b/tests/functional/push/test_remove_device_from_push.py new file mode 100644 index 00000000..eee18b3c --- /dev/null +++ b/tests/functional/push/test_remove_device_from_push.py @@ -0,0 +1,62 @@ +import unittest + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub + +import pubnub.enums + +from pubnub.endpoints.push.remove_device import RemoveDeviceFromPush +from tests.helper import pnconf, sdk_name + + +class TestRemoveDeviceFromPush(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + uuid=None + ) + + self.pubnub.uuid = "UUID_RemoveDeviceTest" + self.remove_device = RemoveDeviceFromPush(self.pubnub) + + def test_remove_push_apns(self): + self.remove_device.push_type(pubnub.enums.PNPushType.APNS).device_id("coolDevice") + + params = (pnconf.subscribe_key, "coolDevice") + self.assertEquals(self.remove_device.build_path(), RemoveDeviceFromPush.REMOVE_PATH % params) + + self.assertEqual(self.remove_device.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'type': 'apns', + }) + + def test_remove_push_gcm(self): + self.remove_device.push_type(pubnub.enums.PNPushType.GCM).device_id("coolDevice") + + params = (pnconf.subscribe_key, "coolDevice") + self.assertEquals(self.remove_device.build_path(), RemoveDeviceFromPush.REMOVE_PATH % params) + + self.assertEqual(self.remove_device.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'type': 'gcm', + }) + + def test_remove_push_mpns(self): + self.remove_device.push_type(pubnub.enums.PNPushType.MPNS).device_id("coolDevice") + + params = (pnconf.subscribe_key, "coolDevice") + self.assertEquals(self.remove_device.build_path(), RemoveDeviceFromPush.REMOVE_PATH % params) + + self.assertEqual(self.remove_device.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'type': 'mpns', + }) diff --git a/tests/functional/test_add_channel_to_cg.py b/tests/functional/test_add_channel_to_cg.py new file mode 100644 index 00000000..6fc1636b --- /dev/null +++ b/tests/functional/test_add_channel_to_cg.py @@ -0,0 +1,53 @@ +import unittest + +from pubnub.endpoints.channel_groups.add_channel_to_channel_group import AddChannelToChannelGroup + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub +from tests.helper import pnconf, sdk_name + + +class TestAddChannelToChannelGroup(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + uuid=None + ) + self.pubnub.uuid = "UUID_AddChannelToCGTest" + self.add = AddChannelToChannelGroup(self.pubnub) + + def test_add_single_channel(self): + self.add.channels('ch').channel_group('gr') + + self.assertEquals(self.add.build_path(), + AddChannelToChannelGroup.ADD_PATH % ( + pnconf.subscribe_key, "gr")) + + self.assertEqual(self.add.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'add': "ch" + }) + + self.assertEqual(self.add._channels, ['ch']) + + def test_add_multiple_channels(self): + self.add.channels(['ch1', 'ch2']).channel_group('gr') + + self.assertEquals(self.add.build_path(), + AddChannelToChannelGroup.ADD_PATH % ( + pnconf.subscribe_key, "gr")) + + self.assertEqual(self.add.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'add': "ch1,ch2" + }) + + self.assertEqual(self.add._channels, ['ch1', 'ch2']) diff --git a/tests/functional/test_audit.py b/tests/functional/test_audit.py new file mode 100644 index 00000000..4d8193ee --- /dev/null +++ b/tests/functional/test_audit.py @@ -0,0 +1,65 @@ +import unittest + +from pubnub import utils +from pubnub.endpoints.access.audit import Audit + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub +from tests.helper import pnconf_pam, sdk_name + + +class TestAudit(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf_pam, + sdk_name=sdk_name, + timestamp=MagicMock(return_value=123), + uuid=None + ) + self.pubnub.uuid = "UUID_AuditUnitTest" + self.audit = Audit(self.pubnub) + + def test_audit_channel(self): + self.audit.channels('ch') + + self.assertEquals(self.audit.build_path(), Audit.AUDIT_PATH % pnconf_pam.subscribe_key) + + self.assertEqual(self.audit.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'timestamp': '123', + 'channel': 'ch', + 'signature': utils.sign_sha256(pnconf_pam.secret_key, + pnconf_pam.subscribe_key + "\n" + pnconf_pam.publish_key + "\n" + + "audit\n" + utils.prepare_pam_arguments({ + 'timestamp': 123, + 'channel': 'ch', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + })) + }) + + def test_audit_channel_group(self): + self.audit.channel_groups(['gr1', 'gr2']) + + self.assertEquals(self.audit.build_path(), Audit.AUDIT_PATH % pnconf_pam.subscribe_key) + + self.assertEqual(self.audit.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'timestamp': '123', + 'channel-group': 'gr1,gr2', + 'signature': utils.sign_sha256(pnconf_pam.secret_key, + pnconf_pam.subscribe_key + "\n" + pnconf_pam.publish_key + "\n" + + "audit\n" + utils.prepare_pam_arguments({ + 'timestamp': 123, + 'channel-group': 'gr1,gr2', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + })) + }) diff --git a/tests/functional/test_get_state.py b/tests/functional/test_get_state.py new file mode 100644 index 00000000..d1564309 --- /dev/null +++ b/tests/functional/test_get_state.py @@ -0,0 +1,53 @@ +import unittest + +from pubnub.endpoints.presence.get_state import GetState + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub +from tests.helper import pnconf, sdk_name + + +class TestGetState(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + uuid=None + ) + self.pubnub.uuid = "UUID_GetStateTest" + self.get_state = GetState(self.pubnub) + + def test_get_state_single_channel(self): + self.get_state.channels('ch') + + self.assertEquals(self.get_state.build_path(), GetState.GET_STATE_PATH % (pnconf.subscribe_key, + "ch", + self.pubnub.uuid)) + + self.assertEqual(self.get_state.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + }) + + self.assertEqual(self.get_state._channels, ['ch']) + + def test_get_state_single_group(self): + self.get_state.channel_groups('gr') + + self.assertEquals(self.get_state.build_path(), GetState.GET_STATE_PATH % (pnconf.subscribe_key, + ",", + self.pubnub.uuid)) + + self.assertEqual(self.get_state.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'channel-group': 'gr' + }) + + assert len(self.get_state._channels) == 0 + self.assertEqual(self.get_state._groups, ['gr']) diff --git a/tests/functional/test_grant.py b/tests/functional/test_grant.py new file mode 100644 index 00000000..4b735157 --- /dev/null +++ b/tests/functional/test_grant.py @@ -0,0 +1,75 @@ +import unittest + +from pubnub import utils +from pubnub.endpoints.access.grant import Grant + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub +from tests.helper import pnconf_pam, sdk_name + + +class TestGrant(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf_pam, + sdk_name=sdk_name, + timestamp=MagicMock(return_value=123), + uuid=None + ) + self.pubnub.uuid = "UUID_GrantUnitTest" + self.grant = Grant(self.pubnub) + + def test_grant_read_and_write_to_channel(self): + self.grant.channels('ch').read(True).write(True).ttl(7) + + self.assertEquals(self.grant.build_path(), Grant.GRANT_PATH % pnconf_pam.subscribe_key) + + self.assertEqual(self.grant.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'r': '1', + 'w': '1', + 'ttl': '7', + 'timestamp': '123', + 'channel': 'ch', + 'signature': utils.sign_sha256(pnconf_pam.secret_key, + pnconf_pam.subscribe_key + "\n" + pnconf_pam.publish_key + "\n" + + "grant\n" + utils.prepare_pam_arguments({ + 'r': '1', + 'w': '1', + 'ttl': '7', + 'timestamp': 123, + 'channel': 'ch', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + })) + }) + + def test_grant_read_and_write_to_channel_group(self): + self.grant.channel_groups(['gr1', 'gr2']).read(True).write(True) + + self.assertEquals(self.grant.build_path(), Grant.GRANT_PATH % pnconf_pam.subscribe_key) + + self.assertEqual(self.grant.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'r': '1', + 'w': '1', + 'timestamp': '123', + 'channel-group': 'gr1,gr2', + 'signature': utils.sign_sha256(pnconf_pam.secret_key, + pnconf_pam.subscribe_key + "\n" + pnconf_pam.publish_key + "\n" + + "grant\n" + utils.prepare_pam_arguments({ + 'r': '1', + 'w': '1', + 'timestamp': 123, + 'channel-group': 'gr1,gr2', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + })) + }) diff --git a/tests/functional/test_heartbeat.py b/tests/functional/test_heartbeat.py new file mode 100644 index 00000000..257e3555 --- /dev/null +++ b/tests/functional/test_heartbeat.py @@ -0,0 +1,105 @@ +import unittest + +import json + +from pubnub.endpoints.presence.heartbeat import Heartbeat + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub +from tests.helper import pnconf, sdk_name, pnconf_copy + + +class TestHeartbeat(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf_copy(), + sdk_name=sdk_name + ) + self.pubnub.uuid = "UUID_HeartbeatUnitTest" + self.hb = Heartbeat(self.pubnub) + self.pubnub.config.set_presence_timeout(20) + + def test_sub_single_channel(self): + self.hb.channels('ch') + + self.assertEquals(self.hb.build_path(), Heartbeat.HEARTBEAT_PATH + % (pnconf.subscribe_key, 'ch')) + + self.assertEqual(self.hb.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'heartbeat': '20' + }) + + self.assertEqual(self.hb._channels, ['ch']) + + def test_hb_multiple_channels_using_list(self): + self.hb.channels(['ch1', 'ch2', 'ch3']) + + self.assertEquals(self.hb.build_path(), Heartbeat.HEARTBEAT_PATH + % (pnconf.subscribe_key, "ch1,ch2,ch3")) + + self.assertEqual(self.hb.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'heartbeat': '20' + }) + + self.assertEqual(self.hb._channels, ['ch1', 'ch2', 'ch3']) + + def test_hb_single_group(self): + self.hb.channel_groups("gr") + + self.assertEquals(self.hb.build_path(), Heartbeat.HEARTBEAT_PATH + % (pnconf.subscribe_key, ",")) + + self.assertEqual(self.hb.build_params_callback()({}), { + 'channel-group': 'gr', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'heartbeat': '20' + }) + + self.assertEqual(self.hb._groups, ['gr']) + + def test_hb_multiple_groups_using_list(self): + self.hb.channel_groups(['gr1', 'gr2', 'gr3']) + + self.assertEquals(self.hb.build_path(), Heartbeat.HEARTBEAT_PATH + % (pnconf.subscribe_key, ",")) + + self.assertEqual(self.hb.build_params_callback()({}), { + 'channel-group': 'gr1,gr2,gr3', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'heartbeat': '20' + }) + + self.assertEqual(self.hb._groups, ['gr1', 'gr2', 'gr3']) + + def test_hb_with_state(self): + import six + + state = {"name": "Alex", "count": 7} + self.hb.channels('ch1,ch2').state(state) + + self.assertEquals(self.hb.build_path(), Heartbeat.HEARTBEAT_PATH + % (pnconf.subscribe_key, "ch1,ch2")) + + params = self.hb.build_params_callback()({}) + params['state'] = json.loads(six.moves.urllib.parse.unquote(params['state'])) + + self.assertEqual(params, { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'heartbeat': '20', + 'state': state + }) + + self.assertEqual(self.hb._groups, []) + self.assertEqual(self.hb._channels, ['ch1', 'ch2']) diff --git a/tests/functional/test_here_now.py b/tests/functional/test_here_now.py new file mode 100644 index 00000000..3cb628be --- /dev/null +++ b/tests/functional/test_here_now.py @@ -0,0 +1,59 @@ +import unittest + +from pubnub.endpoints.presence.here_now import HereNow + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub +from tests.helper import pnconf, sdk_name + + +class TestHereNow(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name + ) + self.pubnub.uuid = "UUID_HereNowTest" + self.here_now = HereNow(self.pubnub) + + def test_here_now(self): + self.here_now.channels("ch1") + + self.assertEquals(self.here_now.build_path(), HereNow.HERE_NOW_PATH + % (pnconf.subscribe_key, "ch1")) + + self.assertEqual(self.here_now.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + def test_here_now_groups(self): + self.here_now.channel_groups("gr1") + + self.assertEquals(self.here_now.build_path(), HereNow.HERE_NOW_PATH + % (pnconf.subscribe_key, ",")) + + self.assertEqual(self.here_now.build_params_callback()({}), { + 'channel-group': 'gr1', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + def test_here_now_with_options(self): + self.here_now.channels(["ch1"]).channel_groups("gr1").include_state(True).include_uuids(False) + + self.assertEquals(self.here_now.build_path(), HereNow.HERE_NOW_PATH + % (pnconf.subscribe_key, "ch1")) + + self.assertEqual(self.here_now.build_params_callback()({}), { + 'channel-group': 'gr1', + 'state': '1', + 'disable_uuids': '1', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) diff --git a/tests/functional/test_history.py b/tests/functional/test_history.py new file mode 100644 index 00000000..f6be3427 --- /dev/null +++ b/tests/functional/test_history.py @@ -0,0 +1,52 @@ +import unittest + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.endpoints.history import History +from pubnub.pubnub import PubNub +from tests.helper import pnconf_pam_copy, sdk_name + +pnconf = pnconf_pam_copy() +pnconf.secret_key = None + + +class TestHistory(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + timestamp=MagicMock(return_value=123), + uuid=None + ) + self.pubnub.uuid = "UUID_UnitTest" + self.history = History(self.pubnub) + + def test_history_basic(self): + self.history.channel('ch') + + self.assertEquals(self.history.build_path(), History.HISTORY_PATH % (pnconf.subscribe_key, 'ch')) + + self.assertEqual(self.history.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'count': '100' + }) + + def test_history_full(self): + self.history.channel('ch').start(100000).end(200000).reverse(False).count(3).include_timetoken(True) + + self.assertEquals(self.history.build_path(), History.HISTORY_PATH % (pnconf.subscribe_key, 'ch')) + + self.assertEqual(self.history.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'count': '3', + 'start': '100000', + 'end': '200000', + 'reverse': 'false', + 'include_token': 'true' + }) diff --git a/tests/functional/test_leave.py b/tests/functional/test_leave.py new file mode 100644 index 00000000..620c8df7 --- /dev/null +++ b/tests/functional/test_leave.py @@ -0,0 +1,128 @@ +import unittest + +from pubnub.endpoints.presence.leave import Leave + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub +from tests.helper import pnconf, sdk_name + + +class TestLeave(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + uuid=None + ) + self.pubnub.uuid = "UUID_SubscribeUnitTest" + self.leave = Leave(self.pubnub) + + def test_leave_single_channel(self): + self.leave.channels('ch') + + self.assertEquals(self.leave.build_path(), Leave.LEAVE_PATH % (pnconf.subscribe_key, "ch")) + + self.assertEqual(self.leave.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.leave._channels, ['ch']) + + def test_leave_multiple_channels(self): + self.leave.channels("ch1,ch2,ch3") + + self.assertEquals(self.leave.build_path(), Leave.LEAVE_PATH % (pnconf.subscribe_key, "ch1,ch2,ch3")) + + self.assertEqual(self.leave.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.leave._channels, ['ch1', 'ch2', 'ch3']) + + def test_leave_multiple_channels_using_list(self): + self.leave.channels(['ch1', 'ch2', 'ch3']) + + self.assertEquals(self.leave.build_path(), Leave.LEAVE_PATH % (pnconf.subscribe_key, "ch1,ch2,ch3")) + + self.assertEqual(self.leave.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.leave._channels, ['ch1', 'ch2', 'ch3']) + + def test_leave_multiple_channels_using_tuple(self): + self.leave.channels(('ch1', 'ch2', 'ch3')) + + self.assertEquals(self.leave.build_path(), Leave.LEAVE_PATH % (pnconf.subscribe_key, "ch1,ch2,ch3")) + + self.assertEqual(self.leave.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.leave._channels, ['ch1', 'ch2', 'ch3']) + + def test_leave_single_group(self): + self.leave.channel_groups("gr") + + self.assertEquals(self.leave.build_path(), Leave.LEAVE_PATH + % (pnconf.subscribe_key, ",")) + + self.assertEqual(self.leave.build_params_callback()({}), { + 'channel-group': 'gr', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.leave._groups, ['gr']) + + def test_leave_multiple_groups_using_string(self): + self.leave.channel_groups("gr1,gr2,gr3") + + self.assertEquals(self.leave.build_path(), Leave.LEAVE_PATH + % (pnconf.subscribe_key, ",")) + + self.assertEqual(self.leave.build_params_callback()({}), { + 'channel-group': 'gr1,gr2,gr3', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.leave._groups, ['gr1', 'gr2', 'gr3']) + + def test_leave_multiple_groups_using_list(self): + self.leave.channel_groups(['gr1', 'gr2', 'gr3']) + + self.assertEquals(self.leave.build_path(), Leave.LEAVE_PATH + % (pnconf.subscribe_key, ",")) + + self.assertEqual(self.leave.build_params_callback()({}), { + 'channel-group': 'gr1,gr2,gr3', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.leave._groups, ['gr1', 'gr2', 'gr3']) + + def test_leave_channels_and_groups(self): + self.leave.channels('ch1,ch2').channel_groups(["gr1", "gr2"]) + + self.assertEquals(self.leave.build_path(), Leave.LEAVE_PATH + % (pnconf.subscribe_key, "ch1,ch2")) + + self.assertEqual(self.leave.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'channel-group': 'gr1,gr2', + }) + + self.assertEqual(self.leave._groups, ['gr1', 'gr2']) + self.assertEqual(self.leave._channels, ['ch1', 'ch2']) diff --git a/tests/functional/test_list_channels_in_cg.py b/tests/functional/test_list_channels_in_cg.py new file mode 100644 index 00000000..fb3d27de --- /dev/null +++ b/tests/functional/test_list_channels_in_cg.py @@ -0,0 +1,35 @@ +import unittest + +from pubnub.endpoints.channel_groups.list_channels_in_channel_group import ListChannelsInChannelGroup + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub +from tests.helper import pnconf, sdk_name + + +class TestListChannelsInChannelGroup(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + uuid=None + ) + self.pubnub.uuid = "UUID_ListChannelsInCGTest" + self.list = ListChannelsInChannelGroup(self.pubnub) + + def test_list_channel_group(self): + self.list.channel_group('gr') + + self.assertEquals(self.list.build_path(), + ListChannelsInChannelGroup.LIST_PATH % ( + pnconf.subscribe_key, "gr")) + + self.assertEqual(self.list.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + }) diff --git a/tests/functional/test_publish.py b/tests/functional/test_publish.py new file mode 100644 index 00000000..e6c4845f --- /dev/null +++ b/tests/functional/test_publish.py @@ -0,0 +1,161 @@ +import copy +import unittest + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.endpoints.pubsub.publish import Publish +from pubnub.pubnub import PubNub +from tests.helper import pnconf, sdk_name, url_encode + + +class TestPublish(unittest.TestCase): + def setUp(self): + self.sm = MagicMock( + get_next_sequence=MagicMock(return_value=2) + ) + + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + _publish_sequence_manager=self.sm + ) + + self.pubnub.uuid = "UUID_PublishUnitTest" + self.pub = Publish(self.pubnub) + + def test_pub_message(self): + message = "hi" + encoded_message = url_encode(message) + + self.pub.channel("ch1").message(message) + + self.assertEquals(self.pub.build_path(), "/publish/%s/%s/0/ch1/0/%s" + % (pnconf.publish_key, pnconf.subscribe_key, encoded_message)) + + self.assertEqual(self.pub.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + }) + + def test_pub_list_message(self): + self.pubnub.uuid = "UUID_PublishUnitTest" + + message = ["hi", "hi2", "hi3"] + encoded_message = url_encode(message) + + self.pub.channel("ch1").message(message) + + self.assertEquals(self.pub.build_path(), "/publish/%s/%s/0/ch1/0/%s" + % (pnconf.publish_key, pnconf.subscribe_key, encoded_message)) + + self.assertEqual(self.pub.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + }) + + def test_pub_with_meta(self): + self.pubnub.uuid = "UUID_PublishUnitTest" + + message = ["hi", "hi2", "hi3"] + encoded_message = url_encode(message) + meta = ['m1', 'm2'] + + self.pub.channel("ch1").message(message).meta(meta) + + self.assertEquals(self.pub.build_path(), "/publish/%s/%s/0/ch1/0/%s" + % (pnconf.publish_key, pnconf.subscribe_key, encoded_message)) + + self.assertEqual(self.pub.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'meta': '%5B%22m1%22%2C%20%22m2%22%5D', + }) + + def test_pub_store(self): + self.pubnub.uuid = "UUID_PublishUnitTest" + + message = ["hi", "hi2", "hi3"] + encoded_message = url_encode(message) + + self.pub.channel("ch1").message(message).should_store(True) + + self.assertEquals(self.pub.build_path(), "/publish/%s/%s/0/ch1/0/%s" + % (pnconf.publish_key, pnconf.subscribe_key, encoded_message)) + + self.assertEqual(self.pub.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'store': '1', + }) + + def test_pub_do_not_store(self): + self.pubnub.uuid = "UUID_PublishUnitTest" + + message = ["hi", "hi2", "hi3"] + encoded_message = url_encode(message) + + self.pub.channel("ch1").message(message).should_store(False) + + self.assertEquals(self.pub.build_path(), "/publish/%s/%s/0/ch1/0/%s" + % (pnconf.publish_key, pnconf.subscribe_key, encoded_message)) + + self.assertEqual(self.pub.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'store': '0', + }) + + def test_pub_with_auth(self): + conf = copy.copy(pnconf) + conf.auth_key = "my_auth" + + pubnub = MagicMock( + spec=PubNub, + config=conf, + sdk_name=sdk_name, + uuid="UUID_PublishUnitTest", + _publish_sequence_manager=self.sm + ) + pub = Publish(pubnub) + message = "hey" + encoded_message = url_encode(message) + pub.channel("ch1").message(message) + + self.assertEquals(pub.build_path(), "/publish/%s/%s/0/ch1/0/%s" + % (pnconf.publish_key, pnconf.subscribe_key, encoded_message)) + + self.assertEqual(pub.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': pubnub.uuid, + 'auth': conf.auth_key, + }) + + def test_pub_encrypted_list_message(self): + conf = copy.copy(pnconf) + conf.cipher_key = "testCipher" + + pubnub = MagicMock( + spec=PubNub, + config=conf, + sdk_name=sdk_name, + uuid="UUID_PublishUnitTest", + _publish_sequence_manager=self.sm + ) + pub = Publish(pubnub) + + message = ["hi", "hi2", "hi3"] + encoded_message = "%22FQyKoIWWm7oN27zKyoU0bpjpgx49JxD04EI%2F0a8rg%2Fo%3D%22" + + pub.channel("ch1").message(message) + + self.assertEquals(pub.build_path(), "/publish/%s/%s/0/ch1/0/%s" + % (pnconf.publish_key, pnconf.subscribe_key, encoded_message)) + + self.assertEqual(pub.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': pubnub.uuid, + }) diff --git a/tests/functional/test_remove_cg.py b/tests/functional/test_remove_cg.py new file mode 100644 index 00000000..e4120011 --- /dev/null +++ b/tests/functional/test_remove_cg.py @@ -0,0 +1,35 @@ +import unittest + +from pubnub.endpoints.channel_groups.remove_channel_group import RemoveChannelGroup + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub +from tests.helper import pnconf, sdk_name + + +class TestRemoveChannelGroup(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + uuid=None + ) + self.pubnub.uuid = "UUID_ListChannelsInCGTest" + self.list = RemoveChannelGroup(self.pubnub) + + def test_list_channel_group(self): + self.list.channel_group('gr') + + self.assertEquals(self.list.build_path(), + RemoveChannelGroup.REMOVE_PATH % ( + pnconf.subscribe_key, "gr")) + + self.assertEqual(self.list.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + }) diff --git a/tests/functional/test_remove_channel_from_cg.py b/tests/functional/test_remove_channel_from_cg.py new file mode 100644 index 00000000..f0843bce --- /dev/null +++ b/tests/functional/test_remove_channel_from_cg.py @@ -0,0 +1,53 @@ +import unittest + +from pubnub.endpoints.channel_groups.remove_channel_from_channel_group import RemoveChannelFromChannelGroup + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub +from tests.helper import pnconf, sdk_name + + +class TestRemoveChannelToChannelGroup(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + uuid=None + ) + self.pubnub.uuid = "UUID_RemoveChannelToCGTest" + self.remove = RemoveChannelFromChannelGroup(self.pubnub) + + def test_remove_single_channel(self): + self.remove.channels('ch').channel_group('gr') + + self.assertEquals(self.remove.build_path(), + RemoveChannelFromChannelGroup.REMOVE_PATH % ( + pnconf.subscribe_key, "gr")) + + self.assertEqual(self.remove.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'remove': "ch" + }) + + self.assertEqual(self.remove._channels, ['ch']) + + def test_remove_multiple_channels(self): + self.remove.channels(['ch1', 'ch2']).channel_group('gr') + + self.assertEquals(self.remove.build_path(), + RemoveChannelFromChannelGroup.REMOVE_PATH % ( + pnconf.subscribe_key, "gr")) + + self.assertEqual(self.remove.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'remove': "ch1,ch2" + }) + + self.assertEqual(self.remove._channels, ['ch1', 'ch2']) diff --git a/tests/functional/test_revoke.py b/tests/functional/test_revoke.py new file mode 100644 index 00000000..438ea6cd --- /dev/null +++ b/tests/functional/test_revoke.py @@ -0,0 +1,87 @@ +import unittest + +from pubnub import utils +from pubnub.endpoints.access.revoke import Revoke + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub +from tests.helper import pnconf_pam_copy, sdk_name + +pnconf = pnconf_pam_copy() +# pnconf.secret_key = None + + +class TestRevoke(unittest.TestCase): + def setUp(self): + + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + timestamp=MagicMock(return_value=123), + uuid=None + ) + self.pubnub.uuid = "UUID_RevokeUnitTest" + self.revoke = Revoke(self.pubnub) + + def test_revoke_to_channel(self): + self.revoke.channels('ch') + + self.assertEquals(self.revoke.build_path(), Revoke.GRANT_PATH % pnconf.subscribe_key) + + self.assertEqual(self.revoke.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'timestamp': '123', + 'channel': 'ch', + 'r': '0', + 'w': '0', + 'm': '0', + 'signature': utils.sign_sha256(pnconf.secret_key, + pnconf.subscribe_key + "\n" + pnconf.publish_key + "\n" + + "grant\n" + utils.prepare_pam_arguments({ + 'timestamp': 123, + 'channel': 'ch', + 'r': '0', + 'w': '0', + 'm': '0', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + })) + }) + + def test_revoke_read_to_channel(self): + def revoke(): + self.revoke.channels('ch').read(True).write(True) + + self.assertRaises(NotImplementedError, revoke) + + def test_grant_read_and_write_to_channel_group(self): + self.revoke.channel_groups(['gr1', 'gr2']) + + self.assertEquals(self.revoke.build_path(), Revoke.GRANT_PATH % pnconf.subscribe_key) + + self.assertEqual(self.revoke.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'r': '0', + 'w': '0', + 'm': '0', + 'timestamp': '123', + 'channel-group': 'gr1,gr2', + 'signature': utils.sign_sha256(pnconf.secret_key, + pnconf.subscribe_key + "\n" + pnconf.publish_key + "\n" + + "grant\n" + utils.prepare_pam_arguments({ + 'r': '0', + 'w': '0', + 'm': '0', + 'timestamp': 123, + 'channel-group': 'gr1,gr2', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + })) + }) diff --git a/tests/functional/test_set_state.py b/tests/functional/test_set_state.py new file mode 100644 index 00000000..2761fc29 --- /dev/null +++ b/tests/functional/test_set_state.py @@ -0,0 +1,58 @@ +import json +import unittest + +from pubnub.endpoints.presence.set_state import SetState +from tests import helper + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.pubnub import PubNub +from tests.helper import pnconf, sdk_name + + +class TestSetState(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name, + uuid=None + ) + self.pubnub.uuid = "UUID_SetStateTest" + self.set_state = SetState(self.pubnub) + self.state = {'name': 'Alex', "count": 5} + + def test_set_state_single_channel(self): + self.set_state.channels('ch').state(self.state) + + self.assertEquals(self.set_state.build_path(), SetState.SET_STATE_PATH % (pnconf.subscribe_key, + "ch", + self.pubnub.uuid)) + + params = self.set_state.build_params_callback()({}) + self.assertEqual(params['pnsdk'], sdk_name) + self.assertEqual(params['uuid'], self.pubnub.uuid) + self.assertEqual(json.loads(helper.url_decode(params['state'])), + json.loads(helper.url_decode('%7B%22count%22%3A%205%2C%20%22name%22%3A%20%22Alex%22%7D'))) + + self.assertEqual(self.set_state._channels, ['ch']) + + def test_set_state_single_group(self): + self.set_state.channel_groups('gr').state(self.state) + + self.assertEquals(self.set_state.build_path(), SetState.SET_STATE_PATH % (pnconf.subscribe_key, + ",", + self.pubnub.uuid)) + + params = self.set_state.build_params_callback()({}) + self.assertEqual(params['pnsdk'], sdk_name) + self.assertEqual(params['uuid'], self.pubnub.uuid) + self.assertEqual(params['channel-group'], 'gr') + self.assertEqual(json.loads(helper.url_decode(params['state'])), + json.loads(helper.url_decode('%7B%22count%22%3A%205%2C%20%22name%22%3A%20%22Alex%22%7D'))) + + assert len(self.set_state._channels) == 0 + self.assertEqual(self.set_state._groups, ['gr']) diff --git a/tests/functional/test_stringify.py b/tests/functional/test_stringify.py new file mode 100644 index 00000000..2a72d325 --- /dev/null +++ b/tests/functional/test_stringify.py @@ -0,0 +1,93 @@ +import unittest + +from pubnub.crypto import PubNubCryptodome +from pubnub.models.consumer.access_manager import PNAccessManagerAuditResult, PNAccessManagerGrantResult +from pubnub.models.consumer.channel_group import PNChannelGroupsListResult, PNChannelGroupsAddChannelResult, \ + PNChannelGroupsRemoveGroupResult, PNChannelGroupsRemoveChannelResult +from pubnub.models.consumer.history import PNHistoryResult, PNHistoryItemResult +from pubnub.models.consumer.presence import PNHereNowResult, PNHereNowChannelData, PNHereNowOccupantsData, \ + PNWhereNowResult, PNSetStateResult, PNGetStateResult + +from pubnub.models.consumer.pubsub import PNPublishResult +from pubnub.models.consumer.push import PNPushListProvisionsResult, PNPushAddChannelResult, PNPushRemoveChannelResult, \ + PNPushRemoveAllChannelsResult + + +class TestStringify(unittest.TestCase): + def test_publish(self): + assert str(PNPublishResult(None, 123123)) == "Publish success with timetoken 123123" + + def test_add_channel_to_group(self): + assert str(PNChannelGroupsAddChannelResult()) == "Channel successfully added" + + def test_remove_channel_from_group(self): + assert str(PNChannelGroupsRemoveChannelResult()) == "Channel successfully removed" + + def test_remove_channel_group(self): + assert str(PNChannelGroupsRemoveGroupResult()) == "Group successfully removed" + + def test_list_channel_group(self): + result = PNChannelGroupsListResult([ + 'qwer', + 'asdf', + 'zxcv' + ]) + + assert str(result) == "Group contains following channels: qwer, asdf, zxcv" + + def test_audit(self): + result = PNAccessManagerAuditResult(None, None, None, None, 3600, True, False, True) + + assert str(result) == "Current permissions are valid for 3600 minutes: read True, write False, manage: True" + + def test_grant(self): + result = PNAccessManagerGrantResult(None, None, None, None, 3600, True, False, True) + + assert str(result) == "New permissions are set for 3600 minutes: read True, write False, manage: True" + + def test_history(self): + assert str(PNHistoryResult(None, 123, 789)) == "History result for range 123..789" + + def test_history_item(self): + assert str(PNHistoryItemResult({'blah': 2}, PubNubCryptodome(), 123)) == \ + "History item with tt: 123 and content: {'blah': 2}" + + assert str(PNHistoryItemResult({'blah': 2}, PubNubCryptodome())) == \ + "History item with tt: None and content: {'blah': 2}" + + def test_here_now(self): + assert str(PNHereNowResult(7, 4, None)) == "HereNow Result total occupancy: 4, total channels: 7" + + def test_here_now_channel_data(self): + assert str(PNHereNowChannelData('blah', 5, 9)) == \ + "HereNow Channel Data for channel 'blah': occupancy: 5, occupants: 9" + + def test_here_now_occupants_data(self): + assert str(PNHereNowOccupantsData('myuuid', {'blah': 3})) == \ + "HereNow Occupants Data for 'myuuid': {'blah': 3}" + + def test_where_now(self): + assert str(PNWhereNowResult(['qwer', 'asdf'])) == \ + "User is currently subscribed to qwer, asdf" + + def test_set_state(self): + assert str(PNSetStateResult({})) == "New state {} successfully set" + + def test_get_state(self): + assert str(PNGetStateResult({})) == "Current state is {}" + + def test_push_list(self): + assert str(PNPushListProvisionsResult(['qwer', 'asdf'])) == \ + "Push notification enabled on following channels: qwer, asdf" + + def test_push_add(self): + assert str(PNPushAddChannelResult()) == \ + "Channel successfully added" + + def test_push_remove(self): + assert str(PNPushRemoveChannelResult()) == \ + "Channel successfully removed" + + def test_push_remove_all(self): + assert str(PNPushRemoveAllChannelsResult()) == \ + "All channels successfully removed" diff --git a/tests/functional/test_subscribe.py b/tests/functional/test_subscribe.py new file mode 100644 index 00000000..3e0822b7 --- /dev/null +++ b/tests/functional/test_subscribe.py @@ -0,0 +1,132 @@ +import unittest + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.endpoints.pubsub.subscribe import Subscribe +from pubnub.pubnub import PubNub +from tests.helper import pnconf, sdk_name + + +class TestSubscribe(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf, + sdk_name=sdk_name + ) + self.pubnub.uuid = "UUID_SubscribeUnitTest" + self.sub = Subscribe(self.pubnub) + + def test_pub_single_channel(self): + self.sub.channels('ch') + + self.assertEquals(self.sub.build_path(), Subscribe.SUBSCRIBE_PATH + % (pnconf.subscribe_key, 'ch')) + + self.assertEqual(self.sub.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.sub._channels, ['ch']) + + def test_sub_multiple_channels_using_string(self): + self.sub.channels("ch1,ch2,ch3") + + self.assertEquals(self.sub.build_path(), Subscribe.SUBSCRIBE_PATH + % (pnconf.subscribe_key, "ch1,ch2,ch3")) + + self.assertEqual(self.sub.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.sub._channels, ['ch1', 'ch2', 'ch3']) + + def test_sub_multiple_channels_using_list(self): + self.sub.channels(['ch1', 'ch2', 'ch3']) + + self.assertEquals(self.sub.build_path(), Subscribe.SUBSCRIBE_PATH + % (pnconf.subscribe_key, "ch1,ch2,ch3")) + + self.assertEqual(self.sub.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.sub._channels, ['ch1', 'ch2', 'ch3']) + + def test_sub_multiple_channels_using_tuple(self): + self.sub.channels(('ch1', 'ch2', 'ch3')) + + self.assertEquals(self.sub.build_path(), Subscribe.SUBSCRIBE_PATH + % (pnconf.subscribe_key, "ch1,ch2,ch3")) + + self.assertEqual(self.sub.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.sub._channels, ['ch1', 'ch2', 'ch3']) + + def test_sub_single_group(self): + self.sub.channel_groups("gr") + + self.assertEquals(self.sub.build_path(), Subscribe.SUBSCRIBE_PATH + % (pnconf.subscribe_key, ",")) + + self.assertEqual(self.sub.build_params_callback()({}), { + 'channel-group': 'gr', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.sub._groups, ['gr']) + + def test_sub_multiple_groups_using_string(self): + self.sub.channel_groups("gr1,gr2,gr3") + + self.assertEquals(self.sub.build_path(), Subscribe.SUBSCRIBE_PATH + % (pnconf.subscribe_key, ",")) + + self.assertEqual(self.sub.build_params_callback()({}), { + 'channel-group': 'gr1,gr2,gr3', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.sub._groups, ['gr1', 'gr2', 'gr3']) + + def test_sub_multiple_groups_using_list(self): + self.sub.channel_groups(['gr1', 'gr2', 'gr3']) + + self.assertEquals(self.sub.build_path(), Subscribe.SUBSCRIBE_PATH + % (pnconf.subscribe_key, ",")) + + self.assertEqual(self.sub.build_params_callback()({}), { + 'channel-group': 'gr1,gr2,gr3', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + self.assertEqual(self.sub._groups, ['gr1', 'gr2', 'gr3']) + + def test_sub_multiple(self): + self.sub.channels('ch1,ch2').filter_expression('blah').region('us-east-1').timetoken('123') + + self.assertEquals(self.sub.build_path(), Subscribe.SUBSCRIBE_PATH + % (pnconf.subscribe_key, "ch1,ch2")) + + self.assertEqual(self.sub.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid, + 'filter-expr': 'blah', + 'tr': 'us-east-1', + 'tt': '123' + }) + + self.assertEqual(self.sub._groups, []) + self.assertEqual(self.sub._channels, ['ch1', 'ch2']) diff --git a/tests/functional/test_where_now.py b/tests/functional/test_where_now.py new file mode 100644 index 00000000..7c5e88d0 --- /dev/null +++ b/tests/functional/test_where_now.py @@ -0,0 +1,41 @@ +import unittest + +try: + from mock import MagicMock +except ImportError: + from unittest.mock import MagicMock + +from pubnub.endpoints.presence.where_now import WhereNow +from pubnub.pubnub import PubNub +from tests.helper import pnconf, sdk_name, pnconf_copy + + +class TestWhereNow(unittest.TestCase): + def setUp(self): + self.pubnub = MagicMock( + spec=PubNub, + config=pnconf_copy(), + sdk_name=sdk_name + ) + self.pubnub.config.uuid = "UUID_WhereNowTest" + self.where_now = WhereNow(self.pubnub) + + def test_where_now(self): + self.where_now.uuid("person_uuid") + + self.assertEquals(self.where_now.build_path(), WhereNow.WHERE_NOW_PATH + % (pnconf.subscribe_key, "person_uuid")) + + self.assertEqual(self.where_now.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + + def test_where_now_no_uuid(self): + self.assertEquals(self.where_now.build_path(), WhereNow.WHERE_NOW_PATH + % (pnconf.subscribe_key, self.pubnub.config.uuid)) + + self.assertEqual(self.where_now.build_params_callback()({}), { + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) diff --git a/tests/helper.py b/tests/helper.py new file mode 100644 index 00000000..d01f9063 --- /dev/null +++ b/tests/helper.py @@ -0,0 +1,144 @@ +import threading +import string +import random +import six + +from copy import copy +from pubnub import utils +from pubnub.crypto import PubNubCryptodome +from pubnub.pnconfiguration import PNConfiguration + +try: + from mock import patch +except ImportError: + from unittest.mock import patch # noqa: F401 + +crypto = PubNubCryptodome() + + +pub_key = "pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52" +sub_key = "sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe" + +pub_key_pam = "pub-c-98863562-19a6-4760-bf0b-d537d1f5c582" +sub_key_pam = "sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f" +sec_key_pam = "sec-c-MGFkMjQxYjMtNTUxZC00YzE3LWFiZGYtNzUwMjdjNmM3NDhk" + +pnconf = PNConfiguration() +pnconf.publish_key = pub_key +pnconf.subscribe_key = sub_key +pnconf.enable_subscribe = False + +pnconf_sub = PNConfiguration() +pnconf_sub.publish_key = pub_key +pnconf_sub.subscribe_key = sub_key + +pnconf_enc = PNConfiguration() +pnconf_enc.publish_key = pub_key +pnconf_enc.subscribe_key = sub_key +pnconf_enc.cipher_key = "testKey" +pnconf_enc.enable_subscribe = False + +pnconf_enc_sub = PNConfiguration() +pnconf_enc_sub.publish_key = pub_key +pnconf_enc_sub.subscribe_key = sub_key +pnconf_enc_sub.cipher_key = "testKey" + +pnconf_pam = PNConfiguration() +pnconf_pam.publish_key = pub_key_pam +pnconf_pam.subscribe_key = sub_key_pam +pnconf_pam.secret_key = sec_key_pam +pnconf_pam.enable_subscribe = False + +pnconf_ssl = PNConfiguration() +pnconf_ssl.publish_key = pub_key +pnconf_ssl.subscribe_key = sub_key +pnconf_ssl.ssl = True + + +def pnconf_copy(): + return copy(pnconf) + + +def pnconf_enc_copy(): + return copy(pnconf_enc) + + +def pnconf_sub_copy(): + return copy(pnconf_sub) + + +def pnconf_enc_sub_copy(): + return copy(pnconf_enc_sub) + + +def pnconf_pam_copy(): + return copy(pnconf_pam) + + +def pnconf_ssl_copy(): + return copy(pnconf_ssl) + + +sdk_name = "Python-UnitTest" + + +def url_encode(data): + return utils.url_encode(utils.write_value_as_string(data)) + + +def url_decode(data): + return six.moves.urllib.parse.unquote(data) + + +def gen_channel(prefix): + return "%s-%s" % (prefix, gen_string(8)) + + +def gen_string(l): + return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(l)) + + +def gen_decrypt_func(cipher_key): + def decrypter(entry): + mr = crypto.decrypt(cipher_key, entry) + return mr + + return decrypter + + +class CountDownLatch(object): + def __init__(self, count=1): + self.count = count + self.lock = threading.Condition() + self.done = False + self.t = None + + def count_down(self): + self.lock.acquire() + self.count -= 1 + + if self.count <= 0: + self.done = True + self.lock.notifyAll() + + if self.t is not None: + self.t.cancel() + self.lock.release() + + def _release(self): + self.lock.acquire() + self.count = 0 + self.lock.notifyAll() + self.lock.release() + + def await(self, timeout=5): + self.lock.acquire() + + self.t = threading.Timer(timeout, self._release) + self.t.start() + + while self.count > 0: + self.lock.wait() + + self.t.cancel() + self.lock.release() diff --git a/tests/integrational/README.md b/tests/integrational/README.md new file mode 100644 index 00000000..9c1c5c96 --- /dev/null +++ b/tests/integrational/README.md @@ -0,0 +1,5 @@ +## VCR + +### Matchers +Default VCR.py matchers are: 'method', 'scheme', 'host', 'port', 'path', 'query' +PubNub custom VCR.py matchers are defined inside vcr_helper.py \ No newline at end of file diff --git a/tests/integrational/__init__.py b/tests/integrational/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integrational/asyncio/__init__.py b/tests/integrational/asyncio/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integrational/asyncio/test_channel_groups.py b/tests/integrational/asyncio/test_channel_groups.py new file mode 100644 index 00000000..b36f1e54 --- /dev/null +++ b/tests/integrational/asyncio/test_channel_groups.py @@ -0,0 +1,167 @@ +import asyncio +import pytest + +from pubnub.models.consumer.channel_group import PNChannelGroupsAddChannelResult, PNChannelGroupsListResult, \ + PNChannelGroupsRemoveChannelResult, PNChannelGroupsRemoveGroupResult +from pubnub.pubnub_asyncio import PubNubAsyncio +from tests.helper import pnconf, pnconf_copy, pnconf_pam_copy +from tests.integrational.vcr_asyncio_sleeper import get_sleeper +from tests.integrational.vcr_helper import pn_vcr + + +@get_sleeper('tests/integrational/fixtures/asyncio/groups/add_remove_single_channel.yaml') +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/groups/add_remove_single_channel.yaml', + filter_query_parameters=['uuid', 'pnsdk']) +@pytest.mark.asyncio +def test_add_remove_single_channel(event_loop, sleeper=asyncio.sleep): + pubnub = PubNubAsyncio(pnconf_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = 'test-channel-group-asyncio-uuid1' + + ch = "test-channel-groups-asyncio-ch" + gr = "test-channel-groups-asyncio-cg" + + yield from pubnub.publish().channel(ch).message("hey").future() + # add + env = yield from pubnub.add_channel_to_channel_group() \ + .channels(ch).channel_group(gr).future() + + assert isinstance(env.result, PNChannelGroupsAddChannelResult) + + yield from sleeper(1) + + # list + env = yield from pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + assert len(env.result.channels) == 1 + assert env.result.channels[0] == ch + + # remove + env = yield from pubnub.remove_channel_from_channel_group() \ + .channels(ch).channel_group(gr).future() + + assert isinstance(env.result, PNChannelGroupsRemoveChannelResult) + + yield from sleeper(1) + + # change uuid to let vcr to distinguish list requests + pubnub.config.uuid = 'test-channel-group-asyncio-uuid2' + + # list + env = yield from pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + assert len(env.result.channels) == 0 + + pubnub.stop() + + +@get_sleeper('tests/integrational/fixtures/asyncio/groups/add_remove_multiple_channels.yaml') +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/groups/add_remove_multiple_channels.yaml', + filter_query_parameters=['uuid', 'pnsdk']) +@pytest.mark.asyncio +def test_add_remove_multiple_channels(event_loop, sleeper=asyncio.sleep): + pubnub = PubNubAsyncio(pnconf, custom_event_loop=event_loop) + + ch1 = "channel-groups-tornado-ch1" + ch2 = "channel-groups-tornado-ch2" + gr = "channel-groups-tornado-cg" + + # add + env = yield from pubnub.add_channel_to_channel_group() \ + .channels([ch1, ch2]).channel_group(gr).future() + + assert isinstance(env.result, PNChannelGroupsAddChannelResult) + + yield from sleeper(1) + + # list + env = yield from pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + assert len(env.result.channels) == 2 + assert ch1 in env.result.channels + assert ch2 in env.result.channels + + # remove + env = yield from pubnub.remove_channel_from_channel_group() \ + .channels([ch1, ch2]).channel_group(gr).future() + + assert isinstance(env.result, PNChannelGroupsRemoveChannelResult) + + yield from sleeper(1) + + # list + env = yield from pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + assert len(env.result.channels) == 0 + + pubnub.stop() + + +@get_sleeper('tests/integrational/fixtures/asyncio/groups/add_channel_remove_group.yaml') +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/groups/add_channel_remove_group.yaml', + filter_query_parameters=['uuid', 'pnsdk']) +@pytest.mark.asyncio +def test_add_channel_remove_group(event_loop, sleeper=asyncio.sleep): + pubnub = PubNubAsyncio(pnconf, custom_event_loop=event_loop) + + ch = "channel-groups-tornado-ch" + gr = "channel-groups-tornado-cg" + + # add + env = yield from pubnub.add_channel_to_channel_group() \ + .channels(ch).channel_group(gr).future() + + assert isinstance(env.result, PNChannelGroupsAddChannelResult) + + yield from sleeper(1) + + # list + env = yield from pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + assert len(env.result.channels) == 1 + assert env.result.channels[0] == ch + + # remove group + env = yield from pubnub.remove_channel_group().channel_group(gr).future() + + assert isinstance(env.result, PNChannelGroupsRemoveGroupResult) + + yield from sleeper(1) + + # list + env = yield from pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + assert len(env.result.channels) == 0 + + pubnub.stop() + + +@pytest.mark.asyncio +def test_super_call(event_loop): + pubnub = PubNubAsyncio(pnconf_pam_copy(), custom_event_loop=event_loop) + + ch = "channel-groups-tornado-ch" + gr = "channel-groups-tornado-cg" + + # add + env = yield from pubnub.add_channel_to_channel_group() \ + .channels(ch).channel_group(gr).future() + + assert isinstance(env.result, PNChannelGroupsAddChannelResult) + + # list + env = yield from pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + + # remove channel + env = yield from pubnub.remove_channel_from_channel_group().channel_group(gr).channels(ch).future() + assert isinstance(env.result, PNChannelGroupsRemoveChannelResult) + + # remove group + env = yield from pubnub.remove_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsRemoveGroupResult) + + # list + env = yield from pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + + pubnub.stop() diff --git a/tests/integrational/asyncio/test_heartbeat.py b/tests/integrational/asyncio/test_heartbeat.py new file mode 100644 index 00000000..71bb000b --- /dev/null +++ b/tests/integrational/asyncio/test_heartbeat.py @@ -0,0 +1,79 @@ +import logging +import asyncio +import pytest + +import pubnub as pn +from pubnub.pubnub_asyncio import PubNubAsyncio, SubscribeListener +from tests import helper +from tests.helper import pnconf_sub_copy + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +messenger_config = pnconf_sub_copy() +messenger_config.set_presence_timeout(8) +messenger_config.uuid = helper.gen_channel("messenger") + +listener_config = pnconf_sub_copy() +listener_config.uuid = helper.gen_channel("listener") + + +@pytest.mark.asyncio +def test_timeout_event_on_broken_heartbeat(event_loop): + ch = helper.gen_channel("heartbeat-test") + + pubnub = PubNubAsyncio(messenger_config, custom_event_loop=event_loop) + pubnub_listener = PubNubAsyncio(listener_config, custom_event_loop=event_loop) + + pubnub.config.uuid = helper.gen_channel("messenger") + pubnub_listener.config.uuid = helper.gen_channel("listener") + + # - connect to :ch-pnpres + callback_presence = SubscribeListener() + pubnub_listener.add_listener(callback_presence) + pubnub_listener.subscribe().channels(ch).with_presence().execute() + yield from callback_presence.wait_for_connect() + + envelope = yield from callback_presence.wait_for_presence_on(ch) + assert ch == envelope.channel + assert 'join' == envelope.event + assert pubnub_listener.uuid == envelope.uuid + + # - connect to :ch + callback_messages = SubscribeListener() + pubnub.add_listener(callback_messages) + pubnub.subscribe().channels(ch).execute() + + useless_connect_future = callback_messages.wait_for_connect() + presence_future = asyncio.ensure_future(callback_presence.wait_for_presence_on(ch)) + + # - assert join event + yield from asyncio.wait([useless_connect_future, presence_future]) + + prs_envelope = presence_future.result() + + assert ch == prs_envelope.channel + assert 'join' == prs_envelope.event + assert pubnub.uuid == prs_envelope.uuid + + # wait for one heartbeat call + yield from asyncio.sleep(8) + + # - break messenger heartbeat loop + pubnub._subscription_manager._stop_heartbeat_timer() + + # - assert for timeout + envelope = yield from callback_presence.wait_for_presence_on(ch) + assert ch == envelope.channel + assert 'timeout' == envelope.event + assert pubnub.uuid == envelope.uuid + + pubnub.unsubscribe().channels(ch).execute() + yield from callback_messages.wait_for_disconnect() + + # - disconnect from :ch-pnpres + pubnub_listener.unsubscribe().channels(ch).execute() + yield from callback_presence.wait_for_disconnect() + + pubnub.stop() + pubnub_listener.stop() diff --git a/tests/integrational/asyncio/test_here_now.py b/tests/integrational/asyncio/test_here_now.py new file mode 100644 index 00000000..c2937eea --- /dev/null +++ b/tests/integrational/asyncio/test_here_now.py @@ -0,0 +1,161 @@ +import asyncio +import pytest + +from pubnub.models.consumer.presence import PNHereNowResult +from pubnub.pubnub_asyncio import PubNubAsyncio +from tests.helper import pnconf_sub_copy, pnconf_pam_copy +from tests.integrational.vcr_asyncio_sleeper import get_sleeper, VCR599Listener +from tests.integrational.vcr_helper import pn_vcr + + +@get_sleeper('tests/integrational/fixtures/asyncio/here_now/single_channel.yaml') +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/here_now/single_channel.yaml', + filter_query_parameters=['tr', 'uuid', 'pnsdk']) +@pytest.mark.asyncio +def test_single_channel(event_loop, sleeper=asyncio.sleep): + pubnub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = 'test-here-now-asyncio-uuid1' + ch = "test-here-now-asyncio-ch" + + callback = VCR599Listener(1) + pubnub.add_listener(callback) + pubnub.subscribe().channels(ch).execute() + + yield from callback.wait_for_connect() + + yield from sleeper(5) + + env = yield from pubnub.here_now() \ + .channels(ch) \ + .include_uuids(True) \ + .future() + + assert env.result.total_channels == 1 + assert env.result.total_occupancy >= 1 + + channels = env.result.channels + + assert len(channels) == 1 + assert channels[0].occupancy == 1 + assert channels[0].occupants[0].uuid == pubnub.uuid + + result = yield from pubnub.here_now() \ + .channels(ch) \ + .include_state(True) \ + .result() + + assert result.total_channels == 1 + assert result.total_occupancy == 1 + + pubnub.unsubscribe().channels(ch).execute() + yield from callback.wait_for_disconnect() + + pubnub.stop() + + +@get_sleeper('tests/integrational/fixtures/asyncio/here_now/multiple_channels.yaml') +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/here_now/multiple_channels.yaml', + filter_query_parameters=['pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'string_list_in_path', 'query'], + match_on_kwargs={ + 'string_list_in_path': { + 'positions': [4, 6] + } + }) +@pytest.mark.asyncio +def test_multiple_channels(event_loop, sleeper=asyncio.sleep): + pubnub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = 'test-here-now-asyncio-uuid1' + + ch1 = "test-here-now-asyncio-ch1" + ch2 = "test-here-now-asyncio-ch2" + + callback = VCR599Listener(1) + pubnub.add_listener(callback) + pubnub.subscribe().channels([ch1, ch2]).execute() + + yield from callback.wait_for_connect() + + yield from sleeper(5) + env = yield from pubnub.here_now() \ + .channels([ch1, ch2]) \ + .future() + + assert env.result.total_channels == 2 + assert env.result.total_occupancy >= 1 + + channels = env.result.channels + + assert len(channels) == 2 + assert channels[0].occupancy == 1 + assert channels[0].occupants[0].uuid == pubnub.uuid + assert channels[1].occupancy == 1 + assert channels[1].occupants[0].uuid == pubnub.uuid + + result = yield from pubnub.here_now() \ + .channels([ch1, ch2]) \ + .include_state(True) \ + .result() + + assert result.total_channels == 2 + + pubnub.unsubscribe().channels([ch1, ch2]).execute() + yield from callback.wait_for_disconnect() + + pubnub.stop() + + +@get_sleeper('tests/integrational/fixtures/asyncio/here_now/global.yaml') +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/here_now/global.yaml', + filter_query_parameters=['pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'string_list_in_path', 'query'], + match_on_kwargs={ + 'string_list_in_path': { + 'positions': [4] + } + }) +@pytest.mark.asyncio +def test_global(event_loop, sleeper=asyncio.sleep): + pubnub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = 'test-here-now-asyncio-uuid1' + + ch1 = "test-here-now-asyncio-ch1" + ch2 = "test-here-now-asyncio-ch2" + + callback = VCR599Listener(1) + pubnub.add_listener(callback) + pubnub.subscribe().channels([ch1, ch2]).execute() + + yield from callback.wait_for_connect() + + yield from sleeper(5) + + env = yield from pubnub.here_now().future() + + assert env.result.total_channels >= 2 + assert env.result.total_occupancy >= 1 + + pubnub.unsubscribe().channels([ch1, ch2]).execute() + yield from callback.wait_for_disconnect() + + pubnub.stop() + + +@pytest.mark.asyncio +def test_here_now_super_call(event_loop): + pubnub = PubNubAsyncio(pnconf_pam_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = 'test-here-now-asyncio-uuid1' + + env = yield from pubnub.here_now().future() + assert isinstance(env.result, PNHereNowResult) + + env = yield from pubnub.here_now().channel_groups("gr").include_uuids(True).include_state(True).future() + assert isinstance(env.result, PNHereNowResult) + + env = yield from pubnub.here_now().channels('ch.bar*').channel_groups("gr.k").future() + assert isinstance(env.result, PNHereNowResult) + + env = yield from pubnub.here_now().channels(['ch.bar*', 'ch2']).channel_groups("gr.k").future() + assert isinstance(env.result, PNHereNowResult) + + pubnub.stop() diff --git a/tests/integrational/asyncio/test_invocations.py b/tests/integrational/asyncio/test_invocations.py new file mode 100644 index 00000000..11afb420 --- /dev/null +++ b/tests/integrational/asyncio/test_invocations.py @@ -0,0 +1,100 @@ +import logging + +import pytest +import pubnub as pn + +from pubnub.exceptions import PubNubException +from pubnub.models.consumer.pubsub import PNPublishResult +from pubnub.pubnub_asyncio import PubNubAsyncio, AsyncioEnvelope, PubNubAsyncioException +from tests.helper import pnconf_copy +from tests.integrational.vcr_helper import pn_vcr + +pn.set_stream_logger('pubnub', logging.DEBUG) + +ch = "asyncio-int-publish" +corrupted_keys = pnconf_copy() +corrupted_keys.publish_key = "blah" +corrupted_keys.subscribe_key = "blah" + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/invocations/future.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) +@pytest.mark.asyncio +def test_publish_future(event_loop): + pubnub = PubNubAsyncio(pnconf_copy(), custom_event_loop=event_loop) + result = yield from pubnub.publish().message('hey').channel('blah').result() + assert isinstance(result, PNPublishResult) + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/invocations/future_raises_pubnub_error.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) +@pytest.mark.asyncio +def test_publish_future_raises_pubnub_error(event_loop): + pubnub = PubNubAsyncio(corrupted_keys, custom_event_loop=event_loop) + + with pytest.raises(PubNubException) as exinfo: + yield from pubnub.publish().message('hey').channel('blah').result() + + assert 'Invalid Subscribe Key' in str(exinfo.value) + assert 400 == exinfo.value._status_code + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/invocations/future_raises_ll_error.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) +@pytest.mark.asyncio +def test_publish_future_raises_lower_level_error(event_loop): + pubnub = PubNubAsyncio(corrupted_keys, custom_event_loop=event_loop) + + pubnub._connector.close() + + with pytest.raises(RuntimeError) as exinfo: + yield from pubnub.publish().message('hey').channel('blah').result() + + assert 'Session is closed' in str(exinfo.value) + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/invocations/envelope.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) +@pytest.mark.asyncio +def test_publish_envelope(event_loop): + pubnub = PubNubAsyncio(pnconf_copy(), custom_event_loop=event_loop) + envelope = yield from pubnub.publish().message('hey').channel('blah').future() + assert isinstance(envelope, AsyncioEnvelope) + assert not envelope.is_error() + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/invocations/envelope_raises.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) +@pytest.mark.asyncio +def test_publish_envelope_raises(event_loop): + pubnub = PubNubAsyncio(corrupted_keys, custom_event_loop=event_loop) + e = yield from pubnub.publish().message('hey').channel('blah').future() + assert isinstance(e, PubNubAsyncioException) + assert e.is_error() + assert 400 == e.value()._status_code + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/invocations/envelope_raises_ll_error.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) +@pytest.mark.asyncio +def test_publish_envelope_raises_lower_level_error(event_loop): + pubnub = PubNubAsyncio(corrupted_keys, custom_event_loop=event_loop) + + pubnub._connector.close() + + e = yield from pubnub.publish().message('hey').channel('blah').future() + assert isinstance(e, PubNubAsyncioException) + assert e.is_error() + assert str(e.value()) == 'Session is closed' + + pubnub.stop() diff --git a/tests/integrational/asyncio/test_pam.py b/tests/integrational/asyncio/test_pam.py new file mode 100644 index 00000000..2599a732 --- /dev/null +++ b/tests/integrational/asyncio/test_pam.py @@ -0,0 +1,356 @@ +import pytest + +from pubnub.models.consumer.access_manager import PNAccessManagerGrantResult, PNAccessManagerAuditResult +from pubnub.pubnub_asyncio import PubNubAsyncio +from tests.helper import pnconf_pam_copy +from tests.integrational.vcr_helper import pn_vcr + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/pam/global_level.yaml', + filter_query_parameters=['signature', 'timestamp', 'pnsdk']) +@pytest.mark.asyncio +def test_global_level(event_loop): + pubnub = PubNubAsyncio(pnconf_pam_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = "my_uuid" + + env = (yield from pubnub.grant() + .write(True) + .read(True) + .future()) + + assert isinstance(env.result, PNAccessManagerGrantResult) + assert len(env.result.channels) == 0 + assert len(env.result.groups) == 0 + assert env.result.read_enabled is True + assert env.result.write_enabled is True + assert env.result.manage_enabled is False + + env = (yield from pubnub.audit() + .future()) + + assert isinstance(env.result, PNAccessManagerAuditResult) + assert len(env.result.channels) >= 0 + assert len(env.result.groups) >= 0 + assert env.result.read_enabled is True + assert env.result.write_enabled is True + assert env.result.manage_enabled is False + + env = yield from pubnub.revoke().future() + + assert isinstance(env.result, PNAccessManagerGrantResult) + assert len(env.result.channels) == 0 + assert len(env.result.groups) == 0 + assert env.result.read_enabled is False + assert env.result.write_enabled is False + assert env.result.manage_enabled is False + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/pam/single_channel.yaml', + filter_query_parameters=['signature', 'timestamp', 'pnsdk']) +@pytest.mark.asyncio +def test_single_channel(event_loop): + pubnub = PubNubAsyncio(pnconf_pam_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = "my_uuid" + ch = "test-pam-asyncio-ch" + + env = (yield from pubnub.grant() + .channels(ch) + .write(True) + .read(True) + .future()) + + assert isinstance(env.result, PNAccessManagerGrantResult) + assert env.result.channels[ch].read_enabled == 1 + assert env.result.channels[ch].write_enabled == 1 + assert env.result.channels[ch].manage_enabled == 0 + + env = (yield from pubnub.audit() + .channels(ch) + .future()) + + assert isinstance(env.result, PNAccessManagerAuditResult) + assert env.result.channels[ch].read_enabled == 1 + assert env.result.channels[ch].write_enabled == 1 + assert env.result.channels[ch].manage_enabled == 0 + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/pam/single_channel_with_auth.yaml', + filter_query_parameters=['signature', 'timestamp', 'pnsdk']) +@pytest.mark.asyncio +def test_single_channel_with_auth(event_loop): + pubnub = PubNubAsyncio(pnconf_pam_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = "test-pam-asyncio-uuid" + ch = "test-pam-asyncio-ch" + auth = "test-pam-asyncio-auth" + + env = (yield from pubnub.grant() + .channels(ch) + .write(True) + .read(True) + .auth_keys(auth) + .future()) + + assert isinstance(env.result, PNAccessManagerGrantResult) + assert env.result.channels[ch].auth_keys[auth].read_enabled == 1 + assert env.result.channels[ch].auth_keys[auth].write_enabled == 1 + assert env.result.channels[ch].auth_keys[auth].manage_enabled == 0 + + env = (yield from pubnub.audit() + .channels(ch) + .auth_keys(auth) + .future()) + + assert isinstance(env.result, PNAccessManagerAuditResult) + assert env.result.channels[ch].auth_keys[auth].read_enabled == 1 + assert env.result.channels[ch].auth_keys[auth].write_enabled == 1 + assert env.result.channels[ch].auth_keys[auth].manage_enabled == 0 + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/pam/multiple_channels.yaml', + filter_query_parameters=['signature', 'timestamp', 'pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'path', 'string_list_in_query'], + match_on_kwargs={ + 'list_keys': ['channel'], + 'filter_keys': ['signature', 'timestamp'] + }) +@pytest.mark.asyncio +def test_multiple_channels(event_loop): + pubnub = PubNubAsyncio(pnconf_pam_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = "test-pam-asyncio-uuid" + ch1 = "test-pam-asyncio-ch1" + ch2 = "test-pam-asyncio-ch2" + + env = (yield from pubnub.grant() + .channels([ch1, ch2]) + .write(True) + .read(True) + .future()) + + assert isinstance(env.result, PNAccessManagerGrantResult) + assert env.result.channels[ch1].read_enabled is True + assert env.result.channels[ch2].read_enabled is True + assert env.result.channels[ch1].write_enabled is True + assert env.result.channels[ch2].write_enabled is True + assert env.result.channels[ch1].manage_enabled is False + assert env.result.channels[ch2].manage_enabled is False + + env = (yield from pubnub.audit() + .channels([ch1, ch2]) + .future()) + + assert isinstance(env.result, PNAccessManagerAuditResult) + assert env.result.channels[ch1].read_enabled is True + assert env.result.channels[ch2].read_enabled is True + assert env.result.channels[ch1].write_enabled is True + assert env.result.channels[ch2].write_enabled is True + assert env.result.channels[ch1].manage_enabled is False + assert env.result.channels[ch2].manage_enabled is False + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/pam/multiple_channels_with_auth.yaml', + filter_query_parameters=['signature', 'timestamp', 'pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'path', 'string_list_in_query'], + match_on_kwargs={ + 'list_keys': ['channel'], + 'filter_keys': ['signature', 'timestamp'] + }) +@pytest.mark.asyncio +def test_multiple_channels_with_auth(event_loop): + pubnub = PubNubAsyncio(pnconf_pam_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = "my_uuid" + ch1 = "test-pam-asyncio-ch1" + ch2 = "test-pam-asyncio-ch2" + auth = "test-pam-asyncio-auth" + + env = (yield from pubnub.grant() + .channels([ch1, ch2]) + .write(True) + .read(True) + .auth_keys(auth) + .future()) + + assert isinstance(env.result, PNAccessManagerGrantResult) + assert env.result.channels[ch1].auth_keys[auth].read_enabled is True + assert env.result.channels[ch2].auth_keys[auth].read_enabled is True + assert env.result.channels[ch1].auth_keys[auth].write_enabled is True + assert env.result.channels[ch2].auth_keys[auth].write_enabled is True + assert env.result.channels[ch1].auth_keys[auth].manage_enabled is False + assert env.result.channels[ch2].auth_keys[auth].manage_enabled is False + + env = (yield from pubnub.audit() + .channels([ch1, ch2]) + .future()) + + assert isinstance(env.result, PNAccessManagerAuditResult) + assert env.result.channels[ch1].auth_keys[auth].read_enabled is True + assert env.result.channels[ch2].auth_keys[auth].read_enabled is True + assert env.result.channels[ch1].auth_keys[auth].write_enabled is True + assert env.result.channels[ch2].auth_keys[auth].write_enabled is True + assert env.result.channels[ch1].auth_keys[auth].manage_enabled is False + assert env.result.channels[ch2].auth_keys[auth].manage_enabled is False + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/pam/single_channel_group.yaml', + filter_query_parameters=['signature', 'timestamp', 'pnsdk']) +@pytest.mark.asyncio +def test_single_channel_group(event_loop): + pubnub = PubNubAsyncio(pnconf_pam_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = "test-pam-asyncio-uuid" + cg = "test-pam-asyncio-cg" + + env = (yield from pubnub.grant() + .channel_groups(cg) + .write(True) + .read(True) + .future()) + + assert isinstance(env.result, PNAccessManagerGrantResult) + assert env.result.level == 'channel-group' + assert env.result.groups[cg].read_enabled == 1 + assert env.result.groups[cg].write_enabled == 1 + assert env.result.groups[cg].manage_enabled == 0 + + env = (yield from pubnub.audit() + .channel_groups(cg) + .future()) + + assert isinstance(env.result, PNAccessManagerAuditResult) + assert env.result.level == 'channel-group' + assert env.result.groups[cg].read_enabled == 1 + assert env.result.groups[cg].write_enabled == 1 + assert env.result.groups[cg].manage_enabled == 0 + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/pam/single_channel_group_with_auth.yaml', + filter_query_parameters=['signature', 'timestamp', 'pnsdk']) +@pytest.mark.asyncio +def test_single_channel_group_with_auth(event_loop): + pubnub = PubNubAsyncio(pnconf_pam_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = "test-pam-asyncio-uuid" + gr = "test-pam-asyncio-cg" + auth = "test-pam-asyncio-auth" + + env = (yield from pubnub.grant() + .channel_groups(gr) + .write(True) + .read(True) + .auth_keys(auth) + .future()) + + assert isinstance(env.result, PNAccessManagerGrantResult) + assert env.result.level == 'channel-group+auth' + assert env.result.groups[gr].auth_keys[auth].read_enabled == 1 + assert env.result.groups[gr].auth_keys[auth].write_enabled == 1 + assert env.result.groups[gr].auth_keys[auth].manage_enabled == 0 + + env = (yield from pubnub.audit() + .channel_groups(gr) + .auth_keys(auth) + .future()) + + assert isinstance(env.result, PNAccessManagerAuditResult) + assert env.result.groups[gr].auth_keys[auth].read_enabled == 1 + assert env.result.groups[gr].auth_keys[auth].write_enabled == 1 + assert env.result.groups[gr].auth_keys[auth].manage_enabled == 0 + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/pam/multiple_channel_groups.yaml', + filter_query_parameters=['signature', 'timestamp', 'pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'path', 'string_list_in_query'], + match_on_kwargs={ + 'list_keys': ['channel-group'], + 'filter_keys': ['signature', 'timestamp'] + }) +@pytest.mark.asyncio +def test_multiple_channel_groups(event_loop): + pubnub = PubNubAsyncio(pnconf_pam_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = "my_uuid" + gr1 = "test-pam-asyncio-cg1" + gr2 = "test-pam-asyncio-cg2" + + env = (yield from pubnub.grant() + .channel_groups([gr1, gr2]) + .write(True) + .read(True) + .future()) + + assert isinstance(env.result, PNAccessManagerGrantResult) + assert env.result.groups[gr1].read_enabled is True + assert env.result.groups[gr2].read_enabled is True + assert env.result.groups[gr1].write_enabled is True + assert env.result.groups[gr2].write_enabled is True + assert env.result.groups[gr1].manage_enabled is False + assert env.result.groups[gr2].manage_enabled is False + + env = (yield from pubnub.audit() + .channel_groups([gr1, gr2]) + .future()) + + assert isinstance(env.result, PNAccessManagerAuditResult) + assert env.result.groups[gr1].read_enabled is True + assert env.result.groups[gr2].read_enabled is True + assert env.result.groups[gr1].write_enabled is True + assert env.result.groups[gr2].write_enabled is True + assert env.result.groups[gr1].manage_enabled is False + assert env.result.groups[gr2].manage_enabled is False + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/pam/multiple_channel_groups_with_auth.yaml', + filter_query_parameters=['signature', 'timestamp', 'pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'path', 'string_list_in_query'], + match_on_kwargs={ + 'list_keys': ['channel-group'], + 'filter_keys': ['signature', 'timestamp'] + }) +@pytest.mark.asyncio +def test_multiple_channel_groups_with_auth(event_loop): + pubnub = PubNubAsyncio(pnconf_pam_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = "my_uuid" + gr1 = "test-pam-asyncio-cg1" + gr2 = "test-pam-asyncio-cg2" + auth = "test-pam-asyncio-auth" + + env = (yield from pubnub.grant() + .channel_groups([gr1, gr2]) + .write(True) + .read(True) + .auth_keys(auth) + .future()) + + assert isinstance(env.result, PNAccessManagerGrantResult) + assert env.result.groups[gr1].auth_keys[auth].read_enabled is True + assert env.result.groups[gr2].auth_keys[auth].read_enabled is True + assert env.result.groups[gr1].auth_keys[auth].write_enabled is True + assert env.result.groups[gr2].auth_keys[auth].write_enabled is True + assert env.result.groups[gr1].auth_keys[auth].manage_enabled is False + assert env.result.groups[gr2].auth_keys[auth].manage_enabled is False + + env = (yield from pubnub.audit() + .channel_groups([gr1, gr2]) + .future()) + + assert isinstance(env.result, PNAccessManagerAuditResult) + assert env.result.groups[gr1].auth_keys[auth].read_enabled is True + assert env.result.groups[gr2].auth_keys[auth].read_enabled is True + assert env.result.groups[gr1].auth_keys[auth].write_enabled is True + assert env.result.groups[gr2].auth_keys[auth].write_enabled is True + assert env.result.groups[gr1].auth_keys[auth].manage_enabled is False + assert env.result.groups[gr2].auth_keys[auth].manage_enabled is False + + pubnub.stop() diff --git a/tests/integrational/asyncio/test_publish.py b/tests/integrational/asyncio/test_publish.py new file mode 100644 index 00000000..1047acf0 --- /dev/null +++ b/tests/integrational/asyncio/test_publish.py @@ -0,0 +1,256 @@ +import logging + +import asyncio +import pytest +import pubnub as pn + +from pubnub.exceptions import PubNubException +from pubnub.models.consumer.common import PNStatus +from pubnub.models.consumer.pubsub import PNPublishResult +from pubnub.pnconfiguration import PNConfiguration +from pubnub.pubnub_asyncio import PubNubAsyncio, AsyncioEnvelope, PubNubAsyncioException +from tests.helper import pnconf_copy, pnconf_enc_copy, gen_decrypt_func, pnconf_pam_copy +from tests.integrational.vcr_helper import pn_vcr + +pn.set_stream_logger('pubnub', logging.DEBUG) + +ch = "asyncio-int-publish" + + +@pytest.mark.asyncio +def assert_success_await(pub): + envelope = yield from pub.future() + + assert isinstance(envelope, AsyncioEnvelope) + assert isinstance(envelope.result, PNPublishResult) + assert isinstance(envelope.status, PNStatus) + assert envelope.result.timetoken > 0 + assert len(envelope.status.original_response) > 0 + + +@pytest.mark.asyncio +def assert_client_side_error(pub, expected_err_msg): + try: + yield from pub.future() + except PubNubException as e: + assert expected_err_msg in str(e) + + +@pytest.mark.asyncio +def assert_success_publish_get(pubnub, msg): + yield from assert_success_await(pubnub.publish().channel(ch).message(msg)) + + +@pytest.mark.asyncio +def assert_success_publish_post(pubnub, msg): + yield from assert_success_await(pubnub.publish().channel(ch).message(msg).use_post(True)) + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/publish/mixed_via_get.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) +@pytest.mark.asyncio +def test_publish_mixed_via_get(event_loop): + pubnub = PubNubAsyncio(pnconf_copy(), custom_event_loop=event_loop) + yield from asyncio.gather( + asyncio.ensure_future(assert_success_publish_get(pubnub, "hi")), + asyncio.ensure_future(assert_success_publish_get(pubnub, 5)), + asyncio.ensure_future(assert_success_publish_get(pubnub, True)), + asyncio.ensure_future(assert_success_publish_get(pubnub, ["hi", "hi2", "hi3"]))) + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/publish/object_via_get.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'object_in_path', 'query']) +@pytest.mark.asyncio +def test_publish_object_via_get(event_loop): + pubnub = PubNubAsyncio(pnconf_copy(), custom_event_loop=event_loop) + yield from asyncio.ensure_future(assert_success_publish_get(pubnub, {"name": "Alex", "online": True})) + + pubnub.stop() + + +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/publish/mixed_via_post.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) +@pytest.mark.asyncio +def test_publish_mixed_via_post(event_loop): + pubnub = PubNubAsyncio(pnconf_copy(), custom_event_loop=event_loop) + yield from asyncio.gather( + asyncio.ensure_future(assert_success_publish_post(pubnub, "hi")), + asyncio.ensure_future(assert_success_publish_post(pubnub, 5)), + asyncio.ensure_future(assert_success_publish_post(pubnub, True)), + asyncio.ensure_future(assert_success_publish_post(pubnub, ["hi", "hi2", "hi3"]))) + + pubnub.stop() + + +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/publish/object_via_post.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'path', 'query', 'object_in_body']) +@pytest.mark.asyncio +def test_publish_object_via_post(event_loop): + pubnub = PubNubAsyncio(pnconf_copy(), custom_event_loop=event_loop) + yield from asyncio.ensure_future(assert_success_publish_post(pubnub, {"name": "Alex", "online": True})) + + pubnub.stop() + + +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/publish/mixed_via_get_encrypted.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) +@pytest.mark.asyncio +def test_publish_mixed_via_get_encrypted(event_loop): + pubnub = PubNubAsyncio(pnconf_enc_copy(), custom_event_loop=event_loop) + yield from asyncio.gather( + asyncio.ensure_future(assert_success_publish_get(pubnub, "hi")), + asyncio.ensure_future(assert_success_publish_get(pubnub, 5)), + asyncio.ensure_future(assert_success_publish_get(pubnub, True)), + asyncio.ensure_future(assert_success_publish_get(pubnub, ["hi", "hi2", "hi3"]))) + + pubnub.stop() + + +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/publish/object_via_get_encrypted.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['host', 'method', 'query', 'object_in_path'], + match_on_kwargs={'object_in_path': { + 'decrypter': gen_decrypt_func('testKey')}}) +@pytest.mark.asyncio +def test_publish_object_via_get_encrypted(event_loop): + pubnub = PubNubAsyncio(pnconf_enc_copy(), custom_event_loop=event_loop) + yield from asyncio.ensure_future(assert_success_publish_get(pubnub, {"name": "Alex", "online": True})) + + pubnub.stop() + + +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/publish/mixed_via_post_encrypted.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['method', 'path', 'query', 'body']) +@pytest.mark.asyncio +def test_publish_mixed_via_post_encrypted(event_loop): + pubnub = PubNubAsyncio(pnconf_enc_copy(), custom_event_loop=event_loop) + yield from asyncio.gather( + asyncio.ensure_future(assert_success_publish_post(pubnub, "hi")), + asyncio.ensure_future(assert_success_publish_post(pubnub, 5)), + asyncio.ensure_future(assert_success_publish_post(pubnub, True)), + asyncio.ensure_future(assert_success_publish_post(pubnub, ["hi", "hi2", "hi3"]))) + + pubnub.stop() + + +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/publish/object_via_post_encrypted.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['method', 'path', 'query', 'object_in_body'], + match_on_kwargs={'object_in_body': { + 'decrypter': gen_decrypt_func('testKey')}}) +@pytest.mark.asyncio +def test_publish_object_via_post_encrypted(event_loop): + pubnub = PubNubAsyncio(pnconf_enc_copy(), custom_event_loop=event_loop) + yield from asyncio.ensure_future(assert_success_publish_post(pubnub, {"name": "Alex", "online": True})) + + pubnub.stop() + + +@pytest.mark.asyncio +def test_error_missing_message(event_loop): + pubnub = PubNubAsyncio(pnconf_copy(), custom_event_loop=event_loop) + yield from assert_client_side_error(pubnub.publish().channel(ch).message(None), "Message missing") + + pubnub.stop() + + +@pytest.mark.asyncio +def test_error_missing_channel(event_loop): + pubnub = PubNubAsyncio(pnconf_copy(), custom_event_loop=event_loop) + yield from assert_client_side_error(pubnub.publish().channel("").message("hey"), "Channel missing") + + pubnub.stop() + + +@pytest.mark.asyncio +def test_error_non_serializable(event_loop): + pubnub = PubNubAsyncio(pnconf_copy(), custom_event_loop=event_loop) + + def method(): + pass + + yield from assert_client_side_error(pubnub.publish().channel(ch).message(method), "not JSON serializable") + pubnub.stop() + + +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/publish/meta_object.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['host', 'method', 'path', 'meta_object_in_query']) +@pytest.mark.asyncio +def test_publish_with_meta(event_loop): + pubnub = PubNubAsyncio(pnconf_copy(), custom_event_loop=event_loop) + + yield from assert_success_await(pubnub.publish().channel(ch).message("hey").meta({'a': 2, 'b': 'qwer'})) + pubnub.stop() + + +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/publish/do_not_store.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) +@pytest.mark.asyncio +def test_publish_do_not_store(event_loop): + pubnub = PubNubAsyncio(pnconf_copy(), custom_event_loop=event_loop) + + yield from assert_success_await(pubnub.publish().channel(ch).message("hey").should_store(False)) + pubnub.stop() + + +@pytest.mark.asyncio +def assert_server_side_error_yield(pub, expected_err_msg): + try: + yield from pub.future() + except PubNubAsyncioException as e: + assert expected_err_msg in str(e) + + +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/publish/invalid_key.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) +@pytest.mark.asyncio +def test_error_invalid_key(event_loop): + conf = PNConfiguration() + conf.publish_key = "fake" + conf.subscribe_key = "demo" + conf.enable_subscribe = False + + pubnub = PubNubAsyncio(conf, custom_event_loop=event_loop) + + yield from assert_server_side_error_yield(pubnub.publish().channel(ch).message("hey"), "Invalid Key") + pubnub.stop() + + +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/publish/not_permitted.yaml', + filter_query_parameters=['uuid', 'seqn', 'signature', 'timestamp', 'pnsdk']) +@pytest.mark.asyncio +def test_not_permitted(event_loop): + pnconf = pnconf_pam_copy() + pnconf.secret_key = None + pubnub = PubNubAsyncio(pnconf, custom_event_loop=event_loop) + + yield from assert_server_side_error_yield(pubnub.publish().channel(ch).message("hey"), "HTTP Client Error (403") + pubnub.stop() + + +@pytest.mark.asyncio +def test_publish_super_admin_call(event_loop): + pubnub = PubNubAsyncio(pnconf_pam_copy(), custom_event_loop=event_loop) + + yield from pubnub.publish().channel(ch).message("hey").future() + yield from pubnub.publish().channel("foo.bar").message("hey^&#$").should_store(True).meta({ + 'name': 'alex' + }).future() + + pubnub.stop() diff --git a/tests/integrational/asyncio/test_ssl.py b/tests/integrational/asyncio/test_ssl.py new file mode 100644 index 00000000..8da8c6ea --- /dev/null +++ b/tests/integrational/asyncio/test_ssl.py @@ -0,0 +1,23 @@ +import logging + +import pytest +import pubnub as pn + +from pubnub.pubnub_asyncio import PubNubAsyncio +from tests.helper import pnconf_ssl_copy +from tests.integrational.vcr_helper import pn_vcr + +pn.set_stream_logger('pubnub', logging.DEBUG) + +ch = "asyncio-int-publish" + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/secure/ssl.yaml', + filter_query_parameters=['uuid', 'pnsdk']) +@pytest.mark.asyncio +def test_publish_string_via_get_encrypted(event_loop): + pubnub = PubNubAsyncio(pnconf_ssl_copy(), custom_event_loop=event_loop) + res = yield from pubnub.publish().channel(ch).message("hey").future() + assert res.result.timetoken > 0 + + pubnub.stop() diff --git a/tests/integrational/asyncio/test_state.py b/tests/integrational/asyncio/test_state.py new file mode 100644 index 00000000..84c56b05 --- /dev/null +++ b/tests/integrational/asyncio/test_state.py @@ -0,0 +1,139 @@ +import asyncio +import pytest +import logging +import pubnub as pn + +from pubnub.models.consumer.presence import PNSetStateResult, PNGetStateResult +from pubnub.pubnub_asyncio import PubNubAsyncio +from tests.helper import pnconf, pnconf_copy, pnconf_sub_copy, pnconf_pam_copy +from tests.integrational.vcr_asyncio_sleeper import get_sleeper, VCR599Listener +from tests.integrational.vcr_helper import pn_vcr + + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/state/single_channel.yaml', + filter_query_parameters=['uuid', 'pnsdk'], + match_on=['method', 'host', 'path', 'state_object_in_query']) +@pytest.mark.asyncio +def test_single_channelx(event_loop): + pubnub = PubNubAsyncio(pnconf_copy(), custom_event_loop=event_loop) + ch = 'test-state-asyncio-ch' + pubnub.config.uuid = 'test-state-asyncio-uuid' + state = {"name": "Alex", "count": 5} + + env = yield from pubnub.set_state() \ + .channels(ch) \ + .state(state) \ + .future() + + assert env.result.state['name'] == "Alex" + assert env.result.state['count'] == 5 + + env = yield from pubnub.get_state() \ + .channels(ch) \ + .future() + + assert env.result.channels[ch]['name'] == "Alex" + assert env.result.channels[ch]['count'] == 5 + + pubnub.stop() + + +@get_sleeper('tests/integrational/fixtures/asyncio/state/single_channel_with_subscription.yaml') +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/state/single_channel_with_subscription.yaml', + filter_query_parameters=['uuid', 'pnsdk'], + match_on=['method', 'host', 'path', 'state_object_in_query']) +@pytest.mark.asyncio +def test_single_channel_with_subscription(event_loop, sleeper=asyncio.sleep): + pnconf = pnconf_sub_copy() + pnconf.set_presence_timeout(12) + pubnub = PubNubAsyncio(pnconf, custom_event_loop=event_loop) + ch = 'test-state-asyncio-ch' + pubnub.config.uuid = 'test-state-asyncio-uuid' + state = {"name": "Alex", "count": 5} + + callback = VCR599Listener(1) + pubnub.add_listener(callback) + pubnub.subscribe().channels(ch).execute() + + yield from callback.wait_for_connect() + yield from sleeper(20) + + env = yield from pubnub.set_state() \ + .channels(ch) \ + .state(state) \ + .future() + + assert env.result.state['name'] == "Alex" + assert env.result.state['count'] == 5 + + env = yield from pubnub.get_state() \ + .channels(ch) \ + .future() + + assert env.result.channels[ch]['name'] == "Alex" + assert env.result.channels[ch]['count'] == 5 + + pubnub.unsubscribe().channels(ch).execute() + yield from callback.wait_for_disconnect() + + pubnub.stop() + + +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/state/multiple_channel.yaml', + filter_query_parameters=['uuid', 'pnsdk'], + match_on=['method', 'host', 'path', 'state_object_in_query']) +@pytest.mark.asyncio +def test_multiple_channels(event_loop): + pubnub = PubNubAsyncio(pnconf, custom_event_loop=event_loop) + ch1 = 'test-state-asyncio-ch1' + ch2 = 'test-state-asyncio-ch2' + pubnub.config.uuid = 'test-state-asyncio-uuid' + state = {"name": "Alex", "count": 5} + + env = yield from pubnub.set_state() \ + .channels([ch1, ch2]) \ + .state(state) \ + .future() + + assert env.result.state['name'] == "Alex" + assert env.result.state['count'] == 5 + + env = yield from pubnub.get_state() \ + .channels([ch1, ch2]) \ + .future() + + assert env.result.channels[ch1]['name'] == "Alex" + assert env.result.channels[ch2]['name'] == "Alex" + assert env.result.channels[ch1]['count'] == 5 + assert env.result.channels[ch2]['count'] == 5 + + pubnub.stop() + + +@pytest.mark.asyncio +def test_state_super_admin_call(event_loop): + pnconf = pnconf_pam_copy() + pubnub = PubNubAsyncio(pnconf, custom_event_loop=event_loop) + ch1 = 'test-state-asyncio-ch1' + ch2 = 'test-state-asyncio-ch2' + pubnub.config.uuid = 'test-state-asyncio-uuid' + state = {"name": "Alex", "count": 5} + + env = yield from pubnub.set_state() \ + .channels([ch1, ch2]) \ + .state(state) \ + .future() + assert isinstance(env.result, PNSetStateResult) + + env = yield from pubnub.get_state() \ + .channels([ch1, ch2]) \ + .future() + assert isinstance(env.result, PNGetStateResult) + + pubnub.stop() diff --git a/tests/integrational/asyncio/test_subscribe.py b/tests/integrational/asyncio/test_subscribe.py new file mode 100644 index 00000000..64177e4b --- /dev/null +++ b/tests/integrational/asyncio/test_subscribe.py @@ -0,0 +1,387 @@ +import logging +import asyncio +import pytest +import pubnub as pn + +from pubnub.models.consumer.pubsub import PNMessageResult +from pubnub.pubnub_asyncio import PubNubAsyncio, AsyncioEnvelope, SubscribeListener +from tests.helper import pnconf_sub_copy, pnconf_enc_sub_copy +from tests.integrational.vcr_asyncio_sleeper import get_sleeper, VCR599Listener, VCR599ReconnectionManager +from tests.integrational.vcr_helper import pn_vcr + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +def patch_pubnub(pubnub): + pubnub._subscription_manager._reconnection_manager = VCR599ReconnectionManager(pubnub) + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/subscription/sub_unsub.yaml', + filter_query_parameters=['uuid', 'pnsdk']) +@pytest.mark.asyncio +def test_subscribe_unsubscribe(event_loop): + channel = "test-subscribe-asyncio-ch" + + pubnub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + + callback = SubscribeListener() + pubnub.add_listener(callback) + + pubnub.subscribe().channels(channel).execute() + assert channel in pubnub.get_subscribed_channels() + assert len(pubnub.get_subscribed_channels()) == 1 + + yield from callback.wait_for_connect() + assert channel in pubnub.get_subscribed_channels() + assert len(pubnub.get_subscribed_channels()) == 1 + + pubnub.unsubscribe().channels(channel).execute() + assert channel not in pubnub.get_subscribed_channels() + assert len(pubnub.get_subscribed_channels()) == 0 + + yield from callback.wait_for_disconnect() + assert channel not in pubnub.get_subscribed_channels() + assert len(pubnub.get_subscribed_channels()) == 0 + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/subscription/sub_pub_unsub.yaml', + filter_query_parameters=['pnsdk']) +@pytest.mark.asyncio +def test_subscribe_publish_unsubscribe(event_loop): + pubnub_sub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + pubnub_pub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + + patch_pubnub(pubnub_sub) + patch_pubnub(pubnub_pub) + + pubnub_sub.config.uuid = 'test-subscribe-asyncio-uuid-sub' + pubnub_pub.config.uuid = 'test-subscribe-asyncio-uuid-pub' + + callback = VCR599Listener(1) + channel = "test-subscribe-asyncio-ch" + message = "hey" + pubnub_sub.add_listener(callback) + pubnub_sub.subscribe().channels(channel).execute() + + yield from callback.wait_for_connect() + + publish_future = asyncio.ensure_future(pubnub_pub.publish().channel(channel).message(message).future()) + subscribe_message_future = asyncio.ensure_future(callback.wait_for_message_on(channel)) + + yield from asyncio.wait([ + publish_future, + subscribe_message_future + ]) + + publish_envelope = publish_future.result() + subscribe_envelope = subscribe_message_future.result() + + assert isinstance(subscribe_envelope, PNMessageResult) + assert subscribe_envelope.channel == channel + assert subscribe_envelope.subscription is None + assert subscribe_envelope.message == message + assert subscribe_envelope.timetoken > 0 + + assert isinstance(publish_envelope, AsyncioEnvelope) + assert publish_envelope.result.timetoken > 0 + assert publish_envelope.status.original_response[0] == 1 + + pubnub_sub.unsubscribe().channels(channel).execute() + yield from callback.wait_for_disconnect() + + pubnub_pub.stop() + pubnub_sub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/subscription/sub_pub_unsub_enc.yaml', + filter_query_parameters=['pnsdk']) +@pytest.mark.asyncio +def test_encrypted_subscribe_publish_unsubscribe(event_loop): + pubnub = PubNubAsyncio(pnconf_enc_sub_copy(), custom_event_loop=event_loop) + pubnub.config.uuid = 'test-subscribe-asyncio-uuid' + + callback = VCR599Listener(1) + channel = "test-subscribe-asyncio-ch" + message = "hey" + pubnub.add_listener(callback) + pubnub.subscribe().channels(channel).execute() + + yield from callback.wait_for_connect() + + publish_future = asyncio.ensure_future(pubnub.publish().channel(channel).message(message).future()) + subscribe_message_future = asyncio.ensure_future(callback.wait_for_message_on(channel)) + + yield from asyncio.wait([ + publish_future, + subscribe_message_future + ]) + + publish_envelope = publish_future.result() + subscribe_envelope = subscribe_message_future.result() + + assert isinstance(subscribe_envelope, PNMessageResult) + assert subscribe_envelope.channel == channel + assert subscribe_envelope.subscription is None + assert subscribe_envelope.message == message + assert subscribe_envelope.timetoken > 0 + + assert isinstance(publish_envelope, AsyncioEnvelope) + assert publish_envelope.result.timetoken > 0 + assert publish_envelope.status.original_response[0] == 1 + + pubnub.unsubscribe().channels(channel).execute() + yield from callback.wait_for_disconnect() + + pubnub.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/subscription/join_leave.yaml', + filter_query_parameters=['pnsdk']) +@pytest.mark.asyncio +def test_join_leave(event_loop): + channel = "test-subscribe-asyncio-join-leave-ch" + + pubnub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + pubnub_listener = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + + patch_pubnub(pubnub) + patch_pubnub(pubnub_listener) + + pubnub.config.uuid = "test-subscribe-asyncio-messenger" + pubnub_listener.config.uuid = "test-subscribe-asyncio-listener" + + callback_presence = VCR599Listener(1) + callback_messages = VCR599Listener(1) + + pubnub_listener.add_listener(callback_presence) + pubnub_listener.subscribe().channels(channel).with_presence().execute() + + yield from callback_presence.wait_for_connect() + + envelope = yield from callback_presence.wait_for_presence_on(channel) + assert envelope.channel == channel + assert envelope.event == 'join' + assert envelope.uuid == pubnub_listener.uuid + + pubnub.add_listener(callback_messages) + pubnub.subscribe().channels(channel).execute() + yield from callback_messages.wait_for_connect() + + envelope = yield from callback_presence.wait_for_presence_on(channel) + assert envelope.channel == channel + assert envelope.event == 'join' + assert envelope.uuid == pubnub.uuid + + pubnub.unsubscribe().channels(channel).execute() + yield from callback_messages.wait_for_disconnect() + + envelope = yield from callback_presence.wait_for_presence_on(channel) + + assert envelope.channel == channel + assert envelope.event == 'leave' + assert envelope.uuid == pubnub.uuid + + pubnub_listener.unsubscribe().channels(channel).execute() + yield from callback_presence.wait_for_disconnect() + + pubnub.stop() + pubnub_listener.stop() + + +@get_sleeper('tests/integrational/fixtures/asyncio/subscription/cg_sub_unsub.yaml') +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/subscription/cg_sub_unsub.yaml', + filter_query_parameters=['uuid', 'pnsdk']) +@pytest.mark.asyncio +def test_cg_subscribe_unsubscribe(event_loop, sleeper=asyncio.sleep): + ch = "test-subscribe-asyncio-channel" + gr = "test-subscribe-asyncio-group" + + pubnub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + + envelope = yield from pubnub.add_channel_to_channel_group().channel_group(gr).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + yield from sleeper(3) + + callback_messages = SubscribeListener() + pubnub.add_listener(callback_messages) + pubnub.subscribe().channel_groups(gr).execute() + yield from callback_messages.wait_for_connect() + + pubnub.unsubscribe().channel_groups(gr).execute() + yield from callback_messages.wait_for_disconnect() + + envelope = yield from pubnub.remove_channel_from_channel_group().channel_group(gr).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + pubnub.stop() + + +@get_sleeper('tests/integrational/fixtures/asyncio/subscription/cg_sub_pub_unsub.yaml') +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/subscription/cg_sub_pub_unsub.yaml', + filter_query_parameters=['uuid', 'pnsdk']) +@pytest.mark.asyncio +def test_cg_subscribe_publish_unsubscribe(event_loop, sleeper=asyncio.sleep): + ch = "test-subscribe-asyncio-channel" + gr = "test-subscribe-asyncio-group" + message = "hey" + + pubnub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + + envelope = yield from pubnub.add_channel_to_channel_group().channel_group(gr).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + yield from sleeper(1) + + callback_messages = VCR599Listener(1) + pubnub.add_listener(callback_messages) + pubnub.subscribe().channel_groups(gr).execute() + yield from callback_messages.wait_for_connect() + + subscribe_future = asyncio.ensure_future(callback_messages.wait_for_message_on(ch)) + publish_future = asyncio.ensure_future(pubnub.publish().channel(ch).message(message).future()) + yield from asyncio.wait([subscribe_future, publish_future]) + + sub_envelope = subscribe_future.result() + pub_envelope = publish_future.result() + + assert pub_envelope.status.original_response[0] == 1 + assert pub_envelope.status.original_response[1] == 'Sent' + + assert sub_envelope.channel == ch + assert sub_envelope.subscription == gr + assert sub_envelope.message == message + + pubnub.unsubscribe().channel_groups(gr).execute() + yield from callback_messages.wait_for_disconnect() + + envelope = yield from pubnub.remove_channel_from_channel_group().channel_group(gr).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + pubnub.stop() + + +@get_sleeper('tests/integrational/fixtures/asyncio/subscription/cg_join_leave.yaml') +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/subscription/cg_join_leave.yaml', + filter_query_parameters=['pnsdk']) +@pytest.mark.asyncio +def test_cg_join_leave(event_loop, sleeper=asyncio.sleep): + pubnub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + pubnub_listener = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + + pubnub.config.uuid = "test-subscribe-asyncio-messenger" + pubnub_listener.config.uuid = "test-subscribe-asyncio-listener" + + ch = "test-subscribe-asyncio-join-leave-cg-channel" + gr = "test-subscribe-asyncio-join-leave-cg-group" + + envelope = yield from pubnub.add_channel_to_channel_group().channel_group(gr).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + yield from sleeper(1) + + callback_messages = VCR599Listener(1) + callback_presence = VCR599Listener(1) + + pubnub_listener.add_listener(callback_presence) + pubnub_listener.subscribe().channel_groups(gr).with_presence().execute() + yield from callback_presence.wait_for_connect() + + prs_envelope = yield from callback_presence.wait_for_presence_on(ch) + assert prs_envelope.event == 'join' + assert prs_envelope.uuid == pubnub_listener.uuid + assert prs_envelope.channel == ch + assert prs_envelope.subscription == gr + + pubnub.add_listener(callback_messages) + pubnub.subscribe().channel_groups(gr).execute() + + callback_messages_future = asyncio.ensure_future(callback_messages.wait_for_connect()) + presence_messages_future = asyncio.ensure_future(callback_presence.wait_for_presence_on(ch)) + yield from asyncio.wait([callback_messages_future, presence_messages_future]) + prs_envelope = presence_messages_future.result() + + assert prs_envelope.event == 'join' + assert prs_envelope.uuid == pubnub.uuid + assert prs_envelope.channel == ch + assert prs_envelope.subscription == gr + + pubnub.unsubscribe().channel_groups(gr).execute() + + callback_messages_future = asyncio.ensure_future(callback_messages.wait_for_disconnect()) + presence_messages_future = asyncio.ensure_future(callback_presence.wait_for_presence_on(ch)) + yield from asyncio.wait([callback_messages_future, presence_messages_future]) + prs_envelope = presence_messages_future.result() + + assert prs_envelope.event == 'leave' + assert prs_envelope.uuid == pubnub.uuid + assert prs_envelope.channel == ch + assert prs_envelope.subscription == gr + + pubnub_listener.unsubscribe().channel_groups(gr).execute() + yield from callback_presence.wait_for_disconnect() + + envelope = yield from pubnub.remove_channel_from_channel_group().channel_group(gr).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + pubnub.stop() + pubnub_listener.stop() + + +@get_sleeper('tests/integrational/fixtures/asyncio/subscription/unsubscribe_all.yaml') +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/subscription/unsubscribe_all.yaml', + filter_query_parameters=['pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'string_list_in_path', 'string_list_in_query'], + match_on_kwargs={ + 'string_list_in_path': { + 'positions': [4, 6] + }, + 'string_list_in_query': { + 'list_keys': ['channel-group'] + } + } + ) +@pytest.mark.asyncio +def test_unsubscribe_all(event_loop, sleeper=asyncio.sleep): + pubnub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + + pubnub.config.uuid = "test-subscribe-asyncio-messenger" + + ch = "test-subscribe-asyncio-unsubscribe-all-ch" + ch1 = "test-subscribe-asyncio-unsubscribe-all-ch1" + ch2 = "test-subscribe-asyncio-unsubscribe-all-ch2" + ch3 = "test-subscribe-asyncio-unsubscribe-all-ch3" + gr1 = "test-subscribe-asyncio-unsubscribe-all-gr1" + gr2 = "test-subscribe-asyncio-unsubscribe-all-gr2" + + envelope = yield from pubnub.add_channel_to_channel_group().channel_group(gr1).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + envelope = yield from pubnub.add_channel_to_channel_group().channel_group(gr2).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + yield from sleeper(1) + + callback_messages = VCR599Listener(1) + pubnub.add_listener(callback_messages) + + pubnub.subscribe().channels([ch1, ch2, ch3]).channel_groups([gr1, gr2]).execute() + yield from callback_messages.wait_for_connect() + + assert len(pubnub.get_subscribed_channels()) == 3 + assert len(pubnub.get_subscribed_channel_groups()) == 2 + + pubnub.unsubscribe_all() + + yield from callback_messages.wait_for_disconnect() + + assert len(pubnub.get_subscribed_channels()) == 0 + assert len(pubnub.get_subscribed_channel_groups()) == 0 + + envelope = yield from pubnub.remove_channel_from_channel_group().channel_group(gr1).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + envelope = yield from pubnub.remove_channel_from_channel_group().channel_group(gr2).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + pubnub.stop() diff --git a/tests/integrational/asyncio/test_time.py b/tests/integrational/asyncio/test_time.py new file mode 100644 index 00000000..d4765181 --- /dev/null +++ b/tests/integrational/asyncio/test_time.py @@ -0,0 +1,21 @@ +import pytest +from datetime import date + +from pubnub.pubnub_asyncio import PubNubAsyncio +from tests.helper import pnconf +from tests.integrational.vcr_helper import pn_vcr + + +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/time/get.yaml', + filter_query_parameters=['uuid', 'pnsdk']) +@pytest.mark.asyncio +def test_time(event_loop): + pubnub = PubNubAsyncio(pnconf, custom_event_loop=event_loop) + + res = yield from pubnub.time().result() + + assert int(res) > 0 + assert isinstance(res.date_time(), date) + + pubnub.stop() diff --git a/tests/integrational/asyncio/test_unsubscribe_status.py b/tests/integrational/asyncio/test_unsubscribe_status.py new file mode 100644 index 00000000..768849e3 --- /dev/null +++ b/tests/integrational/asyncio/test_unsubscribe_status.py @@ -0,0 +1,80 @@ +import logging +import asyncio +import pytest +from pubnub.enums import PNOperationType, PNStatusCategory + +from pubnub.callbacks import SubscribeCallback + +import pubnub as pn + +from pubnub.pubnub_asyncio import PubNubAsyncio +from tests.helper import pnconf_pam_copy + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +class AccessDeniedListener(SubscribeCallback): + def __init__(self): + self.access_denied_event = asyncio.Event() + + def message(self, pubnub, message): + pass + + def presence(self, pubnub, presence): + pass + + def status(self, pubnub, status): + if status.operation == PNOperationType.PNUnsubscribeOperation: + if status.category == PNStatusCategory.PNAccessDeniedCategory: + self.access_denied_event.set() + + +class ReconnectedListener(SubscribeCallback): + def __init__(self): + self.reconnected_event = asyncio.Event() + + def message(self, pubnub, message): + pass + + def presence(self, pubnub, presence): + pass + + def status(self, pubnub, status): + if status.operation == PNOperationType.PNUnsubscribeOperation: + if status.category == PNStatusCategory.PNReconnectedCategory: + self.reconnected_event.set() + + +@pytest.mark.asyncio +def test_access_denied_unsubscribe_operation(event_loop): + channel = "not-permitted-channel" + pnconf = pnconf_pam_copy() + pnconf.secret_key = None + pnconf.enable_subscribe = True + + pubnub = PubNubAsyncio(pnconf, custom_event_loop=event_loop) + + callback = AccessDeniedListener() + pubnub.add_listener(callback) + + pubnub.subscribe().channels(channel).execute() + yield from callback.access_denied_event.wait() + + pubnub.stop() + +# +# @pytest.mark.asyncio +# async def test_reconnected_unsubscribe_operation(event_loop): +# channel = "not-permitted-channel" +# pnconf = pnconf_pam_copy() +# pnconf.enable_subscribe = True +# +# pubnub = PubNubAsyncio(pnconf, custom_event_loop=event_loop) +# +# callback = ReconnectedListener() +# pubnub.add_listener(callback) +# +# pubnub.subscribe().channels(channel).execute() +# await callback.reconnected_event.wait() +# +# pubnub.stop() diff --git a/tests/integrational/asyncio/test_where_now.py b/tests/integrational/asyncio/test_where_now.py new file mode 100644 index 00000000..67eca92e --- /dev/null +++ b/tests/integrational/asyncio/test_where_now.py @@ -0,0 +1,100 @@ +import asyncio +import pytest + +from pubnub.models.consumer.presence import PNWhereNowResult +from pubnub.pubnub_asyncio import PubNubAsyncio +from tests.helper import pnconf_sub_copy, pnconf_pam_copy +from tests.integrational.vcr_asyncio_sleeper import get_sleeper, VCR599Listener +from tests.integrational.vcr_helper import pn_vcr + + +@get_sleeper('tests/integrational/fixtures/asyncio/where_now/single_channel.yaml') +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/where_now/single_channel.yaml', + filter_query_parameters=['uuid', 'pnsdk']) +@pytest.mark.asyncio +def test_single_channel(event_loop, sleeper=asyncio.sleep): + pubnub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + ch = 'test-where-now-asyncio-ch' + uuid = 'test-where-now-asyncio-uuid' + pubnub.config.uuid = uuid + + callback = VCR599Listener(1) + pubnub.add_listener(callback) + pubnub.subscribe().channels(ch).execute() + + yield from callback.wait_for_connect() + + yield from sleeper(2) + + env = yield from pubnub.where_now() \ + .uuid(uuid) \ + .future() + + channels = env.result.channels + + assert len(channels) == 1 + assert channels[0] == ch + + pubnub.unsubscribe().channels(ch).execute() + yield from callback.wait_for_disconnect() + + pubnub.stop() + + +@get_sleeper('tests/integrational/fixtures/asyncio/where_now/multiple_channels.yaml') +@pn_vcr.use_cassette( + 'tests/integrational/fixtures/asyncio/where_now/multiple_channels.yaml', + filter_query_parameters=['pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'string_list_in_path', 'query'], + match_on_kwargs={ + 'string_list_in_path': { + 'positions': [4] + } + }) +@pytest.mark.asyncio +def test_multiple_channels(event_loop, sleeper=asyncio.sleep): + pubnub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + + ch1 = 'test-where-now-asyncio-ch1' + ch2 = 'test-where-now-asyncio-ch2' + uuid = 'test-where-now-asyncio-uuid' + pubnub.config.uuid = uuid + + callback = VCR599Listener(1) + pubnub.add_listener(callback) + pubnub.subscribe().channels([ch1, ch2]).execute() + + yield from callback.wait_for_connect() + + yield from sleeper(7) + + env = yield from pubnub.where_now() \ + .uuid(uuid) \ + .future() + + channels = env.result.channels + + assert len(channels) == 2 + assert ch1 in channels + assert ch2 in channels + + pubnub.unsubscribe().channels([ch1, ch2]).execute() + yield from callback.wait_for_disconnect() + + pubnub.stop() + + +@pytest.mark.asyncio +def test_where_now_super_admin_call(event_loop): + pubnub = PubNubAsyncio(pnconf_pam_copy(), custom_event_loop=event_loop) + + uuid = 'test-where-now-asyncio-uuid' + pubnub.config.uuid = uuid + + res = yield from pubnub.where_now() \ + .uuid(uuid) \ + .result() + assert isinstance(res, PNWhereNowResult) + + pubnub.stop() diff --git a/tests/integrational/fixtures/asyncio/groups/add_channel_remove_group.yaml b/tests/integrational/fixtures/asyncio/groups/add_channel_remove_group.yaml new file mode 100644 index 00000000..d6707004 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/groups/add_channel_remove_group.yaml @@ -0,0 +1,63 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?add=channel-groups-tornado-ch + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:51:31 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?add=channel-groups-tornado-ch&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=b33abd30-f0e6-47af-9922-bd5e2a5485eb +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg + response: + body: {string: '{"status": 200, "payload": {"channels": ["channel-groups-tornado-ch"], + "group": "channel-groups-tornado-cg"}, "service": "channel-registry", "error": + false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '156', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:51:32 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=b33abd30-f0e6-47af-9922-bd5e2a5485eb +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg/remove + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:51:32 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg/remove?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=b33abd30-f0e6-47af-9922-bd5e2a5485eb +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg + response: + body: {string: '{"status": 200, "payload": {"channels": [], "group": "channel-groups-tornado-cg"}, + "service": "channel-registry", "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '129', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:51:33 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=b33abd30-f0e6-47af-9922-bd5e2a5485eb +version: 1 diff --git a/tests/integrational/fixtures/asyncio/groups/add_remove_multiple_channels.yaml b/tests/integrational/fixtures/asyncio/groups/add_remove_multiple_channels.yaml new file mode 100644 index 00000000..c7aba546 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/groups/add_remove_multiple_channels.yaml @@ -0,0 +1,63 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?add=channel-groups-tornado-ch1%2Cchannel-groups-tornado-ch2 + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:51:28 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?add=channel-groups-tornado-ch1,channel-groups-tornado-ch2&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=b33abd30-f0e6-47af-9922-bd5e2a5485eb +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg + response: + body: {string: '{"status": 200, "payload": {"channels": ["channel-groups-tornado-ch1", + "channel-groups-tornado-ch2"], "group": "channel-groups-tornado-cg"}, "service": + "channel-registry", "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '187', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:51:29 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=b33abd30-f0e6-47af-9922-bd5e2a5485eb +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?remove=channel-groups-tornado-ch1%2Cchannel-groups-tornado-ch2 + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:51:30 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?remove=channel-groups-tornado-ch1,channel-groups-tornado-ch2&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=b33abd30-f0e6-47af-9922-bd5e2a5485eb +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg + response: + body: {string: '{"status": 200, "payload": {"channels": [], "group": "channel-groups-tornado-cg"}, + "service": "channel-registry", "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '129', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:51:31 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=b33abd30-f0e6-47af-9922-bd5e2a5485eb +version: 1 diff --git a/tests/integrational/fixtures/asyncio/groups/add_remove_single_channel.yaml b/tests/integrational/fixtures/asyncio/groups/add_remove_single_channel.yaml new file mode 100644 index 00000000..a752a633 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/groups/add_remove_single_channel.yaml @@ -0,0 +1,76 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/test-channel-groups-asyncio-ch/0/%22hey%22?seqn=1 + response: + body: {string: '[1,"Sent","14818962866394550"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:51:26 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/test-channel-groups-asyncio-ch/0/%22hey%22?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-channel-group-asyncio-uuid1&seqn=1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-channel-groups-asyncio-cg?add=test-channel-groups-asyncio-ch + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:51:26 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-channel-groups-asyncio-cg?add=test-channel-groups-asyncio-ch&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-channel-group-asyncio-uuid1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-channel-groups-asyncio-cg + response: + body: {string: '{"status": 200, "payload": {"channels": ["test-channel-groups-asyncio-ch"], + "group": "test-channel-groups-asyncio-cg"}, "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '166', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:51:27 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-channel-groups-asyncio-cg?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-channel-group-asyncio-uuid1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-channel-groups-asyncio-cg?remove=test-channel-groups-asyncio-ch + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:51:27 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-channel-groups-asyncio-cg?remove=test-channel-groups-asyncio-ch&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-channel-group-asyncio-uuid1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-channel-groups-asyncio-cg + response: + body: {string: '{"status": 200, "payload": {"channels": [], "group": "test-channel-groups-asyncio-cg"}, + "service": "channel-registry", "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '134', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:51:28 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-channel-groups-asyncio-cg?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-channel-group-asyncio-uuid2 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/here_now/global.yaml b/tests/integrational/fixtures/asyncio/here_now/global.yaml new file mode 100644 index 00000000..bb053986 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/here_now/global.yaml @@ -0,0 +1,52 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-asyncio-ch1,test-here-now-asyncio-ch2/0?tt=0&uuid=test-here-now-asyncio-uuid1 + response: + body: {string: '{"t":{"t":"14818966149684039","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:56:55 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-asyncio-ch1,test-here-now-asyncio-ch2/0?uuid=test-here-now-asyncio-uuid1&tt=0&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe?uuid=test-here-now-asyncio-uuid1 + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"channels": {"test-subscribe-asyncio-join-leave-ch": + {"uuids": ["test-subscribe-asyncio-listener"], "occupancy": 1}, "test-subscribe-asyncio-unsubscribe-all-ch1": + {"uuids": ["test-subscribe-asyncio-messenger"], "occupancy": 1}, "test-subscribe-asyncio-unsubscribe-all-ch2": + {"uuids": ["test-subscribe-asyncio-messenger"], "occupancy": 1}, "test-subscribe-asyncio-unsubscribe-all-ch3": + {"uuids": ["test-subscribe-asyncio-messenger"], "occupancy": 1}, "test-subscribe-asyncio-unsubscribe-all-ch": + {"uuids": ["test-subscribe-asyncio-messenger"], "occupancy": 1}, "test-subscribe-asyncio-ch": + {"uuids": ["fe92df45-c879-449d-a403-90a17bb9e6e6", "test-subscribe-asyncio-uuid-sub", + "test-subscribe-asyncio-uuid"], "occupancy": 3}}, "total_channels": 6, "total_occupancy": + 8}, "service": "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '836', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:57:00 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe?uuid=test-here-now-asyncio-uuid1&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch1,test-here-now-asyncio-ch2/leave?uuid=test-here-now-asyncio-uuid1 + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:57:00 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch1,test-here-now-asyncio-ch2/leave?uuid=test-here-now-asyncio-uuid1&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/here_now/multiple_channels.yaml b/tests/integrational/fixtures/asyncio/here_now/multiple_channels.yaml new file mode 100644 index 00000000..2f286b4c --- /dev/null +++ b/tests/integrational/fixtures/asyncio/here_now/multiple_channels.yaml @@ -0,0 +1,64 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.5] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-asyncio-ch1,test-here-now-asyncio-ch2/0?tt=0&uuid=test-here-now-asyncio-uuid1 + response: + body: {string: '{"t":{"t":"14841814610610668","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Thu, 12 Jan 2017 00:37:41 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-asyncio-ch1,test-here-now-asyncio-ch2/0?uuid=test-here-now-asyncio-uuid1&tt=0&pnsdk=PubNub-Python-Asyncio%2F4.0.5 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.5] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch1,test-here-now-asyncio-ch2?uuid=test-here-now-asyncio-uuid1 + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"channels": {"test-here-now-asyncio-ch2": + {"uuids": ["test-here-now-asyncio-uuid1"], "occupancy": 1}, "test-here-now-asyncio-ch1": + {"uuids": ["test-here-now-asyncio-uuid1"], "occupancy": 1}}, "total_channels": + 2, "total_occupancy": 2}, "service": "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '303', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Thu, + 12 Jan 2017 00:37:47 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch1,test-here-now-asyncio-ch2?uuid=test-here-now-asyncio-uuid1&pnsdk=PubNub-Python-Asyncio%2F4.0.5 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.5] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch1,test-here-now-asyncio-ch2?state=1&uuid=test-here-now-asyncio-uuid1 + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"channels": {"test-here-now-asyncio-ch2": + {"uuids": [{"uuid": "test-here-now-asyncio-uuid1"}], "occupancy": 1}, "test-here-now-asyncio-ch1": + {"uuids": [{"uuid": "test-here-now-asyncio-uuid1"}], "occupancy": 1}}, "total_channels": + 2, "total_occupancy": 2}, "service": "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '323', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Thu, + 12 Jan 2017 00:37:47 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch1,test-here-now-asyncio-ch2?state=1&uuid=test-here-now-asyncio-uuid1&pnsdk=PubNub-Python-Asyncio%2F4.0.5 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.5] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch1,test-here-now-asyncio-ch2/leave?uuid=test-here-now-asyncio-uuid1 + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Thu, + 12 Jan 2017 00:37:48 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch1,test-here-now-asyncio-ch2/leave?uuid=test-here-now-asyncio-uuid1&pnsdk=PubNub-Python-Asyncio%2F4.0.5 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/here_now/single_channel.yaml b/tests/integrational/fixtures/asyncio/here_now/single_channel.yaml new file mode 100644 index 00000000..b93e8b58 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/here_now/single_channel.yaml @@ -0,0 +1,60 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-asyncio-ch/0?tt=0 + response: + body: {string: '{"t":{"t":"14841800755720521","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Thu, 12 Jan 2017 00:14:35 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-asyncio-ch/0?tt=0&uuid=test-here-now-asyncio-uuid1&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch + response: + body: {string: '{"status": 200, "message": "OK", "service": "Presence", "uuids": + ["test-here-now-asyncio-uuid1"], "occupancy": 1}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '113', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Thu, + 12 Jan 2017 00:14:41 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch?uuid=test-here-now-asyncio-uuid1&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch?state=1 + response: + body: {string: '{"status": 200, "message": "OK", "service": "Presence", "uuids": + [{"uuid": "test-here-now-asyncio-uuid1"}], "occupancy": 1}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '123', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Thu, + 12 Jan 2017 00:14:41 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch?uuid=test-here-now-asyncio-uuid1&state=1&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch/leave + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Thu, + 12 Jan 2017 00:14:42 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-asyncio-ch/leave?uuid=test-here-now-asyncio-uuid1&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/invocations/envelope.yaml b/tests/integrational/fixtures/asyncio/invocations/envelope.yaml new file mode 100644 index 00000000..a11be7c4 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/invocations/envelope.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/blah/0/%22hey%22 + response: + body: {string: '[1,"Sent","14818963274425606"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:07 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/blah/0/%22hey%22?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=b33abd30-f0e6-47af-9922-bd5e2a5485eb&seqn=1 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/invocations/envelope_raises.yaml b/tests/integrational/fixtures/asyncio/invocations/envelope_raises.yaml new file mode 100644 index 00000000..28e2f1f8 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/invocations/envelope_raises.yaml @@ -0,0 +1,20 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/blah/blah/0/blah/0/%22hey%22 + response: + body: {string: '{"message":"Invalid Subscribe Key","error":true,"service":"Access + Manager","status":400} + +'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, 16 Dec 2016 13:52:10 + GMT', SERVER: nginx, TRANSFER-ENCODING: chunked} + status: {code: 400, message: Bad Request} + url: http://ps.pndsn.com/publish/blah/blah/0/blah/0/%22hey%22?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=c06c6b93-2c6f-49de-9d5f-12b210366651&seqn=1 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/invocations/future.yaml b/tests/integrational/fixtures/asyncio/invocations/future.yaml new file mode 100644 index 00000000..ecfd0c4d --- /dev/null +++ b/tests/integrational/fixtures/asyncio/invocations/future.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/blah/0/%22hey%22 + response: + body: {string: '[1,"Sent","14818963241977190"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:04 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/blah/0/%22hey%22?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=b33abd30-f0e6-47af-9922-bd5e2a5485eb&seqn=1 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/invocations/future_raises_pubnub_error.yaml b/tests/integrational/fixtures/asyncio/invocations/future_raises_pubnub_error.yaml new file mode 100644 index 00000000..a385f76b --- /dev/null +++ b/tests/integrational/fixtures/asyncio/invocations/future_raises_pubnub_error.yaml @@ -0,0 +1,20 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/blah/blah/0/blah/0/%22hey%22 + response: + body: {string: '{"message":"Invalid Subscribe Key","error":true,"service":"Access + Manager","status":400} + +'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, 16 Dec 2016 13:52:07 + GMT', SERVER: nginx, TRANSFER-ENCODING: chunked} + status: {code: 400, message: Bad Request} + url: http://ps.pndsn.com/publish/blah/blah/0/blah/0/%22hey%22?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=c06c6b93-2c6f-49de-9d5f-12b210366651&seqn=1 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/pam/global_level.yaml b/tests/integrational/fixtures/asyncio/pam/global_level.yaml new file mode 100644 index 00000000..4b45e6e1 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/pam/global_level.yaml @@ -0,0 +1,50 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?r=1&uuid=my_uuid&w=1 + response: + body: {string: '{"message":"Success","payload":{"level":"subkey","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","ttl":1440,"r":1,"w":1,"m":0},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '180', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:10 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?pnsdk=PubNub-Python-Asyncio%2F4.0.4&r=1&signature=HoR4kd5kOwKqZ3RHzjVP5HdgmoWAP-L0OzGlf3pLlXA=×tamp=1481896330&uuid=my_uuid&w=1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?uuid=my_uuid + response: + body: {string: '{"message":"Success","payload":{"level":"subkey","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","r":1,"m":0,"w":1,"ttl":1440,"channels":{"test-pam-asyncio-ch2":{"r":1,"m":0,"w":1,"ttl":166,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}},"test-pam-asyncio-ch":{"r":1,"m":0,"w":1,"ttl":166,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}},"test-pam-asyncio-ch1":{"r":1,"m":0,"w":1,"ttl":166,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}},"history_channel":{"auths":{"blah":{"r":1,"m":0,"w":1}}}},"objects":{},"channel-groups":{"test-pam-asyncio-cg1":{"r":1,"m":0,"w":1,"ttl":166,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}},"test-pam-asyncio-cg2":{"r":1,"m":0,"w":1,"ttl":166,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}},"test-pam-asyncio-cg":{"r":1,"m":0,"w":1,"ttl":166,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '982', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:11 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?pnsdk=PubNub-Python-Asyncio%2F4.0.4&signature=3DcPzxyRzAGRUteyDwv7b7ro_GHlabAUzPtSkTtfUSU=×tamp=1481896330&uuid=my_uuid +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?m=0&r=0&uuid=my_uuid&w=0 + response: + body: {string: '{"message":"Success","payload":{"level":"subkey","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","ttl":1,"r":0,"w":0,"m":0},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '177', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:11 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?m=0&pnsdk=PubNub-Python-Asyncio%2F4.0.4&r=0&signature=0sKgzEts2pTJr7twR9Bh9wrfV46VON0yxg9E7tpgRjU=×tamp=1481896331&uuid=my_uuid&w=0 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/pam/multiple_channel_groups.yaml b/tests/integrational/fixtures/asyncio/pam/multiple_channel_groups.yaml new file mode 100644 index 00000000..1062d7d1 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/pam/multiple_channel_groups.yaml @@ -0,0 +1,34 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel-group=test-pam-asyncio-cg1%2Ctest-pam-asyncio-cg2&r=1&uuid=my_uuid&w=1 + response: + body: {string: '{"message":"Success","payload":{"level":"channel-group","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","ttl":1440,"channel-groups":{"test-pam-asyncio-cg1":{"r":1,"w":1,"m":0},"test-pam-asyncio-cg2":{"r":1,"w":1,"m":0}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '274', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:13 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel-group=test-pam-asyncio-cg1,test-pam-asyncio-cg2&pnsdk=PubNub-Python-Asyncio%2F4.0.4&r=1&signature=VtYBdq4jE9aGehb765EPddcQhQbPxZ0Aqp6YjeMtJpY=×tamp=1481896333&uuid=my_uuid&w=1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel-group=test-pam-asyncio-cg1%2Ctest-pam-asyncio-cg2&uuid=my_uuid + response: + body: {string: '{"message":"Success","payload":{"level":"channel-group","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","channel-groups":{"test-pam-asyncio-cg1":{"r":1,"m":0,"w":1,"ttl":1440,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}},"test-pam-asyncio-cg2":{"r":1,"m":0,"w":1,"ttl":1440,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '413', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:13 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel-group=test-pam-asyncio-cg1,test-pam-asyncio-cg2&pnsdk=PubNub-Python-Asyncio%2F4.0.4&signature=fXT2f9pwZhWWbG-Gaaa0f3l21p5yee4QO-JqrCjBkSU=×tamp=1481896333&uuid=my_uuid +version: 1 diff --git a/tests/integrational/fixtures/asyncio/pam/multiple_channel_groups_with_auth.yaml b/tests/integrational/fixtures/asyncio/pam/multiple_channel_groups_with_auth.yaml new file mode 100644 index 00000000..98622ee6 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/pam/multiple_channel_groups_with_auth.yaml @@ -0,0 +1,34 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?auth=test-pam-asyncio-auth&channel-group=test-pam-asyncio-cg1%2Ctest-pam-asyncio-cg2&r=1&uuid=my_uuid&w=1 + response: + body: {string: '{"message":"Success","payload":{"level":"channel-group+auth","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","ttl":1440,"channel-groups":{"test-pam-asyncio-cg1":{"auths":{"test-pam-asyncio-auth":{"r":1,"w":1,"m":0}}},"test-pam-asyncio-cg2":{"auths":{"test-pam-asyncio-auth":{"r":1,"w":1,"m":0}}}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '351', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:14 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?auth=test-pam-asyncio-auth&channel-group=test-pam-asyncio-cg1,test-pam-asyncio-cg2&pnsdk=PubNub-Python-Asyncio%2F4.0.4&r=1&signature=Lokw1jIF_zlAlk8VKfDZGechmTe9u6HaeSnvtaaQtXM=×tamp=1481896333&uuid=my_uuid&w=1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel-group=test-pam-asyncio-cg1%2Ctest-pam-asyncio-cg2&uuid=my_uuid + response: + body: {string: '{"message":"Success","payload":{"level":"channel-group","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","channel-groups":{"test-pam-asyncio-cg1":{"r":1,"m":0,"w":1,"ttl":1440,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":1440}}},"test-pam-asyncio-cg2":{"r":1,"m":0,"w":1,"ttl":1440,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":1440}}}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '415', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:14 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel-group=test-pam-asyncio-cg1,test-pam-asyncio-cg2&pnsdk=PubNub-Python-Asyncio%2F4.0.4&signature=ZgUT1TBwYYEChvdtr2xQS3Ln7YZD2b6R8ktUW44zbkY=×tamp=1481896334&uuid=my_uuid +version: 1 diff --git a/tests/integrational/fixtures/asyncio/pam/multiple_channels.yaml b/tests/integrational/fixtures/asyncio/pam/multiple_channels.yaml new file mode 100644 index 00000000..da025c12 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/pam/multiple_channels.yaml @@ -0,0 +1,34 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel=test-pam-asyncio-ch1%2Ctest-pam-asyncio-ch2&r=1&uuid=test-pam-asyncio-uuid&w=1 + response: + body: {string: '{"message":"Success","payload":{"level":"channel","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","ttl":1440,"channels":{"test-pam-asyncio-ch1":{"r":1,"w":1,"m":0},"test-pam-asyncio-ch2":{"r":1,"w":1,"m":0}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '262', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:12 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel=test-pam-asyncio-ch1,test-pam-asyncio-ch2&pnsdk=PubNub-Python-Asyncio%2F4.0.4&r=1&signature=fBB-FwdPoO45PXR9NvaTIhGagcvDHpNsMFLDwI16k0U=×tamp=1481896331&uuid=test-pam-asyncio-uuid&w=1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel=test-pam-asyncio-ch1%2Ctest-pam-asyncio-ch2&uuid=test-pam-asyncio-uuid + response: + body: {string: '{"message":"Success","payload":{"level":"channel","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","channels":{"test-pam-asyncio-ch2":{"r":1,"m":0,"w":1,"ttl":1440,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}},"test-pam-asyncio-ch1":{"r":1,"m":0,"w":1,"ttl":1440,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '401', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:12 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel=test-pam-asyncio-ch1,test-pam-asyncio-ch2&pnsdk=PubNub-Python-Asyncio%2F4.0.4&signature=eu_KBB6V9wcllZrZ__wfKB5r8MDD6bk2PJFuHu6rYFo=×tamp=1481896332&uuid=test-pam-asyncio-uuid +version: 1 diff --git a/tests/integrational/fixtures/asyncio/pam/multiple_channels_with_auth.yaml b/tests/integrational/fixtures/asyncio/pam/multiple_channels_with_auth.yaml new file mode 100644 index 00000000..9dddc328 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/pam/multiple_channels_with_auth.yaml @@ -0,0 +1,34 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?auth=test-pam-asyncio-auth&channel=test-pam-asyncio-ch1%2Ctest-pam-asyncio-ch2&r=1&uuid=my_uuid&w=1 + response: + body: {string: '{"message":"Success","payload":{"level":"user","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","ttl":1440,"channels":{"test-pam-asyncio-ch1":{"auths":{"test-pam-asyncio-auth":{"r":1,"w":1,"m":0}}},"test-pam-asyncio-ch2":{"auths":{"test-pam-asyncio-auth":{"r":1,"w":1,"m":0}}}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '331', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:12 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?auth=test-pam-asyncio-auth&channel=test-pam-asyncio-ch1,test-pam-asyncio-ch2&pnsdk=PubNub-Python-Asyncio%2F4.0.4&r=1&signature=8liy0K_7A7VC6EcZ_lZk7pdQRlQaracysvEprI2OwnY=×tamp=1481896332&uuid=my_uuid&w=1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel=test-pam-asyncio-ch1%2Ctest-pam-asyncio-ch2&uuid=my_uuid + response: + body: {string: '{"message":"Success","payload":{"level":"channel","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","channels":{"test-pam-asyncio-ch2":{"r":1,"m":0,"w":1,"ttl":1440,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}},"test-pam-asyncio-ch1":{"r":1,"m":0,"w":1,"ttl":1440,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '401', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:12 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel=test-pam-asyncio-ch1,test-pam-asyncio-ch2&pnsdk=PubNub-Python-Asyncio%2F4.0.4&signature=dbZkXTLoS2rBDyxhUnYv-kCbuYxyxmRzpq_Brl3xKK4=×tamp=1481896332&uuid=my_uuid +version: 1 diff --git a/tests/integrational/fixtures/asyncio/pam/single_channel.yaml b/tests/integrational/fixtures/asyncio/pam/single_channel.yaml new file mode 100644 index 00000000..57a87e08 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/pam/single_channel.yaml @@ -0,0 +1,34 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel=test-pam-asyncio-ch&r=1&uuid=my_uuid&w=1 + response: + body: {string: '{"message":"Success","payload":{"level":"channel","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","ttl":1440,"channels":{"test-pam-asyncio-ch":{"r":1,"w":1,"m":0}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '218', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:11 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel=test-pam-asyncio-ch&pnsdk=PubNub-Python-Asyncio%2F4.0.4&r=1&signature=VbXpLZNb0qIVR7W5vNsq9xzO8Pbl-TVq2emBPu6TkVg=×tamp=1481896331&uuid=my_uuid&w=1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel=test-pam-asyncio-ch&uuid=my_uuid + response: + body: {string: '{"message":"Success","payload":{"level":"channel","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","channels":{"test-pam-asyncio-ch":{"r":1,"m":0,"w":1,"ttl":1440,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '282', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:11 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel=test-pam-asyncio-ch&pnsdk=PubNub-Python-Asyncio%2F4.0.4&signature=D_DmhzxnuCBeA15JtmXgjTTMvbXg_5ZZ-azpArQSAQc=×tamp=1481896331&uuid=my_uuid +version: 1 diff --git a/tests/integrational/fixtures/asyncio/pam/single_channel_group.yaml b/tests/integrational/fixtures/asyncio/pam/single_channel_group.yaml new file mode 100644 index 00000000..cd556cda --- /dev/null +++ b/tests/integrational/fixtures/asyncio/pam/single_channel_group.yaml @@ -0,0 +1,34 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel-group=test-pam-asyncio-cg&r=1&uuid=test-pam-asyncio-uuid&w=1 + response: + body: {string: '{"message":"Success","payload":{"level":"channel-group","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","ttl":1440,"channel-groups":{"test-pam-asyncio-cg":{"r":1,"w":1,"m":0}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '230', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:12 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel-group=test-pam-asyncio-cg&pnsdk=PubNub-Python-Asyncio%2F4.0.4&r=1&signature=BmTSr5gdDP3UkBWaSLt4mBEC9rFFZjNJRR9g_tCxLEQ=×tamp=1481896332&uuid=test-pam-asyncio-uuid&w=1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel-group=test-pam-asyncio-cg&uuid=test-pam-asyncio-uuid + response: + body: {string: '{"message":"Success","payload":{"level":"channel-group","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","channel-groups":{"test-pam-asyncio-cg":{"r":1,"m":0,"w":1,"ttl":1440,"auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":166}}}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '294', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:13 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?channel-group=test-pam-asyncio-cg&pnsdk=PubNub-Python-Asyncio%2F4.0.4&signature=S5p2eOGJ6fXtWge3VGpdwzti7pVNAbUZ05Wb3famUig=×tamp=1481896332&uuid=test-pam-asyncio-uuid +version: 1 diff --git a/tests/integrational/fixtures/asyncio/pam/single_channel_group_with_auth.yaml b/tests/integrational/fixtures/asyncio/pam/single_channel_group_with_auth.yaml new file mode 100644 index 00000000..8817487b --- /dev/null +++ b/tests/integrational/fixtures/asyncio/pam/single_channel_group_with_auth.yaml @@ -0,0 +1,34 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?auth=test-pam-asyncio-auth&channel-group=test-pam-asyncio-cg&r=1&uuid=test-pam-asyncio-uuid&w=1 + response: + body: {string: '{"message":"Success","payload":{"level":"channel-group+auth","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","ttl":1440,"channel-groups":"test-pam-asyncio-cg","auths":{"test-pam-asyncio-auth":{"r":1,"w":1,"m":0}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '267', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:13 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?auth=test-pam-asyncio-auth&channel-group=test-pam-asyncio-cg&pnsdk=PubNub-Python-Asyncio%2F4.0.4&r=1&signature=5TUABkdYUy7WHzCCKrU9H3vPuPZ2gHZAeaDcl7eMA54=×tamp=1481896333&uuid=test-pam-asyncio-uuid&w=1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?auth=test-pam-asyncio-auth&channel-group=test-pam-asyncio-cg&uuid=test-pam-asyncio-uuid + response: + body: {string: '{"message":"Success","payload":{"level":"channel-group+auth","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","channel-group":"test-pam-asyncio-cg","auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":1440}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '266', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:13 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?auth=test-pam-asyncio-auth&channel-group=test-pam-asyncio-cg&pnsdk=PubNub-Python-Asyncio%2F4.0.4&signature=PlsjUwIg9fE8aGoFJ8exIdRAdX9w58jiU5LiEchEV4U=×tamp=1481896333&uuid=test-pam-asyncio-uuid +version: 1 diff --git a/tests/integrational/fixtures/asyncio/pam/single_channel_with_auth.yaml b/tests/integrational/fixtures/asyncio/pam/single_channel_with_auth.yaml new file mode 100644 index 00000000..1d0766a9 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/pam/single_channel_with_auth.yaml @@ -0,0 +1,34 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?auth=test-pam-asyncio-auth&channel=test-pam-asyncio-ch&r=1&uuid=test-pam-asyncio-uuid&w=1 + response: + body: {string: '{"message":"Success","payload":{"level":"user","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","ttl":1440,"channel":"test-pam-asyncio-ch","auths":{"test-pam-asyncio-auth":{"r":1,"w":1,"m":0}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '246', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:11 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?auth=test-pam-asyncio-auth&channel=test-pam-asyncio-ch&pnsdk=PubNub-Python-Asyncio%2F4.0.4&r=1&signature=F4zNd7p_UsQrl_v2vzhJz-ONitOhGhNENOkpddiaxPw=×tamp=1481896331&uuid=test-pam-asyncio-uuid&w=1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?auth=test-pam-asyncio-auth&channel=test-pam-asyncio-ch&uuid=test-pam-asyncio-uuid + response: + body: {string: '{"message":"Success","payload":{"level":"user","subscribe_key":"sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f","channel":"test-pam-asyncio-ch","auths":{"test-pam-asyncio-auth":{"r":1,"m":0,"w":1,"ttl":1440}}},"service":"Access + Manager","status":200}'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-LENGTH: '246', CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Fri, + 16 Dec 2016 13:52:11 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/auth/audit/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f?auth=test-pam-asyncio-auth&channel=test-pam-asyncio-ch&pnsdk=PubNub-Python-Asyncio%2F4.0.4&signature=zuuexSpQPVHApIDglAa2RRJFUycU2nvya_GshRBd8V0=×tamp=1481896331&uuid=test-pam-asyncio-uuid +version: 1 diff --git a/tests/integrational/fixtures/asyncio/publish/do_not_store.yaml b/tests/integrational/fixtures/asyncio/publish/do_not_store.yaml new file mode 100644 index 00000000..704723ff --- /dev/null +++ b/tests/integrational/fixtures/asyncio/publish/do_not_store.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22hey%22?store=0 + response: + body: {string: '[1,"Sent","14820978549499111"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22hey%22?seqn=1&store=0&uuid=dc05f6a6-e648-4cf1-bbfa-b212ef5945e6&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/publish/invalid_key.yaml b/tests/integrational/fixtures/asyncio/publish/invalid_key.yaml new file mode 100644 index 00000000..435e83b9 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/publish/invalid_key.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/fake/demo/0/asyncio-int-publish/0/%22hey%22 + response: + body: {string: '[0,"Invalid Key","14820978550352022"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '37', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:55 GMT'} + status: {code: 400, message: INVALID} + url: http://ps.pndsn.com/publish/fake/demo/0/asyncio-int-publish/0/%22hey%22?seqn=1&uuid=67af3c55-453e-45f7-bdbd-294d5499cd88&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/publish/meta_object.yaml b/tests/integrational/fixtures/asyncio/publish/meta_object.yaml new file mode 100644 index 00000000..4202e1e1 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/publish/meta_object.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22hey%22?meta=%7B%22a%22%3A+2%2C+%22b%22%3A+%22qwer%22%7D + response: + body: {string: '[1,"Sent","14820978548732558"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22hey%22?seqn=1&meta=%7B%22a%22%3A%202%2C%20%22b%22%3A%20%22qwer%22%7D&uuid=5cf73370-124e-4bc0-8d93-ce450d3dbfe3&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/publish/mixed_via_get.yaml b/tests/integrational/fixtures/asyncio/publish/mixed_via_get.yaml new file mode 100644 index 00000000..24e4915b --- /dev/null +++ b/tests/integrational/fixtures/asyncio/publish/mixed_via_get.yaml @@ -0,0 +1,54 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%5B%22hi%22%2C%20%22hi2%22%2C%20%22hi3%22%5D + response: + body: {string: '[1,"Sent","14820978538596935"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:53 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%5B%22hi%22%2C%20%22hi2%22%2C%20%22hi3%22%5D?seqn=4&uuid=ec1fa148-ba88-4d0a-93fb-748bf50599a9&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22hi%22 + response: + body: {string: '[1,"Sent","14820978538628289"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:53 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22hi%22?seqn=1&uuid=ec1fa148-ba88-4d0a-93fb-748bf50599a9&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/true + response: + body: {string: '[1,"Sent","14820978538632877"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:53 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/true?seqn=3&uuid=ec1fa148-ba88-4d0a-93fb-748bf50599a9&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/5 + response: + body: {string: '[1,"Sent","14820978541276088"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/5?seqn=2&uuid=ec1fa148-ba88-4d0a-93fb-748bf50599a9&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/publish/mixed_via_get_encrypted.yaml b/tests/integrational/fixtures/asyncio/publish/mixed_via_get_encrypted.yaml new file mode 100644 index 00000000..a743adda --- /dev/null +++ b/tests/integrational/fixtures/asyncio/publish/mixed_via_get_encrypted.yaml @@ -0,0 +1,54 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22Vx8Hk6iVjiV%2BQae1bfMq2w%3D%3D%22 + response: + body: {string: '[1,"Sent","14820978544948351"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22Vx8Hk6iVjiV%2BQae1bfMq2w%3D%3D%22?seqn=2&uuid=9c6be30f-ac59-44ae-9646-4383d4955bd5&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%226uNMePrQmuhzydmUEs6KAl8teZTZfCbG27ApFSKyfr8%3D%22 + response: + body: {string: '[1,"Sent","14820978544961915"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%226uNMePrQmuhzydmUEs6KAl8teZTZfCbG27ApFSKyfr8%3D%22?seqn=4&uuid=9c6be30f-ac59-44ae-9646-4383d4955bd5&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22Dt7qBesIhJT2DweUJc2HRQ%3D%3D%22 + response: + body: {string: '[1,"Sent","14820978545058783"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22Dt7qBesIhJT2DweUJc2HRQ%3D%3D%22?seqn=1&uuid=9c6be30f-ac59-44ae-9646-4383d4955bd5&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22jw%2FKAwQAoKtQfHyYrROqSQ%3D%3D%22 + response: + body: {string: '[1,"Sent","14820978545186148"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22jw%2FKAwQAoKtQfHyYrROqSQ%3D%3D%22?seqn=3&uuid=9c6be30f-ac59-44ae-9646-4383d4955bd5&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/publish/mixed_via_post.yaml b/tests/integrational/fixtures/asyncio/publish/mixed_via_post.yaml new file mode 100644 index 00000000..c7877c4f --- /dev/null +++ b/tests/integrational/fixtures/asyncio/publish/mixed_via_post.yaml @@ -0,0 +1,54 @@ +interactions: +- request: + body: 'true' + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0 + response: + body: {string: '[1,"Sent","14820978543080292"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0?seqn=3&uuid=36c260f4-12f7-4060-85c1-d34096146bda&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: '"hi"' + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0 + response: + body: {string: '[1,"Sent","14820978543212753"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0?seqn=1&uuid=36c260f4-12f7-4060-85c1-d34096146bda&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: '["hi", "hi2", "hi3"]' + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0 + response: + body: {string: '[1,"Sent","14820978543265053"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0?seqn=4&uuid=36c260f4-12f7-4060-85c1-d34096146bda&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: '5' + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0 + response: + body: {string: '[1,"Sent","14820978543321181"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0?seqn=2&uuid=36c260f4-12f7-4060-85c1-d34096146bda&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/publish/mixed_via_post_encrypted.yaml b/tests/integrational/fixtures/asyncio/publish/mixed_via_post_encrypted.yaml new file mode 100644 index 00000000..8e2bc8f6 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/publish/mixed_via_post_encrypted.yaml @@ -0,0 +1,54 @@ +interactions: +- request: + body: '"Vx8Hk6iVjiV+Qae1bfMq2w=="' + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0 + response: + body: {string: '[1,"Sent","14820978546823218"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0?seqn=2&uuid=3ced65a6-c223-4602-9f66-be071138f35d&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: '"jw/KAwQAoKtQfHyYrROqSQ=="' + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0 + response: + body: {string: '[1,"Sent","14820978546834160"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0?seqn=3&uuid=3ced65a6-c223-4602-9f66-be071138f35d&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: '"Dt7qBesIhJT2DweUJc2HRQ=="' + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0 + response: + body: {string: '[1,"Sent","14820978546866887"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0?seqn=1&uuid=3ced65a6-c223-4602-9f66-be071138f35d&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: '"6uNMePrQmuhzydmUEs6KAl8teZTZfCbG27ApFSKyfr8="' + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0 + response: + body: {string: '[1,"Sent","14820978546879220"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0?seqn=4&uuid=3ced65a6-c223-4602-9f66-be071138f35d&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/publish/not_permitted.yaml b/tests/integrational/fixtures/asyncio/publish/not_permitted.yaml new file mode 100644 index 00000000..f0e32788 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/publish/not_permitted.yaml @@ -0,0 +1,20 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-98863562-19a6-4760-bf0b-d537d1f5c582/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f/0/asyncio-int-publish/0/%22hey%22 + response: + body: {string: '{"message":"Forbidden","payload":{"channels":["asyncio-int-publish"]},"error":true,"service":"Access + Manager","status":403} + +'} + headers: {ACCESS-CONTROL-ALLOW-HEADERS: 'Origin, X-Requested-With, Content-Type, + Accept', ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: 'no-cache, no-store, must-revalidate', CONNECTION: keep-alive, + CONTENT-ENCODING: gzip, CONTENT-TYPE: text/javascript; charset=UTF-8, DATE: 'Sun, + 18 Dec 2016 21:50:55 GMT', SERVER: nginx, TRANSFER-ENCODING: chunked, X-BLOCKS-ENABLED: '0'} + status: {code: 403, message: Forbidden} + url: http://ps.pndsn.com/publish/pub-c-98863562-19a6-4760-bf0b-d537d1f5c582/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f/0/asyncio-int-publish/0/%22hey%22?seqn=1&uuid=48600fc7-b3ea-487e-abdc-622c3feec615&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/publish/object_via_get.yaml b/tests/integrational/fixtures/asyncio/publish/object_via_get.yaml new file mode 100644 index 00000000..9ddd6830 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/publish/object_via_get.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%7B%22online%22%3A%20true%2C%20%22name%22%3A%20%22Alex%22%7D + response: + body: {string: '[1,"Sent","14820978542248113"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%7B%22online%22%3A%20true%2C%20%22name%22%3A%20%22Alex%22%7D?seqn=1&uuid=be0961fa-1d5e-43ec-83f4-39c8cd91f046&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/publish/object_via_get_encrypted.yaml b/tests/integrational/fixtures/asyncio/publish/object_via_get_encrypted.yaml new file mode 100644 index 00000000..a76198ca --- /dev/null +++ b/tests/integrational/fixtures/asyncio/publish/object_via_get_encrypted.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22Kwwg99lDMKM0%2FT%2F3EG49rh%2Bnnex2yBo%2F4kK5L7CC%2FF%2BDtMHVInyW%2FgaiX6J8iUMc%22 + response: + body: {string: '[1,"Sent","14820978545989239"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22Kwwg99lDMKM0%2FT%2F3EG49rh%2Bnnex2yBo%2F4kK5L7CC%2FF%2BDtMHVInyW%2FgaiX6J8iUMc%22?seqn=1&uuid=3487ec85-56c6-4696-b781-3c6f958da670&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/publish/object_via_post.yaml b/tests/integrational/fixtures/asyncio/publish/object_via_post.yaml new file mode 100644 index 00000000..c234109d --- /dev/null +++ b/tests/integrational/fixtures/asyncio/publish/object_via_post.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: '{"online": true, "name": "Alex"}' + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0 + response: + body: {string: '[1,"Sent","14820978544115848"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0?seqn=1&uuid=73b4e16c-38ee-4d54-99f3-2dd4b7f85169&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/publish/object_via_post_encrypted.yaml b/tests/integrational/fixtures/asyncio/publish/object_via_post_encrypted.yaml new file mode 100644 index 00000000..f44a3862 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/publish/object_via_post_encrypted.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: '"Kwwg99lDMKM0/T/3EG49rh+nnex2yBo/4kK5L7CC/F+DtMHVInyW/gaiX6J8iUMc"' + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0 + response: + body: {string: '[1,"Sent","14820978547800881"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:50:54 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0?seqn=1&uuid=174a9cbe-2737-4184-9888-c4cfe6767ed5&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/secure/ssl.yaml b/tests/integrational/fixtures/asyncio/secure/ssl.yaml new file mode 100644 index 00000000..ceed53e9 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/secure/ssl.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: https://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22hey%22?seqn=1 + response: + body: {string: '[1,"Sent","14818963356429731"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:15 GMT'} + status: {code: 200, message: OK} + url: https://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/asyncio-int-publish/0/%22hey%22?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=784bc904-18af-4e75-981e-bd8e6bfbeb61&seqn=1 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/state/multiple_channel.yaml b/tests/integrational/fixtures/asyncio/state/multiple_channel.yaml new file mode 100644 index 00000000..35ab54a6 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/state/multiple_channel.yaml @@ -0,0 +1,33 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch1,test-state-asyncio-ch2/uuid/test-state-asyncio-uuid/data?state=%7B%22count%22%3A+5%2C+%22name%22%3A+%22Alex%22%7D + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"count": 5, "name": + "Alex"}, "service": "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '96', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Sun, + 18 Dec 2016 21:28:29 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch1,test-state-asyncio-ch2/uuid/test-state-asyncio-uuid/data?uuid=test-state-asyncio-uuid&pnsdk=PubNub-Python-Asyncio%2F4.0.4&state=%7B%22count%22%3A%205%2C%20%22name%22%3A%20%22Alex%22%7D +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch1,test-state-asyncio-ch2/uuid/test-state-asyncio-uuid + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"channels": {"test-state-asyncio-ch1": + {"count": 5, "name": "Alex"}, "test-state-asyncio-ch2": {"count": 5, "name": + "Alex"}}}, "service": "Presence", "uuid": "test-state-asyncio-uuid"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '229', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Sun, + 18 Dec 2016 21:28:29 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch1,test-state-asyncio-ch2/uuid/test-state-asyncio-uuid?uuid=test-state-asyncio-uuid&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/state/single_channel.yaml b/tests/integrational/fixtures/asyncio/state/single_channel.yaml new file mode 100644 index 00000000..a832bb09 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/state/single_channel.yaml @@ -0,0 +1,33 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/uuid/test-state-asyncio-uuid/data?state=%7B%22count%22%3A+5%2C+%22name%22%3A+%22Alex%22%7D + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"count": 5, "name": + "Alex"}, "service": "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '96', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Sun, + 18 Dec 2016 21:28:06 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/uuid/test-state-asyncio-uuid/data?uuid=test-state-asyncio-uuid&pnsdk=PubNub-Python-Asyncio%2F4.0.4&state=%7B%22count%22%3A%205%2C%20%22name%22%3A%20%22Alex%22%7D +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/uuid/test-state-asyncio-uuid + response: + body: {string: '{"status": 200, "uuid": "test-state-asyncio-uuid", "service": + "Presence", "message": "OK", "payload": {"count": 5, "name": "Alex"}, "channel": + "test-state-asyncio-ch"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '167', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Sun, + 18 Dec 2016 21:28:06 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/uuid/test-state-asyncio-uuid?uuid=test-state-asyncio-uuid&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/state/single_channel_with_subscription.yaml b/tests/integrational/fixtures/asyncio/state/single_channel_with_subscription.yaml new file mode 100644 index 00000000..d3075feb --- /dev/null +++ b/tests/integrational/fixtures/asyncio/state/single_channel_with_subscription.yaml @@ -0,0 +1,117 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-state-asyncio-ch/0?heartbeat=12&tt=0 + response: + body: {string: '{"t":{"t":"14820964868757435","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Sun, 18 Dec 2016 21:28:06 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-state-asyncio-ch/0?heartbeat=12&tt=0&uuid=test-state-asyncio-uuid&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/heartbeat?heartbeat=12 + response: + body: {string: '{"status": 200, "message": "OK", "service": "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '55', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Sun, + 18 Dec 2016 21:28:11 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/heartbeat?heartbeat=12&uuid=test-state-asyncio-uuid&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/heartbeat?heartbeat=12 + response: + body: {string: '{"status": 200, "message": "OK", "service": "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '55', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Sun, + 18 Dec 2016 21:28:16 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/heartbeat?heartbeat=12&uuid=test-state-asyncio-uuid&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/heartbeat?heartbeat=12 + response: + body: {string: '{"status": 200, "message": "OK", "service": "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '55', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Sun, + 18 Dec 2016 21:28:21 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/heartbeat?heartbeat=12&uuid=test-state-asyncio-uuid&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/heartbeat?heartbeat=12 + response: + body: {string: '{"status": 200, "message": "OK", "service": "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '55', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Sun, + 18 Dec 2016 21:28:26 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/heartbeat?heartbeat=12&uuid=test-state-asyncio-uuid&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/uuid/test-state-asyncio-uuid/data?state=%7B%22count%22%3A+5%2C+%22name%22%3A+%22Alex%22%7D + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"count": 5, "name": + "Alex"}, "service": "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '96', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Sun, + 18 Dec 2016 21:28:27 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/uuid/test-state-asyncio-uuid/data?uuid=test-state-asyncio-uuid&pnsdk=PubNub-Python-Asyncio%2F4.0.4&state=%7B%22count%22%3A%205%2C%20%22name%22%3A%20%22Alex%22%7D +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/uuid/test-state-asyncio-uuid + response: + body: {string: '{"status": 200, "uuid": "test-state-asyncio-uuid", "service": + "Presence", "message": "OK", "payload": {"count": 5, "name": "Alex"}, "channel": + "test-state-asyncio-ch"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '167', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Sun, + 18 Dec 2016 21:28:27 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/uuid/test-state-asyncio-uuid?uuid=test-state-asyncio-uuid&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/leave + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Sun, + 18 Dec 2016 21:28:28 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-state-asyncio-ch/leave?uuid=test-state-asyncio-uuid&pnsdk=PubNub-Python-Asyncio%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/subscription/cg_join_leave.yaml b/tests/integrational/fixtures/asyncio/subscription/cg_join_leave.yaml new file mode 100644 index 00000000..c7ea3e12 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/subscription/cg_join_leave.yaml @@ -0,0 +1,133 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-join-leave-cg-group?add=test-subscribe-asyncio-join-leave-cg-channel&uuid=test-subscribe-asyncio-messenger + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:45 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-join-leave-cg-group?add=test-subscribe-asyncio-join-leave-cg-channel&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-messenger +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=test-subscribe-asyncio-join-leave-cg-group%2Ctest-subscribe-asyncio-join-leave-cg-group-pnpres&tt=0&uuid=test-subscribe-asyncio-listener + response: + body: {string: '{"t":{"t":"14818963663448174","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:46 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&tt=0&uuid=test-subscribe-asyncio-listener&channel-group=test-subscribe-asyncio-join-leave-cg-group,test-subscribe-asyncio-join-leave-cg-group-pnpres +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=test-subscribe-asyncio-join-leave-cg-group%2Ctest-subscribe-asyncio-join-leave-cg-group-pnpres&tr=12&tt=14818963663448174&uuid=test-subscribe-asyncio-listener + response: + body: {string: '{"t":{"t":"14818963671558888","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818963670791786","r":1},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"test-subscribe-asyncio-join-leave-cg-channel-pnpres","d":{"action": + "join", "timestamp": 1481896367, "uuid": "test-subscribe-asyncio-listener", + "occupancy": 1},"b":"test-subscribe-asyncio-join-leave-cg-group-pnpres"}]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '366', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:47 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&tt=14818963663448174&uuid=test-subscribe-asyncio-listener&tr=12&channel-group=test-subscribe-asyncio-join-leave-cg-group,test-subscribe-asyncio-join-leave-cg-group-pnpres +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=test-subscribe-asyncio-join-leave-cg-group&tt=0&uuid=test-subscribe-asyncio-messenger + response: + body: {string: '{"t":{"t":"14818963670970002","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:47 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&tt=0&uuid=test-subscribe-asyncio-messenger&channel-group=test-subscribe-asyncio-join-leave-cg-group +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=test-subscribe-asyncio-join-leave-cg-group%2Ctest-subscribe-asyncio-join-leave-cg-group-pnpres&tr=12&tt=14818963671558888&uuid=test-subscribe-asyncio-listener + response: + body: {string: '{"t":{"t":"14818963680969905","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818963680505104","r":2},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"test-subscribe-asyncio-join-leave-cg-channel-pnpres","d":{"action": + "join", "timestamp": 1481896368, "uuid": "test-subscribe-asyncio-messenger", + "occupancy": 2},"b":"test-subscribe-asyncio-join-leave-cg-group-pnpres"}]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '367', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:48 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&tt=14818963671558888&uuid=test-subscribe-asyncio-listener&tr=12&channel-group=test-subscribe-asyncio-join-leave-cg-group,test-subscribe-asyncio-join-leave-cg-group-pnpres +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=test-subscribe-asyncio-join-leave-cg-group%2Ctest-subscribe-asyncio-join-leave-cg-group-pnpres&tr=12&tt=14818963680969905&uuid=test-subscribe-asyncio-listener + response: + body: {string: '{"t":{"t":"14818963683554558","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818963682712656","r":1},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"test-subscribe-asyncio-join-leave-cg-channel-pnpres","d":{"action": + "leave", "timestamp": 1481896368, "uuid": "test-subscribe-asyncio-messenger", + "occupancy": 1},"b":"test-subscribe-asyncio-join-leave-cg-group-pnpres"}]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '368', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:48 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&tt=14818963680969905&uuid=test-subscribe-asyncio-listener&tr=12&channel-group=test-subscribe-asyncio-join-leave-cg-group,test-subscribe-asyncio-join-leave-cg-group-pnpres +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?channel-group=test-subscribe-asyncio-join-leave-cg-group&uuid=test-subscribe-asyncio-messenger + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:52:48 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-messenger&channel-group=test-subscribe-asyncio-join-leave-cg-group +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?channel-group=test-subscribe-asyncio-join-leave-cg-group&uuid=test-subscribe-asyncio-listener + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:52:48 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-listener&channel-group=test-subscribe-asyncio-join-leave-cg-group +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-join-leave-cg-group?remove=test-subscribe-asyncio-join-leave-cg-channel&uuid=test-subscribe-asyncio-messenger + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:48 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-join-leave-cg-group?remove=test-subscribe-asyncio-join-leave-cg-channel&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-messenger +version: 1 diff --git a/tests/integrational/fixtures/asyncio/subscription/cg_sub_pub_unsub.yaml b/tests/integrational/fixtures/asyncio/subscription/cg_sub_pub_unsub.yaml new file mode 100644 index 00000000..354cff55 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/subscription/cg_sub_pub_unsub.yaml @@ -0,0 +1,86 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-group?add=test-subscribe-asyncio-channel + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:43 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-group?add=test-subscribe-asyncio-channel&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=816d9356-41d0-4b1d-ba5c-b3488822ab64 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=test-subscribe-asyncio-group&tt=0 + response: + body: {string: '{"t":{"t":"14818963649240210","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:45 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&tt=0&uuid=816d9356-41d0-4b1d-ba5c-b3488822ab64&channel-group=test-subscribe-asyncio-group +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/test-subscribe-asyncio-channel/0/%22hey%22?seqn=1 + response: + body: {string: '[1,"Sent","14818963650918583"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:45 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/test-subscribe-asyncio-channel/0/%22hey%22?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=816d9356-41d0-4b1d-ba5c-b3488822ab64&seqn=1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=test-subscribe-asyncio-group&tr=12&tt=14818963649240210 + response: + body: {string: '{"t":{"t":"14818963650918833","r":12},"m":[{"a":"2","f":0,"i":"816d9356-41d0-4b1d-ba5c-b3488822ab64","s":1,"p":{"t":"14818963650918583","r":12},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"test-subscribe-asyncio-channel","d":"hey","b":"test-subscribe-asyncio-group"}]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '277', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:45 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&tt=14818963649240210&uuid=816d9356-41d0-4b1d-ba5c-b3488822ab64&tr=12&channel-group=test-subscribe-asyncio-group +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?channel-group=test-subscribe-asyncio-group + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:52:45 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=816d9356-41d0-4b1d-ba5c-b3488822ab64&channel-group=test-subscribe-asyncio-group +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-group?remove=test-subscribe-asyncio-channel + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:45 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-group?remove=test-subscribe-asyncio-channel&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=816d9356-41d0-4b1d-ba5c-b3488822ab64 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/subscription/cg_sub_unsub.yaml b/tests/integrational/fixtures/asyncio/subscription/cg_sub_unsub.yaml new file mode 100644 index 00000000..c7f71da8 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/subscription/cg_sub_unsub.yaml @@ -0,0 +1,60 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-group?add=test-subscribe-asyncio-channel + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:40 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-group?add=test-subscribe-asyncio-channel&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=474f7988-1e54-462b-89d4-13e50f26f43c +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=test-subscribe-asyncio-group&tt=0 + response: + body: {string: '{"t":{"t":"14818963632209414","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:43 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&tt=0&uuid=474f7988-1e54-462b-89d4-13e50f26f43c&channel-group=test-subscribe-asyncio-group +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?channel-group=test-subscribe-asyncio-group + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:52:43 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=474f7988-1e54-462b-89d4-13e50f26f43c&channel-group=test-subscribe-asyncio-group +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-group?remove=test-subscribe-asyncio-channel + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:43 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-group?remove=test-subscribe-asyncio-channel&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=474f7988-1e54-462b-89d4-13e50f26f43c +version: 1 diff --git a/tests/integrational/fixtures/asyncio/subscription/join_leave.yaml b/tests/integrational/fixtures/asyncio/subscription/join_leave.yaml new file mode 100644 index 00000000..6b379c93 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/subscription/join_leave.yaml @@ -0,0 +1,103 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-join-leave-ch,test-subscribe-asyncio-join-leave-ch-pnpres/0?tt=0&uuid=test-subscribe-asyncio-listener + response: + body: {string: '{"t":{"t":"14818963579052943","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:38 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-join-leave-ch,test-subscribe-asyncio-join-leave-ch-pnpres/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-listener&tt=0 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-join-leave-ch,test-subscribe-asyncio-join-leave-ch-pnpres/0?tr=12&tt=14818963579052943&uuid=test-subscribe-asyncio-listener + response: + body: {string: '{"t":{"t":"14818963588185526","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818963587725382","r":2},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"test-subscribe-asyncio-join-leave-ch-pnpres","d":{"action": + "join", "timestamp": 1481896358, "uuid": "test-subscribe-asyncio-listener", + "occupancy": 1},"b":"test-subscribe-asyncio-join-leave-ch-pnpres"}]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '352', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:38 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-join-leave-ch,test-subscribe-asyncio-join-leave-ch-pnpres/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-listener&tr=12&tt=14818963579052943 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-join-leave-ch/0?tt=0&uuid=test-subscribe-asyncio-messenger + response: + body: {string: '{"t":{"t":"14818963587880346","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:38 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-join-leave-ch/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-messenger&tt=0 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-join-leave-ch,test-subscribe-asyncio-join-leave-ch-pnpres/0?tr=12&tt=14818963588185526&uuid=test-subscribe-asyncio-listener + response: + body: {string: '{"t":{"t":"14818963592503447","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818963592048448","r":2},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"test-subscribe-asyncio-join-leave-ch-pnpres","d":{"action": + "join", "timestamp": 1481896359, "uuid": "test-subscribe-asyncio-messenger", + "occupancy": 2},"b":"test-subscribe-asyncio-join-leave-ch-pnpres"}]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '353', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:39 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-join-leave-ch,test-subscribe-asyncio-join-leave-ch-pnpres/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-listener&tr=12&tt=14818963588185526 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-join-leave-ch,test-subscribe-asyncio-join-leave-ch-pnpres/0?tr=12&tt=14818963592503447&uuid=test-subscribe-asyncio-listener + response: + body: {string: '{"t":{"t":"14818963595693130","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818963594851376","r":1},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"test-subscribe-asyncio-join-leave-ch-pnpres","d":{"action": + "leave", "timestamp": 1481896359, "uuid": "test-subscribe-asyncio-messenger", + "occupancy": 1},"b":"test-subscribe-asyncio-join-leave-ch-pnpres"}]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '354', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:39 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-join-leave-ch,test-subscribe-asyncio-join-leave-ch-pnpres/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-listener&tr=12&tt=14818963592503447 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-subscribe-asyncio-join-leave-ch/leave?uuid=test-subscribe-asyncio-messenger + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:52:39 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-subscribe-asyncio-join-leave-ch/leave?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-messenger +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-subscribe-asyncio-join-leave-ch/leave?uuid=test-subscribe-asyncio-listener + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:52:40 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-subscribe-asyncio-join-leave-ch/leave?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-listener +version: 1 diff --git a/tests/integrational/fixtures/asyncio/subscription/sub_pub_unsub.yaml b/tests/integrational/fixtures/asyncio/subscription/sub_pub_unsub.yaml new file mode 100644 index 00000000..c963f4b6 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/subscription/sub_pub_unsub.yaml @@ -0,0 +1,56 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-ch/0?tt=0&uuid=test-subscribe-asyncio-uuid-sub + response: + body: {string: '{"t":{"t":"14818963571353315","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:37 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-ch/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-uuid-sub&tt=0 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/test-subscribe-asyncio-ch/0/%22hey%22?seqn=1&uuid=test-subscribe-asyncio-uuid-pub + response: + body: {string: '[1,"Sent","14818963573025400"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:37 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/test-subscribe-asyncio-ch/0/%22hey%22?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-uuid-pub&seqn=1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-ch/0?tr=12&tt=14818963571353315&uuid=test-subscribe-asyncio-uuid-sub + response: + body: {string: '{"t":{"t":"14818963573055360","r":12},"m":[{"a":"2","f":0,"i":"test-subscribe-asyncio-uuid-pub","s":1,"p":{"t":"14818963573025400","r":12},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"test-subscribe-asyncio-ch","d":"hey"}]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '232', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:37 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-ch/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-uuid-sub&tr=12&tt=14818963571353315 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-subscribe-asyncio-ch/leave?uuid=test-subscribe-asyncio-uuid-sub + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:52:37 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-subscribe-asyncio-ch/leave?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-uuid-sub +version: 1 diff --git a/tests/integrational/fixtures/asyncio/subscription/sub_pub_unsub_enc.yaml b/tests/integrational/fixtures/asyncio/subscription/sub_pub_unsub_enc.yaml new file mode 100644 index 00000000..a14b1e02 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/subscription/sub_pub_unsub_enc.yaml @@ -0,0 +1,56 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-ch/0?tt=0&uuid=test-subscribe-asyncio-uuid + response: + body: {string: '{"t":{"t":"14818963573055360","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:37 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-ch/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-uuid&tt=0 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/test-subscribe-asyncio-ch/0/%22D7oVjBCciNszAo%2FEROu5Jw%3D%3D%22?seqn=1&uuid=test-subscribe-asyncio-uuid + response: + body: {string: '[1,"Sent","14818963577217258"]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '30', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:37 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/test-subscribe-asyncio-ch/0/%22D7oVjBCciNszAo%2FEROu5Jw%3D%3D%22?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-uuid&seqn=1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-ch/0?tr=12&tt=14818963573055360&uuid=test-subscribe-asyncio-uuid + response: + body: {string: '{"t":{"t":"14818963577286072","r":12},"m":[{"a":"2","f":0,"i":"test-subscribe-asyncio-uuid","s":1,"p":{"t":"14818963577217258","r":12},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"test-subscribe-asyncio-ch","d":"D7oVjBCciNszAo/EROu5Jw=="}]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '249', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:37 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-ch/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-uuid&tr=12&tt=14818963573055360 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-subscribe-asyncio-ch/leave?uuid=test-subscribe-asyncio-uuid + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:52:37 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-subscribe-asyncio-ch/leave?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-uuid +version: 1 diff --git a/tests/integrational/fixtures/asyncio/subscription/sub_unsub.yaml b/tests/integrational/fixtures/asyncio/subscription/sub_unsub.yaml new file mode 100644 index 00000000..06e21c66 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/subscription/sub_unsub.yaml @@ -0,0 +1,30 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-ch/0?tt=0 + response: + body: {string: '{"t":{"t":"14818963568306880","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:36 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-ch/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=fe92df45-c879-449d-a403-90a17bb9e6e6&tt=0 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-subscribe-asyncio-ch/leave + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:52:37 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-subscribe-asyncio-ch/leave?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=fe92df45-c879-449d-a403-90a17bb9e6e6 +version: 1 diff --git a/tests/integrational/fixtures/asyncio/subscription/unsubscribe_all.yaml b/tests/integrational/fixtures/asyncio/subscription/unsubscribe_all.yaml new file mode 100644 index 00000000..09ed2650 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/subscription/unsubscribe_all.yaml @@ -0,0 +1,90 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-unsubscribe-all-gr1?add=test-subscribe-asyncio-unsubscribe-all-ch&uuid=test-subscribe-asyncio-messenger + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:48 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-unsubscribe-all-gr1?add=test-subscribe-asyncio-unsubscribe-all-ch&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-messenger +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-unsubscribe-all-gr2?add=test-subscribe-asyncio-unsubscribe-all-ch&uuid=test-subscribe-asyncio-messenger + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:48 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-unsubscribe-all-gr2?add=test-subscribe-asyncio-unsubscribe-all-ch&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-messenger +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-unsubscribe-all-ch3,test-subscribe-asyncio-unsubscribe-all-ch1,test-subscribe-asyncio-unsubscribe-all-ch2/0?channel-group=test-subscribe-asyncio-unsubscribe-all-gr2%2Ctest-subscribe-asyncio-unsubscribe-all-gr1&tt=0&uuid=test-subscribe-asyncio-messenger + response: + body: {string: '{"t":{"t":"14818963699240141","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:50 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-subscribe-asyncio-unsubscribe-all-ch3,test-subscribe-asyncio-unsubscribe-all-ch1,test-subscribe-asyncio-unsubscribe-all-ch2/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&tt=0&uuid=test-subscribe-asyncio-messenger&channel-group=test-subscribe-asyncio-unsubscribe-all-gr2,test-subscribe-asyncio-unsubscribe-all-gr1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-subscribe-asyncio-unsubscribe-all-ch3,test-subscribe-asyncio-unsubscribe-all-ch1,test-subscribe-asyncio-unsubscribe-all-ch2/leave?channel-group=test-subscribe-asyncio-unsubscribe-all-gr2%2Ctest-subscribe-asyncio-unsubscribe-all-gr1&uuid=test-subscribe-asyncio-messenger + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:52:50 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-subscribe-asyncio-unsubscribe-all-ch3,test-subscribe-asyncio-unsubscribe-all-ch1,test-subscribe-asyncio-unsubscribe-all-ch2/leave?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-messenger&channel-group=test-subscribe-asyncio-unsubscribe-all-gr2,test-subscribe-asyncio-unsubscribe-all-gr1 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-unsubscribe-all-gr1?remove=test-subscribe-asyncio-unsubscribe-all-ch&uuid=test-subscribe-asyncio-messenger + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:50 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-unsubscribe-all-gr1?remove=test-subscribe-asyncio-unsubscribe-all-ch&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-messenger +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-unsubscribe-all-gr2?remove=test-subscribe-asyncio-unsubscribe-all-ch&uuid=test-subscribe-asyncio-messenger + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '79', + CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:50 + GMT', SERVER: Pubnub} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/test-subscribe-asyncio-unsubscribe-all-gr2?remove=test-subscribe-asyncio-unsubscribe-all-ch&pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-subscribe-asyncio-messenger +version: 1 diff --git a/tests/integrational/fixtures/asyncio/time/get.yaml b/tests/integrational/fixtures/asyncio/time/get.yaml new file mode 100644 index 00000000..1a702bb8 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/time/get.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/time/0 + response: + body: {string: '[14818963707386265]'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '19', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:50 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/time/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-state-asyncio-uuid +version: 1 diff --git a/tests/integrational/fixtures/asyncio/where_now/multiple_channels.yaml b/tests/integrational/fixtures/asyncio/where_now/multiple_channels.yaml new file mode 100644 index 00000000..bac000c7 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/where_now/multiple_channels.yaml @@ -0,0 +1,45 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-where-now-asyncio-ch1,test-where-now-asyncio-ch2/0?tt=0&uuid=test-where-now-asyncio-uuid + response: + body: {string: '{"t":{"t":"14818963736399219","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:53 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-where-now-asyncio-ch1,test-where-now-asyncio-ch2/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-where-now-asyncio-uuid&tt=0 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/uuid/test-where-now-asyncio-uuid?uuid=test-where-now-asyncio-uuid + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"channels": ["test-where-now-asyncio-ch1", + "test-where-now-asyncio-ch2"]}, "service": "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '142', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:53:00 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/uuid/test-where-now-asyncio-uuid?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-where-now-asyncio-uuid +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-where-now-asyncio-ch1,test-where-now-asyncio-ch2/leave?uuid=test-where-now-asyncio-uuid + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:53:01 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-where-now-asyncio-ch1,test-where-now-asyncio-ch2/leave?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-where-now-asyncio-uuid +version: 1 diff --git a/tests/integrational/fixtures/asyncio/where_now/single_channel.yaml b/tests/integrational/fixtures/asyncio/where_now/single_channel.yaml new file mode 100644 index 00000000..3776926c --- /dev/null +++ b/tests/integrational/fixtures/asyncio/where_now/single_channel.yaml @@ -0,0 +1,45 @@ +interactions: +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-where-now-asyncio-ch/0?tt=0 + response: + body: {string: '{"t":{"t":"14818963708992326","r":12},"m":[]}'} + headers: {ACCESS-CONTROL-ALLOW-METHODS: GET, ACCESS-CONTROL-ALLOW-ORIGIN: '*', + CACHE-CONTROL: no-cache, CONNECTION: keep-alive, CONTENT-LENGTH: '45', CONTENT-TYPE: text/javascript; + charset="UTF-8", DATE: 'Fri, 16 Dec 2016 13:52:51 GMT'} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-where-now-asyncio-ch/0?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-where-now-asyncio-uuid&tt=0 +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/uuid/test-where-now-asyncio-uuid + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"channels": ["test-where-now-asyncio-ch"]}, + "service": "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '111', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:52:53 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/uuid/test-where-now-asyncio-uuid?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-where-now-asyncio-uuid +- request: + body: null + headers: + USER-AGENT: [PubNub-Python-Asyncio/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-where-now-asyncio-ch/leave + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: {ACCEPT-RANGES: bytes, ACCESS-CONTROL-ALLOW-METHODS: 'OPTIONS, GET, POST', + ACCESS-CONTROL-ALLOW-ORIGIN: '*', AGE: '0', CACHE-CONTROL: no-cache, CONNECTION: keep-alive, + CONTENT-LENGTH: '74', CONTENT-TYPE: text/javascript; charset="UTF-8", DATE: 'Fri, + 16 Dec 2016 13:52:53 GMT', SERVER: Pubnub Presence} + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-where-now-asyncio-ch/leave?pnsdk=PubNub-Python-Asyncio%2F4.0.4&uuid=test-where-now-asyncio-uuid +version: 1 diff --git a/tests/integrational/fixtures/native_sync/channel_groups/add_channel_remove_group.yaml b/tests/integrational/fixtures/native_sync/channel_groups/add_channel_remove_group.yaml new file mode 100644 index 00000000..64b75854 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/channel_groups/add_channel_remove_group.yaml @@ -0,0 +1,123 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg/remove + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:16 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg?add=channel-groups-unit-ch + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:16 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg + response: + body: {string: '{"status": 200, "payload": {"channels": ["channel-groups-unit-ch"], + "group": "channel-groups-unit-cg"}, "service": "channel-registry", "error": + false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['150'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:17 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg/remove + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:17 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg + response: + body: {string: '{"status": 200, "payload": {"channels": [], "group": "channel-groups-unit-cg"}, + "service": "channel-registry", "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['126'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:19 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/channel_groups/add_remove_multiple_channels.yaml b/tests/integrational/fixtures/native_sync/channel_groups/add_remove_multiple_channels.yaml new file mode 100644 index 00000000..0e01b79a --- /dev/null +++ b/tests/integrational/fixtures/native_sync/channel_groups/add_remove_multiple_channels.yaml @@ -0,0 +1,123 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg/remove + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:19 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg?add=channel-groups-unit-ch1%2Cchannel-groups-unit-ch2 + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:19 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg + response: + body: {string: '{"status": 200, "payload": {"channels": ["channel-groups-unit-ch1", + "channel-groups-unit-ch2"], "group": "channel-groups-unit-cg"}, "service": + "channel-registry", "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['178'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:20 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg?remove=channel-groups-unit-ch1%2Cchannel-groups-unit-ch2 + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:20 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg + response: + body: {string: '{"status": 200, "payload": {"channels": [], "group": "channel-groups-unit-cg"}, + "service": "channel-registry", "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['126'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:21 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/channel_groups/single_channel.yaml b/tests/integrational/fixtures/native_sync/channel_groups/single_channel.yaml new file mode 100644 index 00000000..b50fad7f --- /dev/null +++ b/tests/integrational/fixtures/native_sync/channel_groups/single_channel.yaml @@ -0,0 +1,123 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-native-cg/remove + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:21 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-native-cg?add=channel-groups-native-ch + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:21 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-native-cg + response: + body: {string: '{"status": 200, "payload": {"channels": ["channel-groups-native-ch"], + "group": "channel-groups-native-cg"}, "service": "channel-registry", "error": + false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['154'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:23 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-native-cg?remove=channel-groups-native-ch + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:23 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-native-cg + response: + body: {string: '{"status": 200, "payload": {"channels": [], "group": "channel-groups-native-cg"}, + "service": "channel-registry", "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['128'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:26 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/history/basic.yaml b/tests/integrational/fixtures/native_sync/history/basic.yaml new file mode 100644 index 00000000..2df5fae0 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/history/basic.yaml @@ -0,0 +1,125 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/history-native-sync-ch/0/%22hey-0%22?seqn=1 + response: + body: {string: '[1,"Sent","14820999261239656"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:26 GMT'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/history-native-sync-ch/0/%22hey-1%22?seqn=2 + response: + body: {string: '[1,"Sent","14820999261946479"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:26 GMT'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/history-native-sync-ch/0/%22hey-2%22?seqn=3 + response: + body: {string: '[1,"Sent","14820999262698311"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:26 GMT'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/history-native-sync-ch/0/%22hey-3%22?seqn=4 + response: + body: {string: '[1,"Sent","14820999263462219"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:26 GMT'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/history-native-sync-ch/0/%22hey-4%22?seqn=5 + response: + body: {string: '[1,"Sent","14820999264622346"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:26 GMT'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/history/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/history-native-sync-ch?count=5 + response: + body: {string: '[["hey-0","hey-1","hey-2","hey-3","hey-4"],14820999261239656,14820999264622346]'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:31 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/history/encoded.yaml b/tests/integrational/fixtures/native_sync/history/encoded.yaml new file mode 100644 index 00000000..410b61f6 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/history/encoded.yaml @@ -0,0 +1,125 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/history-native-sync-ch/0/%22QfD1NCBJCmt1aPPGU2cshw%3D%3D%22?seqn=1 + response: + body: {string: '[1,"Sent","14820999316486003"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:31 GMT'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/history-native-sync-ch/0/%22cIioHNL2bZY8a%2FMa5fBsAA%3D%3D%22?seqn=2 + response: + body: {string: '[1,"Sent","14820999317435640"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:31 GMT'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/history-native-sync-ch/0/%228YmOnXcBGHtlYIdpGkOvUA%3D%3D%22?seqn=3 + response: + body: {string: '[1,"Sent","14820999318312588"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:31 GMT'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/history-native-sync-ch/0/%22arJa5qQszd4hc65Y4Y2CxA%3D%3D%22?seqn=4 + response: + body: {string: '[1,"Sent","14820999319032490"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:31 GMT'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/history-native-sync-ch/0/%22OJvWYC%2FbWXFvcw%2FTNic9hQ%3D%3D%22?seqn=5 + response: + body: {string: '[1,"Sent","14820999319748646"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:31 GMT'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/history/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/history-native-sync-ch?count=5 + response: + body: {string: '[["QfD1NCBJCmt1aPPGU2cshw==","cIioHNL2bZY8a/Ma5fBsAA==","8YmOnXcBGHtlYIdpGkOvUA==","arJa5qQszd4hc65Y4Y2CxA==","OJvWYC/bWXFvcw/TNic9hQ=="],14820999316486003,14820999319748646]'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['174'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:37 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/history/not_permitted.yaml b/tests/integrational/fixtures/native_sync/history/not_permitted.yaml new file mode 100644 index 00000000..75d42ca6 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/history/not_permitted.yaml @@ -0,0 +1,25 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/history/sub-key/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f/channel/history-native-sync-ch?count=5&signature=DFG6A6mYSj-s8dj3w_cQNBJdMCPCYeHLpiAgeIbCb-g%3D×tamp=1482099937 + response: + body: {string: '[[],0,0]'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['8'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:37 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/invalid_key.yaml b/tests/integrational/fixtures/native_sync/publish/invalid_key.yaml new file mode 100644 index 00000000..5d221834 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/invalid_key.yaml @@ -0,0 +1,22 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/fake/demo/0/ch1/0/%22hey%22?seqn=1 + response: + body: {string: '[0,"Invalid Key","14820999375199241"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['37'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:37 GMT'] + status: {code: 400, message: INVALID} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_bool_get.yaml b/tests/integrational/fixtures/native_sync/publish/publish_bool_get.yaml new file mode 100644 index 00000000..5399b41b --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_bool_get.yaml @@ -0,0 +1,22 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0/true?pnsdk=PubNub-Python%2F4.0.4&seqn=1 + response: + body: {string: '[1,"Sent","14820999376228286"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:37 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_bool_post.yaml b/tests/integrational/fixtures/native_sync/publish/publish_bool_post.yaml new file mode 100644 index 00000000..7d650ef0 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_bool_post.yaml @@ -0,0 +1,23 @@ +interactions: +- request: + body: 'true' + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + Content-Length: ['4'] + User-Agent: [PubNub-Python/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0?seqn=1 + response: + body: {string: '[1,"Sent","14820999377437961"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:37 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_do_not_store.yaml b/tests/integrational/fixtures/native_sync/publish/publish_do_not_store.yaml new file mode 100644 index 00000000..5ef8c59a --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_do_not_store.yaml @@ -0,0 +1,22 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0/%22D7oVjBCciNszAo%2FEROu5Jw%3D%3D%22?seqn=1&store=0 + response: + body: {string: '[1,"Sent","14820999378413753"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:37 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_encrypted_list_get.yaml b/tests/integrational/fixtures/native_sync/publish/publish_encrypted_list_get.yaml new file mode 100644 index 00000000..4683f3b7 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_encrypted_list_get.yaml @@ -0,0 +1,22 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0/%22M1ScRuKXCKfL%2FCQTTWnsvFgm0XoB6QgeMVp0pFTFEZQ%3D%22?seqn=1 + response: + body: {string: '[1,"Sent","14820999379661923"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:37 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_encrypted_list_post.yaml b/tests/integrational/fixtures/native_sync/publish/publish_encrypted_list_post.yaml new file mode 100644 index 00000000..d3bffdcb --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_encrypted_list_post.yaml @@ -0,0 +1,23 @@ +interactions: +- request: + body: '"M1ScRuKXCKfL/CQTTWnsvA3JeWehc1Cp2AD5dmPl4c4="' + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + Content-Length: ['46'] + User-Agent: [PubNub-Python/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0?seqn=1 + response: + body: {string: '[1,"Sent","14820999380905641"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:38 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_encrypted_string_get.yaml b/tests/integrational/fixtures/native_sync/publish/publish_encrypted_string_get.yaml new file mode 100644 index 00000000..fbb8d01c --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_encrypted_string_get.yaml @@ -0,0 +1,22 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0/%22X6%2B3Pm2irEIUtmFispcmehGTHkVSMTmrmdxgjazaA9Q%3D%22?seqn=1 + response: + body: {string: '[1,"Sent","14820999381884038"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:38 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_encrypted_string_post.yaml b/tests/integrational/fixtures/native_sync/publish/publish_encrypted_string_post.yaml new file mode 100644 index 00000000..a3d5dfed --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_encrypted_string_post.yaml @@ -0,0 +1,23 @@ +interactions: +- request: + body: '"X6+3Pm2irEIUtmFispcmes4XvlaBriIlGg2pjG8T6eg="' + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + Content-Length: ['46'] + User-Agent: [PubNub-Python/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0?seqn=1 + response: + body: {string: '[1,"Sent","14820999383119516"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:38 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_int_get.yaml b/tests/integrational/fixtures/native_sync/publish/publish_int_get.yaml new file mode 100644 index 00000000..176bc776 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_int_get.yaml @@ -0,0 +1,22 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0/5?seqn=1 + response: + body: {string: '[1,"Sent","14820999384088589"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:38 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_int_post.yaml b/tests/integrational/fixtures/native_sync/publish/publish_int_post.yaml new file mode 100644 index 00000000..faae9332 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_int_post.yaml @@ -0,0 +1,23 @@ +interactions: +- request: + body: '5' + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + Content-Length: ['1'] + User-Agent: [PubNub-Python/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0?seqn=1 + response: + body: {string: '[1,"Sent","14820999385319018"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:38 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_list_get.yaml b/tests/integrational/fixtures/native_sync/publish/publish_list_get.yaml new file mode 100644 index 00000000..c394cd70 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_list_get.yaml @@ -0,0 +1,22 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0/%5B%22hi%22%2C%20%22hi2%22%2C%20%22hi3%22%5D?seqn=1 + response: + body: {string: '[1,"Sent","14820999386271370"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:38 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_list_post.yaml b/tests/integrational/fixtures/native_sync/publish/publish_list_post.yaml new file mode 100644 index 00000000..00bf40f2 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_list_post.yaml @@ -0,0 +1,23 @@ +interactions: +- request: + body: '["hi", "hi2", "hi3"]' + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + Content-Length: ['20'] + User-Agent: [PubNub-Python/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0?seqn=1 + response: + body: {string: '[1,"Sent","14820999387500502"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:38 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_object_get.yaml b/tests/integrational/fixtures/native_sync/publish/publish_object_get.yaml new file mode 100644 index 00000000..b0dbf9f2 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_object_get.yaml @@ -0,0 +1,22 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0/%7B%22online%22%3A%20true%2C%20%22name%22%3A%20%22Alex%22%7D + response: + body: {string: '[1,"Sent","14820999388469350"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:38 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_object_post.yaml b/tests/integrational/fixtures/native_sync/publish/publish_object_post.yaml new file mode 100644 index 00000000..3c72af14 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_object_post.yaml @@ -0,0 +1,23 @@ +interactions: +- request: + body: '{"online": true, "name": "Alex"}' + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + Content-Length: ['32'] + User-Agent: [PubNub-Python/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0 + response: + body: {string: '[1,"Sent","14820999389689577"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:38 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_string_get.yaml b/tests/integrational/fixtures/native_sync/publish/publish_string_get.yaml new file mode 100644 index 00000000..ce3e8c47 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_string_get.yaml @@ -0,0 +1,22 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0/%22hi%22?seqn=1 + response: + body: {string: '[1,"Sent","14820999390622229"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:39 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_string_post.yaml b/tests/integrational/fixtures/native_sync/publish/publish_string_post.yaml new file mode 100644 index 00000000..20619566 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_string_post.yaml @@ -0,0 +1,23 @@ +interactions: +- request: + body: '"hi"' + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + Content-Length: ['4'] + User-Agent: [PubNub-Python/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0?seqn=1 + response: + body: {string: '[1,"Sent","14820999391849243"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:39 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/publish/publish_with_meta.yaml b/tests/integrational/fixtures/native_sync/publish/publish_with_meta.yaml new file mode 100644 index 00000000..3fe0641b --- /dev/null +++ b/tests/integrational/fixtures/native_sync/publish/publish_with_meta.yaml @@ -0,0 +1,22 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0/%22D7oVjBCciNszAo%2FEROu5Jw%3D%3D%22?meta=%7B%22b%22%3A+%22qwer%22%2C+%22a%22%3A+2%7D&seqn=1 + response: + body: {string: '[1,"Sent","14820999392820954"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:39 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/ssl/ssl.yaml b/tests/integrational/fixtures/native_sync/ssl/ssl.yaml new file mode 100644 index 00000000..17ecf8ee --- /dev/null +++ b/tests/integrational/fixtures/native_sync/ssl/ssl.yaml @@ -0,0 +1,22 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: https://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/ch1/0/%22hi%22?seqn=1 + response: + body: {string: '[1,"Sent","14820999394535296"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:39 GMT'] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/state/state_of_multiple_channels.yaml b/tests/integrational/fixtures/native_sync/state/state_of_multiple_channels.yaml new file mode 100644 index 00000000..8b4a20dc --- /dev/null +++ b/tests/integrational/fixtures/native_sync/state/state_of_multiple_channels.yaml @@ -0,0 +1,51 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-native-sync-ch-1,state-native-sync-ch-2/uuid/state-native-sync-uuid/data?state=%7B%22count%22%3A+5%2C+%22name%22%3A+%22Alex%22%7D + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"count": 5, "name": + "Alex"}, "service": "Presence"}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: ['OPTIONS, GET, POST'] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Connection: [keep-alive] + Content-Length: ['96'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:39 GMT'] + Server: [Pubnub Presence] + cache-control: [no-cache] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-native-sync-ch-1,state-native-sync-ch-2/uuid/state-native-sync-uuid + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"channels": {"state-native-sync-ch-1": + {"count": 5, "name": "Alex"}, "state-native-sync-ch-2": {"count": 5, "name": + "Alex"}}}, "service": "Presence", "uuid": "state-native-sync-uuid"}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: ['OPTIONS, GET, POST'] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Connection: [keep-alive] + Content-Length: ['228'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:40 GMT'] + Server: [Pubnub Presence] + cache-control: [no-cache] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/state/state_of_single_channel.yaml b/tests/integrational/fixtures/native_sync/state/state_of_single_channel.yaml new file mode 100644 index 00000000..9c8be904 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/state/state_of_single_channel.yaml @@ -0,0 +1,50 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-native-sync-ch/uuid/state-native-sync-uuid/data?state=%7B%22count%22%3A+5%2C+%22name%22%3A+%22Alex%22%7D + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"count": 5, "name": + "Alex"}, "service": "Presence"}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: ['OPTIONS, GET, POST'] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Connection: [keep-alive] + Content-Length: ['96'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:40 GMT'] + Server: [Pubnub Presence] + cache-control: [no-cache] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-native-sync-ch/uuid/state-native-sync-uuid + response: + body: {string: '{"status": 200, "uuid": "state-native-sync-uuid", "service": "Presence", + "message": "OK", "payload": {"count": 5, "name": "Alex"}, "channel": "state-native-sync-ch"}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: ['OPTIONS, GET, POST'] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Connection: [keep-alive] + Content-Length: ['165'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 18 Dec 2016 22:25:40 GMT'] + Server: [Pubnub Presence] + cache-control: [no-cache] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_threads/channel_groups/add_channel_remove_group.yaml b/tests/integrational/fixtures/native_threads/channel_groups/add_channel_remove_group.yaml new file mode 100644 index 00000000..ef08f27c --- /dev/null +++ b/tests/integrational/fixtures/native_threads/channel_groups/add_channel_remove_group.yaml @@ -0,0 +1,99 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg?add=channel-groups-unit-ch + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:48:44 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg + response: + body: {string: '{"status": 200, "payload": {"channels": ["channel-groups-unit-ch"], + "group": "channel-groups-unit-cg"}, "service": "channel-registry", "error": + false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['150'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:48:45 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg/remove + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:48:45 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg + response: + body: {string: '{"status": 200, "payload": {"channels": [], "group": "channel-groups-unit-cg"}, + "service": "channel-registry", "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['126'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:48:46 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_threads/channel_groups/add_remove_multiple_channels.yaml b/tests/integrational/fixtures/native_threads/channel_groups/add_remove_multiple_channels.yaml new file mode 100644 index 00000000..6c782de9 --- /dev/null +++ b/tests/integrational/fixtures/native_threads/channel_groups/add_remove_multiple_channels.yaml @@ -0,0 +1,99 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg?add=channel-groups-unit-ch1%2Cchannel-groups-unit-ch2 + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:48:46 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg + response: + body: {string: '{"status": 200, "payload": {"channels": ["channel-groups-unit-ch1", + "channel-groups-unit-ch2"], "group": "channel-groups-unit-cg"}, "service": + "channel-registry", "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['178'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:48:47 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg?remove=channel-groups-unit-ch1%2Cchannel-groups-unit-ch2 + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:48:47 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg + response: + body: {string: '{"status": 200, "payload": {"channels": [], "group": "channel-groups-unit-cg"}, + "service": "channel-registry", "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['126'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:48:48 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_threads/channel_groups/single_channel.yaml b/tests/integrational/fixtures/native_threads/channel_groups/single_channel.yaml new file mode 100644 index 00000000..a2748e1a --- /dev/null +++ b/tests/integrational/fixtures/native_threads/channel_groups/single_channel.yaml @@ -0,0 +1,99 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg?add=channel-groups-unit-ch + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:48:48 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg + response: + body: {string: '{"status": 200, "payload": {"channels": ["channel-groups-unit-ch"], + "group": "channel-groups-unit-cg"}, "service": "channel-registry", "error": + false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['150'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:48:50 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg?remove=channel-groups-unit-ch + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['79'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:48:50 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-unit-cg + response: + body: {string: '{"status": 200, "payload": {"channels": [], "group": "channel-groups-unit-cg"}, + "service": "channel-registry", "error": false}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['126'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:48:51 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_threads/state/state_of_multiple_channels.yaml b/tests/integrational/fixtures/native_threads/state/state_of_multiple_channels.yaml new file mode 100644 index 00000000..a2033e6b --- /dev/null +++ b/tests/integrational/fixtures/native_threads/state/state_of_multiple_channels.yaml @@ -0,0 +1,51 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-native-sync-ch-1,state-native-sync-ch-2/uuid/state-native-sync-uuid/data?state=%7B%22name%22%3A+%22Alex%22%2C+%22count%22%3A+5%7D + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"count": 5, "name": + "Alex"}, "service": "Presence"}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: ['OPTIONS, GET, POST'] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Connection: [keep-alive] + Content-Length: ['96'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:49:18 GMT'] + Server: [Pubnub Presence] + cache-control: [no-cache] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-native-sync-ch-1,state-native-sync-ch-2/uuid/state-native-sync-uuid + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"channels": {"state-native-sync-ch-1": + {"count": 5, "name": "Alex"}, "state-native-sync-ch-2": {"count": 5, "name": + "Alex"}}}, "service": "Presence", "uuid": "state-native-sync-uuid"}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: ['OPTIONS, GET, POST'] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Connection: [keep-alive] + Content-Length: ['228'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:49:18 GMT'] + Server: [Pubnub Presence] + cache-control: [no-cache] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_threads/state/state_of_single_channel.yaml b/tests/integrational/fixtures/native_threads/state/state_of_single_channel.yaml new file mode 100644 index 00000000..2199794d --- /dev/null +++ b/tests/integrational/fixtures/native_threads/state/state_of_single_channel.yaml @@ -0,0 +1,50 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-native-sync-ch/uuid/state-native-sync-uuid/data?state=%7B%22name%22%3A+%22Alex%22%2C+%22count%22%3A+5%7D + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"count": 5, "name": + "Alex"}, "service": "Presence"}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: ['OPTIONS, GET, POST'] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Connection: [keep-alive] + Content-Length: ['96'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:49:19 GMT'] + Server: [Pubnub Presence] + cache-control: [no-cache] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-native-sync-ch/uuid/state-native-sync-uuid + response: + body: {string: '{"status": 200, "uuid": "state-native-sync-uuid", "service": "Presence", + "message": "OK", "payload": {"count": 5, "name": "Alex"}, "channel": "state-native-sync-ch"}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: ['OPTIONS, GET, POST'] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Connection: [keep-alive] + Content-Length: ['165'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Mon, 19 Dec 2016 14:49:19 GMT'] + Server: [Pubnub Presence] + cache-control: [no-cache] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/tornado/groups/add_channel_remove_group.yaml b/tests/integrational/fixtures/tornado/groups/add_channel_remove_group.yaml new file mode 100644 index 00000000..6658775c --- /dev/null +++ b/tests/integrational/fixtures/tornado/groups/add_channel_remove_group.yaml @@ -0,0 +1,175 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?add=channel-groups-tornado-ch&pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 06:58:00 GMT'] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['79'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?uuid=87e4cc02-04fe-4864-ab09-52d7b0bc2bc3&pnsdk=PubNub-Python-Tornado%2F4.0.4&add=channel-groups-tornado-ch +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "payload": {"channels": ["channel-groups-tornado-ch"], + "group": "channel-groups-tornado-cg"}, "service": "channel-registry", "error": + false}'} + headers: + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 06:58:02 GMT'] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['156'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?uuid=87e4cc02-04fe-4864-ab09-52d7b0bc2bc3&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg/remove?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 06:58:02 GMT'] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['79'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg/remove?uuid=87e4cc02-04fe-4864-ab09-52d7b0bc2bc3&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "payload": {"channels": [], "group": "channel-groups-tornado-cg"}, + "service": "channel-registry", "error": false}'} + headers: + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 06:58:03 GMT'] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['129'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?uuid=87e4cc02-04fe-4864-ab09-52d7b0bc2bc3&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/groups/add_remove_multiple_channel.yaml b/tests/integrational/fixtures/tornado/groups/add_remove_multiple_channel.yaml new file mode 100644 index 00000000..5bc1d4d3 --- /dev/null +++ b/tests/integrational/fixtures/tornado/groups/add_remove_multiple_channel.yaml @@ -0,0 +1,175 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?add=channel-groups-tornado-ch1%2Cchannel-groups-tornado-ch2&pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 06:58:03 GMT'] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['79'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?uuid=87e4cc02-04fe-4864-ab09-52d7b0bc2bc3&pnsdk=PubNub-Python-Tornado%2F4.0.4&add=channel-groups-tornado-ch1,channel-groups-tornado-ch2 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "payload": {"channels": ["channel-groups-tornado-ch1", + "channel-groups-tornado-ch2"], "group": "channel-groups-tornado-cg"}, "service": + "channel-registry", "error": false}'} + headers: + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 06:58:04 GMT'] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['187'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?uuid=87e4cc02-04fe-4864-ab09-52d7b0bc2bc3&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?pnsdk=PubNub-Python-Tornado%2F4.0.4&remove=channel-groups-tornado-ch1%2Cchannel-groups-tornado-ch2 + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 06:58:04 GMT'] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['79'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?uuid=87e4cc02-04fe-4864-ab09-52d7b0bc2bc3&remove=channel-groups-tornado-ch1,channel-groups-tornado-ch2&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "payload": {"channels": [], "group": "channel-groups-tornado-cg"}, + "service": "channel-registry", "error": false}'} + headers: + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 06:58:05 GMT'] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['129'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?uuid=87e4cc02-04fe-4864-ab09-52d7b0bc2bc3&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/groups/add_remove_single_channel.yaml b/tests/integrational/fixtures/tornado/groups/add_remove_single_channel.yaml new file mode 100644 index 00000000..d9941f96 --- /dev/null +++ b/tests/integrational/fixtures/tornado/groups/add_remove_single_channel.yaml @@ -0,0 +1,175 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?add=channel-groups-tornado-ch&pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 06:58:05 GMT'] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['79'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?uuid=87e4cc02-04fe-4864-ab09-52d7b0bc2bc3&pnsdk=PubNub-Python-Tornado%2F4.0.4&add=channel-groups-tornado-ch +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "payload": {"channels": ["channel-groups-tornado-ch"], + "group": "channel-groups-tornado-cg"}, "service": "channel-registry", "error": + false}'} + headers: + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 06:58:06 GMT'] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['156'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?uuid=87e4cc02-04fe-4864-ab09-52d7b0bc2bc3&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?pnsdk=PubNub-Python-Tornado%2F4.0.4&remove=channel-groups-tornado-ch + response: + body: {string: '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 06:58:06 GMT'] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['79'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?uuid=87e4cc02-04fe-4864-ab09-52d7b0bc2bc3&remove=channel-groups-tornado-ch&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "payload": {"channels": [], "group": "channel-groups-tornado-cg"}, + "service": "channel-registry", "error": false}'} + headers: + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 06:58:07 GMT'] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['129'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/channel-groups-tornado-cg?uuid=87e4cc02-04fe-4864-ab09-52d7b0bc2bc3&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/heartbeat/timeout.yaml b/tests/integrational/fixtures/tornado/heartbeat/timeout.yaml new file mode 100644 index 00000000..cf80e577 --- /dev/null +++ b/tests/integrational/fixtures/tornado/heartbeat/timeout.yaml @@ -0,0 +1,377 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/heartbeat-tornado-ch,heartbeat-tornado-ch-pnpres/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tt=0 + response: + body: {string: !!python/unicode '{"t":{"t":"14720341188112072","r":12},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Wed, 24 Aug 2016 10:21:58 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/heartbeat-tornado-ch,heartbeat-tornado-ch-pnpres/0?tt=0&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=heartbeat-tornado-listener +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/heartbeat-tornado-ch,heartbeat-tornado-ch-pnpres/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&tt=14720341188112072 + response: + body: {string: !!python/unicode '{"t":{"t":"14720341195231188","r":12},"m":[{"a":"2","f":0,"p":{"t":"14720341194420285","r":1},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"heartbeat-tornado-ch-pnpres","d":{"action": + "join", "timestamp": 1472034119, "uuid": "heartbeat-tornado-listener", "occupancy": + 1},"b":"heartbeat-tornado-ch-pnpres"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['315'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Wed, 24 Aug 2016 10:21:59 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/heartbeat-tornado-ch,heartbeat-tornado-ch-pnpres/0?tt=14720341188112072&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&uuid=heartbeat-tornado-listener +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/heartbeat-tornado-ch/0?heartbeat=8&pnsdk=PubNub-Python-Tornado%2F4.0.4&tt=0 + response: + body: {string: !!python/unicode '{"t":{"t":"14720341194868942","r":12},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Wed, 24 Aug 2016 10:21:59 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/heartbeat-tornado-ch/0?heartbeat=8&tt=0&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=heartbeat-tornado-messenger +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/heartbeat-tornado-ch,heartbeat-tornado-ch-pnpres/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&tt=14720341195231188 + response: + body: {string: !!python/unicode '{"t":{"t":"14720341206425665","r":12},"m":[{"a":"2","f":0,"p":{"t":"14720341205063074","r":1},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"heartbeat-tornado-ch-pnpres","d":{"action": + "join", "timestamp": 1472034120, "uuid": "heartbeat-tornado-messenger", "occupancy": + 2},"b":"heartbeat-tornado-ch-pnpres"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['316'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Wed, 24 Aug 2016 10:22:00 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/heartbeat-tornado-ch,heartbeat-tornado-ch-pnpres/0?tt=14720341195231188&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&uuid=heartbeat-tornado-listener +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/heartbeat-tornado-ch/heartbeat?heartbeat=8&pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['55'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Wed, 24 Aug 2016 10:22:02 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/heartbeat-tornado-ch/heartbeat?heartbeat=8&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=heartbeat-tornado-messenger +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/heartbeat-tornado-ch/heartbeat?heartbeat=8&pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['55'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Wed, 24 Aug 2016 10:22:05 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['3'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/heartbeat-tornado-ch/heartbeat?heartbeat=8&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=heartbeat-tornado-messenger +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/heartbeat-tornado-ch/heartbeat?heartbeat=8&pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['55'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Wed, 24 Aug 2016 10:22:08 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/heartbeat-tornado-ch/heartbeat?heartbeat=8&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=heartbeat-tornado-messenger +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/heartbeat-tornado-ch,heartbeat-tornado-ch-pnpres/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&tt=14720341206425665 + response: + body: {string: !!python/unicode '{"t":{"t":"14720341368999461","r":12},"m":[{"a":"2","f":0,"p":{"t":"14720341367516371","r":1},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"heartbeat-tornado-ch-pnpres","d":{"action": + "timeout", "timestamp": 1472034136, "uuid": "heartbeat-tornado-messenger", + "occupancy": 1},"b":"heartbeat-tornado-ch-pnpres"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['319'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Wed, 24 Aug 2016 10:22:16 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/heartbeat-tornado-ch,heartbeat-tornado-ch-pnpres/0?tt=14720341206425665&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&uuid=heartbeat-tornado-listener +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/heartbeat-tornado-ch,heartbeat-tornado-ch-pnpres/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&tt=14720341368999461 + response: + body: {string: !!python/unicode '{"t":{"t":"14720341368363471","r":3},"m":[{"a":"2","f":0,"p":{"t":"14720341367516371","r":1},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"heartbeat-tornado-ch-pnpres","d":{"action": + "timeout", "timestamp": 1472034136, "uuid": "heartbeat-tornado-messenger", + "occupancy": 1},"b":"heartbeat-tornado-ch-pnpres"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['318'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Wed, 24 Aug 2016 10:22:16 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/heartbeat-tornado-ch,heartbeat-tornado-ch-pnpres/0?tt=14720341368999461&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&uuid=heartbeat-tornado-listener +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/heartbeat-tornado-ch/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: !!python/unicode '{"status": 200, "action": "leave", "message": + "OK", "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Wed, 24 Aug 2016 10:22:17 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/heartbeat-tornado-ch/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=heartbeat-tornado-listener +version: 1 diff --git a/tests/integrational/fixtures/tornado/here_now/global.yaml b/tests/integrational/fixtures/tornado/here_now/global.yaml new file mode 100644 index 00000000..e2c4c09f --- /dev/null +++ b/tests/integrational/fixtures/tornado/here_now/global.yaml @@ -0,0 +1,189 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel1/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tt=0 + response: + body: {string: '{"t":{"t":"14717797368453656","r":3},"m":[]}'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 11:42:16 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Length + - ['44'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel1/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-here-now-uuid&tt=0 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel2,test-here-now-channel1/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=3&tt=0 + response: + body: {string: '{"t":{"t":"14717797368952132","r":3},"m":[]}'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 11:42:16 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Length + - ['44'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel2,test-here-now-channel1/0?tr=3&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-here-now-uuid&tt=0 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel2,test-here-now-channel1/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=3&tt=0 + response: + body: {string: '{"t":{"t":"14717797368988362","r":3},"m":[]}'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 11:42:16 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Length + - ['44'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel2,test-here-now-channel1/0?tr=3&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-here-now-uuid&tt=0 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"channels": {"test-here-now-channel1": + {"uuids": ["test-here-now-uuid"], "occupancy": 1}, "test-here-now-channel2": + {"uuids": ["test-here-now-uuid"], "occupancy": 1}}, "total_channels": 2, "total_occupancy": + 2}, "service": "Presence"}'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 11:42:23 GMT'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Length + - ['279'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-here-now-uuid +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel1,test-here-now-channel2/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 11:42:24 GMT'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel1,test-here-now-channel2/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-here-now-uuid +version: 1 diff --git a/tests/integrational/fixtures/tornado/here_now/multiple.yaml b/tests/integrational/fixtures/tornado/here_now/multiple.yaml new file mode 100644 index 00000000..f4c2ec68 --- /dev/null +++ b/tests/integrational/fixtures/tornado/here_now/multiple.yaml @@ -0,0 +1,156 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel1/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"t":{"t":"14717792920472577","r":3},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['44'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 11:34:52 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel1/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-here-now-uuid&tt=0 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel1,test-here-now-channel2/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"t":{"t":"14717792933219598","r":3},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['44'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 11:34:53 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel1,test-here-now-channel2/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=3&uuid=test-here-now-uuid&tt=0 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel1,test-here-now-channel2?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"channels": {"test-here-now-channel1": + {"uuids": ["test-here-now-uuid"], "occupancy": 1}, "test-here-now-channel2": + {"uuids": ["test-here-now-uuid"], "occupancy": 1}}, "total_channels": 2, "total_occupancy": + 2}, "service": "Presence"}'} + headers: + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Content-Length + - ['279'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 11:34:58 GMT'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel1,test-here-now-channel2?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-here-now-uuid +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel1,test-here-now-channel2/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 11:34:59 GMT'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel1,test-here-now-channel2/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-here-now-uuid +version: 1 diff --git a/tests/integrational/fixtures/tornado/here_now/single.yaml b/tests/integrational/fixtures/tornado/here_now/single.yaml new file mode 100644 index 00000000..7a5b7835 --- /dev/null +++ b/tests/integrational/fixtures/tornado/here_now/single.yaml @@ -0,0 +1,121 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tt=0 + response: + body: {string: '{"t":{"t":"14708495143208374","r":12},"m":[]}'} + headers: + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 17:18:34 GMT'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel/0?tt=0&uuid=test-here-now-uuid&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "message": "OK", "service": "Presence", "uuids": + ["test-here-now-uuid"], "occupancy": 1}'} + headers: + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Content-Length + - ['104'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 17:18:38 GMT'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel?uuid=test-here-now-uuid&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 17:18:39 GMT'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel/leave?uuid=test-here-now-uuid&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/invocations/future_raises.yaml b/tests/integrational/fixtures/tornado/invocations/future_raises.yaml new file mode 100644 index 00000000..32c5823a --- /dev/null +++ b/tests/integrational/fixtures/tornado/invocations/future_raises.yaml @@ -0,0 +1,44 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.2] + method: GET + uri: http://ps.pndsn.com/publish/blah/blah/0/blah/0/%22hey%22?pnsdk=PubNub-Python-Tornado%2F4.0.2 + response: + body: {string: !!python/unicode '{"message":"Invalid Subscribe Key","error":true,"service":"Access + Manager","status":400} + +'} + headers: + - !!python/tuple + - Access-Control-Allow-Headers + - ['Origin, X-Requested-With, Content-Type, Accept'] + - !!python/tuple + - Transfer-Encoding + - [chunked] + - !!python/tuple + - Server + - [nginx] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - ['no-cache, no-store, must-revalidate'] + - !!python/tuple + - Date + - ['Thu, 15 Dec 2016 15:22:40 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset=UTF-8] + status: {code: 400, message: Bad Request} + url: http://ps.pndsn.com/publish/blah/blah/0/blah/0/%22hey%22?pnsdk=PubNub-Python-Tornado%2F4.0.2&seqn=1&uuid=3293317b-a598-4a4e-b54a-3fac8ae3f8d5 +version: 1 diff --git a/tests/integrational/fixtures/tornado/invocations/result_raises.yaml b/tests/integrational/fixtures/tornado/invocations/result_raises.yaml new file mode 100644 index 00000000..8baa2a13 --- /dev/null +++ b/tests/integrational/fixtures/tornado/invocations/result_raises.yaml @@ -0,0 +1,44 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.2] + method: GET + uri: http://ps.pndsn.com/publish/blah/blah/0/blah/0/%22hey%22?pnsdk=PubNub-Python-Tornado%2F4.0.2 + response: + body: {string: !!python/unicode '{"message":"Invalid Subscribe Key","error":true,"service":"Access + Manager","status":400} + +'} + headers: + - !!python/tuple + - Access-Control-Allow-Headers + - ['Origin, X-Requested-With, Content-Type, Accept'] + - !!python/tuple + - Transfer-Encoding + - [chunked] + - !!python/tuple + - Server + - [nginx] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - ['no-cache, no-store, must-revalidate'] + - !!python/tuple + - Date + - ['Thu, 15 Dec 2016 15:16:59 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset=UTF-8] + status: {code: 400, message: Bad Request} + url: http://ps.pndsn.com/publish/blah/blah/0/blah/0/%22hey%22?pnsdk=PubNub-Python-Tornado%2F4.0.2&seqn=1&uuid=189c0a7b-13b1-4d4c-a257-14fc2a124aaa +version: 1 diff --git a/tests/integrational/fixtures/tornado/publish/do_not_store.yaml b/tests/integrational/fixtures/tornado/publish/do_not_store.yaml new file mode 100644 index 00000000..1203d39d --- /dev/null +++ b/tests/integrational/fixtures/tornado/publish/do_not_store.yaml @@ -0,0 +1,68 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22hey%22?pnsdk=PubNub-Python-Tornado%2F4.0.4&store=0 + response: + body: {string: '[1,"Sent","14707213568554057"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Date + - ['Tue, 09 Aug 2016 05:42:36 GMT'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22hey%22?store=0&uuid=1e52240e-f46d-4309-b227-196ad53070cd&seqn=1&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22hey%22?pnsdk=PubNub-Python-Tornado%2F4.0.4&store=0 + response: + body: {string: '[1,"Sent","14707213569308777"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Date + - ['Tue, 09 Aug 2016 05:42:36 GMT'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22hey%22?store=0&uuid=1e52240e-f46d-4309-b227-196ad53070cd&seqn=2&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/publish/invalid_key.yaml b/tests/integrational/fixtures/tornado/publish/invalid_key.yaml new file mode 100644 index 00000000..8debf48f --- /dev/null +++ b/tests/integrational/fixtures/tornado/publish/invalid_key.yaml @@ -0,0 +1,68 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/fake/demo/0/tornado-publish/0/%22hey%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[0,"Invalid Key","14707240653092162"]'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Length + - ['37'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Date + - ['Tue, 09 Aug 2016 06:27:45 GMT'] + status: {code: 400, message: INVALID} + url: http://ps.pndsn.com/publish/fake/demo/0/tornado-publish/0/%22hey%22?uuid=efbce3be-6fe8-4225-b03b-b6813b291f7d&seqn=1&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/fake/demo/0/tornado-publish/0/%22hey%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[0,"Invalid Key","14707240653816927"]'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Length + - ['37'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Date + - ['Tue, 09 Aug 2016 06:27:45 GMT'] + status: {code: 400, message: INVALID} + url: http://ps.pndsn.com/publish/fake/demo/0/tornado-publish/0/%22hey%22?uuid=efbce3be-6fe8-4225-b03b-b6813b291f7d&seqn=2&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/publish/meta_object.yaml b/tests/integrational/fixtures/tornado/publish/meta_object.yaml new file mode 100644 index 00000000..d004374e --- /dev/null +++ b/tests/integrational/fixtures/tornado/publish/meta_object.yaml @@ -0,0 +1,68 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22hey%22?meta=%7B%22a%22%3A+2%2C+%22b%22%3A+%22qwer%22%7D&pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14707233493629583"]'} + headers: + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Tue, 09 Aug 2016 06:15:49 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['30'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22hey%22?meta=%7B%22a%22%3A%202%2C%20%22b%22%3A%20%22qwer%22%7D&uuid=02c13b1a-5ab8-4e31-841f-5d926189f571&seqn=1&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22hey%22?meta=%7B%22a%22%3A+2%2C+%22b%22%3A+%22qwer%22%7D&pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14707233494525529"]'} + headers: + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Tue, 09 Aug 2016 06:15:49 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['30'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22hey%22?meta=%7B%22a%22%3A%202%2C%20%22b%22%3A%20%22qwer%22%7D&uuid=02c13b1a-5ab8-4e31-841f-5d926189f571&seqn=2&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/publish/mixed_via_get.yaml b/tests/integrational/fixtures/tornado/publish/mixed_via_get.yaml new file mode 100644 index 00000000..4eacd808 --- /dev/null +++ b/tests/integrational/fixtures/tornado/publish/mixed_via_get.yaml @@ -0,0 +1,266 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22hi%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654961878754"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:36 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22hi%22?seqn=1&uuid=d09bd6c1-5c4d-4355-a76f-adecfe132ebc&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22hi%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654962988338"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:36 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22hi%22?seqn=2&uuid=d09bd6c1-5c4d-4355-a76f-adecfe132ebc&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/5?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654963998910"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:36 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/5?seqn=1&uuid=d09bd6c1-5c4d-4355-a76f-adecfe132ebc&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/5?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654965094211"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:36 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/5?seqn=2&uuid=d09bd6c1-5c4d-4355-a76f-adecfe132ebc&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/true?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654966264107"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:36 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/true?seqn=1&uuid=d09bd6c1-5c4d-4355-a76f-adecfe132ebc&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/true?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654968497326"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:36 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/true?seqn=2&uuid=d09bd6c1-5c4d-4355-a76f-adecfe132ebc&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%5B%22hi%22%2C%20%22hi2%22%2C%20%22hi3%22%5D?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654969624146"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:36 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%5B%22hi%22%2C%20%22hi2%22%2C%20%22hi3%22%5D?seqn=1&uuid=d09bd6c1-5c4d-4355-a76f-adecfe132ebc&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%5B%22hi%22%2C%20%22hi2%22%2C%20%22hi3%22%5D?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654971058947"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:37 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%5B%22hi%22%2C%20%22hi2%22%2C%20%22hi3%22%5D?seqn=2&uuid=d09bd6c1-5c4d-4355-a76f-adecfe132ebc&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/publish/mixed_via_get_encrypted.yaml b/tests/integrational/fixtures/tornado/publish/mixed_via_get_encrypted.yaml new file mode 100644 index 00000000..9dfd47d0 --- /dev/null +++ b/tests/integrational/fixtures/tornado/publish/mixed_via_get_encrypted.yaml @@ -0,0 +1,266 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22Dt7qBesIhJT2DweUJc2HRQ%3D%3D%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654973576283"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:37 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22Dt7qBesIhJT2DweUJc2HRQ%3D%3D%22?seqn=1&uuid=ef46bb56-9efa-4d85-8794-dffb8f883275&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22Dt7qBesIhJT2DweUJc2HRQ%3D%3D%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654974534808"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:37 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22Dt7qBesIhJT2DweUJc2HRQ%3D%3D%22?seqn=2&uuid=ef46bb56-9efa-4d85-8794-dffb8f883275&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22Vx8Hk6iVjiV%2BQae1bfMq2w%3D%3D%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654975469383"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:37 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22Vx8Hk6iVjiV%2BQae1bfMq2w%3D%3D%22?seqn=1&uuid=ef46bb56-9efa-4d85-8794-dffb8f883275&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22Vx8Hk6iVjiV%2BQae1bfMq2w%3D%3D%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654976370725"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:37 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22Vx8Hk6iVjiV%2BQae1bfMq2w%3D%3D%22?seqn=2&uuid=ef46bb56-9efa-4d85-8794-dffb8f883275&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22jw%2FKAwQAoKtQfHyYrROqSQ%3D%3D%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654977343057"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:37 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22jw%2FKAwQAoKtQfHyYrROqSQ%3D%3D%22?seqn=1&uuid=ef46bb56-9efa-4d85-8794-dffb8f883275&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22jw%2FKAwQAoKtQfHyYrROqSQ%3D%3D%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654978302189"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:37 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22jw%2FKAwQAoKtQfHyYrROqSQ%3D%3D%22?seqn=2&uuid=ef46bb56-9efa-4d85-8794-dffb8f883275&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%226uNMePrQmuhzydmUEs6KAl8teZTZfCbG27ApFSKyfr8%3D%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654979370691"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:37 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%226uNMePrQmuhzydmUEs6KAl8teZTZfCbG27ApFSKyfr8%3D%22?seqn=1&uuid=ef46bb56-9efa-4d85-8794-dffb8f883275&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%226uNMePrQmuhzydmUEs6KAl8teZTZfCbG27ApFSKyfr8%3D%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706654980293520"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:11:38 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%226uNMePrQmuhzydmUEs6KAl8teZTZfCbG27ApFSKyfr8%3D%22?seqn=2&uuid=ef46bb56-9efa-4d85-8794-dffb8f883275&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/publish/mixed_via_post.yaml b/tests/integrational/fixtures/tornado/publish/mixed_via_post.yaml new file mode 100644 index 00000000..ee23e7eb --- /dev/null +++ b/tests/integrational/fixtures/tornado/publish/mixed_via_post.yaml @@ -0,0 +1,266 @@ +interactions: +- request: + body: '"hi"' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706789261217101"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 17:55:26 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Connection + - [close] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?seqn=1&uuid=dd43a67d-45af-45cb-af78-13c18720f404&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: '"hi"' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706789261901583"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 17:55:26 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Connection + - [close] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?seqn=2&uuid=dd43a67d-45af-45cb-af78-13c18720f404&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: '5' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706789262581697"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 17:55:26 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Connection + - [close] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?seqn=1&uuid=dd43a67d-45af-45cb-af78-13c18720f404&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: '5' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706789263258448"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 17:55:26 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Connection + - [close] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?seqn=2&uuid=dd43a67d-45af-45cb-af78-13c18720f404&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: 'true' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706789263937508"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 17:55:26 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Connection + - [close] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?seqn=1&uuid=dd43a67d-45af-45cb-af78-13c18720f404&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: 'true' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706789264623948"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 17:55:26 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Connection + - [close] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?seqn=2&uuid=dd43a67d-45af-45cb-af78-13c18720f404&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: '["hi", "hi2", "hi3"]' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706789265622885"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 17:55:26 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Connection + - [close] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?seqn=1&uuid=dd43a67d-45af-45cb-af78-13c18720f404&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: '["hi", "hi2", "hi3"]' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706789266306131"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 17:55:26 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Connection + - [close] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?seqn=2&uuid=dd43a67d-45af-45cb-af78-13c18720f404&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/publish/mixed_via_post_encrypted.yaml b/tests/integrational/fixtures/tornado/publish/mixed_via_post_encrypted.yaml new file mode 100644 index 00000000..49504c09 --- /dev/null +++ b/tests/integrational/fixtures/tornado/publish/mixed_via_post_encrypted.yaml @@ -0,0 +1,266 @@ +interactions: +- request: + body: '"Dt7qBesIhJT2DweUJc2HRQ=="' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706724320847330"]'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 16:07:12 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?uuid=6aba7bea-b223-416e-b9f7-1e5406fc5381&seqn=1&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: '"Dt7qBesIhJT2DweUJc2HRQ=="' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706724321905127"]'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 16:07:12 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?uuid=6aba7bea-b223-416e-b9f7-1e5406fc5381&seqn=2&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: '"Vx8Hk6iVjiV+Qae1bfMq2w=="' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706724322939251"]'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 16:07:12 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?uuid=6aba7bea-b223-416e-b9f7-1e5406fc5381&seqn=1&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: '"Vx8Hk6iVjiV+Qae1bfMq2w=="' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706724323960752"]'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 16:07:12 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?uuid=6aba7bea-b223-416e-b9f7-1e5406fc5381&seqn=2&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: '"jw/KAwQAoKtQfHyYrROqSQ=="' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706724325062358"]'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 16:07:12 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?uuid=6aba7bea-b223-416e-b9f7-1e5406fc5381&seqn=1&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: '"jw/KAwQAoKtQfHyYrROqSQ=="' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706724326150829"]'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 16:07:12 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?uuid=6aba7bea-b223-416e-b9f7-1e5406fc5381&seqn=2&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: '"6uNMePrQmuhzydmUEs6KAl8teZTZfCbG27ApFSKyfr8="' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706724327259504"]'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 16:07:12 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?uuid=6aba7bea-b223-416e-b9f7-1e5406fc5381&seqn=1&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: '"6uNMePrQmuhzydmUEs6KAl8teZTZfCbG27ApFSKyfr8="' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706724328343318"]'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 16:07:12 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?uuid=6aba7bea-b223-416e-b9f7-1e5406fc5381&seqn=2&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/publish/not_permitted.yaml b/tests/integrational/fixtures/tornado/publish/not_permitted.yaml new file mode 100644 index 00000000..6a5171a3 --- /dev/null +++ b/tests/integrational/fixtures/tornado/publish/not_permitted.yaml @@ -0,0 +1,98 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-98863562-19a6-4760-bf0b-d537d1f5c582/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f/0/not_permitted_channel/0/%22hey%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"message":"Forbidden","payload":{"channels":["not_permitted_channel"]},"error":true,"service":"Access + Manager","status":403} + +'} + headers: + - !!python/tuple + - Server + - [nginx] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - X-Blocks-Enabled + - ['0'] + - !!python/tuple + - Cache-Control + - ['no-cache, no-store, must-revalidate'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Date + - ['Sun, 16 Oct 2016 17:25:46 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset=UTF-8] + - !!python/tuple + - Access-Control-Allow-Headers + - ['Origin, X-Requested-With, Content-Type, Accept'] + - !!python/tuple + - X-Consumed-Content-Encoding + - [gzip] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Transfer-Encoding + - [chunked] + status: {code: 403, message: Forbidden} + url: http://ps.pndsn.com/publish/pub-c-98863562-19a6-4760-bf0b-d537d1f5c582/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f/0/not_permitted_channel/0/%22hey%22?seqn=1&uuid=2bf14161-016e-4d0c-823a-d29acd1b2505&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-98863562-19a6-4760-bf0b-d537d1f5c582/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f/0/not_permitted_channel/0/%22hey%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"message":"Forbidden","payload":{"channels":["not_permitted_channel"]},"error":true,"service":"Access + Manager","status":403} + +'} + headers: + - !!python/tuple + - Server + - [nginx] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - X-Blocks-Enabled + - ['0'] + - !!python/tuple + - Cache-Control + - ['no-cache, no-store, must-revalidate'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Date + - ['Sun, 16 Oct 2016 17:25:46 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset=UTF-8] + - !!python/tuple + - Access-Control-Allow-Headers + - ['Origin, X-Requested-With, Content-Type, Accept'] + - !!python/tuple + - X-Consumed-Content-Encoding + - [gzip] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Transfer-Encoding + - [chunked] + status: {code: 403, message: Forbidden} + url: http://ps.pndsn.com/publish/pub-c-98863562-19a6-4760-bf0b-d537d1f5c582/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f/0/not_permitted_channel/0/%22hey%22?seqn=2&uuid=2bf14161-016e-4d0c-823a-d29acd1b2505&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/publish/object_via_get.yaml b/tests/integrational/fixtures/tornado/publish/object_via_get.yaml new file mode 100644 index 00000000..488ca2b2 --- /dev/null +++ b/tests/integrational/fixtures/tornado/publish/object_via_get.yaml @@ -0,0 +1,68 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%7B%22online%22%3A%20true%2C%20%22name%22%3A%20%22Alex%22%7D?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706653397219269"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:08:59 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%7B%22online%22%3A%20true%2C%20%22name%22%3A%20%22Alex%22%7D?seqn=1&uuid=a21d5862-c1e8-4baf-9fb2-b7e1ea9a05f6&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%7B%22online%22%3A%20true%2C%20%22name%22%3A%20%22Alex%22%7D?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706653398506519"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:08:59 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%7B%22online%22%3A%20true%2C%20%22name%22%3A%20%22Alex%22%7D?seqn=2&uuid=a21d5862-c1e8-4baf-9fb2-b7e1ea9a05f6&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/publish/object_via_get_encrypted.yaml b/tests/integrational/fixtures/tornado/publish/object_via_get_encrypted.yaml new file mode 100644 index 00000000..54848726 --- /dev/null +++ b/tests/integrational/fixtures/tornado/publish/object_via_get_encrypted.yaml @@ -0,0 +1,68 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22Kwwg99lDMKM0%2FT%2F3EG49rh%2Bnnex2yBo%2F4kK5L7CC%2FF%2BDtMHVInyW%2FgaiX6J8iUMc%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706653400646308"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:09:00 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22Kwwg99lDMKM0%2FT%2F3EG49rh%2Bnnex2yBo%2F4kK5L7CC%2FF%2BDtMHVInyW%2FgaiX6J8iUMc%22?seqn=1&uuid=bae44d11-c6ec-4478-b78c-244684ffb7e0&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22Kwwg99lDMKM0%2FT%2F3EG49rh%2Bnnex2yBo%2F4kK5L7CC%2FF%2BDtMHVInyW%2FgaiX6J8iUMc%22?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706653401928744"]'} + headers: + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 14:09:00 GMT'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0/%22Kwwg99lDMKM0%2FT%2F3EG49rh%2Bnnex2yBo%2F4kK5L7CC%2FF%2BDtMHVInyW%2FgaiX6J8iUMc%22?seqn=2&uuid=bae44d11-c6ec-4478-b78c-244684ffb7e0&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/publish/object_via_post.yaml b/tests/integrational/fixtures/tornado/publish/object_via_post.yaml new file mode 100644 index 00000000..ccfc2e2b --- /dev/null +++ b/tests/integrational/fixtures/tornado/publish/object_via_post.yaml @@ -0,0 +1,68 @@ +interactions: +- request: + body: '{"online": true, "name": "Alex"}' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706787329216107"]'} + headers: + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 17:52:12 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?seqn=1&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=ae3a3afd-d92b-4cb2-a1a8-e93f88d2f6ff +- request: + body: '{"online": true, "name": "Alex"}' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706787330184998"]'} + headers: + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 17:52:13 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?seqn=2&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=ae3a3afd-d92b-4cb2-a1a8-e93f88d2f6ff +version: 1 diff --git a/tests/integrational/fixtures/tornado/publish/object_via_post_encrypted.yaml b/tests/integrational/fixtures/tornado/publish/object_via_post_encrypted.yaml new file mode 100644 index 00000000..ca5525d9 --- /dev/null +++ b/tests/integrational/fixtures/tornado/publish/object_via_post_encrypted.yaml @@ -0,0 +1,68 @@ +interactions: +- request: + body: '"Kwwg99lDMKM0/T/3EG49rh+nnex2yBo/4kK5L7CC/F+DtMHVInyW/gaiX6J8iUMc"' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706781595277610"]'} + headers: + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 17:42:39 GMT'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?uuid=7313f601-1fc1-4c50-a1b8-2a611f8b86cc&pnsdk=PubNub-Python-Tornado%2F4.0.4&seqn=1 +- request: + body: '"Kwwg99lDMKM0/T/3EG49rh+nnex2yBo/4kK5L7CC/F+DtMHVInyW/gaiX6J8iUMc"' + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: POST + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '[1,"Sent","14706781596540558"]'} + headers: + - !!python/tuple + - Date + - ['Mon, 08 Aug 2016 17:42:39 GMT'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/tornado-publish/0?uuid=7313f601-1fc1-4c50-a1b8-2a611f8b86cc&pnsdk=PubNub-Python-Tornado%2F4.0.4&seqn=2 +version: 1 diff --git a/tests/integrational/fixtures/tornado/state/multiple_channel.yaml b/tests/integrational/fixtures/tornado/state/multiple_channel.yaml new file mode 100644 index 00000000..d9c578a5 --- /dev/null +++ b/tests/integrational/fixtures/tornado/state/multiple_channel.yaml @@ -0,0 +1,89 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-tornado-ch1,state-tornado-ch2/uuid/state-tornado-uuid/data?pnsdk=PubNub-Python-Tornado%2F4.0.4&state=%7B%22count%22%3A+5%2C+%22name%22%3A+%22Alex%22%7D + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"count": 5, "name": + "Alex"}, "service": "Presence"}'} + headers: + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Content-Length + - ['96'] + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 07:39:00 GMT'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-tornado-ch1,state-tornado-ch2/uuid/state-tornado-uuid/data?pnsdk=PubNub-Python-Tornado%2F4.0.4&state=%7B%22count%22%3A%205%2C%20%22name%22%3A%20%22Alex%22%7D&uuid=state-tornado-uuid +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-tornado-ch1,state-tornado-ch2/uuid/state-tornado-uuid?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"channels": {"state-tornado-ch2": + {"count": 5, "name": "Alex"}, "state-tornado-ch1": {"count": 5, "name": "Alex"}}}, + "service": "Presence", "uuid": "state-tornado-uuid"}'} + headers: + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Content-Length + - ['214'] + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 07:39:01 GMT'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-tornado-ch1,state-tornado-ch2/uuid/state-tornado-uuid?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=state-tornado-uuid +version: 1 diff --git a/tests/integrational/fixtures/tornado/state/single_channel.yaml b/tests/integrational/fixtures/tornado/state/single_channel.yaml new file mode 100644 index 00000000..212771fb --- /dev/null +++ b/tests/integrational/fixtures/tornado/state/single_channel.yaml @@ -0,0 +1,88 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-tornado-ch/uuid/state-tornado-uuid/data?pnsdk=PubNub-Python-Tornado%2F4.0.4&state=%7B%22name%22%3A+%22Alex%22%2C+%22count%22%3A+5%7D + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"count": 5, "name": + "Alex"}, "service": "Presence"}'} + headers: + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 07:32:02 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Length + - ['96'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Connection + - [close] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-tornado-ch/uuid/state-tornado-uuid/data?pnsdk=PubNub-Python-Tornado%2F4.0.4&state=%7B%22name%22%3A%20%22Alex%22%2C%20%22count%22%3A%205%7D&uuid=state-tornado-uuid +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-tornado-ch/uuid/state-tornado-uuid?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "uuid": "state-tornado-uuid", "service": "Presence", + "message": "OK", "payload": {"count": 5, "name": "Alex"}, "channel": "state-tornado-ch"}'} + headers: + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Date + - ['Wed, 10 Aug 2016 07:32:02 GMT'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Length + - ['157'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Connection + - [close] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/state-tornado-ch/uuid/state-tornado-uuid?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=state-tornado-uuid +version: 1 diff --git a/tests/integrational/fixtures/tornado/subscribe/group_join_leave.yaml b/tests/integrational/fixtures/tornado/subscribe/group_join_leave.yaml new file mode 100644 index 00000000..491fc202 --- /dev/null +++ b/tests/integrational/fixtures/tornado/subscribe/group_join_leave.yaml @@ -0,0 +1,380 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/subscribe-test-group?add=subscribe-test-channel + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + - !!python/tuple + - Content-Length + - ['79'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:03 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/subscribe-test-group?add=subscribe-test-channel&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-subscribe-messenger +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-test-group%2Csubscribe-test-group-pnpres&tt=0 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869649333428","r":12},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:05 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-test-group,subscribe-test-group-pnpres&pnsdk=PubNub-Python-Tornado%2F4.0.4&tt=0&uuid=test-subscribe-listener +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-test-group%2Csubscribe-test-group-pnpres&tr=12&tt=14818869649333428 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869660519117","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818869659745206","r":1},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"subscribe-test-channel-pnpres","d":{"action": + "join", "timestamp": 1481886965, "uuid": "test-subscribe-listener", "occupancy": + 1},"b":"subscribe-test-group-pnpres"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['314'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:06 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-test-group,subscribe-test-group-pnpres&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&tt=14818869649333428&uuid=test-subscribe-listener +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-test-group&tt=0 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869660187938","r":12},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:06 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-test-group&pnsdk=PubNub-Python-Tornado%2F4.0.4&tt=0&uuid=test-subscribe-messenger +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-test-group%2Csubscribe-test-group-pnpres&tr=12&tt=14818869660519117 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869669268862","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818869668806336","r":2},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"subscribe-test-channel-pnpres","d":{"action": + "join", "timestamp": 1481886966, "uuid": "test-subscribe-messenger", "occupancy": + 2},"b":"subscribe-test-group-pnpres"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['315'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:06 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-test-group,subscribe-test-group-pnpres&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&tt=14818869660519117&uuid=test-subscribe-listener +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?channel-group=subscribe-test-group + response: + body: {string: !!python/unicode '{"status": 200, "action": "leave", "message": + "OK", "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:07 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?channel-group=subscribe-test-group&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-subscribe-messenger +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-test-group%2Csubscribe-test-group-pnpres&tr=12&tt=14818869669268862 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869671710838","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818869670946160","r":1},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"subscribe-test-channel-pnpres","d":{"action": + "leave", "timestamp": 1481886967, "uuid": "test-subscribe-messenger", "occupancy": + 1},"b":"subscribe-test-group-pnpres"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['316'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:07 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-test-group,subscribe-test-group-pnpres&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&tt=14818869669268862&uuid=test-subscribe-listener +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-test-group%2Csubscribe-test-group-pnpres&tr=12&tt=14818869671710838 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869675101369","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818869674639626","r":2},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"subscribe-test-channel-pnpres","d":{"action": + "leave", "timestamp": 1481886967, "uuid": "test-subscribe-listener", "occupancy": + 0},"b":"subscribe-test-group-pnpres"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['315'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:07 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-test-group,subscribe-test-group-pnpres&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&tt=14818869671710838&uuid=test-subscribe-listener +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?channel-group=subscribe-test-group + response: + body: {string: !!python/unicode '{"status": 200, "action": "leave", "message": + "OK", "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:07 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?channel-group=subscribe-test-group&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-subscribe-listener +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/subscribe-test-group?remove=subscribe-test-channel + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + - !!python/tuple + - Content-Length + - ['79'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:07 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/subscribe-test-group?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-subscribe-messenger&remove=subscribe-test-channel +version: 1 diff --git a/tests/integrational/fixtures/tornado/subscribe/group_sub_pub_unsub.yaml b/tests/integrational/fixtures/tornado/subscribe/group_sub_pub_unsub.yaml new file mode 100644 index 00000000..54770c32 --- /dev/null +++ b/tests/integrational/fixtures/tornado/subscribe/group_sub_pub_unsub.yaml @@ -0,0 +1,230 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/subscribe-unsubscribe-group?add=subscribe-unsubscribe-channel + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + - !!python/tuple + - Content-Length + - ['79'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:07 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/subscribe-unsubscribe-group?add=subscribe-unsubscribe-channel&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=eb63e8cb-b81c-4ccc-b411-bb53264e3c09 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-unsubscribe-group&tt=0 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869687160475","r":12},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:08 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-unsubscribe-group&pnsdk=PubNub-Python-Tornado%2F4.0.4&tt=0&uuid=eb63e8cb-b81c-4ccc-b411-bb53264e3c09 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/subscribe-unsubscribe-channel/0/%22hey%22 + response: + body: {string: !!python/unicode '[1,"Sent","14818869688799557"]'} + headers: + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:08 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/subscribe-unsubscribe-channel/0/%22hey%22?pnsdk=PubNub-Python-Tornado%2F4.0.4&seqn=1&uuid=eb63e8cb-b81c-4ccc-b411-bb53264e3c09 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-unsubscribe-group&tr=12&tt=14818869687160475 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869688928592","r":12},"m":[{"a":"2","f":0,"i":"eb63e8cb-b81c-4ccc-b411-bb53264e3c09","s":1,"p":{"t":"14818869688799557","r":12},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"subscribe-unsubscribe-channel","d":"hey","b":"subscribe-unsubscribe-group"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['275'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:08 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-unsubscribe-group&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&tt=14818869687160475&uuid=eb63e8cb-b81c-4ccc-b411-bb53264e3c09 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?channel-group=subscribe-unsubscribe-group + response: + body: {string: !!python/unicode '{"status": 200, "action": "leave", "message": + "OK", "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:09 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?channel-group=subscribe-unsubscribe-group&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=eb63e8cb-b81c-4ccc-b411-bb53264e3c09 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/subscribe-unsubscribe-group?remove=subscribe-unsubscribe-channel + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + - !!python/tuple + - Content-Length + - ['79'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:09 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/subscribe-unsubscribe-group?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=eb63e8cb-b81c-4ccc-b411-bb53264e3c09&remove=subscribe-unsubscribe-channel +version: 1 diff --git a/tests/integrational/fixtures/tornado/subscribe/group_sub_unsub.yaml b/tests/integrational/fixtures/tornado/subscribe/group_sub_unsub.yaml new file mode 100644 index 00000000..0d73d43b --- /dev/null +++ b/tests/integrational/fixtures/tornado/subscribe/group_sub_unsub.yaml @@ -0,0 +1,164 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/subscribe-unsubscribe-group?add=subscribe-unsubscribe-channel + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + - !!python/tuple + - Content-Length + - ['79'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:09 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/subscribe-unsubscribe-group?add=subscribe-unsubscribe-channel&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=709e16b4-d30b-4854-98c2-c4e965564abb +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-unsubscribe-group&tt=0 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869688928592","r":12},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:10 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/,/0?channel-group=subscribe-unsubscribe-group&pnsdk=PubNub-Python-Tornado%2F4.0.4&tt=0&uuid=709e16b4-d30b-4854-98c2-c4e965564abb +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?channel-group=subscribe-unsubscribe-group + response: + body: {string: !!python/unicode '{"status": 200, "action": "leave", "message": + "OK", "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:10 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/,/leave?channel-group=subscribe-unsubscribe-group&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=709e16b4-d30b-4854-98c2-c4e965564abb +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/subscribe-unsubscribe-group?remove=subscribe-unsubscribe-channel + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: + - !!python/tuple + - Content-Length + - ['79'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:10 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/subscribe-unsubscribe-group?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=709e16b4-d30b-4854-98c2-c4e965564abb&remove=subscribe-unsubscribe-channel +version: 1 diff --git a/tests/integrational/fixtures/tornado/subscribe/join_leave.yaml b/tests/integrational/fixtures/tornado/subscribe/join_leave.yaml new file mode 100644 index 00000000..58076023 --- /dev/null +++ b/tests/integrational/fixtures/tornado/subscribe/join_leave.yaml @@ -0,0 +1,294 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch,subscribe-tornado-ch-pnpres/0?tt=0 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869603870494","r":12},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:00 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch,subscribe-tornado-ch-pnpres/0?tt=0&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=subscribe-tornado-listener-3 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch,subscribe-tornado-ch-pnpres/0?tr=12&tt=14818869603870494 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869613057835","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818869612281954","r":1},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"subscribe-tornado-ch-pnpres","d":{"action": + "join", "timestamp": 1481886961, "uuid": "subscribe-tornado-listener-3", "occupancy": + 1},"b":"subscribe-tornado-ch-pnpres"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['317'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:01 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch,subscribe-tornado-ch-pnpres/0?tt=14818869603870494&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&uuid=subscribe-tornado-listener-3 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch/0?tt=0 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869612949707","r":12},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:01 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch/0?tt=0&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=subscribe-tornado-messenger-3 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch,subscribe-tornado-ch-pnpres/0?tr=12&tt=14818869613057835 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869622225817","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818869621699814","r":2},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"subscribe-tornado-ch-pnpres","d":{"action": + "join", "timestamp": 1481886962, "uuid": "subscribe-tornado-messenger-3", + "occupancy": 2},"b":"subscribe-tornado-ch-pnpres"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['318'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:02 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch,subscribe-tornado-ch-pnpres/0?tt=14818869613057835&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&uuid=subscribe-tornado-listener-3 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch,subscribe-tornado-ch-pnpres/0?tr=12&tt=14818869622225817 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869626041325","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818869625576502","r":2},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"subscribe-tornado-ch-pnpres","d":{"action": + "leave", "timestamp": 1481886962, "uuid": "subscribe-tornado-messenger-3", + "occupancy": 1},"b":"subscribe-tornado-ch-pnpres"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['319'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:02 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch,subscribe-tornado-ch-pnpres/0?tt=14818869622225817&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&uuid=subscribe-tornado-listener-3 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/subscribe-tornado-ch/leave + response: + body: {string: !!python/unicode '{"status": 200, "action": "leave", "message": + "OK", "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:02 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/subscribe-tornado-ch/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=subscribe-tornado-messenger-3 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch,subscribe-tornado-ch-pnpres/0?tr=12&tt=14818869626041325 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869630029993","r":12},"m":[{"a":"2","f":0,"p":{"t":"14818869628593295","r":1},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"subscribe-tornado-ch-pnpres","d":{"action": + "join", "timestamp": 1481886962, "uuid": "subscribe-tornado-listener-3", "occupancy": + 1},"b":"subscribe-tornado-ch-pnpres"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['317'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:03 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch,subscribe-tornado-ch-pnpres/0?tt=14818869626041325&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&uuid=subscribe-tornado-listener-3 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/subscribe-tornado-ch/leave + response: + body: {string: !!python/unicode '{"status": 200, "action": "leave", "message": + "OK", "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:03 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/subscribe-tornado-ch/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=subscribe-tornado-listener-3 +version: 1 diff --git a/tests/integrational/fixtures/tornado/subscribe/sub_pub_unsub.yaml b/tests/integrational/fixtures/tornado/subscribe/sub_pub_unsub.yaml new file mode 100644 index 00000000..b5934a99 --- /dev/null +++ b/tests/integrational/fixtures/tornado/subscribe/sub_pub_unsub.yaml @@ -0,0 +1,144 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch/0?tt=0 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869631121257","r":12},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:03 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch/0?tt=0&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=18aa1154-a3bd-4e71-994d-8685b56eeecc +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/subscribe-tornado-ch/0/%22hey%22 + response: + body: {string: !!python/unicode '[1,"Sent","14818869633015166"]'} + headers: + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:03 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/subscribe-tornado-ch/0/%22hey%22?pnsdk=PubNub-Python-Tornado%2F4.0.4&seqn=1&uuid=18aa1154-a3bd-4e71-994d-8685b56eeecc +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch/0?tr=12&tt=14818869631121257 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869633017084","r":12},"m":[{"a":"2","f":0,"i":"18aa1154-a3bd-4e71-994d-8685b56eeecc","s":1,"p":{"t":"14818869633015166","r":12},"k":"sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe","c":"subscribe-tornado-ch","d":"hey"}]}'} + headers: + - !!python/tuple + - Content-Length + - ['232'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:03 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch/0?tt=14818869631121257&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&uuid=18aa1154-a3bd-4e71-994d-8685b56eeecc +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/subscribe-tornado-ch/leave + response: + body: {string: !!python/unicode '{"status": 200, "action": "leave", "message": + "OK", "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:03 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/subscribe-tornado-ch/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=18aa1154-a3bd-4e71-994d-8685b56eeecc +version: 1 diff --git a/tests/integrational/fixtures/tornado/subscribe/sub_unsub.yaml b/tests/integrational/fixtures/tornado/subscribe/sub_unsub.yaml new file mode 100644 index 00000000..44debda4 --- /dev/null +++ b/tests/integrational/fixtures/tornado/subscribe/sub_unsub.yaml @@ -0,0 +1,78 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch/0?tt=0 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869633017084","r":12},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:03 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/subscribe-tornado-ch/0?tt=0&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=5107666e-798c-459b-89b2-5329353ea8e1 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/subscribe-tornado-ch/leave + response: + body: {string: !!python/unicode '{"status": 200, "action": "leave", "message": + "OK", "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:03 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/subscribe-tornado-ch/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=5107666e-798c-459b-89b2-5329353ea8e1 +version: 1 diff --git a/tests/integrational/fixtures/tornado/subscribe/subscribe_tep_by_step.yaml b/tests/integrational/fixtures/tornado/subscribe/subscribe_tep_by_step.yaml new file mode 100644 index 00000000..42e38e7d --- /dev/null +++ b/tests/integrational/fixtures/tornado/subscribe/subscribe_tep_by_step.yaml @@ -0,0 +1,156 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel1/0?tt=0 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869706095939","r":12},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:10 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel1/0?tt=0&pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-here-now-uuid +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel1,test-here-now-channel2,test-here-now-channel3/0?tr=12&tt=0 + response: + body: {string: !!python/unicode '{"t":{"t":"14818869716760786","r":12},"m":[]}'} + headers: + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:11 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-channel1,test-here-now-channel2,test-here-now-channel3/0?tt=0&pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&uuid=test-here-now-uuid +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel1,test-here-now-channel2 + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "payload": {"channels": + {"test-here-now-channel1": {"uuids": ["test-here-now-uuid"], "occupancy": + 1}, "test-here-now-channel2": {"uuids": ["test-here-now-uuid"], "occupancy": + 1}}, "total_channels": 2, "total_occupancy": 2}, "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['279'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:16 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel1,test-here-now-channel2?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-here-now-uuid +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel1,test-here-now-channel2/leave + response: + body: {string: !!python/unicode '{"status": 200, "action": "leave", "message": + "OK", "service": "Presence"}'} + headers: + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Date + - ['Fri, 16 Dec 2016 11:16:17 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Age + - ['0'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/test-here-now-channel1,test-here-now-channel2/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=test-here-now-uuid +version: 1 diff --git a/tests/integrational/fixtures/tornado/where_now/multiple_channels.yaml b/tests/integrational/fixtures/tornado/where_now/multiple_channels.yaml new file mode 100644 index 00000000..73cb3d4e --- /dev/null +++ b/tests/integrational/fixtures/tornado/where_now/multiple_channels.yaml @@ -0,0 +1,187 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/where-now-tornado-ch1/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tt=0 + response: + body: {string: '{"t":{"t":"14717822576549802","r":12},"m":[]}'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 12:24:17 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/where-now-tornado-ch1/0?tt=0&uuid=where-now-tornado-uuid&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/where-now-tornado-ch1,where-now-tornado-ch2/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&tt=0 + response: + body: {string: '{"t":{"t":"14717822577171975","r":12},"m":[]}'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 12:24:17 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/where-now-tornado-ch1,where-now-tornado-ch2/0?tr=12&tt=0&uuid=where-now-tornado-uuid&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/where-now-tornado-ch1,where-now-tornado-ch2/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tr=12&tt=0 + response: + body: {string: '{"t":{"t":"14717822577229301","r":12},"m":[]}'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Content-Length + - ['45'] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 12:24:17 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/where-now-tornado-ch1,where-now-tornado-ch2/0?tr=12&tt=0&uuid=where-now-tornado-uuid&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/uuid/where-now-tornado-uuid?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"channels": ["where-now-tornado-ch2", + "where-now-tornado-ch1"]}, "service": "Presence"}'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Content-Length + - ['132'] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 12:24:22 GMT'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Server + - [Pubnub Presence] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/uuid/where-now-tornado-uuid?uuid=where-now-tornado-uuid&pnsdk=PubNub-Python-Tornado%2F4.0.4 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/where-now-tornado-ch1,where-now-tornado-ch2/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 12:24:23 GMT'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Server + - [Pubnub Presence] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/where-now-tornado-ch1,where-now-tornado-ch2/leave?uuid=where-now-tornado-uuid&pnsdk=PubNub-Python-Tornado%2F4.0.4 +version: 1 diff --git a/tests/integrational/fixtures/tornado/where_now/single_channel.yaml b/tests/integrational/fixtures/tornado/where_now/single_channel.yaml new file mode 100644 index 00000000..c4ea740e --- /dev/null +++ b/tests/integrational/fixtures/tornado/where_now/single_channel.yaml @@ -0,0 +1,121 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/where-now-tornado-ch/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&tt=0 + response: + body: {string: '{"t":{"t":"14717827927747241","r":3},"m":[]}'} + headers: + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Content-Length + - ['44'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 12:33:12 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/where-now-tornado-ch/0?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=where-now-tornado-uuid&tt=0 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/uuid/where-now-tornado-uuid?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "message": "OK", "payload": {"channels": ["where-now-tornado-ch"]}, + "service": "Presence"}'} + headers: + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Content-Length + - ['106'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 12:33:23 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/uuid/where-now-tornado-uuid?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=where-now-tornado-uuid +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/where-now-tornado-ch/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4 + response: + body: {string: '{"status": 200, "action": "leave", "message": "OK", "service": + "Presence"}'} + headers: + - !!python/tuple + - Access-Control-Allow-Methods + - ['OPTIONS, GET, POST'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Server + - [Pubnub Presence] + - !!python/tuple + - Content-Length + - ['74'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Date + - ['Sun, 21 Aug 2016 12:33:23 GMT'] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + status: {code: 200, message: OK} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/where-now-tornado-ch/leave?pnsdk=PubNub-Python-Tornado%2F4.0.4&uuid=where-now-tornado-uuid +version: 1 diff --git a/tests/integrational/fixtures/twisted/groups/add_channels.yaml b/tests/integrational/fixtures/twisted/groups/add_channels.yaml new file mode 100644 index 00000000..ea1c56e4 --- /dev/null +++ b/tests/integrational/fixtures/twisted/groups/add_channels.yaml @@ -0,0 +1,16 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/cgttg?add=cgttc0%2Ccgttc1&pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/cgttg?add=cgttc0,cgttc1&pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=a88dfb31-755d-4db4-844e-0b56b0699f92 +version: 1 diff --git a/tests/integrational/fixtures/twisted/groups/add_single_channel.yaml b/tests/integrational/fixtures/twisted/groups/add_single_channel.yaml new file mode 100644 index 00000000..ed06f381 --- /dev/null +++ b/tests/integrational/fixtures/twisted/groups/add_single_channel.yaml @@ -0,0 +1,16 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/cgttg?add=cgttc&pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/cgttg?add=cgttc&pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=a88dfb31-755d-4db4-844e-0b56b0699f92 +version: 1 diff --git a/tests/integrational/fixtures/twisted/groups/list_channels.yaml b/tests/integrational/fixtures/twisted/groups/list_channels.yaml new file mode 100644 index 00000000..531de4f9 --- /dev/null +++ b/tests/integrational/fixtures/twisted/groups/list_channels.yaml @@ -0,0 +1,16 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/cgttg?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '{"status": 200, "payload": {"channels": ["cgttc0", + "cgttc1"], "group": "cgttg"}, "service": "channel-registry", "error": false}'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/cgttg?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=4b7a6c42-966f-41ad-a395-c9e9ef5919ec +version: 1 diff --git a/tests/integrational/fixtures/twisted/groups/remove_channels.yaml b/tests/integrational/fixtures/twisted/groups/remove_channels.yaml new file mode 100644 index 00000000..540e2127 --- /dev/null +++ b/tests/integrational/fixtures/twisted/groups/remove_channels.yaml @@ -0,0 +1,16 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/cgttg?pnsdk=PubNub-Python-Twisted%2F4.0.4&remove=cgttc0%2Ccgttc1 + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/cgttg?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=a88dfb31-755d-4db4-844e-0b56b0699f92&remove=cgttc0,cgttc1 +version: 1 diff --git a/tests/integrational/fixtures/twisted/groups/remove_single_channel.yaml b/tests/integrational/fixtures/twisted/groups/remove_single_channel.yaml new file mode 100644 index 00000000..e992888f --- /dev/null +++ b/tests/integrational/fixtures/twisted/groups/remove_single_channel.yaml @@ -0,0 +1,16 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/cgttg?pnsdk=PubNub-Python-Twisted%2F4.0.4&remove=cgttc + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "channel-registry", + "error": false}'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/v1/channel-registration/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel-group/cgttg?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=a88dfb31-755d-4db4-844e-0b56b0699f92&remove=cgttc +version: 1 diff --git a/tests/integrational/fixtures/twisted/here_now/global.yaml b/tests/integrational/fixtures/twisted/here_now/global.yaml new file mode 100644 index 00000000..9d9d53a8 --- /dev/null +++ b/tests/integrational/fixtures/twisted/here_now/global.yaml @@ -0,0 +1,18 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "payload": {"channels": + {"twisted-test-1": {"uuids": ["00de2586-7ad8-4955-b5f6-87cae3215d02"], "occupancy": + 1}, "twisted-test": {"uuids": ["00de2586-7ad8-4955-b5f6-87cae3215d02"], "occupancy": + 1}}, "total_channels": 2, "total_occupancy": 2}, "service": "Presence"}'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=9c7b940a-e5c7-42d5-af9b-c6ddcf58bdc9 +version: 1 diff --git a/tests/integrational/fixtures/twisted/here_now/multiple.yaml b/tests/integrational/fixtures/twisted/here_now/multiple.yaml new file mode 100644 index 00000000..3a131396 --- /dev/null +++ b/tests/integrational/fixtures/twisted/here_now/multiple.yaml @@ -0,0 +1,17 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/twisted-test-1,twisted-test-1?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "payload": {"channels": + {"twisted-test-1": {"uuids": ["00de2586-7ad8-4955-b5f6-87cae3215d02"], "occupancy": + 1}}, "total_channels": 1, "total_occupancy": 1}, "service": "Presence"}'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/twisted-test-1,twisted-test-1?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=9c7b940a-e5c7-42d5-af9b-c6ddcf58bdc9 +version: 1 diff --git a/tests/integrational/fixtures/twisted/here_now/single.yaml b/tests/integrational/fixtures/twisted/here_now/single.yaml new file mode 100644 index 00000000..d9740977 --- /dev/null +++ b/tests/integrational/fixtures/twisted/here_now/single.yaml @@ -0,0 +1,16 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/twisted-test?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "service": "Presence", + "uuids": ["00de2586-7ad8-4955-b5f6-87cae3215d02"], "occupancy": 1}'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/twisted-test?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=9c7b940a-e5c7-42d5-af9b-c6ddcf58bdc9 +version: 1 diff --git a/tests/integrational/fixtures/twisted/publish/do_not_store.yaml b/tests/integrational/fixtures/twisted/publish/do_not_store.yaml new file mode 100644 index 00000000..f60ce282 --- /dev/null +++ b/tests/integrational/fixtures/twisted/publish/do_not_store.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%22whatever%22?pnsdk=PubNub-Python-Twisted%2F4.0.4&store=0 + response: + body: {string: !!python/unicode '[1,"Sent","14768809388217046"]'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%22whatever%22?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=359b199b-9f4f-4368-bbc8-33e09b28a280&store=0&seqn=1 +version: 1 diff --git a/tests/integrational/fixtures/twisted/publish/forbidden.yaml b/tests/integrational/fixtures/twisted/publish/forbidden.yaml new file mode 100644 index 00000000..3906efc5 --- /dev/null +++ b/tests/integrational/fixtures/twisted/publish/forbidden.yaml @@ -0,0 +1,18 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-98863562-19a6-4760-bf0b-d537d1f5c582/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f/0/not_permitted_channel/0/%22hey%22?pnsdk=PubNub-Python-Twisted%2F4.0.4&signature=oZNiMOxZ6Zg-pAnPpdrQ7rLM2n4Vmk_p8wewWF51wng%3D×tamp=1477397184 + response: + body: {string: '{"message":"Forbidden","payload":{"channels":["not_permitted_channel"]},"error":true,"service":"Access + Manager","status":403} + +'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 403, message: ''} + url: http://ps.pndsn.com/publish/pub-c-98863562-19a6-4760-bf0b-d537d1f5c582/sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f/0/not_permitted_channel/0/%22hey%22?timestamp=1477397184&pnsdk=PubNub-Python-Twisted%2F4.0.4&signature=oZNiMOxZ6Zg-pAnPpdrQ7rLM2n4Vmk_p8wewWF51wng=&seqn=1&uuid=c7accbb8-2606-41bb-9484-7cea7e13817e +version: 1 diff --git a/tests/integrational/fixtures/twisted/publish/invalid_key.yaml b/tests/integrational/fixtures/twisted/publish/invalid_key.yaml new file mode 100644 index 00000000..885d0011 --- /dev/null +++ b/tests/integrational/fixtures/twisted/publish/invalid_key.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/fake/demo/0/twisted-test/0/%22hey%22?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '[0,"Invalid Key","14767989321048626"]'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 400, message: ''} + url: http://ps.pndsn.com/publish/fake/demo/0/twisted-test/0/%22hey%22?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=7b9b30d1-27b5-4764-bbee-60c7c584b04d&seqn=1 +version: 1 diff --git a/tests/integrational/fixtures/twisted/publish/meta_object.yaml b/tests/integrational/fixtures/twisted/publish/meta_object.yaml new file mode 100644 index 00000000..9753f6c8 --- /dev/null +++ b/tests/integrational/fixtures/twisted/publish/meta_object.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%22hi%22?meta=%7B%22a%22%3A+2%2C+%22b%22%3A+true%7D&pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '[1,"Sent","14768802793338041"]'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%22hi%22?pnsdk=PubNub-Python-Twisted%2F4.0.4&meta=%7B%22a%22%3A%202%2C%20%22b%22%3A%20true%7D&uuid=b299acc9-2b04-46ff-aab2-945c0c7f0678&seqn=1 +version: 1 diff --git a/tests/integrational/fixtures/twisted/publish/mixed_encrypted_via_get.yaml b/tests/integrational/fixtures/twisted/publish/mixed_encrypted_via_get.yaml new file mode 100644 index 00000000..cf6b666b --- /dev/null +++ b/tests/integrational/fixtures/twisted/publish/mixed_encrypted_via_get.yaml @@ -0,0 +1,54 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%22Dt7qBesIhJT2DweUJc2HRQ%3D%3D%22?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '[1,"Sent","14768059311032132"]'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%22Dt7qBesIhJT2DweUJc2HRQ%3D%3D%22?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=16bfed08-6b5a-4d83-ac10-a37b800d5f3a&seqn=1 +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%22Vx8Hk6iVjiV%2BQae1bfMq2w%3D%3D%22?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '[1,"Sent","14768059313886330"]'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%22Vx8Hk6iVjiV%2BQae1bfMq2w%3D%3D%22?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=00072bd8-45b7-42ac-9f54-f238c4af89b4&seqn=1 +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%22jw%2FKAwQAoKtQfHyYrROqSQ%3D%3D%22?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '[1,"Sent","14768059316467095"]'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%22jw%2FKAwQAoKtQfHyYrROqSQ%3D%3D%22?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=192154f7-3211-4677-8d8a-92b8bf25aff4&seqn=1 +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%226uNMePrQmuhzydmUEs6KAl8teZTZfCbG27ApFSKyfr8%3D%22?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '[1,"Sent","14768059389216173"]'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%226uNMePrQmuhzydmUEs6KAl8teZTZfCbG27ApFSKyfr8%3D%22?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=014b69e9-2481-47cb-8239-a8cc56b24502&seqn=1 +version: 1 diff --git a/tests/integrational/fixtures/twisted/publish/mixed_via_get.yaml b/tests/integrational/fixtures/twisted/publish/mixed_via_get.yaml new file mode 100644 index 00000000..4b165ba5 --- /dev/null +++ b/tests/integrational/fixtures/twisted/publish/mixed_via_get.yaml @@ -0,0 +1,54 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%22hi%22?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '[1,"Sent","14767908153114904"]'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%22hi%22?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=1ae81865-e92f-4c23-8e21-684a7bcdbe8a&seqn=1 +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/5?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '[1,"Sent","14767908155795869"]'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/5?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=1ae81865-e92f-4c23-8e21-684a7bcdbe8a&seqn=2 +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/true?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '[1,"Sent","14767908158387685"]'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/true?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=1ae81865-e92f-4c23-8e21-684a7bcdbe8a&seqn=3 +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%5B%22hi%22%2C%20%22hi2%22%2C%20%22hi3%22%5D?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '[1,"Sent","14767908161061457"]'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%5B%22hi%22%2C%20%22hi2%22%2C%20%22hi3%22%5D?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=1ae81865-e92f-4c23-8e21-684a7bcdbe8a&seqn=4 +version: 1 diff --git a/tests/integrational/fixtures/twisted/publish/object_via_get.yaml b/tests/integrational/fixtures/twisted/publish/object_via_get.yaml new file mode 100644 index 00000000..7967acb3 --- /dev/null +++ b/tests/integrational/fixtures/twisted/publish/object_via_get.yaml @@ -0,0 +1,15 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%7B%22three%22%3A%20true%2C%20%22one%22%3A%202%7D?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '[1,"Sent","14767908163698950"]'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/publish/pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/0/twisted-test/0/%7B%22three%22%3A%20true%2C%20%22one%22%3A%202%7D?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=1ae81865-e92f-4c23-8e21-684a7bcdbe8a&seqn=1 +version: 1 diff --git a/tests/integrational/fixtures/twisted/state/multiple_channels.yaml b/tests/integrational/fixtures/twisted/state/multiple_channels.yaml new file mode 100644 index 00000000..4735109d --- /dev/null +++ b/tests/integrational/fixtures/twisted/state/multiple_channels.yaml @@ -0,0 +1,16 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/twisted-test-0,twisted-test-1/uuid/someuuid/data?pnsdk=PubNub-Python-Twisted%2F4.0.4&state=%7B%22whatever%22%3A+%22something%22%7D + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "payload": {"whatever": + "something"}, "service": "Presence"}'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/twisted-test-0,twisted-test-1/uuid/someuuid/data?state=%7B%22whatever%22%3A%20%22something%22%7D&pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=someuuid +version: 1 diff --git a/tests/integrational/fixtures/twisted/state/single_channel.yaml b/tests/integrational/fixtures/twisted/state/single_channel.yaml new file mode 100644 index 00000000..73e43c3c --- /dev/null +++ b/tests/integrational/fixtures/twisted/state/single_channel.yaml @@ -0,0 +1,16 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/twisted-test/uuid/someuuid/data?pnsdk=PubNub-Python-Twisted%2F4.0.4&state=%7B%22whatever%22%3A+%22something%22%7D + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "payload": {"whatever": + "something"}, "service": "Presence"}'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/channel/twisted-test/uuid/someuuid/data?state=%7B%22whatever%22%3A%20%22something%22%7D&pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=someuuid +version: 1 diff --git a/tests/integrational/fixtures/twisted/where_now/multiple.yaml b/tests/integrational/fixtures/twisted/where_now/multiple.yaml new file mode 100644 index 00000000..d4a3f1a4 --- /dev/null +++ b/tests/integrational/fixtures/twisted/where_now/multiple.yaml @@ -0,0 +1,16 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/uuid/00de2586-7ad8-4955-b5f6-87cae3215d02?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "payload": {"channels": + ["twisted-test-2", "twisted-test-1"]}, "service": "Presence"}'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/uuid/00de2586-7ad8-4955-b5f6-87cae3215d02?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=d8f596f2-dc2c-4015-af8a-73374f770590 +version: 1 diff --git a/tests/integrational/fixtures/twisted/where_now/single.yaml b/tests/integrational/fixtures/twisted/where_now/single.yaml new file mode 100644 index 00000000..50deae83 --- /dev/null +++ b/tests/integrational/fixtures/twisted/where_now/single.yaml @@ -0,0 +1,16 @@ +interactions: +- request: + body: !!python/unicode + headers: + user-agent: [PubNub-Python-Twisted/4.0.4] + method: GET + uri: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/uuid/00de2586-7ad8-4955-b5f6-87cae3215d02?pnsdk=PubNub-Python-Twisted%2F4.0.4 + response: + body: {string: !!python/unicode '{"status": 200, "message": "OK", "payload": {"channels": + ["twisted-test-1"]}, "service": "Presence"}'} + headers: !!python/object:twisted.web.http_headers.Headers + _rawHeaders: + user-agent: [PubNub-Python-Twisted/4.0.4] + status: {code: 200, message: ''} + url: http://ps.pndsn.com/v2/presence/sub-key/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/uuid/00de2586-7ad8-4955-b5f6-87cae3215d02?pnsdk=PubNub-Python-Twisted%2F4.0.4&uuid=16de4bd1-c7a2-4913-9617-5ea0f624be4f +version: 1 diff --git a/tests/integrational/native_sync/__init__.py b/tests/integrational/native_sync/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integrational/native_sync/test_channel_groups.py b/tests/integrational/native_sync/test_channel_groups.py new file mode 100644 index 00000000..13b2d8cb --- /dev/null +++ b/tests/integrational/native_sync/test_channel_groups.py @@ -0,0 +1,171 @@ +import logging +import time +import unittest + +import pubnub +from pubnub.models.consumer.channel_group import PNChannelGroupsAddChannelResult, PNChannelGroupsListResult, \ + PNChannelGroupsRemoveChannelResult, PNChannelGroupsRemoveGroupResult +from pubnub.pubnub import PubNub +from tests.helper import pnconf_copy +from tests.integrational.vcr_helper import use_cassette_and_stub_time_sleep_native + +pubnub.set_stream_logger('pubnub', logging.DEBUG) + + +class TestPubNubChannelGroups(unittest.TestCase): + @use_cassette_and_stub_time_sleep_native( + 'tests/integrational/fixtures/native_sync/channel_groups/single_channel.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_single_channel(self): + ch = "channel-groups-native-ch" + gr = "channel-groups-native-cg" + pubnub = PubNub(pnconf_copy()) + + # cleanup + envelope = pubnub.remove_channel_group() \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsRemoveGroupResult) + + # add + envelope = pubnub.add_channel_to_channel_group() \ + .channels(ch) \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsAddChannelResult) + + time.sleep(2) + + # list + envelope = pubnub.list_channels_in_channel_group() \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsListResult) + assert len(envelope.result.channels) == 1 + assert envelope.result.channels[0] == ch + + # remove + envelope = pubnub.remove_channel_from_channel_group() \ + .channels(ch) \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsRemoveChannelResult) + + time.sleep(2) + + # list + envelope = pubnub.list_channels_in_channel_group() \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsListResult) + assert len(envelope.result.channels) == 0 + + @use_cassette_and_stub_time_sleep_native( + 'tests/integrational/fixtures/native_sync/channel_groups/add_remove_multiple_channels.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_add_remove_multiple_channels(self): + ch1 = "channel-groups-unit-ch1" + ch2 = "channel-groups-unit-ch2" + gr = "channel-groups-unit-cg" + pubnub = PubNub(pnconf_copy()) + + # cleanup + envelope = pubnub.remove_channel_group() \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsRemoveGroupResult) + + # add + envelope = pubnub.add_channel_to_channel_group() \ + .channels([ch1, ch2]) \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsAddChannelResult) + + time.sleep(1) + + # list + envelope = pubnub.list_channels_in_channel_group() \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsListResult) + assert len(envelope.result.channels) == 2 + assert ch1 in envelope.result.channels + assert ch2 in envelope.result.channels + + # remove + envelope = pubnub.remove_channel_from_channel_group() \ + .channels([ch1, ch2]) \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsRemoveChannelResult) + + time.sleep(1) + + # list + envelope = pubnub.list_channels_in_channel_group() \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsListResult) + assert len(envelope.result.channels) == 0 + + @use_cassette_and_stub_time_sleep_native( + 'tests/integrational/fixtures/native_sync/channel_groups/add_channel_remove_group.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_add_channel_remove_group(self): + ch = "channel-groups-unit-ch" + gr = "channel-groups-unit-cg" + pubnub = PubNub(pnconf_copy()) + + # cleanup + envelope = pubnub.remove_channel_group() \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsRemoveGroupResult) + + # add + envelope = pubnub.add_channel_to_channel_group() \ + .channels(ch) \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsAddChannelResult) + + time.sleep(1) + + # list + envelope = pubnub.list_channels_in_channel_group() \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsListResult) + assert len(envelope.result.channels) == 1 + assert envelope.result.channels[0] == ch + + # remove + envelope = pubnub.remove_channel_group() \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsRemoveGroupResult) + + time.sleep(1) + + # list + envelope = pubnub.list_channels_in_channel_group() \ + .channel_group(gr) \ + .sync() + + assert isinstance(envelope.result, PNChannelGroupsListResult) + assert len(envelope.result.channels) == 0 diff --git a/tests/integrational/native_sync/test_history.py b/tests/integrational/native_sync/test_history.py new file mode 100644 index 00000000..f96c218f --- /dev/null +++ b/tests/integrational/native_sync/test_history.py @@ -0,0 +1,102 @@ +import logging +import time +import unittest +import pubnub +import pytest + +from pubnub.exceptions import PubNubException +from pubnub.models.consumer.history import PNHistoryResult +from pubnub.models.consumer.pubsub import PNPublishResult +from pubnub.pubnub import PubNub +from tests.helper import pnconf_copy, pnconf_enc_copy, pnconf_pam_copy +from tests.integrational.vcr_helper import use_cassette_and_stub_time_sleep_native + +pubnub.set_stream_logger('pubnub', logging.DEBUG) + +COUNT = 5 + + +class TestPubNubHistory(unittest.TestCase): + @use_cassette_and_stub_time_sleep_native('tests/integrational/fixtures/native_sync/history/basic.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_basic(self): + ch = "history-native-sync-ch" + pubnub = PubNub(pnconf_copy()) + pubnub.config.uuid = "history-native-sync-uuid" + + for i in range(COUNT): + envelope = pubnub.publish().channel(ch).message("hey-%s" % i).sync() + assert isinstance(envelope.result, PNPublishResult) + assert envelope.result.timetoken > 0 + + time.sleep(5) + + envelope = pubnub.history().channel(ch).count(COUNT).sync() + + assert isinstance(envelope.result, PNHistoryResult) + assert envelope.result.start_timetoken > 0 + assert envelope.result.end_timetoken > 0 + assert len(envelope.result.messages) == 5 + + assert envelope.result.messages[0].entry == 'hey-0' + assert envelope.result.messages[1].entry == 'hey-1' + assert envelope.result.messages[2].entry == 'hey-2' + assert envelope.result.messages[3].entry == 'hey-3' + assert envelope.result.messages[4].entry == 'hey-4' + + @use_cassette_and_stub_time_sleep_native('tests/integrational/fixtures/native_sync/history/encoded.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_encrypted(self): + ch = "history-native-sync-ch" + pubnub = PubNub(pnconf_enc_copy()) + pubnub.config.uuid = "history-native-sync-uuid" + + for i in range(COUNT): + envelope = pubnub.publish().channel(ch).message("hey-%s" % i).sync() + assert isinstance(envelope.result, PNPublishResult) + assert envelope.result.timetoken > 0 + + time.sleep(5) + + envelope = pubnub.history().channel(ch).count(COUNT).sync() + + assert isinstance(envelope.result, PNHistoryResult) + assert envelope.result.start_timetoken > 0 + assert envelope.result.end_timetoken > 0 + assert len(envelope.result.messages) == 5 + + assert envelope.result.messages[0].entry == 'hey-0' + assert envelope.result.messages[1].entry == 'hey-1' + assert envelope.result.messages[2].entry == 'hey-2' + assert envelope.result.messages[3].entry == 'hey-3' + assert envelope.result.messages[4].entry == 'hey-4' + + @use_cassette_and_stub_time_sleep_native('tests/integrational/fixtures/native_sync/history/not_permitted.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_not_permitted(self): + ch = "history-native-sync-ch" + pubnub = PubNub(pnconf_pam_copy()) + pubnub.config.uuid = "history-native-sync-uuid" + + with pytest.raises(PubNubException): + pubnub.history().channel(ch).count(5).sync() + + def test_super_call_with_channel_only(self): + ch = "history-native-sync-ch" + pubnub = PubNub(pnconf_pam_copy()) + pubnub.config.uuid = "history-native-sync-uuid" + + envelope = pubnub.history().channel(ch).sync() + assert isinstance(envelope.result, PNHistoryResult) + + assert not envelope.status.is_error() + + def test_super_call_with_all_params(self): + ch = "history-native-sync-ch" + pubnub = PubNub(pnconf_pam_copy()) + pubnub.config.uuid = "history-native-sync-uuid" + + envelope = pubnub.history().channel(ch).count(2).include_timetoken(True).reverse(True).start(1).end(2).sync() + assert isinstance(envelope.result, PNHistoryResult) + + assert not envelope.status.is_error() diff --git a/tests/integrational/native_sync/test_publish.py b/tests/integrational/native_sync/test_publish.py new file mode 100644 index 00000000..8aa6dd48 --- /dev/null +++ b/tests/integrational/native_sync/test_publish.py @@ -0,0 +1,306 @@ +import logging +import unittest + +import pubnub +from pubnub.exceptions import PubNubException +from pubnub.models.consumer.pubsub import PNPublishResult +from pubnub.pnconfiguration import PNConfiguration +from pubnub.pubnub import PubNub +from tests.helper import pnconf, pnconf_enc +from tests.integrational.vcr_helper import pn_vcr + +pubnub.set_stream_logger('pubnub', logging.DEBUG) + + +class TestPubNubPublish(unittest.TestCase): + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_string_get.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_string_get(self): + try: + env = PubNub(pnconf).publish() \ + .channel("ch1") \ + .message("hi") \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_list_get.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_list_get(self): + try: + env = PubNub(pnconf).publish() \ + .channel("ch1") \ + .message(["hi", "hi2", "hi3"]) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/native_sync/publish/publish_object_get.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'object_in_path', 'query']) + def test_publish_object_get(self): + try: + env = PubNub(pnconf).publish() \ + .channel("ch1") \ + .message({"name": "Alex", "online": True}) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_bool_get.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_bool_get(self): + try: + env = PubNub(pnconf).publish() \ + .channel("ch1") \ + .message(True) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_int_get.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_int_get(self): + try: + env = PubNub(pnconf).publish() \ + .channel("ch1") \ + .message(5) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_encrypted_string_get.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_encrypted_string_get(self): + try: + env = PubNub(pnconf_enc).publish() \ + .channel("ch1") \ + .message("encrypted string") \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_encrypted_list_get.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_encrypted_list_get(self): + try: + env = PubNub(pnconf_enc).publish() \ + .channel("ch1") \ + .message(["encrypted", "list"]) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_string_post.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_string_post(self): + try: + env = PubNub(pnconf).publish() \ + .channel("ch1") \ + .message("hi") \ + .use_post(True) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_list_post.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_list_post(self): + try: + env = PubNub(pnconf).publish() \ + .channel("ch1") \ + .message(["hi", "hi2", "hi3"]) \ + .use_post(True) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_object_post.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'path', 'query', 'object_in_body']) + def test_publish_object_post(self): + try: + env = PubNub(pnconf).publish() \ + .channel("ch1") \ + .message({"name": "Alex", "online": True}) \ + .use_post(True) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_bool_post.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_bool_post(self): + try: + env = PubNub(pnconf).publish() \ + .channel("ch1") \ + .message(True) \ + .use_post(True) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_int_post.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_int_post(self): + try: + env = PubNub(pnconf).publish() \ + .channel("ch1") \ + .message(5) \ + .use_post(True) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_encrypted_string_post.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_encrypted_string_post(self): + try: + env = PubNub(pnconf_enc).publish() \ + .channel("ch1") \ + .message("encrypted string POST") \ + .use_post(True) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_encrypted_list_post.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_encrypted_list_post(self): + try: + env = PubNub(pnconf_enc).publish() \ + .channel("ch1") \ + .message(["encrypted", "list", "POST"]) \ + .use_post(True) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/invalid_key.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_invalid_key(self): + config = PNConfiguration() + config.publish_key = "fake" + config.subscribe_key = "demo" + config.enable_subscribe = False + + try: + PubNub(config).publish() \ + .channel("ch1") \ + .message("hey") \ + .sync() + + self.fail(Exception("Should throw exception", 'pnsdk')) + except PubNubException as e: + assert "Invalid Key" in str(e) + + def test_missing_message_error(self): + try: + PubNub(pnconf).publish() \ + .channel("ch1") \ + .message(None) \ + .sync() + + self.fail(Exception("Should throw exception")) + except PubNubException as e: + assert "Message missing" in str(e) + + def test_missing_channel_error(self): + try: + PubNub(pnconf).publish() \ + .channel("") \ + .message("hey") \ + .sync() + + self.fail(Exception("Should throw exception")) + except PubNubException as e: + assert "Channel missing" in str(e) + + def test_non_serializable_error(self): + def func(): + pass + + try: + PubNub(pnconf).publish() \ + .channel("ch1") \ + .message(func) \ + .sync() + + self.fail(Exception("Should throw exception")) + except PubNubException as e: + assert "not JSON serializable" in str(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_with_meta.yaml', + filter_query_parameters=['uuid', 'pnsdk'], match_on=['meta_object_in_query']) + def test_publish_with_meta(self): + meta = {'a': 2, 'b': 'qwer'} + + try: + env = PubNub(pnconf_enc).publish() \ + .channel("ch1") \ + .message("hey") \ + .meta(meta) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/publish/publish_do_not_store.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_do_not_store(self): + try: + env = PubNub(pnconf_enc).publish() \ + .channel("ch1") \ + .message("hey") \ + .should_store(False) \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) diff --git a/tests/integrational/native_sync/test_ssl.py b/tests/integrational/native_sync/test_ssl.py new file mode 100644 index 00000000..b000c463 --- /dev/null +++ b/tests/integrational/native_sync/test_ssl.py @@ -0,0 +1,29 @@ +import logging +import unittest + +import pubnub +from pubnub.exceptions import PubNubException +from pubnub.models.consumer.pubsub import PNPublishResult +from pubnub.pubnub import PubNub +from tests.helper import pnconf_copy +from tests.integrational.vcr_helper import pn_vcr + +pubnub.set_stream_logger('pubnub', logging.DEBUG) + + +class TestPubNubPublish(unittest.TestCase): + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/ssl/ssl.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_publish_string_get(self): + pnconf = pnconf_copy() + pnconf.ssl = True + try: + env = PubNub(pnconf).publish() \ + .channel("ch1") \ + .message("hi") \ + .sync() + + assert isinstance(env.result, PNPublishResult) + assert env.result.timetoken > 1 + except PubNubException as e: + self.fail(e) diff --git a/tests/integrational/native_sync/test_state.py b/tests/integrational/native_sync/test_state.py new file mode 100644 index 00000000..2e93f0e8 --- /dev/null +++ b/tests/integrational/native_sync/test_state.py @@ -0,0 +1,55 @@ +import logging +import unittest + +import pubnub +from pubnub.models.consumer.presence import PNSetStateResult, PNGetStateResult +from pubnub.pubnub import PubNub +from tests.helper import pnconf_copy +from tests.integrational.vcr_helper import pn_vcr + +pubnub.set_stream_logger('pubnub', logging.DEBUG) + + +class TestPubNubState(unittest.TestCase): + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/state/state_of_single_channel.yaml', + filter_query_parameters=['uuid', 'pnsdk'], match_on=['state_object_in_query']) + def test_single_channel(self): + ch = "state-native-sync-ch" + pubnub = PubNub(pnconf_copy()) + pubnub.config.uuid = "state-native-sync-uuid" + state = {"name": "Alex", "count": 5} + + envelope = pubnub.set_state().channels(ch).state(state).sync() + + assert isinstance(envelope.result, PNSetStateResult) + assert envelope.result.state['name'] == "Alex" + assert envelope.result.state['count'] == 5 + + envelope = pubnub.get_state().channels(ch).sync() + + assert isinstance(envelope.result, PNGetStateResult) + assert envelope.result.channels[ch]['name'] == "Alex" + assert envelope.result.channels[ch]['count'] == 5 + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/state/state_of_multiple_channels.yaml', + filter_query_parameters=['uuid', 'pnsdk'], match_on=['state_object_in_query']) + def test_multiple_channels(self): + ch1 = "state-native-sync-ch-1" + ch2 = "state-native-sync-ch-2" + pubnub = PubNub(pnconf_copy()) + pubnub.config.uuid = "state-native-sync-uuid" + state = {"name": "Alex", "count": 5} + + envelope = pubnub.set_state().channels([ch1, ch2]).state(state).sync() + + assert isinstance(envelope.result, PNSetStateResult) + assert envelope.result.state['name'] == "Alex" + assert envelope.result.state['count'] == 5 + + envelope = pubnub.get_state().channels([ch1, ch2]).sync() + + assert isinstance(envelope.result, PNGetStateResult) + assert envelope.result.channels[ch1]['name'] == "Alex" + assert envelope.result.channels[ch1]['count'] == 5 + assert envelope.result.channels[ch2]['name'] == "Alex" + assert envelope.result.channels[ch2]['count'] == 5 diff --git a/tests/integrational/native_threads/__init__.py b/tests/integrational/native_threads/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integrational/native_threads/test_channel_groups.py b/tests/integrational/native_threads/test_channel_groups.py new file mode 100644 index 00000000..60f3651a --- /dev/null +++ b/tests/integrational/native_threads/test_channel_groups.py @@ -0,0 +1,186 @@ +import logging +import threading +import time +import unittest + +import pubnub +from pubnub.models.consumer.channel_group import PNChannelGroupsAddChannelResult, PNChannelGroupsListResult, \ + PNChannelGroupsRemoveChannelResult, PNChannelGroupsRemoveGroupResult +from pubnub.pubnub import PubNub +from tests.helper import pnconf_copy +from tests.integrational.vcr_helper import use_cassette_and_stub_time_sleep_native + +pubnub.set_stream_logger('pubnub', logging.DEBUG) + + +class TestPubNubChannelGroups(unittest.TestCase): + def setUp(self): + self.event = threading.Event() + + def callback(self, response, status): + self.response = response + self.status = status + self.event.set() + + @use_cassette_and_stub_time_sleep_native( + 'tests/integrational/fixtures/native_threads/channel_groups/single_channel.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_single_channel(self): + ch = "channel-groups-unit-ch" + gr = "channel-groups-unit-cg" + pubnub = PubNub(pnconf_copy()) + + # add + pubnub.add_channel_to_channel_group() \ + .channels(ch) \ + .channel_group(gr) \ + .async(self.callback) + + self.event.wait() + assert not self.status.is_error() + assert isinstance(self.response, PNChannelGroupsAddChannelResult) + self.event.clear() + + time.sleep(1) + + # list + pubnub.list_channels_in_channel_group() \ + .channel_group(gr) \ + .async(self.callback) + + self.event.wait() + assert isinstance(self.response, PNChannelGroupsListResult) + assert len(self.response.channels) == 1 + assert self.response.channels[0] == ch + self.event.clear() + + # remove + pubnub.remove_channel_from_channel_group() \ + .channels(ch) \ + .channel_group(gr) \ + .async(self.callback) + + self.event.wait() + assert isinstance(self.response, PNChannelGroupsRemoveChannelResult) + self.event.clear() + + time.sleep(1) + + # list + pubnub.list_channels_in_channel_group() \ + .channel_group(gr) \ + .async(self.callback) + + self.event.wait() + assert isinstance(self.response, PNChannelGroupsListResult) + assert len(self.response.channels) == 0 + self.event.clear() + + @use_cassette_and_stub_time_sleep_native( + 'tests/integrational/fixtures/native_threads/channel_groups/add_remove_multiple_channels.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_add_remove_multiple_channels(self): + ch1 = "channel-groups-unit-ch1" + ch2 = "channel-groups-unit-ch2" + gr = "channel-groups-unit-cg" + pubnub = PubNub(pnconf_copy()) + + # add + pubnub.add_channel_to_channel_group() \ + .channels([ch1, ch2]) \ + .channel_group(gr) \ + .async(self.callback) + + self.event.wait() + assert not self.status.is_error() + assert isinstance(self.response, PNChannelGroupsAddChannelResult) + self.event.clear() + + time.sleep(1) + + # list + pubnub.list_channels_in_channel_group() \ + .channel_group(gr) \ + .async(self.callback) + + self.event.wait() + assert isinstance(self.response, PNChannelGroupsListResult) + assert len(self.response.channels) == 2 + assert ch1 in self.response.channels + assert ch2 in self.response.channels + self.event.clear() + + # remove + pubnub.remove_channel_from_channel_group() \ + .channels([ch1, ch2]) \ + .channel_group(gr) \ + .async(self.callback) + + self.event.wait() + assert isinstance(self.response, PNChannelGroupsRemoveChannelResult) + self.event.clear() + + time.sleep(1) + + # list + pubnub.list_channels_in_channel_group() \ + .channel_group(gr) \ + .async(self.callback) + + self.event.wait() + assert isinstance(self.response, PNChannelGroupsListResult) + assert len(self.response.channels) == 0 + self.event.clear() + + @use_cassette_and_stub_time_sleep_native( + 'tests/integrational/fixtures/native_threads/channel_groups/add_channel_remove_group.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + def test_add_channel_remove_group(self): + ch = "channel-groups-unit-ch" + gr = "channel-groups-unit-cg" + pubnub = PubNub(pnconf_copy()) + + # add + pubnub.add_channel_to_channel_group() \ + .channels(ch) \ + .channel_group(gr) \ + .async(self.callback) + + self.event.wait() + assert not self.status.is_error() + assert isinstance(self.response, PNChannelGroupsAddChannelResult) + self.event.clear() + + time.sleep(1) + + # list + pubnub.list_channels_in_channel_group() \ + .channel_group(gr) \ + .async(self.callback) + + self.event.wait() + assert isinstance(self.response, PNChannelGroupsListResult) + assert len(self.response.channels) == 1 + assert self.response.channels[0] == ch + self.event.clear() + + # remove + pubnub.remove_channel_group() \ + .channel_group(gr) \ + .async(self.callback) + + self.event.wait() + assert isinstance(self.response, PNChannelGroupsRemoveGroupResult) + self.event.clear() + + time.sleep(1) + + # list + pubnub.list_channels_in_channel_group() \ + .channel_group(gr) \ + .async(self.callback) + + self.event.wait() + assert isinstance(self.response, PNChannelGroupsListResult) + assert len(self.response.channels) == 0 + self.event.clear() diff --git a/tests/integrational/native_threads/test_heartbeat.py b/tests/integrational/native_threads/test_heartbeat.py new file mode 100644 index 00000000..f8bc977a --- /dev/null +++ b/tests/integrational/native_threads/test_heartbeat.py @@ -0,0 +1,76 @@ +import logging +import unittest +import time +import pubnub as pn + +from pubnub.pubnub import PubNub, SubscribeListener +from tests import helper +from tests.helper import pnconf_sub_copy + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +# TODO: add a success heartbeat test + +messenger_config = pnconf_sub_copy() +messenger_config.set_presence_timeout(8) +messenger_config.uuid = helper.gen_channel("messenger") + +listener_config = pnconf_sub_copy() +listener_config.uuid = helper.gen_channel("listener") + + +class TestPubNubHeartbeat(unittest.TestCase): + def test_timeout_event_on_broken_heartbeat(self): + ch = helper.gen_channel("heartbeat-test") + + pubnub = PubNub(messenger_config) + pubnub_listener = PubNub(listener_config) + + pubnub.config.uuid = helper.gen_channel("messenger") + pubnub_listener.config.uuid = helper.gen_channel("listener") + + callback_presence = SubscribeListener() + callback_messages = SubscribeListener() + + # - connect to :ch-pnpres + pubnub_listener.add_listener(callback_presence) + pubnub_listener.subscribe().channels(ch).with_presence().execute() + callback_presence.wait_for_connect() + + presence_message = callback_presence.wait_for_presence_on(ch) + assert ch == presence_message.channel + assert 'join' == presence_message.event + assert pubnub_listener.uuid == presence_message.uuid + + # - connect to :ch + pubnub.add_listener(callback_messages) + pubnub.subscribe().channels(ch).execute() + callback_messages.wait_for_connect() + + prs_envelope = callback_presence.wait_for_presence_on(ch) + assert ch == prs_envelope.channel + assert 'join' == prs_envelope.event + assert pubnub.uuid == prs_envelope.uuid + + # wait for one heartbeat call + time.sleep(6) + + # - break messenger heartbeat loop + pubnub._subscription_manager._stop_heartbeat_timer() + + # - assert for timeout + presence_message = callback_presence.wait_for_presence_on(ch) + assert ch == presence_message.channel + assert 'timeout' == presence_message.event + assert pubnub.uuid == presence_message.uuid + + pubnub.unsubscribe().channels(ch).execute() + callback_messages.wait_for_disconnect() + + # - disconnect from :ch-pnpres + pubnub_listener.unsubscribe().channels(ch).execute() + callback_presence.wait_for_disconnect() + + pubnub.stop() + pubnub_listener.stop() diff --git a/tests/integrational/native_threads/test_here_now.py b/tests/integrational/native_threads/test_here_now.py new file mode 100644 index 00000000..cbbee208 --- /dev/null +++ b/tests/integrational/native_threads/test_here_now.py @@ -0,0 +1,92 @@ +import unittest +import logging +import time +import pubnub +import threading + +from pubnub.pubnub import PubNub, SubscribeListener, NonSubscribeListener +from tests import helper +from tests.helper import pnconf_sub_copy + +pubnub.set_stream_logger('pubnub', logging.DEBUG) + + +class TestPubNubState(unittest.TestCase): + def setUp(self): + self.event = threading.Event() + + def callback(self, response, status): + self.response = response + self.status = status + self.event.set() + + def test_single_channel(self): + pubnub = PubNub(pnconf_sub_copy()) + ch = helper.gen_channel("herenow-asyncio-channel") + uuid = helper.gen_channel("herenow-asyncio-uuid") + pubnub.config.uuid = uuid + + subscribe_listener = SubscribeListener() + here_now_listener = NonSubscribeListener() + pubnub.add_listener(subscribe_listener) + pubnub.subscribe().channels(ch).execute() + + subscribe_listener.wait_for_connect() + + time.sleep(2) + + pubnub.here_now() \ + .channels(ch) \ + .include_uuids(True) \ + .async(here_now_listener.callback) + + if here_now_listener.await() is False: + self.fail("HereNow operation timeout") + + result = here_now_listener.result + channels = result.channels + + assert len(channels) == 1 + assert channels[0].occupancy == 1 + assert channels[0].occupants[0].uuid == pubnub.uuid + + pubnub.unsubscribe().channels(ch).execute() + subscribe_listener.wait_for_disconnect() + + pubnub.stop() + + def test_multiple_channels(self): + pubnub = PubNub(pnconf_sub_copy()) + ch1 = helper.gen_channel("here-now-native-sync-ch1") + ch2 = helper.gen_channel("here-now-native-sync-ch2") + pubnub.config.uuid = "here-now-native-sync-uuid" + + subscribe_listener = SubscribeListener() + here_now_listener = NonSubscribeListener() + pubnub.add_listener(subscribe_listener) + pubnub.subscribe().channels([ch1, ch2]).execute() + + subscribe_listener.wait_for_connect() + + time.sleep(5) + + pubnub.here_now() \ + .channels([ch1, ch2]) \ + .async(here_now_listener.callback) + + if here_now_listener.await() is False: + self.fail("HereNow operation timeout") + + result = here_now_listener.result + channels = result.channels + + assert len(channels) == 2 + assert channels[0].occupancy == 1 + assert channels[0].occupants[0].uuid == pubnub.uuid + assert channels[1].occupancy == 1 + assert channels[1].occupants[0].uuid == pubnub.uuid + + pubnub.unsubscribe().channels([ch1, ch2]).execute() + subscribe_listener.wait_for_disconnect() + + pubnub.stop() diff --git a/tests/integrational/native_threads/test_publish.py b/tests/integrational/native_threads/test_publish.py new file mode 100644 index 00000000..7cab5162 --- /dev/null +++ b/tests/integrational/native_threads/test_publish.py @@ -0,0 +1,204 @@ +import logging +import threading +import unittest +import pubnub +from pubnub.enums import PNStatusCategory + +from pubnub.models.consumer.pubsub import PNPublishResult +from pubnub.pnconfiguration import PNConfiguration +from pubnub.pubnub import PubNub +from tests.helper import pnconf, pnconf_enc, pnconf_pam_copy + +pubnub.set_stream_logger('pubnub', logging.DEBUG) + + +class TestPubNubSuccessPublish(unittest.TestCase): + def setUp(self): + self.event = threading.Event() + + def callback(self, response, status): + self.response = response + self.status = status + self.event.set() + + def assert_success(self): + self.event.wait() + if self.status.is_error(): + self.fail(str(self.status.error_data.exception)) + assert isinstance(self.response, PNPublishResult) + assert self.response.timetoken > 1 + self.event.clear() + self.response = None + self.status = None + + def assert_success_publish_get(self, msg): + PubNub(pnconf).publish() \ + .channel("ch1") \ + .message(msg) \ + .async(self.callback) + + self.assert_success() + + def assert_success_publish_post(self, msg): + PubNub(pnconf).publish() \ + .channel("ch1") \ + .message(msg) \ + .use_post(True) \ + .async(self.callback) + + self.assert_success() + + def test_publish_get(self): + self.assert_success_publish_get("hi") + self.assert_success_publish_get(5) + self.assert_success_publish_get(True) + self.assert_success_publish_get(["hi", "hi2", "hi3"]) + self.assert_success_publish_get({"name": "Alex", "online": True}) + + def test_publish_post(self): + self.assert_success_publish_post("hi") + self.assert_success_publish_post(5) + self.assert_success_publish_post(True) + self.assert_success_publish_post(["hi", "hi2", "hi3"]) + self.assert_success_publish_post({"name": "Alex", "online": True}) + + def test_publish_encrypted_list_get(self): + pubnub = PubNub(pnconf_enc) + + pubnub.publish() \ + .channel("ch1") \ + .message(["encrypted", "list"]) \ + .async(self.callback) + + self.assert_success() + + def test_publish_encrypted_string_get(self): + PubNub(pnconf_enc).publish() \ + .channel("ch1") \ + .message("encrypted string") \ + .async(self.callback) + + self.assert_success() + + def test_publish_encrypted_list_post(self): + PubNub(pnconf_enc).publish() \ + .channel("ch1") \ + .message(["encrypted", "list"]) \ + .use_post(True) \ + .async(self.callback) + + self.assert_success() + + def test_publish_encrypted_string_post(self): + PubNub(pnconf_enc).publish() \ + .channel("ch1") \ + .message("encrypted string") \ + .use_post(True) \ + .async(self.callback) + + self.assert_success() + + def test_publish_with_meta(self): + meta = {'a': 2, 'b': 'qwer'} + + PubNub(pnconf_enc).publish() \ + .channel("ch1") \ + .message("hey") \ + .meta(meta) \ + .async(self.callback) + + self.assert_success() + + def test_publish_do_not_store(self): + PubNub(pnconf_enc).publish() \ + .channel("ch1") \ + .message("hey") \ + .should_store(False) \ + .async(self.callback) + + self.assert_success() + + +class TestPubNubErrorPublish(unittest.TestCase): + def setUp(self): + self.event = threading.Event() + + def callback(self, response, status): + self.response = response + self.status = status + self.event.set() + + def test_invalid_key(self): + self.invalid_key_message = "" + config = PNConfiguration() + config.publish_key = "fake" + config.subscribe_key = "demo" + config.enable_subscribe = False + + PubNub(config).publish() \ + .channel("ch1") \ + .message("hey") \ + .async(self.callback) + + self.event.wait() + + assert self.status.is_error() + assert self.status.category is PNStatusCategory.PNBadRequestCategory + assert self.status.original_response[0] is 0 + assert self.status.original_response[1] == 'Invalid Key' + assert "HTTP Client Error (400):" in str(self.status.error_data.exception) + assert "Invalid Key" in str(self.status.error_data.exception) + + def test_missing_message(self): + PubNub(pnconf).publish() \ + .channel("ch1") \ + .message(None) \ + .async(self.callback) + + self.event.wait() + + assert self.status.is_error() + assert self.response is None + assert "Message missing" in str(self.status.error_data.exception) + + def test_missing_chanel(self): + PubNub(pnconf).publish() \ + .channel("") \ + .message("hey") \ + .async(self.callback) + + assert self.status.is_error() + assert self.response is None + assert "Channel missing" in str(self.status.error_data.exception) + + def test_non_serializable(self): + def method(): + pass + + PubNub(pnconf).publish() \ + .channel("ch1") \ + .message(method) \ + .async(self.callback) + + self.event.wait() + + assert self.status.is_error() + assert self.response is None + assert "not JSON serializable" in str(self.status.error_data.exception) + + def test_not_permitted(self): + pnconf = pnconf_pam_copy() + pnconf.secret_key = None + + PubNub(pnconf).publish() \ + .channel("not_permitted_channel") \ + .message("correct message") \ + .async(self.callback) + + self.event.wait() + + assert self.status.is_error() + assert self.response is None + assert "HTTP Client Error (403)" in str(self.status.error_data.exception) + assert "Forbidden" in str(self.status.error_data.exception) + assert "Access Manager" in str(self.status.error_data.exception) diff --git a/tests/integrational/native_threads/test_state.py b/tests/integrational/native_threads/test_state.py new file mode 100644 index 00000000..793fc223 --- /dev/null +++ b/tests/integrational/native_threads/test_state.py @@ -0,0 +1,84 @@ +import logging +import threading +import unittest + +import pubnub +from pubnub.models.consumer.presence import PNSetStateResult, PNGetStateResult +from pubnub.pubnub import PubNub +from tests.helper import pnconf_copy +from tests.integrational.vcr_helper import pn_vcr + +pubnub.set_stream_logger('pubnub', logging.DEBUG) + + +class TestPubNubState(unittest.TestCase): + def setUp(self): + self.event = threading.Event() + + def callback(self, response, status): + self.response = response + self.status = status + self.event.set() + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_threads/state/state_of_single_channel.yaml', + filter_query_parameters=['uuid', 'pnsdk'], match_on=['state_object_in_query']) + def test_single_channel(self): + ch = "state-native-sync-ch" + pubnub = PubNub(pnconf_copy()) + pubnub.config.uuid = "state-native-sync-uuid" + state = {"name": "Alex", "count": 5} + + pubnub.set_state() \ + .channels(ch) \ + .state(state) \ + .async(self.callback) + + self.event.wait() + assert not self.status.is_error() + assert isinstance(self.response, PNSetStateResult) + assert self.response.state['name'] == "Alex" + assert self.response.state['count'] == 5 + + self.event.clear() + pubnub.get_state() \ + .channels(ch) \ + .async(self.callback) + + self.event.wait() + assert not self.status.is_error() + assert isinstance(self.response, PNGetStateResult) + assert self.response.channels[ch]['name'] == "Alex" + assert self.response.channels[ch]['count'] == 5 + + @pn_vcr.use_cassette('tests/integrational/fixtures/native_threads/state/state_of_multiple_channels.yaml', + filter_query_parameters=['uuid', 'pnsdk'], match_on=['state_object_in_query']) + def test_multiple_channels(self): + ch1 = "state-native-sync-ch-1" + ch2 = "state-native-sync-ch-2" + pubnub = PubNub(pnconf_copy()) + pubnub.config.uuid = "state-native-sync-uuid" + state = {"name": "Alex", "count": 5} + + pubnub.set_state() \ + .channels([ch1, ch2]) \ + .state(state) \ + .async(self.callback) + + self.event.wait() + assert not self.status.is_error() + assert isinstance(self.response, PNSetStateResult) + assert self.response.state['name'] == "Alex" + assert self.response.state['count'] == 5 + + self.event.clear() + pubnub.get_state() \ + .channels([ch1, ch2]) \ + .async(self.callback) + + self.event.wait() + assert not self.status.is_error() + assert isinstance(self.response, PNGetStateResult) + assert self.response.channels[ch1]['name'] == "Alex" + assert self.response.channels[ch1]['count'] == 5 + assert self.response.channels[ch2]['name'] == "Alex" + assert self.response.channels[ch2]['count'] == 5 diff --git a/tests/integrational/native_threads/test_subscribe.py b/tests/integrational/native_threads/test_subscribe.py new file mode 100644 index 00000000..8d9c6692 --- /dev/null +++ b/tests/integrational/native_threads/test_subscribe.py @@ -0,0 +1,262 @@ +import logging +import unittest +import time +import pubnub as pn + +from pubnub.exceptions import PubNubException +from pubnub.models.consumer.channel_group import PNChannelGroupsAddChannelResult, PNChannelGroupsRemoveChannelResult +from pubnub.models.consumer.pubsub import PNPublishResult, PNMessageResult +from pubnub.pubnub import PubNub, SubscribeListener, NonSubscribeListener +from tests import helper +from tests.helper import pnconf_sub_copy + + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +class TestPubNubSubscription(unittest.TestCase): + def test_subscribe_unsubscribe(self): + pubnub = PubNub(pnconf_sub_copy()) + ch = helper.gen_channel("test-subscribe-sub-unsub") + + try: + listener = SubscribeListener() + pubnub.add_listener(listener) + + pubnub.subscribe().channels(ch).execute() + assert ch in pubnub.get_subscribed_channels() + assert len(pubnub.get_subscribed_channels()) == 1 + + listener.wait_for_connect() + assert ch in pubnub.get_subscribed_channels() + assert len(pubnub.get_subscribed_channels()) == 1 + + pubnub.unsubscribe().channels(ch).execute() + assert ch not in pubnub.get_subscribed_channels() + assert len(pubnub.get_subscribed_channels()) == 0 + + listener.wait_for_disconnect() + assert ch not in pubnub.get_subscribed_channels() + assert len(pubnub.get_subscribed_channels()) == 0 + + except PubNubException as e: + self.fail(e) + finally: + pubnub.stop() + + def test_subscribe_pub_unsubscribe(self): + ch = helper.gen_channel("test-subscribe-sub-pub-unsub") + pubnub = PubNub(pnconf_sub_copy()) + subscribe_listener = SubscribeListener() + publish_operation = NonSubscribeListener() + message = "hey" + + try: + pubnub.add_listener(subscribe_listener) + + pubnub.subscribe().channels(ch).execute() + subscribe_listener.wait_for_connect() + + pubnub.publish().channel(ch).message(message).async(publish_operation.callback) + + if publish_operation.await() is False: + self.fail("Publish operation timeout") + + publish_result = publish_operation.result + assert isinstance(publish_result, PNPublishResult) + assert publish_result.timetoken > 0 + + result = subscribe_listener.wait_for_message_on(ch) + assert isinstance(result, PNMessageResult) + assert result.channel == ch + assert result.subscription is None + assert result.timetoken > 0 + assert result.message == message + + pubnub.unsubscribe().channels(ch).execute() + subscribe_listener.wait_for_disconnect() + except PubNubException as e: + self.fail(e) + finally: + pubnub.stop() + + def test_join_leave(self): + ch = helper.gen_channel("test-subscribe-join-leave") + + pubnub = PubNub(pnconf_sub_copy()) + pubnub_listener = PubNub(pnconf_sub_copy()) + callback_messages = SubscribeListener() + callback_presence = SubscribeListener() + + pubnub.config.uuid = helper.gen_channel("messenger") + pubnub_listener.config.uuid = helper.gen_channel("listener") + + try: + pubnub.add_listener(callback_messages) + pubnub_listener.add_listener(callback_presence) + + pubnub_listener.subscribe().channels(ch).with_presence().execute() + callback_presence.wait_for_connect() + + envelope = callback_presence.wait_for_presence_on(ch) + assert envelope.channel == ch + assert envelope.event == 'join' + assert envelope.uuid == pubnub_listener.uuid + + pubnub.subscribe().channels(ch).execute() + callback_messages.wait_for_connect() + + envelope = callback_presence.wait_for_presence_on(ch) + assert envelope.channel == ch + assert envelope.event == 'join' + assert envelope.uuid == pubnub.uuid + + pubnub.unsubscribe().channels(ch).execute() + callback_messages.wait_for_disconnect() + + envelope = callback_presence.wait_for_presence_on(ch) + assert envelope.channel == ch + assert envelope.event == 'leave' + assert envelope.uuid == pubnub.uuid + + pubnub_listener.unsubscribe().channels(ch).execute() + callback_presence.wait_for_disconnect() + except PubNubException as e: + self.fail(e) + finally: + pubnub.stop() + pubnub_listener.stop() + + def test_cg_subscribe_unsubscribe(self): + ch = helper.gen_channel("test-subscribe-unsubscribe-channel") + gr = helper.gen_channel("test-subscribe-unsubscribe-group") + + pubnub = PubNub(pnconf_sub_copy()) + callback_messages = SubscribeListener() + cg_operation = NonSubscribeListener() + + pubnub.add_channel_to_channel_group()\ + .channel_group(gr)\ + .channels(ch)\ + .async(cg_operation.callback) + result = cg_operation.await_result() + assert isinstance(result, PNChannelGroupsAddChannelResult) + cg_operation.reset() + + time.sleep(1) + + pubnub.add_listener(callback_messages) + pubnub.subscribe().channel_groups(gr).execute() + callback_messages.wait_for_connect() + + pubnub.unsubscribe().channel_groups(gr).execute() + callback_messages.wait_for_disconnect() + + pubnub.remove_channel_from_channel_group()\ + .channel_group(gr)\ + .channels(ch)\ + .async(cg_operation.callback) + result = cg_operation.await_result() + assert isinstance(result, PNChannelGroupsRemoveChannelResult) + + pubnub.stop() + + def test_subscribe_cg_publish_unsubscribe(self): + ch = helper.gen_channel("test-subscribe-unsubscribe-channel") + gr = helper.gen_channel("test-subscribe-unsubscribe-group") + message = "hey" + + pubnub = PubNub(pnconf_sub_copy()) + callback_messages = SubscribeListener() + non_subscribe_listener = NonSubscribeListener() + + pubnub.add_channel_to_channel_group() \ + .channel_group(gr) \ + .channels(ch) \ + .async(non_subscribe_listener.callback) + result = non_subscribe_listener.await_result_and_reset() + assert isinstance(result, PNChannelGroupsAddChannelResult) + + time.sleep(1) + + pubnub.add_listener(callback_messages) + pubnub.subscribe().channel_groups(gr).execute() + callback_messages.wait_for_connect() + + pubnub.publish().message(message).channel(ch).async(non_subscribe_listener.callback) + result = non_subscribe_listener.await_result_and_reset() + assert isinstance(result, PNPublishResult) + assert result.timetoken > 0 + + pubnub.unsubscribe().channel_groups(gr).execute() + callback_messages.wait_for_disconnect() + + pubnub.remove_channel_from_channel_group() \ + .channel_group(gr) \ + .channels(ch) \ + .async(non_subscribe_listener.callback) + result = non_subscribe_listener.await_result_and_reset() + assert isinstance(result, PNChannelGroupsRemoveChannelResult) + + pubnub.stop() + + def test_subscribe_cg_join_leave(self): + ch = helper.gen_channel("test-subscribe-unsubscribe-channel") + gr = helper.gen_channel("test-subscribe-unsubscribe-group") + + pubnub = PubNub(pnconf_sub_copy()) + pubnub_listener = PubNub(pnconf_sub_copy()) + non_subscribe_listener = NonSubscribeListener() + + pubnub.add_channel_to_channel_group() \ + .channel_group(gr) \ + .channels(ch) \ + .async(non_subscribe_listener.callback) + result = non_subscribe_listener.await_result_and_reset() + assert isinstance(result, PNChannelGroupsAddChannelResult) + + time.sleep(1) + + callback_messages = SubscribeListener() + callback_presence = SubscribeListener() + + pubnub_listener.add_listener(callback_presence) + pubnub_listener.subscribe().channel_groups(gr).with_presence().execute() + callback_presence.wait_for_connect() + + prs_envelope = callback_presence.wait_for_presence_on(ch) + assert prs_envelope.event == 'join' + assert prs_envelope.uuid == pubnub_listener.uuid + assert prs_envelope.channel == ch + assert prs_envelope.subscription == gr + + pubnub.add_listener(callback_messages) + pubnub.subscribe().channel_groups(gr).execute() + + prs_envelope = callback_presence.wait_for_presence_on(ch) + + assert prs_envelope.event == 'join' + assert prs_envelope.uuid == pubnub.uuid + assert prs_envelope.channel == ch + assert prs_envelope.subscription == gr + + pubnub.unsubscribe().channel_groups(gr).execute() + prs_envelope = callback_presence.wait_for_presence_on(ch) + + assert prs_envelope.event == 'leave' + assert prs_envelope.uuid == pubnub.uuid + assert prs_envelope.channel == ch + assert prs_envelope.subscription == gr + + pubnub_listener.unsubscribe().channel_groups(gr).execute() + callback_presence.wait_for_disconnect() + + pubnub.remove_channel_from_channel_group() \ + .channel_group(gr) \ + .channels(ch) \ + .async(non_subscribe_listener.callback) + result = non_subscribe_listener.await_result_and_reset() + assert isinstance(result, PNChannelGroupsRemoveChannelResult) + + pubnub.stop() + pubnub_listener.stop() diff --git a/tests/integrational/native_threads/test_where_now.py b/tests/integrational/native_threads/test_where_now.py new file mode 100644 index 00000000..f8945592 --- /dev/null +++ b/tests/integrational/native_threads/test_where_now.py @@ -0,0 +1,90 @@ +import unittest +import logging +import time +import pubnub +import threading + +from pubnub.pubnub import PubNub, SubscribeListener, NonSubscribeListener +from tests import helper +from tests.helper import pnconf_sub_copy + +pubnub.set_stream_logger('pubnub', logging.DEBUG) + + +class TestPubNubState(unittest.TestCase): + def setUp(self): + self.event = threading.Event() + + def callback(self, response, status): + self.response = response + self.status = status + self.event.set() + + def test_single_channel(self): + pubnub = PubNub(pnconf_sub_copy()) + ch = helper.gen_channel("wherenow-asyncio-channel") + uuid = helper.gen_channel("wherenow-asyncio-uuid") + pubnub.config.uuid = uuid + + subscribe_listener = SubscribeListener() + where_now_listener = NonSubscribeListener() + pubnub.add_listener(subscribe_listener) + pubnub.subscribe().channels(ch).execute() + + subscribe_listener.wait_for_connect() + + time.sleep(2) + + pubnub.where_now() \ + .uuid(uuid) \ + .async(where_now_listener.callback) + + if where_now_listener.await() is False: + self.fail("WhereNow operation timeout") + + result = where_now_listener.result + channels = result.channels + + assert len(channels) == 1 + assert channels[0] == ch + + pubnub.unsubscribe().channels(ch).execute() + subscribe_listener.wait_for_disconnect() + + pubnub.stop() + + def test_multiple_channels(self): + + pubnub = PubNub(pnconf_sub_copy()) + ch1 = "state-native-sync-ch-1" + ch2 = "state-native-sync-ch-2" + pubnub.config.uuid = "state-native-sync-uuid" + uuid = pubnub.config.uuid + + subscribe_listener = SubscribeListener() + where_now_listener = NonSubscribeListener() + pubnub.add_listener(subscribe_listener) + pubnub.subscribe().channels([ch1, ch2]).execute() + + subscribe_listener.wait_for_connect() + + time.sleep(2) + + pubnub.where_now() \ + .uuid(uuid) \ + .async(where_now_listener.callback) + + if where_now_listener.await() is False: + self.fail("WhereNow operation timeout") + + result = where_now_listener.result + channels = result.channels + + assert len(channels) == 2 + assert ch1 in channels + assert ch2 in channels + + pubnub.unsubscribe().channels([ch1, ch2]).execute() + subscribe_listener.wait_for_disconnect() + + pubnub.stop() diff --git a/tests/integrational/python_v35/__init__.py b/tests/integrational/python_v35/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integrational/python_v35/test_asyncio_async_await_syntax.py b/tests/integrational/python_v35/test_asyncio_async_await_syntax.py new file mode 100644 index 00000000..7c5481d1 --- /dev/null +++ b/tests/integrational/python_v35/test_asyncio_async_await_syntax.py @@ -0,0 +1,50 @@ +import asyncio +import logging +import pytest # noqa: F401 +import pubnub as pn + +from pubnub.models.consumer.pubsub import PNMessageResult +from pubnub.pubnub_asyncio import PubNubAsyncio, AsyncioEnvelope, SubscribeListener +from tests import helper +from tests.helper import pnconf_sub_copy + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +@pytest.mark.asyncio +async def test_subscribe_publish_unsubscribe(event_loop): + pubnub = PubNubAsyncio(pnconf_sub_copy(), custom_event_loop=event_loop) + + callback = SubscribeListener() + channel = helper.gen_channel("test-sub-pub-unsub") + message = "hey" + pubnub.add_listener(callback) + pubnub.subscribe().channels(channel).execute() + + await callback.wait_for_connect() + + publish_future = asyncio.ensure_future(pubnub.publish().channel(channel).message(message).future()) + subscribe_message_future = asyncio.ensure_future(callback.wait_for_message_on(channel)) + + await asyncio.wait([ + publish_future, + subscribe_message_future + ]) + + publish_envelope = publish_future.result() + subscribe_envelope = subscribe_message_future.result() + + assert isinstance(subscribe_envelope, PNMessageResult) + assert subscribe_envelope.channel == channel + assert subscribe_envelope.subscription is None + assert subscribe_envelope.message == message + assert subscribe_envelope.timetoken > 0 + + assert isinstance(publish_envelope, AsyncioEnvelope) + assert publish_envelope.result.timetoken > 0 + assert publish_envelope.status.original_response[0] == 1 + + pubnub.unsubscribe().channels(channel).execute() + await callback.wait_for_disconnect() + + pubnub.stop() diff --git a/tests/integrational/python_v35/test_tornado_async_await_syntax.py b/tests/integrational/python_v35/test_tornado_async_await_syntax.py new file mode 100644 index 00000000..cf742d16 --- /dev/null +++ b/tests/integrational/python_v35/test_tornado_async_await_syntax.py @@ -0,0 +1,48 @@ +import logging +import tornado +import pubnub as pn + +from tornado.testing import AsyncTestCase +from pubnub.pubnub_tornado import PubNubTornado, SubscribeListener +from tests import helper +from tests.helper import pnconf_sub_copy + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +class SubscriptionTest(object): + def __init__(self): + super(SubscriptionTest, self).__init__() + self.pubnub = None + self.pubnub_listener = None + + +class TestChannelSubscription(AsyncTestCase, SubscriptionTest): + def setUp(self): + super(TestChannelSubscription, self).setUp() + self.pubnub = PubNubTornado(pnconf_sub_copy(), custom_ioloop=self.io_loop) + self.pubnub_listener = PubNubTornado(pnconf_sub_copy(), custom_ioloop=self.io_loop) + + @tornado.testing.gen_test + async def test_subscribe_publish_unsubscribe(self): + ch = helper.gen_channel("subscribe-test") + message = "hey" + + callback_messages = SubscribeListener() + self.pubnub.add_listener(callback_messages) + self.pubnub.subscribe().channels(ch).execute() + await callback_messages.wait_for_connect() + + sub_env, pub_env = await tornado.gen.multi([ + callback_messages.wait_for_message_on(ch), + self.pubnub.publish().channel(ch).message(message).future()]) + + assert pub_env.status.original_response[0] == 1 + assert pub_env.status.original_response[1] == 'Sent' + + assert sub_env.channel == ch + assert sub_env.subscription is None + assert sub_env.message == message + + self.pubnub.unsubscribe().channels(ch).execute() + await callback_messages.wait_for_disconnect() diff --git a/tests/integrational/tornado/__init__.py b/tests/integrational/tornado/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integrational/tornado/test_channel_groups.py b/tests/integrational/tornado/test_channel_groups.py new file mode 100644 index 00000000..31244544 --- /dev/null +++ b/tests/integrational/tornado/test_channel_groups.py @@ -0,0 +1,130 @@ +import tornado +from tornado import gen +from tornado.testing import AsyncTestCase + +from pubnub.models.consumer.channel_group import PNChannelGroupsAddChannelResult, PNChannelGroupsListResult, \ + PNChannelGroupsRemoveChannelResult, PNChannelGroupsRemoveGroupResult +from pubnub.pubnub_tornado import PubNubTornado +from tests.helper import pnconf +from tests.integrational.tornado.vcr_tornado_decorator import use_cassette_and_stub_time_sleep + + +class TestChannelGroups(AsyncTestCase): + def setUp(self): + super(TestChannelGroups, self).setUp() + self.pubnub = PubNubTornado(pnconf, custom_ioloop=self.io_loop) + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/groups/add_remove_single_channel.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + @tornado.testing.gen_test + def test_add_remove_single_channel(self): + ch = "channel-groups-tornado-ch" + gr = "channel-groups-tornado-cg" + + # add + env = yield self.pubnub.add_channel_to_channel_group() \ + .channels(ch).channel_group(gr).future() + + assert isinstance(env.result, PNChannelGroupsAddChannelResult) + + yield gen.sleep(1) + + # list + env = yield self.pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + assert len(env.result.channels) == 1 + assert env.result.channels[0] == ch + + # remove + env = yield self.pubnub.remove_channel_from_channel_group() \ + .channels(ch).channel_group(gr).future() + + assert isinstance(env.result, PNChannelGroupsRemoveChannelResult) + + yield gen.sleep(1) + + # list + env = yield self.pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + assert len(env.result.channels) == 0 + + self.pubnub.stop() + self.stop() + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/groups/add_remove_multiple_channel.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + @tornado.testing.gen_test + def test_add_remove_multiple_channels(self): + ch1 = "channel-groups-tornado-ch1" + ch2 = "channel-groups-tornado-ch2" + gr = "channel-groups-tornado-cg" + + # add + env = yield self.pubnub.add_channel_to_channel_group() \ + .channels([ch1, ch2]).channel_group(gr).future() + + assert isinstance(env.result, PNChannelGroupsAddChannelResult) + + yield gen.sleep(1) + + # list + env = yield self.pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + assert len(env.result.channels) == 2 + assert ch1 in env.result.channels + assert ch2 in env.result.channels + + # remove + env = yield self.pubnub.remove_channel_from_channel_group() \ + .channels([ch1, ch2]).channel_group(gr).future() + + assert isinstance(env.result, PNChannelGroupsRemoveChannelResult) + + yield gen.sleep(1) + + # list + env = yield self.pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + assert len(env.result.channels) == 0 + + self.pubnub.stop() + self.stop() + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/groups/add_channel_remove_group.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + @tornado.testing.gen_test + def test_add_channel_remove_group(self): + ch = "channel-groups-tornado-ch" + gr = "channel-groups-tornado-cg" + + # add + env = yield self.pubnub.add_channel_to_channel_group() \ + .channels(ch).channel_group(gr).future() + + assert isinstance(env.result, PNChannelGroupsAddChannelResult) + + yield gen.sleep(1) + + # list + env = yield self.pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + assert len(env.result.channels) == 1 + assert env.result.channels[0] == ch + + # remove group + env = yield self.pubnub.remove_channel_group().channel_group(gr).future() + + assert isinstance(env.result, PNChannelGroupsRemoveGroupResult) + + yield gen.sleep(1) + + # list + env = yield self.pubnub.list_channels_in_channel_group().channel_group(gr).future() + assert isinstance(env.result, PNChannelGroupsListResult) + assert len(env.result.channels) == 0 + + self.pubnub.stop() + self.stop() diff --git a/tests/integrational/tornado/test_heartbeat.py b/tests/integrational/tornado/test_heartbeat.py new file mode 100644 index 00000000..630db880 --- /dev/null +++ b/tests/integrational/tornado/test_heartbeat.py @@ -0,0 +1,86 @@ +import logging +import tornado.testing +import pubnub as pn + +from tornado.testing import AsyncTestCase +from tornado import gen +from pubnub.pubnub_tornado import PubNubTornado, SubscribeListener +from tests.helper import pnconf_sub_copy +from tests.integrational.tornado.vcr_tornado_decorator import use_cassette_and_stub_time_sleep + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +class SubscriptionTest(object): + def __init__(self): + super(SubscriptionTest, self).__init__() + self.pubnub = None + self.pubnub_listener = None + + +class TestChannelSubscription(AsyncTestCase, SubscriptionTest): + def setUp(self): + super(TestChannelSubscription, self).setUp() + + messenger_config = pnconf_sub_copy() + messenger_config.set_presence_timeout(8) + messenger_config.uuid = "heartbeat-tornado-messenger" + + listener_config = pnconf_sub_copy() + listener_config.uuid = "heartbeat-tornado-listener" + + self.pubnub = PubNubTornado(messenger_config, custom_ioloop=self.io_loop) + self.pubnub_listener = PubNubTornado(listener_config, custom_ioloop=self.io_loop) + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/heartbeat/timeout.yaml', + filter_query_parameters=['uuid', 'pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'string_list_in_path', 'query'], + match_on_kwargs={ + 'string_list_in_path': { + 'positions': [4] + } + }) + @tornado.testing.gen_test(timeout=20) + def test_timeout_event_on_broken_heartbeat(self): + ch = "heartbeat-tornado-ch" + + # - connect to :ch-pnpres + callback_presence = SubscribeListener() + self.pubnub_listener.add_listener(callback_presence) + self.pubnub_listener.subscribe().channels(ch).with_presence().execute() + yield callback_presence.wait_for_connect() + + envelope = yield callback_presence.wait_for_presence_on(ch) + assert ch == envelope.channel + assert 'join' == envelope.event + assert self.pubnub_listener.uuid == envelope.uuid + + # - connect to :ch + callback_messages = SubscribeListener() + self.pubnub.add_listener(callback_messages) + self.pubnub.subscribe().channels(ch).execute() + + # - assert join event + useless, prs_envelope = yield [ + callback_messages.wait_for_connect(), + callback_presence.wait_for_presence_on(ch)] + assert ch == prs_envelope.channel + assert 'join' == prs_envelope.event + assert self.pubnub.uuid == prs_envelope.uuid + + # wait for one heartbeat call + yield gen.sleep(8) + + # - break messenger heartbeat loop + self.pubnub._subscription_manager._stop_heartbeat_timer() + + # - assert for timeout + envelope = yield callback_presence.wait_for_presence_on(ch) + assert ch == envelope.channel + assert 'timeout' == envelope.event + assert self.pubnub.uuid == envelope.uuid + + # - disconnect from :ch-pnpres + self.pubnub_listener.unsubscribe().channels(ch).execute() + yield callback_presence.wait_for_disconnect() diff --git a/tests/integrational/tornado/test_here_now.py b/tests/integrational/tornado/test_here_now.py new file mode 100644 index 00000000..1f607e5d --- /dev/null +++ b/tests/integrational/tornado/test_here_now.py @@ -0,0 +1,109 @@ +import logging + +import tornado +import tornado.gen +from tornado import gen +from tornado.testing import AsyncTestCase + +import pubnub as pn +from pubnub.pubnub_tornado import PubNubTornado, SubscribeListener +from tests.helper import pnconf_sub_copy +from tests.integrational.tornado.tornado_helper import connect_to_channel, disconnect_from_channel +from tests.integrational.tornado.vcr_tornado_decorator import use_cassette_and_stub_time_sleep + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +class TestPubNubAsyncHereNow(AsyncTestCase): + def setUp(self): + super(TestPubNubAsyncHereNow, self).setUp() + self.pubnub = PubNubTornado(pnconf_sub_copy(), custom_ioloop=self.io_loop) + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/here_now/single.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + @tornado.testing.gen_test(timeout=15) + def test_here_now_single_channel(self): + ch = 'test-here-now-channel' + self.pubnub.config.uuid = 'test-here-now-uuid' + yield connect_to_channel(self.pubnub, ch) + yield tornado.gen.sleep(10) + env = yield self.pubnub.here_now() \ + .channels(ch) \ + .include_uuids(True) \ + .future() + + assert env.result.total_channels == 1 + assert env.result.total_occupancy >= 1 + + channels = env.result.channels + + assert len(channels) == 1 + assert channels[0].occupancy == 1 + assert channels[0].occupants[0].uuid == self.pubnub.uuid + + yield disconnect_from_channel(self.pubnub, ch) + self.pubnub.stop() + self.stop() + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/here_now/multiple.yaml', + filter_query_parameters=['uuid', 'tt', 'tr', 'pnsdk']) + @tornado.testing.gen_test(timeout=120) + def test_here_now_multiple_channels(self): + ch1 = 'test-here-now-channel1' + ch2 = 'test-here-now-channel2' + self.pubnub.config.uuid = 'test-here-now-uuid' + # print("connecting to the first...") + yield connect_to_channel(self.pubnub, ch1) + # print("...connected to the first") + yield gen.sleep(1) + # print("connecting to the second...") + self.pubnub.subscribe().channels(ch2).execute() + # print("...connected to the second") + yield gen.sleep(5) + env = yield self.pubnub.here_now() \ + .channels([ch1, ch2]) \ + .future() + + assert env.result.total_channels == 2 + assert env.result.total_occupancy >= 1 + + channels = env.result.channels + + assert len(channels) == 2 + assert channels[0].occupancy >= 1 + assert channels[0].occupants[0].uuid == self.pubnub.uuid + assert channels[1].occupancy >= 1 + assert channels[1].occupants[0].uuid == self.pubnub.uuid + + yield disconnect_from_channel(self.pubnub, [ch1, ch2]) + self.pubnub.stop() + self.stop() + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/here_now/global.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + @tornado.testing.gen_test(timeout=15) + def test_here_now_global(self): + ch1 = 'test-here-now-channel1' + ch2 = 'test-here-now-channel2' + self.pubnub.config.uuid = 'test-here-now-uuid' + + callback_messages = SubscribeListener() + self.pubnub.add_listener(callback_messages) + self.pubnub.subscribe().channels(ch1).execute() + yield callback_messages.wait_for_connect() + + self.pubnub.subscribe().channels(ch2).execute() + yield gen.sleep(6) + + env = yield self.pubnub.here_now().future() + + assert env.result.total_channels >= 2 + assert env.result.total_occupancy >= 1 + + yield disconnect_from_channel(self.pubnub, [ch1, ch2]) + + self.pubnub.stop() + self.stop() diff --git a/tests/integrational/tornado/test_invocations.py b/tests/integrational/tornado/test_invocations.py new file mode 100644 index 00000000..18383205 --- /dev/null +++ b/tests/integrational/tornado/test_invocations.py @@ -0,0 +1,98 @@ +import logging +import pytest +import pubnub as pn +import tornado + +from tests.integrational.tornado.vcr_tornado_decorator import use_cassette_and_stub_time_sleep + +pn.set_stream_logger('pubnub', logging.DEBUG) + +from tornado.testing import AsyncTestCase +from pubnub.exceptions import PubNubException +from pubnub.models.consumer.pubsub import PNPublishResult +from pubnub.pnconfiguration import PNConfiguration +from pubnub.pubnub_tornado import PubNubTornado, TornadoEnvelope, PubNubTornadoException +from tests.helper import pnconf_sub_copy + +corrupted_keys = PNConfiguration() +corrupted_keys.publish_key = "blah" +corrupted_keys.subscribe_key = "blah" + +pn.set_stream_logger('pubnub', logging.DEBUG) + +ch = "tornado-publish" + + +class TestPubNubTornadoInvocations(AsyncTestCase): + def setUp(self): + super(TestPubNubTornadoInvocations, self).setUp() + + @tornado.testing.gen_test + def test_publish_resultx(self): + pubnub = PubNubTornado(pnconf_sub_copy(), custom_ioloop=self.io_loop) + result = yield pubnub.publish().message('hey').channel('blah').result() + assert isinstance(result, PNPublishResult) + + pubnub.stop() + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/invocations/result_raises.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + @tornado.testing.gen_test + def test_publish_result_raises_pubnub_error(self): + pubnub = PubNubTornado(corrupted_keys, custom_ioloop=self.io_loop) + with pytest.raises(PubNubException) as exinfo: + yield pubnub.publish().message('hey').channel('blah').result() + + assert 'Invalid Subscribe Key' in str(exinfo.value) + assert 400 == exinfo.value._status_code + + pubnub.stop() + + @tornado.testing.gen_test + def xtest_publish_result_raises_lower_level_error(self): + pubnub = PubNubTornado(pnconf_sub_copy(), custom_ioloop=self.io_loop) + + # TODO: find a better way ot emulate broken connection + pubnub.http.close() + + with self.assertRaises(Exception) as context: + yield pubnub.publish().message('hey').channel('blah').result() + + assert 'fetch() called on closed AsyncHTTPClient' in str(context.exception.message) + + pubnub.stop() + + @tornado.testing.gen_test + def test_publish_futurex(self): + pubnub = PubNubTornado(pnconf_sub_copy(), custom_ioloop=self.io_loop) + envelope = yield pubnub.publish().message('hey').channel('blah').future() + assert isinstance(envelope, TornadoEnvelope) + assert not envelope.is_error() + + pubnub.stop() + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/invocations/future_raises.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + @tornado.testing.gen_test + def test_publish_future_raises(self): + pubnub = PubNubTornado(corrupted_keys, custom_ioloop=self.io_loop) + e = yield pubnub.publish().message('hey').channel('blah').future() + assert isinstance(e, PubNubTornadoException) + assert e.is_error() + assert 400 == e.value()._status_code + + pubnub.stop() + + @tornado.testing.gen_test + def xtest_publish_future_raises_lower_level_error(self): + pubnub = PubNubTornado(corrupted_keys, custom_ioloop=self.io_loop) + + pubnub.http.close() + + e = yield pubnub.publish().message('hey').channel('blah').future() + assert isinstance(e, PubNubTornadoException) + assert str(e) == "fetch() called on closed AsyncHTTPClient" + + pubnub.stop() diff --git a/tests/integrational/tornado/test_publish.py b/tests/integrational/tornado/test_publish.py new file mode 100644 index 00000000..cc13631d --- /dev/null +++ b/tests/integrational/tornado/test_publish.py @@ -0,0 +1,251 @@ +import logging + +import tornado +from tornado.concurrent import Future +from tornado.testing import AsyncTestCase + +import pubnub as pn +from pubnub.exceptions import PubNubException +from pubnub.models.consumer.common import PNStatus +from pubnub.models.consumer.pubsub import PNPublishResult +from pubnub.pnconfiguration import PNConfiguration +from pubnub.pubnub_tornado import PubNubTornado, TornadoEnvelope +from tests.helper import pnconf, pnconf_enc, gen_decrypt_func, pnconf_pam_copy +from tests.integrational.vcr_helper import pn_vcr + +pn.set_stream_logger('pubnub', logging.DEBUG) + +ch = "tornado-publish" + + +class TestPubNubAsyncPublish(AsyncTestCase): + def setUp(self): + AsyncTestCase.setUp(self) + self.env = None + + def callback(self, tornado_res): + self.env = tornado_res.result() + self.pubnub.stop() + self.stop() + + def assert_success(self, pub): + pub.future().add_done_callback(self.callback) + + self.pubnub.start() + self.wait() + + assert isinstance(self.env, TornadoEnvelope) + assert isinstance(self.env.result, PNPublishResult) + assert isinstance(self.env.status, PNStatus) + assert self.env.result.timetoken > 0 + assert len(self.env.status.original_response) > 0 + + @tornado.testing.gen_test + def assert_success_yield(self, pub): + envelope = yield pub.future() + + assert isinstance(envelope, TornadoEnvelope) + assert isinstance(envelope.result, PNPublishResult) + assert isinstance(envelope.status, PNStatus) + assert envelope.result.timetoken > 0 + assert len(envelope.status.original_response) > 0 + + def assert_success_publish_get(self, msg): + self.pubnub = PubNubTornado(pnconf, custom_ioloop=self.io_loop) + self.assert_success(self.pubnub.publish().channel(ch).message(msg)) + self.assert_success_yield(self.pubnub.publish().channel(ch).message(msg)) + + def assert_success_publish_post(self, msg): + self.pubnub = PubNubTornado(pnconf, custom_ioloop=self.io_loop) + self.assert_success(self.pubnub.publish().channel(ch).message(msg).use_post(True)) + self.assert_success_yield(self.pubnub.publish().channel(ch).message(msg).use_post(True)) + + def assert_success_publish_get_encrypted(self, msg): + self.pubnub = PubNubTornado(pnconf_enc, custom_ioloop=self.io_loop) + self.assert_success(self.pubnub.publish().channel(ch).message(msg)) + self.assert_success_yield(self.pubnub.publish().channel(ch).message(msg)) + + def assert_success_publish_post_encrypted(self, msg): + self.pubnub = PubNubTornado(pnconf_enc, custom_ioloop=self.io_loop) + self.assert_success(self.pubnub.publish().channel(ch).message(msg).use_post(True)) + self.assert_success_yield(self.pubnub.publish().channel(ch).message(msg).use_post(True)) + + def assert_client_side_error(self, pub, expected_err_msg): + try: + yield pub.future() + + self.pubnub.start() + self.wait() + except PubNubException as e: + assert expected_err_msg in str(e) + + self.pubnub.stop() + self.stop() + + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/tornado/publish/mixed_via_get.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + def test_publish_mixed_via_get(self): + self.assert_success_publish_get("hi") + self.assert_success_publish_get(5) + self.assert_success_publish_get(True) + self.assert_success_publish_get(["hi", "hi2", "hi3"]) + + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/tornado/publish/object_via_get.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'object_in_path', 'query']) + def test_publish_object_via_get(self): + self.assert_success_publish_get({"name": "Alex", "online": True}) + + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/tornado/publish/mixed_via_post.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['method', 'scheme', 'host', 'port', 'path', 'query']) + def test_publish_mixed_via_post(self): + self.assert_success_publish_post("hi") + self.assert_success_publish_post(5) + self.assert_success_publish_post(True) + self.assert_success_publish_post(["hi", "hi2", "hi3"]) + + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/tornado/publish/object_via_post.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['host', 'method', 'path', 'query', 'object_in_body']) + def test_publish_object_via_post(self): + self.assert_success_publish_post({"name": "Alex", "online": True}) + + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/tornado/publish/mixed_via_get_encrypted.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + def test_publish_mixed_via_get_encrypted(self): + self.assert_success_publish_get_encrypted("hi") + self.assert_success_publish_get_encrypted(5) + self.assert_success_publish_get_encrypted(True) + self.assert_success_publish_get_encrypted(["hi", "hi2", "hi3"]) + + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/tornado/publish/object_via_get_encrypted.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['host', 'method', 'query', 'object_in_path'], + match_on_kwargs={'object_in_path': { + 'decrypter': gen_decrypt_func('testKey')}}) + def test_publish_object_via_get_encrypted(self): + self.assert_success_publish_get_encrypted({"name": "Alex", "online": True}) + + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/tornado/publish/mixed_via_post_encrypted.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['method', 'path', 'query', 'body']) + def test_publish_mixed_via_post_encrypted(self): + self.assert_success_publish_post_encrypted("hi") + self.assert_success_publish_post_encrypted(5) + self.assert_success_publish_post_encrypted(True) + self.assert_success_publish_post_encrypted(["hi", "hi2", "hi3"]) + + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/tornado/publish/object_via_post_encrypted.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['method', 'path', 'query', 'object_in_body'], + match_on_kwargs={'object_in_body': { + 'decrypter': gen_decrypt_func('testKey')}}) + def test_publish_object_via_post_encrypted(self): + self.assert_success_publish_post_encrypted({"name": "Alex", "online": True}) + + def test_error_missing_message(self): + self.pubnub = PubNubTornado(pnconf, custom_ioloop=self.io_loop) + + self.assert_client_side_error(self.pubnub.publish().channel(ch).message(None), "Message missing") + + def test_error_missing_channel(self): + self.pubnub = PubNubTornado(pnconf, custom_ioloop=self.io_loop) + + self.assert_client_side_error(self.pubnub.publish().channel("").message("hey"), "Channel missing") + + def test_error_non_serializable(self): + self.pubnub = PubNubTornado(pnconf, custom_ioloop=self.io_loop) + + def method(): + pass + + self.assert_client_side_error(self.pubnub.publish().channel(ch).message(method), "not JSON serializable") + + def sserr_cb(self, env): + assert isinstance(env, Future) + exception = env.exception() + + self.pubnub.stop() + # this kind of assertion will not fail the test if'll be moved below `self.stop()` call + # but also not raises correct exception, timeout exception will be raised on fail instead + assert self.expected_err_msg in str(exception) + self.stop() + + def assert_server_side_error(self, pub, expected_err_msg): + self.expected_err_msg = expected_err_msg + pub.result().add_done_callback(self.sserr_cb) + + self.pubnub.start() + self.wait() + + @tornado.testing.gen_test + def assert_server_side_error_yield(self, pub, expected_err_msg): + + try: + yield pub.result() + + self.pubnub.start() + self.wait() + except PubNubException as e: + assert expected_err_msg in str(e) + + self.pubnub.stop() + self.stop() + + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/tornado/publish/invalid_key.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + def test_error_invalid_key(self): + conf = PNConfiguration() + conf.publish_key = "fake" + conf.subscribe_key = "demo" + + self.pubnub = PubNubTornado(conf, custom_ioloop=self.io_loop) + + self.assert_server_side_error(self.pubnub.publish().channel(ch).message("hey"), "Invalid Key") + self.assert_server_side_error_yield(self.pubnub.publish().channel(ch).message("hey"), "Invalid Key") + + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/tornado/publish/not_permitted.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + def test_error_not_permitted_403(self): + my_pnconf = pnconf_pam_copy() + my_pnconf.secret_key = None + self.pubnub = PubNubTornado(my_pnconf, custom_ioloop=self.io_loop) + + self.assert_server_side_error( + self.pubnub.publish().channel("not_permitted_channel").message("hey"), "HTTP Client Error (403)") + self.assert_server_side_error_yield( + self.pubnub.publish().channel("not_permitted_channel").message("hey"), "HTTP Client Error (403)") + + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/tornado/publish/meta_object.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['host', 'method', 'path', 'meta_object_in_query']) + def test_publish_with_meta(self): + self.pubnub = PubNubTornado(pnconf, custom_ioloop=self.io_loop) + + self.assert_success( + self.pubnub.publish().channel(ch).message("hey").meta({'a': 2, 'b': 'qwer'})) + self.assert_success_yield( + self.pubnub.publish().channel(ch).message("hey").meta({'a': 2, 'b': 'qwer'})) + + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/tornado/publish/do_not_store.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + def test_publish_do_not_store(self): + self.pubnub = PubNubTornado(pnconf, custom_ioloop=self.io_loop) + + self.assert_success( + self.pubnub.publish().channel(ch).message("hey").should_store(False)) + self.assert_success_yield( + self.pubnub.publish().channel(ch).message("hey").should_store(False)) diff --git a/tests/integrational/tornado/test_ssl.py b/tests/integrational/tornado/test_ssl.py new file mode 100644 index 00000000..34e002c6 --- /dev/null +++ b/tests/integrational/tornado/test_ssl.py @@ -0,0 +1,42 @@ +import logging +import pytest +import sys +import tornado +import pubnub as pn + +from tornado.testing import AsyncTestCase +from pubnub.models.consumer.common import PNStatus +from pubnub.models.consumer.pubsub import PNPublishResult +from pubnub.pubnub_tornado import PubNubTornado, TornadoEnvelope +from tests.helper import pnconf_ssl_copy + +try: + import __pypy__ +except ImportError: + __pypy__ = None + +pn.set_stream_logger('pubnub', logging.DEBUG) + +ch = "tornado-int-publish" + + +class TestPubNubAsyncPublish(AsyncTestCase): + # TODO: add vcr + @pytest.mark.skipif(__pypy__ is not None, + reason="TODO: configure SSL certificate to make test pass") + @tornado.testing.gen_test + def test_publish_ssl(self): + print(sys.version_info) + pubnub = PubNubTornado(pnconf_ssl_copy(), custom_ioloop=self.io_loop) + msg = "hey" + pub = pubnub.publish().channel(ch).message(msg) + + envelope = yield pub.future() + + assert isinstance(envelope, TornadoEnvelope) + assert isinstance(envelope.result, PNPublishResult) + assert isinstance(envelope.status, PNStatus) + assert envelope.result.timetoken > 0 + assert len(envelope.status.original_response) > 0 + + pubnub.stop() diff --git a/tests/integrational/tornado/test_state.py b/tests/integrational/tornado/test_state.py new file mode 100644 index 00000000..87b5eabd --- /dev/null +++ b/tests/integrational/tornado/test_state.py @@ -0,0 +1,76 @@ +import tornado +import logging +import pubnub as pn + +from tornado.testing import AsyncTestCase +from pubnub.pubnub_tornado import PubNubTornado +from tests.helper import pnconf_copy +from tests.integrational.tornado.vcr_tornado_decorator import use_cassette_and_stub_time_sleep + + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +class TestPubNubState(AsyncTestCase): + def setUp(self): + super(TestPubNubState, self).setUp() + self.pubnub = PubNubTornado(pnconf_copy(), custom_ioloop=self.io_loop) + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/state/single_channel.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['method', 'host', 'path', 'state_object_in_query']) + @tornado.testing.gen_test + def test_state_single_channel(self): + ch = "state-tornado-ch" + self.pubnub.config.uuid = 'state-tornado-uuid' + state = {"name": "Alex", "count": 5} + + env = yield self.pubnub.set_state() \ + .channels(ch) \ + .state(state) \ + .future() + + assert env.result.state['name'] == "Alex" + assert env.result.state['count'] == 5 + + env = yield self.pubnub.get_state() \ + .channels(ch) \ + .future() + + assert env.result.channels[ch]['name'] == "Alex" + assert env.result.channels[ch]['count'] == 5 + + self.pubnub.stop() + self.stop() + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/state/multiple_channel.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk'], + match_on=['method', 'host', 'path', 'state_object_in_query']) + @tornado.testing.gen_test + def test_multiple_channels(self): + ch1 = "state-tornado-ch1" + ch2 = "state-tornado-ch2" + self.pubnub.config.uuid = 'state-tornado-uuid' + state = {"name": "Alex", "count": 5} + + env = yield self.pubnub.set_state() \ + .channels([ch1, ch2]) \ + .state(state) \ + .future() + + assert env.result.state['name'] == "Alex" + assert env.result.state['count'] == 5 + + env = yield self.pubnub.get_state() \ + .channels([ch1, ch2]) \ + .future() + + assert env.result.channels[ch1]['name'] == "Alex" + assert env.result.channels[ch2]['name'] == "Alex" + assert env.result.channels[ch1]['count'] == 5 + assert env.result.channels[ch2]['count'] == 5 + + self.pubnub.stop() + self.stop() diff --git a/tests/integrational/tornado/test_subscribe.py b/tests/integrational/tornado/test_subscribe.py new file mode 100644 index 00000000..8320079a --- /dev/null +++ b/tests/integrational/tornado/test_subscribe.py @@ -0,0 +1,303 @@ +import logging + +import tornado +from tornado import gen +from tornado.testing import AsyncTestCase + +import pubnub as pn +from pubnub.pubnub_tornado import PubNubTornado, SubscribeListener +from tests.helper import pnconf_sub_copy +from tests.integrational.tornado.vcr_tornado_decorator import use_cassette_and_stub_time_sleep + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +class SubscriptionTest(object): + def __init__(self): + super(SubscriptionTest, self).__init__() + self.pubnub = None + self.pubnub_listener = None + + +class TestChannelSubscription(AsyncTestCase, SubscriptionTest): + def setUp(self): + super(TestChannelSubscription, self).setUp() + self.pubnub = PubNubTornado(pnconf_sub_copy(), custom_ioloop=self.io_loop) + self.pubnub_listener = PubNubTornado(pnconf_sub_copy(), custom_ioloop=self.io_loop) + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/subscribe/sub_unsub.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + @tornado.testing.gen_test(timeout=300) + def test_subscribe_unsubscribe(self): + ch = "subscribe-tornado-ch" + + callback_messages = SubscribeListener() + self.pubnub.add_listener(callback_messages) + + self.pubnub.subscribe().channels(ch).execute() + assert ch in self.pubnub.get_subscribed_channels() + assert len(self.pubnub.get_subscribed_channels()) == 1 + + yield callback_messages.wait_for_connect() + assert ch in self.pubnub.get_subscribed_channels() + assert len(self.pubnub.get_subscribed_channels()) == 1 + + self.pubnub.unsubscribe().channels(ch).execute() + assert ch not in self.pubnub.get_subscribed_channels() + assert len(self.pubnub.get_subscribed_channels()) == 0 + + yield callback_messages.wait_for_disconnect() + assert ch not in self.pubnub.get_subscribed_channels() + assert len(self.pubnub.get_subscribed_channels()) == 0 + + self.pubnub.stop() + self.stop() + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/subscribe/sub_pub_unsub.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + @tornado.testing.gen_test(timeout=30) + def test_subscribe_publish_unsubscribe(self): + ch = "subscribe-tornado-ch" + message = "hey" + + callback_messages = SubscribeListener() + self.pubnub.add_listener(callback_messages) + self.pubnub.subscribe().channels(ch).execute() + yield callback_messages.wait_for_connect() + + sub_env, pub_env = yield [ + callback_messages.wait_for_message_on(ch), + self.pubnub.publish().channel(ch).message(message).future()] + + assert pub_env.status.original_response[0] == 1 + assert pub_env.status.original_response[1] == 'Sent' + + assert sub_env.channel == ch + assert sub_env.subscription is None + assert sub_env.message == message + + self.pubnub.unsubscribe().channels(ch).execute() + yield callback_messages.wait_for_disconnect() + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/subscribe/join_leave.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + @tornado.testing.gen_test(timeout=30) + def test_join_leave(self): + ch = "subscribe-tornado-ch" + + # HINT: use random generated uuids to test without VCR + # rnd = gen_string(4) + # self.pubnub.config.uuid = "subscribe-tornado-messenger-%s" % rnd + # self.pubnub_listener.config.uuid = "subscribe-tornado-listener-%s" % rnd + + self.pubnub.config.uuid = "subscribe-tornado-messenger-3" + self.pubnub_listener.config.uuid = "subscribe-tornado-listener-3" + + callback_presence = SubscribeListener() + self.pubnub_listener.add_listener(callback_presence) + self.pubnub_listener.subscribe().channels(ch).with_presence().execute() + yield callback_presence.wait_for_connect() + + envelope = yield callback_presence.wait_for_presence_on(ch) + assert envelope.channel == ch + assert envelope.event == 'join' + assert envelope.uuid == self.pubnub_listener.uuid + + callback_messages = SubscribeListener() + self.pubnub.add_listener(callback_messages) + self.pubnub.subscribe().channels(ch).execute() + yield callback_messages.wait_for_connect() + + envelope = yield callback_presence.wait_for_presence_on(ch) + assert envelope.channel == ch + assert envelope.event == 'join' + assert envelope.uuid == self.pubnub.uuid + + self.pubnub.unsubscribe().channels(ch).execute() + yield callback_messages.wait_for_disconnect() + + envelope = yield callback_presence.wait_for_presence_on(ch) + assert envelope.channel == ch + assert envelope.event == 'leave' + assert envelope.uuid == self.pubnub.uuid + + self.pubnub_listener.unsubscribe().channels(ch).execute() + yield callback_presence.wait_for_disconnect() + self.pubnub.stop() + self.stop() + + +class TestChannelGroupSubscription(AsyncTestCase, SubscriptionTest): + def setUp(self): + super(TestChannelGroupSubscription, self).setUp() + self.pubnub = PubNubTornado(pnconf_sub_copy(), custom_ioloop=self.io_loop) + self.pubnub_listener = PubNubTornado(pnconf_sub_copy(), custom_ioloop=self.io_loop) + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/subscribe/group_sub_unsub.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + @tornado.testing.gen_test(timeout=60) + def test_group_subscribe_unsubscribe(self): + ch = "subscribe-unsubscribe-channel" + gr = "subscribe-unsubscribe-group" + + envelope = yield self.pubnub.add_channel_to_channel_group().channel_group(gr).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + yield gen.sleep(1) + + callback_messages = SubscribeListener() + self.pubnub.add_listener(callback_messages) + self.pubnub.subscribe().channel_groups(gr).execute() + yield callback_messages.wait_for_connect() + + self.pubnub.unsubscribe().channel_groups(gr).execute() + yield callback_messages.wait_for_disconnect() + + envelope = yield self.pubnub.remove_channel_from_channel_group().channel_group(gr).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/subscribe/group_sub_pub_unsub.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + @tornado.testing.gen_test(timeout=60) + def test_group_subscribe_publish_unsubscribe(self): + ch = "subscribe-unsubscribe-channel" + gr = "subscribe-unsubscribe-group" + message = "hey" + + envelope = yield self.pubnub.add_channel_to_channel_group().channel_group(gr).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + yield gen.sleep(1) + + callback_messages = SubscribeListener() + self.pubnub.add_listener(callback_messages) + self.pubnub.subscribe().channel_groups(gr).execute() + yield callback_messages.wait_for_connect() + + sub_envelope, pub_envelope = yield [ + callback_messages.wait_for_message_on(ch), + self.pubnub.publish().channel(ch).message(message).future()] + + assert pub_envelope.status.original_response[0] == 1 + assert pub_envelope.status.original_response[1] == 'Sent' + + assert sub_envelope.channel == ch + assert sub_envelope.subscription == gr + assert sub_envelope.message == message + + self.pubnub.unsubscribe().channel_groups(gr).execute() + yield callback_messages.wait_for_disconnect() + + envelope = yield self.pubnub.remove_channel_from_channel_group().channel_group(gr).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/subscribe/group_join_leave.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + @tornado.testing.gen_test(timeout=60) + def test_group_join_leave(self): + self.pubnub.config.uuid = "test-subscribe-messenger" + self.pubnub_listener.config.uuid = "test-subscribe-listener" + + ch = "subscribe-test-channel" + gr = "subscribe-test-group" + + envelope = yield self.pubnub.add_channel_to_channel_group().channel_group(gr).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + yield gen.sleep(1) + + callback_messages = SubscribeListener() + callback_presence = SubscribeListener() + + self.pubnub_listener.add_listener(callback_presence) + self.pubnub_listener.subscribe().channel_groups(gr).with_presence().execute() + yield callback_presence.wait_for_connect() + + prs_envelope = yield callback_presence.wait_for_presence_on(ch) + assert prs_envelope.event == 'join' + assert prs_envelope.uuid == self.pubnub_listener.uuid + assert prs_envelope.channel == ch + assert prs_envelope.subscription == gr + + self.pubnub.add_listener(callback_messages) + self.pubnub.subscribe().channel_groups(gr).execute() + + useless, prs_envelope = yield [ + callback_messages.wait_for_connect(), + callback_presence.wait_for_presence_on(ch) + ] + + assert prs_envelope.event == 'join' + assert prs_envelope.uuid == self.pubnub.uuid + assert prs_envelope.channel == ch + assert prs_envelope.subscription == gr + + self.pubnub.unsubscribe().channel_groups(gr).execute() + + useless, prs_envelope = yield [ + callback_messages.wait_for_disconnect(), + callback_presence.wait_for_presence_on(ch) + ] + + assert prs_envelope.event == 'leave' + assert prs_envelope.uuid == self.pubnub.uuid + assert prs_envelope.channel == ch + assert prs_envelope.subscription == gr + + self.pubnub_listener.unsubscribe().channel_groups(gr).execute() + yield callback_presence.wait_for_disconnect() + + envelope = yield self.pubnub.remove_channel_from_channel_group().channel_group(gr).channels(ch).future() + assert envelope.status.original_response['status'] == 200 + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/subscribe/subscribe_tep_by_step.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) + @tornado.testing.gen_test(timeout=30) + def test_subscribe_step_by_step(self): + ch1 = 'test-here-now-channel1' + ch2 = 'test-here-now-channel2' + ch3 = 'test-here-now-channel3' + self.pubnub.config.uuid = 'test-here-now-uuid' + callback_messages = SubscribeListener() + self.pubnub.add_listener(callback_messages) + print("connecting to the first...") + self.pubnub.subscribe().channels(ch1).execute() + yield callback_messages.wait_for_connect() + print("...connected to the first") + yield gen.sleep(1) + print("connecting to the second...") + self.pubnub.subscribe().channels(ch2).execute() + self.pubnub.subscribe().channels(ch3).execute() + self.pubnub.subscribe().channels(ch3).execute() + self.pubnub.subscribe().channels(ch2).execute() + print("...connected to the second") + yield gen.sleep(5) + env = yield self.pubnub.here_now() \ + .channels([ch1, ch2]) \ + .future() + + assert env.result.total_channels == 2 + assert env.result.total_occupancy >= 1 + + channels = env.result.channels + + assert len(channels) == 2 + assert channels[0].occupancy >= 1 + assert channels[0].occupants[0].uuid == self.pubnub.uuid + assert channels[1].occupancy >= 1 + assert channels[1].occupants[0].uuid == self.pubnub.uuid + + self.pubnub.unsubscribe().channels([ch1, ch2]).execute() + yield callback_messages.wait_for_disconnect() + + self.pubnub.unsubscribe().channels(ch3).execute() + + self.pubnub.stop() + self.stop() diff --git a/tests/integrational/tornado/test_where_now.py b/tests/integrational/tornado/test_where_now.py new file mode 100644 index 00000000..9d08e2f5 --- /dev/null +++ b/tests/integrational/tornado/test_where_now.py @@ -0,0 +1,70 @@ +import tornado +from tornado import gen +from tornado.testing import AsyncTestCase + +from pubnub.pubnub_tornado import PubNubTornado, SubscribeListener +from tests.helper import pnconf_sub_copy +from tests.integrational.tornado.tornado_helper import connect_to_channel, disconnect_from_channel +from tests.integrational.tornado.vcr_tornado_decorator import use_cassette_and_stub_time_sleep + + +class TestPubNubAsyncWhereNow(AsyncTestCase): + def setUp(self): + super(TestPubNubAsyncWhereNow, self).setUp() + self.pubnub = PubNubTornado(pnconf_sub_copy(), custom_ioloop=self.io_loop) + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/where_now/single_channel.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + @tornado.testing.gen_test(timeout=15) + def test_where_now_single_channel(self): + ch = "where-now-tornado-ch" + uuid = "where-now-tornado-uuid" + self.pubnub.config.uuid = uuid + + yield connect_to_channel(self.pubnub, ch) + yield gen.sleep(10) + env = yield self.pubnub.where_now() \ + .uuid(uuid) \ + .future() + + channels = env.result.channels + + assert len(channels) == 1 + assert channels[0] == ch + + yield disconnect_from_channel(self.pubnub, ch) + self.pubnub.stop() + self.stop() + + @use_cassette_and_stub_time_sleep( + 'tests/integrational/fixtures/tornado/where_now/multiple_channels.yaml', + filter_query_parameters=['uuid', 'pnsdk']) + @tornado.testing.gen_test(timeout=15) + def test_multiple_channels(self): + ch1 = "where-now-tornado-ch1" + ch2 = "where-now-tornado-ch2" + uuid = "where-now-tornado-uuid" + self.pubnub.config.uuid = uuid + + callback_messages = SubscribeListener() + self.pubnub.add_listener(callback_messages) + self.pubnub.subscribe().channels(ch1).execute() + yield callback_messages.wait_for_connect() + + self.pubnub.subscribe().channels(ch2).execute() + yield gen.sleep(5) + + env = yield self.pubnub.where_now() \ + .uuid(uuid) \ + .future() + + channels = env.result.channels + + assert len(channels) == 2 + assert ch1 in channels + assert ch2 in channels + + yield disconnect_from_channel(self.pubnub, [ch1, ch2]) + self.pubnub.stop() + self.stop() diff --git a/tests/integrational/tornado/tornado_helper.py b/tests/integrational/tornado/tornado_helper.py new file mode 100644 index 00000000..96d6ea7b --- /dev/null +++ b/tests/integrational/tornado/tornado_helper.py @@ -0,0 +1,39 @@ +from tornado.locks import Event +from tornado import gen + +from pubnub import utils +from pubnub.callbacks import SubscribeCallback + + +class ConnectionEvent(SubscribeCallback): + def __init__(self, event, expected_status_checker): + self.event = event + self.expected_status_checker = expected_status_checker + + def status(self, pubnub, status): + if self.expected_status_checker(status): + self.event.set() + + def presence(self, pubnub, presence): + pass + + def message(self, pubnub, message): + pass + + +@gen.coroutine +def connect_to_channel(pubnub, channel): + event = Event() + callback = ConnectionEvent(event, utils.is_subscribed_event) + pubnub.add_listener(callback) + pubnub.subscribe().channels(channel).execute() + yield event.wait() + + +@gen.coroutine +def disconnect_from_channel(pubnub, channel): + event = Event() + callback = ConnectionEvent(event, utils.is_unsubscribed_event) + pubnub.add_listener(callback) + pubnub.unsubscribe().channels(channel).execute() + yield event.wait() diff --git a/tests/integrational/tornado/vcr_tornado_decorator.py b/tests/integrational/tornado/vcr_tornado_decorator.py new file mode 100644 index 00000000..8b5cc94c --- /dev/null +++ b/tests/integrational/tornado/vcr_tornado_decorator.py @@ -0,0 +1,42 @@ +import six + +from tests.integrational.vcr_helper import pn_vcr + +try: + from mock import patch +except ImportError: + from unittest.mock import patch + + +def use_cassette_and_stub_time_sleep(cassette_name, **kwargs): + context = pn_vcr.use_cassette(cassette_name, **kwargs) + full_path = "{}/{}".format(pn_vcr.cassette_library_dir, cassette_name) + cs = context.cls(path=full_path).load(path=full_path) + + if 'filter_query_parameters' in kwargs: + kwargs['filter_query_parameters'].append('pnsdk') + + import tornado.gen + + @tornado.gen.coroutine + def returner(): + return + + def _inner(f): + @patch('tornado.gen.sleep', return_value=returner()) + @six.wraps(f) + def stubbed(*args, **kwargs): + with context: + largs = list(args) + # 1 - index + largs.pop(1) + return f(*largs, **kwargs) + + @six.wraps(f) + def original(*args): + with context: + return f(*args) + + return stubbed if len(cs) > 0 else original + + return _inner diff --git a/tests/integrational/twisted/__init__.py b/tests/integrational/twisted/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integrational/twisted/test_cg.py b/tests/integrational/twisted/test_cg.py new file mode 100644 index 00000000..6aa94e08 --- /dev/null +++ b/tests/integrational/twisted/test_cg.py @@ -0,0 +1,101 @@ +import twisted + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks, returnValue +from twisted.trial import unittest +from twisted.web.client import HTTPConnectionPool + +from pubnub.models.consumer.channel_group import PNChannelGroupsAddChannelResult, PNChannelGroupsListResult, \ + PNChannelGroupsRemoveChannelResult +from pubnub.pubnub_twisted import PubNubTwisted + +from tests.helper import pnconf +from tests.integrational.vcr_helper import pn_vcr + +twisted.internet.base.DelayedCall.debug = True + + +class CGTestCase(unittest.TestCase): + def setUp(self): + self.pool = HTTPConnectionPool(reactor, persistent=False) + self.pubnub = PubNubTwisted(pnconf, reactor=reactor, pool=self.pool) + + def tearDown(self): + return self.pool.closeCachedConnections() + + def assert_valid_cg_envelope(self, envelope, type): + self.assertIsInstance(envelope.result, type) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/groups/add_single_channel.yaml', + filter_query_parameters=['uuid']) + def test_adding_channel(self): + channel = 'cgttc' + group = 'cgttg' + + envelope = yield self.pubnub.add_channel_to_channel_group() \ + .channels(channel).channel_group(group).deferred() + + self.assert_valid_cg_envelope(envelope, PNChannelGroupsAddChannelResult) + + returnValue(envelope) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/groups/remove_single_channel.yaml', + filter_query_parameters=['uuid']) + def test_removing_channel(self): + channel = 'cgttc' + group = 'cgttg' + + envelope = yield self.pubnub.remove_channel_from_channel_group() \ + .channels(channel).channel_group(group).deferred() + + self.assert_valid_cg_envelope(envelope, PNChannelGroupsRemoveChannelResult) + + returnValue(envelope) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/groups/add_channels.yaml', + filter_query_parameters=['uuid']) + def test_adding_channels(self): + channel = ['cgttc0', 'cgttc1'] + group = 'cgttg' + + envelope = yield self.pubnub.add_channel_to_channel_group() \ + .channels(channel).channel_group(group).deferred() + + self.assert_valid_cg_envelope(envelope, PNChannelGroupsAddChannelResult) + + returnValue(envelope) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/groups/remove_channels.yaml', + filter_query_parameters=['uuid']) + def test_removing_channels(self): + channel = ['cgttc0', 'cgttc1'] + group = 'cgttg' + + envelope = yield self.pubnub.remove_channel_from_channel_group() \ + .channels(channel).channel_group(group).deferred() + + self.assert_valid_cg_envelope(envelope, PNChannelGroupsRemoveChannelResult) + + returnValue(envelope) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/groups/list_channels.yaml', + filter_query_parameters=['uuid']) + def test_list_channels(self): + group = 'cgttg' + + envelope = yield self.pubnub.list_channels_in_channel_group() \ + .channel_group(group).deferred() + + self.assert_valid_cg_envelope(envelope, PNChannelGroupsListResult) + + returnValue(envelope) diff --git a/tests/integrational/twisted/test_here_now.py b/tests/integrational/twisted/test_here_now.py new file mode 100644 index 00000000..0b38133d --- /dev/null +++ b/tests/integrational/twisted/test_here_now.py @@ -0,0 +1,84 @@ +import twisted + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks, returnValue +from twisted.trial import unittest +from twisted.web.client import HTTPConnectionPool + +from pubnub.models.consumer.presence import PNHereNowResult + +from pubnub.pubnub_twisted import PubNubTwisted, TwistedEnvelope + +from tests.helper import pnconf +from tests.integrational.vcr_helper import pn_vcr + +twisted.internet.base.DelayedCall.debug = True + +channel = 'twisted-test' +channels = 'twisted-test-1', 'twisted-test-1' + + +class HereNowTest(unittest.TestCase): + def setUp(self): + self.pool = HTTPConnectionPool(reactor, persistent=False) + self.pubnub = PubNubTwisted(pnconf, reactor=reactor, pool=self.pool) + + def tearDown(self): + return self.pool.closeCachedConnections() + + class PNHereNowChannelData(object): + def __init__(self, channel_name, occupancy, occupants): + self.channel_name = channel_name + self.occupancy = occupancy + self.occupants = occupants + + def assert_valid_here_now_envelope(self, envelope, result_channels): + def get_uuids(here_now_channel_data): + return [here_now_channel_data.channel_name, + here_now_channel_data.occupancy, + map(lambda x: x.uuid, here_now_channel_data.occupants)] + + self.assertIsInstance(envelope, TwistedEnvelope) + self.assertIsInstance(envelope.result, PNHereNowResult) + self.assertEqual(map(get_uuids, envelope.result.channels), result_channels) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/here_now/global.yaml', + filter_query_parameters=['uuid']) + def test_global_here_now(self): + envelope = yield self.pubnub.here_now() \ + .include_uuids(True) \ + .deferred() + + self.assert_valid_here_now_envelope(envelope, + [[u'twisted-test-1', 1, [u'00de2586-7ad8-4955-b5f6-87cae3215d02']], + [u'twisted-test', 1, [u'00de2586-7ad8-4955-b5f6-87cae3215d02']]]) + returnValue(envelope) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/here_now/single.yaml', + filter_query_parameters=['uuid']) + def test_here_now_single_channel(self): + envelope = yield self.pubnub.here_now() \ + .channels(channel) \ + .include_uuids(True) \ + .deferred() + + self.assert_valid_here_now_envelope(envelope, [['twisted-test', 1, [u'00de2586-7ad8-4955-b5f6-87cae3215d02']]]) + returnValue(envelope) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/here_now/multiple.yaml', + filter_query_parameters=['uuid']) + def test_here_now_multiple_channels(self): + envelope = yield self.pubnub.here_now() \ + .channels(channels) \ + .include_uuids(True) \ + .deferred() + + self.assert_valid_here_now_envelope(envelope, + [[u'twisted-test-1', 1, [u'00de2586-7ad8-4955-b5f6-87cae3215d02']]]) + returnValue(envelope) diff --git a/tests/integrational/twisted/test_publish.py b/tests/integrational/twisted/test_publish.py new file mode 100644 index 00000000..77cee7ec --- /dev/null +++ b/tests/integrational/twisted/test_publish.py @@ -0,0 +1,183 @@ +import twisted +import pytest + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks, returnValue +from twisted.trial import unittest +from twisted.web.client import HTTPConnectionPool + +from pubnub.exceptions import PubNubException +from pubnub.errors import PNERR_MESSAGE_MISSING, PNERR_CHANNEL_MISSING + +from pubnub.models.consumer.common import PNStatus +from pubnub.models.consumer.pubsub import PNPublishResult +from pubnub.pnconfiguration import PNConfiguration + +from pubnub.pubnub_twisted import PubNubTwisted, TwistedEnvelope, PubNubTwistedException + +from tests.helper import pnconf, pnconf_pam_copy, pnconf_enc_copy +from tests.integrational.vcr_helper import pn_vcr + +twisted.internet.base.DelayedCall.debug = True + +channel = 'twisted-test' + + +class PublishTestCase(unittest.TestCase): + def setUp(self): + self.pool = HTTPConnectionPool(reactor, persistent=False) + self.pubnub = PubNubTwisted(pnconf, reactor=reactor, pool=self.pool) + + def tearDown(self): + return self.pool.closeCachedConnections() + + # for async + def error_envelope_asserter(self, expected_err_msg): + def assert_error_message(envelope): + assert envelope.status.error_data.information == expected_err_msg + + return assert_error_message + + def assert_client_error(self, publish, message): + try: + publish.deferred() + except PubNubException as exception: + self.assertTrue(message in exception.message) + else: + self.fail('Expected PubNubException not raised') + + def assert_client_side_error(self, envelope, expected_err_msg): + assert envelope.status.error_data.information == expected_err_msg + + def assert_valid_publish_envelope(self, envelope): + assert isinstance(envelope, TwistedEnvelope) + assert isinstance(envelope.result, PNPublishResult) + assert isinstance(envelope.status, PNStatus) + assert envelope.result.timetoken > 0 + + @inlineCallbacks + def deferred(self, event): + envelope = yield event.deferred() + returnValue(envelope) + + @inlineCallbacks + def assert_success_publish_get(self, message, meta=None): + publish = self.pubnub.publish().channel(channel).message(message).meta(meta) + envelope = yield self.deferred(publish) + self.assert_valid_publish_envelope(envelope) + returnValue(envelope) + + @inlineCallbacks + def assert_success_encrypted_publish_get(self, message): + pubnub = PubNubTwisted(pnconf_enc_copy()) + publish = pubnub.publish().channel(channel).message(message) + envelope = yield self.deferred(publish) + self.assert_valid_publish_envelope(envelope) + returnValue(envelope) + + @inlineCallbacks + def assert_success_publish_post(self, message): + publish = self.pubnub.publish().channel(channel).message(message).use_post(True) + envelope = yield self.deferred(publish) + self.assert_valid_publish_envelope(envelope) + returnValue(envelope) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/publish/mixed_via_get.yaml', + filter_query_parameters=['uuid', 'seqn']) + def test_publish_mixed_via_get(self): + d0 = yield self.assert_success_publish_get("hi") + d1 = yield self.assert_success_publish_get(5) + d2 = yield self.assert_success_publish_get(True) + d3 = yield self.assert_success_publish_get(["hi", "hi2", "hi3"]) + returnValue([d0, d1, d2, d3]) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/publish/mixed_encrypted_via_get.yaml', + filter_query_parameters=['uuid', 'seqn']) + def test_publish_mixed_encrypted_via_get(self): + d0 = yield self.assert_success_encrypted_publish_get("hi") + d1 = yield self.assert_success_encrypted_publish_get(5) + d2 = yield self.assert_success_encrypted_publish_get(True) + d3 = yield self.assert_success_encrypted_publish_get(["hi", "hi2", "hi3"]) + returnValue([d0, d1, d2, d3]) + + # TODO: uncomment this when vcr for post is fixed + # @inlineCallbacks + # @pn_vcr.use_cassette( + # 'tests/integrational/fixtures/twisted/publish/mixed_via_post.yaml', + # filter_query_parameters=['uuid', 'seqn']) + # def test_publish_mixed_via_post(self): + # d0 = yield self.assert_success_publish_post("hi") + # d1 = yield self.assert_success_publish_post(5) + # d2 = yield self.assert_success_publish_post(True) + # d3 = yield self.assert_success_publish_post(["hi", "hi2", "hi3"]) + # returnValue([d0, d1, d2, d3]) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/publish/object_via_get.yaml', + filter_query_parameters=['uuid', 'seqn']) + def test_publish_object_via_get(self): + d0 = yield self.assert_success_publish_get({"one": 2, "three": True}) + returnValue(d0) + + def test_error_missing_message(self): + self.assert_client_error( + self.pubnub.publish().channel(channel).message(None), + PNERR_MESSAGE_MISSING + ) + + def test_error_missing_channel(self): + self.assert_client_error( + self.pubnub.publish().channel('').message('whatever'), + PNERR_CHANNEL_MISSING + ) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/publish/invalid_key.yaml', + filter_query_parameters=['uuid', 'seqn']) + def test_error_invalid_key(self): + conf = PNConfiguration() + conf.publish_key = "fake" + conf.subscribe_key = "demo" + pubnub = PubNubTwisted(conf) + with pytest.raises(PubNubTwistedException) as exception: + yield pubnub.publish().channel(channel).message("hey").deferred() + + self.assertEqual(exception.value.status.error_data.information, + "HTTP Client Error (400): [0, u'Invalid Key', u'14767989321048626']") + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/publish/forbidden.yaml', + filter_query_parameters=['uuid', 'seqn', 'timestamp', 'signature']) + def test_error_forbidden(self): + pubnub = PubNubTwisted(pnconf_pam_copy()) + with pytest.raises(PubNubTwistedException) as exception: + yield pubnub.publish().channel("not_permitted_channel").message("hey").deferred() + + self.assertEqual(exception.value.status.error_data.information, + "HTTP Client Error (403): {u'status': 403, u'message': u'Forbidden', u'payload':" + " {u'channels': [u'not_permitted_channel']}, u'service': u'Access Manager', u'error': True}") + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/publish/meta_object.yaml', + filter_query_parameters=['uuid', 'seqn'], + match_on=['host', 'method', 'path', 'meta_object_in_query']) + def test_publish_with_meta(self): + yield self.assert_success_publish_get('hi', {'a': 2, 'b': True}) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/publish/do_not_store.yaml', + filter_query_parameters=['uuid', 'seqn']) + def test_publish_do_not_store(self): + publish = self.pubnub.publish().channel(channel).message('whatever').should_store(False) + envelope = yield self.deferred(publish) + self.assert_valid_publish_envelope(envelope) + returnValue(envelope) diff --git a/tests/integrational/twisted/test_state.py b/tests/integrational/twisted/test_state.py new file mode 100644 index 00000000..2c0c6402 --- /dev/null +++ b/tests/integrational/twisted/test_state.py @@ -0,0 +1,55 @@ +from copy import copy + +import twisted + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks, returnValue +from twisted.trial import unittest +from twisted.web.client import HTTPConnectionPool + +from pubnub.models.consumer.presence import PNSetStateResult + +from pubnub.pubnub_twisted import PubNubTwisted, TwistedEnvelope + +from tests.helper import pnconf +from tests.integrational.vcr_helper import pn_vcr + +twisted.internet.base.DelayedCall.debug = True + +channel = 'twisted-test' +channels = ['twisted-test-0', 'twisted-test-1'] +state = {'whatever': 'something'} + + +class StateTestCase(unittest.TestCase): + def setUp(self): + pnconf_uuid_set = copy(pnconf) + pnconf_uuid_set.uuid = 'someuuid' + self.pool = HTTPConnectionPool(reactor, persistent=False) + self.pubnub = PubNubTwisted(pnconf_uuid_set, reactor=reactor, pool=self.pool) + + def tearDown(self): + return self.pool.closeCachedConnections() + + def assert_valid_state_envelope(self, envelope): + self.assertIsInstance(envelope, TwistedEnvelope) + self.assertIsInstance(envelope.result, PNSetStateResult) + self.assertEqual(envelope.result.state, state) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/state/single_channel.yaml', + filter_query_parameters=['uuid']) + def test_state_single_channel(self): + envelope = yield self.pubnub.set_state().channels(channel).state(state).deferred() + self.assert_valid_state_envelope(envelope) + returnValue(envelope) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/state/multiple_channels.yaml', + filter_query_parameters=['uuid']) + def test_state_multiple_channels(self): + envelope = yield self.pubnub.set_state().channels(channels).state(state).deferred() + self.assert_valid_state_envelope(envelope) + returnValue(envelope) diff --git a/tests/integrational/twisted/test_where_now.py b/tests/integrational/twisted/test_where_now.py new file mode 100644 index 00000000..dea8d971 --- /dev/null +++ b/tests/integrational/twisted/test_where_now.py @@ -0,0 +1,49 @@ +import twisted + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks, returnValue +from twisted.trial import unittest +from twisted.web.client import HTTPConnectionPool + +from pubnub.models.consumer.presence import PNWhereNowResult + +from pubnub.pubnub_twisted import PubNubTwisted, TwistedEnvelope + +from tests.helper import pnconf +from tests.integrational.vcr_helper import pn_vcr + +twisted.internet.base.DelayedCall.debug = True + +uuid_looking_for = '00de2586-7ad8-4955-b5f6-87cae3215d02' + + +class WhereNowTestCase(unittest.TestCase): + def setUp(self): + self.pool = HTTPConnectionPool(reactor, persistent=False) + self.pubnub = PubNubTwisted(pnconf, reactor=reactor, pool=self.pool) + + def tearDown(self): + return self.pool.closeCachedConnections() + + def assert_valid_where_now_envelope(self, envelope, channels): + self.assertIsInstance(envelope, TwistedEnvelope) + self.assertIsInstance(envelope.result, PNWhereNowResult) + self.assertEqual(envelope.result.channels, channels) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/where_now/single.yaml', + filter_query_parameters=['uuid']) + def test_where_now_single_channel(self): + envelope = yield self.pubnub.where_now().uuid(uuid_looking_for).deferred() + self.assert_valid_where_now_envelope(envelope, [u'twisted-test-1']) + returnValue(envelope) + + @inlineCallbacks + @pn_vcr.use_cassette( + 'tests/integrational/fixtures/twisted/where_now/multiple.yaml', + filter_query_parameters=['uuid']) + def test_where_now_multiple_channels(self): + envelope = yield self.pubnub.where_now().uuid(uuid_looking_for).deferred() + self.assert_valid_where_now_envelope(envelope, [u'twisted-test-2', u'twisted-test-1']) + returnValue(envelope) diff --git a/tests/integrational/vcr_asyncio_sleeper.py b/tests/integrational/vcr_asyncio_sleeper.py new file mode 100644 index 00000000..0068f745 --- /dev/null +++ b/tests/integrational/vcr_asyncio_sleeper.py @@ -0,0 +1,85 @@ +""" +Placed into the separate file to avoid python <3.4 tests using it +""" +import six + +from pubnub.exceptions import PubNubException +from pubnub.pubnub_asyncio import SubscribeListener, AsyncioReconnectionManager + +from tests.integrational.vcr_helper import pn_vcr + + +def get_sleeper(cassette_name): + """ + Loads cassette just to check if it is in record or playback mode + """ + context = pn_vcr.use_cassette(cassette_name) + full_path = "{}/{}".format(pn_vcr.cassette_library_dir, cassette_name) + cs = context.cls(path=full_path).load(path=full_path) + + import asyncio + + @asyncio.coroutine + def fake_sleeper(v): + yield from asyncio.sleep(0) + + def decorate(f): + @six.wraps(f) + def call(*args, event_loop=None): + yield from f(*args, sleeper=(fake_sleeper if (len(cs) > 0) else asyncio.sleep), event_loop=event_loop) + + return call + return decorate + + +class VCR599Listener(SubscribeListener): + """ + The wrapper for SubscribeListener. + + Provides option to ignore the certain amount of 599 VCR errors. + + 599 VCR errors can be undesirable raised in case when the request + was not recorded since it was in long-polling phase at the time when + the test was finished. + + This means if you use this listener you should determine the amount + of 599 errors can be raised by you own and explicitly pass it to constructor. + """ + + import asyncio + + def __init__(self, raise_times): + self.silent_limit = raise_times + self.raised_times = 0 + + super(VCR599Listener, self).__init__() + + @asyncio.coroutine + def _wait_for(self, coro): + try: + res = yield from super(VCR599Listener, self)._wait_for(coro) + return res + except PubNubException as e: + if 'HTTP Server Error (599)' in str(e): + self.raised_times += 1 + if self.raised_times > self.silent_limit: + raise e + else: + """ HACK: case assumes that this is a long-polling request that wasn't fulfilled + at the time when it was recorded. To simulate this a sleep method was used. + """ + pass + # yield from asyncio.sleep(1000) + else: + raise + + +class VCR599ReconnectionManager(AsyncioReconnectionManager): + def __init__(self, pubnub): + super(VCR599ReconnectionManager, self).__init__(pubnub) + + def start_polling(self): + print(">>> Skip polling after 599 Error") + + def stop_polling(self): + pass diff --git a/tests/integrational/vcr_helper.py b/tests/integrational/vcr_helper.py new file mode 100644 index 00000000..8c8017a5 --- /dev/null +++ b/tests/integrational/vcr_helper.py @@ -0,0 +1,212 @@ +import json +import os +import six +import vcr +try: + from mock import patch +except ImportError: + from unittest.mock import patch + +from tests.helper import url_decode + +vcr_dir = os.path.dirname(os.path.dirname((os.path.dirname(os.path.abspath(__file__))))) + +pn_vcr = vcr.VCR( + cassette_library_dir=vcr_dir +) + + +def meta_object_in_query_matcher(r1, r2): + return assert_request_equal_with_object_in_query(r1, r2, 'meta') + + +def state_object_in_query_matcher(r1, r2): + return assert_request_equal_with_object_in_query(r1, r2, 'state') + + +def assert_request_equal_with_object_in_query(r1, r2, query_field_name): + try: + for v in r1.query: + if v[0] == query_field_name: + for w in r2.query: + if w[0] == query_field_name: + assert json.loads(v[1]) == json.loads(w[1]) + else: + for w in r2.query: + if w[0] == v[0]: + assert w[1] == v[1] + + except AssertionError: + return False + + return True + + +def object_in_path_matcher(r1, r2, decrypter=None): + try: + path1 = r1.path.split('/') + path2 = r2.path.split('/') + + for k, v in enumerate(path1): + if k == (len(path1) - 1): + if decrypter is not None: + assert decrypter(url_decode(v)) == decrypter(url_decode(path2[k])) + else: + assert json.loads(url_decode(v)) == json.loads(url_decode(path2[k])) + else: + assert v == path2[k] + + except AssertionError: + return False + + return True + + +def object_in_body_matcher(r1, r2, decrypter=None): + try: + if decrypter is not None: + assert decrypter(r1.body.decode('utf-8')) == decrypter(r2.body.decode('utf-8')) + else: + assert json.loads(url_decode(r1.body.decode('utf-8'))) == json.loads(url_decode(r2.body.decode('utf-8'))) + + except AssertionError: + return False + + return True + + +def string_list_in_path_matcher(r1, r2, positions=None): + """ + For here_now requests: + /v2/presence/sub-key/my_sub_key/channel/ch1,ch2?key=val + /v2/presence/sub-key/my_sub_key/channel/ch2,ch1?key=val + """ + + if positions is None: + positions = [] + elif isinstance(positions, six.integer_types): + positions = [positions] + + try: + path1 = r1.path.split('/') + path2 = r2.path.split('/') + + for k, v in enumerate(path1): + if k in positions: + ary1 = v.split(',') + ary1.sort() + ary2 = path2[k].split(',') + ary2.sort() + + assert ary1 == ary2 + else: + assert v == path2[k] + + except (AssertionError, IndexError) as e: + return False + except Exception as e: + print("Non-Assertion Exception: %s" % e) + raise + + return True + + +def string_list_in_query_matcher(r1, r2, list_keys=None, filter_keys=None): + """ + For here_now requests: + + /v1/auth/grant/sub-key/my_sub_key?channel=ch1,ch2×tamp=123 + /v1/auth/grant/sub-key/my_sub_key?channel=ch2,ch1×tamp=124 + + NOTICE: The :filter_query_parameters: cassette param should be specified alongside with :filter_keys: + """ + + if list_keys is None: + list_keys = [] + elif isinstance(list_keys, six.string_types): + list_keys = [list_keys] + + if filter_keys is None: + filter_keys = [] + elif isinstance(filter_keys, six.string_types): + filter_keys = [filter_keys] + + try: + list1 = r1.query + list2 = r2.query + + for ik, tp in enumerate(list1): + k, v = tp + if k in filter_keys: + continue + + if k in list_keys: + ary1 = v.split(',') + ary1.sort() + ary2 = list2[ik][1].split(',') + ary2.sort() + + assert ary1 == ary2 + else: + assert v == list2[ik][1] + + except (AssertionError, IndexError) as e: + return False + except Exception as e: + print("Non-Assertion Exception: %s" % e) + raise + + return True + + +def check_the_difference_matcher(r1, r2): + """ A helper to check the difference between two requests """ + + try: + assert r1.body == r2.body + assert r1.headers == r2.headers + assert r1.host == r2.host + assert r1.method == r2.method + assert r1.query == r2.query + assert r1.port == r2.port + assert r1.protocol == r2.protocol + assert r1.scheme == r2.scheme + assert r1.path == r2.path + except AssertionError: + return False + + return True + + +pn_vcr.register_matcher('meta_object_in_query', meta_object_in_query_matcher) +pn_vcr.register_matcher('state_object_in_query', state_object_in_query_matcher) +pn_vcr.register_matcher('object_in_path', object_in_path_matcher) +pn_vcr.register_matcher('object_in_body', object_in_body_matcher) +pn_vcr.register_matcher('check_the_difference', check_the_difference_matcher) +pn_vcr.register_matcher('string_list_in_path', string_list_in_path_matcher) +pn_vcr.register_matcher('string_list_in_query', string_list_in_query_matcher) + + +def use_cassette_and_stub_time_sleep_native(cassette_name, **kwargs): + context = pn_vcr.use_cassette(cassette_name, **kwargs) + full_path = "{0}/{1}".format(pn_vcr.cassette_library_dir, cassette_name) + cs = context.cls(path=full_path).load(path=full_path) + + def _inner(f): + @patch('time.sleep', return_value=None) + @six.wraps(f) + def stubbed(*args, **kwargs): + with context: + largs = list(args) + # 1 - index + largs.pop(1) + return f(*largs, **kwargs) + + @six.wraps(f) + def original(*args): + with context: + return f(*args) + + return stubbed if len(cs) > 0 else original + + return _inner diff --git a/tests/manual/__init__.py b/tests/manual/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/manual/asyncio/__init__.py b/tests/manual/asyncio/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/manual/asyncio/test_reconnections.py b/tests/manual/asyncio/test_reconnections.py new file mode 100644 index 00000000..6a8fa456 --- /dev/null +++ b/tests/manual/asyncio/test_reconnections.py @@ -0,0 +1,69 @@ +import logging +import asyncio + +import aiohttp +import pytest +import pubnub as pn +from pubnub.callbacks import SubscribeCallback +from pubnub.enums import PNReconnectionPolicy + +from pubnub.pnconfiguration import PNConfiguration +from pubnub.pubnub_asyncio import PubNubAsyncio +from tests.helper import pnconf_sub_copy + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +class MySubscribeCallback(SubscribeCallback): + def status(self, pubnub, status): + pass + + def message(self, pubnub, message): + pass + + def presence(self, pubnub, presence): + pass + + +@pytest.mark.asyncio +def test_blah(): + pnconf = pnconf_sub_copy() + assert isinstance(pnconf, PNConfiguration) + pnconf.reconnect_policy = PNReconnectionPolicy.EXPONENTIAL + pubnub = PubNubAsyncio(pnconf) + time_until_open_again = 8 + + @asyncio.coroutine + def close_soon(): + yield from asyncio.sleep(2) + pubnub._connector.close() + print(">>> connection is broken") + + @asyncio.coroutine + def open_again(): + yield from asyncio.sleep(time_until_open_again) + pubnub.set_connector(aiohttp.TCPConnector(conn_timeout=pubnub.config.connect_timeout, verify_ssl=True)) + print(">>> connection is open again") + + @asyncio.coroutine + def countdown(): + asyncio.sleep(2) + opened = False + count = time_until_open_again + + while not opened: + print(">>> %ds to open again" % count) + count -= 1 + if count <= 0: + break + yield from asyncio.sleep(1) + + my_listener = MySubscribeCallback() + pubnub.add_listener(my_listener) + pubnub.subscribe().channels('my_channel').execute() + + asyncio.ensure_future(close_soon()) + asyncio.ensure_future(open_again()) + asyncio.ensure_future(countdown()) + + yield from asyncio.sleep(1000) diff --git a/tests/manual/native/__init__.py b/tests/manual/native/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/manual/native/test_reconnection.py b/tests/manual/native/test_reconnection.py new file mode 100644 index 00000000..ff4e3017 --- /dev/null +++ b/tests/manual/native/test_reconnection.py @@ -0,0 +1,50 @@ +# subscribe to pubnub channel, push messages into the queue, +# queue worker send send them to cli +import logging +import os +import sys + +d = os.path.dirname +PUBNUB_ROOT = d(d(d(os.path.dirname(os.path.abspath(__file__))))) +sys.path.append(PUBNUB_ROOT) + +import pubnub as pn + +from pubnub.callbacks import SubscribeCallback +from pubnub.enums import PNReconnectionPolicy, PNStatusCategory +from pubnub.pnconfiguration import PNConfiguration +from pubnub.pubnub import PubNub + + +pn.set_stream_logger('pubnub', logging.DEBUG) +logger = logging.getLogger("myapp") + + +class MySubscribeCallback(SubscribeCallback): + def status(self, pubnub, status): + print("### status changed to: %s" % status.category) + if status.category == PNStatusCategory.PNReconnectedCategory: + pubnub.stop() + + def message(self, pubnub, message): + pass + + def presence(self, pubnub, presence): + pass + + +pnconf = PNConfiguration() +pnconf.publish_key = "demo" +pnconf.subscribe_key = "demo" +pnconf.origin = "localhost:8089" +pnconf.subscribe_request_timeout = 10 +pnconf.reconnect_policy = PNReconnectionPolicy.LINEAR +pubnub = PubNub(pnconf) + +time_until_open_again = 8 + +my_listener = MySubscribeCallback() +pubnub.add_listener(my_listener) +pubnub.subscribe().channels('my_channel').execute() + +# atexit.register(pubnub.stop) diff --git a/tests/manual/tornado/__init__.py b/tests/manual/tornado/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/manual/tornado/subscribe_stub.py b/tests/manual/tornado/subscribe_stub.py new file mode 100644 index 00000000..901e87f0 --- /dev/null +++ b/tests/manual/tornado/subscribe_stub.py @@ -0,0 +1,66 @@ +import time +import tornado + +from tornado import web +from tornado import gen + +# pn.set_stream_logger('pubnub', logging.DEBUG) + +ioloop = tornado.ioloop.IOLoop.current() + + +class SubscribeHandler(web.RequestHandler): + def timestamp(self): + return int(time.time() * 10000000) + + @gen.coroutine + def get(self): + tt = self.get_argument('tt') + + if tt is None: + raise gen.Return(self.send_error(500, message={ + "error": "Channel missing" + })) + + tt = int(tt) + + if tt > 0: + yield gen.sleep(5000) + + self.write('{"t":{"t":"%d","r":12},"m":[]}' % self.timestamp()) + + +class HeartbeatHandler(web.RequestHandler): + def timestamp(self): + return int(time.time() * 10000000) + + @gen.coroutine + def get(self): + self.write('{"status": 200, "message": "OK", "service": "Presence"}') + + +class TimeHandler(web.RequestHandler): + def timestamp(self): + return int(time.time() * 10000000) + + @gen.coroutine + def get(self): + self.write('[%d]' % self.timestamp()) + + +def main(): + app = web.Application( + [ + (r"/v2/subscribe/demo/my_channel/0", SubscribeHandler), + (r"/v2/presence/sub-key/demo/channel/my_channel/heartbeat", HeartbeatHandler), + (r"/time/0", TimeHandler), + ], + cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__", + xsrf_cookies=True, + ) + app.listen(8089) + ioloop.start() + + +if __name__ == "__main__": + main() diff --git a/tests/manual/tornado/test_reconnections.py b/tests/manual/tornado/test_reconnections.py new file mode 100644 index 00000000..847b5379 --- /dev/null +++ b/tests/manual/tornado/test_reconnections.py @@ -0,0 +1,73 @@ +import tornado +import logging +from tornado import gen +from tornado.testing import AsyncTestCase +import pubnub as pn + +from pubnub.callbacks import SubscribeCallback +from pubnub.enums import PNReconnectionPolicy +from pubnub.pnconfiguration import PNConfiguration +from pubnub.pubnub_tornado import PubNubTornado + + +pn.set_stream_logger('pubnub', logging.DEBUG) + + +class MySubscribeCallback(SubscribeCallback): + def status(self, pubnub, status): + pass + + def message(self, pubnub, message): + pass + + def presence(self, pubnub, presence): + pass + + +class TestPubNubReconnection(AsyncTestCase): + """ + Tornado Reconnection Manager test design to be invoked alongside with 'subscribe_stub.py' + helper to start and drop a connection. + """ + @tornado.testing.gen_test(timeout=300) + def test_reconnection(self): + pnconf = PNConfiguration() + pnconf.publish_key = "demo" + pnconf.subscribe_key = "demo" + pnconf.origin = "localhost:8089" + pnconf.subscribe_request_timeout = 10 + pnconf.reconnect_policy = PNReconnectionPolicy.LINEAR + pubnub = PubNubTornado(pnconf, custom_ioloop=self.io_loop) + time_until_open_again = 10 + + @gen.coroutine + def close_soon(): + yield gen.sleep(3) + pubnub.http.close() + pubnub.http = None + print(">>> connection is broken") + + @gen.coroutine + def open_again(): + yield gen.sleep(time_until_open_again) + pubnub.http = tornado.httpclient.AsyncHTTPClient(max_clients=PubNubTornado.MAX_CLIENTS) + print(">>> connection is open again") + + @gen.coroutine + def countdown(): + yield gen.sleep(2) + opened = False + count = time_until_open_again + + while not opened: + print(">>> %ds to open again" % count) + count -= 1 + if count <= 0: + break + yield gen.sleep(1) + + my_listener = MySubscribeCallback() + pubnub.add_listener(my_listener) + pubnub.subscribe().channels('my_channel').execute() + + yield gen.sleep(1000) diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/test_crypto.py b/tests/unit/test_crypto.py new file mode 100644 index 00000000..9fa15251 --- /dev/null +++ b/tests/unit/test_crypto.py @@ -0,0 +1,28 @@ +import unittest + +from pubnub.crypto import PubNubCryptodome +from tests.helper import gen_decrypt_func + +crypto = PubNubCryptodome() +todecode = 'QfD1NCBJCmt1aPPGU2cshw==' +key = 'testKey' + + +class TestDecode(unittest.TestCase): + def test_decode_aes(self): + hey = """ + + dfjn + t564 + + sdfhp\n + """ + + assert crypto.decrypt(key, crypto.encrypt(key, hey)) == hey + assert crypto.decrypt(key, todecode) == "hey-0" + + def test_vc_body_decoder(self): + input = b'"9P/7+NNs54o7Go41yh+3rIn8BW0H0ad+mKlKTKGw2i1eoQP1ddHrnIzkRUPEC3ko"' + # print(json.loads(input.decode('utf-8'))) + assert {"name": "Alex", "online": True} == \ + gen_decrypt_func('testKey')(input.decode('utf-8')) diff --git a/tests/unit/test_herenow.py b/tests/unit/test_herenow.py new file mode 100644 index 00000000..f2c307dc --- /dev/null +++ b/tests/unit/test_herenow.py @@ -0,0 +1,209 @@ +import unittest + +from pubnub.models.consumer.presence import PNHereNowResult + +empty = {'service': 'Presence', 'status': 200, 'message': 'OK', 'occupancy': 0, 'uuids': []} +empty_disable_uuids = {'message': 'OK', 'occupancy': 0, 'status': 200, 'service': 'Presence'} +empty_multiple = {'service': 'Presence', 'status': 200, 'payload': { + 'total_channels': 0, 'total_occupancy': 0, 'channels': {}}, 'message': 'OK'} + +single = {'status': 200, 'message': 'OK', 'service': 'Presence', 'occupancy': 1, + 'uuids': ['08c36f36-7b00-41c9-b101-d07b19bc30cb']} +single_disable_uuids = {'service': 'Presence', 'occupancy': 1, 'message': 'OK', 'status': 200} +single_with_state = {'status': 200, 'message': 'OK', 'occupancy': 1, 'service': 'Presence', + 'uuids': [{'state': {'count': 5, 'name': 'Alex'}, 'uuid': '6cbb18b9-3f58-4bd3-91c0-5dbf18a4af13'}]} + +multiple = {'status': 200, 'message': 'OK', 'payload': {'total_channels': 2, 'channels': { + 'here-now-GIY92DGX': {'occupancy': 1, 'uuids': ['c71b2961-9624-4801-90bc-6c89a725a422']}, + 'here-now-B1WZA4LO': {'occupancy': 1, 'uuids': ['c71b2961-9624-4801-90bc-6c89a725a422']}}, 'total_occupancy': 2}, + 'service': 'Presence'} +multiple_disable_uuids = { + 'payload': {'channels': {'here-now-IUX5HV7O': {'occupancy': 1}, 'here-now-FGE5Q9UY': {'occupancy': 1}}, + 'total_occupancy': 2, 'total_channels': 2}, 'message': 'OK', 'service': 'Presence', 'status': 200} +multiple_with_state = {'payload': {'total_channels': 2, 'channels': { + 'here-now-UBJJ5P3W': { + 'uuids': [{'state': {'count': 5, 'name': 'Alex'}, 'uuid': 'abf41ee8-0853-4e1c-8450-906933695b09'}], + 'occupancy': 1 + }, + 'here-now-EZTFTHZY': {'uuids': [{'uuid': 'abf41ee8-0853-4e1c-8450-906933695b09'}], 'occupancy': 1}}, + 'total_occupancy': 2}, 'status': 200, 'message': 'OK', 'service': 'Presence'} + + +class TestHereNowResultGenerator(unittest.TestCase): + def test_empty_occupancy(self): + channel_names = ['blah'] + response = PNHereNowResult.from_json(empty, channel_names) + channels = response.channels + + assert response.total_channels == 1 + assert response.total_occupancy == 0 + assert isinstance(channels, list) + assert len(channels) == 1 + + def test_empty_disable_uuids(self): + channel_names = ['blah'] + response = PNHereNowResult.from_json(empty_disable_uuids, channel_names) + channels = response.channels + + assert response.total_channels == 1 + assert response.total_occupancy == 0 + assert isinstance(channels, list) + assert len(channels) == 1 + + def test_empty_multiple(self): + channel_names = ['blah'] + response = PNHereNowResult.from_json(empty_multiple, channel_names) + channels = response.channels + + assert response.total_channels == 1 + assert response.total_occupancy == 0 + assert isinstance(channels, list) + assert len(channels) == 1 + + def test_single(self): + channel_names = ['blah'] + response = PNHereNowResult.from_json(single, channel_names) + channels = response.channels + + assert response.total_channels == 1 + assert response.total_occupancy == 1 + assert isinstance(channels, list) + assert len(channels) == 1 + + channel = channels[0] + + assert channel.channel_name == 'blah' + assert channel.occupancy == 1 + assert len(channel.occupants) == 1 + + occupants = channel.occupants[0] + + assert occupants.state is None + assert occupants.uuid == single['uuids'][0] + + def test_single_disable_uuids(self): + channel_names = ['blah'] + response = PNHereNowResult.from_json(single_disable_uuids, channel_names) + channels = response.channels + + assert response.total_channels == 1 + assert response.total_occupancy == 1 + assert isinstance(channels, list) + assert len(channels) == 1 + + channel = channels[0] + + assert channel.channel_name == 'blah' + assert channel.occupancy == 1 + assert len(channel.occupants) == 0 + + def test_single_with_state(self): + channel_names = ['blah'] + response = PNHereNowResult.from_json(single_with_state, channel_names) + channels = response.channels + + assert response.total_channels == 1 + assert response.total_occupancy == 1 + assert isinstance(channels, list) + assert len(channels) == 1 + + channel = channels[0] + + assert channel.channel_name == 'blah' + assert channel.occupancy == 1 + assert len(channel.occupants) == 1 + + occupants = channel.occupants[0] + + assert occupants.uuid == single_with_state['uuids'][0]['uuid'] + assert occupants.state == single_with_state['uuids'][0]['state'] + + def test_multiple(self): + channel_names = list(multiple['payload']['channels']) + response = PNHereNowResult.from_json(multiple, channel_names) + channels = response.channels + + assert response.total_channels == 2 + assert response.total_occupancy == 2 + assert isinstance(channels, list) + assert len(channels) == 2 + + channel1 = channels[0] + + assert channel1.channel_name == channel_names[0] + assert channel1.occupancy == multiple['payload']['channels'][channel_names[0]]['occupancy'] + assert len(channel1.occupants) == 1 + + occupants = channel1.occupants[0] + + assert occupants.state is None + assert occupants.uuid == multiple['payload']['channels'][channel_names[0]]['uuids'][0] + + channel2 = channels[1] + + assert channel2.channel_name == channel_names[1] + assert channel2.occupancy == multiple['payload']['channels'][channel_names[1]]['occupancy'] + assert len(channel2.occupants) == 1 + + occupants = channel2.occupants[0] + + assert occupants.state is None + assert occupants.uuid == multiple['payload']['channels'][channel_names[1]]['uuids'][0] + + def test_multiple_disable_uuids(self): + channel_names = list(multiple_disable_uuids['payload']['channels']) + response = PNHereNowResult.from_json(multiple_disable_uuids, channel_names) + channels = response.channels + + assert response.total_channels == 2 + assert response.total_occupancy == 2 + assert isinstance(channels, list) + assert len(channels) == 2 + + channel1 = channels[0] + + assert channel1.channel_name == channel_names[0] + assert channel1.occupancy == multiple_disable_uuids['payload']['channels'][channel_names[0]]['occupancy'] + assert channel1.occupants is None + + channel2 = channels[1] + + assert channel2.channel_name == channel_names[1] + assert channel2.occupancy == multiple_disable_uuids['payload']['channels'][channel_names[1]]['occupancy'] + assert channel2.occupants is None + + def test_multiple_with_state(self): + channel_names = list(reversed(sorted(list(multiple_with_state['payload']['channels'])))) + response = PNHereNowResult.from_json(multiple_with_state, channel_names) + channels = response.channels + + assert response.total_channels == 2 + assert response.total_occupancy == 2 + assert isinstance(channels, list) + assert len(channels) == 2 + + if channels[0].channel_name == channel_names[0]: + channel1 = channels[0] + channel2 = channels[1] + else: + channel1 = channels[1] + channel2 = channels[0] + + assert channel1.channel_name == channel_names[0] + assert channel1.occupancy == multiple_with_state['payload']['channels'][channel1.channel_name]['occupancy'] + assert len(channel1.occupants) == 1 + + occupants = channel1.occupants[0] + + assert occupants.state['name'] == "Alex" + assert occupants.state['count'] == 5 + assert occupants.uuid == multiple_with_state['payload']['channels'][channel1.channel_name]['uuids'][0]['uuid'] + + assert channel2.channel_name == channel_names[1] + assert channel2.occupancy == multiple_with_state['payload']['channels'][channel2.channel_name]['occupancy'] + assert len(channel2.occupants) == 1 + + occupants = channel2.occupants[0] + + assert occupants.state is None + assert occupants.uuid == multiple_with_state['payload']['channels'][channel2.channel_name]['uuids'][0]['uuid'] diff --git a/tests/unit/test_time.py b/tests/unit/test_time.py new file mode 100644 index 00000000..0b6fb5f1 --- /dev/null +++ b/tests/unit/test_time.py @@ -0,0 +1,18 @@ +import unittest + +from datetime import date + +from pubnub.models.consumer.time import PNTimeResponse + + +class TestTime(unittest.TestCase): + def test_parse(self): + time = PNTimeResponse([14695274331639244]) + + assert int(time) == 14695274331639244 + assert time.value_as_int == 14695274331639244 + + assert str(time) == "14695274331639244" + assert time.value_as_string == "14695274331639244" + + assert isinstance(time.date_time(), date) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py new file mode 100644 index 00000000..109df187 --- /dev/null +++ b/tests/unit/test_utils.py @@ -0,0 +1,78 @@ +import unittest + +from pubnub import utils +from pubnub.utils import build_url + +try: + from urllib.parse import urlparse +except ImportError: + from urlparse import urlparse + +try: + from urllib.parse import parse_qs +except ImportError: + from urlparse import parse_qs + + +class TestWriteValueAsString(unittest.TestCase): + def test_string(self): + assert utils.write_value_as_string("blah") == "\"blah\"" + assert utils.write_value_as_string(u"blah") == "\"blah\"" + + def test_bool(self): + assert utils.write_value_as_string(False) == "false" + assert utils.write_value_as_string(True) == "true" + + def test_list(self): + assert utils.write_value_as_string(["ch1", "ch2"]) == "[\"ch1\", \"ch2\"]" + + def test_tuple(self): + assert utils.write_value_as_string(("ch1", "ch2")) == "[\"ch1\", \"ch2\"]" + + +class TestUUID(unittest.TestCase): + def test_uuid(self): + assert isinstance(utils.uuid(), str) + assert len(utils.uuid()) == 36 + + +class TestBuildUrl(unittest.TestCase): + def test_build_url(self): + def match(expected_str, actual_str): + expected = urlparse(expected_str) + actual = urlparse(actual_str) + assert expected.scheme == actual.scheme + assert expected.netloc == actual.netloc + assert expected.path == actual.path + self.assertEqual(parse_qs(expected.query), parse_qs(actual.query)) + + match("http://ex.com/news?a=2&b=qwer", + build_url("http", "ex.com", "/news", "a=2&b=qwer")) + match("https://ex.com/?a=2&b=qwer", + build_url("https", "ex.com", "/", "a=2&b=qwer")) + + +class TestJoin(unittest.TestCase): + def test_join_items_and_encode(self): + assert "a%2Fb,c%20d" == utils.join_items_and_encode(['a/b', 'c d']) + + +class TestPreparePAMArguments(unittest.TestCase): + def test_prepare_pam_arguments(self): + params = { + 'abc': True, + 'poq': 4, + 'def': False + } + + result = utils.prepare_pam_arguments(params) + assert result == 'abc=True&def=False&poq=4' + + def test_sign_sha_256(self): + input = """sub-c-7ba2ac4c-4836-11e6-85a4-0619f8945a4f +pub-c-98863562-19a6-4760-bf0b-d537d1f5c582 +grant +channel=asyncio-pam-FI2FCS0A&pnsdk=PubNub-Python-Asyncio%252F4.0.4&r=1×tamp=1468409553&uuid=a4dbf92e-e5cb-428f-b6e6-35cce03500a2&w=1""" # noqa: E501 + result = utils.sign_sha256("my_key", input) + + assert "zXtkplNSczgpsfhaYajoEfQnIgRTUCgE9AE6Y0mS_J8=" == result diff --git a/tests/unit/test_vcr_helper.py b/tests/unit/test_vcr_helper.py new file mode 100644 index 00000000..1ec1cfc1 --- /dev/null +++ b/tests/unit/test_vcr_helper.py @@ -0,0 +1,36 @@ +import unittest + +from tests.integrational.vcr_helper import string_list_in_path_matcher, string_list_in_query_matcher + + +class Request(object): + def __init__(self, path=None, query=None): + self.path = path + self.query = query + + +class TestVCRMatchers(unittest.TestCase): + def test_string_list_in_path_matcher(self): + r1 = Request('/v2/presence/sub-key/my_sub_key/channel/ch1,ch2') + r2 = Request('/v2/presence/sub-key/my_sub_key/channel/ch2,ch1') + r3 = Request('/v2/presence/sub-key/my_sub_key/channel/ch2,ch3') + r4 = Request( + '/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-asyncio-ch2,test-here-now-asyncio-ch1/0') # noqa: E501 + r5 = Request( + '/v2/subscribe/sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe/test-here-now-asyncio-ch1,test-here-now-asyncio-ch2/0') # noqa: E501 + + assert string_list_in_path_matcher(r1, r2, 6) + assert not string_list_in_path_matcher(r2, r3, 6) + assert string_list_in_path_matcher(r4, r5, 4) + assert not string_list_in_path_matcher(r4, r5) + + def test_string_list_in_path_query_matcher(self): + r1 = Request( + query=[('channel', 'test-pam-asyncio-ch1,test-pam-asyncio-ch2'), ('pnsdk', 'PubNub-Python-Asyncio/4.0.4'), + ('r', '1'), ('uuid', 'test-pam-asyncio-uuid'), ('w', '1')]) + r2 = Request( + query=[('channel', 'test-pam-asyncio-ch2,test-pam-asyncio-ch1'), ('pnsdk', 'PubNub-Python-Asyncio/4.0.4'), + ('r', '1'), ('uuid', 'test-pam-asyncio-uuid'), ('w', '1')]) + + assert string_list_in_query_matcher(r1, r2, ['channel']) + assert not string_list_in_query_matcher(r1, r2)