Asterisk - The Open Source Telephony Project  GIT-master-b7027de
asterisk_processor.py
Go to the documentation of this file.
1 #
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
20 Asterisk RESTful HTTP binding code.
21 """
22 
23 import os
24 import re
25 
26 from swagger_model import Stringify, SwaggerError, SwaggerPostProcessor
27 
28 try:
29  from collections import OrderedDict
30 except ImportError:
31  from odict import OrderedDict
32 
33 
34 def 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 
44 def 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 
54 def 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 is '-':
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 decendents 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_property(self, prop, context)
def process_resource_api(self, resource_api, context)
def process_model(self, model, context)
def __init__(self, name, parent)
def process_parameter(self, parameter, context)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
def process_type(self, swagger_type, context)
def process_operation(self, operation, context)