diff --git a/lib/codegen/codegen.dart b/lib/codegen/codegen.dart index 9d85deef..854ca241 100644 --- a/lib/codegen/codegen.dart +++ b/lib/codegen/codegen.dart @@ -14,6 +14,7 @@ import 'js/axios.dart'; import 'js/fetch.dart'; import 'others/har.dart'; import 'others/curl.dart'; +import 'julia/http.dart'; class Codegen { String? getCode( @@ -57,13 +58,15 @@ class Codegen { case CodegenLanguage.pythonRequests: return PythonRequestsCodeGen().getCode(rM, boundary: boundary); case CodegenLanguage.rustActix: - return RustActixCodeGen().getCode(rM, boundary: boundary); + return RustActixCodeGen().getCode(rM, boundary: boundary); case CodegenLanguage.rustReqwest: return RustReqwestCodeGen().getCode(rM); case CodegenLanguage.rustUreq: - return RustUreqCodeGen().getCode(rM, boundary: boundary); + return RustUreqCodeGen().getCode(rM, boundary: boundary); case CodegenLanguage.goHttp: return GoHttpCodeGen().getCode(rM); + case CodegenLanguage.juliaHttp: + return JuliaHttpClientCodeGen().getCode(rM); } } } diff --git a/lib/codegen/julia/http.dart b/lib/codegen/julia/http.dart new file mode 100644 index 00000000..b1d1e96b --- /dev/null +++ b/lib/codegen/julia/http.dart @@ -0,0 +1,198 @@ +import 'dart:io'; +import 'dart:convert'; +import 'package:jinja/jinja.dart' as jj; +import 'package:apidash/consts.dart'; +import 'package:apidash/utils/utils.dart' + show getNewUuid, getValidRequestUri, padMultilineString, stripUriParams; +import 'package:apidash/models/models.dart' show RequestModel; + +class JuliaHttpClientCodeGen { + final String kTemplateStart = """using HTTP,JSON + +url = "{{url}}" + +"""; + + String kTemplateParams = """ +{% set new_params = params | replace(":", "=>") | replace("{", "(") | replace("}", ")") %} + +params = Dict{{new_params}} +"""; + + int kParamsPadding = 9; + + String kTemplateBody = ''' +{% set new_params = body | replace(":", "=>") | replace("{", "(") | replace("}", ")") %} + +payload = Dict{{new_params}} +'''; + + String kTemplateJson = """ +{% set new_params = body | replace(":", "=>") | replace("{", "(") | replace("}", ")") %} + +payload = Dict{{new_params}} +"""; + + String kTemplateHeaders = """ +{% set new_params = headers | replace(":", "=>") | replace("{", "(") | replace("}", ")") %} + +headers = Dict{{new_params}} +"""; + + String kTemplateFormHeaderContentType = ''' +multipart/form-data; boundary={{boundary}}'''; + + int kHeadersPadding = 10; + + String kTemplateRequest = """ + + +response = HTTP.{{method}}(url +"""; + + final String kStringFormDataBody = r''' +function build_data_list(fields) + dataList = [] + for field in fields + name = field["name"] + value = field["value"] + type_ = get(field, "type", "text") + + push!(dataList, b"--{{boundary}}") + if type_ == "text" + push!(dataList, b"Content-Disposition: form-data; name=\"$name\"") + push!(dataList, b"Content-Type: text/plain") + push!(dataList, b"") + push!(dataList, codeunits(value)) + elseif type_ == "file" + push!(dataList, b"Content-Disposition: form-data; name=\"$name\"; filename=\"$value\"") + push!(dataList, b"Content-Type: $value") + push!(dataList, b"") + push!(dataList, String(read(value))) + end + end + push!(dataList, "--{{boundary}}--") + push!(dataList, b"") + return dataList +end + +dataList = build_data_list({{fields_list}}) +payload = join(dataList, b"\r\n") +'''; + + String kStringRequestParams = """, query=params"""; + + String kStringRequestBody = """, payload=payload"""; + + String kStringRequestJson = """, JSON.json(payload)"""; + + String kStringRequestHeaders = """, headers=headers"""; + + final String kStringRequestEnd = """ +) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + + String? getCode(RequestModel requestModel) { + try { + String result = ""; + bool hasQuery = false; + bool hasHeaders = false; + bool hasBody = false; + bool hasJsonBody = false; + String uuid = getNewUuid(); + + var rec = getValidRequestUri( + requestModel.url, + requestModel.enabledRequestParams, + ); + Uri? uri = rec.$1; + if (uri != null) { + var templateStartUrl = jj.Template(kTemplateStart); + result += templateStartUrl.render({ + "url": stripUriParams(uri), + }); + + if (uri.hasQuery) { + var params = uri.queryParameters; + if (params.isNotEmpty) { + hasQuery = true; + var templateParams = jj.Template(kTemplateParams); + var paramsString = kEncoder.convert(params); + paramsString = padMultilineString(paramsString, kParamsPadding); + result += templateParams.render({"params": paramsString}); + } + } + + if (requestModel.hasJsonData) { + hasJsonBody = true; + var templateBody = jj.Template(kTemplateJson); + result += templateBody.render({"body": requestModel.requestBody}); + } else if (requestModel.hasTextData) { + hasBody = true; + var templateBody = jj.Template(kTemplateBody); + 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": uuid, + }); + } + if (headers.isNotEmpty || hasBody) { + hasHeaders = true; + if (hasBody) { + headers[HttpHeaders.contentTypeHeader] = + requestModel.requestBodyContentType.header; + } + var headersString = kEncoder.convert(headers); + headersString = padMultilineString(headersString, kHeadersPadding); + var templateHeaders = jj.Template(kTemplateHeaders); + result += templateHeaders.render({"headers": headersString}); + } + } + if (requestModel.hasFormData) { + var formDataBodyData = jj.Template(kStringFormDataBody); + result += formDataBodyData.render( + { + "fields_list": json.encode(requestModel.formDataMapList), + "boundary": uuid, + }, + ); + } + var templateRequest = jj.Template(kTemplateRequest); + result += templateRequest.render({ + "method": requestModel.method.name.toLowerCase(), + }); + + if (hasQuery) { + result += kStringRequestParams; + } + + if (hasBody || requestModel.hasFormData) { + result += kStringRequestBody; + } + + if (hasJsonBody || requestModel.hasFormData) { + result += kStringRequestJson; + } + + if (hasHeaders || requestModel.hasFormData) { + result += kStringRequestHeaders; + } + + result += kStringRequestEnd; + } + return result; + } catch (e) { + return null; + } + } +} diff --git a/lib/consts.dart b/lib/consts.dart index 210254d0..a557023d 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -276,7 +276,9 @@ enum CodegenLanguage { pythonRequests("Python (requests)", "python", "py"), rustActix("Rust (Actix Client)", "rust", "rs"), rustReqwest("Rust (reqwest)", "rust", "rs"), - rustUreq("Rust (ureq)", "rust", "rs"); + rustUreq("Rust (ureq)", "rust", "rs"), + juliaHttp("Julia (HTTP)", "julia", "jl"); + const CodegenLanguage(this.label, this.codeHighlightLang, this.ext); final String label; diff --git a/test/codegen/julia_http_codegen_test.dart b/test/codegen/julia_http_codegen_test.dart new file mode 100644 index 00000000..2b5161eb --- /dev/null +++ b/test/codegen/julia_http_codegen_test.dart @@ -0,0 +1,453 @@ +import 'package:apidash/codegen/codegen.dart'; +import 'package:apidash/consts.dart'; +import 'package:test/test.dart'; +import '../request_models.dart'; + +void main() { + final codeGen = Codegen(); + + group('GET Request', () { + test('GET 1', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev" + + +response = HTTP.get(url) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet1, "https"), + expectedCode); + }); + test('GET 2', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev/country/data" + + +params = Dict( + "code"=> "US" + ) + +response = HTTP.get(url, query=params) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet2, "https"), + expectedCode); + }); + test('GET 3', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev/country/data" + + +params = Dict( + "code"=> "IND" + ) + +response = HTTP.get(url, query=params) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet3, "https"), + expectedCode); + }); + test('GET 4', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev/humanize/social" + + +params = Dict( + "num"=> "8700000", + "digits"=> "3", + "system"=> "SS", + "add_space"=> "true", + "trailing_zeros"=> "true" + ) + +response = HTTP.get(url, query=params) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet4, "https"), + expectedCode); + }); + + test('GET 5', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.github.com/repos/foss42/apidash" + + +headers = Dict( + "User-Agent"=> "Test Agent" + ) + +response = HTTP.get(url, headers=headers) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet5, "https"), + expectedCode); + }); + + test('GET 6', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.github.com/repos/foss42/apidash" + + +params = Dict( + "raw"=> "true" + ) + +headers = Dict( + "User-Agent"=> "Test Agent" + ) + +response = HTTP.get(url, query=params, headers=headers) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet6, "https"), + expectedCode); + }); + + test('GET 7', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev" + + +response = HTTP.get(url) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet7, "https"), + expectedCode); + }); + + test('GET 8', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.github.com/repos/foss42/apidash" + + +params = Dict( + "raw"=> "true" + ) + +headers = Dict( + "User-Agent"=> "Test Agent" + ) + +response = HTTP.get(url, query=params, headers=headers) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet8, "https"), + expectedCode); + }); + + test('GET 9', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev/humanize/social" + + +params = Dict( + "num"=> "8700000", + "add_space"=> "true" + ) + +response = HTTP.get(url, query=params) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet9, "https"), + expectedCode); + }); + + test('GET 10', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev/humanize/social" + + +headers = Dict( + "User-Agent"=> "Test Agent" + ) + +response = HTTP.get(url, headers=headers) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode( + CodegenLanguage.juliaHttp, requestModelGet10, "https"), + expectedCode); + }); + + test('GET 11', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev/humanize/social" + + +params = Dict( + "num"=> "8700000", + "digits"=> "3" + ) + +headers = Dict( + "User-Agent"=> "Test Agent" + ) + +response = HTTP.get(url, query=params, headers=headers) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode( + CodegenLanguage.juliaHttp, requestModelGet11, "https"), + expectedCode); + }); + + test('GET 12', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev/humanize/social" + + +response = HTTP.get(url) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode( + CodegenLanguage.juliaHttp, requestModelGet12, "https"), + expectedCode); + }); + }); + + group('HEAD Request', () { + test('HEAD 1', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev" + + +response = HTTP.head(url) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode( + CodegenLanguage.juliaHttp, requestModelHead1, "https"), + expectedCode); + }); + + test('HEAD 2', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev" + + +response = HTTP.head(url) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode( + CodegenLanguage.juliaHttp, requestModelHead2, "https"), + expectedCode); + }); + }); + + group('POST Request', () { + test('POST 1', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev/case/lower" + + +payload = Dict( +"text"=> "I LOVE Flutter" +) + +headers = Dict( + "content-type"=> "text/plain" + ) + +response = HTTP.post(url, payload=payload, headers=headers) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode( + CodegenLanguage.juliaHttp, requestModelPost1, "https"), + expectedCode); + }); + + test('POST 2', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev/case/lower" + + +payload = Dict( +"text"=> "I LOVE Flutter", +"flag"=> null, +"male"=> true, +"female"=> false, +"no"=> 1.2, +"arr"=> ["null", "true", "false", null] +) + +response = HTTP.post(url, JSON.json(payload)) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode( + CodegenLanguage.juliaHttp, requestModelPost2, "https"), + expectedCode); + }); + test('POST 3', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://api.apidash.dev/case/lower" + + +payload = Dict( +"text"=> "I LOVE Flutter" +) + +headers = Dict( + "User-Agent"=> "Test Agent" + ) + +response = HTTP.post(url, JSON.json(payload), headers=headers) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode( + CodegenLanguage.juliaHttp, requestModelPost3, "https"), + expectedCode); + }); + }); + group('PUT Request', () { + test('PUT 1', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://reqres.in/api/users/2" + + +payload = Dict( +"name"=> "morpheus", +"job"=> "zion resident" +) + +response = HTTP.put(url, JSON.json(payload)) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode(CodegenLanguage.juliaHttp, requestModelPut1, "https"), + expectedCode); + }); + }); + group('PATCH Request', () { + test('PATCH 1', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://reqres.in/api/users/2" + + +payload = Dict( +"name"=> "marfeus", +"job"=> "accountant" +) + +response = HTTP.patch(url, JSON.json(payload)) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode( + CodegenLanguage.juliaHttp, requestModelPatch1, "https"), + expectedCode); + }); + }); + group('DELETE Request', () { + test('DELETE 1', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://reqres.in/api/users/2" + + +response = HTTP.delete(url) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode( + CodegenLanguage.juliaHttp, requestModelDelete1, "https"), + expectedCode); + }); + test('DELETE 2', () { + const expectedCode = r"""using HTTP,JSON + +url = "https://reqres.in/api/users/2" + + +payload = Dict( +"name"=> "marfeus", +"job"=> "accountant" +) + +response = HTTP.delete(url, JSON.json(payload)) + +println("Status Code:", response.status) +println("Response Body:", String(response.body)) +"""; + expect( + codeGen.getCode( + CodegenLanguage.juliaHttp, requestModelDelete2, "https"), + expectedCode); + }); + }); +}