19"""Swagger data model objects.
21These objects should map directly to the Swagger api-docs, without a lot of
22additional fields. In the process of translation, it should also validate the
23model for consistency against the Swagger spec (i.e., fail if fields are
24missing, or have incorrect values).
26See https://github.com/wordnik/swagger-core/wiki/API-Declaration for the spec.
29from __future__
import print_function
38SWAGGER_VERSIONS = [
"1.1",
"1.2"]
54 """Simple mix-in to make the repr of the model classes more meaningful.
57 return "%s(%s)" % (self.__class__, pprint.saferepr(self.__dict__))
61 '''Performs a lexicographical comparison between two version numbers.
63 This properly handles simple major.minor.whatever.sure.why.not version
64 numbers, but fails miserably if there
's any letters in there.
71 @param lhs Left hand side of the comparison
72 @param rhs Right hand side of the comparison
73 @return < 0
if lhs < rhs
74 @return == 0
if lhs == rhs
75 @return > 0
if lhs > rhs
77 lhs = [int(v) for v
in lhs.split(
'.')]
78 rhs = [int(v)
for v
in rhs.split(
'.')]
79 return (lhs > rhs) - (lhs < rhs)
83 """Context information for parsing.
85 This object is immutable. To change contexts (like adding an item to the
86 stack), use the
next()
and next_stack() functions to build a new one.
94 return "ParsingContext(swagger_version=%s, stack=%s)" % (
103 swagger_version = property(get_swagger_version)
105 stack = property(get_stack)
111 """Returns a new item pushed to the stack.
113 @param json: Current JSON object.
114 @param id_field: Field identifying this object.
115 @return New context
with additional item
in the stack.
117 if not id_field
in json:
118 raise SwaggerError(
"Missing id_field: %s" % id_field, self)
119 new_stack = self.
stack + [
'%s=%s' % (id_field,
str(json[id_field]))]
122 def next(self, version=None, stack=None):
124 version = self.version
131 """Raised when an error is encountered mapping the JSON objects into the
138 @param msg: String message
for the error.
139 @param context: ParsingContext object
140 @param cause: Optional exception that caused this one.
142 super(Exception, self).__init__(msg, context, cause)
146 """Post processing interface for model objects. This processor can add
147 fields to model objects for additional information to use
in the
151 """Post process a ResourceApi object.
153 @param resource_api: ResourceApi object.
154 @param context: Current context
in the API.
159 """Post process an Api object.
161 @param api: Api object.
162 @param context: Current context
in the API.
167 """Post process a Operation object.
169 @param operation: Operation object.
170 @param context: Current context
in the API.
175 """Post process a Parameter object.
177 @param parameter: Parameter object.
178 @param context: Current context
in the API.
183 """Post process a Model object.
185 @param model: Model object.
186 @param context: Current context
in the API.
191 """Post process a Property object.
193 @param property: Property object.
194 @param context: Current context
in the API.
199 """Post process a SwaggerType object.
201 @param swagger_type: ResourceListing object.
202 @param context: Current context
in the API.
207 """Post process the overall ResourceListing object.
209 @param resource_listing: ResourceListing object.
210 @param context: Current context
in the API.
216 """Model of a allowableValues of type RANGE
218 See https://github.com/wordnik/swagger-core/wiki/datatypes
229 """Model of a allowableValues of type LIST
231 See https://github.com/wordnik/swagger-core/wiki/datatypes
237 return "Allowed values: {0}".format(
", ".join(self.
values))
241 """Parse a JSON allowableValues object.
243 This returns None, AllowableList
or AllowableRange, depending on the
244 valueType
in the JSON. If the valueType
is not recognized, a SwaggerError
250 if not 'valueType' in json:
253 value_type = json[
'valueType']
255 if value_type ==
'RANGE':
256 if not 'min' in json
and not 'max' in json:
259 if value_type ==
'LIST':
260 if not 'values' in json:
263 raise SwaggerError(
"Unkown valueType %s" % value_type, context)
267 """Model of an operation's parameter.
269 See https://github.com/wordnik/swagger-core/wiki/parameters
272 required_fields = ['name',
'paramType',
'dataType']
283 def load(self, parameter_json, processor, context):
284 context = context.next_stack(parameter_json,
'name')
286 self.
name = parameter_json.get(
'name')
287 self.
param_type = parameter_json.get(
'paramType')
288 self.
description = parameter_json.get(
'description')
or ''
289 self.
data_type = parameter_json.get(
'dataType')
290 self.
required = parameter_json.get(
'required')
or False
293 parameter_json.get(
'allowableValues'), context)
294 self.
allow_multiple = parameter_json.get(
'allowMultiple')
or False
295 processor.process_parameter(self, context)
296 if parameter_json.get(
'allowedValues'):
298 "Field 'allowedValues' invalid; use 'allowableValues'",
307 """Model of an error response.
309 See https://github.com/wordnik/swagger-core/wiki/errors
312 required_fields = ['code',
'reason']
318 def load(self, err_json, processor, context):
319 context = context.next_stack(err_json,
'code')
321 self.
code = err_json.get(
'code')
322 self.
reason = err_json.get(
'reason')
327 """Model of a data type.
339 def load(self, type_name, processor, context):
341 if type_name ==
'integer':
342 raise SwaggerError(
"The type for integer should be 'int'", context)
344 self.
name = type_name
346 self.
is_list = type_param
is not None
355 processor.process_type(self, context)
360 """Model of an operation on an API
362 See https://github.com/wordnik/swagger-core/wiki/API-Declaration
365 required_fields = ['httpMethod',
'nickname',
'responseClass',
'summary']
377 def load(self, op_json, processor, context):
378 context = context.next_stack(op_json,
'nickname')
381 self.
nickname = op_json.get(
'nickname')
383 response_class = op_json.get(
'responseClass')
385 response_class, processor, context)
395 "upgrade: websocket is only valid on GET operations",
398 params_json = op_json.get(
'parameters')
or []
400 Parameter().
load(j, processor, context)
for j
in params_json]
402 p
for p
in self.
parameters if p.is_type(
'query')]
405 p
for p
in self.
parameters if p.is_type(
'path')]
408 p
for p
in self.
parameters if p.is_type(
'header')]
416 p
for p
in self.
parameters if p.is_type(
'body')]
418 raise SwaggerError(
"Cannot have more than one body param", context)
422 self.
summary = op_json.get(
'summary')
423 self.
notes = op_json.get(
'notes')
424 err_json = op_json.get(
'errorResponses')
or []
428 processor.process_operation(self, context)
433 """Model of a single API in an API declaration.
435 See https://github.com/wordnik/swagger-core/wiki/API-Declaration
438 required_fields = ['path',
'operations']
445 def load(self, api_json, processor, context):
446 context = context.next_stack(api_json,
'path')
448 self.
path = api_json.get(
'path')
450 op_json = api_json.get(
'operations')
454 processor.process_api(self, context)
459 """Returns the type parameter if the given type_string is List[].
461 @param type_string: Type string to parse
462 @returns Type parameter of the list,
or None if not a List.
464 list_match = re.match('^List\[(.*)\]$', type_string)
465 return list_match
and list_match.group(1)
469 """Model of a Swagger property.
471 See https://github.com/wordnik/swagger-core/wiki/datatypes
474 required_fields = ['type']
482 def load(self, property_json, processor, context):
485 context = context.next_stack({
'name': self.
name},
'name')
486 self.
description = property_json.get(
'description')
or ''
487 self.
required = property_json.get(
'required')
or False
489 type = property_json.get(
'type')
492 processor.process_property(self, context)
497 """Model of a Swagger model.
499 See https://github.com/wordnik/swagger-core/wiki/datatypes
502 required_fields = ['description',
'properties']
515 def load(self, id, model_json, processor, context):
516 context = context.next_stack(model_json,
'id')
519 self.
id = model_json.get(
'id')
522 raise SwaggerError(
"Model id doesn't match name", context)
523 self.
subtypes = model_json.get(
'subTypes')
or []
524 if self.
subtypes and context.version_less_than(
"1.2"):
525 raise SwaggerError(
"Type extension support added in Swagger 1.2",
528 props = model_json.get(
'properties').items()
or []
530 Property(k).
load(j, processor, context)
for (k, j)
in props]
533 discriminator = model_json.get(
'discriminator')
536 if context.version_less_than(
"1.2"):
537 raise SwaggerError(
"Discriminator support added in Swagger 1.2",
540 discr_props = [p
for p
in self.
__properties if p.name == discriminator]
543 "Discriminator '%s' does not name a property of '%s'" % (
544 discriminator, self.
id),
550 indent=2, separators=(
',',
': '))
552 processor.process_model(self, context)
568 """Returns the discriminator, digging through base types if needed.
583 """Returns the full list of all subtypes, including sub-subtypes.
587 for subsubtypes
in subtype.all_subtypes()]
588 return sorted(res, key=
lambda m: m.id)
591 """Returns True if type has any subtypes.
597 """Model class for an API Declaration.
599 See https://github.com/wordnik/swagger-core/wiki/API-Declaration
603 'swaggerVersion',
'_author',
'_copyright',
'apiVersion',
'basePath',
604 'resourcePath',
'apis',
'models'
620 return self.
__load_file(api_declaration_file, processor, context)
623 except Exception
as e:
624 print(
"Error: ", traceback.format_exc(), file=sys.stderr)
626 "Error loading %s" % api_declaration_file, context, e)
628 def __load_file(self, api_declaration_file, processor, context):
629 with open(api_declaration_file)
as fp:
630 self.
load(json.load(fp), processor, context)
632 expected_resource_path =
'/api-docs/' + \
633 os.path.basename(api_declaration_file) \
637 print(
"%s != %s" % (self.
resource_path, expected_resource_path),
639 raise SwaggerError(
"resourcePath has incorrect value", context)
643 def load(self, api_decl_json, processor, context):
644 """Loads a resource from a single Swagger resource.json file.
655 self.
author = api_decl_json.get(
'_author')
656 self.
copyright = api_decl_json.get(
'_copyright')
658 self.
base_path = api_decl_json.get(
'basePath')
661 api_json = api_decl_json.get(
'apis')
or []
663 Api().
load(j, processor, context)
for j
in api_json]
665 for api
in self.
apis:
666 if api.path
in paths:
667 raise SwaggerError(
"API with duplicated path: %s" % api.path, context)
670 models = api_decl_json.get(
'models').items()
or []
672 for (id, json)
in models]
675 model_dict = dict((m.id, m)
for m
in self.
models)
677 def link_subtype(name):
678 res = model_dict.get(name)
682 res.set_extends_type(m)
685 m.set_subtype_types([
686 link_subtype(subtype)
for subtype
in m.subtypes])
691 """Model of an API listing in the resources.json file.
694 required_fields = ['path',
'description']
701 def load(self, api_json, processor, context):
702 context = context.next_stack(api_json,
'path')
704 self.
path = api_json[
'path'].
replace(
'{format}',
'json')
707 if not self.
path or self.
path[0] !=
'/':
709 processor.process_resource_api(self, context)
715 processor.process_resource_api(self, [self.
file])
719 """Model of Swagger's resources.json file.
722 required_fields = ['apiVersion',
'basePath',
'apis']
733 return self.
__load_file(resource_file, processor, context)
736 except Exception
as e:
737 print(
"Error: ", traceback.format_exc(), file=sys.stderr)
739 "Error loading %s" % resource_file, context, e)
741 def __load_file(self, resource_file, processor, context):
742 with open(resource_file)
as fp:
743 return self.
load(json.load(fp), processor, context)
745 def load(self, resources_json, processor, context):
754 self.
base_path = resources_json[
'basePath']
755 apis_json = resources_json[
'apis']
758 processor.process_resource_listing(self, context)
763 """Checks a JSON object for a set of required fields.
765 If any required field is missing, a SwaggerError
is raised.
767 @param json: JSON object to check.
768 @param required_fields: List of required fields.
769 @param context: Current context
in the API.
771 missing_fields = [f for f
in required_fields
if not f
in json]
775 "Missing fields: %s" %
', '.join(missing_fields), context)
def __init__(self, values)
def __init__(self, min_value, max_value)
def __load_file(self, api_declaration_file, processor, context)
def load(self, api_decl_json, processor, context)
def load_file(self, api_declaration_file, processor)
def load(self, api_json, processor, context)
def load(self, err_json, processor, context)
def set_subtype_types(self, subtype_types)
def set_extends_type(self, extends_type)
def load(self, id, model_json, processor, context)
def load(self, op_json, processor, context)
def is_type(self, other_type)
def load(self, parameter_json, processor, context)
def get_swagger_version(self)
def version_less_than(self, ver)
def next(self, version=None, stack=None)
def next_stack(self, json, id_field)
def __init__(self, swagger_version, stack)
def load(self, property_json, processor, context)
def load_api_declaration(self, base_dir, processor)
def load(self, api_json, processor, context)
def load_file(self, resource_file, processor)
def load(self, resources_json, processor, context)
def __load_file(self, resource_file, processor, context)
def __init__(self, msg, context, cause=None)
def process_resource_listing(self, resource_listing, context)
def process_resource_api(self, resource_api, context)
def process_operation(self, operation, context)
def process_type(self, swagger_type, context)
def process_model(self, model, context)
def process_parameter(self, parameter, context)
def process_property(self, property, context)
def process_api(self, api, context)
def load(self, type_name, processor, context)
static int set(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int replace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
def get_list_parameter_type(type_string)
def load_allowable_values(json, context)
def compare_versions(lhs, rhs)
def validate_required_fields(json, required_fields, context)
static int load_file(const char *filename, char **ret)
Read a TEXT file into a string and return the length.