Exemplo n.º 1
0
def _get_table_metadata(*, table_key: str, index: int,
                        source: str) -> Dict[str, Any]:

    results_dict = {
        'tableData': {},
        'msg': '',
    }

    try:
        table_endpoint = _get_table_endpoint()
        url = '{0}/{1}'.format(table_endpoint, table_key)
        response = request_metadata(url=url)
    except ValueError as e:
        # envoy client BadResponse is a subclass of ValueError
        message = 'Encountered exception: ' + str(e)
        results_dict['msg'] = message
        results_dict['status_code'] = getattr(e, 'code',
                                              HTTPStatus.INTERNAL_SERVER_ERROR)
        logging.exception(message)
        return results_dict

    status_code = response.status_code
    results_dict['status_code'] = status_code

    if status_code != HTTPStatus.OK:
        message = 'Encountered error: Metadata request failed'
        results_dict['msg'] = message
        logging.error(message)
        return results_dict

    try:
        table_data_raw: dict = response.json()

        # Ideally the response should include 'key' to begin with
        table_data_raw['key'] = table_key

        results_dict['tableData'] = marshall_table_full(table_data_raw)
        results_dict['msg'] = 'Success'
        return results_dict
    except Exception as e:
        message = 'Encountered exception: ' + str(e)
        results_dict['msg'] = message
        logging.exception(message)
        # explicitly raise the exception which will trigger 500 api response
        results_dict['status_code'] = getattr(e, 'code',
                                              HTTPStatus.INTERNAL_SERVER_ERROR)
        return results_dict
Exemplo n.º 2
0
def _get_related_dashboards_metadata(*, url: str) -> Dict[str, Any]:

    results_dict = {
        'dashboards': [],
        'msg': '',
    }

    try:
        response = request_metadata(url=url)
    except ValueError as e:
        # envoy client BadResponse is a subclass of ValueError
        message = 'Encountered exception: ' + str(e)
        results_dict['msg'] = message
        results_dict['status_code'] = getattr(e, 'code',
                                              HTTPStatus.INTERNAL_SERVER_ERROR)
        logging.exception(message)
        return results_dict

    status_code = response.status_code
    results_dict['status_code'] = status_code

    if status_code != HTTPStatus.OK:
        message = f'Encountered {status_code} Error: Related dashboard metadata request failed'
        results_dict['msg'] = message
        logging.error(message)
        return results_dict

    try:
        dashboard_data_raw = response.json().get('dashboards', [])
        return {
            'dashboards': [
                marshall_dashboard_partial(dashboard)
                for dashboard in dashboard_data_raw
            ],
            'msg':
            'Success',
            'status_code':
            status_code
        }
    except Exception as e:
        message = 'Encountered exception: ' + str(e)
        results_dict['msg'] = message
        logging.exception(message)
        # explicitly raise the exception which will trigger 500 api response
        results_dict['status_code'] = getattr(e, 'code',
                                              HTTPStatus.INTERNAL_SERVER_ERROR)
        return results_dict
Exemplo n.º 3
0
def get_feature_generation_code() -> Response:
    """
    Call metadata service to fetch feature generation code
    :return:
    """
    try:
        feature_key = get_query_param(request.args, 'key')

        endpoint = _get_feature_endpoint()

        url = f'{endpoint}/{feature_key}/generation_code'
        response = request_metadata(url=url, method=request.method)
        payload = response.json()
        return make_response(jsonify(payload), 200)
    except Exception as e:
        payload = jsonify({'msg': 'Encountered exception: ' + str(e)})
        return make_response(payload, HTTPStatus.INTERNAL_SERVER_ERROR)
Exemplo n.º 4
0
def update_bookmark() -> Response:
    """
    Call metadata service to PUT or DELETE a bookmark
    Params
    :param type: Resource type for the bookmarked item. e.g. 'table'
    :param key: Resource key for the bookmarked item.
    :return:
    """
    @action_logging
    def _log_update_bookmark(*, resource_key: str, resource_type: str,
                             method: str) -> None:
        pass  # pragma: no cover

    try:
        if app.config['AUTH_USER_METHOD']:
            user = app.config['AUTH_USER_METHOD'](app)
        else:
            raise Exception('AUTH_USER_METHOD is not configured')

        args = request.get_json()
        resource_type = get_query_param(args, 'type')
        resource_key = get_query_param(args, 'key')

        url = '{0}{1}/{2}/follow/{3}/{4}'.format(
            app.config['METADATASERVICE_BASE'], USER_ENDPOINT, user.user_id,
            resource_type, resource_key)

        _log_update_bookmark(resource_key=resource_key,
                             resource_type=resource_type,
                             method=request.method)

        response = request_metadata(url=url, method=request.method)
        status_code = response.status_code

        return make_response(
            jsonify({
                'msg': 'success',
                'response': response.json()
            }), status_code)
    except Exception as e:
        message = 'Encountered exception: ' + str(e)
        logging.exception(message)
        return make_response(jsonify({'msg': message}),
                             HTTPStatus.INTERNAL_SERVER_ERROR)
Exemplo n.º 5
0
def put_column_description() -> Response:
    @action_logging
    def _log_put_column_description(*, table_key: str, column_name: str,
                                    description: str, source: str) -> None:
        pass  # pragma: no cover

    try:
        args = request.get_json()

        table_key = get_query_param(args, 'key')
        table_endpoint = _get_table_endpoint()

        column_name = get_query_param(args, 'column_name')
        description = get_query_param(args, 'description')

        src = get_query_param(args, 'source')

        table_uri = TableUri.from_uri(table_key)
        if not is_table_editable(table_uri.schema, table_uri.table):
            return make_response('', HTTPStatus.FORBIDDEN)

        url = '{0}/{1}/column/{2}/description'.format(table_endpoint,
                                                      table_key, column_name)
        _log_put_column_description(table_key=table_key,
                                    column_name=column_name,
                                    description=description,
                                    source=src)

        response = request_metadata(url=url,
                                    method='PUT',
                                    data=json.dumps(
                                        {'description': description}))
        status_code = response.status_code

        if status_code == HTTPStatus.OK:
            message = 'Success'
        else:
            message = 'Update column description failed'

        payload = jsonify({'msg': message})
        return make_response(payload, status_code)
    except Exception as e:
        payload = jsonify({'msg': 'Encountered exception: ' + str(e)})
        return make_response(payload, HTTPStatus.INTERNAL_SERVER_ERROR)
Exemplo n.º 6
0
def get_bookmark() -> Response:
    """
    Call metadata service to fetch a specified user's bookmarks.
    If no 'user_id' is specified, it will fetch the logged-in user's bookmarks
    :param user_id: (optional) the user whose bookmarks are fetched.
    :return: a JSON object with an array of bookmarks under 'bookmarks' key
    """
    try:
        user_id = request.args.get('user_id')
        if user_id is None:
            if app.config['AUTH_USER_METHOD']:
                user_id = app.config['AUTH_USER_METHOD'](app).user_id
            else:
                raise Exception('AUTH_USER_METHOD is not configured')

        url = '{0}{1}/{2}/follow/'.format(app.config['METADATASERVICE_BASE'],
                                          USER_ENDPOINT, user_id)

        response = request_metadata(url=url, method=request.method)
        status_code = response.status_code

        if status_code == HTTPStatus.OK:
            message = 'Success'
            tables = response.json().get('table')
            table_bookmarks = [
                marshall_table_partial(table) for table in tables
            ]
        else:
            message = f'Encountered error: failed to get bookmark for user_id: {user_id}'
            logging.error(message)
            table_bookmarks = []

        return make_response(
            jsonify({
                'msg': message,
                'bookmarks': table_bookmarks
            }), status_code)
    except Exception as e:
        message = 'Encountered exception: ' + str(e)
        logging.exception(message)
        return make_response(jsonify({'msg': message}),
                             HTTPStatus.INTERNAL_SERVER_ERROR)
Exemplo n.º 7
0
def popular_tables() -> Response:
    """
    call the metadata service endpoint to get the current popular tables
    :return: a json output containing an array of popular table metadata as 'popular_tables'

    Schema Defined Here:
    https://github.com/lyft/amundsenmetadatalibrary/blob/master/metadata_service/api/popular_tables.py
    """
    try:
        if app.config['AUTH_USER_METHOD'] and app.config[
                'POPULAR_TABLE_PERSONALIZATION']:
            user_id = app.config['AUTH_USER_METHOD'](app).user_id
        else:
            user_id = ''

        service_base = app.config['METADATASERVICE_BASE']
        count = app.config['POPULAR_TABLE_COUNT']
        url = f'{service_base}{POPULAR_TABLES_ENDPOINT}/{user_id}?limit={count}'

        response = request_metadata(url=url)
        status_code = response.status_code

        if status_code == HTTPStatus.OK:
            message = 'Success'
            response_list = response.json().get('popular_tables')
            popular_tables = [
                marshall_table_partial(result) for result in response_list
            ]
        else:
            message = 'Encountered error: Request to metadata service failed with status code ' + str(
                status_code)
            logging.error(message)
            popular_tables = [{}]

        payload = jsonify({'results': popular_tables, 'msg': message})
        return make_response(payload, status_code)
    except Exception as e:
        message = 'Encountered exception: ' + str(e)
        logging.exception(message)
        payload = jsonify({'results': [{}], 'msg': message})
        return make_response(payload, HTTPStatus.INTERNAL_SERVER_ERROR)
Exemplo n.º 8
0
def get_user_own() -> Response:
    """
    Calls metadata service to GET owned resources
    :return: a JSON object with an array of owned resources
    """
    try:
        user_id = get_query_param(request.args, 'user_id')

        url = '{0}{1}/{2}/own/'.format(app.config['METADATASERVICE_BASE'],
                                       USER_ENDPOINT,
                                       user_id)
        response = request_metadata(url=url, method=request.method)
        status_code = response.status_code
        owned_tables_raw = response.json().get('table')
        owned_tables = [marshall_table_partial(table) for table in owned_tables_raw]
        return make_response(jsonify({'msg': 'success', 'own': owned_tables}), status_code)

    except Exception as e:
        message = 'Encountered exception: ' + str(e)
        logging.exception(message)
        return make_response(jsonify({'msg': message}), HTTPStatus.INTERNAL_SERVER_ERROR)
Exemplo n.º 9
0
def get_table_description() -> Response:
    try:
        table_endpoint = _get_table_endpoint()
        table_key = get_query_param(request.args, 'key')

        url = '{0}/{1}/description'.format(table_endpoint, table_key)

        response = request_metadata(url=url)
        status_code = response.status_code

        if status_code == HTTPStatus.OK:
            message = 'Success'
            description = response.json().get('description')
        else:
            message = 'Get table description failed'
            description = None

        payload = jsonify({'description': description, 'msg': message})
        return make_response(payload, status_code)
    except Exception as e:
        payload = jsonify({'description': None, 'msg': 'Encountered exception: ' + str(e)})
        return make_response(payload, HTTPStatus.INTERNAL_SERVER_ERROR)
Exemplo n.º 10
0
def get_table_lineage() -> Response:
    """
    Call metadata service to fetch table lineage for a given table
    :return:
    """
    try:
        table_endpoint = _get_table_endpoint()
        table_key = get_query_param(request.args, 'key')
        url = f'{table_endpoint}/{table_key}/lineage'
        response = request_metadata(url=url, method=request.method)
        json = response.json()
        downstream = [marshall_lineage_table(table) for table in json.get('downstream_entities')]
        upstream = [marshall_lineage_table(table) for table in json.get('upstream_entities')]

        payload = {
            'downstream_entities': downstream,
            'upstream_entities': upstream,
        }
        return make_response(jsonify(payload), 200)
    except Exception as e:
        payload = jsonify({'msg': 'Encountered exception: ' + str(e)})
        return make_response(payload, HTTPStatus.INTERNAL_SERVER_ERROR)
Exemplo n.º 11
0
def put_column_description() -> Response:
    @action_logging
    def _log_put_column_description(*, table_key: str, column_name: str,
                                    description: str, source: str) -> None:
        pass  # pragma: no cover

    try:
        args = request.get_json()

        table_key = get_table_key(args)
        table_endpoint = _get_table_endpoint()

        column_name = get_query_param(args, 'column_name')
        description = get_query_param(args, 'description')
        description = ' '.join(description.split())

        src = get_query_param(args, 'source')

        url = '{0}/{1}/column/{2}/description/{3}'.format(
            table_endpoint, table_key, column_name, description)
        _log_put_column_description(table_key=table_key,
                                    column_name=column_name,
                                    description=description,
                                    source=src)

        response = request_metadata(url=url, method='PUT')
        status_code = response.status_code

        if status_code == HTTPStatus.OK:
            message = 'Success'
        else:
            message = 'Update column description failed'

        payload = jsonify({'msg': message})
        return make_response(payload, status_code)
    except Exception as e:
        payload = jsonify({'msg': 'Encountered exception: ' + str(e)})
        return make_response(payload, HTTPStatus.INTERNAL_SERVER_ERROR)
Exemplo n.º 12
0
def update_feature_owner() -> Response:
    try:
        args = request.get_json()
        feature_key = get_query_param(args, 'key')
        owner = get_query_param(args, 'owner')

        endpoint = _get_feature_endpoint()

        url = '{0}/{1}/owner/{2}'.format(endpoint, feature_key, owner)
        method = request.method

        response = request_metadata(url=url, method=method)
        status_code = response.status_code

        if status_code == HTTPStatus.OK:
            message = 'Updated owner'
        else:
            message = 'There was a problem updating owner {0}'.format(owner)

        payload = jsonify({'msg': message})
        return make_response(payload, status_code)
    except Exception as e:
        payload = jsonify({'msg': 'Encountered exception: ' + str(e)})
        return make_response(payload, HTTPStatus.INTERNAL_SERVER_ERROR)
Exemplo n.º 13
0
    def _authorize_access(self, user_id: str) -> None:
        """
        Get Mode user ID via metadata service. Note that metadata service needs to be at least v2.5.2 and
        Databuilder should also have ingested Mode user.
        https://github.com/lyft/amundsendatabuilder#modedashboarduserextractor

        :param user_id:
        :return:
        :raise: PermissionError when user is not allowed to access the dashboard
        """

        metadata_svc_url = '{0}{1}/{2}'.format(
            app.config['METADATASERVICE_BASE'], USER_ENDPOINT, user_id)
        response = request_metadata(url=metadata_svc_url)
        response.raise_for_status()

        user = load_user(response.json())
        if user.is_active and user.other_key_values and user.other_key_values.get(
                'mode_user_id'):
            return

        raise PermissionError(
            'User {} is not authorized to preview Mode Dashboard'.format(
                user_id))
Exemplo n.º 14
0
def _get_table_metadata(*, table_key: str, index: int,
                        source: str) -> Dict[str, Any]:
    def _map_user_object_to_schema(u: Dict) -> Dict:
        return dump_user(load_user(u))

    results_dict = {
        'tableData': {},
        'msg': '',
    }

    try:
        table_endpoint = _get_table_endpoint()
        url = '{0}/{1}'.format(table_endpoint, table_key)
        response = request_metadata(url=url)
    except ValueError as e:
        # envoy client BadResponse is a subclass of ValueError
        message = 'Encountered exception: ' + str(e)
        results_dict['msg'] = message
        results_dict['status_code'] = getattr(e, 'code',
                                              HTTPStatus.INTERNAL_SERVER_ERROR)
        logging.exception(message)
        return results_dict

    status_code = response.status_code
    results_dict['status_code'] = status_code

    if status_code != HTTPStatus.OK:
        message = 'Encountered error: Metadata request failed'
        results_dict['msg'] = message
        logging.error(message)
        return results_dict

    try:
        # Filter and parse the response dictionary from the metadata service
        params = [
            'columns',
            'cluster',
            'database',
            'owners',
            'is_view',
            'schema',
            'table_description',
            'table_name',
            'table_readers',
            'table_writer',
            'tags',
            'watermarks',
            'source',
        ]

        results = {key: response.json().get(key, None) for key in params}
        results['key'] = table_key

        is_editable = results['schema'] not in app.config['UNEDITABLE_SCHEMAS']
        results['is_editable'] = is_editable

        # In the list of owners, sanitize each entry
        results['owners'] = [
            _map_user_object_to_schema(owner) for owner in results['owners']
        ]

        # In the list of reader_objects, sanitize the reader value on each entry
        readers = results['table_readers']
        for reader_object in readers:
            reader_object['reader'] = _map_user_object_to_schema(
                reader_object['reader'])

        # If order is provided, we sort the column based on the pre-defined order
        if app.config['COLUMN_STAT_ORDER']:
            columns = results['columns']
            for col in columns:
                # the stat_type isn't defined in COLUMN_STAT_ORDER, we just use the max index for sorting
                col['stats'].sort(
                    key=lambda x: app.config['COLUMN_STAT_ORDER'].get(
                        x['stat_type'], len(app.config['COLUMN_STAT_ORDER'])))
                col['is_editable'] = is_editable

        # Temp code to make 'partition_key' and 'partition_value' part of the table
        results['partition'] = _get_partition_data(results['watermarks'])

        results_dict['tableData'] = results
        results_dict['msg'] = 'Success'
        return results_dict
    except Exception as e:
        message = 'Encountered exception: ' + str(e)
        results_dict['msg'] = message
        logging.exception(message)
        # explicitly raise the exception which will trigger 500 api response
        results_dict['status_code'] = getattr(e, 'code',
                                              HTTPStatus.INTERNAL_SERVER_ERROR)
        return results_dict