import yaml # type: ignore from apispec import APISpec from common_apispec.marshmellow_plugin import CustomMarshmallowPlugin from common_apispec.views import ResourceMeta from common_apispec import FlaskApiSpec from webargs.fields import DelimitedList class YamlProcessor: """YAML processor.""" @staticmethod def load_yaml(file_path: str) -> dict: """Load YAML file.""" with open(file_path, "r") as file: return yaml.safe_load(file) @staticmethod def save_yaml(data: dict, file_path: str) -> None: """Save data to YAML file.""" with open(file_path, "w") as file: yaml.safe_dump(data, file) @classmethod def merge(cls, file1_path: str, file2_path: str) -> dict: """Merge two OpenAPI files.""" yaml1 = cls.load_yaml(file1_path) yaml2 = cls.load_yaml(file2_path) merged_yaml = {**yaml1, **yaml2} if "paths" in yaml1 and "paths" in yaml2: merged_yaml["paths"] = {**yaml1["paths"], **yaml2["paths"]} if "components" in yaml1 and "components" in yaml2: merged_yaml["components"] = { "schemas": { **yaml1["components"]["schemas"], **yaml2["components"]["schemas"], } } return merged_yaml class ApiSpecProcessor: """OWS API Spec.""" def __delimited_list2param(self, field, **kwargs) -> dict: """Convert DelimitedList field to OpenAPI parameter.""" ret: dict = {} if isinstance(field, DelimitedList): if self.openapi_version.major < 3: # type: ignore ret["collectionFormat"] = "csv" else: ret["explode"] = False ret["style"] = "form" return ret @classmethod def generate_spec(cls, application, settings) -> dict: """Initialize the OWSApiSpec class.""" ma_plugin = CustomMarshmallowPlugin() spec = APISpec( title=settings["info"].pop("title"), version=settings["info"].pop("version"), openapi_version=settings.pop("openapi"), plugins=[ma_plugin], servers=settings.pop("servers"), ) ma_plugin.converter.add_parameter_attribute_function(cls.__delimited_list2param) # type: ignore application.config.update({"APISPEC_SPEC": spec}) docs = FlaskApiSpec(application, document_options=False) for name, rule in application.view_functions.items(): view_cls = getattr(rule, "view_class", None) if view_cls and isinstance(view_cls, ResourceMeta): blueprint_name, endpoint_name = name.split(".") docs.register( view_cls, endpoint=endpoint_name, blueprint=blueprint_name ) if not hasattr(rule, "__apispec__") or name == "static": continue try: blueprint_name, _ = name.split(".") except ValueError: blueprint_name = None try: docs.register(rule, blueprint=blueprint_name) except (TypeError, KeyError): pass return spec.to_dict()