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']
378 def load(self, op_json, processor, context):
379 context = context.next_stack(op_json,
'nickname')
382 self.
nickname = op_json.get(
'nickname')
384 response_class = op_json.get(
'responseClass')
386 response_class, processor, context)
387 since = op_json.get(
'since')
or []
388 self.
since =
", ".join(since)
398 "upgrade: websocket is only valid on GET operations",
401 params_json = op_json.get(
'parameters')
or []
403 Parameter().
load(j, processor, context)
for j
in params_json]
405 p
for p
in self.
parameters if p.is_type(
'query')]
408 p
for p
in self.
parameters if p.is_type(
'path')]
411 p
for p
in self.
parameters if p.is_type(
'header')]
419 p
for p
in self.
parameters if p.is_type(
'body')]
421 raise SwaggerError(
"Cannot have more than one body param", context)
425 self.
summary = op_json.get(
'summary')
426 self.
notes = op_json.get(
'notes')
427 err_json = op_json.get(
'errorResponses')
or []
431 processor.process_operation(self, context)
436 """Model of a single API in an API declaration.
438 See https://github.com/wordnik/swagger-core/wiki/API-Declaration
441 required_fields = ['path',
'operations']
448 def load(self, api_json, processor, context):
449 context = context.next_stack(api_json,
'path')
451 self.
path = api_json.get(
'path')
453 op_json = api_json.get(
'operations')
457 processor.process_api(self, context)
462 """Returns the type parameter if the given type_string is List[].
464 @param type_string: Type string to parse
465 @returns Type parameter of the list,
or None if not a List.
467 list_match = re.match(r'^List\[(.*)\]$', type_string)
468 return list_match
and list_match.group(1)
472 """Model of a Swagger property.
474 See https://github.com/wordnik/swagger-core/wiki/datatypes
477 required_fields = ['type']
485 def load(self, property_json, processor, context):
488 context = context.next_stack({
'name': self.
name},
'name')
489 self.
description = property_json.get(
'description')
or ''
490 self.
required = property_json.get(
'required')
or False
492 type = property_json.get(
'type')
495 processor.process_property(self, context)
500 """Model of a Swagger model.
502 See https://github.com/wordnik/swagger-core/wiki/datatypes
505 required_fields = ['description',
'properties']
518 def load(self, id, model_json, processor, context):
519 context = context.next_stack(model_json,
'id')
522 self.
id = model_json.get(
'id')
525 raise SwaggerError(
"Model id doesn't match name", context)
526 self.
subtypes = model_json.get(
'subTypes')
or []
527 if self.
subtypes and context.version_less_than(
"1.2"):
528 raise SwaggerError(
"Type extension support added in Swagger 1.2",
531 props = model_json.get(
'properties').items()
or []
533 Property(k).
load(j, processor, context)
for (k, j)
in props]
536 discriminator = model_json.get(
'discriminator')
539 if context.version_less_than(
"1.2"):
540 raise SwaggerError(
"Discriminator support added in Swagger 1.2",
543 discr_props = [p
for p
in self.
__properties if p.name == discriminator]
546 "Discriminator '%s' does not name a property of '%s'" % (
547 discriminator, self.
id),
553 indent=2, separators=(
',',
': '))
555 processor.process_model(self, context)
571 """Returns the discriminator, digging through base types if needed.
586 """Returns the full list of all subtypes, including sub-subtypes.
590 for subsubtypes
in subtype.all_subtypes()]
591 return sorted(res, key=
lambda m: m.id)
594 """Returns True if type has any subtypes.
600 """Model class for an API Declaration.
602 See https://github.com/wordnik/swagger-core/wiki/API-Declaration
606 'swaggerVersion',
'_author',
'_copyright',
'apiVersion',
'basePath',
607 'resourcePath',
'apis',
'models'
624 return self.
__load_file(api_declaration_file, processor, context)
627 except Exception
as e:
628 print(
"Error: ", traceback.format_exc(), file=sys.stderr)
630 "Error loading %s" % api_declaration_file, context, e)
632 def __load_file(self, api_declaration_file, processor, context):
633 with open(api_declaration_file)
as fp:
634 self.
load(json.load(fp), processor, context)
636 expected_resource_path =
'/api-docs/' + \
637 os.path.basename(api_declaration_file) \
641 print(
"%s != %s" % (self.
resource_path, expected_resource_path),
643 raise SwaggerError(
"resourcePath has incorrect value", context)
647 def load(self, api_decl_json, processor, context):
648 """Loads a resource from a single Swagger resource.json file.
659 self.
author = api_decl_json.get(
'_author')
660 self.
copyright = api_decl_json.get(
'_copyright')
662 self.
base_path = api_decl_json.get(
'basePath')
665 since = api_decl_json.get(
'since')
or []
666 self.
since =
", ".join(since)
667 api_json = api_decl_json.get(
'apis')
or []
669 Api().
load(j, processor, context)
for j
in api_json]
671 for api
in self.
apis:
672 if api.path
in paths:
673 raise SwaggerError(
"API with duplicated path: %s" % api.path, context)
676 models = api_decl_json.get(
'models').items()
or []
678 for (id, json)
in models]
681 model_dict = dict((m.id, m)
for m
in self.
models)
683 def link_subtype(name):
684 res = model_dict.get(name)
688 res.set_extends_type(m)
691 m.set_subtype_types([
692 link_subtype(subtype)
for subtype
in m.subtypes])
697 """Model of an API listing in the resources.json file.
700 required_fields = ['path',
'description']
707 def load(self, api_json, processor, context):
708 context = context.next_stack(api_json,
'path')
710 self.
path = api_json[
'path'].
replace(
'{format}',
'json')
713 if not self.
path or self.
path[0] !=
'/':
715 processor.process_resource_api(self, context)
721 processor.process_resource_api(self, [self.
file])
725 """Model of Swagger's resources.json file.
728 required_fields = ['apiVersion',
'basePath',
'apis']
739 return self.
__load_file(resource_file, processor, context)
742 except Exception
as e:
743 print(
"Error: ", traceback.format_exc(), file=sys.stderr)
745 "Error loading %s" % resource_file, context, e)
747 def __load_file(self, resource_file, processor, context):
748 with open(resource_file)
as fp:
749 return self.
load(json.load(fp), processor, context)
751 def load(self, resources_json, processor, context):
760 self.
base_path = resources_json[
'basePath']
761 apis_json = resources_json[
'apis']
764 processor.process_resource_listing(self, context)
769 """Checks a JSON object for a set of required fields.
771 If any required field is missing, a SwaggerError
is raised.
773 @param json: JSON object to check.
774 @param required_fields: List of required fields.
775 @param context: Current context
in the API.
777 missing_fields = [f for f
in required_fields
if not f
in json]
781 "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.