5. Using parameters, math service

In this paragraph we’re going to create a simple service, which can add two numbers result=a+b.

Create the service and test locally

To add two numbers, we should read the request message on execute, then perform the mathematic function and return the response message like we did in “Hello World”.

services/valuea/samples/add_numbers.py
 1import valuea_framework.broker.Service
 2
 3
 4class Service(valuea_framework.broker.Service.BaseService):
 5    def __init__(self, *args, **kwargs):
 6        super(Service, self).__init__(*args, **kwargs)
 7        self._json_schema_messages['a'] = 'The parameter a should contain a valid number.'
 8
 9    def execute(self):
10        msg = self.get_message()
11        return {'result': msg['a'] + msg['b']}

The self.get_message() function will return the request message and implements two different response types, the one we describe here will return a dictionary (hence the msg['a'] variable), the other option is to return an object (self.get_message(as_object=True)), which uses msg.a to point to an item.

Note

Using self.get_message(as_object=True) list [] items are added to the “items” attribute of the object. The items in {'a':[1,2,3]} can be found at a.items.

Note

You can set a user readable error with self._json_schema_messages.

Executing our example is comparable to our earlier sample too, extended with a set_message.

run_add_numbers_local_1.py
 1#!/usr/bin/env python
 2
 3import services.valuea.samples.add_numbers
 4
 5
 6srv = services.valuea.samples.add_numbers.Service()
 7srv.set_message({'a': 1, 'b': 2})
 8result = srv.execute()
 9
10print (result)
11
12
13srv = services.valuea.samples.add_numbers.Service()
14srv.set_message({'a': 'x', 'b': 2})
15result = srv.execute()
16print (result)

To demonstrate our lack of validation, we have executed our request twice, first with legal input, then with illegal input. When executed the last one will throw an error, such as “TypeError: cannot concatenate 'str' and 'int' objects”.

Validate our input before processing

The ValueA framework implements jsonschema to validate requests, which is the json variant for xsd validations.

To enable validation, just add a json schema file in the same directory using an equal filename with extension .json. The framework will automatically pick this up when processing the request.

services/valuea/samples/add_numbers.json
 1{
 2	"$schema": "http://json-schema.org/draft-04/schema#",
 3	"description": "add_numbers",
 4	"properties": {
 5		"a": {
 6			"type": "integer"
 7		},
 8		"b": {
 9			"type": "integer"
10		}
11	},
12	"required": [
13		"a", "b"
14	]
15}

Now we want to test the service including validation within a local python script, which requires us to execute the validation ourselves.

run_add_numbers_local_2.py
 1#!/usr/bin/env python
 2
 3import services.valuea.samples.add_numbers
 4
 5srv = services.valuea.samples.add_numbers.Service()
 6srv.set_message({'a': 'x', 'b': 2})
 7# execute validations, normally processed within the listener
 8validation_output = srv.validate()
 9if validation_output:
10    # Oo, our validation returned issues, print all
11    for message in validation_output:
12        print (message)
13else:
14    # all well, print result
15    result = srv.execute()
16    print (result)

The above script executes srv.validate(), which may return a list of error messages. When it does we print them on screen. In our case we expect something similar to this:

{'message': "'x' is not of type u'integer'", 'ref': u'a', 'user_message': 'The parameter a should contain a valid number.'}

This tells us field a (the ref attribute) raises an error “‘x’ is not of type integer”.

Expose service and test

Now we’re going to expose our service to the message queue and execute the same commands using our queue, we should still have the msg_broker.py available which we’re going to start here again.

Note

When services have changed, remember to restart the listener, otherwise it might still return previous results. Python code is compiled (to bytecode) the first time it’s executed.

run_add_numbers_queue_1.py
 1#!/usr/bin/env python
 2from valuea_framework.connectors.rabbitmq.Simple import SimpleRpcClient
 3
 4rpcclient = SimpleRpcClient(hostname='192.168.56.101',
 5                            exchange='default_exchange',
 6                            username='admin',
 7                            password='admin')
 8
 9
10remote_procedure = 'valuea.samples.add_numbers'
11
12# add two numbers
13print(" [x] Requesting %s" % remote_procedure)
14response = rpcclient.call(remote_procedure, {'a': 1, 'b': 2})
15print(" [.] Got %r" % response)
16
17# call with illegal parameters
18print(" [x] Requesting %s" % remote_procedure)
19response = rpcclient.call(remote_procedure, {'a': 'x', 'b': 2})
20print(" [.] Got %r" % response)

The above block should produce a response similar to the one below:

[x] Requesting valuea.samples.add_numbers
[.] Got {u'result': 3}
[x] Requesting valuea.samples.add_numbers
[.] Got {u'error': {u'messsageid': u'a4302c91-7930-4c06-965e-cf4d868fbd89', u'validations': [{u'message': u"u'x' is not of type u'integer'", u'ref': u'a', u'user_message': u'The parameter a should contain a valid number.'}]}}

Adding 1 and 2 obviously results in 3, but adding ‘x’ to a value is kind of impossible and results in an error response. Having our user interface handle either result should be easy, as both are valid JSON.