def test_api_action_links(read_json, registry):
    transport = Payload(read_json('transport.json'))
    address = transport.get('meta/gateway')[1]
    service_name = 'foo'
    link_name = 'self'
    links_path = '|'.join([
        'links',
        address,
        nomap(service_name),
        nomap(link_name),
    ])
    action = Action(
        **{
            'action': 'bar',
            'params': [],
            'transport': transport,
            'component': None,
            'path': '/path/to/file.py',
            'name': service_name,
            'version': '1.0',
            'framework_version': '1.0.0',
        })

    # Clear transport links
    assert FIELD_MAPPINGS['links'] in transport
    del transport[FIELD_MAPPINGS['links']]
    assert FIELD_MAPPINGS['links'] not in transport
    assert not transport.path_exists(links_path, delimiter='|')

    # Set a link
    uri = 'http://api.example.com/v1/users/123'
    assert action.set_link(link_name, uri) == action
    assert transport.path_exists(links_path, delimiter='|')
    assert transport.get(links_path, delimiter='|') == uri
def test_api_action_errors(read_json, registry):
    transport = Payload(read_json('transport.json'))
    address = transport.get('meta/gateway')[1]
    service_name = 'foo'
    service_version = '1.0'
    errors_path = '|'.join([
        'errors',
        address,
        nomap(service_name),
        service_version,
    ])
    action = Action(
        **{
            'action': 'bar',
            'params': [],
            'transport': transport,
            'component': None,
            'path': '/path/to/file.py',
            'name': service_name,
            'version': '1.0',
            'framework_version': '1.0.0',
        })

    # Clear transport errors
    assert FIELD_MAPPINGS['errors'] in transport
    del transport[FIELD_MAPPINGS['errors']]
    assert FIELD_MAPPINGS['errors'] not in transport
    assert not transport.path_exists(errors_path, delimiter='|')

    # Set an error
    msg = 'Error message'
    code = 99
    status = '500 Internal Server Error'
    assert action.error(msg, code=code, status=status) == action
    assert transport.path_exists(errors_path, delimiter='|')
    errors = transport.get(errors_path, delimiter='|')
    assert isinstance(errors, list)
    assert len(errors) == 1
    error = errors[0]
    assert isinstance(error, ErrorPayload)
    assert error.get('message') == msg
    assert error.get('code') == code
    assert error.get('status') == status

    # Add a second error
    assert action.error(msg, code=code, status=status) == action
    errors = transport.get(errors_path, delimiter='|')
    assert isinstance(errors, list)
    assert len(errors) == 2
    for error in errors:
        assert isinstance(error, ErrorPayload)
def test_api_action_call_remote(read_json, registry):
    service_name = 'foo'
    service_version = '1.0'

    # Check that registry does not have mappings
    assert not registry.has_mappings
    # Add an empty test action to mappings
    registry.update_registry({
        service_name: {
            service_version: {
                FIELD_MAPPINGS['files']: True,
                FIELD_MAPPINGS['actions']: {
                    'test': {}
                },
            },
        },
    })

    transport = Payload(read_json('transport.json'))
    calls_path = 'calls/{}/{}'.format(nomap(service_name), service_version)
    action = Action(
        **{
            'action': 'test',
            'params': [],
            'transport': transport,
            'component': None,
            'path': '/path/to/file.py',
            'name': service_name,
            'version': service_version,
            'framework_version': '1.0.0',
        })

    # Clear transport calls
    assert transport.path_exists('calls')
    del transport[FIELD_MAPPINGS['calls']]
    assert not transport.path_exists('calls')

    # Prepare call arguments
    params = [Param('dummy', value=123)]
    c_addr = '87.65.43.21:4321'
    c_name = 'foo'
    c_version = '1.1'
    c_action = 'bar'
    c_params = [{
        PARAM['name']: 'dummy',
        PARAM['value']: 123,
        PARAM['type']: TYPE_INTEGER
    }]

    # Make a remotr call
    kwargs = {
        'address': c_addr,
        'service': c_name,
        'version': c_version,
        'action': c_action,
        'params': params,
        'timeout': 2.0,
    }
    assert action.remote_call(**kwargs) == action
    assert transport.path_exists(calls_path)
    calls = transport.get(calls_path)
    assert isinstance(calls, list)
    assert len(calls) == 1
    call = calls[0]
    assert isinstance(call, dict)
    assert get_path(call, 'gateway', default='NO') == 'ktp://{}'.format(c_addr)
    assert get_path(call, 'name', default='NO') == c_name
    assert get_path(call, 'version', default='NO') == c_version
    assert get_path(call, 'action', default='NO') == c_action
    assert get_path(call, 'params', default='NO') == c_params

    # Make a call and add files
    files_path = '|'.join([
        'files',
        transport.get('meta/gateway')[1],
        nomap(c_name),
        c_version,
        nomap(c_action),
    ])
    kwargs['files'] = [action.new_file('download', '/tmp/file.ext')]
    assert action.remote_call(**kwargs) == action
    tr_files = transport.get(files_path, delimiter='|')
    assert isinstance(tr_files, list)
    assert len(tr_files) == 1
    assert tr_files[0] == {
        FIELD_MAPPINGS['name']: 'download',
        FIELD_MAPPINGS['token']: '',
        FIELD_MAPPINGS['filename']: 'file.ext',
        FIELD_MAPPINGS['size']: 0,
        FIELD_MAPPINGS['mime']: 'text/plain',
        FIELD_MAPPINGS['path']: 'file:///tmp/file.ext',
    }

    # Set file server mappings to False and try to call with local files
    registry.update_registry({
        service_name: {
            service_version: {
                FIELD_MAPPINGS['files']: False,
                FIELD_MAPPINGS['actions']: {
                    'test': {}
                },
            },
        },
    })

    # TODO: Figure out why existing action does not see new mappungs.
    #       Action should read the mapping values from previous statement.
    action = Action(
        **{
            'action': 'test',
            'params': [],
            'transport': transport,
            'component': None,
            'path': '/path/to/file.py',
            'name': service_name,
            'version': service_version,
            'framework_version': '1.0.0',
        })

    with pytest.raises(NoFileServerError):
        action.remote_call(**kwargs)
def test_api_action_relate(read_json, registry):
    transport = Payload(read_json('transport.json'))
    address = transport.get('meta/gateway')[1]
    service_name = 'foo'
    pk = '1'
    rel_path_tpl = '|'.join([
        'relations',
        address,
        nomap(service_name),
        nomap(pk),
        '{}',  # Placeholder for address
        service_name,
    ])
    action = Action(
        **{
            'action': 'bar',
            'params': [],
            'transport': transport,
            'component': None,
            'path': '/path/to/file.py',
            'name': service_name,
            'version': '1.0',
            'framework_version': '1.0.0',
        })

    # Clear transport relations
    assert FIELD_MAPPINGS['relations'] in transport
    del transport[FIELD_MAPPINGS['relations']]
    assert FIELD_MAPPINGS['relations'] not in transport

    # Format relations path for local relations
    rel_path = rel_path_tpl.format(address)

    # Check relate one
    assert transport.get(rel_path, default='NO', delimiter='|') == 'NO'
    assert action.relate_one(pk, service_name, '321') == action
    assert transport.get(rel_path, delimiter='|') == '321'

    # Clear transport relations
    del transport[FIELD_MAPPINGS['relations']]

    # Check relate many
    fkeys = ['321', '123']
    assert transport.get(rel_path, default='NO', delimiter='|') == 'NO'
    assert action.relate_many(pk, service_name, fkeys) == action
    assert transport.get(rel_path, delimiter='|') == fkeys

    # Check that relate many fails when a list os not given
    with pytest.raises(TypeError):
        action.relate_many(pk, service_name, 1)

    # Clear transport relations
    del transport[FIELD_MAPPINGS['relations']]

    # Format relations path for remote relations
    remote = 'ktp://87.65.43.21:4321'
    rel_path = rel_path_tpl.format(remote)

    # Check relate one remote
    assert transport.get(rel_path, default='NO', delimiter='|') == 'NO'
    assert action.relate_one_remote(pk, remote, service_name, '321') == action
    assert transport.get(rel_path, delimiter='|') == '321'

    # Clear transport relations
    del transport[FIELD_MAPPINGS['relations']]

    # Check relate many
    assert transport.get(rel_path, default='NO', delimiter='|') == 'NO'
    assert action.relate_many_remote(pk, remote, service_name, fkeys) == action
    assert transport.get(rel_path, delimiter='|') == fkeys

    # Check that relate many fails when a list os not given
    with pytest.raises(TypeError):
        action.relate_many_remote(pk, remote, service_name, 1)
def test_api_action_data(read_json, registry):
    transport = Payload(read_json('transport.json'))
    address = transport.get('meta/gateway')[1]
    service_name = 'users'
    service_version = '1.0.0'
    action_name = 'create'
    data_path = '|'.join([
        'data',
        address,
        nomap(service_name),
        service_version,
        nomap(action_name),
    ])
    action = Action(
        **{
            'action': action_name,
            'params': [],
            'transport': transport,
            'component': None,
            'path': '/path/to/file.py',
            'name': service_name,
            'version': service_version,
            'framework_version': '1.0.0',
        })

    # Clear transport data
    assert FIELD_MAPPINGS['data'] in transport
    del transport[FIELD_MAPPINGS['data']]
    assert FIELD_MAPPINGS['data'] not in transport
    assert not transport.path_exists(data_path, delimiter='|')

    # Set a transport entity
    entity = {'foo': 'bar'}
    assert action.set_entity(entity) == action
    assert transport.path_exists(data_path, delimiter='|')
    assert transport.get(data_path, delimiter='|') == [entity]
    # Set another entity
    assert action.set_entity(entity) == action
    assert transport.get(data_path, delimiter='|') == [entity, entity]

    # Check that entity can only be a dictionary
    with pytest.raises(TypeError):
        action.set_entity(1)

    # Clear transport data
    assert FIELD_MAPPINGS['data'] in transport
    del transport[FIELD_MAPPINGS['data']]
    assert FIELD_MAPPINGS['data'] not in transport
    assert not transport.path_exists(data_path, delimiter='|')

    # Set a transport collection
    collection = [{'foo': 1}, {'bar': 2}]
    assert action.set_collection(collection) == action
    assert transport.path_exists(data_path, delimiter='|')
    assert transport.get(data_path, delimiter='|') == [collection]
    # Set another collection
    assert action.set_collection(collection) == action
    assert transport.get(data_path, delimiter='|') == [collection, collection]

    # Check that collection can only be list
    with pytest.raises(TypeError):
        action.set_collection(1)

    # Items in a collection can only be dict
    with pytest.raises(TypeError):
        action.set_collection([1])