from __future__ import annotations import argparse import shutil import subprocess import sys import tempfile from dataclasses import dataclass from pathlib import Path import yaml ROOT = Path(__file__).resolve().parent.parent GENERATED_ROOT = ROOT / "ebay_client" / "generated" @dataclass(frozen=True) class ApiSpec: name: str spec_path: Path output_path: Path project_name: str package_name: str API_SPECS = { "notification": ApiSpec( name="notification", spec_path=ROOT / "commerce_notification_v1_oas3.yaml", output_path=GENERATED_ROOT / "notification", project_name="ebay-notification-generated", package_name="notification_generated", ), "inventory": ApiSpec( name="inventory", spec_path=ROOT / "sell_inventory_v1_oas3.yaml", output_path=GENERATED_ROOT / "inventory", project_name="ebay-inventory-generated", package_name="inventory_generated", ), "fulfillment": ApiSpec( name="fulfillment", spec_path=ROOT / "sell_fulfillment_v1_oas3.yaml", output_path=GENERATED_ROOT / "fulfillment", project_name="ebay-fulfillment-generated", package_name="fulfillment_generated", ), "account": ApiSpec( name="account", spec_path=ROOT / "sell_account_v1_oas3.yaml", output_path=GENERATED_ROOT / "account", project_name="ebay-account-generated", package_name="account_generated", ), "feed": ApiSpec( name="feed", spec_path=ROOT / "sell_feed_v1_oas3.yaml", output_path=GENERATED_ROOT / "feed", project_name="ebay-feed-generated", package_name="feed_generated", ), } def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Generate isolated low-level clients from the eBay OpenAPI contracts.") parser.add_argument("--api", choices=sorted(API_SPECS), help="Generate only one API package.") parser.add_argument("--fail-on-warning", action="store_true", help="Fail if the generator emits warnings.") return parser.parse_args() def build_config(spec: ApiSpec) -> dict[str, object]: return { "project_name_override": spec.project_name, "package_name_override": spec.package_name, "field_prefix": "field_", "generate_all_tags": False, "http_timeout": 30, "literal_enums": False, "post_hooks": [], "use_path_prefixes_for_title_model_names": False, } def run_generation(spec: ApiSpec, *, fail_on_warning: bool) -> None: if not spec.spec_path.exists(): raise FileNotFoundError(f"OpenAPI spec not found: {spec.spec_path}") with tempfile.TemporaryDirectory(prefix=f"ebay_{spec.name}_") as temp_dir_name: temp_dir = Path(temp_dir_name) config_path = temp_dir / "config.yaml" temp_output = temp_dir / "output" config_path.write_text(yaml.safe_dump(build_config(spec), sort_keys=True), encoding="utf-8") command = [ sys.executable, "-m", "openapi_python_client", "generate", "--path", str(spec.spec_path), "--meta", "none", "--overwrite", "--config", str(config_path), "--output-path", str(temp_output), ] if fail_on_warning: command.append("--fail-on-warning") subprocess.run(command, check=True, cwd=str(ROOT)) generated_package = temp_output / spec.package_name if not generated_package.exists(): generated_package = temp_output if spec.output_path.exists(): shutil.rmtree(spec.output_path) shutil.copytree(generated_package, spec.output_path) def main() -> int: args = parse_args() specs = [API_SPECS[args.api]] if args.api else [API_SPECS[name] for name in sorted(API_SPECS)] for spec in specs: print(f"Generating {spec.name} from {spec.spec_path.name} -> {spec.output_path}") run_generation(spec, fail_on_warning=args.fail_on_warning) return 0 if __name__ == "__main__": raise SystemExit(main())