From 8e9c2046abda9af107647a7b65ad68a83aeb0db3 Mon Sep 17 00:00:00 2001 From: mmjsmohit Date: Sun, 28 May 2023 18:45:05 +0530 Subject: [PATCH 1/7] Language Dropdown added. --- lib/consts.dart | 8 +++++ lib/providers/ui_providers.dart | 2 ++ lib/widgets/codegen_previewer.dart | 27 ++++++++++++++++ lib/widgets/dropdowns.dart | 52 ++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+) diff --git a/lib/consts.dart b/lib/consts.dart index c3c0a12c..ecdc8515 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -228,6 +228,14 @@ const kMethodsWithBody = [ const kDefaultHttpMethod = HTTPVerb.get; const kDefaultContentType = ContentType.json; +enum CodegenLanguage { + dartHttp("Dart - http"), + kotlinOkHttp("Kotlin - OkHttp"); + + const CodegenLanguage(this.label); + final String label; +} + const JsonEncoder kEncoder = JsonEncoder.withIndent(' '); const LineSplitter kSplitter = LineSplitter(); diff --git a/lib/providers/ui_providers.dart b/lib/providers/ui_providers.dart index da817bb9..a9732df8 100644 --- a/lib/providers/ui_providers.dart +++ b/lib/providers/ui_providers.dart @@ -1,3 +1,4 @@ +import 'package:apidash/consts.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; final navRailIndexStateProvider = StateProvider((ref) => 0); @@ -7,3 +8,4 @@ final sentRequestIdStateProvider = StateProvider((ref) => null); final codePaneVisibleStateProvider = StateProvider((ref) => false); final saveDataStateProvider = StateProvider((ref) => false); final clearDataStateProvider = StateProvider((ref) => false); +final codegenLanguageStateProvider = StateProvider((ref) => CodegenLanguage.dartHttp); \ No newline at end of file diff --git a/lib/widgets/codegen_previewer.dart b/lib/widgets/codegen_previewer.dart index 8edd624c..ef392586 100644 --- a/lib/widgets/codegen_previewer.dart +++ b/lib/widgets/codegen_previewer.dart @@ -1,7 +1,10 @@ +import 'package:apidash/widgets/dropdowns.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:highlighter/highlighter.dart' show highlight; import 'package:apidash/consts.dart'; import 'package:apidash/utils/utils.dart'; +import '../providers/ui_providers.dart'; import 'code_previewer.dart' show convert; import 'buttons.dart'; @@ -144,6 +147,7 @@ class _ViewCodePaneState extends State { style: Theme.of(context).textTheme.titleMedium, ), ), + const DropdownButtonCodeCodegenLanguage(), CopyButton(toCopy: widget.code), SaveInDownloadsButton( content: stringToBytes(widget.code), @@ -171,3 +175,26 @@ class _ViewCodePaneState extends State { ); } } + +class DropdownButtonCodeCodegenLanguage extends ConsumerStatefulWidget { + const DropdownButtonCodeCodegenLanguage({ + super.key, + }); + + @override + ConsumerState createState() => _DropdownButtonCodeCodegenLanguageState(); +} + +class _DropdownButtonCodeCodegenLanguageState + extends ConsumerState { + @override + Widget build(BuildContext context) { + final requestCodeLanguage = ref.watch(codegenLanguageStateProvider); + return DropdownButtonCodegenLanguage( + codegenLanguage: requestCodeLanguage, + onChanged: (CodegenLanguage? value) { + ref.read(codegenLanguageStateProvider.notifier).state = value!; + }, + ); + } +} \ No newline at end of file diff --git a/lib/widgets/dropdowns.dart b/lib/widgets/dropdowns.dart index 92c7dcfb..20e4a078 100644 --- a/lib/widgets/dropdowns.dart +++ b/lib/widgets/dropdowns.dart @@ -109,3 +109,55 @@ class _DropdownButtonContentTypeState extends State { ); } } + +class DropdownButtonCodegenLanguage extends StatefulWidget { + const DropdownButtonCodegenLanguage({ + Key? key, + this.codegenLanguage, + this.onChanged, + }) : super(key: key); + + @override + State createState() => + _DropdownButtonCodegenLanguageState(); + final CodegenLanguage? codegenLanguage; + final void Function(CodegenLanguage?)? onChanged; +} + +class _DropdownButtonCodegenLanguageState + extends State { + @override + Widget build(BuildContext context) { + final surfaceColor = Theme.of(context).colorScheme.surface; + return DropdownButton( + focusColor: surfaceColor, + value: widget.codegenLanguage, + icon: const Icon( + Icons.unfold_more_rounded, + size: 16, + ), + elevation: 4, + style: kCodeStyle.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + underline: Container( + height: 0, + ), + onChanged: widget.onChanged, + borderRadius: kBorderRadius12, + items: CodegenLanguage.values + .map>((CodegenLanguage value) { + return DropdownMenuItem( + value: value, + child: Padding( + padding: kPs8, + child: Text( + value.label, + style: kTextStyleButton, + ), + ), + ); + }).toList(), + ); + } +} \ No newline at end of file From d54a4473cb9db48aa1ea50a5f7cc968a2fe86951 Mon Sep 17 00:00:00 2001 From: mmjsmohit Date: Tue, 30 May 2023 23:50:26 +0530 Subject: [PATCH 2/7] Kotlin Code Generator Added --- lib/codegen/codegen.dart | 31 +++++++- lib/codegen/kotlin/pkg_okhttp.dart | 78 +++++++++++++++++++ .../editor_pane/details_card/code_pane.dart | 7 +- test/widget_test.dart | 30 +++++++ 4 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 lib/codegen/kotlin/pkg_okhttp.dart create mode 100644 test/widget_test.dart diff --git a/lib/codegen/codegen.dart b/lib/codegen/codegen.dart index 8883a098..414fded1 100644 --- a/lib/codegen/codegen.dart +++ b/lib/codegen/codegen.dart @@ -1 +1,30 @@ -export 'dart/pkg_http.dart'; +import 'package:apidash/codegen/kotlin/pkg_okhttp.dart'; +import 'package:apidash/consts.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../models/request_model.dart'; +import '../providers/collection_providers.dart'; +import '../providers/settings_providers.dart'; +import 'dart/pkg_http.dart'; + +class Codegen { + const Codegen({required this.codegenLanguage}); + final CodegenLanguage codegenLanguage; + String? getCode( + WidgetRef ref, + RequestModel requestModel, + String defaultUriScheme, + ) { + final activeRequestModel = ref.watch(activeRequestModelProvider); + final defaultUriScheme = + ref.watch(settingsProvider.select((value) => value.defaultUriScheme)); + switch (codegenLanguage) { + case CodegenLanguage.dartHttp: + return DartHttpCodeGen().getCode(activeRequestModel!, defaultUriScheme); + case CodegenLanguage.kotlinOkHttp: + return KotlinOkHttpCodeGen().getCode(activeRequestModel!); + default: + throw ArgumentError('Invalid codegenLanguage'); + } + } +} diff --git a/lib/codegen/kotlin/pkg_okhttp.dart b/lib/codegen/kotlin/pkg_okhttp.dart new file mode 100644 index 00000000..365efb3c --- /dev/null +++ b/lib/codegen/kotlin/pkg_okhttp.dart @@ -0,0 +1,78 @@ +import 'package:apidash/consts.dart'; + +import '../../models/request_model.dart'; + +class KotlinOkHttpCodeGen { + final String headerSnippet = """import okhttp3.MediaType.Companion.toMediaType +import okhttp3.MultipartBody +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() +"""; + + final String footerSnippet = """ .build() +val response = client.newCall(request).execute() + +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; + + return result; + } + + String addQueryParams(RequestModel requestModel) { + String result = ""; + if (requestModel.requestParams == null) { + return result; + } + for (final queryParam in requestModel.requestParams!) { + result = + """$result .addQueryParameter("${queryParam.k}", "${queryParam.v}")\n"""; + } + return result; + } + + String addHeaders(RequestModel requestModel) { + String result = ""; + if (requestModel.requestHeaders == null) { + return result; + } + for (final header in requestModel.requestHeaders!) { + result = """$result .addHeader("${header.k}", "${header.v}")\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"""; + } + if (requestModel.method == HTTPVerb.delete) { + result = """$result .method("DELETE", body)\n"""; + } + return result; + } +} diff --git a/lib/screens/home_page/editor_pane/details_card/code_pane.dart b/lib/screens/home_page/editor_pane/details_card/code_pane.dart index 4317e3b4..da649415 100644 --- a/lib/screens/home_page/editor_pane/details_card/code_pane.dart +++ b/lib/screens/home_page/editor_pane/details_card/code_pane.dart @@ -13,8 +13,6 @@ class CodePane extends ConsumerStatefulWidget { } class _CodePaneState extends ConsumerState { - final DartHttpCodeGen dartHttpCodeGen = DartHttpCodeGen(); - @override void initState() { super.initState(); @@ -22,10 +20,13 @@ class _CodePaneState extends ConsumerState { @override Widget build(BuildContext context) { + final CodegenLanguage codegenLanguage = + ref.watch(codegenLanguageStateProvider); + final Codegen codegen = Codegen(codegenLanguage: codegenLanguage); final activeRequestModel = ref.watch(activeRequestModelProvider); final defaultUriScheme = ref.watch(settingsProvider.select((value) => value.defaultUriScheme)); - final code = dartHttpCodeGen.getCode(activeRequestModel!, defaultUriScheme); + final code = codegen.getCode(ref, activeRequestModel!, defaultUriScheme); if (code == null) { return const ErrorMessage( message: "An error was encountered while generating code. $kRaiseIssue", diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 00000000..3a3f03aa --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:apidash/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} From a3ad56cb5377bfbd74fb7c05543c5a47ad9c5ee4 Mon Sep 17 00:00:00 2001 From: mmjsmohit Date: Thu, 1 Jun 2023 22:15:46 +0530 Subject: [PATCH 3/7] Added Kotlin OkHttp Tests and refactoring. --- lib/codegen/codegen.dart | 11 +- lib/codegen/kotlin/pkg_okhttp.dart | 2 + lib/consts.dart | 4 +- .../editor_pane/details_card/code_pane.dart | 88 +++++++++++- lib/widgets/codegen_previewer.dart | 101 -------------- test/codegen/kotlin_okhttp_codegen_test.dart | 125 ++++++++++++++++++ test/widget_test.dart | 30 ----- test/widgets/codegen_previewer_test.dart | 1 + 8 files changed, 219 insertions(+), 143 deletions(-) create mode 100644 test/codegen/kotlin_okhttp_codegen_test.dart delete mode 100644 test/widget_test.dart diff --git a/lib/codegen/codegen.dart b/lib/codegen/codegen.dart index 414fded1..94041bd0 100644 --- a/lib/codegen/codegen.dart +++ b/lib/codegen/codegen.dart @@ -8,21 +8,16 @@ import '../providers/settings_providers.dart'; import 'dart/pkg_http.dart'; class Codegen { - const Codegen({required this.codegenLanguage}); - final CodegenLanguage codegenLanguage; String? getCode( - WidgetRef ref, + CodegenLanguage codegenLanguage, RequestModel requestModel, String defaultUriScheme, ) { - final activeRequestModel = ref.watch(activeRequestModelProvider); - final defaultUriScheme = - ref.watch(settingsProvider.select((value) => value.defaultUriScheme)); switch (codegenLanguage) { case CodegenLanguage.dartHttp: - return DartHttpCodeGen().getCode(activeRequestModel!, defaultUriScheme); + return DartHttpCodeGen().getCode(requestModel, defaultUriScheme); case CodegenLanguage.kotlinOkHttp: - return KotlinOkHttpCodeGen().getCode(activeRequestModel!); + return KotlinOkHttpCodeGen().getCode(requestModel); default: throw ArgumentError('Invalid codegenLanguage'); } diff --git a/lib/codegen/kotlin/pkg_okhttp.dart b/lib/codegen/kotlin/pkg_okhttp.dart index 365efb3c..260b00d2 100644 --- a/lib/codegen/kotlin/pkg_okhttp.dart +++ b/lib/codegen/kotlin/pkg_okhttp.dart @@ -69,6 +69,8 @@ val body = "${requestModel.requestBody}".toRequestBody(mediaType)\n"""; 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"""; diff --git a/lib/consts.dart b/lib/consts.dart index ecdc8515..5c205f86 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -229,8 +229,8 @@ const kDefaultHttpMethod = HTTPVerb.get; const kDefaultContentType = ContentType.json; enum CodegenLanguage { - dartHttp("Dart - http"), - kotlinOkHttp("Kotlin - OkHttp"); + dartHttp("Dart (http)"), + kotlinOkHttp("Kotlin (OkHttp)"); const CodegenLanguage(this.label); final String label; diff --git a/lib/screens/home_page/editor_pane/details_card/code_pane.dart b/lib/screens/home_page/editor_pane/details_card/code_pane.dart index da649415..739fef61 100644 --- a/lib/screens/home_page/editor_pane/details_card/code_pane.dart +++ b/lib/screens/home_page/editor_pane/details_card/code_pane.dart @@ -1,3 +1,4 @@ +import 'package:apidash/utils/convert_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:apidash/providers/providers.dart'; @@ -13,6 +14,8 @@ class CodePane extends ConsumerStatefulWidget { } class _CodePaneState extends ConsumerState { + final Codegen codegen = Codegen(); + @override void initState() { super.initState(); @@ -22,11 +25,13 @@ class _CodePaneState extends ConsumerState { Widget build(BuildContext context) { final CodegenLanguage codegenLanguage = ref.watch(codegenLanguageStateProvider); - final Codegen codegen = Codegen(codegenLanguage: codegenLanguage); + final activeRequestModel = ref.watch(activeRequestModelProvider); final defaultUriScheme = ref.watch(settingsProvider.select((value) => value.defaultUriScheme)); - final code = codegen.getCode(ref, activeRequestModel!, defaultUriScheme); + + final code = + codegen.getCode(codegenLanguage, activeRequestModel!, defaultUriScheme); if (code == null) { return const ErrorMessage( message: "An error was encountered while generating code. $kRaiseIssue", @@ -37,3 +42,82 @@ class _CodePaneState extends ConsumerState { ); } } + +class ViewCodePane extends ConsumerStatefulWidget { + const ViewCodePane({ + super.key, + required this.code, + }); + + final String code; + + @override + ConsumerState createState() => _ViewCodePaneState(); +} + +class _ViewCodePaneState extends ConsumerState { + @override + Widget build(BuildContext context) { + var codeTheme = Theme.of(context).brightness == Brightness.light + ? kLightCodeTheme + : kDarkCodeTheme; + final textContainerdecoration = BoxDecoration( + color: Color.alphaBlend( + (Theme.of(context).brightness == Brightness.dark + ? Theme.of(context).colorScheme.onPrimaryContainer + : Theme.of(context).colorScheme.primaryContainer) + .withOpacity(kForegroundOpacity), + Theme.of(context).colorScheme.surface), + border: Border.all(color: Theme.of(context).colorScheme.surfaceVariant), + borderRadius: kBorderRadius8, + ); + + return Padding( + padding: kP10, + child: Column( + children: [ + SizedBox( + height: kHeaderHeight, + child: Row( + children: [ + Expanded( + child: Text( + "Code", + style: Theme.of(context).textTheme.titleMedium, + ), + ), + DropdownButtonCodegenLanguage( + codegenLanguage: ref.watch(codegenLanguageStateProvider), + onChanged: (CodegenLanguage? value) { + ref + .read(codegenLanguageStateProvider.notifier) + .update((state) => value!); + }, + ), + CopyButton(toCopy: widget.code), + SaveInDownloadsButton( + content: stringToBytes(widget.code), + mimeType: "application/vnd.dart", + ) + ], + ), + ), + kVSpacer10, + Expanded( + child: Container( + width: double.maxFinite, + padding: kP8, + decoration: textContainerdecoration, + child: CodeGenPreviewer( + code: widget.code, + theme: codeTheme, + language: 'dart', + textStyle: kCodeStyle, + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/codegen_previewer.dart b/lib/widgets/codegen_previewer.dart index ef392586..489f60fe 100644 --- a/lib/widgets/codegen_previewer.dart +++ b/lib/widgets/codegen_previewer.dart @@ -1,12 +1,6 @@ -import 'package:apidash/widgets/dropdowns.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:highlighter/highlighter.dart' show highlight; -import 'package:apidash/consts.dart'; -import 'package:apidash/utils/utils.dart'; -import '../providers/ui_providers.dart'; import 'code_previewer.dart' show convert; -import 'buttons.dart'; class CodeGenPreviewer extends StatefulWidget { const CodeGenPreviewer({ @@ -103,98 +97,3 @@ List generateSpans( var spans = convert(parsed.nodes!, theme); return spans; } - -class ViewCodePane extends StatefulWidget { - const ViewCodePane({ - super.key, - required this.code, - }); - - final String code; - - @override - State createState() => _ViewCodePaneState(); -} - -class _ViewCodePaneState extends State { - @override - Widget build(BuildContext context) { - var codeTheme = Theme.of(context).brightness == Brightness.light - ? kLightCodeTheme - : kDarkCodeTheme; - final textContainerdecoration = BoxDecoration( - color: Color.alphaBlend( - (Theme.of(context).brightness == Brightness.dark - ? Theme.of(context).colorScheme.onPrimaryContainer - : Theme.of(context).colorScheme.primaryContainer) - .withOpacity(kForegroundOpacity), - Theme.of(context).colorScheme.surface), - border: Border.all(color: Theme.of(context).colorScheme.surfaceVariant), - borderRadius: kBorderRadius8, - ); - - return Padding( - padding: kP10, - child: Column( - children: [ - SizedBox( - height: kHeaderHeight, - child: Row( - children: [ - Expanded( - child: Text( - "Code", - style: Theme.of(context).textTheme.titleMedium, - ), - ), - const DropdownButtonCodeCodegenLanguage(), - CopyButton(toCopy: widget.code), - SaveInDownloadsButton( - content: stringToBytes(widget.code), - mimeType: "application/vnd.dart", - ) - ], - ), - ), - kVSpacer10, - Expanded( - child: Container( - width: double.maxFinite, - padding: kP8, - decoration: textContainerdecoration, - child: CodeGenPreviewer( - code: widget.code, - theme: codeTheme, - language: 'dart', - textStyle: kCodeStyle, - ), - ), - ), - ], - ), - ); - } -} - -class DropdownButtonCodeCodegenLanguage extends ConsumerStatefulWidget { - const DropdownButtonCodeCodegenLanguage({ - super.key, - }); - - @override - ConsumerState createState() => _DropdownButtonCodeCodegenLanguageState(); -} - -class _DropdownButtonCodeCodegenLanguageState - extends ConsumerState { - @override - Widget build(BuildContext context) { - final requestCodeLanguage = ref.watch(codegenLanguageStateProvider); - return DropdownButtonCodegenLanguage( - codegenLanguage: requestCodeLanguage, - onChanged: (CodegenLanguage? value) { - ref.read(codegenLanguageStateProvider.notifier).state = value!; - }, - ); - } -} \ No newline at end of file diff --git a/test/codegen/kotlin_okhttp_codegen_test.dart b/test/codegen/kotlin_okhttp_codegen_test.dart new file mode 100644 index 00000000..8b9654a7 --- /dev/null +++ b/test/codegen/kotlin_okhttp_codegen_test.dart @@ -0,0 +1,125 @@ +import 'package:apidash/codegen/kotlin/pkg_okhttp.dart'; +import 'package:apidash/models/request_model.dart'; +import 'package:test/test.dart'; +import 'package:apidash/consts.dart'; + +void main() { + group('KotlinOkHttpCodeGen', () { + final kotlinOkHttpCodeGen = KotlinOkHttpCodeGen(); + + test('getCode returns valid code for GET request', () { + const requestModel = RequestModel( + url: 'https://jsonplaceholder.typicode.com/todos/1', + method: HTTPVerb.get, + id: '', + ); + const expectedCode = """import okhttp3.MediaType.Companion.toMediaType +import okhttp3.MultipartBody +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://jsonplaceholder.typicode.com/todos/1") + .build() +val response = client.newCall(request).execute() + +println(response.body!!.string()) +"""; + expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode); + }); + + test('getCode returns valid code for POST request', () { + const requestModel = RequestModel( + url: 'https://jsonplaceholder.typicode.com/posts', + method: HTTPVerb.post, + requestBody: '{"title": "foo","body": "bar","userId": 1}', + requestBodyContentType: ContentType.json, + id: '1', + ); + const expectedCode = """import okhttp3.MediaType.Companion.toMediaType +import okhttp3.MultipartBody +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 mediaType = "application/json".toMediaType() +val body = "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}".toRequestBody(mediaType) +val request = Request.Builder() + .url("https://jsonplaceholder.typicode.com/posts") + .post(body) + .build() +val response = client.newCall(request).execute() + +println(response.body!!.string()) +"""; + expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode); + }); + + test('getCode returns valid code for DELETE request', () { + const requestModel = RequestModel( + url: 'https://jsonplaceholder.typicode.com/posts/1', + method: HTTPVerb.delete, + requestBody: '{"title": "foo","body": "bar","userId": 1}', + requestBodyContentType: ContentType.json, + id: '1', + ); + const expectedCode = """import okhttp3.MediaType.Companion.toMediaType +import okhttp3.MultipartBody +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 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() + +println(response.body!!.string()) +"""; + expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode); + }); + + test('getCode returns valid code for HEAD request', () { + const requestModel = RequestModel( + url: 'https://jsonplaceholder.typicode.com/posts/1', + method: HTTPVerb.head, + id: '1', + ); + const expectedCode = """import okhttp3.MediaType.Companion.toMediaType +import okhttp3.MultipartBody +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://jsonplaceholder.typicode.com/posts/1") + .head() + .build() +val response = client.newCall(request).execute() + +println(response.body!!.string()) +"""; + expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode); + }); + }); +} diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index 3a3f03aa..00000000 --- a/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:apidash/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/test/widgets/codegen_previewer_test.dart b/test/widgets/codegen_previewer_test.dart index c5db6728..70f0b56b 100644 --- a/test/widgets/codegen_previewer_test.dart +++ b/test/widgets/codegen_previewer_test.dart @@ -1,3 +1,4 @@ +import 'package:apidash/screens/home_page/editor_pane/details_card/code_pane.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:apidash/widgets/codegen_previewer.dart'; From 8235d208c347a05544524b32ec0aaa725ddec814 Mon Sep 17 00:00:00 2001 From: mmjsmohit Date: Sun, 4 Jun 2023 15:42:47 +0530 Subject: [PATCH 4/7] Added Query, Header tests and Refactoring --- lib/codegen/codegen.dart | 5 +-- .../editor_pane/details_card/code_pane.dart | 24 +++++++----- test/codegen/kotlin_okhttp_codegen_test.dart | 39 +++++++++++++++++++ test/widgets/codegen_previewer_test.dart | 4 ++ 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/lib/codegen/codegen.dart b/lib/codegen/codegen.dart index 94041bd0..eb5bb0ef 100644 --- a/lib/codegen/codegen.dart +++ b/lib/codegen/codegen.dart @@ -1,10 +1,7 @@ import 'package:apidash/codegen/kotlin/pkg_okhttp.dart'; import 'package:apidash/consts.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../models/request_model.dart'; -import '../providers/collection_providers.dart'; -import '../providers/settings_providers.dart'; +import 'package:apidash/models/models.dart' show RequestModel; import 'dart/pkg_http.dart'; class Codegen { diff --git a/lib/screens/home_page/editor_pane/details_card/code_pane.dart b/lib/screens/home_page/editor_pane/details_card/code_pane.dart index 739fef61..f894eacf 100644 --- a/lib/screens/home_page/editor_pane/details_card/code_pane.dart +++ b/lib/screens/home_page/editor_pane/details_card/code_pane.dart @@ -39,23 +39,33 @@ class _CodePaneState extends ConsumerState { } return ViewCodePane( code: code, + codegenLanguage: codegenLanguage, + onChangedCodegenLanguage: (CodegenLanguage? value) { + ref + .read(codegenLanguageStateProvider.notifier) + .update((state) => value!); + }, ); } } -class ViewCodePane extends ConsumerStatefulWidget { +class ViewCodePane extends StatefulWidget { const ViewCodePane({ super.key, required this.code, + required this.codegenLanguage, + required this.onChangedCodegenLanguage, }); final String code; + final CodegenLanguage codegenLanguage; + final Function(CodegenLanguage?) onChangedCodegenLanguage; @override - ConsumerState createState() => _ViewCodePaneState(); + State createState() => _ViewCodePaneState(); } -class _ViewCodePaneState extends ConsumerState { +class _ViewCodePaneState extends State { @override Widget build(BuildContext context) { var codeTheme = Theme.of(context).brightness == Brightness.light @@ -87,12 +97,8 @@ class _ViewCodePaneState extends ConsumerState { ), ), DropdownButtonCodegenLanguage( - codegenLanguage: ref.watch(codegenLanguageStateProvider), - onChanged: (CodegenLanguage? value) { - ref - .read(codegenLanguageStateProvider.notifier) - .update((state) => value!); - }, + codegenLanguage: widget.codegenLanguage, + onChanged: widget.onChangedCodegenLanguage, ), CopyButton(toCopy: widget.code), SaveInDownloadsButton( diff --git a/test/codegen/kotlin_okhttp_codegen_test.dart b/test/codegen/kotlin_okhttp_codegen_test.dart index 8b9654a7..0dae2b22 100644 --- a/test/codegen/kotlin_okhttp_codegen_test.dart +++ b/test/codegen/kotlin_okhttp_codegen_test.dart @@ -1,4 +1,5 @@ import 'package:apidash/codegen/kotlin/pkg_okhttp.dart'; +import 'package:apidash/models/kvrow_model.dart'; import 'package:apidash/models/request_model.dart'; import 'package:test/test.dart'; import 'package:apidash/consts.dart'; @@ -117,6 +118,44 @@ val request = Request.Builder() .build() val response = client.newCall(request).execute() +println(response.body!!.string()) +"""; + expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode); + }); + + test( + 'getCode returns valid code for requests with headers and query parameters', + () { + const requestModel = RequestModel( + url: 'https://jsonplaceholder.typicode.com/posts', + method: HTTPVerb.get, + requestParams: [ + KVRow('userId', 1), + ], + requestHeaders: [ + KVRow('Custom-Header-1', 'Value-1'), + KVRow('Custom-Header-2', 'Value-2') + ], + id: '1', + ); + const expectedCode = """import okhttp3.MediaType.Companion.toMediaType +import okhttp3.MultipartBody +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://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() + println(response.body!!.string()) """; expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode); diff --git a/test/widgets/codegen_previewer_test.dart b/test/widgets/codegen_previewer_test.dart index 70f0b56b..a5b8e03c 100644 --- a/test/widgets/codegen_previewer_test.dart +++ b/test/widgets/codegen_previewer_test.dart @@ -59,6 +59,8 @@ void main() async { Expanded( child: ViewCodePane( code: code, + codegenLanguage: CodegenLanguage.dartHttp, + onChangedCodegenLanguage: (p0) {}, ), ), ], @@ -85,6 +87,8 @@ void main() async { Expanded( child: ViewCodePane( code: code, + codegenLanguage: CodegenLanguage.dartHttp, + onChangedCodegenLanguage: (p0) {}, ), ), ], From 1537152cab188594c96525f4a34f7cc577e63542 Mon Sep 17 00:00:00 2001 From: mmjsmohit Date: Mon, 5 Jun 2023 09:36:35 +0530 Subject: [PATCH 5/7] Refactoring --- .../editor_pane/details_card/code_pane.dart | 80 ----------------- lib/widgets/view_codepane.dart | 89 +++++++++++++++++++ lib/widgets/widgets.dart | 1 + test/codegen/kotlin_okhttp_codegen_test.dart | 3 +- test/widgets/codegen_previewer_test.dart | 4 +- 5 files changed, 93 insertions(+), 84 deletions(-) create mode 100644 lib/widgets/view_codepane.dart diff --git a/lib/screens/home_page/editor_pane/details_card/code_pane.dart b/lib/screens/home_page/editor_pane/details_card/code_pane.dart index f894eacf..ee54c8da 100644 --- a/lib/screens/home_page/editor_pane/details_card/code_pane.dart +++ b/lib/screens/home_page/editor_pane/details_card/code_pane.dart @@ -1,4 +1,3 @@ -import 'package:apidash/utils/convert_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:apidash/providers/providers.dart'; @@ -48,82 +47,3 @@ class _CodePaneState extends ConsumerState { ); } } - -class ViewCodePane extends StatefulWidget { - const ViewCodePane({ - super.key, - required this.code, - required this.codegenLanguage, - required this.onChangedCodegenLanguage, - }); - - final String code; - final CodegenLanguage codegenLanguage; - final Function(CodegenLanguage?) onChangedCodegenLanguage; - - @override - State createState() => _ViewCodePaneState(); -} - -class _ViewCodePaneState extends State { - @override - Widget build(BuildContext context) { - var codeTheme = Theme.of(context).brightness == Brightness.light - ? kLightCodeTheme - : kDarkCodeTheme; - final textContainerdecoration = BoxDecoration( - color: Color.alphaBlend( - (Theme.of(context).brightness == Brightness.dark - ? Theme.of(context).colorScheme.onPrimaryContainer - : Theme.of(context).colorScheme.primaryContainer) - .withOpacity(kForegroundOpacity), - Theme.of(context).colorScheme.surface), - border: Border.all(color: Theme.of(context).colorScheme.surfaceVariant), - borderRadius: kBorderRadius8, - ); - - return Padding( - padding: kP10, - child: Column( - children: [ - SizedBox( - height: kHeaderHeight, - child: Row( - children: [ - Expanded( - child: Text( - "Code", - style: Theme.of(context).textTheme.titleMedium, - ), - ), - DropdownButtonCodegenLanguage( - codegenLanguage: widget.codegenLanguage, - onChanged: widget.onChangedCodegenLanguage, - ), - CopyButton(toCopy: widget.code), - SaveInDownloadsButton( - content: stringToBytes(widget.code), - mimeType: "application/vnd.dart", - ) - ], - ), - ), - kVSpacer10, - Expanded( - child: Container( - width: double.maxFinite, - padding: kP8, - decoration: textContainerdecoration, - child: CodeGenPreviewer( - code: widget.code, - theme: codeTheme, - language: 'dart', - textStyle: kCodeStyle, - ), - ), - ), - ], - ), - ); - } -} diff --git a/lib/widgets/view_codepane.dart b/lib/widgets/view_codepane.dart new file mode 100644 index 00000000..6123523d --- /dev/null +++ b/lib/widgets/view_codepane.dart @@ -0,0 +1,89 @@ +import 'package:apidash/consts.dart'; +import 'package:flutter/material.dart'; + +import '../utils/utils.dart'; +import 'widgets.dart' + show + DropdownButtonCodegenLanguage, + CopyButton, + SaveInDownloadsButton, + CodeGenPreviewer; + +class ViewCodePane extends StatefulWidget { + const ViewCodePane({ + super.key, + required this.code, + required this.codegenLanguage, + required this.onChangedCodegenLanguage, + }); + + final String code; + final CodegenLanguage codegenLanguage; + final Function(CodegenLanguage?) onChangedCodegenLanguage; + + @override + State createState() => _ViewCodePaneState(); +} + +class _ViewCodePaneState extends State { + @override + Widget build(BuildContext context) { + var codeTheme = Theme.of(context).brightness == Brightness.light + ? kLightCodeTheme + : kDarkCodeTheme; + final textContainerdecoration = BoxDecoration( + color: Color.alphaBlend( + (Theme.of(context).brightness == Brightness.dark + ? Theme.of(context).colorScheme.onPrimaryContainer + : Theme.of(context).colorScheme.primaryContainer) + .withOpacity(kForegroundOpacity), + Theme.of(context).colorScheme.surface), + border: Border.all(color: Theme.of(context).colorScheme.surfaceVariant), + borderRadius: kBorderRadius8, + ); + + return Padding( + padding: kP10, + child: Column( + children: [ + SizedBox( + height: kHeaderHeight, + child: Row( + children: [ + Expanded( + child: Text( + "Code", + style: Theme.of(context).textTheme.titleMedium, + ), + ), + DropdownButtonCodegenLanguage( + codegenLanguage: widget.codegenLanguage, + onChanged: widget.onChangedCodegenLanguage, + ), + CopyButton(toCopy: widget.code), + SaveInDownloadsButton( + content: stringToBytes(widget.code), + mimeType: "application/vnd.dart", + ) + ], + ), + ), + kVSpacer10, + Expanded( + child: Container( + width: double.maxFinite, + padding: kP8, + decoration: textContainerdecoration, + child: CodeGenPreviewer( + code: widget.code, + theme: codeTheme, + language: 'dart', + textStyle: kCodeStyle, + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/widgets.dart b/lib/widgets/widgets.dart index 75426ad1..cd71b3c4 100644 --- a/lib/widgets/widgets.dart +++ b/lib/widgets/widgets.dart @@ -16,3 +16,4 @@ export 'request_widgets.dart'; export 'response_widgets.dart'; export 'snackbars.dart'; export 'markdown.dart'; +export 'view_codepane.dart'; diff --git a/test/codegen/kotlin_okhttp_codegen_test.dart b/test/codegen/kotlin_okhttp_codegen_test.dart index 0dae2b22..2582c4ae 100644 --- a/test/codegen/kotlin_okhttp_codegen_test.dart +++ b/test/codegen/kotlin_okhttp_codegen_test.dart @@ -1,6 +1,5 @@ import 'package:apidash/codegen/kotlin/pkg_okhttp.dart'; -import 'package:apidash/models/kvrow_model.dart'; -import 'package:apidash/models/request_model.dart'; +import 'package:apidash/models/models.dart' show KVRow, RequestModel; import 'package:test/test.dart'; import 'package:apidash/consts.dart'; diff --git a/test/widgets/codegen_previewer_test.dart b/test/widgets/codegen_previewer_test.dart index a5b8e03c..4b51aa8a 100644 --- a/test/widgets/codegen_previewer_test.dart +++ b/test/widgets/codegen_previewer_test.dart @@ -1,7 +1,7 @@ -import 'package:apidash/screens/home_page/editor_pane/details_card/code_pane.dart'; +import 'package:apidash/widgets/widgets.dart' + show ViewCodePane, CodeGenPreviewer; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:apidash/widgets/codegen_previewer.dart'; import 'package:apidash/consts.dart'; import '../test_consts.dart'; From 7ca2f59c0e50ca3c8eff25d65896179867f95561 Mon Sep 17 00:00:00 2001 From: mmjsmohit Date: Mon, 5 Jun 2023 15:33:50 +0530 Subject: [PATCH 6/7] Refactoring --- lib/widgets/codegen_previewer.dart | 85 +++++++++++++++++++++++++++- lib/widgets/view_codepane.dart | 89 ------------------------------ lib/widgets/widgets.dart | 1 - 3 files changed, 84 insertions(+), 91 deletions(-) delete mode 100644 lib/widgets/view_codepane.dart diff --git a/lib/widgets/codegen_previewer.dart b/lib/widgets/codegen_previewer.dart index 489f60fe..86202388 100644 --- a/lib/widgets/codegen_previewer.dart +++ b/lib/widgets/codegen_previewer.dart @@ -1,6 +1,10 @@ +import 'package:apidash/consts.dart'; +import 'package:apidash/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:highlighter/highlighter.dart' show highlight; -import 'code_previewer.dart' show convert; +import 'code_previewer.dart'; +import 'widgets.dart' + show CopyButton, DropdownButtonCodegenLanguage, SaveInDownloadsButton; class CodeGenPreviewer extends StatefulWidget { const CodeGenPreviewer({ @@ -97,3 +101,82 @@ List generateSpans( var spans = convert(parsed.nodes!, theme); return spans; } + +class ViewCodePane extends StatefulWidget { + const ViewCodePane({ + super.key, + required this.code, + required this.codegenLanguage, + required this.onChangedCodegenLanguage, + }); + + final String code; + final CodegenLanguage codegenLanguage; + final Function(CodegenLanguage?) onChangedCodegenLanguage; + + @override + State createState() => _ViewCodePaneState(); +} + +class _ViewCodePaneState extends State { + @override + Widget build(BuildContext context) { + var codeTheme = Theme.of(context).brightness == Brightness.light + ? kLightCodeTheme + : kDarkCodeTheme; + final textContainerdecoration = BoxDecoration( + color: Color.alphaBlend( + (Theme.of(context).brightness == Brightness.dark + ? Theme.of(context).colorScheme.onPrimaryContainer + : Theme.of(context).colorScheme.primaryContainer) + .withOpacity(kForegroundOpacity), + Theme.of(context).colorScheme.surface), + border: Border.all(color: Theme.of(context).colorScheme.surfaceVariant), + borderRadius: kBorderRadius8, + ); + + return Padding( + padding: kP10, + child: Column( + children: [ + SizedBox( + height: kHeaderHeight, + child: Row( + children: [ + Expanded( + child: Text( + "Code", + style: Theme.of(context).textTheme.titleMedium, + ), + ), + DropdownButtonCodegenLanguage( + codegenLanguage: widget.codegenLanguage, + onChanged: widget.onChangedCodegenLanguage, + ), + CopyButton(toCopy: widget.code), + SaveInDownloadsButton( + content: stringToBytes(widget.code), + mimeType: "application/vnd.dart", + ) + ], + ), + ), + kVSpacer10, + Expanded( + child: Container( + width: double.maxFinite, + padding: kP8, + decoration: textContainerdecoration, + child: CodeGenPreviewer( + code: widget.code, + theme: codeTheme, + language: 'dart', + textStyle: kCodeStyle, + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/view_codepane.dart b/lib/widgets/view_codepane.dart deleted file mode 100644 index 6123523d..00000000 --- a/lib/widgets/view_codepane.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'package:apidash/consts.dart'; -import 'package:flutter/material.dart'; - -import '../utils/utils.dart'; -import 'widgets.dart' - show - DropdownButtonCodegenLanguage, - CopyButton, - SaveInDownloadsButton, - CodeGenPreviewer; - -class ViewCodePane extends StatefulWidget { - const ViewCodePane({ - super.key, - required this.code, - required this.codegenLanguage, - required this.onChangedCodegenLanguage, - }); - - final String code; - final CodegenLanguage codegenLanguage; - final Function(CodegenLanguage?) onChangedCodegenLanguage; - - @override - State createState() => _ViewCodePaneState(); -} - -class _ViewCodePaneState extends State { - @override - Widget build(BuildContext context) { - var codeTheme = Theme.of(context).brightness == Brightness.light - ? kLightCodeTheme - : kDarkCodeTheme; - final textContainerdecoration = BoxDecoration( - color: Color.alphaBlend( - (Theme.of(context).brightness == Brightness.dark - ? Theme.of(context).colorScheme.onPrimaryContainer - : Theme.of(context).colorScheme.primaryContainer) - .withOpacity(kForegroundOpacity), - Theme.of(context).colorScheme.surface), - border: Border.all(color: Theme.of(context).colorScheme.surfaceVariant), - borderRadius: kBorderRadius8, - ); - - return Padding( - padding: kP10, - child: Column( - children: [ - SizedBox( - height: kHeaderHeight, - child: Row( - children: [ - Expanded( - child: Text( - "Code", - style: Theme.of(context).textTheme.titleMedium, - ), - ), - DropdownButtonCodegenLanguage( - codegenLanguage: widget.codegenLanguage, - onChanged: widget.onChangedCodegenLanguage, - ), - CopyButton(toCopy: widget.code), - SaveInDownloadsButton( - content: stringToBytes(widget.code), - mimeType: "application/vnd.dart", - ) - ], - ), - ), - kVSpacer10, - Expanded( - child: Container( - width: double.maxFinite, - padding: kP8, - decoration: textContainerdecoration, - child: CodeGenPreviewer( - code: widget.code, - theme: codeTheme, - language: 'dart', - textStyle: kCodeStyle, - ), - ), - ), - ], - ), - ); - } -} diff --git a/lib/widgets/widgets.dart b/lib/widgets/widgets.dart index a6f8f859..b259e7bf 100644 --- a/lib/widgets/widgets.dart +++ b/lib/widgets/widgets.dart @@ -16,5 +16,4 @@ export 'request_widgets.dart'; export 'response_widgets.dart'; export 'snackbars.dart'; export 'markdown.dart'; -export 'view_codepane.dart'; export 'uint8_audio_player.dart'; From 49fdcc9ac6c0e50579855b011a330ce0a03be168 Mon Sep 17 00:00:00 2001 From: mmjsmohit Date: Mon, 5 Jun 2023 17:44:15 +0530 Subject: [PATCH 7/7] Rafactoring `codegen_previewer.dart` --- lib/widgets/codegen_previewer.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/widgets/codegen_previewer.dart b/lib/widgets/codegen_previewer.dart index 86202388..5833d547 100644 --- a/lib/widgets/codegen_previewer.dart +++ b/lib/widgets/codegen_previewer.dart @@ -1,7 +1,7 @@ -import 'package:apidash/consts.dart'; -import 'package:apidash/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:highlighter/highlighter.dart' show highlight; +import 'package:apidash/consts.dart'; +import 'package:apidash/utils/utils.dart'; import 'code_previewer.dart'; import 'widgets.dart' show CopyButton, DropdownButtonCodegenLanguage, SaveInDownloadsButton;