123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- #
- # Asterisk -- An open source telephony toolkit.
- #
- # Copyright (C) 2013, Digium, Inc.
- #
- # David M. Lee, II <dlee@digium.com>
- #
- # See http://www.asterisk.org for more information about
- # the Asterisk project. Please do not directly contact
- # any of the maintainers of this project for assistance;
- # the project provides a web site, mailing lists and IRC
- # channels for your use.
- #
- # This program is free software, distributed under the terms of
- # the GNU General Public License Version 2. See the LICENSE file
- # at the top of the source tree.
- #
- """Implementation of SwaggerPostProcessor which adds fields needed to generate
- Asterisk RESTful HTTP binding code.
- """
- import os
- import re
- from swagger_model import Stringify, SwaggerError, SwaggerPostProcessor
- try:
- from collections import OrderedDict
- except ImportError:
- from odict import OrderedDict
- def simple_name(name):
- """Removes the {markers} from a path segement.
- @param name: Swagger path segement, with {pathVar} markers.
- """
- if name.startswith('{') and name.endswith('}'):
- return name[1:-1]
- return name
- def wikify(str):
- """Escapes a string for the wiki.
- @param str: String to escape
- """
- return re.sub(r'([{}\[\]])', r'\\\1', str)
- def snakify(name):
- """Helper to take a camelCase or dash-seperated name and make it
- snake_case.
- """
- r = ''
- prior_lower = False
- for c in name:
- if c.isupper() and prior_lower:
- r += "_"
- if c is '-':
- c = '_'
- prior_lower = c.islower()
- r += c.lower()
- return r
- class PathSegment(Stringify):
- """Tree representation of a Swagger API declaration.
- """
- def __init__(self, name, parent):
- """Ctor.
- @param name: Name of this path segment. May have {pathVar} markers.
- @param parent: Parent PathSegment.
- """
- #: Segment name, with {pathVar} markers removed
- self.name = simple_name(name)
- #: True if segment is a {pathVar}, else None.
- self.is_wildcard = None
- #: Underscore seperated name all ancestor segments
- self.full_name = None
- #: Dictionary of child PathSegements
- self.__children = OrderedDict()
- #: List of operations on this segement
- self.operations = []
- if self.name != name:
- self.is_wildcard = True
- if not self.name:
- assert(not parent)
- self.full_name = ''
- if not parent or not parent.name:
- self.full_name = name
- else:
- self.full_name = "%s_%s" % (parent.full_name, self.name)
- def get_child(self, path):
- """Walks decendents to get path, creating it if necessary.
- @param path: List of path names.
- @return: PageSegment corresponding to path.
- """
- assert simple_name(path[0]) == self.name
- if (len(path) == 1):
- return self
- child = self.__children.get(path[1])
- if not child:
- child = PathSegment(path[1], self)
- self.__children[path[1]] = child
- return child.get_child(path[1:])
- def children(self):
- """Gets list of children.
- """
- return self.__children.values()
- def num_children(self):
- """Gets count of children.
- """
- return len(self.__children)
- class AsteriskProcessor(SwaggerPostProcessor):
- """A SwaggerPostProcessor which adds fields needed to generate Asterisk
- RESTful HTTP binding code.
- """
- #: How Swagger types map to C.
- type_mapping = {
- 'string': 'const char *',
- 'boolean': 'int',
- 'number': 'int',
- 'int': 'int',
- 'long': 'long',
- 'double': 'double',
- 'float': 'float',
- }
- #: String conversion functions for string to C type.
- convert_mapping = {
- 'string': '',
- 'int': 'atoi',
- 'long': 'atol',
- 'double': 'atof',
- 'boolean': 'ast_true',
- }
- #: JSON conversion functions
- json_convert_mapping = {
- 'string': 'ast_json_string_get',
- 'int': 'ast_json_integer_get',
- 'long': 'ast_json_integer_get',
- 'double': 'ast_json_real_get',
- 'boolean': 'ast_json_is_true',
- }
- def __init__(self, wiki_prefix):
- self.wiki_prefix = wiki_prefix
- def process_resource_api(self, resource_api, context):
- resource_api.wiki_prefix = self.wiki_prefix
- # Derive a resource name from the API declaration's filename
- resource_api.name = re.sub('\..*', '',
- os.path.basename(resource_api.path))
- # Now in all caps, for include guard
- resource_api.name_caps = resource_api.name.upper()
- resource_api.name_title = resource_api.name.capitalize()
- resource_api.c_name = snakify(resource_api.name)
- # Construct the PathSegement tree for the API.
- if resource_api.api_declaration:
- resource_api.root_path = PathSegment('', None)
- for api in resource_api.api_declaration.apis:
- segment = resource_api.root_path.get_child(api.path.split('/'))
- for operation in api.operations:
- segment.operations.append(operation)
- api.full_name = segment.full_name
- # Since every API path should start with /[resource], root should
- # have exactly one child.
- if resource_api.root_path.num_children() != 1:
- raise SwaggerError(
- "Should not mix resources in one API declaration", context)
- # root_path isn't needed any more
- resource_api.root_path = list(resource_api.root_path.children())[0]
- if resource_api.name != resource_api.root_path.name:
- raise SwaggerError(
- "API declaration name should match", context)
- resource_api.root_full_name = resource_api.root_path.full_name
- def process_api(self, api, context):
- api.wiki_path = wikify(api.path)
- def process_operation(self, operation, context):
- # Nicknames are camelCase, Asterisk coding is snake case
- operation.c_nickname = snakify(operation.nickname)
- operation.c_http_method = 'AST_HTTP_' + operation.http_method
- if not operation.summary.endswith("."):
- raise SwaggerError("Summary should end with .", context)
- operation.wiki_summary = wikify(operation.summary or "")
- operation.wiki_notes = wikify(operation.notes or "")
- for error_response in operation.error_responses:
- error_response.wiki_reason = wikify(error_response.reason or "")
- operation.parse_body = (operation.body_parameter or operation.has_query_parameters) and True
- def process_parameter(self, parameter, context):
- if parameter.param_type == 'body':
- parameter.is_body_parameter = True;
- parameter.c_data_type = 'struct ast_json *'
- else:
- parameter.is_body_parameter = False;
- if not parameter.data_type in self.type_mapping:
- raise SwaggerError(
- "Invalid parameter type %s" % parameter.data_type, context)
- # Type conversions
- parameter.c_data_type = self.type_mapping[parameter.data_type]
- parameter.c_convert = self.convert_mapping[parameter.data_type]
- parameter.json_convert = self.json_convert_mapping[parameter.data_type]
- # Parameter names are camelcase, Asterisk convention is snake case
- parameter.c_name = snakify(parameter.name)
- # You shouldn't put a space between 'char *' and the variable
- if parameter.c_data_type.endswith('*'):
- parameter.c_space = ''
- else:
- parameter.c_space = ' '
- parameter.wiki_description = wikify(parameter.description)
- if parameter.allowable_values:
- parameter.wiki_allowable_values = parameter.allowable_values.to_wiki()
- else:
- parameter.wiki_allowable_values = None
- def process_model(self, model, context):
- model.description_dox = model.description.replace('\n', '\n * ')
- model.description_dox = re.sub(' *\n', '\n', model.description_dox)
- model.wiki_description = wikify(model.description)
- model.c_id = snakify(model.id)
- return model
- def process_property(self, prop, context):
- if "-" in prop.name:
- raise SwaggerError("Property names cannot have dashes", context)
- if prop.name != prop.name.lower():
- raise SwaggerError("Property name should be all lowercase",
- context)
- prop.wiki_description = wikify(prop.description)
- def process_type(self, swagger_type, context):
- swagger_type.c_name = snakify(swagger_type.name)
- swagger_type.c_singular_name = snakify(swagger_type.singular_name)
- swagger_type.wiki_name = wikify(swagger_type.name)
|