Asterisk - The Open Source Telephony Project GIT-master-55f4e6d
asterisk_processor.py
Go to the documentation of this file.
2# Asterisk -- An open source telephony toolkit.
3#
4# Copyright (C) 2013, Digium, Inc.
5#
6# David M. Lee, II <dlee@digium.com>
7#
8# See http://www.asterisk.org for more information about
9# the Asterisk project. Please do not directly contact
10# any of the maintainers of this project for assistance;
11# the project provides a web site, mailing lists and IRC
12# channels for your use.
13#
14# This program is free software, distributed under the terms of
15# the GNU General Public License Version 2. See the LICENSE file
16# at the top of the source tree.
17#
18
19"""Implementation of SwaggerPostProcessor which adds fields needed to generate
20Asterisk RESTful HTTP binding code.
21"""
22
23import os
24import re
25
26from swagger_model import Stringify, SwaggerError, SwaggerPostProcessor
27
28try:
29 from collections import OrderedDict
30except ImportError:
31 from odict import OrderedDict
32
33
34def simple_name(name):
35 """Removes the {markers} from a path segement.
36
37 @param name: Swagger path segement, with {pathVar} markers.
38 """
39 if name.startswith('{') and name.endswith('}'):
40 return name[1:-1]
41 return name
42
43
44def wikify(str):
45 """Escapes a string for the wiki.
46
47 @param str: String to escape
48 """
49 # Replace all line breaks with line feeds
50 str = re.sub(r'<br\s*/?>', '\n', str)
51 return re.sub(r'([{}\[\]])', r'\\\1', str)
52
53
54def snakify(name):
55 """Helper to take a camelCase or dash-seperated name and make it
56 snake_case.
57 """
58 r = ''
59 prior_lower = False
60 for c in name:
61 if c.isupper() and prior_lower:
62 r += "_"
63 if c == '-':
64 c = '_'
65 prior_lower = c.islower()
66 r += c.lower()
67 return r
68
69
71 """Tree representation of a Swagger API declaration.
72 """
73 def __init__(self, name, parent):
74 """Ctor.
75
76 @param name: Name of this path segment. May have {pathVar} markers.
77 @param parent: Parent PathSegment.
78 """
79 #: Segment name, with {pathVar} markers removed
80 self.name = simple_name(name)
81 #: True if segment is a {pathVar}, else None.
82 self.is_wildcard = None
83 #: Underscore seperated name all ancestor segments
84 self.full_name = None
85 #: Dictionary of child PathSegements
86 self.__children = OrderedDict()
87 #: List of operations on this segement
88 self.operations = []
89
90 if self.name != name:
91 self.is_wildcard = True
92
93 if not self.name:
94 assert(not parent)
95 self.full_name = ''
96 if not parent or not parent.name:
97 self.full_name = name
98 else:
99 self.full_name = "%s_%s" % (parent.full_name, self.name)
100
101 def get_child(self, path):
102 """Walks descendants to get path, creating it if necessary.
103
104 @param path: List of path names.
105 @return: PageSegment corresponding to path.
106 """
107 assert simple_name(path[0]) == self.name
108 if (len(path) == 1):
109 return self
110 child = self.__children.get(path[1])
111 if not child:
112 child = PathSegment(path[1], self)
113 self.__children[path[1]] = child
114 return child.get_child(path[1:])
115
116 def children(self):
117 """Gets list of children.
118 """
119 return self.__children.values()
120
121 def num_children(self):
122 """Gets count of children.
123 """
124 return len(self.__children)
125
126
128 """A SwaggerPostProcessor which adds fields needed to generate Asterisk
129 RESTful HTTP binding code.
130 """
131
132 #: How Swagger types map to C.
133 type_mapping = {
134 'string': 'const char *',
135 'boolean': 'int',
136 'number': 'int',
137 'int': 'int',
138 'long': 'long',
139 'double': 'double',
140 'float': 'float',
141 }
142
143 #: String conversion functions for string to C type.
144 convert_mapping = {
145 'string': '',
146 'int': 'atoi',
147 'long': 'atol',
148 'double': 'atof',
149 'boolean': 'ast_true',
150 }
151
152 #: JSON conversion functions
153 json_convert_mapping = {
154 'string': 'ast_json_string_get',
155 'int': 'ast_json_integer_get',
156 'long': 'ast_json_integer_get',
157 'double': 'ast_json_real_get',
158 'boolean': 'ast_json_is_true',
159 }
160
161 def __init__(self, wiki_prefix):
162 self.wiki_prefix = wiki_prefix
163
164 def process_resource_api(self, resource_api, context):
165 resource_api.wiki_prefix = self.wiki_prefix
166 # Derive a resource name from the API declaration's filename
167 resource_api.name = re.sub('\..*', '',
168 os.path.basename(resource_api.path))
169 # Now in all caps, for include guard
170 resource_api.name_caps = resource_api.name.upper()
171 resource_api.name_title = resource_api.name.capitalize()
172 resource_api.c_name = snakify(resource_api.name)
173 # Construct the PathSegement tree for the API.
174 if resource_api.api_declaration:
175 resource_api.root_path = PathSegment('', None)
176 for api in resource_api.api_declaration.apis:
177 segment = resource_api.root_path.get_child(api.path.split('/'))
178 for operation in api.operations:
179 segment.operations.append(operation)
180 api.full_name = segment.full_name
181
182 # Since every API path should start with /[resource], root should
183 # have exactly one child.
184 if resource_api.root_path.num_children() != 1:
185 raise SwaggerError(
186 "Should not mix resources in one API declaration", context)
187 # root_path isn't needed any more
188 resource_api.root_path = list(resource_api.root_path.children())[0]
189 if resource_api.name != resource_api.root_path.name:
190 raise SwaggerError(
191 "API declaration name should match", context)
192 resource_api.root_full_name = resource_api.root_path.full_name
193
194 def process_api(self, api, context):
195 api.wiki_path = wikify(api.path)
196
197 def process_operation(self, operation, context):
198 # Nicknames are camelCase, Asterisk coding is snake case
199 operation.c_nickname = snakify(operation.nickname)
200 operation.c_http_method = 'AST_HTTP_' + operation.http_method
201 if not operation.summary.endswith("."):
202 raise SwaggerError("Summary should end with .", context)
203 operation.wiki_summary = wikify(operation.summary or "")
204 operation.wiki_notes = wikify(operation.notes or "")
205 for error_response in operation.error_responses:
206 error_response.wiki_reason = wikify(error_response.reason or "")
207 operation.parse_body = (operation.body_parameter or operation.has_query_parameters) and True
208
209 def process_parameter(self, parameter, context):
210 if parameter.param_type == 'body':
211 parameter.is_body_parameter = True;
212 parameter.c_data_type = 'struct ast_json *'
213 else:
214 parameter.is_body_parameter = False;
215 if not parameter.data_type in self.type_mapping:
216 raise SwaggerError(
217 "Invalid parameter type %s" % parameter.data_type, context)
218 # Type conversions
219 parameter.c_data_type = self.type_mapping[parameter.data_type]
220 parameter.c_convert = self.convert_mapping[parameter.data_type]
221 parameter.json_convert = self.json_convert_mapping[parameter.data_type]
222
223 # Parameter names are camelcase, Asterisk convention is snake case
224 parameter.c_name = snakify(parameter.name)
225 # You shouldn't put a space between 'char *' and the variable
226 if parameter.c_data_type.endswith('*'):
227 parameter.c_space = ''
228 else:
229 parameter.c_space = ' '
230 parameter.wiki_description = wikify(parameter.description)
231 if parameter.allowable_values:
232 parameter.wiki_allowable_values = parameter.allowable_values.to_wiki()
233 else:
234 parameter.wiki_allowable_values = None
235
236 def process_model(self, model, context):
237 model.description_dox = model.description.replace('\n', '\n * ')
238 model.description_dox = re.sub(' *\n', '\n', model.description_dox)
239 model.wiki_description = wikify(model.description)
240 model.c_id = snakify(model.id)
241 return model
242
243 def process_property(self, prop, context):
244 if "-" in prop.name:
245 raise SwaggerError("Property names cannot have dashes", context)
246 if prop.name != prop.name.lower():
247 raise SwaggerError("Property name should be all lowercase",
248 context)
249 prop.wiki_description = wikify(prop.description)
250
251 def process_type(self, swagger_type, context):
252 swagger_type.c_name = snakify(swagger_type.name)
253 swagger_type.c_singular_name = snakify(swagger_type.singular_name)
254 swagger_type.wiki_name = wikify(swagger_type.name)
def process_resource_api(self, resource_api, context)
def process_operation(self, operation, context)
def process_type(self, swagger_type, context)
def process_property(self, prop, context)
def process_model(self, model, context)
def process_parameter(self, parameter, context)
def __init__(self, name, parent)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)