Skip to content

Commit

Permalink
variable config validation (#23)
Browse files Browse the repository at this point in the history
* variable config validation

* restore UnexpectedVariableValueTypeError message

* update UnexpectedVariableTypeError message
  • Loading branch information
sky3d authored and vutenkov committed May 16, 2018
1 parent 9a61a95 commit 143fc84
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 7 deletions.
6 changes: 6 additions & 0 deletions cloud4rpi/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ def __apply_commands(self, cmd):
return update

def declare(self, variables):
for name, value in variables.items():
utils.guard_against_invalid_variable_type(name,
value.get('type', None))
self.__variables = variables

def declare_diag(self, diag):
Expand Down Expand Up @@ -92,6 +95,9 @@ def read_diag(self):
def publish_config(self, cfg=None):
if cfg is None:
cfg = self.read_config()
else:
cfg = utils.validate_config(cfg)

return self.__api.publish_config(cfg)

def publish_data(self, data=None):
Expand Down
15 changes: 14 additions & 1 deletion cloud4rpi/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ class InvalidTokenError(Exception):
pass


class InvalidConfigError(TypeError):
pass


class UnexpectedVariableTypeError(TypeError):
pass


class UnexpectedVariableValueTypeError(TypeError):
pass

Expand All @@ -30,8 +38,13 @@ class NotSupportedError(Exception):
subprocess.CalledProcessError: 'Try run with sudo',
InvalidTokenError:
'Device token {0} is invalid. Please verify it.',
InvalidConfigError:
'Configuration is invalid. It must be an array.',
UnexpectedVariableTypeError:
('Unexpected type for the "{0}" variable. '
'It must be "bool", "numeric" or "string".'),
UnexpectedVariableValueTypeError:
'Unexpected value type for variable: {0}',
'Unexpected value type for variable: {0}'
}


Expand Down
32 changes: 26 additions & 6 deletions cloud4rpi/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from datetime import datetime, tzinfo, timedelta
from cloud4rpi import config
from cloud4rpi.errors import InvalidTokenError
from cloud4rpi.errors import InvalidConfigError
from cloud4rpi.errors import UnexpectedVariableTypeError
from cloud4rpi.errors import UnexpectedVariableValueTypeError
from cloud4rpi.errors import TYPE_WARN_MSG

Expand All @@ -19,6 +21,12 @@

log = logging.getLogger(config.loggerName)

BOOL_TYPE = 'bool'
NUMERIC_TYPE = 'numeric'
STRING_TYPE = 'string'

SUPPORTED_VARIABLE_TYPES = [BOOL_TYPE, NUMERIC_TYPE, STRING_TYPE]


class UtcTzInfo(tzinfo):
# pylint: disable=W0223
Expand Down Expand Up @@ -70,9 +78,9 @@ def validate_variable_value(name, var_type, value):
return value

convert = {
'bool': to_bool,
'numeric': to_numeric,
'string': to_string,
BOOL_TYPE: to_bool,
NUMERIC_TYPE: to_numeric,
STRING_TYPE: to_string,
}
c = convert.get(var_type, None)
if c is None:
Expand All @@ -83,9 +91,21 @@ def validate_variable_value(name, var_type, value):
raise UnexpectedVariableValueTypeError('"{0}"={1}'.format(name, value))


def variables_to_config(variables):
return [{'name': name, 'type': value['type']}
for name, value in variables.items()]
def validate_config(cfg):
if not isinstance(cfg, list):
raise InvalidConfigError()

for item in cfg:
guard_against_invalid_variable_type(
item.get('name', None),
item.get('type', None)
)
return cfg


def guard_against_invalid_variable_type(name, var_type):
if var_type not in SUPPORTED_VARIABLE_TYPES:
raise UnexpectedVariableTypeError(name)


def utcnow():
Expand Down
33 changes: 33 additions & 0 deletions test/test_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import unittest
from mock import Mock
import cloud4rpi
from cloud4rpi.errors import InvalidConfigError
from cloud4rpi.errors import UnexpectedVariableTypeError
from cloud4rpi.errors import UnexpectedVariableValueTypeError


Expand Down Expand Up @@ -49,6 +51,17 @@ def testDeclareVariables(self):
cfg = device.read_config()
self.assertEqual(cfg, [{'name': 'CPUTemp', 'type': 'numeric'}])

def testDeclareVariablesValidation(self):
api = ApiClientMock()
device = cloud4rpi.Device(api)
with self.assertRaises(UnexpectedVariableTypeError):
device.declare({
'CPUTemp': {
'type': 'number',
'bind': MockSensor()
}
})

def testDeclareDiag(self):
api = ApiClientMock()
device = cloud4rpi.Device(api)
Expand Down Expand Up @@ -183,6 +196,26 @@ def testReadBeforePublishConfig(self):
cfg = [{'name': 'CPUTemp', 'type': 'numeric'}]
api.publish_config.assert_called_with(cfg)

def testPublishConfigFail_NotAnArray(self):
api = ApiClientMock()
device = cloud4rpi.Device(api)

cfg = {'name': 'CPUTemp', 'type': 'numeric'}
with self.assertRaises(InvalidConfigError):
device.publish_config(cfg)

api.publish_config.assert_not_called()

def testPublishConfigFail_UnexpectedVariableType(self):
api = ApiClientMock()
device = cloud4rpi.Device(api)

cfg = [{'name': 'CPUTemp', 'type': 'number'}]
with self.assertRaises(UnexpectedVariableTypeError):
device.publish_config(cfg)

api.publish_config.assert_not_called()

def testPublishDiag(self):
api = ApiClientMock()
device = cloud4rpi.Device(api)
Expand Down

0 comments on commit 143fc84

Please sign in to comment.