-
Notifications
You must be signed in to change notification settings - Fork 129
Expand file tree
/
Copy pathdiff-rest-api-spec.py
More file actions
executable file
·152 lines (121 loc) · 4.7 KB
/
diff-rest-api-spec.py
File metadata and controls
executable file
·152 lines (121 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/usr/bin/env python3
"""
Script to generate and diff REST API specifications.
This script:
1. Runs `make generate` to generate the schema
2. Runs the Rust compiler to generate REST API output
3. Diffs JSON files between rest-api-spec and the generated output
"""
import os
import subprocess
import sys
import json
from pathlib import Path
import difflib
def run_command(cmd, cwd=None, description=None):
"""Run a shell command and handle errors."""
if description:
print(f"Running: {description}")
print(f"Executing: {cmd}")
try:
result = subprocess.run(
cmd, shell=True, cwd=cwd, check=True, capture_output=True, text=True
)
print(f"✓ Success: {description or cmd}")
if result.stdout.strip():
print(f"Output: {result.stdout.strip()}")
return result
except subprocess.CalledProcessError as e:
print(f"✗ Failed: {description or cmd}")
print(f"Error code: {e.returncode}")
if e.stdout:
print(f"Stdout: {e.stdout}")
if e.stderr:
print(f"Stderr: {e.stderr}")
sys.exit(1)
def remove_descriptions(key, obj):
"""Recursively remove 'description' fields from JSON object."""
if isinstance(obj, dict):
return {
k: remove_descriptions(k, v) for k, v in obj.items() if k != "description" and k != "feature_flag"
}
elif isinstance(obj, list):
if key == "options":
return sorted(obj)
return [remove_descriptions("item", item) for item in obj]
else:
return obj
def compare_json_files(file1, file2):
"""Compare two JSON files and return diff if they differ, ignoring description fields."""
try:
with open(file1, "r") as f1, open(file2, "r") as f2:
json1 = json.load(f1)
json2 = json.load(f2)
# Remove description fields
json1_no_desc = remove_descriptions("root", json1)
json2_no_desc = remove_descriptions("root", json2)
# Pretty print for comparison
json1_str = json.dumps(json1_no_desc, indent=2, sort_keys=True)
json2_str = json.dumps(json2_no_desc, indent=2, sort_keys=True)
if json1_str == json2_str:
return None
# Generate diff
diff = difflib.unified_diff(
json1_str.splitlines(keepends=True),
json2_str.splitlines(keepends=True),
fromfile=str(file1),
tofile=str(file2),
lineterm="",
)
return list(diff)
except Exception as e:
return [f"Error comparing {file1} and {file2}: {str(e)}"]
def main():
"""Main function to execute the workflow."""
script_dir = Path(__file__).parent
compiler_dir = script_dir / "compiler-rs" / "clients_schema_to_rest_api_spec"
print("=== Elasticsearch REST API Spec Diff Tool ===\n")
# Step 1: Run make generate
run_command("make generate", cwd=script_dir, description="Generate schema")
# Step 2: Run Rust compiler
rust_cmd = "cargo run -- -i ../../output/schema/schema.json -o ./rest-api-output/"
run_command(rust_cmd, cwd=compiler_dir, description="Generate REST API output")
# Step 3: Compare JSON files
print("\n=== Comparing JSON files ===")
spec_dir = (
script_dir
/ "../elasticsearch/rest-api-spec/src/main/resources/rest-api-spec/api"
).resolve()
output_dir = compiler_dir / "rest-api-output"
# Get all JSON files from spec directory
spec_files = list(spec_dir.glob("**/*.json"))
if not spec_files:
print(f"No JSON files found in {spec_dir}")
sys.exit(1)
total_diffs = 0
for spec_file in sorted(spec_files):
if spec_file.name == "_common.json":
continue # Skip common file
if spec_file.name.startswith("fleet"):
continue # Fleet files are deliberately empty in rest-api-spec
api_name = spec_file.stem
if "." in api_name:
namespace, api_name, _ = spec_file.name.split(".")
else:
namespace = "_global"
if not os.path.exists(script_dir / "specification" / namespace / api_name):
continue
# Find corresponding file in output directory
relative_path = spec_file.relative_to(spec_dir)
output_file = output_dir / relative_path
diff = compare_json_files(spec_file, output_file)
if diff:
print(f"✗ Differences found in {relative_path}")
for line in diff:
print(line.rstrip("\n"))
if line.startswith("+ ") or line.startswith("- "):
total_diffs += 1
print("-" * 80)
print(f"\nTotal differences found: {total_diffs}")
if __name__ == "__main__":
main()