Ejemplo n.º 1
0
def api_frontpage(request):
    """Frontpage of weaver."""

    # import here to avoid circular import errors
    from weaver.config import get_weaver_configuration

    settings = get_settings(request)
    weaver_url = get_weaver_url(settings)
    weaver_config = get_weaver_configuration(settings)

    weaver_api = asbool(settings.get("weaver.wps_restapi"))
    weaver_api_url = get_wps_restapi_base_url(settings) if weaver_api else None
    weaver_api_def = weaver_api_url + sd.api_swagger_ui_uri if weaver_api else None
    weaver_api_doc = settings.get("weaver.wps_restapi_doc", None) if weaver_api else None
    weaver_api_ref = settings.get("weaver.wps_restapi_ref", None) if weaver_api else None
    weaver_wps = asbool(settings.get("weaver.wps"))
    weaver_wps_url = get_wps_url(settings) if weaver_wps else None
    weaver_conform_url = weaver_url + sd.api_conformance_uri
    weaver_process_url = weaver_url + sd.processes_uri
    weaver_links = [
        {"href": weaver_url, "rel": "self", "type": CONTENT_TYPE_APP_JSON, "title": "This document"},
        {"href": weaver_conform_url, "rel": "conformance", "type": CONTENT_TYPE_APP_JSON,
         "title": "WPS 2.0/3.0 REST-JSON Binding Extension conformance classes implemented by this service."},
    ]
    if weaver_api_def:
        weaver_links.append({"href": weaver_api_def, "rel": "service", "type": CONTENT_TYPE_APP_JSON,
                             "title": "API definition of this service."})
    if isinstance(weaver_api_doc, str):
        if "." in weaver_api_doc:   # pylint: disable=E1135,unsupported-membership-test
            ext_type = weaver_api_doc.split(".")[-1]
            doc_type = "application/{}".format(ext_type)
        else:
            doc_type = CONTENT_TYPE_TEXT_PLAIN  # default most basic type
        weaver_links.append({"href": weaver_api_doc, "rel": "documentation", "type": doc_type,
                             "title": "API documentation about this service."})
    if weaver_api_ref:
        weaver_links.append({"href": weaver_api_ref, "rel": "reference", "type": CONTENT_TYPE_APP_JSON,
                             "title": "API reference specification of this service."})
    weaver_links.append({"href": weaver_process_url, "rel": "processes", "type": CONTENT_TYPE_APP_JSON,
                         "title": "Processes offered by this service."})

    return {
        "message": "Weaver Information",
        "configuration": weaver_config,
        "parameters": [
            {"name": "api", "enabled": weaver_api,
             "url": weaver_api_url,
             "doc": weaver_api_doc,
             "api": weaver_api_def,
             "ref": weaver_api_ref},
            {"name": "wps", "enabled": weaver_wps,
             "url": weaver_wps_url},
        ],
        "links": weaver_links,
    }
Ejemplo n.º 2
0
    def __init__(self, *args, **kwargs):
        db_args, db_kwargs = MongodbStore.get_args_kwargs(*args, **kwargs)
        StoreProcesses.__init__(self)
        MongodbStore.__init__(self, *db_args, **db_kwargs)
        registry = kwargs.get("registry")
        default_processes = kwargs.get("default_processes")
        self.settings = kwargs.get("settings",
                                   {}) if not registry else registry.settings
        self.default_host = get_weaver_url(self.settings)
        self.default_wps_endpoint = get_wps_url(self.settings)

        # enforce default process re-registration to receive any applicable update
        if default_processes:
            self._register_defaults(default_processes)
Ejemplo n.º 3
0
def register_builtin_processes(container):
    # type: (AnySettingsContainer) -> None
    """
    Registers every ``builtin`` CWL package to the processes database.

    CWL definitions must be located within the :mod:`weaver.processes.builtin` module.
    """
    restapi_url = get_wps_restapi_base_url(container)
    builtin_apps_mapping = _get_builtin_reference_mapping(os.path.abspath(os.path.dirname(__file__)))
    builtin_processes = []
    for process_id, process_path in builtin_apps_mapping.items():
        process_info = get_process_definition({}, package=None, reference=process_path)
        process_url = "/".join([restapi_url, "processes", process_id])
        process_package = _get_builtin_package(process_id, process_info["package"])
        process_abstract = _get_builtin_metadata(process_id, process_path, "__doc__", clean=True)
        process_version = _get_builtin_metadata(process_id, process_path, "__version__")
        process_title = _get_builtin_metadata(process_id, process_path, "__title__")
        process_payload = {
            "processDescription": {
                "process": {
                    "id": process_id,
                    "type": PROCESS_BUILTIN,
                    "title": process_title,
                    "version": process_version,
                    "abstract": process_abstract,
                }
            },
            "deploymentProfileName": "http://www.opengis.net/profiles/eoc/builtinApplication",
            "executionUnit": [{"unit": process_package}],
        }
        process_payload["processDescription"]["process"].update(ows_context_href(process_url))
        builtin_processes.append(Process(
            id=process_id,
            type=PROCESS_BUILTIN,
            title=process_title,
            version=process_version,
            abstract=process_abstract,
            payload=process_payload,
            package=process_package,
            inputs=process_info["inputs"],
            outputs=process_info["outputs"],
            processDescriptionURL=process_url,
            processEndpointWPS1=get_wps_url(container),
            executeEndpoint="/".join([process_url, "jobs"]),
            visibility=VISIBILITY_PUBLIC,
        ))

    # registration of missing/updated apps automatically applied with 'default_processes'
    get_db(container).get_store(StoreProcesses, default_processes=builtin_processes)
Ejemplo n.º 4
0
    def __init__(self, *args, **kwargs):
        db_args, db_kwargs = MongodbStore.get_args_kwargs(*args, **kwargs)
        StoreProcesses.__init__(self)
        MongodbStore.__init__(self, *db_args, **db_kwargs)
        registry = kwargs.get("registry")
        settings = kwargs.get("settings", {}) if not registry else registry.settings
        default_processes = kwargs.get("default_processes")
        self.default_host = get_weaver_url(settings)
        self.default_wps_endpoint = get_wps_url(settings)

        # enforce default process re-registration to receive any applicable update
        if default_processes:
            registered_processes = [process.identifier for process in self.list_processes()]
            for process in default_processes:
                process_name = self._get_process_id(process)
                if process_name in registered_processes:
                    self.delete_process(process_name)
                self._add_process(process)
Ejemplo n.º 5
0
    def test_deploy_process_default_endpoint_wps1(self):
        """Validates that the default (localhost) endpoint to execute WPS requests are saved during deployment."""
        process_name = self.fully_qualified_test_process_name()
        process_data = self.get_process_deploy_template(process_name)
        package_mock = mocked_process_package()

        with contextlib.ExitStack() as stack:
            for pkg in package_mock:
                stack.enter_context(pkg)
            uri = "/processes"
            resp = self.app.post_json(uri,
                                      params=process_data,
                                      headers=self.json_headers,
                                      expect_errors=True)
            # TODO: status should be 201 when properly modified to match API conformance
            assert resp.status_code == 200

        weaver_wps_path = get_wps_url(self.config.registry.settings)
        process_wps_endpoint = self.process_store.fetch_by_id(
            process_name).processEndpointWPS1
        assert isinstance(process_wps_endpoint,
                          str) and len(process_wps_endpoint)
        assert process_wps_endpoint == weaver_wps_path
Ejemplo n.º 6
0
    def wps_execute(self, version, accept):
        wps_url = get_wps_url(self.settings)
        if version == "1.0.0":
            test_content = "Test file in Docker - WPS KVP"
            wps_method = "GET"
        elif version == "2.0.0":
            test_content = "Test file in Docker - WPS XML"
            wps_method = "POST"
        else:
            raise ValueError("Invalid WPS version: {}".format(version))
        test_content += " {} request - Accept {}".format(wps_method, accept.split("/")[-1].upper())

        with contextlib.ExitStack() as stack_exec:
            # setup
            dir_name = tempfile.gettempdir()
            tmp_path = tempfile.NamedTemporaryFile(dir=dir_name, mode="w", suffix=".txt")
            tmp_file = stack_exec.enter_context(tmp_path)  # noqa
            tmp_file.write(test_content)
            tmp_file.seek(0)
            for mock_exec in mocked_execute_process():
                stack_exec.enter_context(mock_exec)

            # execute
            if version == "1.0.0":
                wps_inputs = ["file={}@mimeType={}".format(tmp_file.name, CONTENT_TYPE_TEXT_PLAIN)]
                wps_params = {
                    "service": "WPS",
                    "request": "Execute",
                    "version": version,
                    "identifier": self.process_id,
                    "DataInputs": wps_inputs,
                }
                wps_headers = {"Accept": accept}
                wps_data = None
            else:
                wps_inputs = [("file", ComplexDataInput(tmp_file.name, mimeType=CONTENT_TYPE_TEXT_PLAIN))]
                wps_outputs = [(self.out_key, True)]  # as reference
                wps_exec = WPSExecution(version=version, url=wps_url)
                wps_req = wps_exec.buildRequest(self.process_id, wps_inputs, wps_outputs)
                wps_data = lxml.etree.tostring(wps_req)
                wps_headers = {"Accept": accept, "Content-Type": CONTENT_TYPE_APP_XML}
                wps_params = None
            resp = mocked_sub_requests(self.app, wps_method, wps_url,
                                       params=wps_params, data=wps_data, headers=wps_headers, only_local=True)
            assert resp.status_code in [200, 201], \
                "Failed with: [{}]\nTest: [{}]\nReason:\n{}".format(resp.status_code, test_content, resp.text)

            # parse response status
            if accept == CONTENT_TYPE_APP_XML:
                assert resp.content_type in CONTENT_TYPE_ANY_XML, test_content
                xml = lxml.etree.fromstring(str2bytes(resp.text))
                status_url = xml.get("statusLocation")
                job_id = status_url.split("/")[-1]
            elif accept == CONTENT_TYPE_APP_JSON:
                assert resp.content_type == CONTENT_TYPE_APP_JSON, test_content
                status_url = resp.json["location"]
                job_id = resp.json["jobID"]
            assert status_url
            assert job_id

            # job monitoring
            result = self.monitor_job(status_url)

        self.validate_outputs(job_id, result, test_content)
Ejemplo n.º 7
0
def api_frontpage_body(settings):
    # type: (SettingsType) -> JSON
    """
    Generates the JSON body describing the Weaver API and documentation references.
    """

    # import here to avoid circular import errors
    from weaver.config import get_weaver_configuration

    weaver_url = get_weaver_url(settings)
    weaver_config = get_weaver_configuration(settings)

    weaver_api = asbool(settings.get("weaver.wps_restapi"))
    weaver_api_url = get_wps_restapi_base_url(settings) if weaver_api else None
    weaver_api_oas_ui = weaver_api_url + sd.api_openapi_ui_service.path if weaver_api else None
    weaver_api_swagger = weaver_api_url + sd.api_swagger_ui_service.path if weaver_api else None
    weaver_api_doc = settings.get("weaver.wps_restapi_doc",
                                  None) if weaver_api else None
    weaver_api_ref = settings.get("weaver.wps_restapi_ref",
                                  None) if weaver_api else None
    weaver_api_spec = weaver_api_url + sd.openapi_json_service.path if weaver_api else None
    weaver_wps = asbool(settings.get("weaver.wps"))
    weaver_wps_url = get_wps_url(settings) if weaver_wps else None
    weaver_conform_url = weaver_url + sd.api_conformance_service.path
    weaver_process_url = weaver_url + sd.processes_service.path
    weaver_jobs_url = weaver_url + sd.jobs_service.path
    weaver_links = [{
        "href": weaver_url,
        "rel": "self",
        "type": CONTENT_TYPE_APP_JSON,
        "title": "This landing page."
    }, {
        "href": weaver_conform_url,
        "rel": "http://www.opengis.net/def/rel/ogc/1.0/conformance",
        "type": CONTENT_TYPE_APP_JSON,
        "title": "Conformance classes implemented by this service."
    }, {
        "href": __meta__.__license_url__,
        "rel": "license",
        "type": CONTENT_TYPE_TEXT_PLAIN,
        "title": __meta__.__license_long__
    }]
    if weaver_api:
        weaver_links.extend([
            {
                "href": weaver_api_url,
                "rel": "service",
                "type": CONTENT_TYPE_APP_JSON,
                "title": "WPS REST API endpoint of this service."
            },
            {
                "href": weaver_api_spec,
                "rel": "service-desc",
                "type": CONTENT_TYPE_APP_JSON,
                "title": "OpenAPI specification of this service."
            },
            {
                "href": weaver_api_oas_ui,
                "rel": "service-doc",
                "type": CONTENT_TYPE_TEXT_HTML,
                "title":
                "Human readable OpenAPI documentation of this service."
            },
            {
                "href": weaver_api_spec,
                "rel": "OpenAPI",
                "type": CONTENT_TYPE_APP_JSON,
                "title": "OpenAPI specification of this service."
            },
            {
                "href": weaver_api_swagger,
                "rel": "swagger-ui",
                "type": CONTENT_TYPE_TEXT_HTML,
                "title": "WPS REST API definition of this service."
            },
            {
                "href": weaver_process_url,
                "rel": "http://www.opengis.net/def/rel/ogc/1.0/processes",
                "type": CONTENT_TYPE_APP_JSON,
                "title": "Processes offered by this service."
            },
            {
                "href": sd.OGC_API_REPO_URL,
                "rel": "ogcapi-processes-repository",
                "type": CONTENT_TYPE_TEXT_HTML,
                "title": "OGC API - Processes schema definitions repository."
            },
            {
                "href":
                weaver_jobs_url,
                "rel":
                "http://www.opengis.net/def/rel/ogc/1.0/job-list",
                "type":
                CONTENT_TYPE_APP_JSON,
                "title":
                "Job search and listing endpoint of executions registered under this service."
            },
            {
                "href": sd.CWL_BASE_URL,
                "rel": "cwl-home",
                "type": CONTENT_TYPE_TEXT_HTML,
                "title": "Common Workflow Language (CWL) homepage."
            },
            {
                "href": sd.CWL_REPO_URL,
                "rel": "cwl-repository",
                "type": CONTENT_TYPE_TEXT_HTML,
                "title": "Common Workflow Language (CWL) repositories."
            },
            {
                "href": sd.CWL_SPEC_URL,
                "rel": "cwl-specification",
                "type": CONTENT_TYPE_TEXT_HTML,
                "title": "Common Workflow Language (CWL) specification."
            },
            {
                "href": sd.CWL_USER_GUIDE_URL,
                "rel": "cwl-user-guide",
                "type": CONTENT_TYPE_TEXT_HTML,
                "title": "Common Workflow Language (CWL) user guide."
            },
            {
                "href":
                sd.CWL_CMD_TOOL_URL,
                "rel":
                "cwl-command-line-tool",
                "type":
                CONTENT_TYPE_TEXT_HTML,
                "title":
                "Common Workflow Language (CWL) CommandLineTool specification."
            },
            {
                "href": sd.CWL_WORKFLOW_URL,
                "rel": "cwl-workflow",
                "type": CONTENT_TYPE_TEXT_HTML,
                "title":
                "Common Workflow Language (CWL) Workflow specification."
            },
        ])
        if weaver_api_ref:
            # sample:
            #   https://app.swaggerhub.com/apis/geoprocessing/WPS/
            weaver_links.append({
                "href":
                weaver_api_ref,
                "rel":
                "reference",
                "type":
                CONTENT_TYPE_APP_JSON,
                "title":
                "API reference specification of this service."
            })
        if isinstance(weaver_api_doc, str):
            # sample:
            #   https://raw.githubusercontent.com/opengeospatial/wps-rest-binding/develop/docs/18-062.pdf
            if "." in weaver_api_doc:  # pylint: disable=E1135,unsupported-membership-test
                ext_type = weaver_api_doc.split(".")[-1]
                doc_type = "application/{}".format(ext_type)
            else:
                doc_type = CONTENT_TYPE_TEXT_PLAIN  # default most basic type
            weaver_links.append({
                "href":
                weaver_api_doc,
                "rel":
                "documentation",
                "type":
                doc_type,
                "title":
                "API reference documentation about this service."
            })
        else:
            weaver_links.append({
                "href":
                __meta__.__documentation_url__,
                "rel":
                "documentation",
                "type":
                CONTENT_TYPE_TEXT_HTML,
                "title":
                "API reference documentation about this service."
            })
    if weaver_wps:
        weaver_links.extend([
            {
                "href": weaver_wps_url,
                "rel": "wps",
                "type": CONTENT_TYPE_TEXT_XML,
                "title": "WPS 1.0.0/2.0 XML endpoint of this service."
            },
            {
                "href": "http://docs.opengeospatial.org/is/14-065/14-065.html",
                "rel": "wps-specification",
                "type": CONTENT_TYPE_TEXT_HTML,
                "title": "WPS 1.0.0/2.0 definition of this service."
            },
            {
                "href": "http://schemas.opengis.net/wps/",
                "rel": "wps-schema-repository",
                "type": CONTENT_TYPE_TEXT_HTML,
                "title": "WPS 1.0.0/2.0 XML schemas repository."
            },
            {
                "href": "http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd",
                "rel": "wps-schema-1",
                "type": CONTENT_TYPE_TEXT_XML,
                "title": "WPS 1.0.0 XML validation schemas entrypoint."
            },
            {
                "href": "http://schemas.opengis.net/wps/2.0/wps.xsd",
                "rel": "wps-schema-2",
                "type": CONTENT_TYPE_TEXT_XML,
                "title": "WPS 2.0 XML validation schemas entrypoint."
            },
        ])
    return {
        "message":
        "Weaver Information",
        "configuration":
        weaver_config,
        "description":
        __meta__.__description__,
        "parameters": [
            {
                "name": "api",
                "enabled": weaver_api,
                "url": weaver_api_url,
                "api": weaver_api_oas_ui
            },
            {
                "name": "wps",
                "enabled": weaver_wps,
                "url": weaver_wps_url
            },
        ],
        "links":
        weaver_links,
    }
Ejemplo n.º 8
0
    def wps_execute(self, version, accept):
        wps_url = get_wps_url(self.settings)
        if version == "1.0.0":
            test_content = "Test file in Docker - WPS KVP"
            wps_method = "GET"
        elif version == "2.0.0":
            test_content = "Test file in Docker - WPS XML"
            wps_method = "POST"
        else:
            raise ValueError("Invalid WPS version: {}".format(version))
        test_content += " {} request - Accept {}".format(wps_method, accept.split("/")[-1].upper())

        with contextlib.ExitStack() as stack_exec:
            # setup
            dir_name = tempfile.gettempdir()
            tmp_file = stack_exec.enter_context(tempfile.NamedTemporaryFile(dir=dir_name, mode="w", suffix=".txt"))
            tmp_file.write(test_content)
            tmp_file.seek(0)
            for mock_exec in mocked_execute_process():
                stack_exec.enter_context(mock_exec)

            # execute
            if version == "1.0.0":
                wps_inputs = ["file={}@mimeType={}".format(tmp_file.name, CONTENT_TYPE_TEXT_PLAIN)]
                wps_params = {
                    "service": "WPS",
                    "request": "Execute",
                    "version": version,
                    "identifier": self.process_id,
                    "DataInputs": wps_inputs,
                }
                wps_headers = {"Accept": accept}
                wps_data = None
            else:
                wps_inputs = [("file", ComplexDataInput(tmp_file.name, mimeType=CONTENT_TYPE_TEXT_PLAIN))]
                wps_outputs = [(self.out_key, True)]  # as reference
                wps_exec = WPSExecution(version=version, url=wps_url)
                wps_req = wps_exec.buildRequest(self.process_id, wps_inputs, wps_outputs)
                wps_data = xml_util.tostring(wps_req)
                wps_headers = {"Accept": accept, "Content-Type": CONTENT_TYPE_APP_XML}
                wps_params = None
            resp = mocked_sub_requests(self.app, wps_method, wps_url,
                                       params=wps_params, data=wps_data, headers=wps_headers, only_local=True)
            assert resp.status_code in [200, 201], (
                "Failed with: [{}]\nTest: [{}]\nReason:\n{}".format(resp.status_code, test_content, resp.text)
            )

            # parse response status
            if accept == CONTENT_TYPE_APP_XML:
                assert resp.content_type in CONTENT_TYPE_ANY_XML, test_content
                xml_body = xml_util.fromstring(str2bytes(resp.text))
                status_url = xml_body.get("statusLocation")
                job_id = status_url.split("/")[-1].split(".")[0]
            elif accept == CONTENT_TYPE_APP_JSON:
                assert resp.content_type == CONTENT_TYPE_APP_JSON, test_content
                status_url = resp.json["location"]
                job_id = resp.json["jobID"]
            assert status_url
            assert job_id

            if accept == CONTENT_TYPE_APP_XML:
                wps_out_url = self.settings["weaver.wps_output_url"]
                weaver_url = self.settings["weaver.url"]
                assert status_url == f"{wps_out_url}/{job_id}.xml", "Status URL should be XML file for WPS-1 request"
                # remap to employ JSON monitor method (could be done with XML parsing otherwise)
                status_url = f"{weaver_url}/jobs/{job_id}"

            # job monitoring
            results = self.monitor_job(status_url)
            outputs = self.get_outputs(status_url)

            # validate XML status is updated accordingly
            wps_xml_status = os.path.join(self.settings["weaver.wps_output_dir"], job_id + ".xml")
            assert os.path.isfile(wps_xml_status)
            with open(wps_xml_status, "r") as status_file:
                assert "ProcessSucceeded" in status_file.read()

        self.validate_outputs(job_id, results, outputs, test_content)