Beispiel #1
0
def test_optional_argument():
    assert parse_url_path('foo/bar/:changes') == {
        'path': 'foo/bar',
        'changes': None
    }
    assert parse_url_path('foo/bar/:changes/42') == {
        'path': 'foo/bar',
        'changes': 42
    }
    assert parse_url_path('foo/bar/:changes/-42') == {
        'path': 'foo/bar',
        'changes': -42
    }

    assert build_url_path({
        'path': 'foo/bar',
        'changes': None
    }) == 'foo/bar/:changes'
    assert build_url_path({
        'path': 'foo/bar',
        'changes': 42
    }) == 'foo/bar/:changes/42'
    assert build_url_path({
        'path': 'foo/bar',
        'changes': -42
    }) == 'foo/bar/:changes/-42'
Beispiel #2
0
def get_response_type(context: Context, request: Request, params: dict = None):
    if params is None and 'path' in request.path_params:
        path = request.path_params['path'].strip('/')
        params = parse_url_path(path)
    elif params is None:
        params = {}

    if 'format' in params:
        return params['format']

    if 'accept' in request.headers and request.headers['accept']:
        formats = {
            'text/html': 'html',
            'application/xhtml+xml': 'html',
        }
        config = context.get('config')
        for name, exporter in config.exporters.items():
            for media_type in exporter.accept_types:
                formats[media_type] = name

        media_types, _ = cgi.parse_header(request.headers['accept'])
        for media_type in media_types.lower().split(','):
            if media_type in formats:
                return formats[media_type]

    return 'json'
Beispiel #3
0
def test_no_args():
    string = 'foo/bar/:count'
    params = {
        'path': 'foo/bar',
        'count': [],
    }
    assert parse_url_path(string) == params
    assert build_url_path(params) == string
Beispiel #4
0
def test_format():
    string = 'foo/bar/:format/csv'
    params = {
        'path': 'foo/bar',
        'format': 'csv',
    }
    assert parse_url_path(string) == params
    assert build_url_path(params) == string
Beispiel #5
0
def test_id_integer():
    string = 'foo/bar/42'
    params = {
        'path': 'foo/bar',
        'id': {'value': 42, 'type': 'integer'},
    }
    assert parse_url_path(string) == params
    assert build_url_path(params) == string
Beispiel #6
0
def test_parse_url_path():
    assert parse_url_path('foo/bar') == {'path': 'foo/bar'}
    assert parse_url_path('foo/bar/:source/deeply/nested/name') == {
        'path': 'foo/bar',
        'source': 'deeply/nested/name'
    }
    assert parse_url_path('foo/bar/3/:source/vrk') == {
        'path': 'foo/bar',
        'id': {
            'value': '3',
            'type': 'integer'
        },
        'source': 'vrk'
    }
    assert parse_url_path('foo/bar/:limit/100') == {
        'path': 'foo/bar',
        'limit': 100
    }
Beispiel #7
0
def test_id_sha1():
    string = 'foo/bar/69a33b149af7a7eeb25026c8cdc09187477ffe21'
    params = {
        'path': 'foo/bar',
        'id': {
            'value': '69a33b149af7a7eeb25026c8cdc09187477ffe21',
            'type': 'sha1',
        },
    }
    assert parse_url_path(string) == params
    assert build_url_path(params) == string
Beispiel #8
0
def test_sort():
    string = 'foo/bar/:sort/a/-b'
    params = {
        'path': 'foo/bar',
        'sort': [
            {'name': 'a', 'ascending': True},
            {'name': 'b', 'ascending': False},
        ],
    }
    assert parse_url_path(string) == params
    assert build_url_path(params) == string
Beispiel #9
0
def _dependencies(context: Context, model, deps):
    if deps:
        command_calls = {}
        model_names = set()
        prop_names = []
        prop_name_mapping = {}
        for name, dep in deps.items():
            if isinstance(dep, dict):
                command_calls[name] = dep
                continue

            if '.' not in dep:
                context.error(
                    f"Dependency must be in 'object/name.property' form, got: {dep}."
                )
            model_name, prop_name = dep.split('.', 1)
            model_names.add(model_name)
            prop_names.append(prop_name)
            prop_name_mapping[prop_name] = name

        if len(model_names) > 1:
            names = ', '.join(sorted(model_names))
            context.error(
                f"Dependencies are allowed only from single model, but more than one model found: {names}."
            )

        if len(command_calls) > 1:
            context.error(f"Only one command call is allowed.")

        if len(command_calls) > 0:
            if len(model_names) > 0:
                context.error(
                    f"Only one command call or one model is allowed in dependencies."
                )
            for name, cmd in command_calls.items():
                cmd = load(context,
                           Command(),
                           cmd,
                           parent=model,
                           scope='service')
                for value in cmd(context):
                    yield {name: value}
        else:
            model_name = list(model_names)[0]
            params = parse_url_path(model_name)
            depmodel = get_model_from_params(model.manifest, params['path'],
                                             params)
            for row in getall(context,
                              depmodel,
                              depmodel.backend,
                              show=prop_names):
                yield {prop_name_mapping[k]: v for k, v in row.items()}
    else:
        yield {}
Beispiel #10
0
    def dependencies(self, model, deps):
        if deps:
            command_calls = {}
            model_names = set()
            prop_names = []
            prop_name_mapping = {}
            for name, dep in deps.items():
                if isinstance(dep, dict):
                    command_calls[name] = dep
                    continue

                if '.' not in dep:
                    self.error(
                        f"Dependency must be in 'object/name.property' form, got: {dep}."
                    )
                model_name, prop_name = dep.split('.', 1)
                model_names.add(model_name)
                prop_names.append(prop_name)
                prop_name_mapping[prop_name] = name

            if len(model_names) > 1:
                names = ', '.join(sorted(model_names))
                self.error(
                    f"Dependencies are allowed only from single model, but more than one model found: {names}."
                )

            if len(command_calls) > 1:
                self.error(f"Only one command call is allowed.")

            if len(command_calls) > 0:
                if len(model_names) > 0:
                    self.error(
                        f"Only one command call or one model is allowed in dependencies."
                    )
                for name, cmd in command_calls.items():
                    for value in self.run(model, cmd):
                        yield {name: value}
            else:
                model_name = list(model_names)[0]
                params = parse_url_path(model_name)
                for row in self.store.getall(params['path'], {
                        'show': prop_names,
                        'source': params['source']
                }):
                    yield {prop_name_mapping[k]: v for k, v in row.items()}
        else:
            yield {}
Beispiel #11
0
def prepare(context: Context, params: UrlParams, version: Version,
            request: Request) -> UrlParams:
    path = request.path_params['path'].strip('/')
    p = parse_url_path(path)
    params.model = p['path']
    params.id = p.get('id')
    params.dataset = p.get('source')
    params.sort = p.get('sort')
    params.limit = p.get('limit')

    if 'changes' in p:
        params.changes = True
        params.offset = p['changes'] or -10
    else:
        params.changes = False
        params.offset = p.get('offset')

    params.format = get_response_type(context, request, p)
    params.count = p.get('count')
    params.params = p  # XXX: for backwards compatibility
    return params
Beispiel #12
0
async def homepage(request):
    global store

    url_path = request.path_params['path'].strip('/')
    params = parse_url_path(url_path)
    path = params['path']

    fmt = params.get('format', 'html')

    if fmt == 'html':
        header = []
        data = []
        formats = []
        items = []
        datasets = []
        row = []
        loc = get_current_location(path, params)

        if 'source' in params:
            formats = [
                ('CSV', '/' + build_url_path({
                    **params, 'format': 'csv'
                })),
                ('JSON', '/' + build_url_path({
                    **params, 'format': 'json'
                })),
                ('JSONL', '/' + build_url_path({
                    **params, 'format': 'jsonl'
                })),
                ('ASCII', '/' + build_url_path({
                    **params, 'format': 'asciitable'
                })),
            ]

            if 'changes' in params:
                data = get_changes(store, params)
                header = next(data)
                data = list(reversed(list(data)))
            else:
                if 'id' in params:
                    row = list(get_row(store, params))
                else:
                    data = get_data(store, params)
                    header = next(data)
                    data = list(data)

        else:
            datasets_by_object = get_datasets_by_object(store)
            tree = build_path_tree(
                store.objects['default'].get('model', {}).keys(),
                datasets_by_object.keys(),
            )
            items = get_directory_content(tree, path) if path in tree else []
            datasets = list(get_directory_datasets(datasets_by_object, path))

        return templates.TemplateResponse(
            'base.html', {
                'request': request,
                'location': loc,
                'formats': formats,
                'items': items,
                'datasets': datasets,
                'header': header,
                'data': data,
                'row': row,
            })

    elif fmt in ('csv', 'json', 'jsonl', 'asciitable'):

        async def generator(rows, fmt, params):
            for data in store.export(rows, fmt, params):
                yield data

        if 'changes' in params:
            rows = store.changes(params)

        elif 'id' in params:
            rows = [store.get(params['path'], params['id']['value'], params)]
            params['wrap'] = False

        else:
            rows = store.getall(params['path'], params)

        media_types = {
            'csv': 'text/csv',
            'json': 'application/json',
            'jsonl': 'application/x-json-stream',
            'asciitable': 'text/plain',
        }

        if fmt == 'asciitable':
            params['column_width'] = 42

        return StreamingResponse(generator(rows, fmt, params),
                                 media_type=media_types[fmt])

    else:
        raise HTTPException(status_code=404)