Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aws
GitHub Repository: aws/aws-cli
Path: blob/develop/awscli/schema.py
1566 views
1
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License"). You
4
# may not use this file except in compliance with the License. A copy of
5
# the License is located at
6
#
7
# http://aws.amazon.com/apache2.0/
8
#
9
# or in the "license" file accompanying this file. This file is
10
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
# ANY KIND, either express or implied. See the License for the specific
12
# language governing permissions and limitations under the License.
13
from collections import defaultdict
14
15
16
class ParameterRequiredError(ValueError):
17
pass
18
19
20
class SchemaTransformer:
21
"""
22
Transforms a custom argument parameter schema into an internal
23
model representation so that it can be treated like a normal
24
service model. This includes shorthand JSON parsing and
25
automatic documentation generation. The format of the schema
26
follows JSON Schema, which can be found here:
27
28
http://json-schema.org/
29
30
Only a relevant subset of features is supported here:
31
32
* Types: `object`, `array`, `string`, `integer`, `boolean`
33
* Properties: `type`, `description`, `required`, `enum`
34
35
For example::
36
37
{
38
"type": "array",
39
"items": {
40
"type": "object",
41
"properties": {
42
"arg1": {
43
"type": "string",
44
"required": True,
45
"enum": [
46
"Value1",
47
"Value2",
48
"Value3"
49
]
50
},
51
"arg2": {
52
"type": "integer",
53
"description": "The number of calls"
54
}
55
}
56
}
57
}
58
59
Assuming the schema is applied to a service named `foo`, with an
60
operation named `bar` and that the parameter is called `baz`, you
61
could call it with the shorthand JSON like so::
62
63
$ aws foo bar --baz arg1=Value1,arg2=5 arg1=Value2
64
65
"""
66
67
JSON_SCHEMA_TO_AWS_TYPES = {
68
'object': 'structure',
69
'array': 'list',
70
}
71
72
def __init__(self):
73
self._shape_namer = ShapeNameGenerator()
74
75
def transform(self, schema):
76
"""Convert JSON schema to the format used internally by the AWS CLI.
77
78
:type schema: dict
79
:param schema: The JSON schema describing the argument model.
80
81
:rtype: dict
82
:return: The transformed model in a form that can be consumed
83
internally by the AWS CLI. The dictionary returned will
84
have a list of shapes, where the shape representing the
85
transformed schema is always named ``InputShape`` in the
86
returned dictionary.
87
88
"""
89
shapes = {}
90
self._transform(schema, shapes, 'InputShape')
91
return shapes
92
93
def _transform(self, schema, shapes, shape_name):
94
if 'type' not in schema:
95
raise ParameterRequiredError("Missing required key: 'type'")
96
if schema['type'] == 'object':
97
shapes[shape_name] = self._transform_structure(schema, shapes)
98
elif schema['type'] == 'array':
99
shapes[shape_name] = self._transform_list(schema, shapes)
100
elif schema['type'] == 'map':
101
shapes[shape_name] = self._transform_map(schema, shapes)
102
else:
103
shapes[shape_name] = self._transform_scalar(schema)
104
return shapes
105
106
def _transform_scalar(self, schema):
107
return self._populate_initial_shape(schema)
108
109
def _transform_structure(self, schema, shapes):
110
# Transforming a structure involves:
111
# 1. Generating the shape definition for the structure
112
# 2. Generating the shape definitions for its members
113
structure_shape = self._populate_initial_shape(schema)
114
members = {}
115
required_members = []
116
117
for key, value in schema['properties'].items():
118
current_type_name = self._json_schema_to_aws_type(value)
119
current_shape_name = self._shape_namer.new_shape_name(
120
current_type_name
121
)
122
members[key] = {'shape': current_shape_name}
123
if value.get('required', False):
124
required_members.append(key)
125
self._transform(value, shapes, current_shape_name)
126
structure_shape['members'] = members
127
if required_members:
128
structure_shape['required'] = required_members
129
return structure_shape
130
131
def _transform_map(self, schema, shapes):
132
structure_shape = self._populate_initial_shape(schema)
133
for attribute in ['key', 'value']:
134
type_name = self._json_schema_to_aws_type(schema[attribute])
135
shape_name = self._shape_namer.new_shape_name(type_name)
136
structure_shape[attribute] = {'shape': shape_name}
137
self._transform(schema[attribute], shapes, shape_name)
138
return structure_shape
139
140
def _transform_list(self, schema, shapes):
141
# Transforming a structure involves:
142
# 1. Generating the shape definition for the structure
143
# 2. Generating the shape definitions for its 'items' member
144
list_shape = self._populate_initial_shape(schema)
145
member_type = self._json_schema_to_aws_type(schema['items'])
146
member_shape_name = self._shape_namer.new_shape_name(member_type)
147
list_shape['member'] = {'shape': member_shape_name}
148
self._transform(schema['items'], shapes, member_shape_name)
149
return list_shape
150
151
def _populate_initial_shape(self, schema):
152
shape = {'type': self._json_schema_to_aws_type(schema)}
153
if 'description' in schema:
154
shape['documentation'] = schema['description']
155
if 'enum' in schema:
156
shape['enum'] = schema['enum']
157
return shape
158
159
def _json_schema_to_aws_type(self, schema):
160
if 'type' not in schema:
161
raise ParameterRequiredError("Missing required key: 'type'")
162
type_name = schema['type']
163
return self.JSON_SCHEMA_TO_AWS_TYPES.get(type_name, type_name)
164
165
166
class ShapeNameGenerator:
167
def __init__(self):
168
self._name_cache = defaultdict(int)
169
170
def new_shape_name(self, type_name):
171
self._name_cache[type_name] += 1
172
current_index = self._name_cache[type_name]
173
return '%sType%s' % (type_name.capitalize(), current_index)
174
175