[Checkins] SVN: z3c.jsonrpc/trunk/ Added initial implementation
Roger Ineichen
roger at projekt01.ch
Fri Nov 30 10:52:56 EST 2007
Log message for revision 82062:
Added initial implementation
Changed:
A z3c.jsonrpc/trunk/CHANGES.txt
A z3c.jsonrpc/trunk/README.txt
A z3c.jsonrpc/trunk/bootstrap.py
A z3c.jsonrpc/trunk/buildout.cfg
A z3c.jsonrpc/trunk/setup.py
A z3c.jsonrpc/trunk/src/z3c/__init__.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/README.txt
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/SETUP.cfg
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/__init__.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/browser.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/configure.zcml
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/ftesting.zcml
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/interfaces.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/layer.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/meta.zcml
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/namespace.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/publication.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/publisher.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/testing.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/__init__.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/page.pt
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_doc.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_factory.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_request.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-configure.zcml
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-meta.zcml
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.py
A z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.txt
-=-
Added: z3c.jsonrpc/trunk/CHANGES.txt
===================================================================
--- z3c.jsonrpc/trunk/CHANGES.txt (rev 0)
+++ z3c.jsonrpc/trunk/CHANGES.txt 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,8 @@
+=======
+CHANGES
+=======
+
+Version 0.5.0 (unreleased)
+-------------------------
+
+- Initial Release
Property changes on: z3c.jsonrpc/trunk/CHANGES.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/README.txt
===================================================================
--- z3c.jsonrpc/trunk/README.txt (rev 0)
+++ z3c.jsonrpc/trunk/README.txt 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1 @@
+This package provides an JSON-RPC server implementation for Zope3.
Property changes on: z3c.jsonrpc/trunk/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/bootstrap.py
===================================================================
--- z3c.jsonrpc/trunk/bootstrap.py (rev 0)
+++ z3c.jsonrpc/trunk/bootstrap.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 75940 2007-05-24 14:45:00Z srichter $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+ ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+ cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+ os.P_WAIT, sys.executable, sys.executable,
+ '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+ dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
Property changes on: z3c.jsonrpc/trunk/bootstrap.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/buildout.cfg
===================================================================
--- z3c.jsonrpc/trunk/buildout.cfg (rev 0)
+++ z3c.jsonrpc/trunk/buildout.cfg 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,16 @@
+[buildout]
+develop = .
+ externals/z3c.json
+parts = test checker coverage
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.jsonrpc [test]
+
+[checker]
+recipe = lovely.recipe:importchecker
+path = src/z3c/jsonrpc
+
+[coverage]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
Added: z3c.jsonrpc/trunk/setup.py
===================================================================
--- z3c.jsonrpc/trunk/setup.py (rev 0)
+++ z3c.jsonrpc/trunk/setup.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,80 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Setup
+
+$Id:$
+"""
+import os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+ return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+setup (
+ name='z3c.jsonrpc',
+ version='0.5.0dev',
+ author = "Roger Ineichen and the Zope Community",
+ author_email = "zope3-dev at zope.org",
+ description = "JSON RPC server and client implementation for Zope3",
+ long_description=(
+ read('README.txt')
+ + '\n\n' +
+ read('CHANGES.txt')
+ ),
+ license = "ZPL 2.1",
+ keywords = "zope3 z3c json rpc json-rpc server client",
+ classifiers = [
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Zope Public License',
+ 'Programming Language :: Python',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Framework :: Zope3'],
+ url = 'http://cheeseshop.python.org/pypi/z3c.jsonrpc',
+ packages = find_packages('src'),
+ include_package_data = True,
+ package_dir = {'':'src'},
+ namespace_packages = ['z3c'],
+ extras_require = dict(
+ test = [
+ 'z3c.coverage',
+ 'z3c.layer',
+ 'zope.app.testing',
+ 'zope.testbrowser',
+ 'zope.testing',
+ ],
+ ),
+ install_requires = [
+ 'setuptools',
+ 'z3c.json',
+ 'zope.app.component',
+ 'zope.app.publication',
+ 'zope.app.twisted',
+ 'zope.app.wsgi',
+ 'zope.component',
+ 'zope.configuration',
+ 'zope.i18n',
+ 'zope.interface',
+ 'zope.location',
+ 'zope.publisher',
+ 'zope.schema',
+ 'zope.security',
+ 'zope.securitypolicy',
+ 'zope.traversing',
+ ],
+ zip_safe = False,
+)
Property changes on: z3c.jsonrpc/trunk/setup.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/__init__.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/__init__.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/__init__.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+ import pkg_resources
+ pkg_resources.declare_namespace(__name__)
+except ImportError:
+ import pkgutil
+ __path__ = pkgutil.extend_path(__path__, __name__)
Property changes on: z3c.jsonrpc/trunk/src/z3c/__init__.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/README.txt
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/README.txt (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/README.txt 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,273 @@
+=======
+JSONRPC
+=======
+
+JSON is javascript object notation. JSON-RPC performs the same service
+as XML-RPC, except the transport is JSON instead of XML.
+
+Many thanks to Jim Washington for the work on zif.jsonserver. This project uses
+many code writen by Jim. I implemented an additional python JSONRPC proxy which
+can comunicate with the server. This means we can use this library for call
+JSON from python to python. The JSON-RPC proxy uses similar patterns like the
+XML-RPC implementation.
+
+There is also a additional xmlhttp and json javascript implementation wich
+offers a JSON-RPC proxy implementation for JavaScript.
+
+This project provides the proposed request type "application/json". The request
+type "application/json-rpc" is supported as long it is not officialy deprecated.
+
+The goal of this project is to provide a JSON-RPC implementation. Simply
+Browser views wihch handle JSON calls with a BrowserRequest are not supported
+by this package. I'm still not sure if this is good or bad and in which
+direction I will go with this package.
+
+Some of my goals are right now, but can change in the future if I'll understand
+all the concepts arround JSON, e.g. JSPON, JSONP, CrossSite etc:
+
+- provide a secure way to handle JSON calls from client to server.
+ I hope we can implement JSONRequest some days. CrosSite seems to use a
+ intereting concept
+
+- Simple pythonic implementation
+
+- Use together with JQuery (see http://www.jquery.org).
+
+- No other dependency then JQuery and basic zope packages.
+
+- well tested (this is not the case for JavaScript right now)
+
+
+About JSON
+----------
+
+See www.json.org for more information about JSON.
+
+See http://json-rpc.org/wd/JSON-RPC-1-1-WD-20060807.html for more information
+about the JSON 1.1 specification.
+
+
+What this package can't do
+--------------------------
+
+JSON and this package have different limitations. This package can right now
+not handle the following tasks:
+
+- Handle fileupload
+
+- Handle GET request
+
+Note that the JSONRPCRequest implementation is based on the IHTTPRequest, this
+means that there is no other browser page available if you call them in
+python, e.g. getMultiAdapter((context, request), name='myViewName'). This is
+explicit done this way. If you like to use content form such browser pages
+in a JSON request/call, you can inherit your skin form IJSONRPCLayer and
+IBrowserRequest and register your views for this custom layer.
+
+
+JSON-RPC server
+---------------
+
+The JSON server looks for content-type "application/json", and handles those
+requests as JSON-RPC. The official mime-type for JSON is "application/json"
+Old content types ``application/json-rpc`` are supported too.
+
+Let's define a content object:
+
+ >>> import zope.interface
+ >>> class IDemoContent(zope.interface.Interface):
+ ... """Demo content interface."""
+
+ >>> import persistent
+ >>> class DemoContent(persistent.Persistent):
+ ... """Demo content."""
+ ... zope.interface.implements(IDemoContent)
+
+And define a JSONRPC method view:
+
+ >>> from z3c.jsonrpc import publisher
+ >>> class DemoView(publisher.MethodPublisher):
+ ... """Sample JSON view."""
+ ...
+ ... def hello(self):
+ ... return u"Hello World"
+ ...
+ ... def greeting(self, name):
+ ... return u"Hello %s" % name
+ ...
+ ... def kwarguments(self, prefix, foo=None, bar=None):
+ ... # Note; keyword arguments can be found in request.form
+ ... return u"%s %s %s" % (prefix, foo, bar)
+ ...
+ ... def showId(self):
+ ... return u"The json id is: %s" % self.request.jsonId
+ ...
+ ... def forceValueError(self):
+ ... raise ValueError('Something was wrong in server method.')
+
+Make them available under the fake package ``jsonsamples``:
+
+ >>> import sys
+ >>> sys.modules['custom'] = type('Module', (), {})()
+ >>> sys.modules['custom'].IDemoContent = IDemoContent
+ >>> sys.modules['custom'].DemoContent = DemoContent
+ >>> sys.modules['custom'].DemoView = DemoView
+
+Let's show how we can register a jsonrpc view:
+
+ >>> from zope.configuration import xmlconfig
+ >>> import z3c.jsonrpc
+ >>> context = xmlconfig.file('meta.zcml', z3c.jsonrpc)
+ >>> context = xmlconfig.string("""
+ ... <configure
+ ... xmlns:z3c="http://namespaces.zope.org/z3c">
+ ... <z3c:jsonrpc
+ ... for="custom.IDemoContent"
+ ... class="custom.DemoView"
+ ... permission="zope.Public"
+ ... methods="hello greeting kwarguments showId forceValueError"
+ ... layer="z3c.jsonrpc.testing.IJSONRPCTestSkin"
+ ... />
+ ... </configure>
+ ... """, context)
+
+Now we will setup a content object in our site:
+
+ >>> site = getRootFolder()
+ >>> content = DemoContent()
+ >>> site['content'] = content
+
+Now we can call the method from our JSONRPC view:
+
+ >>> from z3c.jsonrpc import testing
+ >>> request = testing.TestRequest()
+ >>> demoView = DemoView(content, request)
+ >>> demoView.hello()
+ u'Hello World'
+
+But this is not intuitive. Let's see how we can traverse to the method ``hello``
+with the traverser:
+
+ >>> from z3c.jsonrpc.publisher import MethodTraverser
+ >>> methodTraverser = MethodTraverser(demoView, request)
+ >>> methodTraverser.publishTraverse(request, 'hello')()
+ u'Hello World'
+
+Now we try to access the JSON-RPC view method with a test browser. As you can
+see, there is no view accessible. This is because the JSONRPC view is not a
+browser view and is not traversable. The error shows that the request factory
+falls back to the browser request factory:
+
+ >>> from zope.testbrowser.testing import Browser
+ >>> browser = Browser()
+ >>> browser.handleErrors = False
+ >>> browser.addHeader('Accept-Language', 'en')
+ >>> browser.addHeader('Content-Type', 'application/json')
+ >>> siteURL = 'http://localhost/++skin++JSONRPCTestSkin'
+ >>> browser.open(siteURL + '/content/hello')
+ Traceback (most recent call last):
+ ...
+ NotFound: Object: <zope.app.folder.folder.Folder...: u'++skin++JSONRPCTestSkin'
+
+
+Testing
+-------
+
+If you need to test a JSONRPC view you can use the test proxy like shown
+below in the ``JSON-RPC proxy`` section.
+
+
+JSON-RPC proxy
+--------------
+
+The jsonrpc package provides also a JSON-RPC proxy implementation. This
+implementation is similar to the one known from xmlrpclib except that it can
+handle JSON instead of XML.
+
+Let's try to call our method called ``hello`` we defined before:
+
+ >>> from z3c.jsonrpc.testing import JSONRPCTestProxy
+ >>> proxy = JSONRPCTestProxy(siteURL + '/content')
+ >>> proxy.hello()
+ u'Hello World'
+
+Now let's make a remote procedure call with a argument:
+
+ >>> proxy.greeting(u'Jessy')
+ u'Hello Jessy'
+
+Now let's make a remote procedure call with a kw arguments. Note that this
+key word arguments are stored in the request.form. But this doesn't change
+anything because this varaibles are also accessible like form variables e.g.
+self.request['foo'].Also note that we don't support the **kw signature:
+
+ >>> proxy.kwarguments('Hello', foo=u'FOO', bar=u'BAR')
+ u'Hello FOO BAR'
+
+There is also a ``id`` in the json response. Let's use such a json request id
+in our JSONRPCProxy:
+
+ >>> proxy = JSONRPCTestProxy(siteURL + '/content', jsonId = u'my id')
+ >>> proxy.showId()
+ u'The json id is: my id'
+
+The proxy also knows this id as jsonId:
+
+ >>> proxy.jsonId
+ u'my id'
+
+See what's happen if the server raises a Exception:
+
+ >>> proxy.forceValueError()
+ Traceback (most recent call last):
+ ...
+ ResponseError: Check proxy.error for error message
+
+and the error message is:
+
+ >>> proxy.error
+ u'ValueError: Something was wrong in server method.'
+
+And now we force a ResponseError with a fake JSONReader. But first we
+need to replace our IJSONReader utility:
+
+ >>> from z3c.json.interfaces import IJSONReader
+ >>> sm = site.getSiteManager()
+ >>> fakeJSONReader = testing.ForceResponseErrorJSONReader()
+ >>> sm.registerUtility(fakeJSONReader, IJSONReader)
+
+also setup the site hook:
+
+ >>> from zope.app.component import hooks
+ >>> hooks.setSite(site)
+
+and just call a method this will now raise a ResponseError:
+
+ >>> proxy = JSONRPCTestProxy(siteURL + '/content')
+ >>> proxy.hello()
+ Traceback (most recent call last):
+ ...
+ ResponseError: Unacceptable JSON expression: {"id":"jsonrpc", "method":"hello", "no-params"}
+
+the error message is stored in the proxy too:
+
+ >>> proxy.error
+ u'Unacceptable JSON expression: {"id":"jsonrpc", "method":"hello", "no-params"}'
+
+
+Transport
+~~~~~~~~~
+
+We used the JSONRPCTestProxy here for testing. This JSON-RPC proxy is a wrapper
+for the original JSONRPCProxy and adds handleErrors support and a special
+Transport layer which uses a testing caller. You can use one of the different
+Transport layers defined in the z3c.json.transport module in real usecases
+together with the default JSONRPCProxy implementation.
+
+
+cleanup
+-------
+
+Now we need to clean up the custom module.
+
+ >>> del sys.modules['custom']
\ No newline at end of file
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/SETUP.cfg
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/SETUP.cfg (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/SETUP.cfg 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,3 @@
+<data-files zopeskel/etc/package-includes>
+ z3c.jsonrpc-*.zcml
+</data-files>
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/__init__.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/__init__.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/__init__.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1 @@
+# Make a package.
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/__init__.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/browser.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/browser.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/browser.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,34 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+
+import zope.component
+from zope.publisher.interfaces import NotFound
+from z3c.jsonrpc.interfaces import IMethodPublisher
+
+
+class JSONRPCTraversablePage(object):
+ """Mixin for JSON-RPC traversable views, pages, forms etc.
+
+ Make sure if you inherit from this class that no other super class
+ will override the publishTraverse method.
+ """
+
+ def publishTraverse(self, request, name):
+ view = zope.component.queryMultiAdapter((self, request), name=name)
+ if view is None or not IMethodPublisher.providedBy(view):
+ raise NotFound(self, name, request)
+ return view
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/browser.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/configure.zcml
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/configure.zcml (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/configure.zcml 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,70 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="zope">
+
+
+ <interface
+ interface="z3c.jsonrpc.layer.IJSONRPCLayer"
+ type="zope.publisher.interfaces.browser.IBrowserSkinType"
+ />
+
+
+ <!-- JSON-RPC server -->
+ <publisher
+ name="Z3C JSONRPC"
+ factory=".publication.JSONRPCFactory"
+ methods="POST"
+ mimetypes="application/json application/json-rpc"
+ priority="30"
+ />
+
+ <!-- publisher -->
+ <view
+ for="zope.interface.Interface"
+ type=".interfaces.IJSONRPCRequest"
+ provides=".interfaces.IJSONRPCPublisher"
+ factory="zope.app.publication.traversers.SimpleComponentTraverser"
+ permission="zope.Public"
+ />
+
+ <view
+ for="zope.app.container.interfaces.IItemContainer"
+ type=".interfaces.IJSONRPCRequest"
+ provides=".interfaces.IJSONRPCPublisher"
+ factory="zope.app.container.traversal.ItemTraverser"
+ permission="zope.Public"
+ />
+
+ <view
+ for="zope.app.container.interfaces.IReadContainer"
+ type=".interfaces.IJSONRPCRequest"
+ provides=".interfaces.IJSONRPCPublisher"
+ factory="zope.app.container.traversal.ContainerTraverser"
+ permission="zope.Public"
+ />
+
+ <view
+ for=".interfaces.IMethodPublisher"
+ type=".interfaces.IJSONRPCRequest"
+ provides=".interfaces.IJSONRPCPublisher"
+ factory=".publisher.MethodTraverser"
+ permission="zope.Public"
+ />
+
+
+ <!-- skin namespace -->
+ <view
+ name="skin" type="z3c.jsonrpc.interfaces.IJSONRPCRequest"
+ provides="zope.traversing.interfaces.ITraversable" for="*"
+ factory=".namespace.skin"
+ />
+
+
+ <!-- error handling -->
+ <defaultView
+ for="zope.interface.common.interfaces.IException"
+ type=".layer.IJSONRPCLayer"
+ name="error"
+ />
+
+</configure>
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/ftesting.zcml
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/ftesting.zcml (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/ftesting.zcml 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,72 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:meta="http://namespaces.zope.org/meta"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ xmlns:z3c="http://namespaces.zope.org/z3c"
+ i18n_domain="zope">
+
+ <include package="zope.i18n" file="meta.zcml" />
+ <include package="zope.app.component" file="meta.zcml" />
+ <include package="zope.app.component.browser" file="meta.zcml" />
+ <include package="zope.app.container.browser" file="meta.zcml" />
+ <include package="zope.app.form.browser" file="meta.zcml" />
+ <include package="zope.app.pagetemplate" file="meta.zcml" />
+ <include package="zope.app.publication" file="meta.zcml" />
+ <include package="zope.app.publisher" file="meta.zcml" />
+ <include package="zope.app.security" file="meta.zcml" />
+ <include package="zope.securitypolicy" file="meta.zcml" />
+
+ <!-- uhh, remove this later -->
+ <browser:menu id="zmi_views" title="Views" />
+ <browser:menu id="zmi_actions" title="Actions" />
+
+ <include package="zope.annotation" />
+ <include package="zope.app.appsetup" />
+ <include package="zope.app.component" />
+ <include package="zope.app.container" />
+ <include package="zope.app.error" />
+ <include package="zope.app.folder" />
+ <include package="zope.app.i18n" />
+ <include package="zope.app.publication" />
+ <include package="zope.app.publisher.browser" />
+ <include package="zope.app.publisher" />
+ <include package="zope.app.security" />
+ <include package="zope.app.session" />
+ <include package="zope.app.twisted" />
+ <include package="zope.app.wsgi" />
+ <include package="zope.component" />
+ <include package="zope.contentprovider" />
+ <include package="zope.location" />
+ <include package="zope.publisher" />
+ <include package="zope.traversing" />
+ <include package="zope.traversing.browser" />
+
+ <securityPolicy
+ component="zope.securitypolicy.zopepolicy.ZopeSecurityPolicy"
+ />
+
+ <role id="zope.Manager" title="Manager" />
+ <grantAll role="zope.Manager" />
+ <grantAll role="zope.Anonymous" />
+
+ <principal
+ id="zope.manager"
+ title="Manager"
+ login="Manager"
+ password="password"
+ />
+
+ <grant
+ role="zope.Manager"
+ principal="zope.manager"
+ />
+
+ <include package="z3c.json" />
+ <include package="z3c.jsonrpc" />
+
+ <interface
+ interface="z3c.jsonrpc.testing.IJSONRPCTestSkin"
+ type="z3c.jsonrpc.interfaces.IJSONRPCSkinType"
+ name="JSONRPCTestSkin"
+ />
+
+</configure>
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/ftesting.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/interfaces.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/interfaces.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/interfaces.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,63 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+from zope.interface.interfaces import IInterface
+from zope.publisher.interfaces import IPublishTraverse
+from zope.publisher.interfaces.http import IHTTPApplicationRequest
+from zope.publisher.interfaces.http import IHTTPCredentials
+#from zope.publisher.interfaces.xmlrpc import IXMLRPCPublication
+from zope.publisher.interfaces import IPublication
+from zope.app.publication.interfaces import IRequestFactory
+from zope.publisher.interfaces.http import IHTTPRequest
+
+JSON_CHARSETS = ('utf-8','utf-16', 'utf-32')
+PYTHON_KW_MARKER = "pythonKwMaRkEr"
+
+
+class IMethodPublisher(zope.interface.Interface):
+ """Marker interface for an object that wants to publish methods."""
+
+
+class IJSONRPCRequestFactory(IRequestFactory):
+ """Browser request factory"""
+
+
+class IJSONRPCPublisher(IPublishTraverse):
+ """JSON-RPC Publisher
+ like zope.publisher.interfaces.xmlrpc.IXMLRPCPublisher
+ """
+
+
+class IJSONRPCPublication(IPublication):
+ """Publication for JOSN-RPC-based protocol."""
+
+
+class IJSONRPCSkinType(IInterface):
+ """A skin is a set of layers."""
+
+
+class IJSONRPCApplicationRequest(IHTTPApplicationRequest):
+ """HTTP application request."""
+
+
+class IJSONRPCRequest(IJSONRPCApplicationRequest, IHTTPCredentials,
+ IHTTPRequest):
+ """JSON-RPC request."""
+
+ jsonID = zope.interface.Attribute("""JSON-RPC ID for the request""")
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/interfaces.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/layer.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/layer.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/layer.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,35 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+
+from zope.publisher.interfaces import browser
+from z3c.jsonrpc import interfaces
+
+
+class IJSONRPCLayer(interfaces.IJSONRPCRequest):
+ """Layer for JSONRPC requests.
+
+ IMPORTANT:
+ If you like to use ZPT templates and call adatapers in MethodPublisher
+ views, you need to make sure that your layer provides the relevant
+ adapters. This means, you will probably use a layer inherited from
+ IBrowserRequest and IJSONRPCRequest in your skin.
+
+ Or you can register your icons etc. for the IJSONRequest layer etc. if you
+ need to return content produced by ZPT tempaltes. Also note that icons
+ or other resources are only available in IBrowserRequest or in your custom
+ layer.
+ """
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/layer.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/meta.zcml
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/meta.zcml (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/meta.zcml 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,14 @@
+<configure
+ xmlns:meta="http://namespaces.zope.org/meta"
+ xmlns="http://namespaces.zope.org/zope">
+
+ <meta:directive
+ namespace="http://namespaces.zope.org/z3c"
+ name="jsonrpc"
+ schema=".zcml.IJSONRPCDirective"
+ handler=".zcml.jsonrpc"
+ />
+
+ <meta:provides feature="z3c.jsonrpc" />
+
+</configure>
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/meta.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/namespace.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/namespace.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/namespace.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,24 @@
+# Make a package.
+
+import zope.component
+import zope.traversing.namespace
+from zope.component.interfaces import ComponentLookupError
+from zope.publisher.http import applySkin
+from zope.traversing.interfaces import TraversalError
+
+from z3c.jsonrpc import interfaces
+
+
+class skin(zope.traversing.namespace.skin):
+ """JSONRPC skin type interface."""
+
+ skin_type = interfaces.IJSONRPCSkinType
+
+ def traverse(self, name, ignored):
+ self.request.shiftNameToApplication()
+ try:
+ skin = zope.component.getUtility(self.skin_type, name)
+ except ComponentLookupError:
+ raise TraversalError("++skin++%s" % name)
+ applySkin(self.request, skin, self.skin_type)
+ return self.context
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/namespace.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/publication.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/publication.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/publication.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,45 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+
+from zope.app.publication.http import BaseHTTPPublication
+from zope.app.publication.interfaces import IRequestPublicationFactory
+
+from z3c.jsonrpc import interfaces
+from z3c.jsonrpc.publisher import JSONRPCRequest
+
+
+class JSONRPCPublication(BaseHTTPPublication):
+ """JSON RPC Publication."""
+
+ zope.interface.implements(interfaces.IJSONRPCPublication)
+
+
+
+class JSONRPCFactory(object):
+ zope.interface.implements(IRequestPublicationFactory)
+
+ def canHandle(self, environment):
+ return True
+
+ def __call__(self):
+ request_class = zope.component.queryUtility(
+ interfaces.IJSONRPCRequestFactory, default=JSONRPCRequest)
+ return request_class, JSONRPCPublication
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/publication.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/publisher.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/publisher.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/publisher.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,268 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import traceback
+import logging
+
+import zope.interface
+import zope.component
+from zope.location.location import Location
+from zope.i18n.interfaces import IUserPreferredCharsets
+from zope.publisher.http import HTTPRequest
+from zope.publisher.http import HTTPResponse
+from zope.publisher.http import getCharsetUsingRequest
+from zope.security.proxy import isinstance
+
+from z3c.json.interfaces import IJSONReader
+from z3c.json.interfaces import IJSONWriter
+from z3c.json.converter import premarshal
+from z3c.jsonrpc import interfaces
+from z3c.jsonrpc.interfaces import JSON_CHARSETS
+from z3c.jsonrpc.interfaces import PYTHON_KW_MARKER
+
+DEBUG = logging.DEBUG
+logger = logging.getLogger()
+
+
+def intsort(item):
+ return int(item[0])
+
+
+class MethodPublisher(Location):
+ """Base class for JSON-RPC views that publish methods
+ like zope.app.publisher.xmlrpc.MethodPublisher
+ """
+ zope.interface.implements(interfaces.IMethodPublisher)
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+ def __getParent(self):
+ return hasattr(self, '_parent') and self._parent or self.context
+
+ def __setParent(self, parent):
+ self._parent = parent
+
+ __parent__ = property(__getParent, __setParent)
+
+
+class MethodTraverser(object):
+ zope.interface.implements(interfaces.IJSONRPCPublisher)
+
+ __used_for__ = interfaces.IMethodPublisher
+
+ def __init__(self, context, request):
+ self.context = context
+
+ def publishTraverse(self, request, name):
+ return getattr(self.context, name)
+
+
+class JSONRPCRequest(HTTPRequest):
+ """JSON-RPC request implementation based on IHTTPRequest."""
+
+ _jsonId = 'jsonrpc'
+ jsonId = None
+
+ zope.interface.implements(interfaces.IJSONRPCRequest,
+ interfaces.IJSONRPCApplicationRequest)
+
+ def __init__(self, body_instream, environ, response=None):
+ self.form = {}
+ self._args = ()
+ self.charsets = None
+ super(JSONRPCRequest, self).__init__(body_instream, environ, response)
+
+ def _createResponse(self):
+ """return a response"""
+ return JSONRPCResponse()
+
+ def _decode(self, text):
+ """Try to decode the text using one of the available charsets."""
+ if self.charsets is None:
+ envadapter = IUserPreferredCharsets(self)
+ self.charsets = envadapter.getPreferredCharsets() or ['utf-8']
+ for charset in self.charsets:
+ try:
+ text = unicode(text, charset)
+ break
+ except UnicodeError:
+ pass
+ return text
+
+ def processInputs(self):
+ """take the converted request and make useful args of it."""
+ json = zope.component.getUtility(IJSONReader)
+ stream = self._body_instream
+ input = []
+ incoming = stream.read(1000)
+ while incoming:
+ input.append(incoming)
+ incoming = stream.read(1000)
+ input = ''.join(input)
+ # ensure unicode
+ if not isinstance(input, unicode):
+ input = self._decode(input)
+ data = json.read(input)
+ if self.jsonId is None:
+ self.jsonId = data.get('id', self._jsonId)
+ params = data['params']
+
+ if isinstance(params, list):
+ # json-rpc 1.0
+ args = params
+ # now, look for keyword parameters, the old way
+ kwargs = None
+ notPositional = []
+ for k in args:
+ if isinstance(k, dict):
+ if k.has_key(PYTHON_KW_MARKER):
+ if isinstance(k[PYTHON_KW_MARKER], dict):
+ j = k[PYTHON_KW_MARKER]
+ kwargs = j
+ notPositional.append(k)
+ if notPositional:
+ for k in notPositional:
+ args.remove(k)
+ if kwargs:
+ for m in kwargs.keys():
+ self.form[str(m)] = kwargs[m]
+ elif isinstance(params, dict):
+ # Note: the JSONRPCProxy uses allways a dict for params. This means
+ # we only use this part for extract the data.
+ # json-rpc 1.1 (to be proposed)
+ # get the numeric params for positional params
+ temp_positional = []
+ for key in params:
+ if str(key).isdigit():
+ temp_positional.append((key, params[key]))
+ temp_positional.sort(key=intsort)
+ args = []
+ # make args from positional args and remove them from params
+ for item in temp_positional:
+ args.append(item[1])
+ del params[item[0]]
+ # drop remaining named params into request.form
+ for named_param in params:
+ # named_param is unicode; python needs string for param names
+ self.form[str(named_param)] = params[named_param]
+ else:
+ raise TypeError, 'Unsupported type for JSON-RPC "params" (%s)' \
+ % type(params)
+ self._args = tuple(args)
+ # make environment, cookies, etc., available to request.get()
+ super(JSONRPCRequest,self).processInputs()
+ self._environ['JSONRPC_MODE'] = True
+
+ # split here on '.' for get path suffix steps
+ functionstr = data['method']
+ function = functionstr.split('.')
+ if function:
+ # translate '.' to '/' in function to represent object traversal.
+ self.setPathSuffix(function)
+
+ def traverse(self, object):
+ return super(JSONRPCRequest, self).traverse(object)
+
+ def keys(self):
+ """See Interface.Common.Mapping.IEnumerableMapping."""
+ d = {}
+ d.update(self._environ)
+ d.update(self._cookies)
+ d.update(self.form)
+ return d.keys()
+
+ def get(self, key, default=None):
+ """See Interface.Common.Mapping.IReadMapping."""
+ marker = object()
+ result = self.form.get(key, marker)
+ if result is not marker:
+ return result
+ return super(JSONRPCRequest, self).get(key, default)
+
+ def __getitem__(self,key):
+ return self.get(key)
+
+
+class JSONRPCResponse(HTTPResponse):
+ """JSON-RPC Response"""
+
+
+ def setResult(self, result):
+ """The result dict contains the following key value pairs
+
+ id -- json request id
+ result -- result or null on error
+ error -- error or null if result is Ok
+
+ """
+ id = self._request.jsonId
+ if id is not None:
+ result = premarshal(result)
+ wrapper = {'id': id}
+ wrapper['result'] = result
+ wrapper['error'] = None
+ json = zope.component.getUtility(IJSONWriter)
+ encoding = getCharsetUsingRequest(self._request)
+ result = json.write(wrapper)
+ body = self._prepareResult(result)
+ super(JSONRPCResponse,self).setResult(body)
+ logger.log(DEBUG, "%s" % result)
+ else:
+ self.setStatus(204)
+ super(JSONRPCResponse,self).setResult('')
+
+ def _prepareResult(self, result):
+ # we've asked json to return unicode; result should be unicode
+ encoding = getCharsetUsingRequest(self._request) or 'utf-8'
+ enc = encoding.lower()
+ if not enc in JSON_CHARSETS:
+ encoding = 'utf-8'
+ # encode outgoing boundary.
+ if isinstance(result, unicode):
+ body = result.encode(encoding)
+ charset = encoding
+ else:
+ # something's wrong. JSON did not return unicode.
+ raise TypeError, "JSON did not return unicode (%s)" % type(result)
+
+ # set content type
+ self.setHeader('content-type', "application/x-javascript;charset=%s" \
+ % charset)
+ return body
+
+ def handleException(self, exc_info):
+ t, value = exc_info[:2]
+ exc_data = []
+ for file, lineno, function, text in traceback.extract_tb(exc_info[2]):
+ exc_data.append("%s %s %s %s %s" % (file, "line",
+ lineno, "in", function))
+ exc_data.append("%s %s" % ( "=>", repr(text)))
+ exc_data.append( "** %s: %s" % exc_info[:2])
+ logger.log(logging.ERROR, "\n".join(exc_data))
+ s = '%s: %s' % (getattr(t, '__name__', t), value)
+ wrapper = {'id': self._request.jsonId}
+ wrapper['result'] = None
+ wrapper['error'] = s
+ json = zope.component.getUtility(IJSONWriter)
+ result = json.write(wrapper)
+ body = self._prepareResult(result)
+ super(JSONRPCResponse, self).setResult(body)
+ logger.log(DEBUG, "Exception: %s" % result)
+ self.setStatus(200)
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/publisher.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/testing.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/testing.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/testing.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,301 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import StringIO
+
+import persistent
+import zope.interface
+from zope.testing import doctest
+from zope.app.testing import functional
+from zope.app.testing.functional import HTTPCaller
+
+from z3c.layer.minimal import IMinimalBrowserLayer
+from z3c.json.interfaces import IJSONReader
+from z3c.json.converter import JSONReader
+from z3c.json.exceptions import ProtocolError
+from z3c.json.proxy import JSONRPCProxy
+from z3c.json.transport import Transport
+from z3c.jsonrpc import layer
+from z3c.jsonrpc.publisher import JSONRPCRequest
+from z3c.jsonrpc.publisher import MethodPublisher
+from z3c.jsonrpc.publication import JSONRPCPublication
+
+
+###############################################################################
+#
+# Test proxy
+#
+###############################################################################
+
+class JSONRPCTestTransport(Transport):
+ """Test transport that delegates to zope.app.testing.functional.HTTPCaller.
+
+ It can be used like a normal transport, including support for basic
+ authentication.
+ """
+
+ verbose = False
+ handleErrors = True
+
+ def request(self, host, handler, request_body, verbose=0):
+ request = "POST %s HTTP/1.0\n" % (handler,)
+ request += "Content-Length: %i\n" % len(request_body)
+ request += "Content-Type: application/json\n"
+
+ host, extra_headers, x509 = self.get_host_info(host)
+ if extra_headers:
+ request += "Authorization: %s\n" % (
+ dict(extra_headers)["Authorization"],)
+
+ request += "\n" + request_body
+ caller = HTTPCaller()
+ response = caller(request, handle_errors=self.handleErrors)
+
+ errcode = response.getStatus()
+ errmsg = response.getStatusString()
+ # This is not the same way that the normal transport deals with the
+ # headers.
+ headers = response.getHeaders()
+
+ if errcode != 200:
+ raise ProtocolError(host + handler, errcode, errmsg, headers)
+
+ return self._parse_response(
+ StringIO.StringIO(response.getBody()), sock=None)
+
+
+def JSONRPCTestProxy(uri, transport=None, encoding=None,
+ verbose=None, jsonId=None, handleErrors=True):
+ """A factory that creates a server proxy using the ZopeJSONRPCTestTransport
+ by default."""
+ if verbose is None:
+ verbose = 0
+ if transport is None:
+ transport = JSONRPCTestTransport()
+ if isinstance(transport, JSONRPCTestTransport):
+ transport.handleErrors = handleErrors
+ return JSONRPCProxy(uri, transport, encoding, verbose, jsonId)
+
+
+###############################################################################
+#
+# Test layer
+#
+###############################################################################
+
+functional.defineLayer("JSONRPCTestingLayer", "ftesting.zcml")
+
+
+###############################################################################
+#
+# Test component
+#
+###############################################################################
+
+class IJSONRPCTestLayer(layer.IJSONRPCLayer):
+ """JSON-RPC test layer interface used for zcml testing."""
+
+
+class IJSONRPCTestSkin(IJSONRPCTestLayer):
+ """The IJSONRPCTestSkin testing skin based on IJSONRPCLayer."""
+
+
+class IA(zope.interface.Interface):
+ """First content stub interface."""
+
+
+class A(persistent.Persistent):
+ """First content stub."""
+
+ zope.interface.implements(IA)
+
+
+class IB(zope.interface.Interface):
+ """First content stub interface."""
+
+
+class B(persistent.Persistent):
+ """First content stub."""
+
+ zope.interface.implements(IB)
+
+
+class MethodsA(MethodPublisher):
+ """Method publisher test class."""
+
+ def hello(self):
+ return "Hello A World"
+
+
+class MethodsB(MethodPublisher):
+ """Method publisher test class."""
+
+ def hello(self):
+ return "Hello B World"
+
+
+class ForceResponseErrorJSONReader(JSONReader):
+ """JSONReader wich only raise a ResponseError because of bad a bad string.
+ """
+ zope.interface.implements(IJSONReader)
+
+ def read(self, aString, encoding=None):
+ aBadString = u'{"id":"jsonrpc", "method":"hello", "no-params"}'
+ return super(ForceResponseErrorJSONReader, self).read(aBadString, encoding)
+
+
+class TestRequest(JSONRPCRequest):
+ """modeled after zope.publisher.xmlrpc.TestRequest"""
+ def __init__(self, body_instream=None, environ=None,
+ response=None, **kw):
+
+ _testEnv = {
+ 'SERVER_URL': 'http://127.0.0.1',
+ 'HTTP_HOST': '127.0.0.1',
+ 'CONTENT_LENGTH': '0',
+ 'GATEWAY_INTERFACE': 'TestFooInterface/1.0',
+ }
+
+ if environ:
+ _testEnv.update(environ)
+ if kw:
+ _testEnv.update(kw)
+ if body_instream is None:
+ body_instream = StringIO.StringIO('')
+
+ super(TestRequest, self).__init__(
+ body_instream, _testEnv, response)
+
+
+class HTTPCaller(functional.HTTPCaller):
+ """HTTPCaller for JSON."""
+
+ def chooseRequestClass(self, method, path, environment):
+ """Choose and return a request class and a publication class"""
+ request_cls, publication_cls = \
+ super(HTTPCaller, self).chooseRequestClass(method, path,
+ environment)
+
+ content_type = environment.get('CONTENT_TYPE', '')
+ is_json = content_type.startswith('application/json')
+
+ if method in ('GET', 'POST', 'HEAD'):
+ if (method == 'POST' and is_json):
+ request_cls = JSONRPCRequest
+ publication_cls = JSONRPCPublication
+
+ return request_cls, publication_cls
+
+
+###############################################################################
+#
+# Doctest setup
+#
+###############################################################################
+
+def FunctionalDocFileSuite(*paths, **kw):
+ globs = kw.setdefault('globs', {})
+ globs['http'] = HTTPCaller()
+ globs['getRootFolder'] = functional.getRootFolder
+ globs['sync'] = functional.sync
+
+ kw['package'] = doctest._normalize_module(kw.get('package'))
+
+ kwsetUp = kw.get('setUp')
+ def setUp(test):
+ functional.FunctionalTestSetup().setUp()
+
+ if kwsetUp is not None:
+ kwsetUp(test)
+ kw['setUp'] = setUp
+
+ kwtearDown = kw.get('tearDown')
+ def tearDown(test):
+ if kwtearDown is not None:
+ kwtearDown(test)
+ functional.FunctionalTestSetup().tearDown()
+ kw['tearDown'] = tearDown
+
+ if 'optionflags' not in kw:
+ kw['optionflags'] = (doctest.ELLIPSIS
+ | doctest.REPORT_NDIFF
+ | doctest.NORMALIZE_WHITESPACE)
+
+ suite = functional.FunctionalDocFileSuite(*paths, **kw)
+ suite.layer = JSONRPCTestingLayer
+ return suite
+
+
+###############################################################################
+#
+# Test helper, make us independent from zope.app.testing
+#
+###############################################################################
+
+# Setup of test text files as modules
+import sys
+
+# Evil hack to make pickling work with classes defined in doc tests
+class NoCopyDict(dict):
+ def copy(self):
+ return self
+
+class FakeModule:
+ """A fake module."""
+
+ def __init__(self, dict):
+ self.__dict = dict
+
+ def __getattr__(self, name):
+ try:
+ return self.__dict[name]
+ except KeyError:
+ raise AttributeError(name)
+
+
+def setUpTestAsModule(test, name=None):
+ if name is None:
+ if test.globs.haskey('__name__'):
+ name = test.globs['__name__']
+ else:
+ name = test.globs.name
+
+ test.globs['__name__'] = name
+ test.globs = NoCopyDict(test.globs)
+ sys.modules[name] = FakeModule(test.globs)
+
+
+def tearDownTestAsModule(test):
+ del sys.modules[test.globs['__name__']]
+ test.globs.clear()
+
+
+###############################################################################
+#
+# Unittest setup
+#
+###############################################################################
+
+
+
+def setUp(test):
+ setUpTestAsModule(test, name='README')
+
+
+def tearDown(test):
+ tearDownTestAsModule(test)
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/testing.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/__init__.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/__init__.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/__init__.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/__init__.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/page.pt
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/page.pt (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/page.pt 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,111 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>z3c.jsonrpc.demo</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<meta http-equiv="cache-control" content="no-cache" />
+<script type="text/javascript" src="xmlhttp.js"
+ tal:attributes="src context/++resource++xmlhttp.js"> </script>
+<script type="text/javascript" src="jsonrpc.js"
+ tal:attributes="src context/++resource++jsonrpc.js"> </script>
+<script type="text/javascript"
+ tal:define="contexturl context/@@absolute_url;
+ viewurl request/URL"
+ tal:content="string:
+ var contextURL = '${contexturl}';
+ var viewURL = '${viewurl}';">
+</script>
+<script type="text/javascript" language="javascript">
+// helloworld
+function helloworld() {
+ var jsonProxy = new JSONRPC(contextURL);
+ jsonProxy.addMethod('helloworld');
+ response = jsonProxy.helloworld();
+ alert(response);
+}
+// hello
+function hello(name) {
+ var name = document.getElementById('helloInput').value;
+ var jsonProxy = new JSONRPC(contextURL);
+ jsonProxy.addMethod('hello');
+ response = jsonProxy.hello(name);
+ document.getElementById('helloOutput').value = response;
+}
+
+// kwargs
+function kwargs(name) {
+ var jsonProxy = new JSONRPC(contextURL);
+ jsonProxy.addMethod('kwargs');
+ postitional = 'postional value'
+ // firstMNamedMissing (not userd)
+ // secondNamedMissing (not userd)
+ var kws = new Object();
+ kws['thirdNamed'] = 'third value';
+ kws['lastNamed'] = 'last value';
+ response = jsonProxy.kwargs(postitional, kws);
+ document.getElementById('postional').innerHTML = response.postional;
+ document.getElementById('first').innerHTML = response.first;
+ document.getElementById('second').innerHTML = response.second;
+ document.getElementById('third').innerHTML = response.third;
+ document.getElementById('last').lastNamed = response.last;
+}
+
+// call intro
+alertIntro();
+</script>
+<style type="text/css">
+body {
+ font: 11px Verdana, Helvetica, Arial, sans-serif;
+}
+.red {
+ color: red;
+}
+</style>
+</head>
+<body>
+ <h2>JSONRPC demo page</h2>
+ <h3>1. Sample</h3>
+ <h4>Simple *Hello World* sample</h4>
+ <pre>
+function helloworld() {
+ var jsonProxy = new JSONRPC(contextURL);
+ jsonProxy.addMethod('helloworld');
+ response = jsonProxy.helloworld();
+ alert(response);
+}
+ </pre>
+ <div>
+ <input type="button" onClick="helloworld(); return false;" value="Hello world" />
+ </div>
+ <h3>2. Sample</h3>
+ <h4>This sample uses one positional argument</h4>
+ <pre>
+function hello(name) {
+ var name = document.getElementById('helloInput').value;
+ var jsonProxy = new JSONRPC(contextURL);
+ jsonProxy.addMethod('hello');
+ response = jsonProxy.hello(name);
+ document.getElementById('helloOutput').value = response;
+}
+ </pre>
+ <div>
+ <input type="text" onBlur="hello(); return false;" id="helloInput" value="" />
+ <input type="button" onClick="hello(); return false;" value="Add your name to the left field and click here" />
+ </div>
+ <div><input type="text" id="helloOutput" value="" /></div>
+ <h3>3. Sample</h3>
+ <h4>JSONRPC call with postional and named arguments.</h4>
+ <h3><span class="red">(NOTE, this fails in JSONRPC version 1.0)</span></h3>
+ <pre>
+
+ </pre>
+ <div>
+ <input type="button" onClick="kwargs(); return false;" value="Hello world" />
+ </div>
+ <div>positional: <span class="red" id="postional"></span></div>
+ <div>firstNamedMissing:<span class="red" id="first"></span></div>
+ <div>secondNamedMissing: <span class="red" id="second"></span></div>
+ <div>thirdNamed: <span class="red" id="third"></span></div>
+ <div>lastNamed: <span class="red" id="last"></span></div>
+</body>
+</html>
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/page.pt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_doc.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_doc.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_doc.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import unittest
+from zope.testing import doctest
+from z3c.jsonrpc import testing
+
+
+def test_suite():
+ return unittest.TestSuite((
+ testing.FunctionalDocFileSuite('../README.txt',
+ setUp=testing.setUp, tearDown=testing.tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+ doctest.DocFileSuite('../zcml.txt',
+ setUp=testing.setUp, tearDown=testing.tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+ ))
+
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_doc.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_factory.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_factory.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_factory.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,74 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import unittest
+
+import zope.interface
+import zope.component
+from zope.component.testing import PlacelessSetup
+
+from zope.app.publication.httpfactory import HTTPPublicationRequestFactory
+
+from z3c.jsonrpc.publication import JSONRPCFactory
+from z3c.jsonrpc.publication import JSONRPCPublication
+from z3c.jsonrpc import interfaces
+
+
+class DummyRequestFactory(object):
+ def __call__(self, input_stream, env):
+ self.input_stream = input_stream
+ self.env = env
+ return self
+
+ def setPublication(self, pub):
+ self.pub = pub
+
+
+class Test(PlacelessSetup, unittest.TestCase):
+
+ def setUp(self):
+ super(Test, self).setUp()
+ self.__factory = HTTPPublicationRequestFactory(None)
+ self.__env = {
+ 'SERVER_URL': 'http://127.0.0.1',
+ 'HTTP_HOST': '127.0.0.1',
+ 'CONTENT_LENGTH': '0',
+ 'GATEWAY_INTERFACE': 'TestFooInterface/1.0'
+ }
+
+ def test_jsonrpcfactory(self):
+ jsonrpcrequestfactory = DummyRequestFactory()
+ zope.interface.directlyProvides(
+ jsonrpcrequestfactory, interfaces.IJSONRPCRequestFactory)
+ zope.component.provideUtility(jsonrpcrequestfactory)
+ env = self.__env
+ factory = JSONRPCFactory()
+ self.assertEqual(factory.canHandle(env), True)
+ request, publication = factory()
+ self.assertEqual(isinstance(request, DummyRequestFactory), True)
+ self.assertEqual(publication, JSONRPCPublication)
+
+
+def test_suite():
+ return unittest.TestSuite((
+ unittest.makeSuite(Test),
+ ))
+
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_factory.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_request.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_request.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_request.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,195 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import unittest
+from StringIO import StringIO
+
+import zope.component
+from zope.publisher.base import DefaultPublication
+from zope.publisher.http import HTTPCharsets
+
+from z3c.json.interfaces import IJSONReader
+from z3c.json.converter import JSONReader
+from z3c.jsonrpc.publisher import JSONRPCRequest
+
+
+class Publication(DefaultPublication):
+
+ require_docstrings = 0
+
+ def getDefaultTraversal(self, request, ob):
+ if hasattr(ob, 'browserDefault'):
+ return ob.browserDefault(request)
+ return ob, ()
+
+
+class TestJSONRPCRequest(JSONRPCRequest, HTTPCharsets):
+ """Make sure that our request also implements IHTTPCharsets, so that we do
+ not need to register any adapters."""
+
+ def __init__(self, *args, **kw):
+ self.request = self
+ JSONRPCRequest.__init__(self, *args, **kw)
+
+
+class TestCall:
+ def __init__(self):
+ self.body = '{"id":"httpReq","method":"action","params":[1]}'
+ self.headers = []
+
+
+jsonrpc_call = TestCall()
+
+
+class ParamTestCall:
+ def __init__(self):
+ self.body = '{"id":"httpReq","method":"keyworded","params":[1,{"pythonKwMaRkEr":{"kw1":"aaa"}}]}'
+ self.headers = []
+
+
+class Param1_1SpecTestCall1:
+ def __init__(self):
+ self.body = '{"id":"httpReq","method":"keyworded","params":{"1":1,"kw1":"aaa"}}'
+ self.headers = []
+
+
+class Param1_1SpecTestCall2:
+ def __init__(self):
+ self.body = '{"id":"httpReq","method":"action1_1","params":{"1":1,"kw1":"aaa"}}'
+ self.headers = []
+
+
+class JSONRPCTests(unittest.TestCase):
+ """The only thing different to HTTP is the input processing; so there
+ is no need to redo all the HTTP tests again.
+ """
+
+ _testEnv = {
+ 'PATH_INFO': '/folder/item2/view/',
+ 'QUERY_STRING': '',
+ 'SERVER_URL': 'http://foobar.com',
+ 'HTTP_HOST': 'foobar.com',
+ 'CONTENT_LENGTH': '0',
+ 'REQUEST_METHOD': 'POST',
+ 'HTTP_AUTHORIZATION': 'Should be in accessible',
+ 'GATEWAY_INTERFACE': 'TestFooInterface/1.0',
+ 'HTTP_OFF_THE_WALL': "Spam 'n eggs",
+ 'HTTP_ACCEPT_CHARSET': 'ISO-8859-1, UTF-8;q=0.66, UTF-16;q=0.33',
+ }
+
+ def setUp(self):
+ super(JSONRPCTests, self).setUp()
+
+ class AppRoot(object):
+ pass
+
+ class Folder(object):
+ pass
+
+ class Item(object):
+
+ def __call__(self, a, b):
+ return "%s, %s" % (`a`, `b`)
+
+ def doit(self, a, b):
+ return 'do something %s %s' % (a, b)
+
+ class View(object):
+
+ def action(self, a, kw1=None):
+ return "Parameter[type: %s; value: %s" %(
+ type(a).__name__, `a`)
+
+ def keyworded(self, a, kw1="spam"):
+ return "kw1: [type: %s; value: %s]" %(
+ type(kw1).__name__, `kw1`)
+
+ def action1_1(self, a, kw1=None):
+ return "Parameter[type: %s; value: %s" %(
+ type(a).__name__, `a`)
+
+ class Item2(object):
+ view = View()
+
+ self.app = AppRoot()
+ self.app.folder = Folder()
+ self.app.folder.item = Item()
+ self.app.folder.item2 = Item2()
+ zope.component.provideUtility(JSONReader(), IJSONReader)
+
+ def _createRequest(self, extra_env={}, body=""):
+ env = self._testEnv.copy()
+ env.update(extra_env)
+ if len(body.body):
+ env['CONTENT_LENGTH'] = str(len(body.body))
+
+ publication = Publication(self.app)
+ instream = StringIO(body.body)
+ request = TestJSONRPCRequest(instream, env)
+ request.setPublication(publication)
+ return request
+
+
+ def testProcessInput(self):
+ req = self._createRequest({}, jsonrpc_call)
+ req.processInputs()
+ self.failUnlessEqual(req._args, (1,))
+ self.failUnlessEqual(tuple(req._path_suffix), ('action',))
+
+
+ def testTraversal(self):
+ req = self._createRequest({}, jsonrpc_call)
+ req.processInputs()
+ action = req.traverse(self.app)
+ self.failUnlessEqual(action(*req._args),
+ "Parameter[type: int; value: 1")
+
+ def testKeyword(self):
+ req = self._createRequest({}, ParamTestCall())
+ req.processInputs()
+ action = req.traverse(self.app)
+ self.failUnlessEqual(action(*req._args, **req.form),
+ "kw1: [type: unicode; value: u'aaa']")
+
+ def test1_1spec_kw(self):
+ req = self._createRequest({}, Param1_1SpecTestCall1())
+ req.processInputs()
+ action = req.traverse(self.app)
+ self.failUnlessEqual(action(*req._args, **req.form),
+ "kw1: [type: unicode; value: u'aaa']")
+
+ def test1_1spec2_p(self):
+ req = self._createRequest({}, Param1_1SpecTestCall2())
+ req.processInputs()
+ action = req.traverse(self.app)
+ self.failUnlessEqual(action(*req._args, **req.form),
+ "Parameter[type: int; value: 1")
+
+ def testJSONRPCMode(self):
+ req = self._createRequest({}, jsonrpc_call)
+ req.processInputs()
+ self.failUnlessEqual(req['JSONRPC_MODE'],True)
+
+
+def test_suite():
+ loader = unittest.TestLoader()
+ return loader.loadTestsFromTestCase(JSONRPCTests)
+
+
+if __name__=='__main__':
+ unittest.TextTestRunner().run(test_suite())
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/tests/test_request.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-configure.zcml
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-configure.zcml (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-configure.zcml 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1 @@
+<include package="z3c.jsonrpc" />
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-meta.zcml
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-meta.zcml (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-meta.zcml 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1 @@
+<include package="z3c.jsonrpc" file="meta.zcml" />
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/z3c.jsonrpc-meta.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.py
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.py (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.py 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,210 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.configuration.fields
+import zope.interface
+import zope.schema
+import zope.security.zcml
+
+from zope.interface import Interface
+from zope.security.checker import CheckerPublic, Checker
+from zope.configuration.exceptions import ConfigurationError
+
+from zope.component.interface import provideInterface
+from zope.app.component.metaconfigure import handler
+
+from z3c.jsonrpc import interfaces
+from z3c.jsonrpc.publisher import MethodPublisher
+
+
+class IJSONRPCDirective(zope.interface.Interface):
+ """Directive for JSONRPC methods."""
+
+ for_ = zope.configuration.fields.GlobalObject(
+ title=u"Published Object Type",
+ description=u"""The types of objects to be published via JSONRPC
+
+ This can be expressed with either a class or an interface
+ """,
+ required=True,
+ )
+
+ interface = zope.configuration.fields.Tokens(
+ title=u"Interface to be published.",
+ required=False,
+ value_type=zope.configuration.fields.GlobalInterface()
+ )
+
+ methods = zope.configuration.fields.Tokens(
+ title=u"Methods (or attributes) to be published",
+ required=False,
+ value_type=zope.configuration.fields.PythonIdentifier()
+ )
+
+ class_ = zope.configuration.fields.GlobalObject(
+ title=u"Class",
+ description=u"A class that provides attributes used by the view.",
+ required=False
+ )
+
+ permission = zope.security.zcml.Permission(
+ title=u"Permission",
+ description=u"""The permission needed to use the view.
+
+ If this option is used and a name is given for the view, then
+ the names defined by the given methods or interfaces will be
+ under the given permission.
+
+ If a name is not given for the view, then, this option is required and
+ the given permission is required to call the individual views defined
+ by the given interface and methods.
+
+ (See the name attribute.)
+
+ If no permission is given, then permissions should be declared
+ for the view using other means, such as the class directive.
+ """,
+ required=False)
+
+ name = zope.schema.TextLine(
+ title=u"The name of the view.",
+ description=u"""
+
+ If a name is given, then rpc methods are accessed by
+ traversing the name and then accessing the methods. In this
+ case, the class should implement
+ zope.pubisher.interfaces.IPublishTraverse.
+
+ If no name is provided, then the names given by the attributes
+ and interfaces are published directly as callable views.
+
+ """,
+ required=False,
+ )
+
+ layer = zope.configuration.fields.GlobalInterface(
+ title=u"The layer the view is declared for",
+ description=u"The default layer for which the default view is "
+ u"applicable. By default it is applied to all layers.",
+ default=interfaces.IJSONRPCRequest,
+ required=False
+ )
+
+
+def jsonrpc(_context, for_=None, interface=None, methods=None, class_=None,
+ permission=None, name=None, layer=None):
+
+ interface = interface or []
+ methods = methods or []
+
+ if layer is not None:
+ if not layer.extends(interfaces.IJSONRPCRequest):
+ raise ConfigurationError(
+ "The layer interface must extend `IJSONRPCRequest`.")
+ else:
+ layer = interfaces.IJSONRPCRequest
+
+ # If there were special permission settings provided, then use them
+ if permission == 'zope.Public':
+ permission = CheckerPublic
+
+ require = {}
+ for attr_name in methods:
+ require[attr_name] = permission
+
+ if interface:
+ for iface in interface:
+ for field_name in iface:
+ require[field_name] = permission
+ _context.action(
+ discriminator = None,
+ callable = provideInterface,
+ args = ('', for_)
+ )
+
+ # Make sure that the class inherits MethodPublisher, so that the views
+ # have a location
+ if class_ is None:
+ class_ = original_class = MethodPublisher
+ else:
+ original_class = class_
+ class_ = type(class_.__name__, (class_, MethodPublisher), {})
+
+ if name:
+ # Register a single jsonrpc view
+
+ if permission:
+ checker = Checker(require)
+
+ def proxyView(context, request, class_=class_, checker=checker):
+ view = class_(context, request)
+ # We need this in case the resource gets unwrapped and
+ # needs to be rewrapped
+ view.__Security_checker__ = checker
+ return view
+
+ class_ = proxyView
+ class_.factory = original_class
+ else:
+ # No permission was defined, so we defer to the checker
+ # of the original class
+ def proxyView(context, request, class_=class_):
+ view = class_(context, request)
+ view.__Security_checker__ = getCheckerForInstancesOf(
+ original_class)
+ return view
+ class_ = proxyView
+ class_.factory = original_class
+
+ # Register the new view.
+ _context.action(
+ discriminator = ('jsonrpc', for_, name, layer),
+ callable = handler,
+ args = ('registerAdapter',
+ class_, (for_, layer), Interface, name,
+ _context.info)
+ )
+ else:
+ if permission:
+ checker = Checker({'__call__': permission})
+ else:
+ raise ConfigurationError(
+ "JSONRPC view has neither a name nor a permission. "
+ "You have to specify at least one of the two.")
+
+ for name in require:
+ # create a new callable class with a security checker;
+ cdict = {'__Security_checker__': checker,
+ '__call__': getattr(class_, name)}
+ new_class = type(class_.__name__, (class_,), cdict)
+ _context.action(
+ discriminator = ('jsonrpc', for_, name, layer),
+ callable = handler,
+ args = ('registerAdapter',
+ new_class, (for_, layer), Interface, name,
+ _context.info)
+ )
+
+ # Register the used interfaces with the site manager
+ if for_ is not None:
+ _context.action(
+ discriminator = None,
+ callable = provideInterface,
+ args = ('', for_)
+ )
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.txt
===================================================================
--- z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.txt (rev 0)
+++ z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.txt 2007-11-30 15:52:55 UTC (rev 82062)
@@ -0,0 +1,75 @@
+==========
+Directives
+==========
+
+JSONRPC directive
+-----------------
+
+Show how we can use the jsonrpc directive. Register the meta configuration for
+the directive.
+
+ >>> from zope.configuration import xmlconfig
+ >>> import z3c.jsonrpc
+ >>> context = xmlconfig.file('meta.zcml', z3c.jsonrpc)
+
+Now register the view defined in the testing module within the ``z3c:jsonrpc``
+directive:
+
+ >>> context = xmlconfig.string("""
+ ... <configure
+ ... xmlns:z3c="http://namespaces.zope.org/z3c">
+ ... <z3c:jsonrpc
+ ... for="z3c.jsonrpc.testing.IA"
+ ... class="z3c.jsonrpc.testing.MethodsA"
+ ... permission="zope.Public"
+ ... methods="hello"
+ ... />
+ ... </configure>
+ ... """, context)
+
+Let's check if the view is registered as adapter:
+
+ >>> import zope.component
+ >>> from z3c.jsonrpc.testing import A
+ >>> from z3c.jsonrpc.testing import TestRequest
+ >>> a = A()
+ >>> request = TestRequest()
+ >>> zope.component.queryMultiAdapter((a, request), name='hello')
+ <z3c.jsonrpc.zcml.MethodsA object at ...>
+
+We can also use a layer interface wich will restrict our view registration to
+a specific request type. Provide such a request type layer:
+
+ >>> from z3c.jsonrpc.testing import IJSONRPCTestLayer
+ >>> demoRequest = TestRequest()
+ >>> zope.interface.directlyProvides(demoRequest, IJSONRPCTestLayer)
+
+And register a new JSON-RPC view:
+
+ >>> context = xmlconfig.string("""
+ ... <configure
+ ... xmlns:z3c="http://namespaces.zope.org/z3c">
+ ... <z3c:jsonrpc
+ ... for="z3c.jsonrpc.testing.IB"
+ ... class="z3c.jsonrpc.testing.MethodsB"
+ ... permission="zope.Public"
+ ... methods="hello"
+ ... layer="z3c.jsonrpc.testing.IJSONRPCTestLayer"
+ ... />
+ ... </configure>
+ ... """, context)
+
+Setup a new content stub:
+
+ >>> from z3c.jsonrpc.testing import B
+ >>> b = B()
+
+And test the view within our new layer:
+
+ >>> zope.component.queryMultiAdapter((b, demoRequest), name='hello')
+ <z3c.jsonrpc.zcml.MethodsB object at ...>
+
+Note the object b does not know the view within the default request layer:
+
+ >>> zope.component.queryMultiAdapter((b, request), name='hello') is None
+ True
Property changes on: z3c.jsonrpc/trunk/src/z3c/jsonrpc/zcml.txt
___________________________________________________________________
Name: svn:eol-style
+ native
More information about the Checkins
mailing list