Skip to content

Commit

Permalink
Added naive support for formData (#68)
Browse files Browse the repository at this point in the history
Based on the operation info the content type is determined. If it is
FORM_URLENCODED, it is handled differetly, because of the specification
requirements.

The support is naive because a definition is created (as in the old
behavior), but rather pointing to a reference to that definition.
I am shallowly recursing through it and mapping it to a formData
dictionary.

A better solution is to extend the visitor pattern when creating
definitions and a support for FORM_URLENCODED, rather than creating
an unused definition. But, since this is a large effort, the naive
solution seems suitable.

Signed-off-by: Anton Obretenov <[email protected]>

Co-authored-by: Anton Obretenov <[email protected]>
  • Loading branch information
ToniO and Anton Obretenov authored Nov 3, 2020
1 parent f06cf93 commit aa0f4f2
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 20 deletions.
9 changes: 8 additions & 1 deletion lib/api_endpoint/api_metamodel2spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def handle_request_mapping(
service_name,
operation_name,
params_metadata,
content_type,
type_dict,
structure_svc,
enum_svc,
Expand All @@ -38,6 +39,7 @@ def handle_request_mapping(
service_name,
operation_name,
params_metadata,
content_type,
type_dict,
structure_svc,
enum_svc,
Expand Down Expand Up @@ -68,6 +70,7 @@ def process_put_post_patch_request(
service_name,
operation_name,
params,
content_type,
type_dict,
structure_svc,
enum_svc,
Expand All @@ -94,13 +97,17 @@ def process_put_post_patch_request(
parx = spec.wrap_body_params(
service_name,
operation_name,
content_type,
body_param_list,
type_dict,
structure_svc,
enum_svc,
show_unreleased_apis)
if parx is not None:
par_array.append(parx)
if isinstance(parx, list):
par_array.extend(parx)
else:
par_array.append(parx)

# Query
query_param_list, other_param_list = utils.extract_query_parameters(
Expand Down
3 changes: 3 additions & 0 deletions lib/api_endpoint/oas3/api_openapi_parameter_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def wrap_body_params(
self,
service_name,
operation_name,
content_type,
body_param_list,
type_dict,
structure_svc,
Expand All @@ -75,6 +76,8 @@ def wrap_body_params(
}
"""
# todo:
# form-url-encoded support; not utilized content_type
# todo:
# not unique enough. make it unique
wrapper_name = utils.get_str_camel_case(service_name + '_' + operation_name, *utils.CAMELCASE_SEPARATOR_LIST)
body_obj = {}
Expand Down
11 changes: 9 additions & 2 deletions lib/api_endpoint/swagger2/api_metamodel2swagger.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@ def get_path(
show_unreleased_apis):
documentation = operation_info.documentation
op_metadata = operation_info.metadata
method_info = op_metadata[http_method]
content_type = method_info.elements["consumes"].string_value if "consumes" in method_info.elements else None

params = operation_info.params
errors = operation_info.errors
output = operation_info.output
http_method = http_method.lower()
par_array, url = self.handle_request_mapping(url, http_method, service_name,
operation_id, params, type_dict,
operation_id, params, content_type, type_dict,
structure_dict, enum_dict, show_unreleased_apis, api_swagg_ph)
response_map = api_swagg_rh.populate_response_map(
output,
Expand All @@ -44,14 +47,18 @@ def get_path(
op_metadata,
show_unreleased_apis)

consumes = None
if content_type == 'FORM_URLENCODED':
consumes = ["application/x-www-form-urlencoded"]
path_obj = utils.build_path(
service_name,
http_method,
url,
documentation,
par_array,
operation_id=operation_id,
responses=response_map)
responses=response_map,
consumes=consumes)
self.post_process_path(path_obj)
path = utils.add_basic_auth(path_obj)
return path
Expand Down
20 changes: 20 additions & 0 deletions lib/api_endpoint/swagger2/api_swagger_parameter_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def wrap_body_params(
self,
service_name,
operation_name,
content_type,
body_param_list,
type_dict,
structure_svc,
Expand Down Expand Up @@ -105,10 +106,29 @@ def wrap_body_params(

type_dict[wrapper_name] = body_obj

if content_type == 'FORM_URLENCODED':
return self.wrap_form_data_params(type_dict, wrapper_name)

schema_obj = {'$ref': ref_path + wrapper_name}
parameter_obj['schema'] = schema_obj
return parameter_obj

def wrap_form_data_params(self, type_dict, wrapper_name):
parameter_list = []
definition = type_dict[wrapper_name]
if "properties" in definition:
for property_name, property_value in definition["properties"].items():
formDataEntry = {"in": "formData",
"name": property_name}
formDataEntry.update({k: v for k, v in property_value.items() if k in ['type', 'description']})
if "required" in definition and property_name in definition["required"]:
formDataEntry["required"] = "true";
parameter_list.append(formDataEntry)
elif "$ref" in definition:
reference = definition["$ref"].replace("#/definitions/", "")
return self.wrap_form_data_params(type_dict, reference)
return parameter_list

def flatten_query_param_spec(
self,
query_param_info,
Expand Down
18 changes: 12 additions & 6 deletions test_api_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,12 @@ def test_process_put_post_patch_request(self):
]
'''
spec = ApiSwaggerParaHandler()
par_array_actual, new_url_actual = self.api_meta2spec.process_put_post_patch_request(self.url, 'com.vmware.package.mock',
'mock_operation_name', params,
self.type_dict, {}, {}, False, spec)
par_array_actual, new_url_actual = self.api_meta2spec.process_put_post_patch_request(self.url,
'com.vmware.package.mock',
'mock_operation_name',
params, None,
self.type_dict, {}, {},
False, spec)
par_array_expected = [{
'required': True,
'in': 'path',
Expand Down Expand Up @@ -288,9 +291,12 @@ def test_process_put_post_patch_request(self):
# case 2: create parameter array using field information of parameters for
# put, post, patch operations in openAPI 3.0
spec = ApiOpenapiParaHandler()
par_array_actual, new_url_actual = self.api_meta2spec.process_put_post_patch_request(self.url, 'com.vmware.package.mock',
'mock_operation_name', params,
self.type_dict, {}, {}, False, spec)
par_array_actual, new_url_actual = self.api_meta2spec.process_put_post_patch_request(self.url,
'com.vmware.package.mock',
'mock_operation_name',
params, None,
self.type_dict, {}, {},
False, spec)
par_array_expected = [{
'required': True,
'in': 'path',
Expand Down
8 changes: 5 additions & 3 deletions test_api_oas.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ def test_wrap_body_params(self):
'ComVmwarePackageMock-1' : {}
}
body_param_list = [self.field_info_mock]
parameter_obj_actual = self.api_openapi_parahandler.wrap_body_params('com.vmware.package.mock-1', 'mockOperationName',
body_param_list, type_dict, self.structure_dict,
self.enum_dict, False)
parameter_obj_actual = self.api_openapi_parahandler.wrap_body_params('com.vmware.package.mock-1',
'mockOperationName', None,
body_param_list, type_dict,
self.structure_dict,
self.enum_dict, False)
parameter_obj_expected = {'$ref': '#/components/requestBodies/ComVmwarePackageMock-1MockOperationName'}
self.assertEqual(parameter_obj_expected, parameter_obj_actual)
type_dict_expected = {
Expand Down
49 changes: 41 additions & 8 deletions test_api_swagger.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,17 @@ def test_convert_field_info_to_swagger_parameter(self):
self.assertEqual(parameter_obj_expected, parameter_obj_actual)

def test_wrap_body_params(self):
# validate parameter object by creating json wrappers around body object
# validate parameter object by creating json wrappers around body object
type_dict = {
'com.vmware.package.mock': {},
'ComVmwarePackageMock': {}
}
body_param_list = [self.field_info_mock]
parameter_obj_actual = self.api_swagger_parahandler.wrap_body_params('com.vmware.package.mock', 'mockOperationName',
body_param_list, type_dict, self.structure_dict,
self.enum_dict, False)
parameter_obj_actual = self.api_swagger_parahandler.wrap_body_params('com.vmware.package.mock',
'mockOperationName', None,
body_param_list, type_dict,
self.structure_dict,
self.enum_dict, False)
parameter_obj_expected = {
'in': 'body',
'name': 'request_body',
Expand All @@ -80,17 +82,48 @@ def test_wrap_body_params(self):
metadata_mock.elements = {"name": element_value_mock}

self.field_info_mock.metadata = {"BodyField": metadata_mock}
parameter_obj_actual = self.api_swagger_parahandler.wrap_body_params('com.vmware.package.mock', 'mockOperationName',
body_param_list, type_dict, self.structure_dict,
self.enum_dict, False)
parameter_obj_actual = self.api_swagger_parahandler.wrap_body_params('com.vmware.package.mock',
'mockOperationName', None,
body_param_list, type_dict,
self.structure_dict,
self.enum_dict, False)
parameter_obj_expected = {
'in': 'body',
'name': 'request_body',
'required': True,
'schema': {'$ref': '#/definitions/ComVmwarePackageMockMockOperationName'}
}
self.assertEqual(parameter_obj_expected, parameter_obj_actual)


def test_wrap_form_data_params(self):
# validate parameter object by creating json wrappers around body object
type_dict = {
'ComVmwarePackageMockWrapper': {
"$ref": "#/definitions/ComVmwarePackageMock"
},
'ComVmwarePackageMock': {
"type": "string",
"properties": {
"param_name": {
"description": "my test name",
"type": "string"
}},
"required": [
"param_name"
]
}}
parameter_obj_actual = self.api_swagger_parahandler.wrap_form_data_params(type_dict,
"ComVmwarePackageMockWrapper")
parameter_obj_expected = [{
'in': 'formData',
'name': 'param_name',
"description": "my test name",
"type": "string",
"required": "true"
}]

self.assertEqual(parameter_obj_expected, parameter_obj_actual)

def test_flatten_query_param_spec(self):
# case 1: parameter object takes reference from type_dict key
# case 1.1: type dict reference value contains properties
Expand Down

0 comments on commit aa0f4f2

Please sign in to comment.