diff --git a/lib/codegen/codegen.dart b/lib/codegen/codegen.dart index d8d47203..f9dcb20f 100644 --- a/lib/codegen/codegen.dart +++ b/lib/codegen/codegen.dart @@ -15,7 +15,7 @@ class Codegen { case CodegenLanguage.dartHttp: return DartHttpCodeGen().getCode(requestModel, defaultUriScheme); case CodegenLanguage.kotlinOkHttp: - return KotlinOkHttpCodeGen().getCode(requestModel); + return KotlinOkHttpCodeGen().getCode(requestModel, defaultUriScheme); case CodegenLanguage.pythonHttpClient: return PythonHttpClientCodeGen() .getCode(requestModel, defaultUriScheme); diff --git a/lib/codegen/kotlin/pkg_okhttp.dart b/lib/codegen/kotlin/pkg_okhttp.dart index 5bb90524..c33c7ce4 100644 --- a/lib/codegen/kotlin/pkg_okhttp.dart +++ b/lib/codegen/kotlin/pkg_okhttp.dart @@ -1,79 +1,157 @@ +import 'dart:convert'; import 'package:apidash/consts.dart'; - +import 'package:jinja/jinja.dart' as jj; +import 'package:apidash/utils/utils.dart' show getValidRequestUri, rowsToMap; import '../../models/request_model.dart'; class KotlinOkHttpCodeGen { - final String headerSnippet = """import okhttp3.MediaType.Companion.toMediaType -import okhttp3.MultipartBody -import okhttp3.OkHttpClient -import okhttp3.Request + final String kTemplateStart = """import okhttp3.OkHttpClient +import okhttp3.Request{{importForQuery}}{{importForBody}} + +fun main() { + val client = OkHttpClient() + +"""; + + final String kStringImportForQuery = """ + +import okhttp3.HttpUrl.Companion.toHttpUrl"""; + + final String kStringImportForBody = """ + import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.RequestBody.Companion.asRequestBody -import java.io.File -import java.util.concurrent.TimeUnit +import okhttp3.MediaType.Companion.toMediaType"""; -val client = OkHttpClient() + final String kTemplateUrl = ''' + + val url = "{{url}}" + +'''; + + final String kTemplateUrlQuery = ''' + + val url = "{{url}}".toHttpUrl().newBuilder() +{{params}} .build() + +'''; + + String kTemplateRequestBody = ''' + + val mediaType = "{{contentType}}".toMediaType() + + val body = """{{body}}""".toRequestBody(mediaType) + +'''; + + final String kStringRequestStart = """ + + val request = Request.Builder() + .url(url) """; - final String footerSnippet = """ .build() -val response = client.newCall(request).execute() + final String kTemplateRequestEnd = """ + .{{method}}({{hasBody}}) + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} -println(response.body!!.string()) """; - String getCode(RequestModel requestModel) { - String result = ""; - result = result + headerSnippet; - if (requestModel.method != HTTPVerb.get && - requestModel.method != HTTPVerb.head) { - result = - """${result}val mediaType = "${requestModel.requestBodyContentType == ContentType.json ? "application/json" : "text/plain"}".toMediaType() -val body = "${requestModel.requestBody}".toRequestBody(mediaType)\n"""; - } - result = "${result}val request = Request.Builder()\n"; - result = "$result .url(\"${requestModel.url}\")\n"; - result = result + addQueryParams(requestModel); - result = result + addRequestMethod(requestModel); - result = result + addHeaders(requestModel); - result = result + footerSnippet; + String? getCode( + RequestModel requestModel, + String defaultUriScheme, + ) { + try { + String result = ""; + bool hasHeaders = false; + bool hasQuery = false; + bool hasBody = false; - return result; - } + String url = requestModel.url; + if (!url.contains("://") && url.isNotEmpty) { + url = "$defaultUriScheme://$url"; + } - String addQueryParams(RequestModel requestModel) { - String result = ""; - if (requestModel.requestParams == null) { + var rec = getValidRequestUri(url, requestModel.requestParams); + Uri? uri = rec.$1; + + if (uri != null) { + String url = "${uri.scheme}://${uri.authority}${uri.path}"; + + if (uri.hasQuery) { + var params = uri.queryParameters; + if (params.isNotEmpty) { + hasQuery = true; + var templateParams = jj.Template(kTemplateUrlQuery); + result += templateParams + .render({"url": url, "params": getQueryParams(params)}); + } + } + if (!hasQuery) { + var templateUrl = jj.Template(kTemplateUrl); + result += templateUrl.render({"url": url}); + } + + var method = requestModel.method; + var requestBody = requestModel.requestBody; + if (kMethodsWithBody.contains(method) && requestBody != null) { + var contentLength = utf8.encode(requestBody).length; + if (contentLength > 0) { + hasBody = true; + String contentType = + kContentTypeMap[requestModel.requestBodyContentType] ?? ""; + var templateBody = jj.Template(kTemplateRequestBody); + result += templateBody + .render({"contentType": contentType, "body": requestBody}); + } + } + + var templateStart = jj.Template(kTemplateStart); + var stringStart = templateStart.render({ + "importForQuery": hasQuery ? kStringImportForQuery : "", + "importForBody": hasBody ? kStringImportForBody : "" + }); + + result = stringStart + result; + result += kStringRequestStart; + + var headersList = requestModel.requestHeaders; + if (headersList != null) { + var headers = rowsToMap(requestModel.requestHeaders) ?? {}; + if (headers.isNotEmpty) { + hasHeaders = true; + result += getHeaders(headers); + } + } + + var templateRequestEnd = jj.Template(kTemplateRequestEnd); + result += templateRequestEnd.render({ + "method": method.name.toLowerCase(), + "hasBody": hasBody ? "body" : "", + }); + } return result; + } catch (e) { + return null; } - for (final queryParam in requestModel.requestParams!) { - result = - """$result .addQueryParameter("${queryParam.name}", "${queryParam.value}")\n"""; + } + + String getQueryParams(Map params) { + String result = ""; + for (final k in params.keys) { + result = """$result .addQueryParameter("$k", "${params[k]}")\n"""; } return result; } - String addHeaders(RequestModel requestModel) { + String getHeaders(Map headers) { String result = ""; - if (requestModel.requestHeaders == null) { - return result; - } - for (final header in requestModel.requestHeaders!) { - result = """$result .addHeader("${header.name}", "${header.value}")\n"""; - } - return result; - } - - String addRequestMethod(RequestModel requestModel) { - String result = ""; - if (requestModel.method != HTTPVerb.get && - requestModel.method != HTTPVerb.head && - requestModel.method != HTTPVerb.delete) { - result = """$result .${requestModel.method.name}(body)\n"""; - } else if (requestModel.method == HTTPVerb.head) { - result = """$result .${requestModel.method.name}()\n"""; - } - if (requestModel.method == HTTPVerb.delete) { - result = """$result .method("DELETE", body)\n"""; + for (final k in headers.keys) { + result = """$result .addHeader("$k", "${headers[k]}")\n"""; } return result; } diff --git a/test/codegen/kotlin_okhttp_codegen_test.dart b/test/codegen/kotlin_okhttp_codegen_test.dart index 2efd1130..1ebdc4b1 100644 --- a/test/codegen/kotlin_okhttp_codegen_test.dart +++ b/test/codegen/kotlin_okhttp_codegen_test.dart @@ -3,124 +3,498 @@ import 'package:test/test.dart'; import '../request_models.dart'; void main() { - group('KotlinOkHttpCodeGen', () { - final kotlinOkHttpCodeGen = KotlinOkHttpCodeGen(); + final kotlinOkHttpCodeGen = KotlinOkHttpCodeGen(); - test('getCode returns valid code for GET request', () { - const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType -import okhttp3.MultipartBody -import okhttp3.OkHttpClient + group('GET Request', () { + test('GET 1', () { + const expectedCode = r"""import okhttp3.OkHttpClient import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.RequestBody.Companion.asRequestBody -import java.io.File -import java.util.concurrent.TimeUnit -val client = OkHttpClient() -val request = Request.Builder() - .url("https://api.foss42.com") - .build() -val response = client.newCall(request).execute() +fun main() { + val client = OkHttpClient() -println(response.body!!.string()) + val url = "https://api.foss42.com" + + val request = Request.Builder() + .url(url) + .get() + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} """; - expect(kotlinOkHttpCodeGen.getCode(requestModelGet1), expectedCode); + expect( + kotlinOkHttpCodeGen.getCode(requestModelGet1, "https"), expectedCode); }); - test('getCode returns valid code for POST request', () { - const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType -import okhttp3.MultipartBody -import okhttp3.OkHttpClient + test('GET 2', () { + const expectedCode = r"""import okhttp3.OkHttpClient import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.RequestBody.Companion.asRequestBody -import java.io.File -import java.util.concurrent.TimeUnit +import okhttp3.HttpUrl.Companion.toHttpUrl -val client = OkHttpClient() -val mediaType = "application/json".toMediaType() -val body = "{"text": "IS Upper"}".toRequestBody(mediaType) -val request = Request.Builder() - .url("https://api.foss42.com/case/lower") - .post(body) - .build() -val response = client.newCall(request).execute() +fun main() { + val client = OkHttpClient() -println(response.body!!.string()) + val url = "https://api.foss42.com/country/data".toHttpUrl().newBuilder() + .addQueryParameter("code", "US") + .build() + + val request = Request.Builder() + .url(url) + .get() + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} """; - expect(kotlinOkHttpCodeGen.getCode(requestModelPost1), expectedCode); + expect( + kotlinOkHttpCodeGen.getCode(requestModelGet2, "https"), expectedCode); }); - test('getCode returns valid code for DELETE request', () { - const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType -import okhttp3.MultipartBody -import okhttp3.OkHttpClient + test('GET 3', () { + const expectedCode = r"""import okhttp3.OkHttpClient import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.RequestBody.Companion.asRequestBody -import java.io.File -import java.util.concurrent.TimeUnit +import okhttp3.HttpUrl.Companion.toHttpUrl -val client = OkHttpClient() -val mediaType = "application/json".toMediaType() -val body = "{"title": "foo","body": "bar","userId": 1}".toRequestBody(mediaType) -val request = Request.Builder() - .url("https://jsonplaceholder.typicode.com/posts/1") - .method("DELETE", body) - .build() -val response = client.newCall(request).execute() +fun main() { + val client = OkHttpClient() -println(response.body!!.string()) + val url = "https://api.foss42.com/country/data".toHttpUrl().newBuilder() + .addQueryParameter("code", "IND") + .build() + + val request = Request.Builder() + .url(url) + .get() + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} """; - expect(kotlinOkHttpCodeGen.getCode(requestModelDelete1), expectedCode); + expect( + kotlinOkHttpCodeGen.getCode(requestModelGet3, "https"), expectedCode); }); - test('getCode returns valid code for HEAD request', () { - const expectedCode = """import okhttp3.MediaType.Companion.toMediaType -import okhttp3.MultipartBody -import okhttp3.OkHttpClient + test('GET 4', () { + const expectedCode = r"""import okhttp3.OkHttpClient import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.RequestBody.Companion.asRequestBody -import java.io.File -import java.util.concurrent.TimeUnit +import okhttp3.HttpUrl.Companion.toHttpUrl -val client = OkHttpClient() -val request = Request.Builder() - .url("https://jsonplaceholder.typicode.com/posts/1") - .head() - .build() -val response = client.newCall(request).execute() +fun main() { + val client = OkHttpClient() -println(response.body!!.string()) + val url = "https://api.foss42.com/humanize/social".toHttpUrl().newBuilder() + .addQueryParameter("num", "8700000") + .addQueryParameter("digits", "3") + .addQueryParameter("system", "SS") + .addQueryParameter("add_space", "true") + .addQueryParameter("trailing_zeros", "true") + .build() + + val request = Request.Builder() + .url(url) + .get() + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} """; - expect(kotlinOkHttpCodeGen.getCode(requestModelHead1), expectedCode); + expect( + kotlinOkHttpCodeGen.getCode(requestModelGet4, "https"), expectedCode); }); - test( - 'getCode returns valid code for requests with headers and query parameters', - () { - const expectedCode = """import okhttp3.MediaType.Companion.toMediaType -import okhttp3.MultipartBody -import okhttp3.OkHttpClient + test('GET 5', () { + const expectedCode = r"""import okhttp3.OkHttpClient +import okhttp3.Request + +fun main() { + val client = OkHttpClient() + + val url = "https://api.github.com/repos/foss42/apidash" + + val request = Request.Builder() + .url(url) + .addHeader("User-Agent", "Test Agent") + .get() + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} +"""; + expect( + kotlinOkHttpCodeGen.getCode(requestModelGet5, "https"), expectedCode); + }); + + test('GET 6', () { + const expectedCode = r"""import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.HttpUrl.Companion.toHttpUrl + +fun main() { + val client = OkHttpClient() + + val url = "https://api.github.com/repos/foss42/apidash".toHttpUrl().newBuilder() + .addQueryParameter("raw", "true") + .build() + + val request = Request.Builder() + .url(url) + .addHeader("User-Agent", "Test Agent") + .get() + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} +"""; + expect( + kotlinOkHttpCodeGen.getCode(requestModelGet6, "https"), expectedCode); + }); + + test('GET 7', () { + const expectedCode = r"""import okhttp3.OkHttpClient +import okhttp3.Request + +fun main() { + val client = OkHttpClient() + + val url = "https://api.foss42.com" + + val request = Request.Builder() + .url(url) + .get() + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} +"""; + expect( + kotlinOkHttpCodeGen.getCode(requestModelGet7, "https"), expectedCode); + }); + + test('GET 8', () { + const expectedCode = r"""import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.HttpUrl.Companion.toHttpUrl + +fun main() { + val client = OkHttpClient() + + val url = "https://api.github.com/repos/foss42/apidash".toHttpUrl().newBuilder() + .addQueryParameter("raw", "true") + .build() + + val request = Request.Builder() + .url(url) + .addHeader("User-Agent", "Test Agent") + .get() + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} +"""; + expect( + kotlinOkHttpCodeGen.getCode(requestModelGet8, "https"), expectedCode); + }); + }); + + group('HEAD Request', () { + test('HEAD 1', () { + const expectedCode = r"""import okhttp3.OkHttpClient +import okhttp3.Request + +fun main() { + val client = OkHttpClient() + + val url = "https://api.foss42.com" + + val request = Request.Builder() + .url(url) + .head() + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} +"""; + expect(kotlinOkHttpCodeGen.getCode(requestModelHead1, "https"), + expectedCode); + }); + + test('HEAD 2', () { + const expectedCode = r"""import okhttp3.OkHttpClient +import okhttp3.Request + +fun main() { + val client = OkHttpClient() + + val url = "http://api.foss42.com" + + val request = Request.Builder() + .url(url) + .head() + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} +"""; + expect( + kotlinOkHttpCodeGen.getCode(requestModelHead2, "http"), expectedCode); + }); + }); + + group('POST Request', () { + test('POST 1', () { + const expectedCode = r'''import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.RequestBody.Companion.asRequestBody -import java.io.File -import java.util.concurrent.TimeUnit +import okhttp3.MediaType.Companion.toMediaType -val client = OkHttpClient() -val request = Request.Builder() - .url("https://jsonplaceholder.typicode.com/posts") - .addQueryParameter("userId", "1") - .addHeader("Custom-Header-1", "Value-1") - .addHeader("Custom-Header-2", "Value-2") - .build() -val response = client.newCall(request).execute() +fun main() { + val client = OkHttpClient() -println(response.body!!.string()) + val url = "https://api.foss42.com/case/lower" + + val mediaType = "text/plain".toMediaType() + + val body = """{ +"text": "I LOVE Flutter" +}""".toRequestBody(mediaType) + + val request = Request.Builder() + .url(url) + .post(body) + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} +'''; + expect(kotlinOkHttpCodeGen.getCode(requestModelPost1, "https"), + expectedCode); + }); + + test('POST 2', () { + const expectedCode = r'''import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.MediaType.Companion.toMediaType + +fun main() { + val client = OkHttpClient() + + val url = "https://api.foss42.com/case/lower" + + val mediaType = "application/json".toMediaType() + + val body = """{ +"text": "I LOVE Flutter" +}""".toRequestBody(mediaType) + + val request = Request.Builder() + .url(url) + .post(body) + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} +'''; + expect(kotlinOkHttpCodeGen.getCode(requestModelPost2, "https"), + expectedCode); + }); + + test('POST 3', () { + const expectedCode = r'''import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.MediaType.Companion.toMediaType + +fun main() { + val client = OkHttpClient() + + val url = "https://api.foss42.com/case/lower" + + val mediaType = "application/json".toMediaType() + + val body = """{ +"text": "I LOVE Flutter" +}""".toRequestBody(mediaType) + + val request = Request.Builder() + .url(url) + .addHeader("User-Agent", "Test Agent") + .post(body) + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} +'''; + expect(kotlinOkHttpCodeGen.getCode(requestModelPost3, "https"), + expectedCode); + }); + }); + group('PUT Request', () { + test('PUT 1', () { + const expectedCode = r'''import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.MediaType.Companion.toMediaType + +fun main() { + val client = OkHttpClient() + + val url = "https://reqres.in/api/users/2" + + val mediaType = "application/json".toMediaType() + + val body = """{ +"name": "morpheus", +"job": "zion resident" +}""".toRequestBody(mediaType) + + val request = Request.Builder() + .url(url) + .put(body) + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} +'''; + expect( + kotlinOkHttpCodeGen.getCode(requestModelPut1, "https"), expectedCode); + }); + }); + + group('PATCH Request', () { + test('PATCH 1', () { + const expectedCode = r'''import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.MediaType.Companion.toMediaType + +fun main() { + val client = OkHttpClient() + + val url = "https://reqres.in/api/users/2" + + val mediaType = "application/json".toMediaType() + + val body = """{ +"name": "marfeus", +"job": "accountant" +}""".toRequestBody(mediaType) + + val request = Request.Builder() + .url(url) + .patch(body) + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} +'''; + expect(kotlinOkHttpCodeGen.getCode(requestModelPatch1, "https"), + expectedCode); + }); + }); + + group('DELETE Request', () { + test('DELETE 1', () { + const expectedCode = r"""import okhttp3.OkHttpClient +import okhttp3.Request + +fun main() { + val client = OkHttpClient() + + val url = "https://reqres.in/api/users/2" + + val request = Request.Builder() + .url(url) + .delete() + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} """; - expect(kotlinOkHttpCodeGen.getCode(requestModelGet2), expectedCode); + expect(kotlinOkHttpCodeGen.getCode(requestModelDelete1, "https"), + expectedCode); + }); + + test('DELETE 2', () { + const expectedCode = r'''import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.MediaType.Companion.toMediaType + +fun main() { + val client = OkHttpClient() + + val url = "https://reqres.in/api/users/2" + + val mediaType = "application/json".toMediaType() + + val body = """{ +"name": "marfeus", +"job": "accountant" +}""".toRequestBody(mediaType) + + val request = Request.Builder() + .url(url) + .delete(body) + .build() + + val response = client.newCall(request).execute() + + println(response.code) + println(response.body?.string()) +} +'''; + expect(kotlinOkHttpCodeGen.getCode(requestModelDelete2, "https"), + expectedCode); }); }); }