Exemple #1
0
    def test_init_with_no_args_creates_new_dict(self):

        target1 = Data()
        self.assertEqual(target1.DATA, {})

        target2 = Data()
        self.assertEqual(target2.DATA, {})

        self.assertFalse(target1.DATA is target2.DATA)
Exemple #2
0
    def __load_settings(self):

        settings_file_path = os.path.join(os.path.dirname(__file__), 'settings.json')
        if not os.path.isfile(settings_file_path):
            print 'There is no settings file at {}.'.format(settings_file_path)
            return Data({}, read_only=True)

        with open(settings_file_path, 'r') as file:
            content = json.load(file)

        return Data(content, read_only = True)
Exemple #3
0
 def return_json(self, value):
     self.__return_data = value
     if isinstance(value, list):
         self.side_effect = [
             Data(entry,
                  read_only=True,
                  PATH=self.__mock_path,
                  RESPONSE=real_mock.MagicMock(name='response'))
             for entry in value
         ]
     else:
         self.return_value = Data(
             value,
             read_only=True,
             PATH=self.__mock_path,
             RESPONSE=real_mock.MagicMock(name='response'))
Exemple #4
0
    def test_create_new_with_None(self):

        target = Data(create_new=None)
        target.B = 10

        self.assertIsNone(target.A)
        self.assertEqual(target.B, 10)
Exemple #5
0
    def test_init_wraps_dicts_in_nested_lists(self):

        target = Data({'A': [[{'x': 1}], [{'y': 2}]]})

        self.assertIsInstance(target.A, list)
        self.assertIsInstance(target.A[0][0], Data)
        self.assertIsInstance(target.A[1][0], Data)
Exemple #6
0
    def test_contains(self):

        target = Data()
        target.B = 10

        self.assertNotIn('A', target)
        self.assertIn('B', target)
Exemple #7
0
    def test_init_uses_kwargs_for_metadata_attributes(self):

        target = Data(FOO=10, BAR=20)

        self.assertEqual(target.FOO, 10)
        self.assertEqual(target.BAR, 20)
        self.assertEqual(target.DATA, {})
Exemple #8
0
    def test_create_new_with_string(self):

        target = Data(create_new=str)
        target.B = 10

        self.assertEqual(target.A, '')
        self.assertEqual(target.B, 10)
Exemple #9
0
    def test_can_set_simple_attributes(self):

        target = Data()

        target.A = 1
        target.B = 'foo'

        self.assertEqual(target.DATA, {'A': 1, 'B': 'foo'})
Exemple #10
0
    def test_can_set_lists_with_simple_items(self):

        target = Data()

        target.A = [1, 2]

        self.assertIsInstance(target.A, list)
        self.assertEqual(target.A, [1, 2])
Exemple #11
0
    def test_can_set_lists_with_nested_dicts(self):

        target = Data()

        target.A = [[{'x': 1}], [{'y': 2}]]

        self.assertIsInstance(target.A, list)
        self.assertIsInstance(target.A[0][0], Data)
        self.assertIsInstance(target.A[1][0], Data)
Exemple #12
0
    def test_can_set_dict_attributes(self):

        target = Data()

        target.A = {'x': 1}

        self.assertIsInstance(target.A, Data)
        self.assertEqual(target.A.DATA, {'x': 1})
        self.assertEqual(target.DATA, {'A': {'x': 1}})
Exemple #13
0
    def test_eq(self):

        self.assertEqual(Data({'A': 10}), Data({'A': 10}))
        self.assertNotEqual(Data({'A': 10}), Data({'A': 20}))

        self.assertEqual(Data({'A': 10}), {'A': 10})
        self.assertNotEqual(Data({'A': 10}), {'A': 20})

        self.assertNotEqual(Data({'A': 10}), 'foo')
class UnitTest_CloudGemFramework_LambdaSettings(unittest.TestCase):
    def setUp(self):

        if 'cgf_lambda_settings' in sys.modules:
            del sys.modules['cgf_lambda_settings']

        if 'CloudCanvas' in sys.modules:
            del sys.modules['CloudCanvas']

    @mock.patch('cgf_lambda_settings._LambdaSettingsModule__load_settings')
    def test_settings_loaded_when_imported(self, mock_load_settings):
        import cgf_lambda_settings
        self.assertIs(cgf_lambda_settings.settings,
                      mock_load_settings.return_value)
        self.assertIs(cgf_lambda_settings.settings,
                      mock_load_settings.return_value)
        mock_load_settings.assert_called_once_with()

    @mock.patch('cgf_lambda_settings._LambdaSettingsModule__load_settings',
                return_value=Data({'Test': 'Settings'}))
    def test_get_settings_returns_setting_value(self, mock_load_settings):
        import CloudCanvas
        import cgf_lambda_settings
        self.assertEquals(cgf_lambda_settings.settings.Test, 'Settings')
        self.assertEquals(cgf_lambda_settings.get_setting('Test'), 'Settings')
        self.assertEquals(CloudCanvas.get_setting('Test'), 'Settings')

    @mock.patch('os.path.isfile', return_value=False)
    def test_load_settings_raises_if_no_settings_file(self, mock_isfile):
        import cgf_lambda_settings
        expected_path = os.path.abspath(
            os.path.join(__file__, '..', '..', 'cgf_lambda_settings',
                         'settings.json'))
        with self.assertRaisesRegexp(RuntimeError, 'settings.json'):
            cgf_lambda_settings._LambdaSettingsModule__load_settings()
        mock_isfile.assert_called_once_with(expected_path)

    @mock.patch('os.path.isfile', return_value=True)
    @mock.patch('json.load', return_value={'Test': 'Setting'})
    def test_load_settings_loads_settings_file(self, mock_json_load,
                                               mock_isfile):
        import cgf_lambda_settings
        mock_open = mock.mock_open()
        with mock.patch('__builtin__.open', mock_open, create=True):
            expected_path = os.path.abspath(
                os.path.join(__file__, '..', '..', 'cgf_lambda_settings',
                             'settings.json'))
            result = cgf_lambda_settings._LambdaSettingsModule__load_settings()
            self.assertEquals(result.DATA, mock_json_load.return_value)
            mock_isfile.assert_called_once_with(expected_path)
            mock_open.assert_called_once_with(expected_path, 'r')
            mock_handle = mock_open()
            mock_json_load.assert_called_once_with(mock_handle)
Exemple #15
0
    def test_init_with_empty_dict_uses_dict(self):

        expected = {}
        target = Data(expected)

        self.assertIs(target.DATA, expected)
Exemple #16
0
    def test_nonzero(self):

        self.assertTrue(Data({'A': 10}))
        self.assertFalse(Data())
Exemple #17
0
    def test_len(self):

        self.assertEqual(len(Data({'A': 10})), 1)
        self.assertEqual(len(Data()), 0)
Exemple #18
0
    def test_getattr_returns_Data_when_not_read_only(self):

        target = Data()

        self.assertIsInstance(target.A.B.C, Data)
Exemple #19
0
    def test_init_wraps_dicts(self):

        target = Data({'A': {'x': 1}})

        self.assertIsInstance(target.A, Data)
        self.assertEqual(target.A.x, 1)
Exemple #20
0
    def __init__(self, url, **kwargs):
        '''Initializes a path object.

        Arguments:

          - url: the url represented by the Path object.

          - kwargs: the configuration used by the Path object. The following 
          configuration properties are supported:

            - session: a boto3.Session object that has the aws credentials and
            region used to sign the request. If not present, the request is not
            signed.

            - role_arn: identifies a role to be assumed before making the request.
            The credentials provided by the session property are used to assume 
            the role, and the requesting credentials are used to make the request.

            - role_session_name: the session name used when assuming the role. If
            not set, then 'cgf_service_client' is used.

            - verbose: enables logging of requests and responses.

        '''

        # TODO: add support for a "spec" configuration item. A spec is a description
        # of the child paths and operations supported by this path. A spec can be
        # generated from a swagger api description, 'compiled' represtantion of that
        # data.
        #
        # Such data isn't that useful in production envrionments but can be invaluable
        # during development. For example, the Path object could popluate itself with
        # attributes representing the expected paths, and delete the attributes
        # representing unsupported operations. This would allow you to use a REPL
        # prompt to easily explore an api.
        #
        # It could also be used to validate request and response data, which helps
        # detect and isoate issues early in development. However, this verification
        # usually isn't worth the overhead in production, after everything is working.
        #
        # A spec for an api would look something like the following. It is basically
        # a tree where the keys are the allowed attributes at each location. Each
        # node can be annotated with metadata denoted using $key$.
        #
        #   {
        #       "foo": {
        #           "{param_name}": {
        #               "$type$": "string",
        #               "bar": {
        #                   "POST": {
        #                       "$body$": {                    <-- denormalized type definition
        #                           "$type$": "MyType",
        #                           "MyProp": {
        #                               "$type$": "string"
        #                           }
        #                       },
        #                       "MyQueryParam": {
        #                           "$type$": "integer"
        #                       },
        #                       "$result$": {                  <-- denormalized type definition
        #                           "$type$": "MyType",
        #                           "MyProp": {
        #                               "$type$": "string"
        #                           }
        #                       }
        #                   }
        #               }
        #           }
        #       }
        #   }
        #
        # A key feature of this approach is the denormalization of the type data, seen
        # in this example where the POST operation returns the same type of data as is
        # provied in the body. This allows each child path to be passed child nodes from
        # the spec without worrying about looksing track of type definitinos. In practice
        # the same object would be refeferenced from multiple locations the in spec, so
        # there isn't a lot of memory being used.
        #
        # Some special handling of '..' would be needed if you want spec data to flow up
        # the path. Currently we don't keep track of the parent of the path, meaning:
        #
        #    assertFalse( foo.bar.APPLY('..') is foo )
        #
        # This can lead to some unexpected behavior where configuration is concerned:
        #
        #    foo = Path(url, A = 1)
        #    assertEquals(foo.CONFIG.A, 1)
        #    bar = foo.APPLY('bar', A = 2)
        #    assertEquals(bar.CONFIG.A, 2)
        #    foo2 = bar.APPLY('..')
        #    assertEquals(foo2.CONFIG.A, 2)
        #    assertEquals(foo2.URL, foo.URL)
        #    assertNotEquals(foo2.CONFIG.A, foo.CONFIG.A)
        #    assertNotEquals(foo2, foo)
        #

        if url.endswith('/'):
            url = url[:-1]

        self.__url = url

        # Apply passes along it's current config using the __config key word argument
        # so that it can be reused. If that arg isn't present, kwargs is the config.
        self.__config = kwargs.get('__config', Data(kwargs))
Exemple #21
0
    def __service_request(self, method, body=None, params=None):
        '''Submits a network requests and waits for a response.

        Arguments:

            method - the functin to be called to execute the request.

            body - the json content submited with the request

            params - query string parameters sent with the request

        Returns:

            The json content from response wrapped by a Data object. The returned 
            object will have a PATH attribute that is this path object and a RESPONSE 
            attribute that is the response object for the http request. The Data
            object will be read only.

        '''

        if self.__config.session:
            if self.__config.role_arn:

                sts = self.__config.session.client('sts')

                res = sts.assume_role(
                    RoleArn=self.__config.role_arn,
                    RoleSessionName=self.__config.role_session_name
                    or 'cgf_service_client')

                access_key = res['Credentials']['AccessKeyId']
                secret_key = res['Credentials']['SecretAccessKey']
                session_token = res['Credentials']['SessionToken']
                auth_description = self.__config.role_arn

            else:

                session_credentials = self.__config.session.get_credentials(
                ).get_frozen_credentials()

                access_key = session_credentials.access_key
                secret_key = session_credentials.secret_key
                session_token = session_credentials.token
                auth_description = 'session'

            auth = requests_aws4auth.AWS4Auth(
                access_key,
                secret_key,
                self.__config.session.region_name,
                'execute-api',
                session_token=session_token)

        else:

            auth = None
            auth_description = None

        if self.__config.verbose:
            print('HTTP', method.__name__.upper(), self.__url, params, body,
                  auth_description)

        response = method(self.__url, params=params, json=body, auth=auth)

        if self.__config.verbose:
            print('  -->', response.status_code, response.text)

        error.HttpError.for_response(response)

        if response.content:

            result = response.json()

            # TODO: the generated api gateway lambda displatch mapping injects this 'result' thing
            # in there, it shouldn't do this because it breaks the contract defined by the swagger.
            # To fix it, we need to:
            #
            # 1) change swagger_processor.lambda_dispatch to return the response provided by the
            # lambda directly. Problem is that breaks the additional_response_template_content feature,
            # which can probabally be removed all together.
            #
            # 2) change the c++ client generator handle the missing result property
            #
            # 3) change cgp to handle the missing result property
            #
            # 4) remove the following code
            #
            result = result.get('result', None)
            if result is None:
                return RuntimeError(
                    'Service response did not include the stupid result property.'
                )
            #
            # END TODO

            return Data(result, read_only=True, PATH=self, RESPONSE=response)

        else:

            return None
Exemple #22
0
    def test_init_sets_simple_attributes_from_dict_when_read_only(self):

        target = Data({'A': 1, 'B': 'foo'}, read_only=True)

        self.assertEqual(target.A, 1)
        self.assertEqual(target.B, 'foo')
Exemple #23
0
    def test_init_sets_simple_attributes_from_dict(self):

        target = Data({'A': 1, 'B': 'foo'})

        self.assertEqual(target.A, 1)
        self.assertEqual(target.B, 'foo')
Exemple #24
0
    def test_getattr_rases_when_read_only(self):

        target = Data(read_only=True)

        with self.assertRaises(RuntimeError):
            target.A