Skip to content

Instantly share code, notes, and snippets.

@alexrashed
Last active February 17, 2022 11:01
Show Gist options
  • Save alexrashed/54e07fc1a06d96b921ca13251c489cbf to your computer and use it in GitHub Desktop.
Save alexrashed/54e07fc1a06d96b921ca13251c489cbf to your computer and use it in GitHub Desktop.
Script to test botocore service spec service and shape assumptions
from typing import cast, Callable
from botocore.model import StructureShape, ListShape, Shape, MapShape, ServiceModel
from localstack.aws.spec import list_services
def traverse_members(service: ServiceModel, shape: Shape, fun: Callable, traversed_shapes=None, hierarchy=0):
if traversed_shapes is None:
traversed_shapes = []
if shape.name in traversed_shapes:
return
else:
traversed_shapes.append(shape.name)
fun(service, shape, hierarchy)
if shape.type_name in ["list", "structure", "map"]:
hierarchy += 1
if shape.type_name == "list":
shape = cast(ListShape, shape)
traverse_members(service, shape.member, fun, traversed_shapes, hierarchy)
elif shape.type_name == "structure":
shape = cast(StructureShape, shape)
for _, member_shape in shape.members.items():
traverse_members(service, member_shape, fun, traversed_shapes, hierarchy)
elif shape.type_name == "map":
shape = cast(MapShape, shape)
traverse_members(service, shape.key, fun, traversed_shapes, hierarchy)
traverse_members(service, shape.value, fun, traversed_shapes, hierarchy)
def main():
def test_shape_assumptions(current_service: ServiceModel, current_shape: Shape, hierarchy: int):
if current_shape.serialization.get("location"):
# test the assumptions:
# - all location traits are defined in the root structure
if hierarchy > 1:
raise Exception(f"Non root location trait: {current_service}/{current_shape}")
# - all location traits are defined in services which have a rest protocol
if current_service.protocol not in ["rest-xml", "rest-json"]:
raise Exception(f"Non-REST service with location trait: {current_service}")
# - maps within the header (location trait is "headers") only contain string values
if current_shape.serialization.get("location") == "headers":
if current_shape.type_name == "map":
current_shape = cast(MapShape, current_shape)
if not (current_shape.key.type_name == current_shape.value.type_name == "string"):
raise Exception(f"Non-string headers location trait shape: {current_service}/{current_shape}")
elif current_shape.type_name == "structure":
current_shape = cast(StructureShape, current_shape)
for _, member_shape in current_shape.members.items():
if member_shape.type_name != "string":
raise Exception(f"Non-string headers location trait shape: {current_service}/{current_shape}")
elif current_shape.type_name == "list":
current_shape = cast(ListShape, current_shape)
if current_shape.member.type_name != "string":
raise Exception(f"Non-string headers location trait shape: {current_service}/{current_shape}")
if current_shape.serialization.get("payload") and current_service.protocol not in ["rest-xml", "rest-json"]:
raise Exception(f"Payload in non-rest service: {current_service}")
services = list_services()
for service in services:
for shape_name in service.shape_names:
shape = service.shape_for(shape_name)
traverse_members(service, shape, test_shape_assumptions)
print("All good...")
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment