Source code for inspirehep.modules.records.json_ref_loader

# -*- coding: utf-8 -*-
#
# This file is part of INSPIRE.
# Copyright (C) 2014-2017 CERN.
#
# INSPIRE is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# INSPIRE is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with INSPIRE. If not, see <http://www.gnu.org/licenses/>.
#
# In applying this license, CERN does not waive the privileges and immunities
# granted to it by virtue of its status as an Intergovernmental Organization
# or submit itself to any jurisdiction.

"""Resource-aware json reference loaders to be used with jsonref."""

from __future__ import absolute_import, division, print_function

from flask import current_app, url_for
from jsonref import JsonLoader, JsonRef
from werkzeug.urls import url_parse

import jsonresolver
from jsonresolver.contrib.jsonref import json_loader_factory

from inspire_schemas.utils import load_schema
from inspire_utils.urls import ensure_scheme
from inspirehep.modules.pidstore.utils import get_pid_type_from_endpoint
from inspirehep.utils import record_getter


[docs]class AbstractRecordLoader(JsonLoader): """Base for resource-aware record loaders. Resolves the refered resource by the given uri by first checking against local resources. """
[docs] def get_record(self, pid_type, recid): raise NotImplementedError()
[docs] def get_remote_json(self, uri, **kwargs): parsed_uri = url_parse(uri) # Add http:// protocol so uri.netloc is correctly parsed. server_name = current_app.config.get('SERVER_NAME') parsed_server = url_parse(ensure_scheme(server_name)) if parsed_uri.netloc and parsed_uri.netloc != parsed_server.netloc: return super(AbstractRecordLoader, self).get_remote_json(uri, **kwargs) path_parts = parsed_uri.path.strip('/').split('/') if len(path_parts) < 2: current_app.logger.error('Bad JSONref URI: {0}'.format(uri)) return None endpoint = path_parts[-2] pid_type = get_pid_type_from_endpoint(endpoint) recid = path_parts[-1] res = self.get_record(pid_type, recid) return res
[docs]class ESJsonLoader(AbstractRecordLoader): """Resolve resources by retrieving them from Elasticsearch."""
[docs] def get_record(self, pid_type, recid): try: return record_getter.get_es_record(pid_type, recid) except record_getter.RecordGetterError: return None
[docs]class DatabaseJsonLoader(AbstractRecordLoader):
[docs] def get_record(self, pid_type, recid): try: return record_getter.get_db_record(pid_type, recid) except record_getter.RecordGetterError: return None
es_record_loader = ESJsonLoader() db_record_loader = DatabaseJsonLoader() SCHEMA_LOADER_CLS = json_loader_factory( jsonresolver.JSONResolver( plugins=['invenio_jsonschemas.jsonresolver'] ) ) """Used in invenio-jsonschemas to resolve relative $ref."""
[docs]def load_resolved_schema(name): """Load a JSON schema with all references resolved. Args: name(str): name of the schema to load. Returns: dict: the JSON schema with resolved references. Examples: >>> resolved_schema = load_resolved_schema('authors') """ schema = load_schema(name) return JsonRef.replace_refs( schema, base_uri=url_for('invenio_jsonschemas.get_schema', schema_path='records/{}.json'.format(name)), loader=SCHEMA_LOADER_CLS() )
[docs]def replace_refs(obj, source='db'): """Replaces record refs in obj by bypassing HTTP requests. Any reference URI that comes from the same server and references a resource will be resolved directly either from the database or from Elasticsearch. :param obj: Dict-like object for which '$ref' fields are recursively replaced. :param source: List of sources from which to resolve the references. It can be any of: * 'db' - resolve from Database * 'es' - resolve from Elasticsearch * 'http' - force using HTTP :returns: The same obj structure with the '$ref' fields replaced with the object available at the given URI. """ loaders = { 'db': db_record_loader, 'es': es_record_loader, 'http': None } if source not in loaders: raise ValueError('source must be one of {}'.format(loaders.keys())) loader = loaders[source] return JsonRef.replace_refs(obj, loader=loader, load_on_repr=False)