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. 
 
   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. 
 
 
  215class 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. 
  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#apis 
  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)
 
 
__init__(self, min_value, max_value)
load(self, api_decl_json, processor, context)
__load_file(self, api_declaration_file, processor, context)
load_file(self, api_declaration_file, processor)
load(self, api_json, processor, context)
load(self, err_json, processor, context)
set_extends_type(self, extends_type)
set_subtype_types(self, subtype_types)
load(self, id, model_json, processor, context)
load(self, op_json, processor, context)
is_type(self, other_type)
load(self, parameter_json, processor, context)
get_swagger_version(self)
version_less_than(self, ver)
__init__(self, swagger_version, stack)
next(self, version=None, stack=None)
next_stack(self, json, id_field)
load(self, property_json, processor, context)
load_api_declaration(self, base_dir, processor)
load(self, api_json, processor, context)
load_file(self, resource_file, processor)
load(self, resources_json, processor, context)
__load_file(self, resource_file, processor, context)
__init__(self, msg, context, cause=None)
process_api(self, api, context)
process_property(self, property, context)
process_model(self, model, context)
process_operation(self, operation, context)
process_resource_api(self, resource_api, context)
process_resource_listing(self, resource_listing, context)
process_parameter(self, parameter, context)
process_type(self, swagger_type, context)
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)
compare_versions(lhs, rhs)
validate_required_fields(json, required_fields, context)
load_allowable_values(json, context)
get_list_parameter_type(type_string)
static int load_file(const char *filename, char **ret)
Read a TEXT file into a string and return the length.