Skip to content

Commit 1b84708

Browse files
committed
Allow calls to legacy ubersmith with UbersmithCore
This new class will allow to relay php function call from python. The parameters will be autowired on call and it will allow you to call ubersmith's internal php function.
1 parent 767bf58 commit 1b84708

File tree

12 files changed

+414
-4
lines changed

12 files changed

+414
-4
lines changed

requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
flask==0.10.1
1+
flask==0.10.1
2+
python-ubersmithclient>=2.0.6
3+
six

test-requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
nose>=1.2.1
22
mock>=1.0.1
3-
pyhamcrest>=1.6
3+
pyhamcrest>=1.6
4+
flexmock>=0.10.2

tests/Integration/__init__.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright 2016 Internap.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
class ContextMatcher(object):
17+
def __init__(self, callback_url, module_id, device_id=None, service_id=None):
18+
self.callback_url = callback_url
19+
self.module_id = module_id
20+
self.device_id = device_id
21+
self.service_id = service_id
22+
23+
def __eq__(self, other):
24+
return self.callback_url == other.callback_url and \
25+
self.module_id == other.module_id and \
26+
((self.device_id and self.device_id == other.device_id) or
27+
(self.service_id and self.service_id == other.service_id))
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Copyright 2016 Internap.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import unittest
16+
17+
from flexmock import flexmock, flexmock_teardown
18+
19+
from . import ContextMatcher
20+
from ubersmith_remote_module_server import ubersmith_core
21+
from ubersmith_remote_module_server.objects import RequestContext
22+
from ubersmith_remote_module_server.router import Router
23+
from ubersmith_remote_module_server.ubersmith_core import UbersmithCore
24+
25+
26+
class FakeModule(object):
27+
def call_uber_core(self):
28+
UbersmithCore.test()
29+
30+
31+
class RouterUbersmithCoreTest(unittest.TestCase):
32+
def test_a_module_can_call_ubersmith_core_without_context(self):
33+
fake_module = FakeModule()
34+
remote_executor_mock = flexmock()
35+
36+
flexmock(ubersmith_core).should_receive('RemoteExecutor').\
37+
with_args(context=RequestContext).and_return(remote_executor_mock)
38+
39+
remote_executor_mock.should_receive('invoke_global').with_args('test', args={}).once()
40+
41+
self.basic_router = Router(env_as_kwarg=False)
42+
self.basic_router.invoke_method(module=fake_module, method='call_uber_core')
43+
44+
def test_module_context_is_injected_when_service_module(self):
45+
fake_module = FakeModule()
46+
remote_executor_mock = flexmock()
47+
48+
example_callback = {'params': {'module_id': '44', 'service_id': '1260'},
49+
'url': 'http://user:[email protected]/'
50+
'api/2.0/?method=service.module_call'}
51+
52+
flexmock(ubersmith_core).should_receive('RemoteExecutor'). \
53+
with_args(context=ContextMatcher(callback_url=example_callback['url'],
54+
module_id='44',
55+
service_id='1260'))\
56+
.and_return(remote_executor_mock)
57+
58+
remote_executor_mock.should_receive('invoke_global')
59+
60+
self.basic_router = Router(env_as_kwarg=False)
61+
self.basic_router.invoke_method(module=fake_module,
62+
method='call_uber_core',
63+
callback=example_callback)
64+
65+
def test_module_context_is_injected_when_device_module(self):
66+
fake_module = FakeModule()
67+
68+
example_callback = {'params': {'module_id': '173', 'device_id': '200025'},
69+
'url': 'http://user:[email protected]/'
70+
'api/2.0/?method=device.module_call'}
71+
72+
remote_executor_mock = flexmock()
73+
flexmock(ubersmith_core).should_receive('RemoteExecutor'). \
74+
with_args(context=ContextMatcher(callback_url=example_callback['url'],
75+
module_id='173',
76+
device_id='200025'))\
77+
.and_return(remote_executor_mock)
78+
79+
remote_executor_mock.should_receive('invoke_global')
80+
81+
self.basic_router = Router(env_as_kwarg=False)
82+
self.basic_router.invoke_method(module=fake_module,
83+
method='call_uber_core',
84+
callback=example_callback)
85+
86+
def tearDown(self):
87+
flexmock_teardown()
88+
super(RouterUbersmithCoreTest, self).tearDown()

tests/__init__.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,19 @@
1010
# distributed under the License is distributed on an "AS IS" BASIS,
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
13-
# limitations under the License.
13+
# limitations under the License.
14+
15+
from flexmock import flexmock
16+
17+
18+
def mock_ubersmith_client():
19+
ubersmith_client = flexmock()
20+
ubersmith_client.api = flexmock()
21+
return ubersmith_client
22+
23+
24+
def mock_ubersmith_api():
25+
ubersmith_api = flexmock()
26+
ubersmith_api.device = flexmock()
27+
ubersmith_api.client = flexmock()
28+
return ubersmith_api

tests/test_remote_executor.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Copyright 2016 Internap.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import unittest
16+
17+
from flexmock import flexmock, flexmock_teardown
18+
19+
from ubersmith_remote_module_server import _remote_executor
20+
from ubersmith_remote_module_server._remote_executor import RemoteExecutor
21+
from ubersmith_remote_module_server.objects import RequestContext
22+
from . import mock_ubersmith_client, mock_ubersmith_api
23+
24+
25+
class RemoteExecutorTest(unittest.TestCase):
26+
def test_invoke_global_from_device_module(self):
27+
ubersmith_client = mock_ubersmith_client()
28+
ubersmith_api = mock_ubersmith_api()
29+
30+
flexmock(_remote_executor).should_receive('ubersmith_client').and_return(ubersmith_client)
31+
32+
ubersmith_client.api.should_receive('init')\
33+
.with_args(url='http://ubersmith.example/url',
34+
password='pass',
35+
user='user')\
36+
.and_return(ubersmith_api)
37+
38+
ubersmith_api.device.should_receive('module_call').\
39+
with_args(device_id=20025,
40+
function='_web_hook_invoke_global',
41+
module_id=173,
42+
module_params=['logevent', [dict(action='hello from python')]])
43+
44+
remote_executor = RemoteExecutor(
45+
context=RequestContext(callback_url='http://user:[email protected]/url',
46+
module_id=173, device_id=20025))
47+
remote_executor.invoke_global('logevent', dict(action='hello from python'))
48+
49+
def test_invoke_global_from_service_module(self):
50+
ubersmith_client = mock_ubersmith_client()
51+
ubersmith_api = mock_ubersmith_api()
52+
53+
flexmock(_remote_executor).should_receive('ubersmith_client').and_return(ubersmith_client)
54+
55+
ubersmith_client.api.should_receive('init')\
56+
.with_args(url='http://ubersmith.example/url',
57+
password='pass',
58+
user='user')\
59+
.and_return(ubersmith_api)
60+
61+
ubersmith_api.client.should_receive('service_module_call').\
62+
with_args(service_id=1260,
63+
function='_web_hook_invoke_global',
64+
module_id=44,
65+
module_params=['logevent', [dict(action='hello from python')]])
66+
67+
remote_executor = RemoteExecutor(
68+
context=RequestContext(callback_url='http://user:[email protected]/'
69+
'url?method=method',
70+
module_id=44,
71+
service_id=1260))
72+
remote_executor.invoke_global('logevent', dict(action='hello from python'))
73+
74+
def tearDown(self):
75+
flexmock_teardown()
76+
super(RemoteExecutorTest, self).tearDown()
77+
78+

tests/test_ubersmith_core.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Copyright 2016 Internap.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import unittest
16+
17+
from flexmock import flexmock, flexmock_teardown
18+
19+
from ubersmith_remote_module_server import ubersmith_core
20+
from ubersmith_remote_module_server.exceptions import NamedArgumentsOnly
21+
from ubersmith_remote_module_server.ubersmith_core import NoRequestContext, UbersmithCore, \
22+
ConfiguredRequestContext
23+
24+
25+
class UbersmithCoreTest(unittest.TestCase):
26+
def test_raises_when_out_of_context(self):
27+
remote_executor_mock = flexmock()
28+
flexmock(ubersmith_core).should_receive('RemoteExecutor').and_return(remote_executor_mock)
29+
with self.assertRaises(NoRequestContext):
30+
UbersmithCore.test()
31+
32+
def test_raises_when_args(self):
33+
remote_executor_mock = flexmock()
34+
flexmock(ubersmith_core).should_receive('RemoteExecutor').and_return(remote_executor_mock)
35+
with self.assertRaises(NamedArgumentsOnly):
36+
UbersmithCore.test('hello')
37+
38+
def test_calls_executor_with_context_when_called(self):
39+
remote_executor_mock = flexmock()
40+
flexmock(ubersmith_core).should_receive('RemoteExecutor').\
41+
with_args(context='context').and_return(remote_executor_mock).once()
42+
remote_executor_mock.should_receive('invoke_global').with_args('test', args={}).once()
43+
44+
with ConfiguredRequestContext(context='context'):
45+
UbersmithCore.test()
46+
47+
def tearDown(self):
48+
flexmock_teardown()
49+
super(UbersmithCoreTest, self).tearDown()
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Copyright 2016 Internap.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import ubersmith_client
16+
from six.moves.urllib.parse import urlparse
17+
18+
19+
class RemoteExecutor(object):
20+
def __init__(self, context):
21+
self.context = context
22+
23+
def invoke_global(self, func_name, args):
24+
url = urlparse(self.context.callback_url)
25+
ubersmith_url = "{0}://{1}{2}".format(url.scheme, url.hostname, url.path)
26+
ubersmith_api = ubersmith_client.api.init(url=ubersmith_url,
27+
user=url.username,
28+
password=url.password)
29+
if self.context.device_id:
30+
ubersmith_api.device.module_call(device_id=self.context.device_id,
31+
module_id=self.context.module_id,
32+
function='_web_hook_invoke_global',
33+
module_params=[func_name, [args]])
34+
else:
35+
ubersmith_api.client.service_module_call(service_id=self.context.service_id,
36+
module_id=self.context.module_id,
37+
function='_web_hook_invoke_global',
38+
module_params=[func_name, [args]])
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright 2016 Internap.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
class NoRequestContext(Exception):
17+
def __init__(self, msg="UbersmithCore was called without a context, "
18+
"you can't save this object"):
19+
super(NoRequestContext, self).__init__(msg)
20+
21+
22+
class NamedArgumentsOnly(Exception):
23+
def __init__(self, msg="UbersmithCore was called with non-named arguments, "
24+
"you MUST use named arguments (kwargs)"):
25+
super(NamedArgumentsOnly, self).__init__(msg)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright 2016 Internap.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
class RequestContext(object):
17+
def __init__(self, callback_url='', module_id='', device_id=None, service_id=None):
18+
self.callback_url = callback_url
19+
self.module_id = module_id
20+
self.device_id = device_id
21+
self.service_id = service_id

0 commit comments

Comments
 (0)