diff --git a/lib/codegen/python/http_client.dart b/lib/codegen/python/http_client.dart index 6913314a..05fb8253 100644 --- a/lib/codegen/python/http_client.dart +++ b/lib/codegen/python/http_client.dart @@ -1,8 +1,7 @@ import 'dart:io'; import 'dart:convert'; import 'package:jinja/jinja.dart' as jj; -import 'package:apidash/utils/utils.dart' - show getValidRequestUri, padMultilineString; +import 'package:apidash/utils/utils.dart' show getValidRequestUri; import 'package:apidash/models/models.dart' show RequestModel; import 'package:apidash/consts.dart'; @@ -20,7 +19,6 @@ queryParams = {{params}} queryParamsStr = '?' + urlencode(queryParams) """; - int kParamsPadding = 14; String kTemplateBody = """ @@ -36,8 +34,6 @@ headers = {{headers}} String kTemplateFormHeaderContentType = ''' multipart/form-data; boundary={{boundary}}'''; - int kHeadersPadding = 10; - String kTemplateConnection = """ conn = http.client.HTTP{{isHttps}}Connection("{{authority}}")"""; @@ -116,43 +112,38 @@ body = b'\r\n'.join(dataList) hasQuery = true; var templateParams = jj.Template(kTemplateParams); var paramsString = kEncoder.convert(params); - paramsString = padMultilineString(paramsString, kParamsPadding); result += templateParams.render({"params": paramsString}); } } - var method = requestModel.method; - var requestBody = requestModel.requestBody; - if (kMethodsWithBody.contains(method) && - requestBody != null && - !requestModel.hasFormData) { - var contentLength = utf8.encode(requestBody).length; - if (contentLength > 0) { - hasBody = true; + if (requestModel.hasData) { + hasBody = true; + if (requestModel.hasJsonData || requestModel.hasTextData) { var templateBody = jj.Template(kTemplateBody); - result += templateBody.render({"body": requestBody}); + result += templateBody.render({"body": requestModel.requestBody}); } } var headersList = requestModel.enabledRequestHeaders; if (headersList != null || hasBody) { var headers = requestModel.enabledHeadersMap; - if (requestModel.hasFormData) { - var formHeaderTemplate = - jj.Template(kTemplateFormHeaderContentType); - headers[HttpHeaders.contentTypeHeader] = formHeaderTemplate.render({ - "boundary": boundary, - }); - } if (headers.isNotEmpty || hasBody) { hasHeaders = true; if (hasBody && !requestModel.hasContentTypeHeader) { - headers[HttpHeaders.contentTypeHeader] = - requestModel.requestBodyContentType.header; + if (requestModel.hasJsonData || requestModel.hasTextData) { + headers[HttpHeaders.contentTypeHeader] = + requestModel.requestBodyContentType.header; + } else if (requestModel.hasFormData) { + var formHeaderTemplate = + jj.Template(kTemplateFormHeaderContentType); + headers[HttpHeaders.contentTypeHeader] = + formHeaderTemplate.render({ + "boundary": boundary, + }); + } } var headersString = kEncoder.convert(headers); - headersString = padMultilineString(headersString, kHeadersPadding); var templateHeaders = jj.Template(kTemplateHeaders); result += templateHeaders.render({"headers": headersString}); } @@ -174,7 +165,7 @@ body = b'\r\n'.join(dataList) var templateRequest = jj.Template(kTemplateRequest); result += templateRequest.render({ - "method": method.name.toUpperCase(), + "method": requestModel.method.name.toUpperCase(), "path": uri.path, "queryParamsStr": hasQuery ? " + queryParamsStr" : "", }); diff --git a/test/codegen/python_http_client_codegen_test.dart b/test/codegen/python_http_client_codegen_test.dart index 770870a5..c1d68702 100644 --- a/test/codegen/python_http_client_codegen_test.dart +++ b/test/codegen/python_http_client_codegen_test.dart @@ -29,8 +29,8 @@ print(data.decode("utf-8")) from urllib.parse import urlencode queryParams = { - "code": "US" - } + "code": "US" +} queryParamsStr = '?' + urlencode(queryParams) conn = http.client.HTTPSConnection("api.apidash.dev") @@ -52,8 +52,8 @@ print(data.decode("utf-8")) from urllib.parse import urlencode queryParams = { - "code": "IND" - } + "code": "IND" +} queryParamsStr = '?' + urlencode(queryParams) conn = http.client.HTTPSConnection("api.apidash.dev") @@ -75,12 +75,12 @@ print(data.decode("utf-8")) from urllib.parse import urlencode queryParams = { - "num": "8700000", - "digits": "3", - "system": "SS", - "add_space": "true", - "trailing_zeros": "true" - } + "num": "8700000", + "digits": "3", + "system": "SS", + "add_space": "true", + "trailing_zeros": "true" +} queryParamsStr = '?' + urlencode(queryParams) conn = http.client.HTTPSConnection("api.apidash.dev") @@ -101,8 +101,8 @@ print(data.decode("utf-8")) const expectedCode = r"""import http.client headers = { - "User-Agent": "Test Agent" - } + "User-Agent": "Test Agent" +} conn = http.client.HTTPSConnection("api.github.com") conn.request("GET", "/repos/foss42/apidash", @@ -124,13 +124,13 @@ print(data.decode("utf-8")) from urllib.parse import urlencode queryParams = { - "raw": "true" - } + "raw": "true" +} queryParamsStr = '?' + urlencode(queryParams) headers = { - "User-Agent": "Test Agent" - } + "User-Agent": "Test Agent" +} conn = http.client.HTTPSConnection("api.github.com") conn.request("GET", "/repos/foss42/apidash" + queryParamsStr, @@ -169,13 +169,13 @@ print(data.decode("utf-8")) from urllib.parse import urlencode queryParams = { - "raw": "true" - } + "raw": "true" +} queryParamsStr = '?' + urlencode(queryParams) headers = { - "User-Agent": "Test Agent" - } + "User-Agent": "Test Agent" +} conn = http.client.HTTPSConnection("api.github.com") conn.request("GET", "/repos/foss42/apidash" + queryParamsStr, @@ -197,9 +197,9 @@ print(data.decode("utf-8")) from urllib.parse import urlencode queryParams = { - "num": "8700000", - "add_space": "true" - } + "num": "8700000", + "add_space": "true" +} queryParamsStr = '?' + urlencode(queryParams) conn = http.client.HTTPSConnection("api.apidash.dev") @@ -220,8 +220,8 @@ print(data.decode("utf-8")) const expectedCode = r"""import http.client headers = { - "User-Agent": "Test Agent" - } + "User-Agent": "Test Agent" +} conn = http.client.HTTPSConnection("api.apidash.dev") conn.request("GET", "/humanize/social", @@ -246,14 +246,14 @@ print(data.decode("utf-8")) from urllib.parse import urlencode queryParams = { - "num": "8700000", - "digits": "3" - } + "num": "8700000", + "digits": "3" +} queryParamsStr = '?' + urlencode(queryParams) headers = { - "User-Agent": "Test Agent" - } + "User-Agent": "Test Agent" +} conn = http.client.HTTPSConnection("api.apidash.dev") conn.request("GET", "/humanize/social" + queryParamsStr, @@ -333,8 +333,8 @@ body = r'''{ }''' headers = { - "content-type": "text/plain" - } + "content-type": "text/plain" +} conn = http.client.HTTPSConnection("api.apidash.dev") conn.request("POST", "/case/lower", @@ -365,8 +365,8 @@ body = r'''{ }''' headers = { - "content-type": "application/json" - } + "content-type": "application/json" +} conn = http.client.HTTPSConnection("api.apidash.dev") conn.request("POST", "/case/lower", @@ -392,9 +392,9 @@ body = r'''{ }''' headers = { - "User-Agent": "Test Agent", - "content-type": "application/json" - } + "User-Agent": "Test Agent", + "content-type": "application/json" +} conn = http.client.HTTPSConnection("api.apidash.dev") conn.request("POST", "/case/lower", @@ -414,17 +414,38 @@ print(data.decode("utf-8")) test('POST 4', () { const expectedCode = r"""import http.client - -body = r'''{ -"text": "I LOVE Flutter" -}''' +import mimetypes +from codecs import encode headers = { - "content-type": "text/plain" - } + "content-type": "multipart/form-data; boundary=b9826c20-773c-1f0c-814d-a1b3d90cd6b3" +} +def build_data_list(fields): + dataList = [] + for field in fields: + name = field.get('name', '') + value = field.get('value', '') + type_ = field.get('type', 'text') + dataList.append(encode('--b9826c20-773c-1f0c-814d-a1b3d90cd6b3')) + if type_ == 'text': + dataList.append(encode(f'Content-Disposition: form-data; name="{name}"')) + dataList.append(encode('Content-Type: text/plain')) + dataList.append(encode('')) + dataList.append(encode(value)) + elif type_ == 'file': + dataList.append(encode(f'Content-Disposition: form-data; name="{name}"; filename="{value}"')) + dataList.append(encode(f'Content-Type: {mimetypes.guess_type(value)[0] or "application/octet-stream"}')) + dataList.append(encode('')) + dataList.append(open(value, 'rb').read()) + dataList.append(encode(f'--b9826c20-773c-1f0c-814d-a1b3d90cd6b3--')) + dataList.append(encode('')) + return dataList + +dataList = build_data_list([{"name":"text","value":"API","type":"text"},{"name":"sep","value":"|","type":"text"},{"name":"times","value":"3","type":"text"}]) +body = b'\r\n'.join(dataList) conn = http.client.HTTPSConnection("api.apidash.dev") -conn.request("POST", "/case/lower", +conn.request("POST", "/io/form", body= body, headers= headers) @@ -435,23 +456,46 @@ print(data.decode("utf-8")) """; expect( codeGen.getCode( - CodegenLanguage.pythonHttpClient, requestModelPost4, "https"), + CodegenLanguage.pythonHttpClient, requestModelPost4, "https", + boundary: "b9826c20-773c-1f0c-814d-a1b3d90cd6b3"), expectedCode); - }, skip: true); + }); test('POST 5', () { const expectedCode = r"""import http.client - -body = r'''{ -"text": "I LOVE Flutter" -}''' +import mimetypes +from codecs import encode headers = { - "content-type": "text/plain" - } + "User-Agent": "Test Agent", + "content-type": "multipart/form-data; boundary=929dc910-7714-1f0c-814d-a1b3d90cd6b3" +} +def build_data_list(fields): + dataList = [] + for field in fields: + name = field.get('name', '') + value = field.get('value', '') + type_ = field.get('type', 'text') + dataList.append(encode('--929dc910-7714-1f0c-814d-a1b3d90cd6b3')) + if type_ == 'text': + dataList.append(encode(f'Content-Disposition: form-data; name="{name}"')) + dataList.append(encode('Content-Type: text/plain')) + dataList.append(encode('')) + dataList.append(encode(value)) + elif type_ == 'file': + dataList.append(encode(f'Content-Disposition: form-data; name="{name}"; filename="{value}"')) + dataList.append(encode(f'Content-Type: {mimetypes.guess_type(value)[0] or "application/octet-stream"}')) + dataList.append(encode('')) + dataList.append(open(value, 'rb').read()) + dataList.append(encode(f'--929dc910-7714-1f0c-814d-a1b3d90cd6b3--')) + dataList.append(encode('')) + return dataList + +dataList = build_data_list([{"name":"text","value":"API","type":"text"},{"name":"sep","value":"|","type":"text"},{"name":"times","value":"3","type":"text"}]) +body = b'\r\n'.join(dataList) conn = http.client.HTTPSConnection("api.apidash.dev") -conn.request("POST", "/case/lower", +conn.request("POST", "/io/form", body= body, headers= headers) @@ -462,23 +506,45 @@ print(data.decode("utf-8")) """; expect( codeGen.getCode( - CodegenLanguage.pythonHttpClient, requestModelPost5, "https"), + CodegenLanguage.pythonHttpClient, requestModelPost5, "https", + boundary: "929dc910-7714-1f0c-814d-a1b3d90cd6b3"), expectedCode); - }, skip: true); + }); test('POST 6', () { const expectedCode = r"""import http.client - -body = r'''{ -"text": "I LOVE Flutter" -}''' +import mimetypes +from codecs import encode headers = { - "content-type": "text/plain" - } + "content-type": "multipart/form-data; boundary=9b1374c0-76e0-1f0c-814d-a1b3d90cd6b3" +} +def build_data_list(fields): + dataList = [] + for field in fields: + name = field.get('name', '') + value = field.get('value', '') + type_ = field.get('type', 'text') + dataList.append(encode('--9b1374c0-76e0-1f0c-814d-a1b3d90cd6b3')) + if type_ == 'text': + dataList.append(encode(f'Content-Disposition: form-data; name="{name}"')) + dataList.append(encode('Content-Type: text/plain')) + dataList.append(encode('')) + dataList.append(encode(value)) + elif type_ == 'file': + dataList.append(encode(f'Content-Disposition: form-data; name="{name}"; filename="{value}"')) + dataList.append(encode(f'Content-Type: {mimetypes.guess_type(value)[0] or "application/octet-stream"}')) + dataList.append(encode('')) + dataList.append(open(value, 'rb').read()) + dataList.append(encode(f'--9b1374c0-76e0-1f0c-814d-a1b3d90cd6b3--')) + dataList.append(encode('')) + return dataList + +dataList = build_data_list([{"name":"token","value":"xyz","type":"text"},{"name":"imfile","value":"/Documents/up/1.png","type":"file"}]) +body = b'\r\n'.join(dataList) conn = http.client.HTTPSConnection("api.apidash.dev") -conn.request("POST", "/case/lower", +conn.request("POST", "/io/img", body= body, headers= headers) @@ -489,23 +555,45 @@ print(data.decode("utf-8")) """; expect( codeGen.getCode( - CodegenLanguage.pythonHttpClient, requestModelPost6, "https"), + CodegenLanguage.pythonHttpClient, requestModelPost6, "https", + boundary: "9b1374c0-76e0-1f0c-814d-a1b3d90cd6b3"), expectedCode); - }, skip: true); + }); test('POST 7', () { const expectedCode = r"""import http.client - -body = r'''{ -"text": "I LOVE Flutter" -}''' +import mimetypes +from codecs import encode headers = { - "content-type": "text/plain" - } + "content-type": "multipart/form-data; boundary=defdf240-76b4-1f0c-814d-a1b3d90cd6b3" +} +def build_data_list(fields): + dataList = [] + for field in fields: + name = field.get('name', '') + value = field.get('value', '') + type_ = field.get('type', 'text') + dataList.append(encode('--defdf240-76b4-1f0c-814d-a1b3d90cd6b3')) + if type_ == 'text': + dataList.append(encode(f'Content-Disposition: form-data; name="{name}"')) + dataList.append(encode('Content-Type: text/plain')) + dataList.append(encode('')) + dataList.append(encode(value)) + elif type_ == 'file': + dataList.append(encode(f'Content-Disposition: form-data; name="{name}"; filename="{value}"')) + dataList.append(encode(f'Content-Type: {mimetypes.guess_type(value)[0] or "application/octet-stream"}')) + dataList.append(encode('')) + dataList.append(open(value, 'rb').read()) + dataList.append(encode(f'--defdf240-76b4-1f0c-814d-a1b3d90cd6b3--')) + dataList.append(encode('')) + return dataList + +dataList = build_data_list([{"name":"token","value":"xyz","type":"text"},{"name":"imfile","value":"/Documents/up/1.png","type":"file"}]) +body = b'\r\n'.join(dataList) conn = http.client.HTTPSConnection("api.apidash.dev") -conn.request("POST", "/case/lower", +conn.request("POST", "/io/img", body= body, headers= headers) @@ -516,23 +604,52 @@ print(data.decode("utf-8")) """; expect( codeGen.getCode( - CodegenLanguage.pythonHttpClient, requestModelPost7, "https"), + CodegenLanguage.pythonHttpClient, requestModelPost7, "https", + boundary: "defdf240-76b4-1f0c-814d-a1b3d90cd6b3"), expectedCode); - }, skip: true); + }); test('POST 8', () { const expectedCode = r"""import http.client +import mimetypes +from codecs import encode +from urllib.parse import urlencode -body = r'''{ -"text": "I LOVE Flutter" -}''' +queryParams = { + "size": "2", + "len": "3" +} +queryParamsStr = '?' + urlencode(queryParams) headers = { - "content-type": "text/plain" - } + "content-type": "multipart/form-data; boundary=a990b150-7683-1f0c-814d-a1b3d90cd6b3" +} +def build_data_list(fields): + dataList = [] + for field in fields: + name = field.get('name', '') + value = field.get('value', '') + type_ = field.get('type', 'text') + dataList.append(encode('--a990b150-7683-1f0c-814d-a1b3d90cd6b3')) + if type_ == 'text': + dataList.append(encode(f'Content-Disposition: form-data; name="{name}"')) + dataList.append(encode('Content-Type: text/plain')) + dataList.append(encode('')) + dataList.append(encode(value)) + elif type_ == 'file': + dataList.append(encode(f'Content-Disposition: form-data; name="{name}"; filename="{value}"')) + dataList.append(encode(f'Content-Type: {mimetypes.guess_type(value)[0] or "application/octet-stream"}')) + dataList.append(encode('')) + dataList.append(open(value, 'rb').read()) + dataList.append(encode(f'--a990b150-7683-1f0c-814d-a1b3d90cd6b3--')) + dataList.append(encode('')) + return dataList + +dataList = build_data_list([{"name":"text","value":"API","type":"text"},{"name":"sep","value":"|","type":"text"},{"name":"times","value":"3","type":"text"}]) +body = b'\r\n'.join(dataList) conn = http.client.HTTPSConnection("api.apidash.dev") -conn.request("POST", "/case/lower", +conn.request("POST", "/io/form" + queryParamsStr, body= body, headers= headers) @@ -543,23 +660,54 @@ print(data.decode("utf-8")) """; expect( codeGen.getCode( - CodegenLanguage.pythonHttpClient, requestModelPost8, "https"), + CodegenLanguage.pythonHttpClient, requestModelPost8, "https", + boundary: "a990b150-7683-1f0c-814d-a1b3d90cd6b3"), expectedCode); - }, skip: true); + }); test('POST 9', () { const expectedCode = r"""import http.client +import mimetypes +from codecs import encode +from urllib.parse import urlencode -body = r'''{ -"text": "I LOVE Flutter" -}''' +queryParams = { + "size": "2", + "len": "3" +} +queryParamsStr = '?' + urlencode(queryParams) headers = { - "content-type": "text/plain" - } + "User-Agent": "Test Agent", + "Keep-Alive": "true", + "content-type": "multipart/form-data; boundary=79088e00-75ec-1f0c-814d-a1b3d90cd6b3" +} +def build_data_list(fields): + dataList = [] + for field in fields: + name = field.get('name', '') + value = field.get('value', '') + type_ = field.get('type', 'text') + dataList.append(encode('--79088e00-75ec-1f0c-814d-a1b3d90cd6b3')) + if type_ == 'text': + dataList.append(encode(f'Content-Disposition: form-data; name="{name}"')) + dataList.append(encode('Content-Type: text/plain')) + dataList.append(encode('')) + dataList.append(encode(value)) + elif type_ == 'file': + dataList.append(encode(f'Content-Disposition: form-data; name="{name}"; filename="{value}"')) + dataList.append(encode(f'Content-Type: {mimetypes.guess_type(value)[0] or "application/octet-stream"}')) + dataList.append(encode('')) + dataList.append(open(value, 'rb').read()) + dataList.append(encode(f'--79088e00-75ec-1f0c-814d-a1b3d90cd6b3--')) + dataList.append(encode('')) + return dataList + +dataList = build_data_list([{"name":"token","value":"xyz","type":"text"},{"name":"imfile","value":"/Documents/up/1.png","type":"file"}]) +body = b'\r\n'.join(dataList) conn = http.client.HTTPSConnection("api.apidash.dev") -conn.request("POST", "/case/lower", +conn.request("POST", "/io/img" + queryParamsStr, body= body, headers= headers) @@ -570,10 +718,11 @@ print(data.decode("utf-8")) """; expect( codeGen.getCode( - CodegenLanguage.pythonHttpClient, requestModelPost9, "https"), + CodegenLanguage.pythonHttpClient, requestModelPost9, "https", + boundary: "79088e00-75ec-1f0c-814d-a1b3d90cd6b3"), expectedCode); }); - }, skip: true); + }); group('PUT Request', () { test('PUT 1', () { @@ -585,8 +734,8 @@ body = r'''{ }''' headers = { - "content-type": "application/json" - } + "content-type": "application/json" +} conn = http.client.HTTPSConnection("reqres.in") conn.request("PUT", "/api/users/2", @@ -615,8 +764,8 @@ body = r'''{ }''' headers = { - "content-type": "application/json" - } + "content-type": "application/json" +} conn = http.client.HTTPSConnection("reqres.in") conn.request("PATCH", "/api/users/2", @@ -662,8 +811,8 @@ body = r'''{ }''' headers = { - "content-type": "application/json" - } + "content-type": "application/json" +} conn = http.client.HTTPSConnection("reqres.in") conn.request("DELETE", "/api/users/2",