Skip to content
Snippets Groups Projects
asterisk_processor.py 7.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • #
    # 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 re
    
    from swagger_model import *
    
    
    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',
    
        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 = 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 "")
    
    
        def process_parameter(self, parameter, context):
            if not parameter.data_type in self.type_mapping:
                raise SwaggerError(
    
                    "Invalid parameter type %s" % parameter.data_type, context)
    
            # Parameter names are camelcase, Asterisk convention is snake case
            parameter.c_name = snakify(parameter.name)
            parameter.c_data_type = self.type_mapping[parameter.data_type]
    
            parameter.c_convert = self.convert_mapping[parameter.data_type]
    
            # 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)
    
    
        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)