1#!/usr/bin/env python
2# encoding: utf8
3#
4# Copyright © Burak Arslan <burak at arskom dot com dot tr>,
5#             Arskom Ltd. http://www.arskom.com.tr
6# All rights reserved.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions are met:
10#
11#    1. Redistributions of source code must retain the above copyright notice,
12#       this list of conditions and the following disclaimer.
13#    2. Redistributions in binary form must reproduce the above copyright
14#       notice, this list of conditions and the following disclaimer in the
15#       documentation and/or other materials provided with the distribution.
16#    3. Neither the name of the owner nor the names of its contributors may be
17#       used to endorse or promote products derived from this software without
18#       specific prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT,
24# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
29# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30#
31
32
33"""
34This example shows how to define and use complex structures
35in spyne.  This example uses an extremely simple in-memory
36dictionary to store the User objects.
37"""
38
39import logging
40import random
41
42from spyne import Application, rpc, Array, ComplexModel, Integer, String, \
43    Service, ResourceNotFoundError
44from spyne.protocol.http import HttpRpc
45from spyne.protocol.xml import XmlDocument
46from spyne.server.wsgi import WsgiApplication
47
48user_database = {}
49userid_seq = 1
50chars = [chr(i) for i in range(ord('a'), ord('z'))]
51
52
53def randchars(n):
54    return ''.join(random.choice(chars) for _ in range(n))
55
56
57class Permission(ComplexModel):
58    __namespace__ = "permission"
59
60    app = String(values=['library', 'delivery', 'accounting'])
61    perms = String(min_occurs=1, max_occurs=2, values=['read', 'write'])
62
63
64class User(ComplexModel):
65    __namespace__ = "user"
66
67    userid = Integer
68    username = String
69    firstname = String
70    lastname = String
71    permissions = Array(Permission)
72
73
74# add superuser to the 'database'
75
76all_permissions = (
77    Permission(app='library', perms=['read', 'write']),
78    Permission(app='delivery', perms=['read', 'write']),
79    Permission(app='accounting', perms=['read', 'write']),
80)
81
82
83def randperms(n):
84    for p in random.sample(all_permissions, n):
85        yield Permission(app=p.app,
86            perms=random.sample(p.perms, random.randint(1, 2)))
87
88
89user_database[0] = User(
90    userid=0,
91    username='root',
92    firstname='Super',
93    lastname='User',
94    permissions=all_permissions
95)
96
97
98def add_user(user):
99    global user_database
100    global userid_seq
101
102    user.userid = userid_seq
103    userid_seq = userid_seq + 1
104    user_database[user.userid] = user
105
106
107class UserManager(Service):
108    @rpc(User, _returns=Integer)
109    def add_user(ctx, user):
110        add_user(user)
111        return user.userid
112
113    @rpc(_returns=User)
114    def super_user(ctx):
115        return user_database[0]
116
117    @rpc(_returns=User)
118    def random_user(ctx):
119        retval = User(
120            username=randchars(random.randrange(3, 12)),
121            firstname=randchars(random.randrange(3, 12)).title(),
122            lastname=randchars(random.randrange(3, 12)).title(),
123            permissions=randperms(random.randint(1, len(all_permissions)))
124        )
125
126        add_user(retval)
127
128        return retval
129
130    @rpc(Integer, _returns=User)
131    def get_user(ctx, userid):
132        global user_database
133
134        # If you rely on dict lookup raising KeyError here, you'll return an
135        # internal error to the client, which tells the client that there's
136        # something wrong in the server. However in this case, KeyError means
137        # invalid request, so it's best to return a client error.
138
139        # For the HttpRpc case, internal error is 500 whereas
140        # ResourceNotFoundError is 404.
141        if not (userid in user_database):
142            raise ResourceNotFoundError(userid)
143
144        return user_database[userid]
145
146    @rpc(User)
147    def modify_user(ctx, user):
148        global user_database
149
150        if not (user.userid in user_database):
151            raise ResourceNotFoundError(user.userid)
152
153        user_database[user.userid] = user
154
155    @rpc(Integer)
156    def delete_user(ctx, userid):
157        global user_database
158
159        if not (userid in user_database):
160            raise ResourceNotFoundError(userid)
161
162        del user_database[userid]
163
164    @rpc(_returns=Array(User))
165    def list_users(ctx):
166        global user_database
167
168        return user_database.values()
169
170
171if __name__ == '__main__':
172    from wsgiref.simple_server import make_server
173
174    logging.basicConfig(level=logging.DEBUG)
175    logging.getLogger('spyne.protocol.xml').setLevel(logging.DEBUG)
176
177    application = Application([UserManager], 'spyne.examples.complex',
178                              in_protocol=HttpRpc(), out_protocol=XmlDocument())
179
180    server = make_server('127.0.0.1', 8000, WsgiApplication(application))
181
182    logging.info("listening to http://127.0.0.1:8000")
183    logging.info("wsdl is at: http://localhost:8000/?wsdl")
184
185    server.serve_forever()
186