#!/usr/bin/env python3 """Apply authoritative childRelationshipName values from SF describe data into the legacy salesforce-crm-standard pack's single-spec JSON. Mirrors the v66 pack extractor's child-relationship handling. The legacy pack does not have a full describe → spec extractor (predates the v66 pack); this script is the equivalent post-hoc mapper for the FK-name field only. Reads `/tmp/sfdescribe/describe/*.json`, builds the `(child_obj_lower, fk_field_lower) → {parent_lower: relName}` index from each parent's `childRelationships[]`, then walks `packs/salesforce-crm-standard/objects/standard_objects_spec.json` and sets `childRelationshipName` on each reference field whose authoritative value is published in the describe data. Polymorphic FKs use the first populated parent's name (verified by the v66 extractor's divergence warning that fires only on real-world divergence). Re-running is idempotent: existing values are overwritten; values for FKs that have no authoritative source remain untouched. """ from __future__ import annotations import json import sys from pathlib import Path DESCRIBE_DIR = Path("/tmp/sfdescribe/describe") PACK_SPEC = Path(__file__).resolve().parents[1] / "objects" / "standard_objects_spec.json" def build_index() -> dict[tuple[str, str], dict[str, str]]: out: dict[tuple[str, str], dict[str, str]] = {} if not DESCRIBE_DIR.exists(): print(f"missing describe dir: {DESCRIBE_DIR}", file=sys.stderr) sys.exit(1) for jf in sorted(DESCRIBE_DIR.glob("*.json")): try: raw = json.loads(jf.read_text()) except Exception: continue result = raw.get("result") if not isinstance(result, dict): continue parent = (result.get("name") or "").lower() if not parent: continue for cr in result.get("childRelationships", []) or []: rn = cr.get("relationshipName") if not rn: continue child = (cr.get("childSObject") or "").lower() field = (cr.get("field") or "").lower() if not child or not field: continue out.setdefault((child, field), {})[parent] = rn return out def main() -> int: index = build_index() if not PACK_SPEC.exists(): print(f"missing pack spec: {PACK_SPEC}", file=sys.stderr) return 1 specs = json.loads(PACK_SPEC.read_text()) applied = 0 skipped = 0 for obj in specs: api = obj.get("apiName") if not api: continue for fld in obj.get("fields", []) or []: if fld.get("type") not in ("reference", "masterDetail"): continue key = (api.lower(), (fld.get("apiName") or "").lower()) published = index.get(key, {}) populated = [v for v in published.values() if v] if not populated: skipped += 1 continue first = populated[0] if any(v != first for v in populated): print(f"WARN polymorphic divergence: {api}.{fld['apiName']} -> {published}", file=sys.stderr) fld["childRelationshipName"] = first applied += 1 PACK_SPEC.write_text(json.dumps(specs, indent=2, ensure_ascii=False) + "\n") print(f"applied={applied} skipped={skipped}") return 0 if __name__ == "__main__": sys.exit(main())