Skip to content

Commit

Permalink
Introduce new data types uint32 and uint64 (#413)
Browse files Browse the repository at this point in the history
* Introduce new data types uint32 and uint64

* Code modifications as per the PR.

* Refactoring the code.

* fixed min-max generation for values

* revert items fixes

* v0.2.15

---------

Co-authored-by: Ashutosh Kumar <[email protected]>
  • Loading branch information
dipendughosh and ashutshkumr authored Mar 24, 2023
1 parent eb10931 commit 5cd21ec
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 32 deletions.
9 changes: 9 additions & 0 deletions openapiart/bundler.py
Original file line number Diff line number Diff line change
Expand Up @@ -871,8 +871,17 @@ def _apply_common_x_field_pattern_properties(
elif property_name == "values":
schema["default"] = [schema["default"]]
if format is not None:
# TODO: fix this
# if property_name == "values":
# schema["items"]["format"] = format
# else:
schema["format"] = format
if "length" in xpattern:
# TODO: fix this
# if property_name == "values":
# schema["items"]["minimum"] = 0
# schema["items"]["maximum"] = 2 ** int(xpattern["length"]) - 1
# else:
schema["minimum"] = 0
schema["maximum"] = 2 ** int(xpattern["length"]) - 1

Expand Down
29 changes: 23 additions & 6 deletions openapiart/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,10 +403,12 @@ def validate_hex(self, hex):
except Exception:
return False

def validate_integer(self, value, min, max):
def validate_integer(self, value, min, max, type_format=None):
if value is None or not isinstance(value, int):
return False
if value < 0:
if type_format == "uint32" and value < 0:
return False
if type_format == "uint64" and value < 0:
return False
if min is not None and value < min:
return False
Expand Down Expand Up @@ -476,8 +478,11 @@ def types_validation(
list: "list",
"int64": "integer",
"int32": "integer",
"uint64": "integer",
"uint32": "integer",
"double": "float",
}
type_format = type_
if type_ in type_map:
type_ = type_map[type_]
if itemtype is not None and itemtype in type_map:
Expand All @@ -500,7 +505,7 @@ def types_validation(
)
verdict = False
elif type_ == "integer":
verdict = v_obj(value, min, max)
verdict = v_obj(value, min, max, type_format)
if verdict is True:
return
min_max = ""
Expand Down Expand Up @@ -724,9 +729,16 @@ def _encode(self):
if isinstance(value, OpenApiObject):
self._raise_status_warnings(key, value)
elif value is not None:
if self._TYPES.get(key, {}).get("format", "") == "int64":
if (
self._TYPES.get(key, {}).get("format", "") == "int64"
or self._TYPES.get(key, {}).get("format", "") == "uint64"
):
value = str(value)
elif self._TYPES.get(key, {}).get("itemformat", "") == "int64":
elif (
self._TYPES.get(key, {}).get("itemformat", "") == "int64"
or self._TYPES.get(key, {}).get("itemformat", "")
== "uint64"
):
value = [str(v) for v in value]
output[key] = value
self._raise_status_warnings(key, value)
Expand Down Expand Up @@ -772,10 +784,15 @@ def _decode(self, obj):
property_value = self._DEFAULTS[property_name]
self._set_choice(property_name)
# convert int64(will be string on wire) to to int
if self._TYPES[property_name].get("format", "") == "int64":
if (
self._TYPES[property_name].get("format", "") == "int64"
or self._TYPES[property_name].get("format", "") == "uint64"
):
property_value = int(property_value)
elif (
self._TYPES[property_name].get("itemformat", "") == "int64"
or self._TYPES[property_name].get("itemformat", "")
== "uint64"
):
property_value = [int(v) for v in property_value]
self._properties[property_name] = property_value
Expand Down
14 changes: 11 additions & 3 deletions openapiart/openapiartgo.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ def __init__(self, **kwargs):
"boolean": "bool",
"integer": "int32",
"int64": "int64",
"uint32": "uint32",
"uint64": "uint64",
"number": "float32",
"numberfloat": "float32",
"numberdouble": "float64",
Expand Down Expand Up @@ -2067,6 +2069,12 @@ def _build_setters_getters(self, fluent_new):
and "int" in field.type
):
field.type = field.type.replace("32", "64")
if (
(field.min is not None and field.min > 4294967295)
or (field.max is not None and field.max > 4294967295)
and "uint" in field.type
):
field.type = field.type.replace("32", "64")
if field.hasminmaxlength:
field.min_length = (
None
Expand Down Expand Up @@ -2863,10 +2871,10 @@ def _get_struct_field_type(self, property_schema, fluent_field=None):
"format"
]
if "format" in property_schema:
format_type = (oapi_type + property_schema["format"]).lower()
if format_type.lower() in self._oapi_go_types:
type_format = (oapi_type + property_schema["format"]).lower()
if type_format.lower() in self._oapi_go_types:
go_type = "{oapi_go_type}".format(
oapi_go_type=self._oapi_go_types[format_type.lower()]
oapi_go_type=self._oapi_go_types[type_format.lower()]
)
elif property_schema["format"].lower() in self._oapi_go_types:
go_type = "{oapi_go_type}".format(
Expand Down
137 changes: 115 additions & 22 deletions openapiart/openapiartprotobuf.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,14 @@ def _write_header(self, info_object):
self._write('import "google/protobuf/descriptor.proto";')
self._write('import "google/protobuf/empty.proto";')

def _get_field_type(self, property_name, openapi_object):
def _get_field_type(
self,
property_name,
openapi_object,
outer_format=None,
outer_min=None,
outer_max=None,
):
"""Convert openapi type -> protobuf type
- type:number -> float
Expand All @@ -218,6 +225,8 @@ def _get_field_type(self, property_name, openapi_object):
- type:integer -> int32
- type:integer [format:int32] -> int32
- type:integer [format:int64] -> int64
- type:integer [format:uint32] -> uint32
- type:integer [format:uint64] -> uint64
- type:boolean -> bool
- type:string -> string
- type:string [format:binary] -> bytes
Expand All @@ -228,7 +237,10 @@ def _get_field_type(self, property_name, openapi_object):
return "bool"
if type == "string":
if "format" in openapi_object:
if openapi_object["format"] == "binary":
if (
str(outer_format) == "binary"
or openapi_object["format"] == "binary"
):
return "bytes"
elif "x-enum" in openapi_object:
enum_msg = self._camelcase("{}".format(property_name))
Expand All @@ -241,36 +253,45 @@ def _get_field_type(self, property_name, openapi_object):
return enum_msg + ".Enum"
return "string"
if type == "integer":
format = openapi_object.get("format")
type_format = openapi_object.get("format")
min = openapi_object.get("minimum")
max = openapi_object.get("maximum")
if (min is not None and min > 2147483647) or (
max is not None and max > 2147483647
):
return "int64"
if format is not None and "int64" in format:
return "int64"
return "int32"
if outer_min is not None:
min = outer_min
if outer_max is not None:
max = outer_max
if outer_format is not None:
type_format = outer_format
return _get_integer_format(type_format, min, max)
if type == "number":
if "format" in openapi_object:
if openapi_object["format"] == "double":
if (
str(outer_format) == "double"
or openapi_object["format"] == "double"
):
return "double"
elif openapi_object["format"] == "float":
elif (
str(outer_format) == "float"
or openapi_object["format"] == "float"
):
return "float"
return "float"
if type == "array":
item_type = self._get_field_type(
property_name, openapi_object["items"]
)
format = openapi_object.get("format")
# TODO: outer min/max shouldn't exist for arrays
type_format = openapi_object.get("format")
min = openapi_object.get("minimum")
max = openapi_object.get("maximum")
if (min is not None and min > 2147483647) or (
max is not None and max > 2147483647
):
item_type = item_type.replace("32", "64")
if format is not None and "int64" in format:
item_type = item_type.replace("32", "64")

item_type = self._get_field_type(
property_name,
openapi_object["items"],
outer_format=type_format,
outer_min=min,
outer_max=max,
)

if item_type == "integer":
item_type = _get_integer_format(type_format, min, max)
return "repeated " + item_type
elif "$ref" in openapi_object:
return openapi_object["$ref"].split("/")[-1].replace(".", "")
Expand Down Expand Up @@ -425,3 +446,75 @@ def _write_rpc(self, url, method, path_item_object):
operation.rpc, operation.request, "", operation.response
)
self._write(line, indent=1)


def _min_max_in_range(format, min, max):
if min is None or max is None:
return True
elif min is None or max is None:
if min is None:
min = max
if max is None:
max = min

if min > max:
return False

if format == "uint32":
return min >= 0 and max <= 4294967295
elif format == "uint64":
return min >= 0 and max <= 18446744073709551615
elif format == "int32":
return min >= -2147483648 and max <= 2147483647
elif format == "int64":
return min >= -9223372036854775808 and max <= 9223372036854775807
else:
raise Exception("unsupported integer format: {}".format(format))


def _format_from_range(min, max):
if min is None and max is None:
return "int32"
elif min is None or max is None:
if min is None:
min = max
if max is None:
max = min

if max < min:
raise Exception("min %d cannot be less than max %d", min, max)

# TODO: this logic needs to be replaced with the one commented out below
if min > 2147483647 or max > 2147483647:
return "int64"
else:
return "int32"
# TODO: following snippet is more accurate but introduces breaking
# changes and hence commented out
# if self._min_max_in_range("uint32", min, max):
# return "uint32"
# if self._min_max_in_range("uint64", min, max):
# return "uint64"
# if self._min_max_in_range("int32", min, max):
# return "int32"
# if self._min_max_in_range("int64", min, max):
# return "int64"


def _get_integer_format(type_format, min, max):
valid_formats = ["int32", "int64", "uint32", "uint64"]
if type_format is not None:
if type_format in valid_formats:
if not _min_max_in_range(type_format, min, max):
raise Exception(
"format {} is not compatible with [min,max] [{},{}]".format(
type_format, min, max
)
)
return type_format
raise Exception(
"unsupported format %s, supported formats are: %s",
type_format,
valid_formats,
)
return _format_from_range(min, max)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import setuptools

pkg_name = "openapiart"
version = "0.2.14"
version = "0.2.15"

base_dir = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(base_dir, "README.md")) as fid:
Expand Down

0 comments on commit 5cd21ec

Please sign in to comment.