From 9cdfafbd9cb40ff034849e6b4c08477a7008de82 Mon Sep 17 00:00:00 2001 From: Apoorv Dwivedi Date: Sat, 9 Mar 2024 20:57:18 +0530 Subject: [PATCH 01/39] Added code generator for curl-rust --- lib/codegen/codegen.dart | 3 + lib/codegen/rust/curl-rust.dart | 113 ++++++++++++++++++++++++++++++++ lib/consts.dart | 4 +- 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 lib/codegen/rust/curl-rust.dart diff --git a/lib/codegen/codegen.dart b/lib/codegen/codegen.dart index 1aec7144..db4334a0 100644 --- a/lib/codegen/codegen.dart +++ b/lib/codegen/codegen.dart @@ -1,3 +1,4 @@ +import 'package:apidash/codegen/rust/curl-rust.dart'; import 'package:apidash/models/models.dart' show RequestModel; import 'package:apidash/consts.dart'; import 'dart/http.dart'; @@ -42,6 +43,8 @@ class Codegen { .getCode(requestModel, defaultUriScheme); case CodegenLanguage.pythonRequests: return PythonRequestsCodeGen().getCode(requestModel, defaultUriScheme); + case CodegenLanguage.rustCurl: + return RustCurlCodeGen().getCode(requestModel, defaultUriScheme); } } } diff --git a/lib/codegen/rust/curl-rust.dart b/lib/codegen/rust/curl-rust.dart new file mode 100644 index 00000000..9d775b74 --- /dev/null +++ b/lib/codegen/rust/curl-rust.dart @@ -0,0 +1,113 @@ +import 'dart:convert'; +import 'package:jinja/jinja.dart' as jj; +import 'package:apidash/utils/utils.dart' + show getNewUuid, getValidRequestUri, padMultilineString; +import 'package:apidash/models/models.dart' show RequestModel; +import 'package:apidash/consts.dart'; + +class RustCurlCodeGen { + final String kTemplateStart = """use curl::easy::Easy; +use serde_json::json; + +fn main() { +"""; + + String kTemplateUrl = """ + let mut easy = Easy::new(); + easy.url("{{url}}").unwrap(); + """; + + String kTemplateMethod = """easy.{{method}}(true).unwrap(); + """; + + String kTemplateRawBody = + """easy.post_field_copy({{body}}.as_bytes()).unwrap(); + """; + + String kTemplateJsonBody = + """easy.post_fields_copy(json!({{body}}).to_string().as_bytes()).unwrap(); + """; + + String kTemplateFormData = """ + let mut form = curl::easy::Form::new(); + {% for field in fields %} + form.part("{{field.name}}") + {% if field.type == "file" %}.file("{{field.value}}"){% else %}.contents(b"{{field.value}}"){% endif %} + .add(); + {% endfor %} + easy.httppost(form); + """; + + String kTemplateHeader = """easy.header("{{header}}", "{{value}}"); + """; + + final String kTemplateEnd = """easy.perform().unwrap(); +} +"""; + + String? getCode( + RequestModel requestModel, + String defaultUriScheme, + ) { + try { + String result = ""; + + String url = requestModel.url; + if (!url.contains("://") && url.isNotEmpty) { + url = "$defaultUriScheme://$url"; + } + + result += kTemplateStart; + + var rec = getValidRequestUri( + url, + requestModel.enabledRequestParams, + ); + + Uri? uri = rec.$1; + + if (uri != null) { + var templateUrl = jj.Template(kTemplateUrl); + result += templateUrl.render({"url": uri.toString()}); + + var method = requestModel.method; + var templateMethod = jj.Template(kTemplateMethod); + result += templateMethod.render({"method": method.name.toLowerCase()}); + + var requestBody = requestModel.requestBody; + if (kMethodsWithBody.contains(method) && requestBody != null) { + if (requestModel.requestBodyContentType == ContentType.text) { + var templateRawBody = jj.Template(kTemplateRawBody); + result += templateRawBody.render({"body": requestBody}); + } else if (requestModel.requestBodyContentType == ContentType.json) { + var templateJsonBody = jj.Template(kTemplateJsonBody); + result += templateJsonBody.render({"body": requestBody}); + } else if (requestModel.isFormDataRequest) { + var templateFormData = jj.Template(kTemplateFormData); + result += templateFormData.render({ + "fields": requestModel.formDataMapList, + }); + } + } + + var headersList = requestModel.enabledRequestHeaders; + if (headersList != null) { + var headers = requestModel.enabledHeadersMap; + for (var header in headers.keys) { + var templateHeader = jj.Template(kTemplateHeader); + result += templateHeader.render({ + "header": header, + "value": headers[header], + }); + } + } + + result += kTemplateEnd; + } + + return result; + } catch (e) { + return null; + } + } +} diff --git a/lib/consts.dart b/lib/consts.dart index d157b7fa..124e98c7 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -271,7 +271,9 @@ enum CodegenLanguage { nodejsFetch("node.js (fetch)", "javascript", "js"), kotlinOkHttp("Kotlin (okhttp3)", "java", "kt"), pythonHttpClient("Python (http.client)", "python", "py"), - pythonRequests("Python (requests)", "python", "py"); + pythonRequests("Python (requests)", "python", "py"), + rustCurl("Rust (curl-rust)", "rust", "rs"); + const CodegenLanguage(this.label, this.codeHighlightLang, this.ext); final String label; From 4dad60d937022b8ae4085b3e3470dcf58b74629f Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 01:55:28 +0100 Subject: [PATCH 02/39] Test that correct pages are displayed for navRailIndexStateProvider value --- pubspec.lock | 8 +++ pubspec.yaml | 7 +- test/providers/ui_providers_test.dart | 93 +++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 test/providers/ui_providers_test.dart diff --git a/pubspec.lock b/pubspec.lock index f48a9105..38a05b26 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -729,6 +729,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + mockito: + dependency: "direct main" + description: + name: mockito + sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" + url: "https://pub.dev" + source: hosted + version: "5.4.4" mpv_dart: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8441a0f5..b4137063 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,11 @@ name: apidash description: API Dash is a beautiful open-source cross-platform API Client built using Flutter which can help you easily create & customize your API requests, visually inspect responses and generate Dart code on the go. -publish_to: "none" +publish_to: 'none' version: 0.3.0+3 environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.7.2" + sdk: '>=3.0.0 <4.0.0' + flutter: '>=3.7.2' dependencies: flutter: @@ -55,6 +55,7 @@ dependencies: dart_style: ^2.3.4 json_text_field: ^1.1.0 csv: ^5.1.1 + mockito: ^5.4.4 dev_dependencies: flutter_test: diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart new file mode 100644 index 00000000..7b32c60a --- /dev/null +++ b/test/providers/ui_providers_test.dart @@ -0,0 +1,93 @@ +import 'dart:io'; + +import 'package:apidash/providers/providers.dart'; +import 'package:apidash/screens/dashboard.dart'; +import 'package:apidash/screens/home_page/home_page.dart'; +import 'package:apidash/screens/intro_page.dart'; +import 'package:apidash/screens/settings_page.dart'; +import 'package:apidash/services/hive_services.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +class MockPathProviderPlatform extends Mock implements MethodChannel {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() async { + const MethodChannel channel = + MethodChannel('plugins.flutter.io/path_provider'); + final MockPathProviderPlatform mock = MockPathProviderPlatform(); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'getApplicationDocumentsDirectory') { + // Create a mock app doc directory for testing + Directory tempDir = + await Directory.systemTemp.createTemp('mock_app_doc_dir'); + return tempDir.path; // Return the path to the mock directory + } + return null; + }); + await openBoxes(); + }); + + group('Testing navRailIndexStateProvider', () { + testWidgets('Dashboard should display correct initial page', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Verify that the HomePage is displayed initially + expect(find.byType(HomePage), findsOneWidget); + expect(find.byType(IntroPage), findsNothing); + expect(find.byType(SettingsPage), findsNothing); + }); + + testWidgets( + "Dashboard should display IntroPage when navRailIndexStateProvider is 1", + (WidgetTester tester) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + navRailIndexStateProvider.overrideWith((ref) => 1), + ], + child: const MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Verify that the IntroPage is displayed + expect(find.byType(IntroPage), findsOneWidget); + expect(find.byType(HomePage), findsNothing); + expect(find.byType(SettingsPage), findsNothing); + }); + testWidgets( + "Dashboard should display SettingsPage when navRailIndexStateProvider is 2", + (WidgetTester tester) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + navRailIndexStateProvider.overrideWith((ref) => 2), + ], + child: const MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Verify that the SettingsPage is displayed + expect(find.byType(SettingsPage), findsOneWidget); + expect(find.byType(IntroPage), findsNothing); + expect(find.byType(HomePage), findsNothing); + }); + }); +} From b264a7e6a4873a74ba8cf60a15b35fd60b34d52e Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 02:13:11 +0100 Subject: [PATCH 03/39] Ensure that navigation rail index update when icon button is pressed --- test/providers/ui_providers_test.dart | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index 7b32c60a..4b632113 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -89,5 +89,26 @@ void main() { expect(find.byType(IntroPage), findsNothing); expect(find.byType(HomePage), findsNothing); }); + + testWidgets( + 'Navigation Rail index should update when icon button is pressed', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Tap on the Intro icon + await tester.tap(find.byIcon(Icons.help_outline)); + await tester.pump(); + + // Verify that the navRailIndexStateProvider is updated + final dashboard = tester.element(find.byType(Dashboard)); + final container = ProviderScope.containerOf(dashboard); + expect(container.read(navRailIndexStateProvider), 1); + }); }); } From ba4bd77795356a71b27138d2d2e8b30ceee17ce1 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 02:17:22 +0100 Subject: [PATCH 04/39] Test that nav rail index persist across widget rebuilds --- test/providers/ui_providers_test.dart | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index 4b632113..3acf30df 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -110,5 +110,40 @@ void main() { final container = ProviderScope.containerOf(dashboard); expect(container.read(navRailIndexStateProvider), 1); }); + + testWidgets('Navigation Rail index should persist across widget rebuilds', + (tester) async { + // Pump the initial widget tree + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Tap on the Settings icon to change the index to 2 + await tester.tap(find.byIcon(Icons.settings_outlined)); + await tester.pump(); // Wait for the animations to complete + + // Rebuild the widget tree with the same ProviderScope + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Verify that the navRailIndexStateProvider still has the updated value + final dashboard = tester.element(find.byType(Dashboard)); + final container = ProviderScope.containerOf(dashboard); + expect(container.read(navRailIndexStateProvider), 2); + + // Verify that the SettingsPage is still displayed after the rebuild + expect(find.byType(SettingsPage), findsOneWidget); + expect(find.byType(IntroPage), findsNothing); + expect(find.byType(HomePage), findsNothing); + }); }); } From a882d5102264b76ab8d855a495c5b0cb2290e6e3 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 02:38:54 +0100 Subject: [PATCH 05/39] Test that UI updates correctly when nav rail index changes --- test/providers/ui_providers_test.dart | 35 ++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index 3acf30df..3ffe1165 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -124,7 +124,7 @@ void main() { // Tap on the Settings icon to change the index to 2 await tester.tap(find.byIcon(Icons.settings_outlined)); - await tester.pump(); // Wait for the animations to complete + await tester.pump(); // Rebuild the widget tree with the same ProviderScope await tester.pumpWidget( @@ -145,5 +145,38 @@ void main() { expect(find.byType(IntroPage), findsNothing); expect(find.byType(HomePage), findsNothing); }); + + testWidgets('UI should update correctly when Navigation Rail index changes', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Grab the Dashboard widget and its ProviderContainer + final dashboard = tester.element(find.byType(Dashboard)); + final container = ProviderScope.containerOf(dashboard); + + // Go to IntroPage + container.read(navRailIndexStateProvider.notifier).state = 1; + await tester.pump(); + + // Verify that the IntroPage is displayed + expect(find.byType(IntroPage), findsOneWidget); + // Verify that the selected icon is the filled version (selectedIcon) + expect(find.byIcon(Icons.help), findsOneWidget); + + // Go to SettingsPage + container.read(navRailIndexStateProvider.notifier).state = 2; + await tester.pump(); + + // Verify that the SettingsPage is displayed + expect(find.byType(SettingsPage), findsOneWidget); + // Verify that the selected icon is the filled version (selectedIcon) + expect(find.byIcon(Icons.settings), findsOneWidget); + }); }); } From 405decbd565a352ff776a53b2677f07113d821df Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 02:48:54 +0100 Subject: [PATCH 06/39] Test that navRailIndexStateProvider is properly disposed --- test/providers/ui_providers_test.dart | 41 +++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index 3ffe1165..d714998e 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -91,7 +91,7 @@ void main() { }); testWidgets( - 'Navigation Rail index should update when icon button is pressed', + 'navRailIndexStateProvider should update when icon button is pressed', (tester) async { await tester.pumpWidget( const ProviderScope( @@ -111,7 +111,8 @@ void main() { expect(container.read(navRailIndexStateProvider), 1); }); - testWidgets('Navigation Rail index should persist across widget rebuilds', + testWidgets( + 'navRailIndexStateProvider should persist across widget rebuilds', (tester) async { // Pump the initial widget tree await tester.pumpWidget( @@ -146,7 +147,8 @@ void main() { expect(find.byType(HomePage), findsNothing); }); - testWidgets('UI should update correctly when Navigation Rail index changes', + testWidgets( + 'UI should update correctly when navRailIndexStateProvider changes', (tester) async { await tester.pumpWidget( const ProviderScope( @@ -178,5 +180,38 @@ void main() { // Verify that the selected icon is the filled version (selectedIcon) expect(find.byIcon(Icons.settings), findsOneWidget); }); + + testWidgets( + 'navRailIndexStateProvider should be disposed when Dashboard is removed', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Grab the Dashboard widget and its ProviderContainer + final dashboard = tester.element(find.byType(Dashboard)); + final container = ProviderScope.containerOf(dashboard); + + // Pumping a different widget to remove the Dashboard from the widget tree + await tester.pumpWidget( + const MaterialApp( + home: Scaffold(body: Text('Different Widget')), + ), + ); + + // Verify that the ProviderContainer has been disposed + // by trying to read from disposed container + bool isDisposed = false; + try { + container.read(navRailIndexStateProvider); + } catch (e) { + isDisposed = true; + } + expect(isDisposed, true); + }); }); } From f71bf00dc3538a6c54bc14a7a2a37fe95e4b9a95 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 03:57:51 +0100 Subject: [PATCH 07/39] Remove mockito --- pubspec.lock | 8 -------- pubspec.yaml | 1 - test/providers/ui_providers_test.dart | 5 +---- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 38a05b26..f48a9105 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -729,14 +729,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" - mockito: - dependency: "direct main" - description: - name: mockito - sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" - url: "https://pub.dev" - source: hosted - version: "5.4.4" mpv_dart: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b4137063..9135b207 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,7 +55,6 @@ dependencies: dart_style: ^2.3.4 json_text_field: ^1.1.0 csv: ^5.1.1 - mockito: ^5.4.4 dev_dependencies: flutter_test: diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index d714998e..f69d8ab3 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -10,9 +10,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; - -class MockPathProviderPlatform extends Mock implements MethodChannel {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -20,7 +17,6 @@ void main() { setUp(() async { const MethodChannel channel = MethodChannel('plugins.flutter.io/path_provider'); - final MockPathProviderPlatform mock = MockPathProviderPlatform(); TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, (MethodCall methodCall) async { if (methodCall.method == 'getApplicationDocumentsDirectory') { @@ -70,6 +66,7 @@ void main() { expect(find.byType(HomePage), findsNothing); expect(find.byType(SettingsPage), findsNothing); }); + testWidgets( "Dashboard should display SettingsPage when navRailIndexStateProvider is 2", (WidgetTester tester) async { From a057281a6f69fc8ff46858b97d4a51a2dffdf250 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 04:07:09 +0100 Subject: [PATCH 08/39] Test that selectedIdEditStateProvider has initial value of null --- test/providers/ui_providers_test.dart | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index f69d8ab3..cc9b25c0 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:apidash/providers/providers.dart'; import 'package:apidash/screens/dashboard.dart'; +import 'package:apidash/screens/home_page/collection_pane.dart'; import 'package:apidash/screens/home_page/home_page.dart'; import 'package:apidash/screens/intro_page.dart'; import 'package:apidash/screens/settings_page.dart'; @@ -211,4 +212,23 @@ void main() { expect(isDisposed, true); }); }); + + group("Testing selectedIdEditStateProvider", () { + testWidgets( + 'selectedIdEditStateProvider should have an initial value of null', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: CollectionPane(), + ), + ), + ); + + // Verify that the initial value is null + final collectionPane = tester.element(find.byType(CollectionPane)); + final container = ProviderScope.containerOf(collectionPane); + expect(container.read(selectedIdEditStateProvider), null); + }); + }); } From 3a7ae7179639f2bf46f0c57ce49d5682eba104d1 Mon Sep 17 00:00:00 2001 From: DenserMeerkat Date: Sat, 16 Mar 2024 15:01:47 +0530 Subject: [PATCH 09/39] feat: automatic row adding --- .../request_pane/request_form_data.dart | 63 +++++++------------ .../request_pane/request_headers.dart | 59 +++++++---------- .../request_pane/request_params.dart | 58 +++++++---------- 3 files changed, 68 insertions(+), 112 deletions(-) diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart index 5d518e22..f77133e7 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart @@ -27,6 +27,8 @@ class _FormDataBodyState extends ConsumerState { @override Widget build(BuildContext context) { final selectedId = ref.watch(selectedIdStateProvider); + ref.watch(selectedRequestModelProvider + .select((value) => value?.requestFormDataList?.length)); var formRows = ref.read(selectedRequestModelProvider)?.requestFormDataList; rows = formRows == null || formRows.isEmpty ? [kFormDataEmptyModel] : formRows; @@ -47,18 +49,21 @@ class _FormDataBodyState extends ConsumerState { initialValue: rows[idx].name, hintText: " Add Key", onChanged: (value) { - rows[idx] = rows[idx].copyWith( - name: value, - ); + rows[idx] = rows[idx].copyWith(name: value); + if (idx == rows.length - 1) rows.add(kFormDataEmptyModel); _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, formDataType: rows[idx].type, onFormDataTypeChanged: (value) { + bool hasChanged = rows[idx].type != value; rows[idx] = rows[idx].copyWith( type: value ?? FormDataType.text, ); rows[idx] = rows[idx].copyWith(value: ""); + if (idx == rows.length - 1 && hasChanged) { + rows.add(kFormDataEmptyModel); + } setState(() {}); _onFieldChange(selectedId!); }, @@ -137,6 +142,7 @@ class _FormDataBodyState extends ConsumerState { hintText: " Add Value", onChanged: (value) { rows[idx] = rows[idx].copyWith(value: value); + if (idx == rows.length - 1) rows.add(kFormDataEmptyModel); _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, @@ -169,45 +175,22 @@ class _FormDataBodyState extends ConsumerState { ), ], ); - return Stack( - children: [ - Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: kBorderRadius12, - ), - margin: kP10, - child: Column( - children: [ - Expanded( - child: DaviTheme( - data: kTableThemeData, - child: Davi(daviModelRows), - ), - ), - ], - ), - ), - Align( - alignment: Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.only(bottom: 30), - child: ElevatedButton.icon( - onPressed: () { - setState(() { - rows.add(kFormDataEmptyModel); - }); - _onFieldChange(selectedId!); - }, - icon: const Icon(Icons.add), - label: const Text( - "Add Form Data", - style: kTextStyleButton, - ), + return Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: kBorderRadius12, + ), + margin: kP10, + child: Column( + children: [ + Expanded( + child: DaviTheme( + data: kTableThemeData, + child: Davi(daviModelRows), ), ), - ), - ], + ], + ), ); } diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart index 503355f8..a6524b23 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart @@ -81,7 +81,12 @@ class EditRequestHeadersState extends ConsumerState { initialValue: rows[idx].name, hintText: "Add Header Name", onChanged: (value) { + isRowEnabledList[idx] = true; rows[idx] = rows[idx].copyWith(name: value); + if (idx == rows.length - 1) { + rows.add(kNameValueEmptyModel); + isRowEnabledList.add(true); + } _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, @@ -109,6 +114,10 @@ class EditRequestHeadersState extends ConsumerState { hintText: " Add Header Value", onChanged: (value) { rows[idx] = rows[idx].copyWith(value: value); + if (idx == rows.length - 1) { + rows.add(kNameValueEmptyModel); + isRowEnabledList.add(true); + } _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, @@ -144,44 +153,22 @@ class EditRequestHeadersState extends ConsumerState { ), ], ); - return Stack( - children: [ - Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: kBorderRadius12, - ), - margin: kP10, - child: Column( - children: [ - Expanded( - child: DaviTheme( - data: kTableThemeData, - child: Davi(model), - ), - ), - ], - ), - ), - Align( - alignment: Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.only(bottom: 30), - child: ElevatedButton.icon( - onPressed: () { - rows.add(kNameValueEmptyModel); - isRowEnabledList.add(true); - _onFieldChange(selectedId!); - }, - icon: const Icon(Icons.add), - label: const Text( - "Add Header", - style: kTextStyleButton, - ), + return Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: kBorderRadius12, + ), + margin: kP10, + child: Column( + children: [ + Expanded( + child: DaviTheme( + data: kTableThemeData, + child: Davi(model), ), ), - ), - ], + ], + ), ); } } diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart index 6e3aca4b..f602200d 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart @@ -83,6 +83,10 @@ class EditRequestURLParamsState extends ConsumerState { hintText: "Add URL Parameter", onChanged: (value) { rows[idx] = rows[idx].copyWith(name: value); + if (idx == rows.length - 1) { + rows.add(kNameValueEmptyModel); + isRowEnabledList.add(true); + } _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, @@ -110,6 +114,10 @@ class EditRequestURLParamsState extends ConsumerState { hintText: "Add Value", onChanged: (value) { rows[idx] = rows[idx].copyWith(value: value); + if (idx == rows.length - 1) { + rows.add(kNameValueEmptyModel); + isRowEnabledList.add(true); + } _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, @@ -145,44 +153,22 @@ class EditRequestURLParamsState extends ConsumerState { ), ], ); - return Stack( - children: [ - Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: kBorderRadius12, - ), - margin: kP10, - child: Column( - children: [ - Expanded( - child: DaviTheme( - data: kTableThemeData, - child: Davi(model), - ), - ), - ], - ), - ), - Align( - alignment: Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.only(bottom: 30), - child: ElevatedButton.icon( - onPressed: () { - rows.add(kNameValueEmptyModel); - isRowEnabledList.add(true); - _onFieldChange(selectedId!); - }, - icon: const Icon(Icons.add), - label: const Text( - "Add Param", - style: kTextStyleButton, - ), + return Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: kBorderRadius12, + ), + margin: kP10, + child: Column( + children: [ + Expanded( + child: DaviTheme( + data: kTableThemeData, + child: Davi(model), ), ), - ), - ], + ], + ), ); } } From e42165061737b299b7597992328977cfef44d5df Mon Sep 17 00:00:00 2001 From: DenserMeerkat Date: Sat, 16 Mar 2024 16:29:35 +0530 Subject: [PATCH 10/39] fix: checkbox empty selection --- .../request_pane/request_headers.dart | 33 +++++++++++------- .../request_pane/request_params.dart | 34 ++++++++++++------- lib/widgets/checkbox.dart | 2 +- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart index a6524b23..560955c0 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart @@ -40,14 +40,16 @@ class EditRequestHeadersState extends ConsumerState { ref.watch(selectedRequestModelProvider .select((value) => value?.requestHeaders?.length)); var rH = ref.read(selectedRequestModelProvider)?.requestHeaders; - rows = (rH == null || rH.isEmpty) + bool isHeadersEmpty = rH == null || rH.isEmpty; + rows = (isHeadersEmpty) ? [ kNameValueEmptyModel, ] : rH; - isRowEnabledList = - ref.read(selectedRequestModelProvider)?.isHeaderEnabledList ?? - List.filled(rows.length, true, growable: true); + isRowEnabledList = ref + .read(selectedRequestModelProvider) + ?.isHeaderEnabledList ?? + List.filled(rows.length, isHeadersEmpty ? false : true, growable: true); DaviModel model = DaviModel( rows: rows, @@ -60,12 +62,17 @@ class EditRequestHeadersState extends ConsumerState { return CheckBox( keyId: "$selectedId-$idx-headers-c-$seed", value: isRowEnabledList[idx], - onChanged: (value) { - setState(() { - isRowEnabledList[idx] = value!; - }); - _onFieldChange(selectedId!); - }, + onChanged: rows.length == 1 && + idx == 0 && + rows[idx].name.isEmpty && + rows[idx].value.isEmpty + ? null + : (value) { + setState(() { + isRowEnabledList[idx] = value!; + }); + _onFieldChange(selectedId!); + }, colorScheme: Theme.of(context).colorScheme, ); }, @@ -85,7 +92,7 @@ class EditRequestHeadersState extends ConsumerState { rows[idx] = rows[idx].copyWith(name: value); if (idx == rows.length - 1) { rows.add(kNameValueEmptyModel); - isRowEnabledList.add(true); + isRowEnabledList.add(false); } _onFieldChange(selectedId!); }, @@ -116,7 +123,7 @@ class EditRequestHeadersState extends ConsumerState { rows[idx] = rows[idx].copyWith(value: value); if (idx == rows.length - 1) { rows.add(kNameValueEmptyModel); - isRowEnabledList.add(true); + isRowEnabledList.add(false); } _onFieldChange(selectedId!); }, @@ -140,7 +147,7 @@ class EditRequestHeadersState extends ConsumerState { rows = [ kNameValueEmptyModel, ]; - isRowEnabledList = [true]; + isRowEnabledList = [false]; }); } else { rows.removeAt(row.index); diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart index f602200d..3bb018cb 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart @@ -41,14 +41,16 @@ class EditRequestURLParamsState extends ConsumerState { ref.watch(selectedRequestModelProvider .select((value) => value?.requestParams?.length)); var rP = ref.read(selectedRequestModelProvider)?.requestParams; - rows = (rP == null || rP.isEmpty) + bool isParamsEmpty = rP == null || rP.isEmpty; + rows = (isParamsEmpty) ? [ kNameValueEmptyModel, ] : rP; - isRowEnabledList = - ref.read(selectedRequestModelProvider)?.isParamEnabledList ?? - List.filled(rows.length, true, growable: true); + isRowEnabledList = ref + .read(selectedRequestModelProvider) + ?.isParamEnabledList ?? + List.filled(rows.length, isParamsEmpty ? false : true, growable: true); DaviModel model = DaviModel( rows: rows, @@ -61,12 +63,16 @@ class EditRequestURLParamsState extends ConsumerState { return CheckBox( keyId: "$selectedId-$idx-params-c-$seed", value: isRowEnabledList[idx], - onChanged: (value) { - setState(() { - isRowEnabledList[idx] = value!; - }); - _onFieldChange(selectedId!); - }, + onChanged: idx + 1 == rows.length && + rows[idx].name.isEmpty && + rows[idx].value.isEmpty + ? null + : (value) { + setState(() { + isRowEnabledList[idx] = value!; + }); + _onFieldChange(selectedId!); + }, colorScheme: Theme.of(context).colorScheme, ); }, @@ -84,8 +90,9 @@ class EditRequestURLParamsState extends ConsumerState { onChanged: (value) { rows[idx] = rows[idx].copyWith(name: value); if (idx == rows.length - 1) { + isRowEnabledList[idx] = true; rows.add(kNameValueEmptyModel); - isRowEnabledList.add(true); + isRowEnabledList.add(false); } _onFieldChange(selectedId!); }, @@ -115,8 +122,9 @@ class EditRequestURLParamsState extends ConsumerState { onChanged: (value) { rows[idx] = rows[idx].copyWith(value: value); if (idx == rows.length - 1) { + isRowEnabledList[idx] = true; rows.add(kNameValueEmptyModel); - isRowEnabledList.add(true); + isRowEnabledList.add(false); } _onFieldChange(selectedId!); }, @@ -140,7 +148,7 @@ class EditRequestURLParamsState extends ConsumerState { rows = [ kNameValueEmptyModel, ]; - isRowEnabledList = [true]; + isRowEnabledList = [false]; }); } else { rows.removeAt(row.index); diff --git a/lib/widgets/checkbox.dart b/lib/widgets/checkbox.dart index 22c3869f..53adf496 100644 --- a/lib/widgets/checkbox.dart +++ b/lib/widgets/checkbox.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; class CheckBox extends StatelessWidget { final String keyId; final bool value; - final ValueChanged onChanged; + final ValueChanged? onChanged; final ColorScheme? colorScheme; const CheckBox({ super.key, From 48d4beff0056a611dbd6bab16a65de0c0f271ea2 Mon Sep 17 00:00:00 2001 From: Apoorv Dwivedi Date: Sun, 17 Mar 2024 02:48:54 +0530 Subject: [PATCH 11/39] Added curl rust code generator --- lib/codegen/codegen.dart | 2 +- lib/codegen/rust/curl-rust.dart | 113 --- lib/codegen/rust/curl_rust.dart | 144 ++++ test/codegen/curl_rust_codegen_test.dart | 997 +++++++++++++++++++++++ 4 files changed, 1142 insertions(+), 114 deletions(-) delete mode 100644 lib/codegen/rust/curl-rust.dart create mode 100644 lib/codegen/rust/curl_rust.dart create mode 100644 test/codegen/curl_rust_codegen_test.dart diff --git a/lib/codegen/codegen.dart b/lib/codegen/codegen.dart index 6b5c5be3..e343b241 100644 --- a/lib/codegen/codegen.dart +++ b/lib/codegen/codegen.dart @@ -1,4 +1,4 @@ -import 'package:apidash/codegen/rust/curl-rust.dart'; +import 'package:apidash/codegen/rust/curl_rust.dart'; import 'package:apidash/models/models.dart' show RequestModel; import 'package:apidash/consts.dart'; import 'package:apidash/utils/utils.dart' show getNewUuid; diff --git a/lib/codegen/rust/curl-rust.dart b/lib/codegen/rust/curl-rust.dart deleted file mode 100644 index 9d775b74..00000000 --- a/lib/codegen/rust/curl-rust.dart +++ /dev/null @@ -1,113 +0,0 @@ -import 'dart:convert'; -import 'package:jinja/jinja.dart' as jj; -import 'package:apidash/utils/utils.dart' - show getNewUuid, getValidRequestUri, padMultilineString; -import 'package:apidash/models/models.dart' show RequestModel; -import 'package:apidash/consts.dart'; - -class RustCurlCodeGen { - final String kTemplateStart = """use curl::easy::Easy; -use serde_json::json; - -fn main() { -"""; - - String kTemplateUrl = """ - let mut easy = Easy::new(); - easy.url("{{url}}").unwrap(); - """; - - String kTemplateMethod = """easy.{{method}}(true).unwrap(); - """; - - String kTemplateRawBody = - """easy.post_field_copy({{body}}.as_bytes()).unwrap(); - """; - - String kTemplateJsonBody = - """easy.post_fields_copy(json!({{body}}).to_string().as_bytes()).unwrap(); - """; - - String kTemplateFormData = """ - let mut form = curl::easy::Form::new(); - {% for field in fields %} - form.part("{{field.name}}") - {% if field.type == "file" %}.file("{{field.value}}"){% else %}.contents(b"{{field.value}}"){% endif %} - .add(); - {% endfor %} - easy.httppost(form); - """; - - String kTemplateHeader = """easy.header("{{header}}", "{{value}}"); - """; - - final String kTemplateEnd = """easy.perform().unwrap(); -} -"""; - - String? getCode( - RequestModel requestModel, - String defaultUriScheme, - ) { - try { - String result = ""; - - String url = requestModel.url; - if (!url.contains("://") && url.isNotEmpty) { - url = "$defaultUriScheme://$url"; - } - - result += kTemplateStart; - - var rec = getValidRequestUri( - url, - requestModel.enabledRequestParams, - ); - - Uri? uri = rec.$1; - - if (uri != null) { - var templateUrl = jj.Template(kTemplateUrl); - result += templateUrl.render({"url": uri.toString()}); - - var method = requestModel.method; - var templateMethod = jj.Template(kTemplateMethod); - result += templateMethod.render({"method": method.name.toLowerCase()}); - - var requestBody = requestModel.requestBody; - if (kMethodsWithBody.contains(method) && requestBody != null) { - if (requestModel.requestBodyContentType == ContentType.text) { - var templateRawBody = jj.Template(kTemplateRawBody); - result += templateRawBody.render({"body": requestBody}); - } else if (requestModel.requestBodyContentType == ContentType.json) { - var templateJsonBody = jj.Template(kTemplateJsonBody); - result += templateJsonBody.render({"body": requestBody}); - } else if (requestModel.isFormDataRequest) { - var templateFormData = jj.Template(kTemplateFormData); - result += templateFormData.render({ - "fields": requestModel.formDataMapList, - }); - } - } - - var headersList = requestModel.enabledRequestHeaders; - if (headersList != null) { - var headers = requestModel.enabledHeadersMap; - for (var header in headers.keys) { - var templateHeader = jj.Template(kTemplateHeader); - result += templateHeader.render({ - "header": header, - "value": headers[header], - }); - } - } - - result += kTemplateEnd; - } - - return result; - } catch (e) { - return null; - } - } -} diff --git a/lib/codegen/rust/curl_rust.dart b/lib/codegen/rust/curl_rust.dart new file mode 100644 index 00000000..c2189eca --- /dev/null +++ b/lib/codegen/rust/curl_rust.dart @@ -0,0 +1,144 @@ +import 'package:jinja/jinja.dart' as jj; +import 'package:apidash/utils/utils.dart' show getValidRequestUri, requestModelToHARJsonRequest; +import 'package:apidash/models/models.dart' show RequestModel; +import 'package:apidash/consts.dart'; + +class RustCurlCodeGen { + final String kTemplateStart = """use curl::easy::Easy; +{% if hasJsonBody %}use serde_json::json; +{% endif %}{% if hasHeaders %}use curl::easy::List; +{% endif %} +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + +"""; + + String kTemplateUrl = """ + easy.url("{{url}}").unwrap(); +"""; + + String kTemplateMethod = """ +{% if method == 'get' or method == 'post' or method == 'put' %} + easy.{{ method }}(true).unwrap(); +{% elif method == 'delete' %} + easy.custom_request("DELETE").unwrap(); +{% elif method == 'patch' %} + easy.custom_request("PATCH").unwrap(); +{% elif method == 'head' %} + easy.nobody(true).unwrap(); +{% endif %} + +"""; + + String kTemplateRawBody =""" + easy.post_fields_copy(r#"{{body}}"#.as_bytes()).unwrap(); + + +"""; + + String kTemplateJsonBody =""" + easy.post_fields_copy(json!({{body}}).to_string().as_bytes()).unwrap(); + + +"""; + + String kTemplateFormData = """ + let mut form = curl::easy::Form::new(); + {% for field in fields %} + form.part("{{field.name}}") + {% if field.type == "file" %}.file("{{field.value}}"){% else %}.contents(b"{{field.value}}"){% endif %} + .add(); + {% endfor %} + easy.httppost(form); + """; + + String kTemplateHeader = """ + {% if headers %}let mut list = List::new();{% for header, value in headers %} + list.append("{{header}}: {{value}}");{% endfor %} + easy.http_headers(list).unwrap(); + {% endif %} + +"""; + + final String kTemplateEnd = """ + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + + String? getCode(RequestModel requestModel) { + try { + String result = ""; + var requestBody = requestModel.requestBody; + + String url = requestModel.url; + + result += jj.Template(kTemplateStart).render({ + "hasJsonBody": requestModel.hasJsonData, + "hasHeaders": requestModel.enabledRequestHeaders != null || requestModel.hasBody + }); + + var rec = getValidRequestUri( + url, + requestModel.enabledRequestParams, + ); + + Uri? uri = rec.$1; + var harJson = + requestModelToHARJsonRequest(requestModel, useEnabled: true); + + var templateUrl = jj.Template(kTemplateUrl); + result += templateUrl.render({"url": harJson["url"]}); + + var methodTemplate = jj.Template(kTemplateMethod); + result += methodTemplate.render({"method": requestModel.method.name}); + + if (uri != null) { + if (requestModel.hasTextData) { + var templateBody = jj.Template(kTemplateRawBody); + result += templateBody.render({"body": requestBody}); + } else if (requestModel.hasJsonData) { + var templateBody = jj.Template(kTemplateJsonBody); + result += templateBody.render({"body": requestBody}); + } else if (requestModel.hasFormData) { + var templateFormData = jj.Template(kTemplateFormData); + result += templateFormData.render({ + "fields": requestModel.formDataMapList, + }); + } + + var headersList = requestModel.enabledRequestHeaders; + if (headersList != null || requestModel.hasBody) { + var headers = requestModel.enabledHeadersMap; + if (requestModel.hasJsonData || requestModel.hasTextData) { + headers.putIfAbsent(kHeaderContentType, + () => requestModel.requestBodyContentType.header); + } + if (headers.isNotEmpty) { + var templateHeader = jj.Template(kTemplateHeader); + result += templateHeader.render({ + "headers": headers, + }); + } + } + + result += kTemplateEnd; + } + + return result; + } catch (e) { + return null; + } + } +} diff --git a/test/codegen/curl_rust_codegen_test.dart b/test/codegen/curl_rust_codegen_test.dart new file mode 100644 index 00000000..aa337c2d --- /dev/null +++ b/test/codegen/curl_rust_codegen_test.dart @@ -0,0 +1,997 @@ +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('GET1', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet1, "https"), + expectedCode); + }); + test('GET2', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/country/data?code=US").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet2, "https"), + expectedCode); + }); + test('GET3', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/country/data?code=IND").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet3, "https"), + expectedCode); + }); + test('GET4', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/humanize/social?num=8700000&digits=3&system=SS&add_space=true&trailing_zeros=true").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet4, "https"), + expectedCode); + }); + test('GET5', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.github.com/repos/foss42/apidash").unwrap(); + easy.get(true).unwrap(); + + let mut list = List::new(); + list.append("User-Agent: Test Agent"); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet5, "https"), + expectedCode); + }); + test('GET6', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.github.com/repos/foss42/apidash?raw=true").unwrap(); + easy.get(true).unwrap(); + + let mut list = List::new(); + list.append("User-Agent: Test Agent"); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet6, "https"), + expectedCode); + }); + test('GET7', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet7, "https"), + expectedCode); + }); + test('GET8', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.github.com/repos/foss42/apidash?raw=true").unwrap(); + easy.get(true).unwrap(); + + let mut list = List::new(); + list.append("User-Agent: Test Agent"); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet8, "https"), + expectedCode); + }); + test('GET9', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/humanize/social?num=8700000&add_space=true").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet9, "https"), + expectedCode); + }); + test('GET10', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/humanize/social").unwrap(); + easy.get(true).unwrap(); + + let mut list = List::new(); + list.append("User-Agent: Test Agent"); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet10, "https"), + expectedCode); + }); + test('GET11', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/humanize/social?num=8700000&digits=3").unwrap(); + easy.get(true).unwrap(); + + let mut list = List::new(); + list.append("User-Agent: Test Agent"); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet11, "https"), + expectedCode); + }); + test('GET12', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/humanize/social").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet12, "https"), + expectedCode); + }); + }); + + group('HEAD Request', () { + test('HEAD1', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev").unwrap(); + easy.nobody(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelHead1, "https"), + expectedCode); + }); + test('HEAD2', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev").unwrap(); + easy.nobody(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelHead2, "https"), + expectedCode); + }); + }); + + group('POST Request', () { + test('POST1', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/case/lower").unwrap(); + easy.post(true).unwrap(); + + easy.post_fields_copy(r#"{ +"text": "I LOVE Flutter" +}"#.as_bytes()).unwrap(); + + let mut list = List::new(); + list.append("Content-Type: text/plain"); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost1, "https"), + expectedCode); + }); + test('POST2', () { + const expectedCode = r""" +use curl::easy::Easy; +use serde_json::json; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/case/lower").unwrap(); + easy.post(true).unwrap(); + + easy.post_fields_copy(json!({ +"text": "I LOVE Flutter", +"flag": null, +"male": true, +"female": false, +"no": 1.2, +"arr": ["null", "true", "false", null] +}).to_string().as_bytes()).unwrap(); + + let mut list = List::new(); + list.append("Content-Type: application/json"); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost2, "https"), + expectedCode); + }); + test('POST3', () { + const expectedCode = r""" +use curl::easy::Easy; +use serde_json::json; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/case/lower").unwrap(); + easy.post(true).unwrap(); + + easy.post_fields_copy(json!({ +"text": "I LOVE Flutter" +}).to_string().as_bytes()).unwrap(); + + let mut list = List::new(); + list.append("User-Agent: Test Agent"); + list.append("Content-Type: application/json"); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost3, "https"), + expectedCode); + }); + test('POST4', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/io/form").unwrap(); + easy.post(true).unwrap(); + + let mut form = curl::easy::Form::new(); + + form.part("text") + .contents(b"API") + .add(); + + form.part("sep") + .contents(b"|") + .add(); + + form.part("times") + .contents(b"3") + .add(); + + easy.httppost(form); + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost4, "https"), + expectedCode); + }); + test('POST5', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/io/form").unwrap(); + easy.post(true).unwrap(); + + let mut form = curl::easy::Form::new(); + + form.part("text") + .contents(b"API") + .add(); + + form.part("sep") + .contents(b"|") + .add(); + + form.part("times") + .contents(b"3") + .add(); + + easy.httppost(form); + let mut list = List::new(); + list.append("User-Agent: Test Agent"); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost5, "https"), + expectedCode); + }); + test('POST6', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/io/img").unwrap(); + easy.post(true).unwrap(); + + let mut form = curl::easy::Form::new(); + + form.part("token") + .contents(b"xyz") + .add(); + + form.part("imfile") + .file("/Documents/up/1.png") + .add(); + + easy.httppost(form); + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost6, "https"), + expectedCode); + }); + test('POST7', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/io/img").unwrap(); + easy.post(true).unwrap(); + + let mut form = curl::easy::Form::new(); + + form.part("token") + .contents(b"xyz") + .add(); + + form.part("imfile") + .file("/Documents/up/1.png") + .add(); + + easy.httppost(form); + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost7, "https"), + expectedCode); + }); + test('POST8', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/io/form?size=2&len=3").unwrap(); + easy.post(true).unwrap(); + + let mut form = curl::easy::Form::new(); + + form.part("text") + .contents(b"API") + .add(); + + form.part("sep") + .contents(b"|") + .add(); + + form.part("times") + .contents(b"3") + .add(); + + easy.httppost(form); + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost8, "https"), + expectedCode); + }); + test('POST9', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/io/img?size=2&len=3").unwrap(); + easy.post(true).unwrap(); + + let mut form = curl::easy::Form::new(); + + form.part("token") + .contents(b"xyz") + .add(); + + form.part("imfile") + .file("/Documents/up/1.png") + .add(); + + easy.httppost(form); + let mut list = List::new(); + list.append("User-Agent: Test Agent"); + list.append("Keep-Alive: true"); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost9, "https"), + expectedCode); + }); + }); + + group('PUT Request', () { + test('PUT1', () { + const expectedCode = r""" +use curl::easy::Easy; +use serde_json::json; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://reqres.in/api/users/2").unwrap(); + easy.put(true).unwrap(); + + easy.post_fields_copy(json!({ +"name": "morpheus", +"job": "zion resident" +}).to_string().as_bytes()).unwrap(); + + let mut list = List::new(); + list.append("Content-Type: application/json"); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPut1, "https"), + expectedCode); + }); + }); + + group('PATCH Request', () { + test('PATCH1', () { + const expectedCode = r""" +use curl::easy::Easy; +use serde_json::json; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://reqres.in/api/users/2").unwrap(); + easy.custom_request("PATCH").unwrap(); + + easy.post_fields_copy(json!({ +"name": "marfeus", +"job": "accountant" +}).to_string().as_bytes()).unwrap(); + + let mut list = List::new(); + list.append("Content-Type: application/json"); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPatch1, "https"), + expectedCode); + }); + }); + + group('DELETE Request', () { + test('DELETE1', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://reqres.in/api/users/2").unwrap(); + easy.custom_request("DELETE").unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelDelete1, "https"), + expectedCode); + }); + test('DELETE2', () { + const expectedCode = r""" +use curl::easy::Easy; +use serde_json::json; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://reqres.in/api/users/2").unwrap(); + easy.custom_request("DELETE").unwrap(); + + easy.post_fields_copy(json!({ +"name": "marfeus", +"job": "accountant" +}).to_string().as_bytes()).unwrap(); + + let mut list = List::new(); + list.append("Content-Type: application/json"); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body:{}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelDelete2, "https"), + expectedCode); + }); + }); + +} From f1ec852acb994b3e12c7a50c49d70b1d7bdcc250 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Sun, 17 Mar 2024 05:39:54 +0100 Subject: [PATCH 12/39] Add test case for selectedIdEditStateProvider after rename button is tapped --- test/providers/ui_providers_test.dart | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index cc9b25c0..ee62131e 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -230,5 +230,35 @@ void main() { final container = ProviderScope.containerOf(collectionPane); expect(container.read(selectedIdEditStateProvider), null); }); + + testWidgets( + 'selectedIdEditStateProvider should not be null after rename button has been tapped', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: CollectionPane(), + ), + ), + ); + + // Tap on the three dots to open the request card menu + await tester.tap(find.byType(RequestList)); + await tester.pump(); + await tester.tap(find.byType(RequestItem)); + await tester.pump(); + await tester.tap(find.byIcon(Icons.more_vert).first); + await tester.pumpAndSettle(); + + // Tap on the "Rename" option in the menu + await tester.tap(find.text('Rename')); + await tester.pumpAndSettle(); + + // Verify that the selectedIdEditStateProvider is not null + final collectionPane = tester.element(find.byType(CollectionPane)); + final container = ProviderScope.containerOf(collectionPane); + expect(container.read(selectedIdEditStateProvider), isNotNull); + expect((container.read(selectedIdEditStateProvider)).runtimeType, String); + }); }); } From 1513cfe3c1447b72b35be69706c9f3e8c1d5f020 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Sun, 17 Mar 2024 05:55:39 +0100 Subject: [PATCH 13/39] Add test case for when selectedIdEditStateProvider is reset --- test/providers/ui_providers_test.dart | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index ee62131e..fb9056e1 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -260,5 +260,44 @@ void main() { expect(container.read(selectedIdEditStateProvider), isNotNull); expect((container.read(selectedIdEditStateProvider)).runtimeType, String); }); + + testWidgets( + 'It should be set back to null when user taps outside name editor', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: CollectionPane(), + ), + ), + ); + + // Grab the CollectionPane widget and its ProviderContainer + final collectionPane = tester.element(find.byType(CollectionPane)); + final container = ProviderScope.containerOf(collectionPane); + + // Tap on the three dots to open the request card menu + await tester.tap(find.byType(RequestList)); + await tester.pump(); + await tester.tap(find.byType(RequestItem)); + await tester.pump(); + await tester.tap(find.byIcon(Icons.more_vert).first); + await tester.pumpAndSettle(); + + // Tap on the "Rename" option in the menu + await tester.tap(find.text('Rename')); + await tester.pumpAndSettle(); + + // Verify that the selectedIdEditStateProvider is not null + expect(container.read(selectedIdEditStateProvider), isNotNull); + expect((container.read(selectedIdEditStateProvider)).runtimeType, String); + + // Tap on the screen to simulate tapping outside the name editor + await tester.tap(find.byType(CollectionPane)); + await tester.pumpAndSettle(); + + // Verify that the selectedIdEditStateProvider is null + expect(container.read(selectedIdEditStateProvider), null); + }); }); } From df25a1b1b366293ff67d52c91dfd9888ff85ef54 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Sun, 17 Mar 2024 06:04:49 +0100 Subject: [PATCH 14/39] Add test case to verify proper disposal of selectedIdEditStateProvider --- test/providers/ui_providers_test.dart | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index fb9056e1..dde4983f 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -299,5 +299,35 @@ void main() { // Verify that the selectedIdEditStateProvider is null expect(container.read(selectedIdEditStateProvider), null); }); + testWidgets("It should be properly disposed", (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: CollectionPane(), + ), + ), + ); + + // Grab the Dashboard widget and its ProviderContainer + final collectionPane = tester.element(find.byType(CollectionPane)); + final container = ProviderScope.containerOf(collectionPane); + + // Pumping a different widget to remove the CollectionPane from the widget tree + await tester.pumpWidget( + const MaterialApp( + home: Scaffold(body: Text('Foo')), + ), + ); + + // Verify that the ProviderContainer has been disposed + // by trying to read from disposed container + bool isDisposed = false; + try { + container.read(selectedIdEditStateProvider); + } catch (e) { + isDisposed = true; + } + expect(isDisposed, true); + }); }); } From 711e3bd74d018401556704469306e27d94cd16fd Mon Sep 17 00:00:00 2001 From: DenserMeerkat Date: Sun, 17 Mar 2024 18:27:13 +0530 Subject: [PATCH 15/39] fix: add buttons back --- .../request_pane/request_form_data.dart | 65 ++++++++++++------- .../request_pane/request_headers.dart | 50 ++++++++++---- .../request_pane/request_params.dart | 50 ++++++++++---- 3 files changed, 115 insertions(+), 50 deletions(-) diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart index f77133e7..cec94eab 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart @@ -18,12 +18,20 @@ class _FormDataBodyState extends ConsumerState { late int seed; final random = Random.secure(); late List rows; + @override void initState() { super.initState(); seed = random.nextInt(kRandMax); } + void _onFieldChange(String selectedId) { + ref.read(collectionStateNotifierProvider.notifier).update( + selectedId, + requestFormDataList: rows, + ); + } + @override Widget build(BuildContext context) { final selectedId = ref.watch(selectedIdStateProvider); @@ -168,36 +176,49 @@ class _FormDataBodyState extends ConsumerState { rows.removeAt(row.index); } _onFieldChange(selectedId!); - setState(() {}); }, ); }, ), ], ); - return Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: kBorderRadius12, - ), - margin: kP10, - child: Column( - children: [ - Expanded( - child: DaviTheme( - data: kTableThemeData, - child: Davi(daviModelRows), + return Stack( + children: [ + Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: kBorderRadius12, + ), + margin: kP10, + child: Column( + children: [ + Expanded( + child: DaviTheme( + data: kTableThemeData, + child: Davi(daviModelRows), + ), + ), + ], + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.only(bottom: 30), + child: ElevatedButton.icon( + onPressed: () { + rows.add(kFormDataEmptyModel); + _onFieldChange(selectedId!); + }, + icon: const Icon(Icons.add), + label: const Text( + "Add Form Data", + style: kTextStyleButton, + ), ), ), - ], - ), + ), + ], ); } - - void _onFieldChange(String selectedId) { - ref.read(collectionStateNotifierProvider.notifier).update( - selectedId, - requestFormDataList: rows, - ); - } } diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart index 560955c0..f77fda32 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart @@ -160,22 +160,44 @@ class EditRequestHeadersState extends ConsumerState { ), ], ); - return Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: kBorderRadius12, - ), - margin: kP10, - child: Column( - children: [ - Expanded( - child: DaviTheme( - data: kTableThemeData, - child: Davi(model), + return Stack( + children: [ + Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: kBorderRadius12, + ), + margin: kP10, + child: Column( + children: [ + Expanded( + child: DaviTheme( + data: kTableThemeData, + child: Davi(model), + ), + ), + ], + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.only(bottom: 30), + child: ElevatedButton.icon( + onPressed: () { + rows.add(kNameValueEmptyModel); + isRowEnabledList.add(false); + _onFieldChange(selectedId!); + }, + icon: const Icon(Icons.add), + label: const Text( + "Add Header", + style: kTextStyleButton, + ), ), ), - ], - ), + ), + ], ); } } diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart index 3bb018cb..dfc78c50 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart @@ -161,22 +161,44 @@ class EditRequestURLParamsState extends ConsumerState { ), ], ); - return Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: kBorderRadius12, - ), - margin: kP10, - child: Column( - children: [ - Expanded( - child: DaviTheme( - data: kTableThemeData, - child: Davi(model), + return Stack( + children: [ + Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: kBorderRadius12, + ), + margin: kP10, + child: Column( + children: [ + Expanded( + child: DaviTheme( + data: kTableThemeData, + child: Davi(model), + ), + ), + ], + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.only(bottom: 30), + child: ElevatedButton.icon( + onPressed: () { + rows.add(kNameValueEmptyModel); + isRowEnabledList.add(false); + _onFieldChange(selectedId!); + }, + icon: const Icon(Icons.add), + label: const Text( + "Add Param", + style: kTextStyleButton, + ), ), ), - ], - ), + ), + ], ); } } From a2c20eedc153ef58412378be0a14eb5de6e3bc4a Mon Sep 17 00:00:00 2001 From: DenserMeerkat Date: Mon, 18 Mar 2024 01:17:18 +0530 Subject: [PATCH 16/39] fix: empty row when rows exist --- .../request_pane/request_form_data.dart | 80 +++++++++-------- .../request_pane/request_headers.dart | 86 ++++++++++--------- .../request_pane/request_params.dart | 86 +++++++++++-------- 3 files changed, 139 insertions(+), 113 deletions(-) diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart index cec94eab..b3d8f67c 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart @@ -17,7 +17,7 @@ class FormDataWidget extends ConsumerStatefulWidget { class _FormDataBodyState extends ConsumerState { late int seed; final random = Random.secure(); - late List rows; + late List formRows; @override void initState() { @@ -28,7 +28,7 @@ class _FormDataBodyState extends ConsumerState { void _onFieldChange(String selectedId) { ref.read(collectionStateNotifierProvider.notifier).update( selectedId, - requestFormDataList: rows, + requestFormDataList: formRows.sublist(0, formRows.length - 1), ); } @@ -37,12 +37,17 @@ class _FormDataBodyState extends ConsumerState { final selectedId = ref.watch(selectedIdStateProvider); ref.watch(selectedRequestModelProvider .select((value) => value?.requestFormDataList?.length)); - var formRows = ref.read(selectedRequestModelProvider)?.requestFormDataList; - rows = - formRows == null || formRows.isEmpty ? [kFormDataEmptyModel] : formRows; + var rF = ref.read(selectedRequestModelProvider)?.requestFormDataList; + bool isFormDataEmpty = rF == null || rF.isEmpty; + List rows = (isFormDataEmpty) + ? [ + kFormDataEmptyModel, + ] + : rF; + formRows = isFormDataEmpty ? rows : rows + [kFormDataEmptyModel]; DaviModel daviModelRows = DaviModel( - rows: rows, + rows: formRows, columns: [ DaviColumn( cellPadding: kpsV5, @@ -50,27 +55,28 @@ class _FormDataBodyState extends ConsumerState { grow: 4, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == formRows.length; return Theme( data: Theme.of(context), child: FormDataField( keyId: "$selectedId-$idx-form-v-$seed", - initialValue: rows[idx].name, + initialValue: formRows[idx].name, hintText: " Add Key", onChanged: (value) { - rows[idx] = rows[idx].copyWith(name: value); - if (idx == rows.length - 1) rows.add(kFormDataEmptyModel); + formRows[idx] = formRows[idx].copyWith(name: value); + if (isLast) formRows.add(kFormDataEmptyModel); _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, - formDataType: rows[idx].type, + formDataType: formRows[idx].type, onFormDataTypeChanged: (value) { - bool hasChanged = rows[idx].type != value; - rows[idx] = rows[idx].copyWith( + bool hasChanged = formRows[idx].type != value; + formRows[idx] = formRows[idx].copyWith( type: value ?? FormDataType.text, ); - rows[idx] = rows[idx].copyWith(value: ""); - if (idx == rows.length - 1 && hasChanged) { - rows.add(kFormDataEmptyModel); + formRows[idx] = formRows[idx].copyWith(value: ""); + if (idx == formRows.length - 1 && hasChanged) { + formRows.add(kFormDataEmptyModel); } setState(() {}); _onFieldChange(selectedId!); @@ -97,7 +103,8 @@ class _FormDataBodyState extends ConsumerState { cellPadding: kpsV5, cellBuilder: (_, row) { int idx = row.index; - return rows[idx].type == FormDataType.file + bool isLast = idx + 1 == formRows.length; + return formRows[idx].type == FormDataType.file ? Align( alignment: Alignment.centerLeft, child: Row( @@ -122,7 +129,7 @@ class _FormDataBodyState extends ConsumerState { if (pickedResult != null && pickedResult.files.isNotEmpty && pickedResult.files.first.path != null) { - rows[idx] = rows[idx].copyWith( + formRows[idx] = formRows[idx].copyWith( value: pickedResult.files.first.path!, ); setState(() {}); @@ -130,9 +137,9 @@ class _FormDataBodyState extends ConsumerState { } }, label: Text( - (rows[idx].type == FormDataType.file && - rows[idx].value.isNotEmpty) - ? rows[idx].value.toString() + (formRows[idx].type == FormDataType.file && + formRows[idx].value.isNotEmpty) + ? formRows[idx].value.toString() : "Select File", textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, @@ -146,11 +153,11 @@ class _FormDataBodyState extends ConsumerState { ) : CellField( keyId: "$selectedId-$idx-form-v-$seed", - initialValue: rows[idx].value, + initialValue: formRows[idx].value, hintText: " Add Value", onChanged: (value) { - rows[idx] = rows[idx].copyWith(value: value); - if (idx == rows.length - 1) rows.add(kFormDataEmptyModel); + formRows[idx] = formRows[idx].copyWith(value: value); + if (isLast) formRows.add(kFormDataEmptyModel); _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, @@ -162,21 +169,24 @@ class _FormDataBodyState extends ConsumerState { pinStatus: PinStatus.none, width: 30, cellBuilder: (_, row) { + bool isLast = row.index + 1 == formRows.length; return InkWell( + onTap: isLast + ? null + : () { + seed = random.nextInt(kRandMax); + if (formRows.length == 2) { + setState(() { + formRows = [kFormDataEmptyModel]; + }); + } else { + formRows.removeAt(row.index); + } + _onFieldChange(selectedId!); + }, child: Theme.of(context).brightness == Brightness.dark ? kIconRemoveDark : kIconRemoveLight, - onTap: () { - seed = random.nextInt(kRandMax); - if (rows.length == 1) { - setState(() { - rows = [kFormDataEmptyModel]; - }); - } else { - rows.removeAt(row.index); - } - _onFieldChange(selectedId!); - }, ); }, ), @@ -207,7 +217,7 @@ class _FormDataBodyState extends ConsumerState { padding: const EdgeInsets.only(bottom: 30), child: ElevatedButton.icon( onPressed: () { - rows.add(kFormDataEmptyModel); + formRows.add(kFormDataEmptyModel); _onFieldChange(selectedId!); }, icon: const Icon(Icons.add), diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart index f77fda32..e5b0df59 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart @@ -15,10 +15,10 @@ class EditRequestHeaders extends ConsumerStatefulWidget { } class EditRequestHeadersState extends ConsumerState { - final random = Random.secure(); - late List rows; - late List isRowEnabledList; late int seed; + final random = Random.secure(); + late List headerRows; + late List isRowEnabledList; @override void initState() { @@ -29,8 +29,9 @@ class EditRequestHeadersState extends ConsumerState { void _onFieldChange(String selectedId) { ref.read(collectionStateNotifierProvider.notifier).update( selectedId, - requestHeaders: rows, - isHeaderEnabledList: isRowEnabledList, + requestHeaders: headerRows.sublist(0, headerRows.length - 1), + isHeaderEnabledList: + isRowEnabledList.sublist(0, headerRows.length - 1), ); } @@ -41,31 +42,30 @@ class EditRequestHeadersState extends ConsumerState { .select((value) => value?.requestHeaders?.length)); var rH = ref.read(selectedRequestModelProvider)?.requestHeaders; bool isHeadersEmpty = rH == null || rH.isEmpty; - rows = (isHeadersEmpty) + List rows = (isHeadersEmpty) ? [ kNameValueEmptyModel, ] : rH; - isRowEnabledList = ref - .read(selectedRequestModelProvider) - ?.isHeaderEnabledList ?? - List.filled(rows.length, isHeadersEmpty ? false : true, growable: true); + headerRows = isHeadersEmpty ? rows : rows + [kNameValueEmptyModel]; + isRowEnabledList = + ref.read(selectedRequestModelProvider)?.isHeaderEnabledList ?? + List.filled(rH?.length ?? 0, true, growable: true); + isRowEnabledList.add(false); DaviModel model = DaviModel( - rows: rows, + rows: headerRows, columns: [ DaviColumn( name: 'Checkbox', width: 30, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == headerRows.length; return CheckBox( keyId: "$selectedId-$idx-headers-c-$seed", value: isRowEnabledList[idx], - onChanged: rows.length == 1 && - idx == 0 && - rows[idx].name.isEmpty && - rows[idx].value.isEmpty + onChanged: isLast ? null : (value) { setState(() { @@ -83,15 +83,16 @@ class EditRequestHeadersState extends ConsumerState { grow: 1, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == headerRows.length; return HeaderField( keyId: "$selectedId-$idx-headers-k-$seed", - initialValue: rows[idx].name, + initialValue: headerRows[idx].name, hintText: "Add Header Name", onChanged: (value) { - isRowEnabledList[idx] = true; - rows[idx] = rows[idx].copyWith(name: value); - if (idx == rows.length - 1) { - rows.add(kNameValueEmptyModel); + headerRows[idx] = headerRows[idx].copyWith(name: value); + if (isLast) { + isRowEnabledList[idx] = true; + headerRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); } _onFieldChange(selectedId!); @@ -115,14 +116,16 @@ class EditRequestHeadersState extends ConsumerState { grow: 1, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == headerRows.length; return CellField( keyId: "$selectedId-$idx-headers-v-$seed", - initialValue: rows[idx].value, + initialValue: headerRows[idx].value, hintText: " Add Header Value", onChanged: (value) { - rows[idx] = rows[idx].copyWith(value: value); - if (idx == rows.length - 1) { - rows.add(kNameValueEmptyModel); + headerRows[idx] = headerRows[idx].copyWith(value: value); + if (isLast) { + isRowEnabledList[idx] = true; + headerRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); } _onFieldChange(selectedId!); @@ -136,25 +139,28 @@ class EditRequestHeadersState extends ConsumerState { pinStatus: PinStatus.none, width: 30, cellBuilder: (_, row) { + bool isLast = row.index + 1 == headerRows.length; return InkWell( + onTap: isLast + ? null + : () { + seed = random.nextInt(kRandMax); + if (headerRows.length == 2) { + setState(() { + headerRows = [ + kNameValueEmptyModel, + ]; + isRowEnabledList = [false]; + }); + } else { + headerRows.removeAt(row.index); + isRowEnabledList.removeAt(row.index); + } + _onFieldChange(selectedId!); + }, child: Theme.of(context).brightness == Brightness.dark ? kIconRemoveDark : kIconRemoveLight, - onTap: () { - seed = random.nextInt(kRandMax); - if (rows.length == 1) { - setState(() { - rows = [ - kNameValueEmptyModel, - ]; - isRowEnabledList = [false]; - }); - } else { - rows.removeAt(row.index); - isRowEnabledList.removeAt(row.index); - } - _onFieldChange(selectedId!); - }, ); }, ), @@ -185,7 +191,7 @@ class EditRequestHeadersState extends ConsumerState { padding: const EdgeInsets.only(bottom: 30), child: ElevatedButton.icon( onPressed: () { - rows.add(kNameValueEmptyModel); + headerRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); _onFieldChange(selectedId!); }, diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart index dfc78c50..a306b46b 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart @@ -16,10 +16,10 @@ class EditRequestURLParams extends ConsumerStatefulWidget { } class EditRequestURLParamsState extends ConsumerState { - final random = Random.secure(); - late List rows; - late List isRowEnabledList; late int seed; + final random = Random.secure(); + late List paramRows; + late List isRowEnabledList; @override void initState() { @@ -30,9 +30,14 @@ class EditRequestURLParamsState extends ConsumerState { void _onFieldChange(String selectedId) { ref.read(collectionStateNotifierProvider.notifier).update( selectedId, - requestParams: rows, - isParamEnabledList: isRowEnabledList, + requestParams: paramRows.sublist(0, paramRows.length - 1), + isParamEnabledList: isRowEnabledList.sublist(0, paramRows.length - 1), ); + debugPrint("URL Params Updated"); + for (var i = 0; i < paramRows.length - 1; i++) { + debugPrint( + "URL Param $i: (${paramRows[i].name}=${paramRows[i].value}) ==> ${isRowEnabledList[i]}"); + } } @override @@ -42,30 +47,30 @@ class EditRequestURLParamsState extends ConsumerState { .select((value) => value?.requestParams?.length)); var rP = ref.read(selectedRequestModelProvider)?.requestParams; bool isParamsEmpty = rP == null || rP.isEmpty; - rows = (isParamsEmpty) + List rows = (isParamsEmpty) ? [ kNameValueEmptyModel, ] : rP; - isRowEnabledList = ref - .read(selectedRequestModelProvider) - ?.isParamEnabledList ?? - List.filled(rows.length, isParamsEmpty ? false : true, growable: true); + paramRows = isParamsEmpty ? rows : rows + [kNameValueEmptyModel]; + isRowEnabledList = + ref.read(selectedRequestModelProvider)?.isParamEnabledList ?? + List.filled(rP?.length ?? 0, true, growable: true); + isRowEnabledList.add(false); DaviModel model = DaviModel( - rows: rows, + rows: paramRows, columns: [ DaviColumn( name: 'Checkbox', width: 30, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == paramRows.length; return CheckBox( keyId: "$selectedId-$idx-params-c-$seed", value: isRowEnabledList[idx], - onChanged: idx + 1 == rows.length && - rows[idx].name.isEmpty && - rows[idx].value.isEmpty + onChanged: isLast ? null : (value) { setState(() { @@ -83,15 +88,16 @@ class EditRequestURLParamsState extends ConsumerState { grow: 1, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == paramRows.length; return CellField( keyId: "$selectedId-$idx-params-k-$seed", - initialValue: rows[idx].name, + initialValue: paramRows[idx].name, hintText: "Add URL Parameter", onChanged: (value) { - rows[idx] = rows[idx].copyWith(name: value); - if (idx == rows.length - 1) { + paramRows[idx] = paramRows[idx].copyWith(name: value); + if (isLast) { isRowEnabledList[idx] = true; - rows.add(kNameValueEmptyModel); + paramRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); } _onFieldChange(selectedId!); @@ -115,15 +121,16 @@ class EditRequestURLParamsState extends ConsumerState { grow: 1, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == paramRows.length; return CellField( keyId: "$selectedId-$idx-params-v-$seed", - initialValue: rows[idx].value, + initialValue: paramRows[idx].value, hintText: "Add Value", onChanged: (value) { - rows[idx] = rows[idx].copyWith(value: value); - if (idx == rows.length - 1) { + paramRows[idx] = paramRows[idx].copyWith(value: value); + if (isLast) { isRowEnabledList[idx] = true; - rows.add(kNameValueEmptyModel); + paramRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); } _onFieldChange(selectedId!); @@ -137,25 +144,28 @@ class EditRequestURLParamsState extends ConsumerState { pinStatus: PinStatus.none, width: 30, cellBuilder: (_, row) { + bool isLast = row.index + 1 == paramRows.length; return InkWell( + onTap: isLast + ? null + : () { + seed = random.nextInt(kRandMax); + if (paramRows.length == 2) { + setState(() { + paramRows = [ + kNameValueEmptyModel, + ]; + isRowEnabledList = [false]; + }); + } else { + paramRows.removeAt(row.index); + isRowEnabledList.removeAt(row.index); + } + _onFieldChange(selectedId!); + }, child: Theme.of(context).brightness == Brightness.dark ? kIconRemoveDark : kIconRemoveLight, - onTap: () { - seed = random.nextInt(kRandMax); - if (rows.length == 1) { - setState(() { - rows = [ - kNameValueEmptyModel, - ]; - isRowEnabledList = [false]; - }); - } else { - rows.removeAt(row.index); - isRowEnabledList.removeAt(row.index); - } - _onFieldChange(selectedId!); - }, ); }, ), @@ -186,7 +196,7 @@ class EditRequestURLParamsState extends ConsumerState { padding: const EdgeInsets.only(bottom: 30), child: ElevatedButton.icon( onPressed: () { - rows.add(kNameValueEmptyModel); + paramRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); _onFieldChange(selectedId!); }, From fb36f05792d32e881e583d702499b6a03736a5aa Mon Sep 17 00:00:00 2001 From: DenserMeerkat Date: Mon, 18 Mar 2024 01:22:16 +0530 Subject: [PATCH 17/39] refactor: remove debugPrint --- .../details_card/request_pane/request_params.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart index a306b46b..319330ba 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart @@ -33,11 +33,6 @@ class EditRequestURLParamsState extends ConsumerState { requestParams: paramRows.sublist(0, paramRows.length - 1), isParamEnabledList: isRowEnabledList.sublist(0, paramRows.length - 1), ); - debugPrint("URL Params Updated"); - for (var i = 0; i < paramRows.length - 1; i++) { - debugPrint( - "URL Param $i: (${paramRows[i].name}=${paramRows[i].value}) ==> ${isRowEnabledList[i]}"); - } } @override From 97d3c30bbad6bfcc39b7c2b79885ce951368ca27 Mon Sep 17 00:00:00 2001 From: Apoorv Dwivedi Date: Mon, 18 Mar 2024 20:26:33 +0530 Subject: [PATCH 18/39] Fixed rust generated code and testcases --- lib/codegen/rust/curl_rust.dart | 17 +++--- test/codegen/curl_rust_codegen_test.dart | 73 ++++++++++++------------ 2 files changed, 46 insertions(+), 44 deletions(-) diff --git a/lib/codegen/rust/curl_rust.dart b/lib/codegen/rust/curl_rust.dart index c2189eca..fdba169c 100644 --- a/lib/codegen/rust/curl_rust.dart +++ b/lib/codegen/rust/curl_rust.dart @@ -1,5 +1,6 @@ import 'package:jinja/jinja.dart' as jj; -import 'package:apidash/utils/utils.dart' show getValidRequestUri, requestModelToHARJsonRequest; +import 'package:apidash/utils/utils.dart' + show getValidRequestUri, requestModelToHARJsonRequest; import 'package:apidash/models/models.dart' show RequestModel; import 'package:apidash/consts.dart'; @@ -31,13 +32,13 @@ fn main() { """; - String kTemplateRawBody =""" + String kTemplateRawBody = """ easy.post_fields_copy(r#"{{body}}"#.as_bytes()).unwrap(); """; - String kTemplateJsonBody =""" + String kTemplateJsonBody = """ easy.post_fields_copy(json!({{body}}).to_string().as_bytes()).unwrap(); @@ -48,14 +49,14 @@ fn main() { {% for field in fields %} form.part("{{field.name}}") {% if field.type == "file" %}.file("{{field.value}}"){% else %}.contents(b"{{field.value}}"){% endif %} - .add(); + .add().unwrap(); {% endfor %} - easy.httppost(form); + easy.httppost(form).unwrap(); """; String kTemplateHeader = """ {% if headers %}let mut list = List::new();{% for header, value in headers %} - list.append("{{header}}: {{value}}");{% endfor %} + list.append("{{header}}: {{value}}").unwrap();{% endfor %} easy.http_headers(list).unwrap(); {% endif %} @@ -86,7 +87,9 @@ fn main() { result += jj.Template(kTemplateStart).render({ "hasJsonBody": requestModel.hasJsonData, - "hasHeaders": requestModel.enabledRequestHeaders != null || requestModel.hasBody + "hasHeaders": (requestModel.enabledRequestHeaders != null && + requestModel.enabledRequestHeaders!.isNotEmpty) || + requestModel.hasBody }); var rec = getValidRequestUri( diff --git a/test/codegen/curl_rust_codegen_test.dart b/test/codegen/curl_rust_codegen_test.dart index aa337c2d..75ef133e 100644 --- a/test/codegen/curl_rust_codegen_test.dart +++ b/test/codegen/curl_rust_codegen_test.dart @@ -135,7 +135,7 @@ fn main() { easy.get(true).unwrap(); let mut list = List::new(); - list.append("User-Agent: Test Agent"); + list.append("User-Agent: Test Agent").unwrap(); easy.http_headers(list).unwrap(); { @@ -169,7 +169,7 @@ fn main() { easy.get(true).unwrap(); let mut list = List::new(); - list.append("User-Agent: Test Agent"); + list.append("User-Agent: Test Agent").unwrap(); easy.http_headers(list).unwrap(); { @@ -232,7 +232,7 @@ fn main() { easy.get(true).unwrap(); let mut list = List::new(); - list.append("User-Agent: Test Agent"); + list.append("User-Agent: Test Agent").unwrap(); easy.http_headers(list).unwrap(); { @@ -295,7 +295,7 @@ fn main() { easy.get(true).unwrap(); let mut list = List::new(); - list.append("User-Agent: Test Agent"); + list.append("User-Agent: Test Agent").unwrap(); easy.http_headers(list).unwrap(); { @@ -329,7 +329,7 @@ fn main() { easy.get(true).unwrap(); let mut list = List::new(); - list.append("User-Agent: Test Agent"); + list.append("User-Agent: Test Agent").unwrap(); easy.http_headers(list).unwrap(); { @@ -354,7 +354,6 @@ fn main() { test('GET12', () { const expectedCode = r""" use curl::easy::Easy; -use curl::easy::List; fn main() { let mut easy = Easy::new(); @@ -461,7 +460,7 @@ fn main() { }"#.as_bytes()).unwrap(); let mut list = List::new(); - list.append("Content-Type: text/plain"); + list.append("Content-Type: text/plain").unwrap(); easy.http_headers(list).unwrap(); { @@ -505,7 +504,7 @@ fn main() { }).to_string().as_bytes()).unwrap(); let mut list = List::new(); - list.append("Content-Type: application/json"); + list.append("Content-Type: application/json").unwrap(); easy.http_headers(list).unwrap(); { @@ -544,8 +543,8 @@ fn main() { }).to_string().as_bytes()).unwrap(); let mut list = List::new(); - list.append("User-Agent: Test Agent"); - list.append("Content-Type: application/json"); + list.append("User-Agent: Test Agent").unwrap(); + list.append("Content-Type: application/json").unwrap(); easy.http_headers(list).unwrap(); { @@ -582,17 +581,17 @@ fn main() { form.part("text") .contents(b"API") - .add(); + .add().unwrap(); form.part("sep") .contents(b"|") - .add(); + .add().unwrap(); form.part("times") .contents(b"3") - .add(); + .add().unwrap(); - easy.httppost(form); + easy.httppost(form).unwrap(); { let mut transfer = easy.transfer(); transfer.write_function(|new_data| { @@ -627,19 +626,19 @@ fn main() { form.part("text") .contents(b"API") - .add(); + .add().unwrap(); form.part("sep") .contents(b"|") - .add(); + .add().unwrap(); form.part("times") .contents(b"3") - .add(); + .add().unwrap(); - easy.httppost(form); + easy.httppost(form).unwrap(); let mut list = List::new(); - list.append("User-Agent: Test Agent"); + list.append("User-Agent: Test Agent").unwrap(); easy.http_headers(list).unwrap(); { @@ -676,13 +675,13 @@ fn main() { form.part("token") .contents(b"xyz") - .add(); + .add().unwrap(); form.part("imfile") .file("/Documents/up/1.png") - .add(); + .add().unwrap(); - easy.httppost(form); + easy.httppost(form).unwrap(); { let mut transfer = easy.transfer(); transfer.write_function(|new_data| { @@ -717,13 +716,13 @@ fn main() { form.part("token") .contents(b"xyz") - .add(); + .add().unwrap(); form.part("imfile") .file("/Documents/up/1.png") - .add(); + .add().unwrap(); - easy.httppost(form); + easy.httppost(form).unwrap(); { let mut transfer = easy.transfer(); transfer.write_function(|new_data| { @@ -758,17 +757,17 @@ fn main() { form.part("text") .contents(b"API") - .add(); + .add().unwrap(); form.part("sep") .contents(b"|") - .add(); + .add().unwrap(); form.part("times") .contents(b"3") - .add(); + .add().unwrap(); - easy.httppost(form); + easy.httppost(form).unwrap(); { let mut transfer = easy.transfer(); transfer.write_function(|new_data| { @@ -803,16 +802,16 @@ fn main() { form.part("token") .contents(b"xyz") - .add(); + .add().unwrap(); form.part("imfile") .file("/Documents/up/1.png") - .add(); + .add().unwrap(); - easy.httppost(form); + easy.httppost(form).unwrap(); let mut list = List::new(); - list.append("User-Agent: Test Agent"); - list.append("Keep-Alive: true"); + list.append("User-Agent: Test Agent").unwrap(); + list.append("Keep-Alive: true").unwrap(); easy.http_headers(list).unwrap(); { @@ -855,7 +854,7 @@ fn main() { }).to_string().as_bytes()).unwrap(); let mut list = List::new(); - list.append("Content-Type: application/json"); + list.append("Content-Type: application/json").unwrap(); easy.http_headers(list).unwrap(); { @@ -898,7 +897,7 @@ fn main() { }).to_string().as_bytes()).unwrap(); let mut list = List::new(); - list.append("Content-Type: application/json"); + list.append("Content-Type: application/json").unwrap(); easy.http_headers(list).unwrap(); { @@ -970,7 +969,7 @@ fn main() { }).to_string().as_bytes()).unwrap(); let mut list = List::new(); - list.append("Content-Type: application/json"); + list.append("Content-Type: application/json").unwrap(); easy.http_headers(list).unwrap(); { From beeece8875e8610898ce460d78b6d6becc5cc8ca Mon Sep 17 00:00:00 2001 From: Apoorv Dwivedi Date: Wed, 20 Mar 2024 23:13:06 +0530 Subject: [PATCH 19/39] added content type header in formdata --- lib/codegen/rust/curl_rust.dart | 2 +- ..._test.dart => rust_curl_codegen_test.dart} | 26 ++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) rename test/codegen/{curl_rust_codegen_test.dart => rust_curl_codegen_test.dart} (97%) diff --git a/lib/codegen/rust/curl_rust.dart b/lib/codegen/rust/curl_rust.dart index fdba169c..205e10c5 100644 --- a/lib/codegen/rust/curl_rust.dart +++ b/lib/codegen/rust/curl_rust.dart @@ -124,7 +124,7 @@ fn main() { var headersList = requestModel.enabledRequestHeaders; if (headersList != null || requestModel.hasBody) { var headers = requestModel.enabledHeadersMap; - if (requestModel.hasJsonData || requestModel.hasTextData) { + if (requestModel.hasJsonData || requestModel.hasTextData || requestModel.hasFormData) { headers.putIfAbsent(kHeaderContentType, () => requestModel.requestBodyContentType.header); } diff --git a/test/codegen/curl_rust_codegen_test.dart b/test/codegen/rust_curl_codegen_test.dart similarity index 97% rename from test/codegen/curl_rust_codegen_test.dart rename to test/codegen/rust_curl_codegen_test.dart index 75ef133e..39aa7d9e 100644 --- a/test/codegen/curl_rust_codegen_test.dart +++ b/test/codegen/rust_curl_codegen_test.dart @@ -592,7 +592,11 @@ fn main() { .add().unwrap(); easy.httppost(form).unwrap(); - { + let mut list = List::new(); + list.append("Content-Type: multipart/form-data").unwrap(); + easy.http_headers(list).unwrap(); + + { let mut transfer = easy.transfer(); transfer.write_function(|new_data| { data.extend_from_slice(new_data); @@ -639,6 +643,7 @@ fn main() { easy.httppost(form).unwrap(); let mut list = List::new(); list.append("User-Agent: Test Agent").unwrap(); + list.append("Content-Type: multipart/form-data").unwrap(); easy.http_headers(list).unwrap(); { @@ -682,7 +687,11 @@ fn main() { .add().unwrap(); easy.httppost(form).unwrap(); - { + let mut list = List::new(); + list.append("Content-Type: multipart/form-data").unwrap(); + easy.http_headers(list).unwrap(); + + { let mut transfer = easy.transfer(); transfer.write_function(|new_data| { data.extend_from_slice(new_data); @@ -723,7 +732,11 @@ fn main() { .add().unwrap(); easy.httppost(form).unwrap(); - { + let mut list = List::new(); + list.append("Content-Type: multipart/form-data").unwrap(); + easy.http_headers(list).unwrap(); + + { let mut transfer = easy.transfer(); transfer.write_function(|new_data| { data.extend_from_slice(new_data); @@ -768,7 +781,11 @@ fn main() { .add().unwrap(); easy.httppost(form).unwrap(); - { + let mut list = List::new(); + list.append("Content-Type: multipart/form-data").unwrap(); + easy.http_headers(list).unwrap(); + + { let mut transfer = easy.transfer(); transfer.write_function(|new_data| { data.extend_from_slice(new_data); @@ -812,6 +829,7 @@ fn main() { let mut list = List::new(); list.append("User-Agent: Test Agent").unwrap(); list.append("Keep-Alive: true").unwrap(); + list.append("Content-Type: multipart/form-data").unwrap(); easy.http_headers(list).unwrap(); { From f99a84375a09ffb5931e4fd73f817983ff117cd1 Mon Sep 17 00:00:00 2001 From: Apoorv Dwivedi Date: Thu, 21 Mar 2024 02:40:47 +0530 Subject: [PATCH 20/39] Fixed headers and corresponding template --- lib/codegen/rust/curl_rust.dart | 4 ++-- test/codegen/rust_curl_codegen_test.dart | 30 ++++-------------------- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/lib/codegen/rust/curl_rust.dart b/lib/codegen/rust/curl_rust.dart index 205e10c5..9f461a64 100644 --- a/lib/codegen/rust/curl_rust.dart +++ b/lib/codegen/rust/curl_rust.dart @@ -89,7 +89,7 @@ fn main() { "hasJsonBody": requestModel.hasJsonData, "hasHeaders": (requestModel.enabledRequestHeaders != null && requestModel.enabledRequestHeaders!.isNotEmpty) || - requestModel.hasBody + (requestModel.hasJsonData || requestModel.hasTextData) }); var rec = getValidRequestUri( @@ -124,7 +124,7 @@ fn main() { var headersList = requestModel.enabledRequestHeaders; if (headersList != null || requestModel.hasBody) { var headers = requestModel.enabledHeadersMap; - if (requestModel.hasJsonData || requestModel.hasTextData || requestModel.hasFormData) { + if (requestModel.hasJsonData || requestModel.hasTextData) { headers.putIfAbsent(kHeaderContentType, () => requestModel.requestBodyContentType.header); } diff --git a/test/codegen/rust_curl_codegen_test.dart b/test/codegen/rust_curl_codegen_test.dart index 39aa7d9e..bcdcdada 100644 --- a/test/codegen/rust_curl_codegen_test.dart +++ b/test/codegen/rust_curl_codegen_test.dart @@ -569,7 +569,6 @@ fn main() { test('POST4', () { const expectedCode = r""" use curl::easy::Easy; -use curl::easy::List; fn main() { let mut easy = Easy::new(); @@ -592,11 +591,7 @@ fn main() { .add().unwrap(); easy.httppost(form).unwrap(); - let mut list = List::new(); - list.append("Content-Type: multipart/form-data").unwrap(); - easy.http_headers(list).unwrap(); - - { + { let mut transfer = easy.transfer(); transfer.write_function(|new_data| { data.extend_from_slice(new_data); @@ -643,7 +638,6 @@ fn main() { easy.httppost(form).unwrap(); let mut list = List::new(); list.append("User-Agent: Test Agent").unwrap(); - list.append("Content-Type: multipart/form-data").unwrap(); easy.http_headers(list).unwrap(); { @@ -668,7 +662,6 @@ fn main() { test('POST6', () { const expectedCode = r""" use curl::easy::Easy; -use curl::easy::List; fn main() { let mut easy = Easy::new(); @@ -687,11 +680,7 @@ fn main() { .add().unwrap(); easy.httppost(form).unwrap(); - let mut list = List::new(); - list.append("Content-Type: multipart/form-data").unwrap(); - easy.http_headers(list).unwrap(); - - { + { let mut transfer = easy.transfer(); transfer.write_function(|new_data| { data.extend_from_slice(new_data); @@ -713,7 +702,6 @@ fn main() { test('POST7', () { const expectedCode = r""" use curl::easy::Easy; -use curl::easy::List; fn main() { let mut easy = Easy::new(); @@ -732,11 +720,7 @@ fn main() { .add().unwrap(); easy.httppost(form).unwrap(); - let mut list = List::new(); - list.append("Content-Type: multipart/form-data").unwrap(); - easy.http_headers(list).unwrap(); - - { + { let mut transfer = easy.transfer(); transfer.write_function(|new_data| { data.extend_from_slice(new_data); @@ -758,7 +742,6 @@ fn main() { test('POST8', () { const expectedCode = r""" use curl::easy::Easy; -use curl::easy::List; fn main() { let mut easy = Easy::new(); @@ -781,11 +764,7 @@ fn main() { .add().unwrap(); easy.httppost(form).unwrap(); - let mut list = List::new(); - list.append("Content-Type: multipart/form-data").unwrap(); - easy.http_headers(list).unwrap(); - - { + { let mut transfer = easy.transfer(); transfer.write_function(|new_data| { data.extend_from_slice(new_data); @@ -829,7 +808,6 @@ fn main() { let mut list = List::new(); list.append("User-Agent: Test Agent").unwrap(); list.append("Keep-Alive: true").unwrap(); - list.append("Content-Type: multipart/form-data").unwrap(); easy.http_headers(list).unwrap(); { From de19fc427970db4f8bc283f69cc3de41fc727dcf Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Thu, 21 Mar 2024 00:04:45 +0100 Subject: [PATCH 21/39] Test initial value of sentRequestIdStateProvider --- test/providers/ui_providers_test.dart | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index dde4983f..c7a2f5a0 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -330,4 +330,23 @@ void main() { expect(isDisposed, true); }); }); + + group("Testing sentRequestIdStateProvider", () { + testWidgets( + 'sentRequestIdStateProvider should have an initial value of null', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: CollectionPane(), + ), + ), + ); + + // Verify that the initial value is null + final collectionPane = tester.element(find.byType(CollectionPane)); + final container = ProviderScope.containerOf(collectionPane); + expect(container.read(sentRequestIdStateProvider), null); + }); + }); } From fd03e704b2669f7f055d574234f46cc565681f6d Mon Sep 17 00:00:00 2001 From: Yousef Rabia <78663127+Yousef-Rabia@users.noreply.github.com> Date: Thu, 21 Mar 2024 02:03:00 +0200 Subject: [PATCH 22/39] Add Android Additional Instructions --- CONTRIBUTING.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 211eaf06..ec24ec76 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -131,6 +131,19 @@ Instead of copy pasting from pub.dev, it is recommended that you use `flutter pu ## Platform-specific Additional Instructions +### Android +Add the `multiDexEnabled true` line to the `defaultConfig` section at `android/app/build.gradle file` + +``` +android { + ... + defaultConfig { + ... + multiDexEnabled true + } +} +``` + ### macOS Add below keys to `macos/Runner/DebugProfile.entitlements` and `macos/Runner/Release.entitlements`. From d2aee57ef173e5cf2f90c718f8a2efc674d6fd5c Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Thu, 21 Mar 2024 01:31:55 +0100 Subject: [PATCH 23/39] Remove test case for sentRequestIdStateProvider I removed test case for this provider because this provider does not exist in the repository anymore. With recent changes, it has been removed. --- test/providers/ui_providers_test.dart | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index c7a2f5a0..dde4983f 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -330,23 +330,4 @@ void main() { expect(isDisposed, true); }); }); - - group("Testing sentRequestIdStateProvider", () { - testWidgets( - 'sentRequestIdStateProvider should have an initial value of null', - (tester) async { - await tester.pumpWidget( - const ProviderScope( - child: MaterialApp( - home: CollectionPane(), - ), - ), - ); - - // Verify that the initial value is null - final collectionPane = tester.element(find.byType(CollectionPane)); - final container = ProviderScope.containerOf(collectionPane); - expect(container.read(sentRequestIdStateProvider), null); - }); - }); } From decd1d68ebc51482ffda81833e4b52201534903b Mon Sep 17 00:00:00 2001 From: Apoorv Dwivedi Date: Thu, 21 Mar 2024 20:53:48 +0530 Subject: [PATCH 24/39] Padded response body print statement --- lib/codegen/rust/curl_rust.dart | 2 +- test/codegen/rust_curl_codegen_test.dart | 54 ++++++++++++------------ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/lib/codegen/rust/curl_rust.dart b/lib/codegen/rust/curl_rust.dart index 9f461a64..9299554f 100644 --- a/lib/codegen/rust/curl_rust.dart +++ b/lib/codegen/rust/curl_rust.dart @@ -74,7 +74,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; diff --git a/test/codegen/rust_curl_codegen_test.dart b/test/codegen/rust_curl_codegen_test.dart index bcdcdada..badc18ba 100644 --- a/test/codegen/rust_curl_codegen_test.dart +++ b/test/codegen/rust_curl_codegen_test.dart @@ -28,7 +28,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -57,7 +57,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -86,7 +86,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -115,7 +115,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -149,7 +149,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -183,7 +183,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -212,7 +212,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -246,7 +246,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -275,7 +275,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -309,7 +309,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -343,7 +343,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -372,7 +372,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -404,7 +404,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -433,7 +433,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -474,7 +474,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -518,7 +518,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -558,7 +558,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -602,7 +602,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -651,7 +651,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -691,7 +691,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -731,7 +731,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -775,7 +775,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -821,7 +821,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -864,7 +864,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -907,7 +907,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -939,7 +939,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( @@ -979,7 +979,7 @@ fn main() { let response_body = String::from_utf8_lossy(&data); - println!("Response body:{}", response_body); + println!("Response body: {}", response_body); println!("Response code: {}", easy.response_code().unwrap()); }"""; expect( From a076a7a6cb3de4bb640a1d4e38091c1a2b7dd502 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Thu, 21 Mar 2024 23:28:24 +0530 Subject: [PATCH 25/39] Update CONTRIBUTING.md --- CONTRIBUTING.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ec24ec76..32f79e5c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -131,19 +131,6 @@ Instead of copy pasting from pub.dev, it is recommended that you use `flutter pu ## Platform-specific Additional Instructions -### Android -Add the `multiDexEnabled true` line to the `defaultConfig` section at `android/app/build.gradle file` - -``` -android { - ... - defaultConfig { - ... - multiDexEnabled true - } -} -``` - ### macOS Add below keys to `macos/Runner/DebugProfile.entitlements` and `macos/Runner/Release.entitlements`. @@ -167,4 +154,16 @@ ClientException with SocketException: Connection failed (OS Error: Operation not You can read more [here](https://docs.flutter.dev/platform-integration/macos/building#setting-up-entitlements) +### Android (Work in Progress) +Add the `multiDexEnabled true` line to the `defaultConfig` section at `android/app/build.gradle file` + +``` +android { + ... + defaultConfig { + ... + multiDexEnabled true + } +} +``` From adfbc2bce84a1ff55cd5b30f339301bde8c83e21 Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Fri, 22 Mar 2024 09:17:24 +0530 Subject: [PATCH 26/39] fixes --- lib/codegen/codegen.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/codegen/codegen.dart b/lib/codegen/codegen.dart index e343b241..f3f606ae 100644 --- a/lib/codegen/codegen.dart +++ b/lib/codegen/codegen.dart @@ -1,4 +1,3 @@ -import 'package:apidash/codegen/rust/curl_rust.dart'; import 'package:apidash/models/models.dart' show RequestModel; import 'package:apidash/consts.dart'; import 'package:apidash/utils/utils.dart' show getNewUuid; @@ -10,6 +9,7 @@ import 'php/guzzle.dart'; import 'python/http_client.dart'; import 'python/requests.dart'; import 'rust/actix.dart'; +import 'rust/curl_rust.dart'; import 'rust/reqwest.dart'; import 'rust/ureq.dart'; import 'js/axios.dart'; @@ -74,10 +74,10 @@ class Codegen { return PythonRequestsCodeGen().getCode(rM, boundary: boundary); case CodegenLanguage.rustActix: return RustActixCodeGen().getCode(rM, boundary: boundary); - case CodegenLanguage.rustReqwest: - return RustReqwestCodeGen().getCode(rM); case CodegenLanguage.rustCurl: return RustCurlCodeGen().getCode(rM); + case CodegenLanguage.rustReqwest: + return RustReqwestCodeGen().getCode(rM); case CodegenLanguage.rustUreq: return RustUreqCodeGen().getCode(rM, boundary: boundary); case CodegenLanguage.phpGuzzle: From 796b38c8e7b144095eeef7a923fd8e48ead0d562 Mon Sep 17 00:00:00 2001 From: DenserMeerkat Date: Fri, 22 Mar 2024 17:17:51 +0530 Subject: [PATCH 27/39] fix: prevent adding multiple empty rows --- .../request_pane/request_form_data.dart | 14 +++++++++++--- .../details_card/request_pane/request_headers.dart | 8 ++++++-- .../details_card/request_pane/request_params.dart | 8 ++++++-- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart index b3d8f67c..fd00b020 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart @@ -18,6 +18,7 @@ class _FormDataBodyState extends ConsumerState { late int seed; final random = Random.secure(); late List formRows; + bool isAddingRow = false; @override void initState() { @@ -45,6 +46,7 @@ class _FormDataBodyState extends ConsumerState { ] : rF; formRows = isFormDataEmpty ? rows : rows + [kFormDataEmptyModel]; + isAddingRow = false; DaviModel daviModelRows = DaviModel( rows: formRows, @@ -64,7 +66,10 @@ class _FormDataBodyState extends ConsumerState { hintText: " Add Key", onChanged: (value) { formRows[idx] = formRows[idx].copyWith(name: value); - if (isLast) formRows.add(kFormDataEmptyModel); + if (isLast && !isAddingRow) { + isAddingRow = true; + formRows.add(kFormDataEmptyModel); + } _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, @@ -75,7 +80,7 @@ class _FormDataBodyState extends ConsumerState { type: value ?? FormDataType.text, ); formRows[idx] = formRows[idx].copyWith(value: ""); - if (idx == formRows.length - 1 && hasChanged) { + if (isLast && hasChanged) { formRows.add(kFormDataEmptyModel); } setState(() {}); @@ -157,7 +162,10 @@ class _FormDataBodyState extends ConsumerState { hintText: " Add Value", onChanged: (value) { formRows[idx] = formRows[idx].copyWith(value: value); - if (isLast) formRows.add(kFormDataEmptyModel); + if (isLast && !isAddingRow) { + isAddingRow = true; + formRows.add(kFormDataEmptyModel); + } _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart index e5b0df59..f473ec3f 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart @@ -19,6 +19,7 @@ class EditRequestHeadersState extends ConsumerState { final random = Random.secure(); late List headerRows; late List isRowEnabledList; + bool isAddingRow = false; @override void initState() { @@ -52,6 +53,7 @@ class EditRequestHeadersState extends ConsumerState { ref.read(selectedRequestModelProvider)?.isHeaderEnabledList ?? List.filled(rH?.length ?? 0, true, growable: true); isRowEnabledList.add(false); + isAddingRow = false; DaviModel model = DaviModel( rows: headerRows, @@ -90,7 +92,8 @@ class EditRequestHeadersState extends ConsumerState { hintText: "Add Header Name", onChanged: (value) { headerRows[idx] = headerRows[idx].copyWith(name: value); - if (isLast) { + if (isLast && !isAddingRow) { + isAddingRow = true; isRowEnabledList[idx] = true; headerRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); @@ -123,7 +126,8 @@ class EditRequestHeadersState extends ConsumerState { hintText: " Add Header Value", onChanged: (value) { headerRows[idx] = headerRows[idx].copyWith(value: value); - if (isLast) { + if (isLast && !isAddingRow) { + isAddingRow = true; isRowEnabledList[idx] = true; headerRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart index 319330ba..7002f8eb 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart @@ -20,6 +20,7 @@ class EditRequestURLParamsState extends ConsumerState { final random = Random.secure(); late List paramRows; late List isRowEnabledList; + bool isAddingRow = false; @override void initState() { @@ -52,6 +53,7 @@ class EditRequestURLParamsState extends ConsumerState { ref.read(selectedRequestModelProvider)?.isParamEnabledList ?? List.filled(rP?.length ?? 0, true, growable: true); isRowEnabledList.add(false); + isAddingRow = false; DaviModel model = DaviModel( rows: paramRows, @@ -90,7 +92,8 @@ class EditRequestURLParamsState extends ConsumerState { hintText: "Add URL Parameter", onChanged: (value) { paramRows[idx] = paramRows[idx].copyWith(name: value); - if (isLast) { + if (isLast && !isAddingRow) { + isAddingRow = true; isRowEnabledList[idx] = true; paramRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); @@ -123,7 +126,8 @@ class EditRequestURLParamsState extends ConsumerState { hintText: "Add Value", onChanged: (value) { paramRows[idx] = paramRows[idx].copyWith(value: value); - if (isLast) { + if (isLast && !isAddingRow) { + isAddingRow = true; isRowEnabledList[idx] = true; paramRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); From 459ab83eb2c993a0ceab399e5925d61eb5c21ae0 Mon Sep 17 00:00:00 2001 From: Sudhar08 <99385366+sudhar08@users.noreply.github.com> Date: Fri, 22 Mar 2024 21:27:39 +0530 Subject: [PATCH 28/39] feat: Add a search/filter for collection pane #305 This feature adds a search/filter functionality to the collection pane, allowing users to easily search for specific requests based on their names. Users can input search queries to filter the requests displayed in the collection pane, improving the navigation and organization of requests within the application. --- lib/providers/collection_providers.dart | 35 +++++++++++++++++++++- lib/screens/home_page/collection_pane.dart | 34 ++++++++++++++++++--- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/lib/providers/collection_providers.dart b/lib/providers/collection_providers.dart index db6b7cf3..30801ecb 100644 --- a/lib/providers/collection_providers.dart +++ b/lib/providers/collection_providers.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'settings_providers.dart'; import 'ui_providers.dart'; @@ -8,6 +9,7 @@ import '../consts.dart'; import 'package:http/http.dart' as http; final selectedIdStateProvider = StateProvider((ref) => null); +final searchQueryProvider = StateProvider((ref) => ''); final selectedRequestModelProvider = StateProvider((ref) { final selectedId = ref.watch(selectedIdStateProvider); @@ -31,7 +33,7 @@ final StateNotifierProvider?> class CollectionStateNotifier extends StateNotifier?> { CollectionStateNotifier(this.ref, this.hiveHandler) : super(null) { - var status = loadData(); + var status = filterRequests(ref.read(searchQueryProvider.notifier).state); Future.microtask(() { if (status) { ref.read(requestSequenceProvider.notifier).state = [ @@ -248,6 +250,37 @@ class CollectionStateNotifier } } + bool filterRequests(String query) { + if (query.isEmpty) { + + loadData(); + return true; + } else { + // Filter requests based on the query + final filteredRequests = state?.values.where((request) => + request.name.toLowerCase().contains(query.toLowerCase())).toList(); + + + if (filteredRequests != null && filteredRequests.isNotEmpty) { + + Map requestMap = {}; + + for (var request in filteredRequests) { + requestMap[request.id] = request; + } + + state = requestMap; + + } else { + print("No matching requests found"); + } + + + + return false; + } + } + Future saveData() async { ref.read(saveDataStateProvider.notifier).state = true; final saveResponse = ref.read(settingsProvider).saveResponses; diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index fd0b518a..c44ed56e 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:apidash/providers/providers.dart'; @@ -66,10 +67,33 @@ class CollectionPane extends ConsumerWidget { style: kTextStyleButton, ), ), + ], ), ), - kVSpacer8, + kVSpacer10, + SizedBox( + height:30, + child:SearchBar( + + onChanged: (value){ + if(value.isNotEmpty){ + ref.read(searchQueryProvider.notifier).state = value; + ref.read(collectionStateNotifierProvider.notifier) + .filterRequests(value); + } + else{ + + } + + }, + hintText: "search", + leading: Icon(Icons.search), + textStyle: MaterialStateTextStyle.resolveWith((states) => TextStyle(fontSize: 15.0)), + elevation: MaterialStateProperty.all(2.0), + )), + kVSpacer10, + const Expanded( child: RequestList(), ), @@ -110,7 +134,7 @@ class _RequestListState extends ConsumerState { final alwaysShowCollectionPaneScrollbar = ref.watch(settingsProvider .select((value) => value.alwaysShowCollectionPaneScrollbar)); - return Scrollbar( + return requestItems.isNotEmpty?Scrollbar( controller: controller, thumbVisibility: alwaysShowCollectionPaneScrollbar ? true : null, radius: const Radius.circular(12), @@ -118,7 +142,7 @@ class _RequestListState extends ConsumerState { padding: kPr8CollectionPane, scrollController: controller, buildDefaultDragHandles: false, - itemCount: requestSequence.length, + itemCount: requestItems.length, onReorder: (int oldIndex, int newIndex) { if (oldIndex < newIndex) { newIndex -= 1; @@ -131,6 +155,8 @@ class _RequestListState extends ConsumerState { }, itemBuilder: (context, index) { var id = requestSequence[index]; + print(requestItems[id]!); + return ReorderableDragStartListener( key: ValueKey(id), index: index, @@ -144,7 +170,7 @@ class _RequestListState extends ConsumerState { ); }, ), - ); + ):Text("data"); } } From b73a4ab9c4d186a362815adc335075d0b72a2fc4 Mon Sep 17 00:00:00 2001 From: alainjr10 Date: Fri, 22 Mar 2024 22:07:05 +0100 Subject: [PATCH 29/39] fix: fixed codegen dropdown overflow --- lib/widgets/codegen_previewer.dart | 2 +- lib/widgets/dropdowns.dart | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/widgets/codegen_previewer.dart b/lib/widgets/codegen_previewer.dart index 298610c7..b09cc577 100644 --- a/lib/widgets/codegen_previewer.dart +++ b/lib/widgets/codegen_previewer.dart @@ -135,7 +135,7 @@ class ViewCodePane extends StatelessWidget { child: Column( children: [ SizedBox( - height: kHeaderHeight, + height: constraints.maxWidth > 340 ? kHeaderHeight : kTabHeight, child: Row( children: [ Expanded( diff --git a/lib/widgets/dropdowns.dart b/lib/widgets/dropdowns.dart index 69008a6b..b40122fc 100644 --- a/lib/widgets/dropdowns.dart +++ b/lib/widgets/dropdowns.dart @@ -157,6 +157,7 @@ class DropdownButtonCodegenLanguage extends StatelessWidget { Widget build(BuildContext context) { final surfaceColor = Theme.of(context).colorScheme.surface; return DropdownButton( + isExpanded: true, focusColor: surfaceColor, value: codegenLanguage, icon: const Icon( From e3084e884494b9994bf7a5a5c17f0c8a447eacbe Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 23 Mar 2024 14:19:24 +0530 Subject: [PATCH 30/39] fixes --- lib/widgets/codegen_previewer.dart | 2 +- lib/widgets/dropdowns.dart | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/widgets/codegen_previewer.dart b/lib/widgets/codegen_previewer.dart index b09cc577..298610c7 100644 --- a/lib/widgets/codegen_previewer.dart +++ b/lib/widgets/codegen_previewer.dart @@ -135,7 +135,7 @@ class ViewCodePane extends StatelessWidget { child: Column( children: [ SizedBox( - height: constraints.maxWidth > 340 ? kHeaderHeight : kTabHeight, + height: kHeaderHeight, child: Row( children: [ Expanded( diff --git a/lib/widgets/dropdowns.dart b/lib/widgets/dropdowns.dart index b40122fc..1be902e9 100644 --- a/lib/widgets/dropdowns.dart +++ b/lib/widgets/dropdowns.dart @@ -182,6 +182,8 @@ class DropdownButtonCodegenLanguage extends StatelessWidget { child: Text( value.label, style: kTextStyleButton, + overflow: TextOverflow.ellipsis, + maxLines: 1, ), ), ); From c38c1846f50c7363b88e818cea3d5ce999442062 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 23 Mar 2024 14:56:22 +0530 Subject: [PATCH 31/39] Update padding in request tables --- lib/consts.dart | 4 ++++ .../details_card/request_pane/request_form_data.dart | 3 ++- .../details_card/request_pane/request_headers.dart | 3 ++- .../editor_pane/details_card/request_pane/request_params.dart | 3 ++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/consts.dart b/lib/consts.dart index 01bc5593..277f6956 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -102,6 +102,9 @@ const kP8CollectionPane = EdgeInsets.only( const kPb10 = EdgeInsets.only( bottom: 10, ); +const kPb15 = EdgeInsets.only( + bottom: 15, +); const kPr8CollectionPane = EdgeInsets.only(right: 8.0); const kpsV5 = EdgeInsets.symmetric(vertical: 2); const kHSpacer4 = SizedBox(width: 4); @@ -112,6 +115,7 @@ const kVSpacer5 = SizedBox(height: 5); const kVSpacer8 = SizedBox(height: 8); const kVSpacer10 = SizedBox(height: 10); const kVSpacer20 = SizedBox(height: 20); +const kVSpacer40 = SizedBox(height: 40); const kTabAnimationDuration = Duration(milliseconds: 200); const kTabHeight = 45.0; diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart index b3d8f67c..ef05f4d6 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart @@ -208,13 +208,14 @@ class _FormDataBodyState extends ConsumerState { child: Davi(daviModelRows), ), ), + kVSpacer20, ], ), ), Align( alignment: Alignment.bottomCenter, child: Padding( - padding: const EdgeInsets.only(bottom: 30), + padding: const EdgeInsets.only(bottom: 5), child: ElevatedButton.icon( onPressed: () { formRows.add(kFormDataEmptyModel); diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart index e5b0df59..28fd0d3d 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart @@ -182,13 +182,14 @@ class EditRequestHeadersState extends ConsumerState { child: Davi(model), ), ), + kVSpacer40, ], ), ), Align( alignment: Alignment.bottomCenter, child: Padding( - padding: const EdgeInsets.only(bottom: 30), + padding: kPb15, child: ElevatedButton.icon( onPressed: () { headerRows.add(kNameValueEmptyModel); diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart index 319330ba..9bbd9d1d 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart @@ -182,13 +182,14 @@ class EditRequestURLParamsState extends ConsumerState { child: Davi(model), ), ), + kVSpacer40, ], ), ), Align( alignment: Alignment.bottomCenter, child: Padding( - padding: const EdgeInsets.only(bottom: 30), + padding: kPb15, child: ElevatedButton.icon( onPressed: () { paramRows.add(kNameValueEmptyModel); From 19c8406d364b0675e3d46796017b4c6b814a2216 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 23 Mar 2024 17:50:19 +0530 Subject: [PATCH 32/39] Fix for clearResponse --- lib/models/request_model.dart | 2 ++ lib/providers/collection_providers.dart | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/models/request_model.dart b/lib/models/request_model.dart index 0ea6d735..acf6a3eb 100644 --- a/lib/models/request_model.dart +++ b/lib/models/request_model.dart @@ -95,6 +95,7 @@ class RequestModel { RequestModel duplicate({ required String id, String? name, + int? requestTabIndex, }) { return RequestModel( id: id, @@ -102,6 +103,7 @@ class RequestModel { url: url, name: name ?? "${this.name} (copy)", description: description, + requestTabIndex: requestTabIndex ?? 0, requestHeaders: requestHeaders != null ? [...requestHeaders!] : null, requestParams: requestParams != null ? [...requestParams!] : null, isHeaderEnabledList: diff --git a/lib/providers/collection_providers.dart b/lib/providers/collection_providers.dart index db6b7cf3..75e9d7c5 100644 --- a/lib/providers/collection_providers.dart +++ b/lib/providers/collection_providers.dart @@ -102,6 +102,7 @@ class CollectionStateNotifier final newModel = currentModel.duplicate( id: id, name: currentModel.name, + requestTabIndex: currentModel.requestTabIndex, ); var map = {...state!}; map[id] = newModel; From 0884a1405835ba3dc98000dfa7bec60b95a9e8b3 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 23 Mar 2024 17:51:49 +0530 Subject: [PATCH 33/39] Code cleanup & Refactoring --- lib/consts.dart | 42 +++++++++- lib/screens/home_page/collection_pane.dart | 4 +- .../request_pane/request_form_data.dart | 18 ++-- .../request_pane/request_headers.dart | 12 +-- .../request_pane/request_params.dart | 12 +-- .../details_card/response_pane.dart | 3 - lib/widgets/buttons.dart | 15 ++-- lib/widgets/request_widgets.dart | 18 ++-- lib/widgets/response_widgets.dart | 83 +++++-------------- lib/widgets/tabs.dart | 10 ++- 10 files changed, 103 insertions(+), 114 deletions(-) diff --git a/lib/consts.dart b/lib/consts.dart index 277f6956..2ad9d185 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -56,6 +56,7 @@ const kForegroundOpacity = 0.05; const kOverlayBackgroundOpacity = 0.5; const kTextStyleButton = TextStyle(fontWeight: FontWeight.bold); +const kTextStyleTab = TextStyle(fontSize: 14); const kTextStyleButtonSmall = TextStyle(fontSize: 12); const kFormDataButtonLabelTextStyle = TextStyle( fontSize: 12, @@ -71,9 +72,13 @@ const kP5 = EdgeInsets.all(5); const kP8 = EdgeInsets.all(8); const kPs8 = EdgeInsets.only(left: 8); const kPs2 = EdgeInsets.only(left: 2); +const kPe8 = EdgeInsets.only(right: 8.0); const kPh20v5 = EdgeInsets.symmetric(horizontal: 20, vertical: 5); const kPh20v10 = EdgeInsets.symmetric(horizontal: 20, vertical: 10); const kP10 = EdgeInsets.all(10); +const kPv8 = EdgeInsets.symmetric(vertical: 8); +const kPv2 = EdgeInsets.symmetric(vertical: 2); +const kPh2 = EdgeInsets.symmetric(horizontal: 2); const kPt24o8 = EdgeInsets.only(top: 24, left: 8.0, right: 8.0, bottom: 8.0); const kPt5o10 = EdgeInsets.only(left: 10.0, right: 10.0, top: 5.0, bottom: 10.0); @@ -105,8 +110,6 @@ const kPb10 = EdgeInsets.only( const kPb15 = EdgeInsets.only( bottom: 15, ); -const kPr8CollectionPane = EdgeInsets.only(right: 8.0); -const kpsV5 = EdgeInsets.symmetric(vertical: 2); const kHSpacer4 = SizedBox(width: 4); const kHSpacer5 = SizedBox(width: 5); const kHSpacer10 = SizedBox(width: 10); @@ -118,7 +121,7 @@ const kVSpacer20 = SizedBox(height: 20); const kVSpacer40 = SizedBox(height: 40); const kTabAnimationDuration = Duration(milliseconds: 200); -const kTabHeight = 45.0; +const kTabHeight = 32.0; const kHeaderHeight = 32.0; const kSegmentHeight = 24.0; const kTextButtonMinWidth = 44.0; @@ -537,3 +540,36 @@ const kLabelSave = "Save"; const kLabelDownload = "Download"; const kLabelSaving = "Saving"; const kLabelSaved = "Saved"; +// Request Pane +const kLabelRequest = "Request"; +const kLabelHideCode = "Hide Code"; +const kLabelViewCode = "View Code"; +const kLabelURLParams = "URL Params"; +const kLabelHeaders = "Headers"; +const kLabelBody = "Body"; +const kNameCheckbox = "Checkbox"; +const kNameURLParam = "URL Parameter"; +const kNameHeader = "Header Name"; +const kNameValue = "Value"; +const kNameField = "Field"; +const kHintAddURLParam = "Add URL Parameter"; +const kHintAddValue = "Add Value"; +const kHintAddName = "Add Name"; +const kHintAddFieldName = "Add Field Name"; +const kLabelAddParam = "Add Param"; +const kLabelAddHeader = "Add Header"; +const kLabelSelectFile = "Select File"; +const kLabelAddFormField = "Add Form Field"; +// Response Pane +const kLabelNotSent = "Not Sent"; +const kLabelResponse = "Response"; +const kLabelResponseBody = "Response Body"; +const kTooltipClearResponse = "Clear Response"; +const kHeaderRow = ["Header Name", "Header Value"]; +const kLabelRequestHeaders = "Request Headers"; +const kLabelResponseHeaders = "Response Headers"; +const kLabelItems = "items"; +const kMsgError = "Error: Response data does not exist."; +const kMsgNullBody = "Response body is missing (null)."; +const kMsgNoContent = "No content"; +const kMsgUnknowContentType = "Unknown Response Content-Type"; diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index fd0b518a..bdcd9b5f 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -26,7 +26,7 @@ class CollectionPane extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Padding( - padding: kPr8CollectionPane, + padding: kPe8, child: Wrap( alignment: WrapAlignment.spaceBetween, children: [ @@ -115,7 +115,7 @@ class _RequestListState extends ConsumerState { thumbVisibility: alwaysShowCollectionPaneScrollbar ? true : null, radius: const Radius.circular(12), child: ReorderableListView.builder( - padding: kPr8CollectionPane, + padding: kPe8, scrollController: controller, buildDefaultDragHandles: false, itemCount: requestSequence.length, diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart index 61bf9d59..a6bafd6e 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart @@ -52,8 +52,8 @@ class _FormDataBodyState extends ConsumerState { rows: formRows, columns: [ DaviColumn( - cellPadding: kpsV5, - name: 'Key', + cellPadding: kPv2, + name: kNameField, grow: 4, cellBuilder: (_, row) { int idx = row.index; @@ -63,7 +63,7 @@ class _FormDataBodyState extends ConsumerState { child: FormDataField( keyId: "$selectedId-$idx-form-v-$seed", initialValue: formRows[idx].name, - hintText: " Add Key", + hintText: kHintAddFieldName, onChanged: (value) { formRows[idx] = formRows[idx].copyWith(name: value); if (isLast && !isAddingRow) { @@ -93,7 +93,7 @@ class _FormDataBodyState extends ConsumerState { ), DaviColumn( width: 40, - cellPadding: kpsV5, + cellPadding: kPv2, cellAlignment: Alignment.center, cellBuilder: (_, row) { return Text( @@ -103,9 +103,9 @@ class _FormDataBodyState extends ConsumerState { }, ), DaviColumn( - name: 'Value', + name: kNameValue, grow: 4, - cellPadding: kpsV5, + cellPadding: kPv2, cellBuilder: (_, row) { int idx = row.index; bool isLast = idx + 1 == formRows.length; @@ -145,7 +145,7 @@ class _FormDataBodyState extends ConsumerState { (formRows[idx].type == FormDataType.file && formRows[idx].value.isNotEmpty) ? formRows[idx].value.toString() - : "Select File", + : kLabelSelectFile, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: kFormDataButtonLabelTextStyle, @@ -159,7 +159,7 @@ class _FormDataBodyState extends ConsumerState { : CellField( keyId: "$selectedId-$idx-form-v-$seed", initialValue: formRows[idx].value, - hintText: " Add Value", + hintText: kHintAddValue, onChanged: (value) { formRows[idx] = formRows[idx].copyWith(value: value); if (isLast && !isAddingRow) { @@ -231,7 +231,7 @@ class _FormDataBodyState extends ConsumerState { }, icon: const Icon(Icons.add), label: const Text( - "Add Form Data", + kLabelAddFormField, style: kTextStyleButton, ), ), diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart index 0e09702c..ee5c574c 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart @@ -59,7 +59,7 @@ class EditRequestHeadersState extends ConsumerState { rows: headerRows, columns: [ DaviColumn( - name: 'Checkbox', + name: kNameCheckbox, width: 30, cellBuilder: (_, row) { int idx = row.index; @@ -80,7 +80,7 @@ class EditRequestHeadersState extends ConsumerState { }, ), DaviColumn( - name: 'Header Name', + name: kNameHeader, width: 70, grow: 1, cellBuilder: (_, row) { @@ -89,7 +89,7 @@ class EditRequestHeadersState extends ConsumerState { return HeaderField( keyId: "$selectedId-$idx-headers-k-$seed", initialValue: headerRows[idx].name, - hintText: "Add Header Name", + hintText: kHintAddName, onChanged: (value) { headerRows[idx] = headerRows[idx].copyWith(name: value); if (isLast && !isAddingRow) { @@ -115,7 +115,7 @@ class EditRequestHeadersState extends ConsumerState { }, ), DaviColumn( - name: 'Header Value', + name: kNameValue, grow: 1, cellBuilder: (_, row) { int idx = row.index; @@ -123,7 +123,7 @@ class EditRequestHeadersState extends ConsumerState { return CellField( keyId: "$selectedId-$idx-headers-v-$seed", initialValue: headerRows[idx].value, - hintText: " Add Header Value", + hintText: kHintAddValue, onChanged: (value) { headerRows[idx] = headerRows[idx].copyWith(value: value); if (isLast && !isAddingRow) { @@ -202,7 +202,7 @@ class EditRequestHeadersState extends ConsumerState { }, icon: const Icon(Icons.add), label: const Text( - "Add Header", + kLabelAddHeader, style: kTextStyleButton, ), ), diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart index 2e690f56..47dca1a6 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart @@ -59,7 +59,7 @@ class EditRequestURLParamsState extends ConsumerState { rows: paramRows, columns: [ DaviColumn( - name: 'Checkbox', + name: kNameCheckbox, width: 30, cellBuilder: (_, row) { int idx = row.index; @@ -80,7 +80,7 @@ class EditRequestURLParamsState extends ConsumerState { }, ), DaviColumn( - name: 'URL Parameter', + name: kNameURLParam, width: 70, grow: 1, cellBuilder: (_, row) { @@ -89,7 +89,7 @@ class EditRequestURLParamsState extends ConsumerState { return CellField( keyId: "$selectedId-$idx-params-k-$seed", initialValue: paramRows[idx].name, - hintText: "Add URL Parameter", + hintText: kHintAddURLParam, onChanged: (value) { paramRows[idx] = paramRows[idx].copyWith(name: value); if (isLast && !isAddingRow) { @@ -115,7 +115,7 @@ class EditRequestURLParamsState extends ConsumerState { }, ), DaviColumn( - name: 'Value', + name: kNameValue, grow: 1, cellBuilder: (_, row) { int idx = row.index; @@ -123,7 +123,7 @@ class EditRequestURLParamsState extends ConsumerState { return CellField( keyId: "$selectedId-$idx-params-v-$seed", initialValue: paramRows[idx].value, - hintText: "Add Value", + hintText: kHintAddValue, onChanged: (value) { paramRows[idx] = paramRows[idx].copyWith(value: value); if (isLast && !isAddingRow) { @@ -202,7 +202,7 @@ class EditRequestURLParamsState extends ConsumerState { }, icon: const Icon(Icons.add), label: const Text( - "Add Param", + kLabelAddParam, style: kTextStyleButton, ), ), diff --git a/lib/screens/home_page/editor_pane/details_card/response_pane.dart b/lib/screens/home_page/editor_pane/details_card/response_pane.dart index 703b873d..c1c770cf 100644 --- a/lib/screens/home_page/editor_pane/details_card/response_pane.dart +++ b/lib/screens/home_page/editor_pane/details_card/response_pane.dart @@ -34,7 +34,6 @@ class ResponseDetails extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - var sm = ScaffoldMessenger.of(context); final responseStatus = ref.watch( selectedRequestModelProvider.select((value) => value?.responseStatus)); final message = ref @@ -52,8 +51,6 @@ class ResponseDetails extends ConsumerWidget { ref .read(collectionStateNotifierProvider.notifier) .clearResponse(selectedRequest?.id); - sm.hideCurrentSnackBar(); - sm.showSnackBar(getSnackBar('Response cleared')); }, ), const Expanded( diff --git a/lib/widgets/buttons.dart b/lib/widgets/buttons.dart index f4280380..41bcddb9 100644 --- a/lib/widgets/buttons.dart +++ b/lib/widgets/buttons.dart @@ -246,15 +246,12 @@ class ClearResponseButton extends StatelessWidget { @override Widget build(BuildContext context) { - return Tooltip( - message: 'Clear response', - child: TextButton( - style: TextButton.styleFrom(minimumSize: const Size(40, 40)), - onPressed: onPressed, - child: const Icon( - Icons.delete, - size: 20, - ), + return IconButton( + tooltip: kTooltipClearResponse, + onPressed: onPressed, + icon: const Icon( + Icons.delete, + size: 16, ), ); } diff --git a/lib/widgets/request_widgets.dart b/lib/widgets/request_widgets.dart index ffac504e..7375a3b4 100644 --- a/lib/widgets/request_widgets.dart +++ b/lib/widgets/request_widgets.dart @@ -48,16 +48,12 @@ class _RequestPaneState extends State return Column( children: [ Padding( - padding: kPh20v10, + padding: kP8, child: SizedBox( height: kHeaderHeight, child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.end, children: [ - Text( - "Request", - style: Theme.of(context).textTheme.titleMedium, - ), FilledButton.tonalIcon( onPressed: widget.onPressedCodeButton, icon: Icon( @@ -68,7 +64,8 @@ class _RequestPaneState extends State label: SizedBox( width: 75, child: Text( - widget.codePaneVisible ? "Hide Code" : "View Code"), + widget.codePaneVisible ? kLabelHideCode : kLabelViewCode, + ), ), ), ], @@ -79,18 +76,19 @@ class _RequestPaneState extends State key: Key(widget.selectedId!), controller: _controller, overlayColor: kColorTransparentState, + labelPadding: kPh2, onTap: widget.onTapTabBar, tabs: [ TabLabel( - text: 'URL Params', + text: kLabelURLParams, showIndicator: widget.showIndicators[0], ), TabLabel( - text: 'Headers', + text: kLabelHeaders, showIndicator: widget.showIndicators[1], ), TabLabel( - text: 'Body', + text: kLabelBody, showIndicator: widget.showIndicators[2], ), ], diff --git a/lib/widgets/response_widgets.dart b/lib/widgets/response_widgets.dart index e641b03d..0b570c19 100644 --- a/lib/widgets/response_widgets.dart +++ b/lib/widgets/response_widgets.dart @@ -23,7 +23,7 @@ class NotSentWidget extends StatelessWidget { color: color, ), Text( - 'Not Sent', + kLabelNotSent, style: Theme.of(context).textTheme.titleMedium?.copyWith(color: color), ), @@ -66,42 +66,19 @@ class ResponsePaneHeader extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: kPh20v10, + padding: kPv8, child: SizedBox( height: kHeaderHeight, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text.rich( - TextSpan( - children: [ - const TextSpan( - text: "Response (", - ), - TextSpan( - text: "$responseStatus", - style: TextStyle( - color: getResponseStatusCodeColor( - responseStatus, - brightness: Theme.of(context).brightness, - ), - fontFamily: kCodeStyle.fontFamily, - ), - ), - const TextSpan( - text: ")", - ), - ], - style: Theme.of(context).textTheme.titleMedium, - ), - ), - kHSpacer20, + kHSpacer10, Expanded( child: Text( - message ?? "", + "$responseStatus: ${message ?? '-'}", softWrap: false, overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.titleMedium!.copyWith( + style: Theme.of(context).textTheme.bodyMedium?.copyWith( fontFamily: kCodeStyle.fontFamily, color: getResponseStatusCodeColor( responseStatus, @@ -110,10 +87,10 @@ class ResponsePaneHeader extends StatelessWidget { ), ), ), - kHSpacer20, + kHSpacer10, Text( humanizeDuration(time), - style: Theme.of(context).textTheme.titleMedium!.copyWith( + style: Theme.of(context).textTheme.bodyMedium?.copyWith( fontFamily: kCodeStyle.fontFamily, color: Theme.of(context).colorScheme.secondary, ), @@ -163,31 +140,15 @@ class _ResponseTabViewState extends State TabBar( key: Key(widget.selectedId!), controller: _controller, + labelPadding: kPh2, overlayColor: kColorTransparentState, onTap: (index) {}, tabs: const [ - SizedBox( - height: kTabHeight, - child: Center( - child: Text( - 'Body', - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - softWrap: false, - style: kTextStyleButton, - ), - ), + TabLabel( + text: kLabelResponseBody, ), - SizedBox( - height: kTabHeight, - child: Center( - child: Text( - 'Headers', - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - style: kTextStyleButton, - ), - ), + TabLabel( + text: kLabelHeaders, ), ], ), @@ -227,8 +188,8 @@ class ResponseHeadersHeader extends StatelessWidget { children: [ Expanded( child: Text( - "$name (${map.length} items)", - style: Theme.of(context).textTheme.labelLarge!.copyWith( + "$name (${map.length} $kLabelItems)", + style: Theme.of(context).textTheme.labelMedium?.copyWith( fontWeight: FontWeight.bold, ), ), @@ -243,8 +204,6 @@ class ResponseHeadersHeader extends StatelessWidget { } } -const kHeaderRow = ["Header Name", "Header Value"]; - class ResponseHeaders extends StatelessWidget { const ResponseHeaders({ super.key, @@ -263,7 +222,7 @@ class ResponseHeaders extends StatelessWidget { children: [ ResponseHeadersHeader( map: responseHeaders, - name: "Response Headers", + name: kLabelResponseHeaders, ), if (responseHeaders.isNotEmpty) kVSpacer5, if (responseHeaders.isNotEmpty) @@ -275,7 +234,7 @@ class ResponseHeaders extends StatelessWidget { kVSpacer10, ResponseHeadersHeader( map: requestHeaders, - name: "Request Headers", + name: kLabelRequestHeaders, ), if (requestHeaders.isNotEmpty) kVSpacer5, if (requestHeaders.isNotEmpty) @@ -302,20 +261,18 @@ class ResponseBody extends StatelessWidget { Widget build(BuildContext context) { final responseModel = selectedRequestModel?.responseModel; if (responseModel == null) { - return const ErrorMessage( - message: - 'Error: Response data does not exist. $kUnexpectedRaiseIssue'); + return const ErrorMessage(message: '$kMsgError $kUnexpectedRaiseIssue'); } var body = responseModel.body; var formattedBody = responseModel.formattedBody; if (body == null) { return const ErrorMessage( - message: 'Response body is missing (null). $kUnexpectedRaiseIssue'); + message: '$kMsgNullBody $kUnexpectedRaiseIssue'); } if (body.isEmpty) { return const ErrorMessage( - message: 'No content', + message: kMsgNoContent, showIcon: false, showIssueButton: false, ); @@ -325,7 +282,7 @@ class ResponseBody extends StatelessWidget { if (mediaType == null) { return ErrorMessage( message: - 'Unknown Response Content-Type - ${responseModel.contentType}. $kUnexpectedRaiseIssue'); + '$kMsgUnknowContentType - ${responseModel.contentType}. $kUnexpectedRaiseIssue'); } var responseBodyView = getResponseBodyViewOptions(mediaType); diff --git a/lib/widgets/tabs.dart b/lib/widgets/tabs.dart index eabf36c8..c2542a26 100644 --- a/lib/widgets/tabs.dart +++ b/lib/widgets/tabs.dart @@ -2,7 +2,11 @@ import 'package:flutter/material.dart'; import 'package:apidash/consts.dart'; class TabLabel extends StatelessWidget { - const TabLabel({super.key, required this.text, this.showIndicator = false}); + const TabLabel({ + super.key, + required this.text, + this.showIndicator = false, + }); final String text; final bool showIndicator; @@ -18,14 +22,14 @@ class TabLabel extends StatelessWidget { textAlign: TextAlign.center, overflow: TextOverflow.fade, softWrap: false, - style: kTextStyleButton, + style: kTextStyleTab, ), ), if (showIndicator) const Align( alignment: Alignment.topCenter, child: Padding( - padding: EdgeInsets.only(top: 6), + padding: EdgeInsets.only(top: 1), child: Icon( Icons.circle, size: 6, From 345f71a7c05b6f1a221f7ff4902e13f79d16035f Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 23 Mar 2024 18:05:20 +0530 Subject: [PATCH 34/39] Fix tests --- test/widgets/request_widgets_test.dart | 4 ---- test/widgets/response_widgets_test.dart | 9 +++------ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/test/widgets/request_widgets_test.dart b/test/widgets/request_widgets_test.dart index b50a033f..b47114b8 100644 --- a/test/widgets/request_widgets_test.dart +++ b/test/widgets/request_widgets_test.dart @@ -21,7 +21,6 @@ void main() { ); expect(find.byType(Center), findsAtLeastNWidgets(1)); - expect(find.text('Request'), findsOneWidget); expect(find.text('Hide Code'), findsOneWidget); expect(find.text('View Code'), findsNothing); expect(find.text('URL Params'), findsOneWidget); @@ -52,7 +51,6 @@ void main() { ); expect(find.byType(Center), findsAtLeastNWidgets(1)); - expect(find.text('Request'), findsOneWidget); expect(find.text('Hide Code'), findsOneWidget); expect(find.text('View Code'), findsNothing); expect(find.text('URL Params'), findsOneWidget); @@ -83,7 +81,6 @@ void main() { ); expect(find.byType(Center), findsAtLeastNWidgets(1)); - expect(find.text('Request'), findsOneWidget); expect(find.text('Hide Code'), findsNothing); expect(find.text('View Code'), findsOneWidget); expect(find.text('URL Params'), findsOneWidget); @@ -117,7 +114,6 @@ void main() { ); expect(find.byType(Center), findsAtLeastNWidgets(1)); - expect(find.text('Request'), findsOneWidget); expect(find.text('URL Params'), findsOneWidget); expect(find.text('Headers'), findsOneWidget); expect(find.text('Body'), findsOneWidget); diff --git a/test/widgets/response_widgets_test.dart b/test/widgets/response_widgets_test.dart index 7877a285..f6f3ecdd 100644 --- a/test/widgets/response_widgets_test.dart +++ b/test/widgets/response_widgets_test.dart @@ -52,11 +52,8 @@ void main() { ), ); - expect(find.byType(RichText), findsAtLeastNWidgets(1)); - expect( - find.textContaining("Response (", findRichText: true), findsOneWidget); - expect(find.text('Hi'), findsOneWidget); expect(find.textContaining("200", findRichText: true), findsOneWidget); + expect(find.textContaining("Hi", findRichText: true), findsOneWidget); expect(find.text(humanizeDuration(const Duration(microseconds: 23))), findsOneWidget); }); @@ -73,7 +70,7 @@ void main() { ), ); - expect(find.text('Body'), findsOneWidget); + expect(find.text('Response Body'), findsOneWidget); expect(find.text('Headers'), findsOneWidget); await tester.tap(find.text('Headers')); @@ -81,7 +78,7 @@ void main() { expect(find.text('first'), findsNothing); expect(find.text('second'), findsOneWidget); - await tester.tap(find.text('Body')); + await tester.tap(find.text('Response Body')); await tester.pumpAndSettle(); expect(find.text('first'), findsOneWidget); From 5a9dd7c07e98304fc4b6594d6376aa61fdf232d1 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 24 Mar 2024 01:18:28 +0530 Subject: [PATCH 35/39] Refactor --- lib/providers/collection_providers.dart | 35 +------------ lib/providers/ui_providers.dart | 2 + lib/screens/home_page/collection_pane.dart | 61 +++++++++++++--------- lib/widgets/textfields.dart | 31 +++++++++-- 4 files changed, 66 insertions(+), 63 deletions(-) diff --git a/lib/providers/collection_providers.dart b/lib/providers/collection_providers.dart index 30801ecb..db6b7cf3 100644 --- a/lib/providers/collection_providers.dart +++ b/lib/providers/collection_providers.dart @@ -1,4 +1,3 @@ -import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'settings_providers.dart'; import 'ui_providers.dart'; @@ -9,7 +8,6 @@ import '../consts.dart'; import 'package:http/http.dart' as http; final selectedIdStateProvider = StateProvider((ref) => null); -final searchQueryProvider = StateProvider((ref) => ''); final selectedRequestModelProvider = StateProvider((ref) { final selectedId = ref.watch(selectedIdStateProvider); @@ -33,7 +31,7 @@ final StateNotifierProvider?> class CollectionStateNotifier extends StateNotifier?> { CollectionStateNotifier(this.ref, this.hiveHandler) : super(null) { - var status = filterRequests(ref.read(searchQueryProvider.notifier).state); + var status = loadData(); Future.microtask(() { if (status) { ref.read(requestSequenceProvider.notifier).state = [ @@ -250,37 +248,6 @@ class CollectionStateNotifier } } - bool filterRequests(String query) { - if (query.isEmpty) { - - loadData(); - return true; - } else { - // Filter requests based on the query - final filteredRequests = state?.values.where((request) => - request.name.toLowerCase().contains(query.toLowerCase())).toList(); - - - if (filteredRequests != null && filteredRequests.isNotEmpty) { - - Map requestMap = {}; - - for (var request in filteredRequests) { - requestMap[request.id] = request; - } - - state = requestMap; - - } else { - print("No matching requests found"); - } - - - - return false; - } - } - Future saveData() async { ref.read(saveDataStateProvider.notifier).state = true; final saveResponse = ref.read(settingsProvider).saveResponses; diff --git a/lib/providers/ui_providers.dart b/lib/providers/ui_providers.dart index 2fddb2ad..62f83f70 100644 --- a/lib/providers/ui_providers.dart +++ b/lib/providers/ui_providers.dart @@ -23,3 +23,5 @@ final nameTextFieldFocusNodeProvider = }); return focusNode; }); + +final searchQueryProvider = StateProvider((ref) => ''); diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index c44ed56e..abfc1f28 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:apidash/providers/providers.dart'; @@ -67,33 +66,44 @@ class CollectionPane extends ConsumerWidget { style: kTextStyleButton, ), ), - ], ), ), kVSpacer10, - SizedBox( - height:30, - child:SearchBar( - - onChanged: (value){ - if(value.isNotEmpty){ - ref.read(searchQueryProvider.notifier).state = value; - ref.read(collectionStateNotifierProvider.notifier) - .filterRequests(value); - } - else{ - - } - - }, - hintText: "search", - leading: Icon(Icons.search), - textStyle: MaterialStateTextStyle.resolveWith((states) => TextStyle(fontSize: 15.0)), - elevation: MaterialStateProperty.all(2.0), - )), + Container( + height: 30, + margin: const EdgeInsets.only(right: 8), + decoration: BoxDecoration( + borderRadius: kBorderRadius8, + border: Border.all( + color: Theme.of(context).colorScheme.surfaceVariant, + ), + ), + child: Row( + children: [ + kHSpacer5, + Icon( + Icons.filter_alt, + size: 18, + color: Theme.of(context).colorScheme.secondary, + ), + kHSpacer5, + Expanded( + child: RawTextField( + style: Theme.of(context).textTheme.bodyMedium, + hintText: "Filter by name or URL", + onChanged: (value) { + if (value.trim().isNotEmpty) { + ref.read(searchQueryProvider.notifier).state = value; + print(value); + } + }, + ), + ), + ], + ), + ), kVSpacer10, - const Expanded( child: RequestList(), ), @@ -134,7 +144,7 @@ class _RequestListState extends ConsumerState { final alwaysShowCollectionPaneScrollbar = ref.watch(settingsProvider .select((value) => value.alwaysShowCollectionPaneScrollbar)); - return requestItems.isNotEmpty?Scrollbar( + return Scrollbar( controller: controller, thumbVisibility: alwaysShowCollectionPaneScrollbar ? true : null, radius: const Radius.circular(12), @@ -155,7 +165,6 @@ class _RequestListState extends ConsumerState { }, itemBuilder: (context, index) { var id = requestSequence[index]; - print(requestItems[id]!); return ReorderableDragStartListener( key: ValueKey(id), @@ -170,7 +179,7 @@ class _RequestListState extends ConsumerState { ); }, ), - ):Text("data"); + ); } } diff --git a/lib/widgets/textfields.dart b/lib/widgets/textfields.dart index da68f9e2..35d3437f 100644 --- a/lib/widgets/textfields.dart +++ b/lib/widgets/textfields.dart @@ -94,14 +94,39 @@ class JsonSearchField extends StatelessWidget { @override Widget build(BuildContext context) { - return TextField( + return RawTextField( controller: controller, onChanged: onChanged, style: kCodeStyle, - decoration: const InputDecoration( + hintText: 'Search..', + ); + } +} + +class RawTextField extends StatelessWidget { + const RawTextField({ + super.key, + this.onChanged, + this.controller, + this.hintText, + this.style, + }); + + final void Function(String)? onChanged; + final TextEditingController? controller; + final String? hintText; + final TextStyle? style; + + @override + Widget build(BuildContext context) { + return TextField( + controller: controller, + onChanged: onChanged, + style: style, + decoration: InputDecoration( isDense: true, border: InputBorder.none, - hintText: 'Search..', + hintText: hintText, ), ); } From 77cb9db9a35566a7c0b543026a3c092ca2a0be55 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 24 Mar 2024 01:20:38 +0530 Subject: [PATCH 36/39] Update collection_pane.dart --- lib/screens/home_page/collection_pane.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index abfc1f28..1ba202eb 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -165,7 +165,6 @@ class _RequestListState extends ConsumerState { }, itemBuilder: (context, index) { var id = requestSequence[index]; - return ReorderableDragStartListener( key: ValueKey(id), index: index, From 1451ea756702f58e4dc7b9aac2d1ca542dcc37c0 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 24 Mar 2024 02:37:47 +0530 Subject: [PATCH 37/39] Update collection_pane.dart --- lib/screens/home_page/collection_pane.dart | 68 +++++++++++----------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index 1ba202eb..7d89a375 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -93,10 +93,7 @@ class CollectionPane extends ConsumerWidget { style: Theme.of(context).textTheme.bodyMedium, hintText: "Filter by name or URL", onChanged: (value) { - if (value.trim().isNotEmpty) { - ref.read(searchQueryProvider.notifier).state = value; - print(value); - } + ref.read(searchQueryProvider.notifier).state = value; }, ), ), @@ -143,41 +140,44 @@ class _RequestListState extends ConsumerState { final requestItems = ref.watch(collectionStateNotifierProvider)!; final alwaysShowCollectionPaneScrollbar = ref.watch(settingsProvider .select((value) => value.alwaysShowCollectionPaneScrollbar)); + final filterQuery = ref.watch(searchQueryProvider).trim(); return Scrollbar( controller: controller, thumbVisibility: alwaysShowCollectionPaneScrollbar ? true : null, radius: const Radius.circular(12), - child: ReorderableListView.builder( - padding: kPr8CollectionPane, - scrollController: controller, - buildDefaultDragHandles: false, - itemCount: requestItems.length, - onReorder: (int oldIndex, int newIndex) { - if (oldIndex < newIndex) { - newIndex -= 1; - } - if (oldIndex != newIndex) { - ref - .read(collectionStateNotifierProvider.notifier) - .reorder(oldIndex, newIndex); - } - }, - itemBuilder: (context, index) { - var id = requestSequence[index]; - return ReorderableDragStartListener( - key: ValueKey(id), - index: index, - child: Padding( - padding: kP1, - child: RequestItem( - id: id, - requestModel: requestItems[id]!, - ), - ), - ); - }, - ), + child: filterQuery.isEmpty + ? ReorderableListView.builder( + padding: kPr8CollectionPane, + scrollController: controller, + buildDefaultDragHandles: false, + itemCount: requestItems.length, + onReorder: (int oldIndex, int newIndex) { + if (oldIndex < newIndex) { + newIndex -= 1; + } + if (oldIndex != newIndex) { + ref + .read(collectionStateNotifierProvider.notifier) + .reorder(oldIndex, newIndex); + } + }, + itemBuilder: (context, index) { + var id = requestSequence[index]; + return ReorderableDragStartListener( + key: ValueKey(id), + index: index, + child: Padding( + padding: kP1, + child: RequestItem( + id: id, + requestModel: requestItems[id]!, + ), + ), + ); + }, + ) + : SizedBox(), ); } } From 4243904aa97d9fbb3d5a4d701312eb343bea9d66 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 24 Mar 2024 02:56:28 +0530 Subject: [PATCH 38/39] Filtered list --- lib/screens/home_page/collection_pane.dart | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index d377e786..0ede2021 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -93,7 +93,8 @@ class CollectionPane extends ConsumerWidget { style: Theme.of(context).textTheme.bodyMedium, hintText: "Filter by name or URL", onChanged: (value) { - ref.read(searchQueryProvider.notifier).state = value; + ref.read(searchQueryProvider.notifier).state = + value.toLowerCase(); }, ), ), @@ -177,7 +178,24 @@ class _RequestListState extends ConsumerState { ); }, ) - : SizedBox(), + : ListView( + padding: kPe8, + controller: controller, + children: requestSequence.map((id) { + var item = requestItems[id]!; + if (item.url.toLowerCase().contains(filterQuery) || + item.name.toLowerCase().contains(filterQuery)) { + return Padding( + padding: kP1, + child: RequestItem( + id: id, + requestModel: item, + ), + ); + } + return const SizedBox(); + }).toList(), + ), ); } } From 8a8ded7d203fd00bb78374cfef6bea020278e113 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 24 Mar 2024 03:18:04 +0530 Subject: [PATCH 39/39] Update tests --- test/providers/ui_providers_test.dart | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index dde4983f..9d34619d 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -214,13 +214,13 @@ void main() { }); group("Testing selectedIdEditStateProvider", () { - testWidgets( - 'selectedIdEditStateProvider should have an initial value of null', - (tester) async { + testWidgets('It should have an initial value of null', (tester) async { await tester.pumpWidget( const ProviderScope( child: MaterialApp( - home: CollectionPane(), + home: Scaffold( + body: CollectionPane(), + ), ), ), ); @@ -237,7 +237,9 @@ void main() { await tester.pumpWidget( const ProviderScope( child: MaterialApp( - home: CollectionPane(), + home: Scaffold( + body: CollectionPane(), + ), ), ), ); @@ -267,7 +269,9 @@ void main() { await tester.pumpWidget( const ProviderScope( child: MaterialApp( - home: CollectionPane(), + home: Scaffold( + body: CollectionPane(), + ), ), ), ); @@ -303,7 +307,9 @@ void main() { await tester.pumpWidget( const ProviderScope( child: MaterialApp( - home: CollectionPane(), + home: Scaffold( + body: CollectionPane(), + ), ), ), );