19 """Swagger data model objects. 21 These objects should map directly to the Swagger api-docs, without a lot of 22 additional fields. In the process of translation, it should also validate the 23 model for consistency against the Swagger spec (i.e., fail if fields are 24 missing, or have incorrect values). 26 See https://github.com/wordnik/swagger-core/wiki/API-Declaration for the spec. 29 from __future__
import print_function
38 SWAGGER_VERSIONS = [
"1.1",
"1.2"]
40 SWAGGER_PRIMITIVES = [
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. 215 class AllowableRange(Stringify):
216 """Model of a allowableValues of type RANGE 218 See https://github.com/wordnik/swagger-core/wiki/datatypes#complex-types 229 """Model of a allowableValues of type LIST 231 See https://github.com/wordnik/swagger-core/wiki/datatypes#complex-types 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. 338 def load(self, type_name, processor, context):
340 if type_name ==
'integer':
341 raise SwaggerError(
"The type for integer should be 'int'", context)
343 self.
name = type_name
345 self.
is_list = type_param
is not None 352 processor.process_type(self, context)
357 """Model of an operation on an API 359 See https://github.com/wordnik/swagger-core/wiki/API-Declaration#apis 362 required_fields = [
'httpMethod',
'nickname',
'responseClass',
'summary']
373 def load(self, op_json, processor, context):
374 context = context.next_stack(op_json,
'nickname')
377 self.
nickname = op_json.get(
'nickname')
378 response_class = op_json.get(
'responseClass')
380 response_class, processor, context)
390 "upgrade: websocket is only valid on GET operations",
393 params_json = op_json.get(
'parameters')
or []
395 Parameter().
load(j, processor, context)
for j
in params_json]
397 p
for p
in self.
parameters if p.is_type(
'query')]
400 p
for p
in self.
parameters if p.is_type(
'path')]
403 p
for p
in self.
parameters if p.is_type(
'header')]
411 p
for p
in self.
parameters if p.is_type(
'body')]
413 raise SwaggerError(
"Cannot have more than one body param", context)
417 self.
summary = op_json.get(
'summary')
418 self.
notes = op_json.get(
'notes')
419 err_json = op_json.get(
'errorResponses')
or []
423 processor.process_operation(self, context)
428 """Model of a single API in an API declaration. 430 See https://github.com/wordnik/swagger-core/wiki/API-Declaration 433 required_fields = [
'path',
'operations']
440 def load(self, api_json, processor, context):
441 context = context.next_stack(api_json,
'path')
443 self.
path = api_json.get(
'path')
445 op_json = api_json.get(
'operations')
449 processor.process_api(self, context)
454 """Returns the type parameter if the given type_string is List[]. 456 @param type_string: Type string to parse 457 @returns Type parameter of the list, or None if not a List. 459 list_match = re.match(
'^List\[(.*)\]$', type_string)
460 return list_match
and list_match.group(1)
464 """Model of a Swagger property. 466 See https://github.com/wordnik/swagger-core/wiki/datatypes 469 required_fields = [
'type']
477 def load(self, property_json, processor, context):
480 context = context.next_stack({
'name': self.
name},
'name')
481 self.
description = property_json.get(
'description')
or '' 482 self.
required = property_json.get(
'required')
or False 484 type = property_json.get(
'type')
487 processor.process_property(self, context)
492 """Model of a Swagger model. 494 See https://github.com/wordnik/swagger-core/wiki/datatypes 497 required_fields = [
'description',
'properties']
509 def load(self, id, model_json, processor, context):
510 context = context.next_stack(model_json,
'id')
513 self.
id = model_json.get(
'id')
515 raise SwaggerError(
"Model id doesn't match name", context)
516 self.
subtypes = model_json.get(
'subTypes')
or []
517 if self.
subtypes and context.version_less_than(
"1.2"):
518 raise SwaggerError(
"Type extension support added in Swagger 1.2",
521 props = model_json.get(
'properties').items()
or []
523 Property(k).
load(j, processor, context)
for (k, j)
in props]
526 discriminator = model_json.get(
'discriminator')
529 if context.version_less_than(
"1.2"):
530 raise SwaggerError(
"Discriminator support added in Swagger 1.2",
533 discr_props = [p
for p
in self.
__properties if p.name == discriminator]
536 "Discriminator '%s' does not name a property of '%s'" % (
537 discriminator, self.
id),
543 indent=2, separators=(
',',
': '))
545 processor.process_model(self, context)
558 """Returns the discriminator, digging through base types if needed. 573 """Returns the full list of all subtypes, including sub-subtypes. 577 for subsubtypes
in subtype.all_subtypes()]
578 return sorted(res, key=
lambda m: m.id)
581 """Returns True if type has any subtypes. 587 """Model class for an API Declaration. 589 See https://github.com/wordnik/swagger-core/wiki/API-Declaration 593 'swaggerVersion',
'_author',
'_copyright',
'apiVersion',
'basePath',
594 'resourcePath',
'apis',
'models' 610 return self.
__load_file(api_declaration_file, processor, context)
613 except Exception
as e:
614 print(
"Error: ", traceback.format_exc(), file=sys.stderr)
616 "Error loading %s" % api_declaration_file, context, e)
618 def __load_file(self, api_declaration_file, processor, context):
619 with open(api_declaration_file)
as fp:
620 self.
load(json.load(fp), processor, context)
622 expected_resource_path =
'/api-docs/' + \
623 os.path.basename(api_declaration_file) \
627 print(
"%s != %s" % (self.
resource_path, expected_resource_path),
629 raise SwaggerError(
"resourcePath has incorrect value", context)
633 def load(self, api_decl_json, processor, context):
634 """Loads a resource from a single Swagger resource.json file. 645 self.
author = api_decl_json.get(
'_author')
646 self.
copyright = api_decl_json.get(
'_copyright')
648 self.
base_path = api_decl_json.get(
'basePath')
651 api_json = api_decl_json.get(
'apis')
or []
653 Api().
load(j, processor, context)
for j
in api_json]
655 for api
in self.
apis:
656 if api.path
in paths:
657 raise SwaggerError(
"API with duplicated path: %s" % api.path, context)
660 models = api_decl_json.get(
'models').items()
or []
662 for (id, json)
in models]
665 model_dict = dict((m.id, m)
for m
in self.
models)
667 def link_subtype(name):
668 res = model_dict.get(name)
672 res.set_extends_type(m)
675 m.set_subtype_types([
676 link_subtype(subtype)
for subtype
in m.subtypes])
681 """Model of an API listing in the resources.json file. 684 required_fields = [
'path',
'description']
691 def load(self, api_json, processor, context):
692 context = context.next_stack(api_json,
'path')
694 self.
path = api_json[
'path'].
replace(
'{format}',
'json')
697 if not self.
path or self.
path[0] !=
'/':
699 processor.process_resource_api(self, context)
705 processor.process_resource_api(self, [self.
file])
709 """Model of Swagger's resources.json file. 712 required_fields = [
'apiVersion',
'basePath',
'apis']
723 return self.
__load_file(resource_file, processor, context)
726 except Exception
as e:
727 print(
"Error: ", traceback.format_exc(), file=sys.stderr)
729 "Error loading %s" % resource_file, context, e)
731 def __load_file(self, resource_file, processor, context):
732 with open(resource_file)
as fp:
733 return self.
load(json.load(fp), processor, context)
735 def load(self, resources_json, processor, context):
744 self.
base_path = resources_json[
'basePath']
745 apis_json = resources_json[
'apis']
748 processor.process_resource_listing(self, context)
753 """Checks a JSON object for a set of required fields. 755 If any required field is missing, a SwaggerError is raised. 757 @param json: JSON object to check. 758 @param required_fields: List of required fields. 759 @param context: Current context in the API. 761 missing_fields = [f
for f
in required_fields
if not f
in json]
765 "Missing fields: %s" %
', '.join(missing_fields), context)
def next_stack(self, json, id_field)
def __init__(self, swagger_version, stack)
def __load_file(self, resource_file, processor, context)
def load_allowable_values(json, context)
def load(self, property_json, processor, context)
def __init__(self, values)
def load(self, api_json, processor, context)
def set_subtype_types(self, subtype_types)
def process_model(self, model, context)
def get_swagger_version(self)
def process_api(self, api, context)
def validate_required_fields(json, required_fields, context)
def get_list_parameter_type(type_string)
def is_type(self, other_type)
def load_file(self, resource_file, processor)
def process_operation(self, operation, context)
def load_file(self, api_declaration_file, processor)
def next(self, version=None, stack=None)
def compare_versions(lhs, rhs)
def load(self, resources_json, processor, context)
def load(self, op_json, processor, context)
def load(self, err_json, processor, context)
def process_property(self, property, context)
def process_resource_api(self, resource_api, context)
static int load_file(const char *filename, char **ret)
Read a TEXT file into a string and return the length.
def load_api_declaration(self, base_dir, processor)
def process_type(self, swagger_type, context)
def __init__(self, msg, context, cause=None)
def __load_file(self, api_declaration_file, processor, context)
def __init__(self, min_value, max_value)
def version_less_than(self, ver)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
def load(self, api_json, processor, context)
def load(self, api_decl_json, processor, context)
def process_parameter(self, parameter, context)
def load(self, type_name, processor, context)
static int replace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
def load(self, id, model_json, processor, context)
static int set(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
def set_extends_type(self, extends_type)
def process_resource_listing(self, resource_listing, context)
static snd_pcm_format_t format
def load(self, parameter_json, processor, context)