def to_json_schema(obj, mode: UpdateMode, schema: Optional[Schema] = None): """Create JSON schema based on an object. Arguments: obj {dict} -- Dictionary object Keyword Arguments: schema {dict} -- Existing schema if exists. (default: {None}) Returns: [dict] -- New or updated schema. """ if schema is not None: schema = convert_from_openapi(schema) if mode == UpdateMode.GEN: builder = SchemaBuilder() if schema is not None: builder.add_schema(schema) builder.add_object(obj) out = builder.to_schema() return out elif schema is None: return {"oneOf": [to_const(obj)]} else: return {"oneOf": [to_const(obj), schema]}
def path_parameter_match( part: str, vname: str, path_item: PathItem, operation: str, oas: OpenAPIObject, ) -> bool: """Matches part of a path against a path parameter with name vname Arguments: part {str} -- part of a path, ie an id vname {str} -- name of a parameter path_item {PathItem} -- a path item maybe containing the parameter operation {MethodNames} -- the name of the operation to check in case the parameter is in the operation oas {OpenAPIObject} -- the schema to traverse to find definitions Returns: bool -- Did we get a match """ return reduce( lambda q, r: q and reduce( lambda a, b: a or valid_schema( b, { **convert_from_openapi(r), "definitions": make_definitions_from_spec(oas), }, ), list(set([part, maybeJson(part)])), False, ), get_matching_parameters(vname, path_item, operation, oas), True, )
def _keep_method_if_required_request_body_is_present( p: PathItem) -> PathItem: out = ( p if (operation_from_string(p, req.method.value) is None ) or (len([ s for s in get_required_request_body_schemas( req, oai, p) if not valid_schema( req.bodyAsJson if req.bodyAsJson is not None else json.loads(req.body) if req.body is not None else "", { # TODO: this line is different than the TS implementation # because I think there is a logic bug there # it should look like this line as we are not sure # if the schema will be a reference or not # perhaps I'm wrong in the assumption... only testing will tell... # otherwise, change the TS implementation in unmock-js and delete this comment. **convert_from_openapi( change_ref(s) if isinstance( s, Reference) else change_refs(s)), "definitions": make_definitions_from_schema(oai), }, ) ]) == 0) else omit_method_from_path_item( p, req.method.value)) return out
def _build_full_schema(self, schema: typing.Union[Schema, Reference], definitions: typing.Any) -> typing.Dict: return { **convert_from_openapi( change_ref(schema) if isinstance(schema, Reference) else change_refs(schema)), "definitions": definitions, }
def _json_schema_from_required_parameters( parameters: Sequence[Parameter], oai: OpenAPIObject ) -> Any: return { "type": "object", "properties": { param.name: convert_from_openapi(param.schema) for param in parameters }, "required": [param.name for param in parameters if param.required], "additionalProperties": True, "definitions": make_definitions_from_spec(oai), }
def _build_full_schema( self, schema: typing.Union[Schema, Reference], spec: OpenAPIObject ) -> typing.Dict: return { **convert_from_openapi( change_ref(schema) if isinstance(schema, Reference) else change_refs(schema) ), "definitions": { k: convert_from_openapi( change_ref(v) if isinstance(v, Reference) else change_refs(v) ) for k, v in ( spec.components.schemas.items() if (spec.components is not None) and (spec.components.schemas is not None) else [] ) }, }
def test_request_logging_mixed_append(tmp_dir): specs_dir = os.path.join(tmp_dir, "specs") spec_path = os.path.join(specs_dir, "another.api.com_mixed.json") os.makedirs(specs_dir) with open(spec_path, "w") as f: json.dump(convert_from_openapi(BASE_SCHEMA), f) with open(spec_path) as f: spec = convert_to_openapi(json.load(f)) assert spec.servers is None request = RequestBuilder.from_dict( dict( method="get", host="another.api.com", pathname="/echo", query={"message": "Hello"}, body="", bodyAsJson={}, path="/echo", protocol="http", headers={}, )) response = ResponseBuilder.from_dict( dict( statusCode=200, body='{"message": "hello"}', bodyAsJson={"message": "hello"}, headers={}, )) log_dir = os.path.join(tmp_dir, "logs") with RequestLoggingCallback(log_dir=log_dir, specs_dir=specs_dir, update_mode=UpdateMode.MIXED) as data_callback: data_callback.log(request, response) assert os.path.exists( os.path.join(log_dir, "another.api.com-recordings.jsonl")) assert os.path.exists(spec_path) with open(spec_path) as f: spec = convert_to_openapi(json.load(f)) assert "http://another.api.com" == spec.servers[0].url
def _build_request_entity_selectors(self, path_item: PathItem) -> typing.Dict: res = {} for method_name in self.methods: method: Operation = getattr(path_item, method_name) operation = ( ApiOperation.UNKNOWN if method is None else ApiOperation( get_x(method, "x-hmt-operation", ApiOperation.UNKNOWN))) if operation == ApiOperation.UPSERT or operation == ApiOperation.INSERT: request_body = typing.cast(RequestBody, method.requestBody) if "application/json" in request_body.content: schema = request_body.content["application/json"].schema if schema is not None: entity_path = self._find_entity( convert_from_openapi(schema), "$") if res is not None: res[method_name] = parse(entity_path) return res
def log(self, request: Request, response: Response): RequestBuilder.validate(request) ResponseBuilder.validate(response) host = request.host reqres = HttpExchange(request=request, response=response) if host not in self._logs: log_file = os.path.join(self._log_dir, "{}-recordings.jsonl".format(host)) if self._append and os.path.exists(log_file): self._logs[host] = open(log_file, "a") else: self._logs[host] = open(log_file, "w") HttpExchangeWriter(self._logs[host]).write(reqres) self._logs[host].flush() logger.debug("Logs for %s were updated", host) if self._update_mode: spec_file = os.path.join( self._specs_dir, "{}_{}.json".format(host, self._update_mode.name.lower()), ) if host not in self._specs: if os.path.exists(spec_file) and self._append: with open(spec_file, "r") as f: self._specs[host] = convert_to_openapi(json.load(f)) else: self._specs[host] = BASE_SCHEMA self._specs[host] = update_openapi(self._specs[host], reqres, self._update_mode) with open(spec_file, "w") as f: spec = convert_from_openapi(self._specs[host]) json.dump(spec, f) f.flush() logger.debug("Schema for %s was updated", host)
def spew( self, request: Request, specs: Sequence[OpenAPISpecification] ) -> Sequence[OpenAPISpecification]: if len(self._endpoints) == 0: return specs req = HttpExchangeWriter.to_dict(request) cs = {spec.source: convert_from_openapi(spec.api) for spec in specs} for endpoint in self._endpoints: res = requests.post(endpoint, json={"request": req, "schemas": cs}) cs = res.json() out = [ OpenAPISpecification(convert_to_openapi(dict_spec), name) for name, dict_spec in cs.items() ] for spec in out: self._mock_data_store.add_mock(spec) return out
def write_build_result(directory: str, result: BuildResult) -> None: """Write builder result to a directory. Arguments: directory {str} -- Directory where to write results, possibly relative. result {BuildResult} -- Builder result. """ output_dir_path = _resolve_path(directory) LOGGER.info("Writing to folder %s.", str(output_dir_path)) _ensure_dir_exists(output_dir_path) openapi_output = output_dir_path / OPENAPI_FILENAME openapi_object = result["openapi"] schema_json = cast(str, json.dumps(convert_from_openapi(openapi_object), indent=2)) with openapi_output.open("wb") as f: LOGGER.debug("Writing to: %s\n", str(openapi_output)) f.write(schema_json.encode(encoding="utf-8"))
def validate_body(req: Request, spec: OpenAPISpecification, op: Operation) -> bool: request_body = get_request_body(spec.api, op.requestBody) if ( request_body is not None and "application/json" in request_body.content and request_body.content["application/json"].schema ): schema = cast( Union[Schema, Reference], request_body.content["application/json"].schema ) return valid_schema( req.bodyAsJson, { **convert_from_openapi( change_ref(schema) if isinstance(schema, Reference) else change_refs(schema) ), "definitions": spec.definitions, }, ) return True
def make_definitions_from_spec(o: OpenAPIObject) -> Mapping[str, Any]: return ({ a: convert_from_openapi( change_ref(b) if isinstance(b, Reference) else change_refs(b)) for a, b in o.components.schemas.items() } if o.components and o.components.schemas else {})
def write_base_schema(target_file: str, schema: OpenAPIObject): with open(target_file, "w") as f: f.write(json.dumps(convert_from_openapi(schema)))