def parse_parameters(self, parameters): """ This contains the main logic for Parsing Parameters present in the operation. Parameters could be of type: URL (Path. Query), Headers, Body (body, form etc). For each parameter, we: - run basic validations for them - process them, and if they are reference, resolve the references - run basic checks for them, and save their config in requisite variables """ for config in parameters.values(): ref = config.get(constants.REF) if ref: config = utils.resolve_reference(self.schema_resolver.spec, ref) in_ = config.get(constants.IN_) if not in_: raise exceptions.ImproperSwaggerException( "In is required field for OpenAPI Parameter") name = config.get(constants.PARAMETER_NAME) if not name: raise exceptions.ImproperSwaggerException( f"Config {config} does not have name") if in_ == constants.PATH_PARAM: self.parse_url_params(name, config, param_type="path") elif in_ == constants.BODY_PARAM: self.parse_body_params(name, config) elif in_ == constants.QUERY_PARAM: self.parse_url_params(name, config) elif in_ == constants.FORM_PARAM: self.data_body[name] = config elif in_ == constants.HEADER_PARAM: self.parse_header_params(name, config) else: raise exceptions.ImproperSwaggerException( f"Config {config} does not have valid parameter type") # schema_resolver.resolve() would expand nested references and definitions, and would give nested object self.data_body = self.schema_resolver.resolve(self.data_body)
def parse_responses(self, responses): """ Resolve the responses. """ return_response = {} for status, config in responses.items(): # Swagger responses are either status code or "default". if status == constants.DEFAULT: response = self.get_response_properties(config) if response: return_response = response continue # No need to do any other checks try: status_code = int(status) except (ValueError, TypeError): raise exceptions.ImproperSwaggerException( f"Swagger {responses} status codes must be Integer Strings" ) else: # 2xx responses are typically used as indication of valid response if 200 <= status_code < 300: # Short-circuit return with first valid response we encountered schema = self.get_response_properties(config) if schema: return schema return return_response
def identify_converter(self): if self.spec_file.endswith(constants.JSON): self.converter = constants.JSON elif self.spec_file.endswith(constants.YAML): self.converter = constants.YAML else: raise exceptions.ImproperSwaggerException( "Incorrect extension for {}".format(self.spec_file))
def parse_body_params(self, name, config): schema = config.get(constants.SCHEMA) if schema: self.data_body = schema else: # If schema is not there, we want to see if we can safely ignore this before raising error required = config.get(constants.REQUIRED, True) if required: raise exceptions.ImproperSwaggerException( f"Body Parameter {name} must specify schema. OP ID: {self.open_api_op.op_id}" )
def get_ref_path_array(ref_definition: str) -> list: """ Find the reference path through its definition """ # Find out which reference it is: if ref_definition.startswith("#/"): ref = ref_definition[2:].split("/") # Ignore #/ and split the rest of string by / else: raise exceptions.ImproperSwaggerException("We only support Local references") return ref
def get_swagger_url(self): """ Utility Method to add swagger URL defaults if missing in configuration. Swagger base url is decided on this priority: 1. From Custom User settings (in conf/conf.py) 2. From Swagger Configuration 3. Default values, wherever possible """ protocol = settings.SERVER_URL.get('protocol') if not protocol: schemes = self.specs.get(constants.SCHEMES, []) protocol = schemes[0] if schemes else "http" host = settings.SERVER_URL.get('host') if not host: host = self.specs.get(constants.HOST, "localhost") api_url = settings.SERVER_URL.get('api_url') if not api_url: api_info = self.specs.get(constants.INFO, {}) api_url = api_info.get(constants.API_URL, self.specs.get(constants.BASE_PATH, "")) if api_url and host: # Check that either API URL starts with / or host ends with / if api_url.startswith("/") and host.endswith("/"): raise exceptions.ImproperSwaggerException( f"Base URL is {host}{api_url} - Is this correct?") if not api_url.startswith("/") and not host.endswith("/"): raise exceptions.ImproperSwaggerException( f"Base URL is {host}{api_url} - Is this correct?") return f"{protocol}://{host}{api_url}"
def parse_url_params(self, name, config, param_type="query"): """ :param name: Parameter name :param config: Parameter Configuration :param param_type: Type of parameter. Query/Path """ _type = config.get(constants.TYPE) if not _type: raise exceptions.ImproperSwaggerException( f"Type not defined for parameter - {name}") if _type not in constants.URL_TYPES: raise exceptions.ImproperSwaggerException( f"Unsupported type for parameter - {name}") # Only use query params if strictly required is_optional_param = not (settings.HIT_ALL_QUERY_PARAMS or config.get(constants.REQUIRED, False)) if param_type == "query" and is_optional_param: return # Special Handling for Page Query Parameters if name in settings.POSITIVE_INTEGER_PARAMS: config[constants.MINIMUM] = 1 config = self.schema_resolver.resolve({name: config}) if config: if self.open_api_op.url_end_parameter( ) == name and self.open_api_op.method == constants.DELETE: config[name]["options"] = {"delete": 1} # Using 1 instead of true since this avoids Language issues. All languages treat 1 as same # However, different languages have different truth values eg: True (Python), true (javascript) self.delete_url_resource = ResourceFieldMap( config[name].get(constants.RESOURCE), name) self.url_params[name] = (param_type, config[name])
def resolve_reference(spec, ref_definition): """ Resolve Reference for Swagger and return the referred part :param spec: Swagger specification :param ref_definition: Path to reference :return: Reference object """ for ref_element in get_ref_path_array(ref_definition): spec = spec.get(ref_element) if not spec: raise exceptions.ImproperSwaggerException(f"Cannot find reference {ref_element} in {spec}") return spec
def responses(self, value): valid_responses = {} for status_code, config in value.items(): if isinstance(status_code, str): if status_code == constants.DEFAULT: valid_responses[status_code] = config else: try: status_code = int(status_code) except (ValueError, TypeError): raise exceptions.ImproperSwaggerException( f"Status Code of {config} must be default/int string" ) if isinstance(status_code, int) and 200 <= status_code < 300: valid_responses[str(status_code)] = config self._responses = valid_responses
def parse_params(self, params, url): # Reset the params self.resource_params = set() for param in params: ref = param.get(swagger_constants.REF) if ref: param = utils.resolve_reference(self.specs, ref) param_type = param.get(swagger_constants.IN_) _name = param.get(swagger_constants.PARAMETER_NAME) if not param_type: raise exceptions.ImproperSwaggerException( f"Param type not defined for {_name}") if param_type in swagger_constants.URL_PARAMS: # Check if resource is defined resource = param.get(swagger_constants.RESOURCE) # Generate resources if none found. Do not generate if empty string if resource is None: resource = self.extract_resource_name_from_param( _name, url, param_type) if resource: resource = self.add_resource(resource) resource_alias = self.resource_map_resolver.get_alias( resource) param[swagger_constants.RESOURCE] = resource_alias self.add_reference_definition(resource_alias, param) self.resource_params.add(resource_alias) elif param_type == swagger_constants.BODY_PARAM: self.resolve_body_param(param) return self.resource_params
def tags(self, value): if not isinstance(value, list): raise exceptions.ImproperSwaggerException( "Tags must be Array - Op ID {}".format(self.func_name)) self._tags = value
def method(self, value): if value not in constants.VALID_METHODS: raise exceptions.ImproperSwaggerException( f"Invalid method {self.method} for {self.url}") self._method = value