def deploy_process(cls, payload, process_id=None, describe_schema=PROCESS_SCHEMA_OGC): # type: (JSON, Optional[str], str) -> JSON """ Deploys a process with :paramref:`payload`. :returns: resulting tuple of ``(process-description, package)`` JSON responses. """ if process_id: payload["processDescription"]["process"]["id"] = process_id # type: ignore exec_list = payload.get("executionUnit", []) if len(exec_list): # test-only feature: # substitute 'href' starting by 'tests/' by the corresponding file in test resources # this allows clean separation of deploy payload from CWL to allow reuse and test CWL locally beforehand exec_href = exec_list[0].get("href", "") if exec_href.startswith("tests/"): exec_unit = load_file(os.path.join(WEAVER_ROOT_DIR, exec_href)) exec_list[0]["unit"] = exec_unit exec_list[0].pop("href") resp = mocked_sub_requests(cls.app, "post_json", "/processes", data=payload, headers=cls.json_headers) assert resp.status_code == 201, "Expected successful deployment.\nError:\n{}".format(resp.text) path = resp.json["processSummary"]["processDescriptionURL"] body = {"value": VISIBILITY_PUBLIC} resp = cls.app.put_json("{}/visibility".format(path), params=body, headers=cls.json_headers) assert resp.status_code == 200, "Expected successful visibility.\nError:\n{}".format(resp.text) info = [] for info_path in ["{}?schema={}".format(path, describe_schema), "{}/package".format(path)]: resp = cls.app.get(info_path, headers=cls.json_headers) assert resp.status_code == 200 info.append(deepcopy(resp.json)) return info
def test_execute_docker_embedded_python_script(self): test_proc = "test-docker-python-script" cwl = load_file(os.path.join(WEAVER_ROOT_DIR, "docs/examples/docker-python-script-report.cwl")) body = { "processDescription": { "process": { "id": test_proc } }, "executionUnit": [{"unit": cwl}], "deploymentProfileName": "http://www.opengis.net/profiles/eoc/dockerizedApplication" } self.deploy_process(body) with contextlib.ExitStack() as stack: for mock_exec in mocked_execute_process(): stack.enter_context(mock_exec) path = f"/processes/{test_proc}/execution" cost = 2.45 amount = 3 body = { "mode": EXECUTE_MODE_ASYNC, "response": EXECUTE_RESPONSE_DOCUMENT, "inputs": [ {"id": "amount", "value": amount}, {"id": "cost", "value": cost} ], "outputs": [ {"id": "quote", "transmissionMode": EXECUTE_TRANSMISSION_MODE_REFERENCE}, ] } resp = mocked_sub_requests(self.app, "POST", path, json=body, headers=self.json_headers, only_local=True) status_url = resp.headers["Location"] results = self.monitor_job(status_url) assert results["quote"]["href"].startswith("http") stack.enter_context(mocked_wps_output(self.settings)) tmpdir = stack.enter_context(tempfile.TemporaryDirectory()) report_file = fetch_file(results["quote"]["href"], tmpdir, self.settings) report_data = load_file(report_file, text=True) assert report_data == f"Order Total: {amount * cost:0.2f}$\n"
def _parse_inputs(inputs): # type: (Optional[Union[str, JSON]]) -> Union[OperationResult, JSON] try: if isinstance(inputs, str): # loaded inputs could be mapping or listing format (any schema: CWL, OGC, OLD) inputs = load_file(inputs) if inputs != "" else [] if not inputs or not isinstance(inputs, (dict, list)): return OperationResult( False, "No inputs or invalid schema provided.", inputs) if isinstance(inputs, list): # list of literals from CLI if any("=" in value for value in inputs): inputs = repr2json_input_values(inputs) # list of single file from CLI (because of 'nargs') elif len(inputs) == 1 and "=" not in inputs[0]: inputs = load_file(inputs[0]) elif len(inputs) == 1 and inputs[0] == "": inputs = [] if isinstance(inputs, list): inputs = {"inputs": inputs} # OLD format provided directly # consider possible ambiguity if literal CWL input is named 'inputs' # - if value of 'inputs' is an object, it can collide with 'OGC' schema, # unless 'value/href' are present or their sub-dict don't have CWL 'class' # - if value of 'inputs' is an array, it can collide with 'OLD' schema, # unless 'value/href' (and 'id' technically) are present values = inputs.get("inputs", null) if (values is null or values is not null and ((isinstance(values, dict) and get_any_value(values) is null and "class" not in values) or (isinstance(values, list) and all( isinstance(v, dict) and get_any_value(v) is null for v in values)))): values = cwl2json_input_values(inputs) if values is null: raise ValueError( "Input values parsed as null. Could not properly detect employed schema." ) except Exception as exc: return OperationResult( False, f"Failed inputs parsing with error: [{exc!s}].", inputs) return values
def _parse_deploy_body(body, process_id): # type: (Optional[Union[JSON, str]], Optional[str]) -> OperationResult data = {} # type: JSON try: if body: if isinstance(body, str) and (body.startswith("http") or os.path.isfile(body)): data = load_file(body) elif isinstance( body, str) and body.startswith("{") and body.endswith("}"): data = yaml.safe_load(body) elif isinstance(body, dict): data = body else: msg = "Cannot load badly formed body. Deploy JSON object or file reference expected." return OperationResult(False, msg, body, {}) elif not body: data = {"processDescription": {"process": {"id": process_id}}} if body and process_id: LOGGER.debug( "Override provided process ID [%s] into provided/loaded body.", process_id) data.setdefault("processDescription", {}) data["processDescription"].setdefault("process", {}) data["processDescription"]["process"][ "id"] = process_id # type: ignore # for convenience, always set visibility by default data.setdefault("processDescription", {}) data["processDescription"].setdefault("process", {}) data["processDescription"]["process"][ "visibility"] = VISIBILITY_PUBLIC # type: ignore except (ValueError, TypeError, ScannerError) as exc: return OperationResult( False, f"Failed resolution of body definition: [{exc!s}]", body) return OperationResult(True, "", data)
def _load_path(file_path, text=False, xml=False): # type: (str, bool, bool) -> Union[JSON, xml_util.XML, str] if xml: return xml_util.parse(file_path) return load_file(file_path, text=text)