blob: c8f5bfe181dc8dfda9b980a7738ed99a48bb5175 [file] [log] [blame]
Matteo Scandolod2044a42017-08-07 16:08:28 -07001# Copyright 2017-present Open Networking Foundation
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Zack Williams9a42f872019-02-15 17:56:04 -070015from __future__ import absolute_import, print_function
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -070016import pdb
Sapan Bhatiac4f803f2017-04-21 11:50:39 +020017import re
Scott Bakerbe2a5172019-04-10 18:02:50 -070018from jinja2.runtime import Undefined
Scott Baker391f5d82018-10-02 16:34:41 -070019from inflect import engine as inflect_engine_class
20
21inflect_engine = inflect_engine_class()
Sapan Bhatia7886e122017-05-17 11:19:39 +020022
Zack Williams045b63d2019-01-22 16:30:57 -070023
Sapan Bhatiaf7934b52017-06-12 05:04:23 -070024class FieldNotFound(Exception):
25 def __init__(self, message):
26 super(FieldNotFound, self).__init__(message)
27
Zack Williams045b63d2019-01-22 16:30:57 -070028
Sapan Bhatiad4567592017-07-24 17:26:26 -040029def xproto_debug(**kwargs):
Zack Williams045b63d2019-01-22 16:30:57 -070030 print(kwargs)
Sapan Bhatiad4567592017-07-24 17:26:26 -040031 pdb.set_trace()
32
Zack Williams045b63d2019-01-22 16:30:57 -070033
Sapan Bhatia943dad52017-05-19 18:41:01 +020034def xproto_unquote(s):
35 return unquote(s)
36
Zack Williams045b63d2019-01-22 16:30:57 -070037
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020038def unquote(s):
Zack Williams67142162019-03-06 14:01:32 -070039 if s and s.startswith('"') and s.endswith('"'):
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020040 return s[1:-1]
Matteo Scandolo292cc2a2017-07-31 19:02:12 -070041 else:
42 return s
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020043
Zack Williams045b63d2019-01-22 16:30:57 -070044
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020045def xproto_singularize(field):
46 try:
47 # The user has set a singular, as an exception that cannot be handled automatically
Zack Williams045b63d2019-01-22 16:30:57 -070048 singular = field["options"]["singular"]
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020049 singular = unquote(singular)
50 except KeyError:
Zack Williams045b63d2019-01-22 16:30:57 -070051 singular = inflect_engine.singular_noun(field["name"])
Scott Bakera1b089a2018-10-05 09:59:17 -070052 if singular is False:
53 # singular_noun returns False on a noun it can't singularize
54 singular = field["name"]
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020055
56 return singular
57
Zack Williams045b63d2019-01-22 16:30:57 -070058
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +020059def xproto_singularize_pluralize(field):
60 try:
61 # The user has set a plural, as an exception that cannot be handled automatically
Zack Williams045b63d2019-01-22 16:30:57 -070062 plural = field["options"]["plural"]
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +020063 plural = unquote(plural)
64 except KeyError:
Zack Williams045b63d2019-01-22 16:30:57 -070065 singular = inflect_engine.singular_noun(field["name"])
Scott Bakera1b089a2018-10-05 09:59:17 -070066 if singular is False:
67 # singular_noun returns False on a noun it can't singularize
68 singular = field["name"]
69
70 plural = inflect_engine.plural_noun(singular)
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +020071
72 return plural
73
Zack Williams045b63d2019-01-22 16:30:57 -070074
Sapan Bhatia7886e122017-05-17 11:19:39 +020075def xproto_pluralize(field):
76 try:
77 # The user has set a plural, as an exception that cannot be handled automatically
Zack Williams045b63d2019-01-22 16:30:57 -070078 plural = field["options"]["plural"]
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020079 plural = unquote(plural)
Sapan Bhatia7886e122017-05-17 11:19:39 +020080 except KeyError:
Zack Williams045b63d2019-01-22 16:30:57 -070081 plural = inflect_engine.plural_noun(field["name"])
Sapan Bhatia7886e122017-05-17 11:19:39 +020082
83 return plural
Sapan Bhatiac4f803f2017-04-21 11:50:39 +020084
Zack Williams045b63d2019-01-22 16:30:57 -070085
86def xproto_base_def(model_name, base, suffix="", suffix_list=[]):
87 if model_name == "XOSBase":
88 return "(models.Model, PlModelMixIn)"
89 elif not base:
90 return ""
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -070091 else:
Zack Williams045b63d2019-01-22 16:30:57 -070092 int_base = [i["name"] + suffix for i in base if i["name"] in suffix_list]
93 ext_base = [i["name"] for i in base if i["name"] not in suffix_list]
94 return "(" + ",".join(int_base + ext_base) + ")"
95
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -070096
Sapan Bhatia504cc972017-04-27 01:56:28 +020097def xproto_first_non_empty(lst):
Scott Bakerbe2a5172019-04-10 18:02:50 -070098 # Returns the first non-empty element in the list. Empty is interpreted to be either
99 # None or the empty string or an instance of jinja2 Undefined(). The value False and the
100 # string "False" are not considered empty, but are values.
101 for item in lst:
102 if (item is not None) and (item != "") and (not isinstance(item, Undefined)):
103 return item
Zack Williams045b63d2019-01-22 16:30:57 -0700104
Sapan Bhatia504cc972017-04-27 01:56:28 +0200105
Sapan Bhatia943dad52017-05-19 18:41:01 +0200106def xproto_api_type(field):
107 try:
Zack Williams045b63d2019-01-22 16:30:57 -0700108 if unquote(field["options"]["content_type"]) == "date":
109 return "double"
Sapan Bhatia943dad52017-05-19 18:41:01 +0200110 except KeyError:
111 pass
112
Zack Williams045b63d2019-01-22 16:30:57 -0700113 return field["type"]
Sapan Bhatia943dad52017-05-19 18:41:01 +0200114
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200115
116def xproto_base_name(n):
117 # Hack - Refactor NetworkParameter* to make this go away
Zack Williams045b63d2019-01-22 16:30:57 -0700118 if n.startswith("NetworkParameter"):
119 return "_"
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200120
Zack Williams045b63d2019-01-22 16:30:57 -0700121 expr = r"^[A-Z]+[a-z]*"
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200122
123 try:
124 match = re.findall(expr, n)[0]
Zack Williams045b63d2019-01-22 16:30:57 -0700125 except BaseException:
126 return "_"
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200127
128 return match
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200129
Zack Williams045b63d2019-01-22 16:30:57 -0700130
Sapan Bhatia943dad52017-05-19 18:41:01 +0200131def xproto_base_fields(m, table):
132 fields = []
133
Zack Williams045b63d2019-01-22 16:30:57 -0700134 for b in m["bases"]:
135 option1 = b["fqn"]
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200136 try:
Zack Williams045b63d2019-01-22 16:30:57 -0700137 option2 = m["package"] + "." + b["name"]
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200138 except TypeError:
139 option2 = option1
Sapan Bhatia943dad52017-05-19 18:41:01 +0200140
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200141 accessor = None
Zack Williams045b63d2019-01-22 16:30:57 -0700142 if option1 in table:
143 accessor = option1
144 elif option2 in table:
145 accessor = option2
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200146
147 if accessor:
148 base_fields = xproto_base_fields(table[accessor], table)
149
Zack Williams045b63d2019-01-22 16:30:57 -0700150 model_fields = [x.copy() for x in table[accessor]["fields"]]
Scott Bakerc237f882018-09-28 14:12:47 -0700151 for field in model_fields:
152 field["accessor"] = accessor
153
Sapan Bhatia943dad52017-05-19 18:41:01 +0200154 fields.extend(base_fields)
155 fields.extend(model_fields)
156
Zack Williams045b63d2019-01-22 16:30:57 -0700157 if "no_sync" in m["options"] and m["options"]["no_sync"]:
158 fields = [
159 f
160 for f in fields
161 if f["name"] != "backend_status" and f["name"] != "backend_code"
162 ]
Matteo Scandolo39b4a272017-11-17 11:09:21 -0800163
Zack Williams045b63d2019-01-22 16:30:57 -0700164 if "no_policy" in m["options"] and m["options"]["no_policy"]:
165 fields = [
166 f
167 for f in fields
168 if f["name"] != "policy_status" and f["name"] != "policy_code"
169 ]
Matteo Scandolo39b4a272017-11-17 11:09:21 -0800170
Sapan Bhatia943dad52017-05-19 18:41:01 +0200171 return fields
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200172
Zack Williams045b63d2019-01-22 16:30:57 -0700173
Scott Bakerc237f882018-09-28 14:12:47 -0700174def xproto_fields(m, table):
175 """ Generate the full list of models for the xproto message `m` including fields from the classes it inherits.
176
177 Inserts the special field "id" at the very beginning.
178
179 Each time we descend a new level of inheritance, increment the offset field numbers by 100. The base
180 class's fields will be numbered from 1-99, the first descendant will be number 100-199, the second
181 descdendant numbered from 200-299, and so on. This assumes any particular model as at most 100
182 fields.
183 """
184
185 model_fields = [x.copy() for x in m["fields"]]
186 for field in model_fields:
187 field["accessor"] = m["fqn"]
188
189 fields = xproto_base_fields(m, table) + model_fields
190
191 # The "id" field is a special field. Every model has one. Put it up front and pretend it's part of the
192
Scott Baker1f7791d2018-10-04 13:21:20 -0700193 if not fields:
Zack Williams045b63d2019-01-22 16:30:57 -0700194 raise Exception(
195 "Model %s has no fields. Check for missing base class." % m["name"]
196 )
Scott Baker1f7791d2018-10-04 13:21:20 -0700197
Zack Williams045b63d2019-01-22 16:30:57 -0700198 id_field = {
199 "type": "int32",
200 "name": "id",
201 "options": {},
202 "id": "1",
203 "accessor": fields[0]["accessor"],
204 }
Scott Bakerc237f882018-09-28 14:12:47 -0700205
206 fields = [id_field] + fields
207
208 # Walk through the list of fields. They will be in depth-first search order from the base model forward. Each time
209 # the model changes, offset the protobuf field numbers by 100.
210 offset = 0
211 last_accessor = fields[0]["accessor"]
212 for field in fields:
Zack Williams045b63d2019-01-22 16:30:57 -0700213 if field["accessor"] != last_accessor:
Scott Bakerc237f882018-09-28 14:12:47 -0700214 last_accessor = field["accessor"]
215 offset += 100
216 field_id = int(field["id"])
217 if (field_id < 1) or (field_id >= 100):
Zack Williams045b63d2019-01-22 16:30:57 -0700218 raise Exception(
219 "Only field numbers from 1 to 99 are permitted, field %s in model %s"
220 % (field["name"], field["accessor"])
221 )
Scott Bakerc237f882018-09-28 14:12:47 -0700222 field["id"] = int(field["id"]) + offset
223
224 # Check for duplicates
225 fields_by_number = {}
226 for field in fields:
227 id = field["id"]
228 dup = fields_by_number.get(id)
229 if dup:
Zack Williams045b63d2019-01-22 16:30:57 -0700230 raise Exception(
231 "Field %s has duplicate number %d with field %s in model %s"
232 % (field["name"], id, dup["name"], field["accessor"])
233 )
Scott Bakerc237f882018-09-28 14:12:47 -0700234 fields_by_number[id] = field
235
236 return fields
237
Zack Williams045b63d2019-01-22 16:30:57 -0700238
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200239def xproto_base_rlinks(m, table):
240 links = []
241
Zack Williams045b63d2019-01-22 16:30:57 -0700242 for base in m["bases"]:
243 b = base["name"]
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200244 if b in table:
245 base_rlinks = xproto_base_rlinks(table[b], table)
246
Zack Williams045b63d2019-01-22 16:30:57 -0700247 model_rlinks = [x.copy() for x in table[b]["rlinks"]]
Scott Bakerd87c02a2018-10-29 16:24:29 -0700248 for link in model_rlinks:
249 link["accessor"] = b
250
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200251 links.extend(base_rlinks)
252 links.extend(model_rlinks)
253
254 return links
255
Zack Williams045b63d2019-01-22 16:30:57 -0700256
Scott Bakerc237f882018-09-28 14:12:47 -0700257def xproto_rlinks(m, table):
258 """ Return the reverse links for the xproto message `m`.
259
Scott Bakerd87c02a2018-10-29 16:24:29 -0700260 If the link includes a reverse_id, then it will be used for the protobuf field id. Each level of inheritance
261 will add an offset of 100 to the supplied reverse_id.
262
263 If there is no reverse_id, then one will automatically be allocated started at id 1900. It is encouraged that
264 all links include reverse_ids, so that field identifiers are deterministic across all protobuf messages.
Scott Bakerc237f882018-09-28 14:12:47 -0700265 """
266
Scott Bakerd87c02a2018-10-29 16:24:29 -0700267 model_rlinks = [x.copy() for x in m["rlinks"]]
268 for link in model_rlinks:
269 link["accessor"] = m["fqn"]
270
271 links = xproto_base_rlinks(m, table) + model_rlinks
Scott Bakerc237f882018-09-28 14:12:47 -0700272
Zack Williams045b63d2019-01-22 16:30:57 -0700273 links = [
274 x for x in links if ("+" not in x["src_port"]) and ("+" not in x["dst_port"])
275 ]
Scott Bakerc237f882018-09-28 14:12:47 -0700276
Scott Bakerd87c02a2018-10-29 16:24:29 -0700277 if links:
278 last_accessor = links[0]["accessor"]
279 offset = 0
280 index = 1900
281 for link in links:
Zack Williams045b63d2019-01-22 16:30:57 -0700282 if link["accessor"] != last_accessor:
Scott Bakerd87c02a2018-10-29 16:24:29 -0700283 last_accessor = link["accessor"]
284 offset += 100
Scott Bakerc237f882018-09-28 14:12:47 -0700285
Scott Bakerd87c02a2018-10-29 16:24:29 -0700286 if link["reverse_id"]:
287 # Statically numbered reverse links. Use the id that the developer supplied, adding the offset based on
288 # inheritance depth.
289 link["id"] = int(link["reverse_id"]) + offset
290 else:
291 # Automatically numbered reverse links. These will eventually go away.
292 link["id"] = index
293 index += 1
294
295 # check for duplicates
Zack Williams045b63d2019-01-22 16:30:57 -0700296 links_by_number = {}
Scott Bakerd87c02a2018-10-29 16:24:29 -0700297 for link in links:
298 id = link["id"]
Zack Williams045b63d2019-01-22 16:30:57 -0700299 dup = links_by_number.get(id)
Scott Bakerd87c02a2018-10-29 16:24:29 -0700300 if dup:
Zack Williams045b63d2019-01-22 16:30:57 -0700301 raise Exception(
302 "Field %s has duplicate number %d in model %s with reverse field %s"
303 % (link["src_port"], id, m["name"], dup["src_port"])
304 )
Scott Bakerd87c02a2018-10-29 16:24:29 -0700305 links_by_number[id] = link
Scott Bakerc237f882018-09-28 14:12:47 -0700306
307 return links
308
309
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200310def xproto_base_links(m, table):
311 links = []
312
Zack Williams045b63d2019-01-22 16:30:57 -0700313 for base in m["bases"]:
314 b = base["name"]
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200315 if b in table:
316 base_links = xproto_base_links(table[b], table)
317
Zack Williams045b63d2019-01-22 16:30:57 -0700318 model_links = table[b]["links"]
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200319 links.extend(base_links)
320 links.extend(model_links)
321 return links
322
Zack Williams045b63d2019-01-22 16:30:57 -0700323
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200324def xproto_string_type(xptags):
Scott Baker08d10402019-04-08 16:19:59 -0700325 if "text" not in xptags:
326 # String fields have a mandatory maximum length.
327 # They are intended for relatively short strings.
Zack Williams045b63d2019-01-22 16:30:57 -0700328 return "string"
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200329 else:
Scott Baker08d10402019-04-08 16:19:59 -0700330 # Text fields have an optional maximuim length.
331 # They are intended for long, potentially multiline strings.
Zack Williams045b63d2019-01-22 16:30:57 -0700332 return "text"
333
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200334
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700335def xproto_tuplify(nested_list_or_set):
Zack Williams045b63d2019-01-22 16:30:57 -0700336 if not isinstance(nested_list_or_set, list) and not isinstance(
337 nested_list_or_set, set
338 ):
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700339 return nested_list_or_set
340 else:
341 return tuple([xproto_tuplify(i) for i in nested_list_or_set])
342
Zack Williams045b63d2019-01-22 16:30:57 -0700343
344def xproto_field_graph_components(fields, model, tag="unique_with"):
Zack Williams9a42f872019-02-15 17:56:04 -0700345 """
346 NOTE: Don't use set theory operators if you want repeatable tests - many
347 of them have non-deterministic behavior
348 """
349
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700350 def find_components(graph):
Zack Williams9a42f872019-02-15 17:56:04 -0700351
352 # 'graph' dict structure:
353 # - keys are strings
354 # - values are sets containing strings that are names of other keys in 'graph'
355
356 # take keys from 'graph' dict and put in 'pending' set
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700357 pending = set(graph.keys())
Zack Williams9a42f872019-02-15 17:56:04 -0700358
359 # create an empty list named 'components'
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700360 components = []
361
Zack Williams9a42f872019-02-15 17:56:04 -0700362 # loop while 'pending' is true - while there are still items in the 'pending' set
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700363 while pending:
Zack Williams9a42f872019-02-15 17:56:04 -0700364
365 # remove a random item from pending set, and put in 'front'
366 # this is the primary source of nondeterminism
Zack Williams045b63d2019-01-22 16:30:57 -0700367 front = {pending.pop()}
Zack Williams9a42f872019-02-15 17:56:04 -0700368
369 # create an empty set named 'component'
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700370 component = set()
371
Zack Williams9a42f872019-02-15 17:56:04 -0700372 # loop while 'front' is true. Front is modified below
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700373 while front:
Zack Williams9a42f872019-02-15 17:56:04 -0700374
375 # take the (only?) item out of the 'front' dict, and put in 'node'
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700376 node = front.pop()
Zack Williams9a42f872019-02-15 17:56:04 -0700377
378 # from 'graph' dict take set with key of 'node' and put into 'neighbors'
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700379 neighbours = graph[node]
Zack Williams9a42f872019-02-15 17:56:04 -0700380
381 # remove the set of items in components from neighbors
Matteo Scandoloa17e6e42018-05-25 10:28:25 -0700382 neighbours -= component # These we have already visited
Zack Williams9a42f872019-02-15 17:56:04 -0700383
384 # add all remaining neighbors to front
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700385 front |= neighbours
386
Zack Williams9a42f872019-02-15 17:56:04 -0700387 # remove neighbors from pending
Matteo Scandoloa17e6e42018-05-25 10:28:25 -0700388 pending -= neighbours
Zack Williams9a42f872019-02-15 17:56:04 -0700389
390 # add neighbors to component
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700391 component |= neighbours
Zack Williams045b63d2019-01-22 16:30:57 -0700392
Zack Williams9a42f872019-02-15 17:56:04 -0700393 # append component set to components list, sorted
394 components.append(sorted(component))
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700395
Zack Williams9a42f872019-02-15 17:56:04 -0700396 # return 'components', which is a list of sets
397 return sorted(components)
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700398
399 field_graph = {}
Zack Williams045b63d2019-01-22 16:30:57 -0700400 field_names = {f["name"] for f in fields}
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700401
402 for f in fields:
403 try:
Zack Williams045b63d2019-01-22 16:30:57 -0700404 tagged_str = unquote(f["options"][tag])
405 tagged_fields = tagged_str.split(",")
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700406
407 for uf in tagged_fields:
408 if uf not in field_names:
Zack Williams045b63d2019-01-22 16:30:57 -0700409 raise FieldNotFound(
410 'Field "%s" not found in model "%s", referenced from field "%s" by option "%s"'
411 % (uf, model["name"], f["name"], tag)
412 )
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700413
Zack Williams045b63d2019-01-22 16:30:57 -0700414 field_graph.setdefault(f["name"], set()).add(uf)
415 field_graph.setdefault(uf, set()).add(f["name"])
Zack Williams9a42f872019-02-15 17:56:04 -0700416
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700417 except KeyError:
418 pass
419
Matteo Scandoloa17e6e42018-05-25 10:28:25 -0700420 return find_components(field_graph)
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700421
Zack Williams045b63d2019-01-22 16:30:57 -0700422
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200423def xproto_api_opts(field):
424 options = []
Zack Williams045b63d2019-01-22 16:30:57 -0700425 if "max_length" in field["options"] and field["type"] == "string":
426 options.append("(val).maxLength = %s" % field["options"]["max_length"])
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700427
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200428 try:
Zack Williams045b63d2019-01-22 16:30:57 -0700429 if field["options"]["null"] == "False":
430 options.append("(val).nonNull = true")
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200431 except KeyError:
432 pass
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700433
Zack Williams045b63d2019-01-22 16:30:57 -0700434 if "link" in field and "model" in field["options"]:
435 options.append('(foreignKey).modelName = "%s"' % field["options"]["model"])
Scott Bakerc4156c32017-12-08 10:58:21 -0800436 if ("options" in field) and ("port" in field["options"]):
Zack Williams045b63d2019-01-22 16:30:57 -0700437 options.append(
438 '(foreignKey).reverseFieldName = "%s"' % field["options"]["port"]
439 )
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700440
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200441 if options:
Zack Williams045b63d2019-01-22 16:30:57 -0700442 options_str = "[" + ", ".join(options) + "]"
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200443 else:
Zack Williams045b63d2019-01-22 16:30:57 -0700444 options_str = ""
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200445
446 return options_str
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700447
Zack Williams045b63d2019-01-22 16:30:57 -0700448
Matteo Scandolo431781c2017-09-06 15:33:07 -0700449def xproto_type_to_swagger_type(f):
450 try:
Zack Williams045b63d2019-01-22 16:30:57 -0700451 content_type = f["options"]["content_type"]
Matteo Scandolo431781c2017-09-06 15:33:07 -0700452 content_type = eval(content_type)
Zack Williams045b63d2019-01-22 16:30:57 -0700453 except BaseException:
Matteo Scandolo431781c2017-09-06 15:33:07 -0700454 content_type = None
455 pass
456
Zack Williams045b63d2019-01-22 16:30:57 -0700457 if "choices" in f["options"]:
458 return "string"
459 elif content_type == "date":
460 return "string"
461 elif f["type"] == "bool":
462 return "boolean"
463 elif f["type"] == "string":
464 return "string"
465 elif f["type"] in ["int", "uint32", "int32"] or "link" in f:
466 return "integer"
467 elif f["type"] in ["double", "float"]:
468 return "string"
469
Matteo Scandolo431781c2017-09-06 15:33:07 -0700470
471def xproto_field_to_swagger_enum(f):
Zack Williams045b63d2019-01-22 16:30:57 -0700472 if "choices" in f["options"]:
Zack Williams9a42f872019-02-15 17:56:04 -0700473 c_list = []
Matteo Scandolo431781c2017-09-06 15:33:07 -0700474
Zack Williams045b63d2019-01-22 16:30:57 -0700475 for c in eval(xproto_unquote(f["options"]["choices"])):
Zack Williams9a42f872019-02-15 17:56:04 -0700476 c_list.append(c[0])
Matteo Scandolo431781c2017-09-06 15:33:07 -0700477
Zack Williams9a42f872019-02-15 17:56:04 -0700478 return sorted(c_list)
Matteo Scandolo431781c2017-09-06 15:33:07 -0700479 else:
Sapan Bhatiabfb233a2018-02-09 14:53:09 -0800480 return False
Scott Bakera33ccb02018-01-26 13:03:28 -0800481
Zack Williams045b63d2019-01-22 16:30:57 -0700482
Scott Bakera33ccb02018-01-26 13:03:28 -0800483def xproto_is_true(x):
484 # TODO: Audit xproto and make specification of trueness more uniform
Zack Williams045b63d2019-01-22 16:30:57 -0700485 if x is True or (x == "True") or (x == '"True"'):
Scott Bakera33ccb02018-01-26 13:03:28 -0800486 return True
487 return False
Scott Bakerbe2a5172019-04-10 18:02:50 -0700488
489
490def xproto_list_evaluates_true(lst):
491 # Returns True if the first non-empty item in the list is interpreted
492 # as True.
493 x = xproto_first_non_empty(lst)
494 return xproto_is_true(x)