Skip to content

Commit e256c20

Browse files
committed
Implement processors for remaining AWS protocols
Add complete implementations for the four missing protocol processors: - aws_json_1_0.py: AWS JSON 1.0 protocol (38 services) Uses X-Amz-Target header with POST requests and application/x-amz-json-1.0 content type - rest_xml.py: REST XML protocol (4 services including S3 Control) Uses HTTP traits (method, uri) like restJson1 but with application/xml request bodies and text/xml responses - aws_query.py: AWS Query protocol (16 services including Redshift) Uses Action and Version query parameters with both GET and POST methods Response content type is text/xml - ec2_query.py: EC2 Query protocol (1 service - EC2) Similar to awsQuery with EC2-specific handling including xml name traits for list parameters. Uses /#Action={operation} path pattern All processors follow the same annotation standards as the existing restJson1 and awsJson1_1 implementations, including x-aws-operation-name, x-aws-protocol, and x-stackql-serviceName extensions.
1 parent 9601b48 commit e256c20

File tree

4 files changed

+1190
-4
lines changed

4 files changed

+1190
-4
lines changed

processors/aws_json_1_0.py

Lines changed: 248 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,250 @@
1+
# processors/aws_json_1_0.py
2+
# AWS JSON 1.0 protocol processor
3+
# Very similar to awsJson1_1 - uses X-Amz-Target header with POST requests
4+
5+
import json, yaml
6+
from pathlib import Path
7+
from processors.shared_functions import (
8+
LiteralStr,
9+
literal_str_representer,
10+
html_to_md,
11+
init_openapi_spec,
12+
add_info,
13+
add_servers,
14+
add_component_schema_string,
15+
add_component_schema_boolean,
16+
add_component_schema_integer,
17+
add_component_schema_timestamp,
18+
add_component_schema_double,
19+
add_component_schema_float,
20+
add_component_schema_long,
21+
add_component_schema_blob,
22+
add_component_schema_enum,
23+
add_component_schema_map,
24+
add_component_schema_document,
25+
add_component_schema_list,
26+
add_component_schema_union,
27+
add_component_schema_structure,
28+
)
29+
30+
yaml.add_representer(LiteralStr, literal_str_representer)
31+
132
def process(model_entry):
33+
34+
services_to_skip = []
35+
file_name = model_entry['filename']
36+
if file_name in services_to_skip:
37+
print(f"skipping {file_name}")
38+
return
39+
40+
protocol = model_entry['protocol']
241
service_name = model_entry['servicename'].split('#')[0].split('com.amazonaws.')[1]
3-
print(f"processing {service_name} with protocol {model_entry['protocol']}")
42+
print(f"processing {service_name} with protocol {protocol}")
43+
44+
model_path = Path(model_entry['filepath'])
45+
46+
with open(model_path, "r", encoding="utf-8") as f:
47+
model_data = json.load(f)
48+
49+
# Basic OpenAPI structure
50+
openapi_spec = init_openapi_spec(service_name, file_name, protocol)
51+
52+
# Add common AWS signature parameters
53+
openapi_spec["components"]["parameters"] = {
54+
"X-Amz-Content-Sha256": {
55+
"name": "X-Amz-Content-Sha256",
56+
"in": "header",
57+
"required": False,
58+
"schema": {"type": "string"}
59+
},
60+
"X-Amz-Date": {
61+
"name": "X-Amz-Date",
62+
"in": "header",
63+
"required": False,
64+
"schema": {"type": "string"}
65+
},
66+
"X-Amz-Algorithm": {
67+
"name": "X-Amz-Algorithm",
68+
"in": "header",
69+
"required": False,
70+
"schema": {"type": "string"}
71+
},
72+
"X-Amz-Credential": {
73+
"name": "X-Amz-Credential",
74+
"in": "header",
75+
"required": False,
76+
"schema": {"type": "string"}
77+
},
78+
"X-Amz-Security-Token": {
79+
"name": "X-Amz-Security-Token",
80+
"in": "header",
81+
"required": False,
82+
"schema": {"type": "string"}
83+
},
84+
"X-Amz-Signature": {
85+
"name": "X-Amz-Signature",
86+
"in": "header",
87+
"required": False,
88+
"schema": {"type": "string"}
89+
},
90+
"X-Amz-SignedHeaders": {
91+
"name": "X-Amz-SignedHeaders",
92+
"in": "header",
93+
"required": False,
94+
"schema": {"type": "string"}
95+
}
96+
}
97+
98+
shapes = model_data.get("shapes", model_data)
99+
100+
shapes_dict = {
101+
"service": [],
102+
"operation": []
103+
}
104+
105+
for shape_name, shape in shapes.items():
106+
if shape.get("type") == "service":
107+
add_info(openapi_spec, shape)
108+
add_servers(openapi_spec, file_name, shape)
109+
shape["my_name"] = shape_name
110+
shapes_dict["service"].append(shape)
111+
elif shape.get("type") == "string":
112+
add_component_schema_string(openapi_spec, shape_name, shape)
113+
elif shape.get("type") == "boolean":
114+
add_component_schema_boolean(openapi_spec, shape_name, shape)
115+
elif shape.get("type") == "integer":
116+
add_component_schema_integer(openapi_spec, shape_name, shape)
117+
elif shape.get("type") == "timestamp":
118+
add_component_schema_timestamp(openapi_spec, shape_name, shape)
119+
elif shape.get("type") == "double":
120+
add_component_schema_double(openapi_spec, shape_name, shape)
121+
elif shape.get("type") == "float":
122+
add_component_schema_float(openapi_spec, shape_name, shape)
123+
elif shape.get("type") == "long":
124+
add_component_schema_long(openapi_spec, shape_name, shape)
125+
elif shape.get("type") == "blob":
126+
add_component_schema_blob(openapi_spec, shape_name, shape)
127+
elif shape.get("type") == "enum":
128+
add_component_schema_enum(openapi_spec, shape_name, shape)
129+
elif shape.get("type") == "map":
130+
add_component_schema_map(openapi_spec, shape_name, shape)
131+
elif shape.get("type") == "document":
132+
add_component_schema_document(openapi_spec, shape_name, shape)
133+
elif shape.get("type") == "list":
134+
add_component_schema_list(openapi_spec, shape_name, shape)
135+
elif shape.get("type") == "union":
136+
add_component_schema_union(openapi_spec, shape_name, shape)
137+
elif shape.get("type") == "structure":
138+
add_component_schema_structure(openapi_spec, shape_name, shape)
139+
elif shape.get("type") == "operation":
140+
shape["my_name"] = shape_name
141+
shapes_dict["operation"].append(shape)
142+
143+
# Get the service name for X-Amz-Target
144+
service_name2 = model_entry['servicename'].split('#')[1]
145+
146+
# Sort operations alphabetically
147+
shapes_dict["operation"].sort(key=lambda x: x["my_name"])
148+
149+
# Setup paths
150+
openapi_spec["paths"] = {}
151+
152+
# Create path for each operation
153+
for operation in shapes_dict["operation"]:
154+
operation_name = operation["my_name"].split('#')[1]
155+
key_string = "/#X-Amz-Target=" + service_name2 + "." + operation_name
156+
openapi_spec["paths"][key_string] = create_path(operation, service_name2)
157+
158+
# Write output YAML
159+
outdir = Path("openapi")
160+
outdir.mkdir(exist_ok=True)
161+
outfile = outdir / f"{service_name}.yaml"
162+
with open(outfile, "w", encoding="utf-8") as f:
163+
yaml.dump(openapi_spec, f, sort_keys=False, allow_unicode=True)
164+
165+
166+
def create_path(operation, service_name2):
167+
result = {}
168+
operation_name = operation["my_name"].split('#')[1]
169+
170+
result["post"] = {}
171+
result_post = result["post"]
172+
173+
result_post["operationId"] = operation_name
174+
result_post["x-aws-operation-name"] = operation_name
175+
result_post["description"] = LiteralStr(html_to_md(operation["traits"].get("smithy.api#documentation", "")))
176+
177+
# Request body with JSON content
178+
result_post["requestBody"] = {}
179+
result_request_body = result_post["requestBody"]
180+
result_request_body["required"] = True
181+
result_request_body["content"] = {}
182+
result_request_body["content"]["application/x-amz-json-1.0"] = {}
183+
result_request_body["content"]["application/x-amz-json-1.0"]["schema"] = {}
184+
185+
if "input" in operation and operation["input"]["target"] != "smithy.api#Unit":
186+
result_request_body["content"]["application/x-amz-json-1.0"]["schema"]["$ref"] = "#/components/schemas/" + operation["input"]["target"].split("#")[1]
187+
else:
188+
result_request_body["content"]["application/x-amz-json-1.0"]["schema"]["type"] = "object"
189+
190+
# X-Amz-Target header parameter
191+
result_post["parameters"] = [
192+
{
193+
"name": "X-Amz-Target",
194+
"in": "header",
195+
"required": True,
196+
"schema": {
197+
"type": "string",
198+
"enum": [service_name2 + "." + operation_name]
199+
}
200+
}
201+
]
202+
203+
# AWS signature parameters at path level
204+
result["parameters"] = [
205+
{'$ref': '#/components/parameters/X-Amz-Content-Sha256'},
206+
{'$ref': '#/components/parameters/X-Amz-Date'},
207+
{'$ref': '#/components/parameters/X-Amz-Algorithm'},
208+
{'$ref': '#/components/parameters/X-Amz-Credential'},
209+
{'$ref': '#/components/parameters/X-Amz-Security-Token'},
210+
{'$ref': '#/components/parameters/X-Amz-Signature'},
211+
{'$ref': '#/components/parameters/X-Amz-SignedHeaders'}
212+
]
213+
214+
# Responses
215+
result_post["responses"] = {}
216+
result_responses = result_post["responses"]
217+
218+
# 200 success response
219+
result_responses["200"] = {
220+
"description": "Success",
221+
"content": {
222+
"application/json": {
223+
"schema": {}
224+
}
225+
}
226+
}
227+
228+
if "output" in operation and operation["output"]["target"] != "smithy.api#Unit":
229+
result_responses["200"]["content"]["application/json"]["schema"]["$ref"] = "#/components/schemas/" + operation["output"]["target"].split('#')[1]
230+
else:
231+
result_responses["200"]["content"]["application/json"]["schema"]["type"] = "object"
232+
233+
# Error responses
234+
error_code = 480
235+
if operation.get("errors"):
236+
for error in operation["errors"]:
237+
error_string = str(error_code)
238+
error_name = error["target"].split('#')[1]
239+
240+
result_responses[error_string] = {
241+
"description": error_name,
242+
"content": {
243+
"application/json": {
244+
"schema": {"$ref": f"#/components/schemas/{error_name}"}
245+
}
246+
}
247+
}
248+
error_code += 1
249+
250+
return result

0 commit comments

Comments
 (0)