import logging from ._colors import _c from .client import AxwayClient from .models import Action, EnvConfig, Manifest, NormResult log = logging.getLogger(__name__) def _applies_to(target_envs: list[str], env_name: str) -> bool: return not target_envs or env_name in target_envs def normalize_env(env: EnvConfig, manifest: Manifest, dry_run: bool) -> NormResult: result = NormResult() tag = f"[{env.name}]" log.info(f"{tag} Connexion à {env.url} …") client = AxwayClient(env) # ── 1. Organisations ────────────────────────────────────────────────────── try: existing_orgs = client.list_orgs() except Exception as e: result.errors.append(f"{tag} Impossible de lister les organisations : {e}") return result for org_spec in manifest.organizations: if not _applies_to(org_spec.environments, env.name): continue action = Action( kind="create_org", env_name=env.name, description=f"Créer organisation « {org_spec.name} »", detail={"org": org_spec.name}, ) if org_spec.name in existing_orgs: log.debug(f"{tag} Org « {org_spec.name} » déjà présente — skip") result.actions_skipped.append(action) continue log.info(_c("yellow", f"{tag} {'[DRY-RUN] ' if dry_run else ''}Création org « {org_spec.name} »")) if not dry_run: try: created = client.create_org(org_spec) existing_orgs[org_spec.name] = created log.info(_c("green", f"{tag} ✅ Org « {org_spec.name} » créée (id={created.get('id')})")) except Exception as e: msg = f"{tag} ❌ Échec création org « {org_spec.name} » : {e}" log.error(msg) result.errors.append(msg) continue else: # Placeholder so apps referencing this org are processed in dry-run existing_orgs[org_spec.name] = {"id": "", "name": org_spec.name} result.actions_done.append(action) # ── 2. Applications ─────────────────────────────────────────────────────── try: existing_apps = client.list_apps() except Exception as e: result.errors.append(f"{tag} Impossible de lister les applications : {e}") return result for app_spec in manifest.applications: if not _applies_to(app_spec.environments, env.name): continue key = (app_spec.org_name, app_spec.name) action_app = Action( kind="create_app", env_name=env.name, description=f"Créer application « {app_spec.name} » dans org « {app_spec.org_name} »", detail={"org": app_spec.org_name, "app": app_spec.name}, ) raw_org = existing_orgs.get(app_spec.org_name) if raw_org is None: msg = ( f"{tag} ❌ Organisation « {app_spec.org_name} » introuvable dans l'API Manager " f"— application « {app_spec.name} » ignorée. " f"Créez l'organisation manuellement dans l'API Manager." ) log.error(msg) result.errors.append(msg) continue org_id = raw_org["id"] if key not in existing_apps: log.info(_c("yellow", f"{tag} {'[DRY-RUN] ' if dry_run else ''}Création app « {app_spec.name} » (org={app_spec.org_name})")) if not dry_run: try: created_app = client.create_app(app_spec, org_id) existing_apps[key] = created_app log.info(_c("green", f"{tag} ✅ App « {app_spec.name} » créée (id={created_app.get('id')})")) except Exception as e: msg = f"{tag} ❌ Échec création app « {app_spec.name} » : {e}" log.error(msg) result.errors.append(msg) continue app_id_new = created_app.get("id", "") for cred in app_spec.credentials: try: client.create_app_ext_credential(app_id_new, cred.client_id, cred.secret) log.info(_c("green", f"{tag} ✅ External credential « {cred.client_id} » ajouté à « {app_spec.name} »")) except Exception as e: msg = f"{tag} ❌ Échec ajout external credential à « {app_spec.name} » : {e}" log.error(msg) result.errors.append(msg) result.actions_done.append(action_app) else: result.actions_skipped.append(action_app) # ── 3. Souscriptions aux APIs ───────────────────────────────────────── if not app_spec.apis: continue raw_app = existing_apps.get(key) if raw_app is None: continue # app non créée (dry-run ou erreur) app_id = raw_app.get("id", "") try: granted = client.list_app_api_access(app_id) granted_ids = {a.get("apiId", a.get("id", "")) for a in granted} granted_names = {a.get("apiName", a.get("name", "")) for a in granted} except Exception as e: log.warning(f"{tag} Impossible de lire les accès API de « {app_spec.name} » : {e}") granted_ids = set() granted_names = set() for api_access in app_spec.apis: action_api = Action( kind="grant_api", env_name=env.name, description=( f"Abonner « {app_spec.name} » à l'API « {api_access.api_name} »" + (f" v{api_access.api_version}" if api_access.api_version else "") ), detail={"app": app_spec.name, "api": api_access.api_name, "version": api_access.api_version}, ) if api_access.api_name in granted_names: log.debug(f"{tag} API « {api_access.api_name} » déjà accordée à « {app_spec.name} » — skip") result.actions_skipped.append(action_api) continue if dry_run: log.info(_c("yellow", f"{tag} [DRY-RUN] Abonnement « {app_spec.name} » → API « {api_access.api_name} »")) result.actions_done.append(action_api) continue fe_api = client.find_frontend_api(api_access.api_name, api_access.api_version) if fe_api is None: msg = ( f"{tag} ⚠️ API front-end « {api_access.api_name} » introuvable " f"— souscription de « {app_spec.name} » ignorée" ) log.warning(msg) result.errors.append(msg) continue api_id = fe_api["id"] if api_id in granted_ids: result.actions_skipped.append(action_api) continue try: client.grant_api_access(app_id, api_id) log.info(_c("green", f"{tag} ✅ Accès API « {api_access.api_name} » accordé à « {app_spec.name} »")) result.actions_done.append(action_api) except Exception as e: msg = f"{tag} ❌ Échec souscription « {app_spec.name} » → « {api_access.api_name} » : {e}" log.error(msg) result.errors.append(msg) return result