From 6b981b33c2a216ec23e0f8317256175e12170fcc Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sun, 6 Apr 2025 12:15:27 +0530 Subject: [PATCH 001/188] chore: implement functions for fake data generation --- lib/utils/fake_data_provider.dart | 144 ++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 lib/utils/fake_data_provider.dart diff --git a/lib/utils/fake_data_provider.dart b/lib/utils/fake_data_provider.dart new file mode 100644 index 000000000..6c5b84e7b --- /dev/null +++ b/lib/utils/fake_data_provider.dart @@ -0,0 +1,144 @@ +import 'dart:math'; + +/// A utility class that provides functions to generate fake data for API testing +class FakeDataProvider { + static final Random _random = Random(); + + static String randomUsername() { + final prefixes = ['user', 'test', 'dev', 'qa', 'admin', 'guest', 'john', 'jane']; + final suffixes = ['123', '2023', '_test', '_dev', '_admin', '_guest']; + + final prefix = prefixes[_random.nextInt(prefixes.length)]; + final suffix = _random.nextInt(10) > 5 ? suffixes[_random.nextInt(suffixes.length)] : ''; + + return '$prefix$suffix'; + } + + static String randomEmail() { + final usernames = ['john', 'jane', 'user', 'test', 'dev', 'admin', 'info', 'support']; + final domains = ['example.com', 'test.com', 'acme.org', 'email.net', 'company.io', 'service.dev']; + + final username = usernames[_random.nextInt(usernames.length)]; + final randomNum = _random.nextInt(1000); + final domain = domains[_random.nextInt(domains.length)]; + + return '$username$randomNum@$domain'; + } + + static String randomId() { + return _random.nextInt(10000).toString().padLeft(4, '0'); + } + + static String randomUuid() { + const chars = 'abcdef0123456789'; + final segments = [8, 4, 4, 4, 12]; //standard length of different segments of uuid + + final uuid = segments.map((segment) { + return List.generate(segment, (_) => chars[_random.nextInt(chars.length)]).join(); + }).join('-'); + + return uuid; + } + + static String randomName() { + final firstNames = ['John', 'Jane', 'Michael', 'Emily', 'David', 'Sarah', 'Robert', 'Lisa']; + final lastNames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller', 'Davis', 'Wilson']; + + final firstName = firstNames[_random.nextInt(firstNames.length)]; + final lastName = lastNames[_random.nextInt(lastNames.length)]; + + return '$firstName $lastName'; + } + + static String randomPhone() { + final areaCode = (100 + _random.nextInt(900)).toString(); + final firstPart = (100 + _random.nextInt(900)).toString(); + final secondPart = (1000 + _random.nextInt(9000)).toString(); + + return '+1-$areaCode-$firstPart-$secondPart'; + } + + static String randomAddress() { + final streetNumbers = List.generate(100, (i) => (i + 1) * 10); + final streetNames = ['Main St', 'Oak Ave', 'Park Rd', 'Maple Dr', 'Pine Ln', 'Cedar Blvd']; + final cities = ['Springfield', 'Rivertown', 'Lakeside', 'Mountainview', 'Brookfield']; + final states = ['CA', 'NY', 'TX', 'FL', 'IL', 'WA']; + final zipCodes = List.generate(90, (i) => 10000 + (i * 1000) + _random.nextInt(999)); + + final streetNumber = streetNumbers[_random.nextInt(streetNumbers.length)]; + final streetName = streetNames[_random.nextInt(streetNames.length)]; + final city = cities[_random.nextInt(cities.length)]; + final state = states[_random.nextInt(states.length)]; + final zipCode = zipCodes[_random.nextInt(zipCodes.length)]; + + return '$streetNumber $streetName, $city, $state $zipCode'; + } + + static String randomDate() { + final now = DateTime.now(); + final randomDays = _random.nextInt(1000) - 500; + final date = now.add(Duration(days: randomDays)); + + return date.toIso8601String().split('T')[0]; + } + + static String randomDateTime() { + final now = DateTime.now(); + final randomDays = _random.nextInt(100) - 50; + final randomHours = _random.nextInt(24); + final randomMinutes = _random.nextInt(60); + final randomSeconds = _random.nextInt(60); + + final dateTime = now.add(Duration( + days: randomDays, + hours: randomHours, + minutes: randomMinutes, + seconds: randomSeconds, + )); + + return dateTime.toIso8601String(); + } + + static String randomBoolean() { + return _random.nextBool().toString(); + } + + static String randomNumber({int min = 0, int max = 1000}) { + return (_random.nextInt(max - min) + min).toString(); + } + + static String randomJson() { + return '{"id": ${randomId()}, "name": "${randomName()}", "email": "${randomEmail()}", "active": ${randomBoolean()}}'; + } + + static String processFakeDataTag(String tag) { + switch (tag.toLowerCase()) { + case 'randomusername': + return randomUsername(); + case 'randomemail': + return randomEmail(); + case 'randomid': + return randomId(); + case 'randomuuid': + return randomUuid(); + case 'randomname': + return randomName(); + case 'randomphone': + return randomPhone(); + case 'randomaddress': + return randomAddress(); + case 'randomdate': + return randomDate(); + case 'randomdatetime': + return randomDateTime(); + case 'randomboolean': + return randomBoolean(); + case 'randomnumber': + return randomNumber(); + case 'randomjson': + return randomJson(); + default: + return '{{$tag}}'; + } + } +} From 358019d30c3234851e0b746ed935e319b7619ff2 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sun, 6 Apr 2025 12:24:18 +0530 Subject: [PATCH 002/188] chore: create pane for fake data access and guide --- .../envvar/editor_pane/fake_data_pane.dart | 214 ++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 lib/screens/envvar/editor_pane/fake_data_pane.dart diff --git a/lib/screens/envvar/editor_pane/fake_data_pane.dart b/lib/screens/envvar/editor_pane/fake_data_pane.dart new file mode 100644 index 000000000..bd1685eb2 --- /dev/null +++ b/lib/screens/envvar/editor_pane/fake_data_pane.dart @@ -0,0 +1,214 @@ +import 'package:apidash_design_system/apidash_design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:apidash/utils/fake_data_provider.dart'; + +class FakeDataProvidersPane extends ConsumerWidget { + const FakeDataProvidersPane({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: kBorderRadius12, + ), + margin: kP10, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Fake Data Providers', + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 10), + Text( + 'Use these placeholders in your API requests to generate random test data automatically.', + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 20), + Text( + 'How to use:', + style: Theme.of(context).textTheme.titleSmall, + ), + const SizedBox(height: 5), + Text( + 'Type {{\$tagName}} in any field where you want to use random data.', + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 10), + Text( + 'For example: {{\$randomEmail}} will be replaced with a randomly generated email address.', + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 30), + Text( + 'Available Placeholders:', + style: Theme.of(context).textTheme.titleSmall, + ), + const SizedBox(height: 20), + _buildFakeDataTable(context), + ], + ), + ), + ), + ), + ], + ), + ); + } + + Widget _buildFakeDataTable(BuildContext context) { + final fakeDataTags = [ + _FakeDataItem( + name: 'randomUsername', + description: 'Random username (e.g., user123, test_dev)', + example: FakeDataProvider.randomUsername(), + ), + _FakeDataItem( + name: 'randomEmail', + description: 'Random email address', + example: FakeDataProvider.randomEmail(), + ), + _FakeDataItem( + name: 'randomId', + description: 'Random numeric ID', + example: FakeDataProvider.randomId(), + ), + _FakeDataItem( + name: 'randomUuid', + description: 'Random UUID', + example: FakeDataProvider.randomUuid(), + ), + _FakeDataItem( + name: 'randomName', + description: 'Random full name', + example: FakeDataProvider.randomName(), + ), + _FakeDataItem( + name: 'randomPhone', + description: 'Random phone number', + example: FakeDataProvider.randomPhone(), + ), + _FakeDataItem( + name: 'randomAddress', + description: 'Random address', + example: FakeDataProvider.randomAddress(), + ), + _FakeDataItem( + name: 'randomDate', + description: 'Random date (YYYY-MM-DD)', + example: FakeDataProvider.randomDate(), + ), + _FakeDataItem( + name: 'randomDateTime', + description: 'Random date and time (ISO format)', + example: FakeDataProvider.randomDateTime(), + ), + _FakeDataItem( + name: 'randomBoolean', + description: 'Random boolean value (true/false)', + example: FakeDataProvider.randomBoolean(), + ), + _FakeDataItem( + name: 'randomNumber', + description: 'Random number between 0-1000', + example: FakeDataProvider.randomNumber(), + ), + _FakeDataItem( + name: 'randomJson', + description: 'Random JSON object with basic fields', + example: FakeDataProvider.randomJson(), + ), + ]; + + return Table( + columnWidths: const { + 0: FlexColumnWidth(1.2), + 1: FlexColumnWidth(2), + 2: FlexColumnWidth(1.5), + 3: FlexColumnWidth(0.5), + }, + defaultVerticalAlignment: TableCellVerticalAlignment.middle, + border: TableBorder.all( + color: Theme.of(context).colorScheme.outline, + width: 1, + ), + children: [ + + TableRow( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainerHigh, + ), + children: [ + _buildTableCell(context, 'Placeholder', isHeader: true), + _buildTableCell(context, 'Description', isHeader: true), + _buildTableCell(context, 'Example Output', isHeader: true), + _buildTableCell(context, 'Copy', isHeader: true), + ], + ), + + for (var item in fakeDataTags) + TableRow( + children: [ + _buildTableCell(context, '{{\$${item.name}}}'), + _buildTableCell(context, item.description), + _buildTableCell(context, item.example), + _buildCopyButton(context, '{{\$${item.name}}}'), + ], + ), + ], + ); + } + + Widget _buildTableCell(BuildContext context, String text, {bool isHeader = false}) { + return Padding( + padding: const EdgeInsets.all(8), + child: Text( + text, + style: isHeader + ? Theme.of(context).textTheme.titleSmall + : Theme.of(context).textTheme.bodyMedium, + ), + ); + } + + Widget _buildCopyButton(BuildContext context, String textToCopy) { + return Padding( + padding: const EdgeInsets.all(4), + child: IconButton( + icon: const Icon(Icons.copy, size: 18), + onPressed: () { + Clipboard.setData(ClipboardData(text: textToCopy)); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Copied $textToCopy to clipboard'), + duration: const Duration(seconds: 1), + ), + ); + }, + tooltip: 'Copy to clipboard', + ), + ); + } +} + +class _FakeDataItem { + final String name; + final String description; + final String example; + + _FakeDataItem({ + required this.name, + required this.description, + required this.example, + }); +} From 94c72aef7786246d8f1a59672d64db83f3926e53 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sun, 6 Apr 2025 18:07:51 +0530 Subject: [PATCH 003/188] chore: edit env utils and ui to implement fake data providers --- lib/screens/envvar/environment_editor.dart | 90 ++++++++++++++++++---- lib/utils/envvar_utils.dart | 22 ++++-- 2 files changed, 90 insertions(+), 22 deletions(-) diff --git a/lib/screens/envvar/environment_editor.dart b/lib/screens/envvar/environment_editor.dart index 4ca07b570..8ea924120 100644 --- a/lib/screens/envvar/environment_editor.dart +++ b/lib/screens/envvar/environment_editor.dart @@ -6,10 +6,81 @@ import 'package:apidash/widgets/widgets.dart'; import 'package:apidash/consts.dart'; import '../common_widgets/common_widgets.dart'; import './editor_pane/variables_pane.dart'; +import './editor_pane/fake_data_pane.dart'; + +final environmentEditorTabProvider = StateProvider((ref) => 0); class EnvironmentEditor extends ConsumerWidget { const EnvironmentEditor({super.key}); + Widget _buildTabBar(BuildContext context, WidgetRef ref) { + final selectedTab = ref.watch(environmentEditorTabProvider); + + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildTabButton( + context, + 'Environment Variables', + 0, + selectedTab == 0, + () => ref.read(environmentEditorTabProvider.notifier).state = 0, + ), + const SizedBox(width: 16), + _buildTabButton( + context, + 'Fake Data Providers', + 1, + selectedTab == 1, + () => ref.read(environmentEditorTabProvider.notifier).state = 1, + ), + ], + ); + } + + Widget _buildTabButton(BuildContext context, String title, int index, bool isSelected, VoidCallback onPressed) { + return ElevatedButton( + onPressed: onPressed, + style: ElevatedButton.styleFrom( + backgroundColor: isSelected + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context).colorScheme.surfaceContainerLow, + foregroundColor: isSelected + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onSurface, + elevation: 0, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + ), + child: Text(title), + ); + } + + Widget _buildTabContent(WidgetRef ref) { + final selectedTab = ref.watch(environmentEditorTabProvider); + + if (selectedTab == 0) { + return Column( + children: [ + const Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox(width: 30), + Text("Variable"), + SizedBox(width: 30), + Text("Value"), + SizedBox(width: 40), + ], + ), + kHSpacer40, + const Divider(), + const Expanded(child: EditEnvironmentVariables()), + ], + ); + } else { + return const FakeDataProvidersPane(); + } + } + @override Widget build(BuildContext context, WidgetRef ref) { final id = ref.watch(selectedEnvironmentIdStateProvider); @@ -84,24 +155,13 @@ class EnvironmentEditor extends ConsumerWidget { borderRadius: kBorderRadius12, ), elevation: 0, - child: const Padding( + child: Padding( padding: kPv6, child: Column( children: [ - kHSpacer40, - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SizedBox(width: 30), - Text("Variable"), - SizedBox(width: 30), - Text("Value"), - SizedBox(width: 40), - ], - ), - kHSpacer40, - Divider(), - Expanded(child: EditEnvironmentVariables()) + _buildTabBar(context, ref), + kHSpacer20, + Expanded(child: _buildTabContent(ref)) ], ), ), diff --git a/lib/utils/envvar_utils.dart b/lib/utils/envvar_utils.dart index c94f6eebc..0c0977da0 100644 --- a/lib/utils/envvar_utils.dart +++ b/lib/utils/envvar_utils.dart @@ -1,5 +1,6 @@ import 'package:apidash_core/apidash_core.dart'; import 'package:apidash/consts.dart'; +import 'fake_data_provider.dart'; String getEnvironmentTitle(String? name) { if (name == null || name.trim() == "") { @@ -41,14 +42,21 @@ String? substituteVariables( Map envVarMap, ) { if (input == null) return null; - if (envVarMap.keys.isEmpty) { - return input; + + String result = input; + + if (envVarMap.keys.isNotEmpty) { + final envVarRegex = RegExp("{{(${envVarMap.keys.join('|')})}}"); + result = result.replaceAllMapped(envVarRegex, (match) { + final key = match.group(1)?.trim() ?? ''; + return envVarMap[key] ?? '{{$key}}'; + }); } - final regex = RegExp("{{(${envVarMap.keys.join('|')})}}"); - - String result = input.replaceAllMapped(regex, (match) { - final key = match.group(1)?.trim() ?? ''; - return envVarMap[key] ?? '{{$key}}'; + + final fakeDataRegex = RegExp(r'{{(\$[a-zA-Z0-9]+)}}'); + result = result.replaceAllMapped(fakeDataRegex, (match) { + final key = match.group(1)?.trim().substring(1) ?? ''; + return FakeDataProvider.processFakeDataTag(key); }); return result; From cdc7391990204b8cbdc191dc18ac71377eb55fec Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sun, 6 Apr 2025 21:10:40 +0530 Subject: [PATCH 004/188] fix: variable hovering shows random generated value --- lib/screens/common_widgets/envvar_span.dart | 8 +++-- lib/utils/envvar_utils.dart | 37 +++++++++++++++++---- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/lib/screens/common_widgets/envvar_span.dart b/lib/screens/common_widgets/envvar_span.dart index c9038d280..ee8a22e2f 100644 --- a/lib/screens/common_widgets/envvar_span.dart +++ b/lib/screens/common_widgets/envvar_span.dart @@ -27,9 +27,11 @@ class EnvVarSpan extends HookConsumerWidget { final showPopover = useState(false); final isMissingVariable = suggestion.isUnknown; - final String scope = isMissingVariable - ? 'unknown' - : getEnvironmentTitle(environments?[suggestion.environmentId]?.name); + final String scope = suggestion.environmentId == "Random" + ? "Random" + : isMissingVariable + ? 'unknown' + : getEnvironmentTitle(environments?[suggestion.environmentId]?.name); final colorScheme = Theme.of(context).colorScheme; var text = Text( diff --git a/lib/utils/envvar_utils.dart b/lib/utils/envvar_utils.dart index 0c0977da0..3a91c70e6 100644 --- a/lib/utils/envvar_utils.dart +++ b/lib/utils/envvar_utils.dart @@ -42,9 +42,9 @@ String? substituteVariables( Map envVarMap, ) { if (input == null) return null; - + String result = input; - + if (envVarMap.keys.isNotEmpty) { final envVarRegex = RegExp("{{(${envVarMap.keys.join('|')})}}"); result = result.replaceAllMapped(envVarRegex, (match) { @@ -52,7 +52,7 @@ String? substituteVariables( return envVarMap[key] ?? '{{$key}}'; }); } - + final fakeDataRegex = RegExp(r'{{(\$[a-zA-Z0-9]+)}}'); result = result.replaceAllMapped(fakeDataRegex, (match) { final key = match.group(1)?.trim().substring(1) ?? ''; @@ -159,9 +159,32 @@ EnvironmentVariableSuggestion getVariableStatus( ); } + // If not found in environments check if it's a random data generator + if (key.startsWith('\$')) { + final generatorType = key.substring(1); + final generator = FakeDataProvider.processFakeDataTag(generatorType); + if (generator != '{{generatorType}}') { + return EnvironmentVariableSuggestion( + environmentId: "Random", + variable: EnvironmentVariableModel( + key: key, + type: EnvironmentVariableType.variable, + value: generator, + enabled: true, + ), + isUnknown: false, + ); + } + } + return EnvironmentVariableSuggestion( - isUnknown: true, - environmentId: "unknown", - variable: EnvironmentVariableModel( - key: key, type: EnvironmentVariableType.variable, value: "unknown")); + isUnknown: true, + environmentId: "unknown", + variable: EnvironmentVariableModel( + key: key, + type: EnvironmentVariableType.variable, + value: "unknown", + // enabled: false, + ), + ); } From 8ac9cb5f0965faa9768b9b1fedd964ae1e3b1de6 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sun, 6 Apr 2025 21:46:24 +0530 Subject: [PATCH 005/188] chore: remove additional check for variable hovering --- lib/utils/envvar_utils.dart | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/utils/envvar_utils.dart b/lib/utils/envvar_utils.dart index 3a91c70e6..448690811 100644 --- a/lib/utils/envvar_utils.dart +++ b/lib/utils/envvar_utils.dart @@ -43,20 +43,14 @@ String? substituteVariables( ) { if (input == null) return null; - String result = input; - - if (envVarMap.keys.isNotEmpty) { - final envVarRegex = RegExp("{{(${envVarMap.keys.join('|')})}}"); - result = result.replaceAllMapped(envVarRegex, (match) { - final key = match.group(1)?.trim() ?? ''; - return envVarMap[key] ?? '{{$key}}'; - }); + if (envVarMap.keys.isEmpty) { + return input; } + final regex = RegExp("{{(${envVarMap.keys.join('|')})}}"); - final fakeDataRegex = RegExp(r'{{(\$[a-zA-Z0-9]+)}}'); - result = result.replaceAllMapped(fakeDataRegex, (match) { - final key = match.group(1)?.trim().substring(1) ?? ''; - return FakeDataProvider.processFakeDataTag(key); + String result = input.replaceAllMapped(regex, (match) { + final key = match.group(1)?.trim() ?? ''; + return envVarMap[key] ?? '{{$key}}'; }); return result; From 54dc0944c0660f8bd89e73528d87fc724716199a Mon Sep 17 00:00:00 2001 From: Manas Hejmadi | Developer <67999871+synapsecode@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:39:20 +0530 Subject: [PATCH 006/188] Manas Hejmadi's GSoC'25 Application (Initial Version) --- ...n_Manas Hejmadi_AI UI Designer For APIs.md | 1005 ++++++++--------- 1 file changed, 485 insertions(+), 520 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md b/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md index 588fa9145..a2a184cf5 100644 --- a/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md +++ b/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md @@ -1,221 +1,197 @@ +# AI-Powered API Response to Dynamic UI Generator -![](images/GSOCBANNER_APIDASH.jpg) +![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/GSOCBANNER.jpg) -# AI-Based API Response to Dynamic UI and Tool Generator +### Personal Information -### Personal Information - **Full Name:** Manas M Hejmadi -- **Contact Info:** manashejmadi@gmail.com `+918904995101` +- **Contact Info:** [manashejmadi@gmail.com](mailto:manashejmadi@gmail.com) `+918904995101` - **Discord Handle:** `synapse_45006` -- **Github Profile:** https://github.com/synapsecode +- **Github Profile:** [https://github.com/synapsecode](https://github.com/synapsecode) - **Resume:** [Link](https://manashejmadi.vercel.app/images/MANAS_RESUME.pdf) - **Website:** [Link](https://manashejmadi.vercel.app/) - **Time Zone**: GMT +05:30 ### University Information + - **University Name:** Dayananda Sagar College of Engineering (DSCE), Bengaluru -- **Program Enrolled In:** BE in Computer Science & Engineering (CSE) -- **Year:** Pre-final Year (Third Year) +- **Program Enrolled In:** BE in Computer Science & Engineering (CSE) +- **Year:** Pre-final Year (Third Year) - **Expected Graduation Date:** May 2026 ### Motivation & Past Experience -1. Have you worked on or contributed to a FOSS project before? Can you attach repo links or relevant PRs? - - **ANS**: Have worked on large codebases but no direct experience with contribution. +1. Have you worked on or contributed to a FOSS project before? Can you attach repo links or relevant PRs? + - **ANS**: Have worked on large codebases but no direct experience with contribution. 2. What is your one project/achievement that you are most proud of? Why? - - **ANS**: I am most proud of my microblogging application, _Microblogger_. It was a large project I undertook with the goal of learning Flutter, and through it, I gained a deep understanding of the framework. What makes me most proud is the Instagram-like story editor that I built within the app. It was a challenging feature that required creative problem-solving, and successfully implementing it taught me a lot. + - **ANS**: I am most proud of my microblogging application, *Microblogger*. It was a large project I undertook with the goal of learning Flutter, and through it, I gained a deep understanding of the framework. What makes me most proud is the Instagram-like story editor that I built within the app. It was a challenging feature that required creative problem-solving, and successfully implementing it taught me a lot. 3. What kind of problems or challenges motivate you the most to solve them? - - **ANS**: I am particularly motivated by challenges that address real pain points for users, as solving these issues has a direct, meaningful impact. I thrive on problems that require creative and efficient solutions, especially when they need to scale effectively. Additionally, I enjoy opportunities that allow me to take ownership of the project and make critical decisions that shape the direction of the solution. + - **ANS**: I am particularly motivated by challenges that address real pain points for users, as solving these issues has a direct, meaningful impact. I thrive on problems that require creative and efficient solutions, especially when they need to scale effectively. Additionally, I enjoy opportunities that allow me to take ownership of the project and make critical decisions that shape the direction of the solution. 4. Will you be working on GSoC full-time? In case not, what will you be studying or working on while working on the project? - - **ANS**: Yes, I will primarily be working full-time on GSoC. Occasionally, I may have exams, course projects, or job/internship responsibilities, but they will not impact my commitment to GSoC. + - **ANS**: Yes, I will primarily be working full-time on GSoC. Occasionally, I may have exams, course projects, or job/internship responsibilities, but they will not impact my commitment to GSoC. 5. Do you mind regularly syncing up with the project mentors? - - **ANS**: I am available after 5PM IST and can get on calls when needed + - **ANS**: I am available after 5PM IST and can get on calls when needed 6. What interests you the most about API Dash? - - **ANS**: It has a clean and minimalist design with a super fast startup time. It does exactly what it's meant to and gets straight to the point! Plus, Flutter and Dart hold a special place for me since they helped me land my first gig. I've really been enjoying using API Dash. + - **ANS**: It has a clean and minimalist design with a super fast startup time. It does exactly what it's meant to and gets straight to the point! Plus, Flutter and Dart hold a special place for me since they helped me land my first gig. I've really been enjoying using API Dash. 7. Can you mention some areas where the project can be improved? - - **ANS**: One feature that I wanted to include is code to collection generation. Where, we just put the flask code (for example) into the textbox and the LLM agent automatically creates relevant Requests and Collections. This would speed up development and testing time. - - -## Project Details + - **ANS**: One feature that I wanted to include is code to collection generation. Where, we just put the flask code (for example) into the textbox and the LLM agent automatically creates relevant Requests and Collections. This would speed up development and testing time. -- **Project Title:** AI-Based API Response to Dynamic UI ( & API Tool Generator ) -- **Issue:** [#617](https://github.com/foss42/apidash/issues/617) -- **Issue Description:** Develop an AI Agent which transforms API responses into dynamic, user-friendly UI components, enabling developers to visualize and interact with data effortlessly. By analyzing API response structures—such as JSON or XML—the agent automatically generates UI elements like tables, charts, forms, and cards, eliminating the need for manual UI development. One can connect an API endpoint, receive real-time responses, and instantly generate UI components that adapt to the data format. It must also support customization options, allowing developers to configure layouts, styles, and interactive elements such as filters, pagination, and sorting. Finally, users must be able to easily export the generated UI and integrate it in their Flutter or Web apps. +## Project Details +- **Project Title:** AI-Powered API Response to Dynamic UI Generator +- **Issue:** [#617](https://github.com/foss42/apidash/issues/617) +- **Issue Description:** Develop an AI Agent which transforms API responses into dynamic, user-friendly UI components, enabling developers to visualize and interact with data effortlessly. By analyzing API response structures—such as JSON or XML—the agent automatically generates UI elements like tables, charts, forms, and cards, eliminating the need for manual UI development. One can connect an API endpoint, receive real-time responses, and instantly generate UI components that adapt to the data format. It must also support customization options, allowing developers to configure layouts, styles, and interactive elements such as filters, pagination, and sorting. Finally, users must be able to easily export the generated UI and integrate it in their Flutter or Web apps. +- **Abstract:** + + This project introduces an AI-powered agent that **automates the transformation of API responses into structured UI schemas and functional UI components** across various frontend frameworks such as ReactJS, Flutter, and HTML. Using Large Language Models (LLMs), the system eliminates the need for manual UI creation by intelligently analyzing API responses and generating corresponding UI structures. Additionally, it enables **dynamic modification of UI components** based on user prompts, allowing for customization in design, layout, and behavior. By seamlessly integrating these capabilities into a unified workflow, the project streamlines frontend development, reducing manual effort and accelerating the UI generation process with a **single-click solution.** + +- **Project Goals:** + - Conversion from API Response (JSON/XML) into functional UI components such as tables, charts and forms. + - Provide customization options for layout, styles, and interactive elements. + - Allow users to **modify UI components through natural language prompts** + - Generate UI components for multiple frontend frameworks such as Flutter/NextJS etc + - **One Click Export** of the generated UI into code + - Seamlessly integrate all features into a single, easy-to-use system. -- **Abstract:** - This project introduces an AI-powered agent that **automates the transformation of API responses into structured UI schemas and functional UI components** across various frontend frameworks. Using Large Language Models (LLMs), the system eliminates the need for manual UI creation by intelligently analyzing API responses and generating corresponding UI structures. Additionally, it enables **dynamic modification of UI components** based on user prompts, allowing for customization in design, layout, and behavior. By seamlessly integrating these capabilities into a unified workflow, the project streamlines frontend development, reducing manual effort and accelerating the UI generation process with a **single-click solution.** - The project also aims to create a simple one-click API Request to Tool Generation pipeline that can enable external AI agents to interact with APIs without any manual coding. - -- **Project Goals:** - - Conversion from API Response into functional UI components such as tables, charts and forms. - - Provide customisation options for layout, styles, and interactive elements. - - Allow users to **modify UI components through natural language prompts** - - **One Click Export** of the generated UI into code - - Creation of an AI-Based Automatic API Tool Generator - ## Proposal Description ### Deliverables - 1. **Internal AI Service** A Layer inside the apidash application that acts as a centralised gateway to local LLMs like **ollama** and other LLM Providers like **ChatGPT**, Claude and Gemini. This enables us to have a simple, modular and flexible approach to integrating agents which can help in future AI-driven implementations within the application. - 2. **API Response Analyser Agent:** Extracts the meaning of the API response and converts it into an internal schema representation for further processing - 3. **Stac Generator Agent:** Converts this generated internal schema along with other metadata into SDUI (Server Driven UI) to aid in visualisation. - 4. **Stac Modifier Agent:** This agent accepts Stac Code generated from the previous step and can make any modification as needed by the user via Natural language prompts - 5. **SDUI To Code Agent** This agent accepts Stac Code generated from the previous step and converts it into working framework-specific code (eg: Flutter) - 6. **Updated API Dash Client:** The changes needed on the API Dash Client would be modifications to the settings page and creation of a new Dialog with 5-6 new screens (examples provided below) - 7. **API Tool Generation Agent & UI:** This agent processes an API request and transforms it into a specific tool format that can be utilized by agentic frameworks. - -### Technical Details +1. **Internal AI Service** A Layer inside the apidash application that acts as a centralised gateway to local LLMs like **ollama** and other LLM Providers like **ChatGPT**, Claude and Gemini. This enables us to have a simple, modular and flexible approach to integrating agents which can help in future AI-driven implementations within the application. +2. **API Response Analyser Agent:** Extracts the meaning of the API response (JSON/XML) and converts it into an internal schema representation for further processing +3. **UI Component Generator Agent:** Converts this generated internal schema along with other data like (language/framework etc) into functional UI component code. +4. **Component Modifier Agent:** This agent accepts UI components generated from the previous step and can make any modification as needed by the user via Natural language prompts +5. **Updated API Dash Client:** The changes needed on the API Dash Client would be modifications to the settings page and creation of a new Dialog with 5-6 new screens (examples provided below) +### Technical Details ### Internal AI Service Architecture -![](images/agentservice_arch.png) +![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/agentservice_arch.png) Parts of the Centralised Internal AI Agent Service Architecture -1. **Input Layer:** the input to each agent will be a JSON/XML input along with an Agent Name. The Agent name will be used to specify which agent we want to call internally. -2. **Orchestrator:** this part takes the inputs and looks at settings, preferences and other contextual data to decide whether to send the request to an internal ollama agent or to a LLM provider like chatgpt, claude or gemini. This layer can also include things like rate limiting to prevent overuse. - ```dart - //Inside the APIDashAIService implementation - static Future _orchestrator( - APIDashAIAgent agent, { String? query, Map? variables } - ) async { - String sP = agent.getSystemPrompt(); - //Perform Templating - if (variables != null) { - for (final v in variables.keys) { - sP = sP.substitutePromptVariable(v, variables[v]); - } - } - final customKey = await _getUserCustomAPIKey(); - //Implement any Rate limiting logic as needed - if (customKey == null) { - //Use local ollama implementation - return await _call_ollama(systemPrompt: sP, input: query ?? ''); - } else { - //Use LLMProvider implementation - return await _call_provider( - provider: customKey.$1, - apiKey: customKey.$2, - systemPrompt: sP, - input: query ?? '', - ); - } - } - ``` +1. **Input Layer:** the input to each agent will be a JSON/XML input along with an Agent Name. The Agent name will be used to specify which agent we want to call internally. +2. **Orchestrator:** this part takes the inputs and looks at settings, preferences and other contextual data to decide whether to send the request to an internal ollama agent or to a LLM provider like chatgpt, claude or gemini. This layer can also include things like rate limiting to prevent overuse. + + ```dart + //Inside the APIDashAIService implementation + static Future orchestrator(APIDashAIAgent agent) async { + final sP = agent.getSystemPrompt(); + final iP = await agent.getInput(); + final customKey = await getUserCustomAPIKey(); + //Implement any Rate limiting logic as needed + if (customKey == null) { + //Use local ollama implementation + return await call_ollama(systemPrompt: sP, input: iP); + } else { + //Use LLMProvider implementation + return await call_provider( + provider: customKey.$1, + apiKey: customKey.$2, + systemPrompt: sP, + input: iP, + ); + } + } + + ``` + 3. **System Prompt:** The System Prompt is a large block of text that sets the overall context and specifies what task the agent must perform. This prompt is unique to each agent and will be constantly iterated upon to ensure the best possible results. *(Regular discussions with mentors needed to fine-tune this)* 4. **Agentic Model**: - - **ollama**: Ollama is a way to run LLMs locally on the host machine. This will be the default option for each agent unless specified otherwise by the user/input. We will be making use of the ollama_dart package to implement this. - ```dart - //Inside the APIDashAIService implementation - static Future ollama( - String systemPrompt, - String input, [ - String model = 'llama3', - ]) async { - //check Ollama Avaiability - final result = await Process.run('curl', ['http://localhost:11434/api/tags']); - if (result.exitCode != 0) { - print('OLLAMA_NOT_ACTIVE'); - return null; - } - final inpS = input == '' ? '' : '\nProvided Inputs:$input'; - final client = OllamaClient(); - final generated = await client.generateCompletion( - request: GenerateCompletionRequest( - model: model, - prompt: "$systemPrompt$inpS", - ), - ); - return generated.response; - } - ``` - - - **API Providers**: LLM services like ChatGPT, Claude and Gemini offer their services via AI endpoints too. By enabling this option in apidash, we are giving power-users the ability to add their favourite LLM's API Keys and use our services via that LLM - ```dart - static Future _call_provider({ - required LLMProvider provider, - required String apiKey, - required String systemPrompt, - required String input, - }) async { - switch (provider) { - case LLMProvider.gemini: - return await APIDashCustomLLMService.gemini( - systemPrompt, input, apiKey); - ... - } - } - - static Future gemini( - String systemPrompt, - String input, - String apiKey, - ) async { - final inpS = input == '' ? '' : '\nProvided Inputs:$input'; - String combinedInput = "$systemPrompt$inpS"; - final url = Uri.parse( - 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=$apiKey'); - final response = await http.post( - url, - headers: {'Content-Type': 'application/json'}, - body: jsonEncode({ - 'contents': [ - { - "parts": [ - {"text": combinedInput} - ] - } - ] - }), - ); - if (response.statusCode == 200) { - final data = jsonDecode(response.body); - return data['candidates']?[0]?['content']?['parts']?[0]?['text']; - } else { - print("GEMINI_ERROR: ${response.statusCode}"); - return null; - } - } - ``` + - **ollama**: Ollama is a way to run LLMs locally on the host machine. This will be the default option for each agent unless specified otherwise by the user/input. We will be making use of the ollama_dart and ollama pub.dev packages to implement this. + + ```dart + //Inside the APIDashAIService implementation + static Future call_ollama({ + required String systemPrompt, + required String input, + }) async { + String ollamaInput = "$systemPrompt\\nInput:$input"; + var ollama = Ollama(model: 'QwQ32B-distilled'); + var result = (await ollama.chat( + prompt: input, + ))?['text']; + return result; + } + + ``` + + - **API Providers**: LLM services like ChatGPT, Claude and Gemini offer their services via AI endpoints too. By enabling this option in apidash, we are giving power-users the ability to add their favourite LLM's API Keys and use our services via that LLM + + ```dart + //Inside the APIDashAIService implementation + static Future call_provider({ + required LLMProvider provider, + required String apiKey, + required String systemPrompt, + required String input, + }) async { + switch (provider) { + case LLMProvider.gemini: + { + final response = await http.post( + Uri.parse(''), + headers: { + 'Authorization': 'Bearer $apiKey', + 'Content-Type': 'application/json', + }, + body: json.encode({ + 'model': '...', + 'prompt': '$systemPrompt $input', + }), + ); + return response.statusCode == 200 + ? json.decode(response.body)['text'] + : 'Error: ${response.statusCode}'; + } + //Similar Implementations for other Providers + default: + return null; + } + } + + ``` + 5. **Validator:** LLMs are prone to hallucinations/wrong results. This layer aims to solve that issue by allowing users to create their own validation schemes. If the response validation is successful, we can move to the next step or else we send it to the governor which takes care of the whole retry mechanisms. -6. **Governor (Retry Mechanism):** If the validation is unsuccessful, the governor attempts to retry the AI request with an **Exponential Backoff** (successive-delays such as 200ms, 400ms, 800ms, 1.6s, 3.2s and so on). Even after 5 retry attempts if the response is not valid, then we return with a `MAX_RETRIES_EXCEEDED` error. - ```dart - //Inside the APIDashAIService implementation - static Future governor(APIDashAIAgent agent) async { - int RETRY_COUNT = 0; - List backoffDelays = [200, 400, 800, 1600, 3200]; - do { - try { - final res = await orchestrator(agent); - if (res == null) { - RETRY_COUNT += 1; - } else { - if (await agent.validator(res)) { - return agent.outputFormatter(res); - } else { - RETRY_COUNT += 1; - } - } - } catch (e) { - print(e); - } - // Exponential Backoff - if (RETRY_COUNT < backoffDelays.length) { - await Future.delayed(Duration( - milliseconds: backoffDelays[RETRY_COUNT], - )); - } - RETRY_COUNT += 1; - } while (RETRY_COUNT < 5); - } - ``` - -8. **Output Formatter:** This is a function that allows us to specify in what way we want our agent to return the validated AI response. This is useful for downstream processing and keeps things elegant. +6. **Governor (Retry Mechanism):** If the validation is unsuccessful, the governor attempts to retry the AI request with an **Exponential Backoff** (successive-delays such as 200ms, 400ms, 800ms, 1.6s, 3.2s and so on). Even after 5 retry attempts if the response is not valid, then we return with a `MAX_RETRIES_EXCEEDED` error. + + ```dart + //Inside the APIDashAIService implementation + static Future governor(APIDashAIAgent agent) async { + int RETRY_COUNT = 0; + List backoffDelays = [200, 400, 800, 1600, 3200]; + do { + try { + final res = await orchestrator(agent); + if (res == null) { + RETRY_COUNT += 1; + } else { + if (await agent.validator(res)) { + return agent.outputFormatter(res); + } else { + RETRY_COUNT += 1; + } + } + } catch (e) { + print(e); + } + // Exponential Backoff + if (RETRY_COUNT < backoffDelays.length) { + await Future.delayed(Duration( + milliseconds: backoffDelays[RETRY_COUNT], + )); + } + RETRY_COUNT += 1; + } while (RETRY_COUNT < 5); + } + + ``` + +7. **Output Formatter:** This is a function that allows us to specify in what way we want our agent to return the validated AI response. This is useful for downstream processing and keeps things elegant. ### Internal AI Service Proposed Implementation + ```dart enum LLMProvider { chatgpt, claude, gemini } @@ -230,44 +206,42 @@ class APIDashAIService { static Future<...> orchestrator(...) async {...} static Future governor(APIDashAIAgent agent) async {...} } + ``` ### Modular AI Agent Blueprint (Abstract Class) + ```dart abstract class APIDashAIAgent { String get agentName; String getSystemPrompt(); + Future getInput(); Future validator(String aiResponse); Future outputFormatter(String validatedResponse); } -extension SystemPromptTemplating on String { - String substitutePromptVariable(String variable, String value) { - return this.replaceAll(":$variable:", value); - } -} -``` -Calling any agent from the frontend can be done like this: -```dart -final agent = DummyAgent(); //extends APIDashAIAgent -final ans = await APIDashAIService.callAgent( - agent, - variables: { - 'VAR_API_RESPONSE': resp_data, //SystemPrompt Templating - } -); +//Every Agent used in APIDash must extend and implement this class +//This approach makes everything extremely modular and testable too + ``` ### Agent Implementations -#### Agent: API_RESPONSE_ANALYZER -![](images/AI_RESP_ANA.png) +### Agent: API_RESPONSE_ANALYZER + +![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/ARANA.png) + +- The API response is first parsed correctly according to its type (JSON / XML) and is then sanitized +- The Agent then goes through the whole response and generates relevant semantic context. This context will be very helpful in determining what is actually needed in the final UI +- The semantic context along with the sanitized response is then sent over to the LLM to be converted into intermediate-representation. This step is essential because it allows us to semantically include/exclude response data leading to good UI generation. -- The API response is first parsed correctly according to its type (JSON / XML) and is then sanitised -- The Agent then goes through the whole response and generates relevant semantic context. This context will be very helpful in determining what is actually needed in the final UI -- The semantic context along with the sanitized response is then sent over to the LLM to be converted into a YAML based intermediate-representation. This step is essential because it allows us to semantically include/exclude response data leading to good UI generation. ```dart class ResponseAnalyzerAgent extends APIDashAIAgent { - ... + @override + String get agentName => 'API_RESPONSE_ANALYZER'; + + @override + Future getInput() async {...} + @override String getSystemPrompt() { return """You are an intelligent response analyzer agent..."""; @@ -275,86 +249,116 @@ class ResponseAnalyzerAgent extends APIDashAIAgent { @override Future validator(String aiResponse) async { - //Specific validations to check if the generation was correct + if(!aiResponse.contains('~~~~~')) return false; //No Separator + final sa, ir = aiResponse.split('~~~~~'); + try { + jsonDecode(ir); + } catch (e) { + return false; //Internal Representation was not valid + } return true; } - + @override Future outputFormatter(String validatedResponse) { //separator specified in system prompt - final sa, ir = validatedResponse.split('~~~~~'); + final sa, ir = validatedResponse.split('~~~~~'); return { 'SEMANTIC_ANALYSIS': sa.trim(), - 'INTERNAL_REPRESENTATION': ir.trim() + 'INTERNAL_REPRESENTATION': jsonDecode(ir) } } } + ``` -#### Agent: STAC_GEN (SDUI Representation Generator) -![](images/STAC_GEN.png) -- Using the internal schema and additional context, the SDUI Stac Generator Agent determines the most appropriate UI component for the data. For example, if the data structure is a `List`, it can generate a table and so on. This data is converted into a SDUI (Server Driven UI) representation called `Stac` from the [Stac](https://pub.dev/packages/stac) flutter package. -- The agent can further refine this decision based on factors such as data type, layout, and design preferences. -- The generated Stac code is basically a json representation of a flutter component and this can be used to create lightning-fast previews +### Agent: UI_COMPONENT_GEN (Component Generator) + +![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/UICOMPGEN.png) + +- Using the internal schema and additional context, the UI Component Generator Agent determines the most appropriate UI component for the data. For example, if the data structure is a `List`, it can generate a table. +- The agent can further refine this decision based on factors such as data type, layout, and design preferences. +- This component is flexible and can generate code for different frameworks such as NextJS, HTML, or Flutter, with embedded HTTP calls and relevant data-fetching logic. + ```dart -class StacGenBot extends APIDashAIAgent { +class UIComponentGeneratorAgent extends APIDashAIAgent { + @override + String get agentName => 'UI_COMPONENT_GEN'; + + @override + Future getInput() async {...} + @override String getSystemPrompt() { - return """You are an expert agent whose sole job is to generate Flutter-SDUI Stac Code"""; + return """You are an intelligent UI Generating Agent...."""; + } + + @override + Future validator(String aiResponse) async { + //Language Specific Validations if needed + } + + @override + Future outputFormatter(String validatedResponse) { + //perform formatting operations as needed + return { + 'CODE': validatedResponse.trim() + } } - //Similar implementation as above but the validator & outputFormatter logic changes } + ``` +### Agent: COMPONENT_MODIFIER (Component Customization) -#### Agent: STAC_MODIFIER (UI Customization using Natural Language) -![](images/STAC_MODIFIER.png) +![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/COMPMOD.png) -- The generated SDUI code can be previewed in a Component Preview Window, allowing users to inspect the result. -- The Stac Modifier Agent is an optional feature that allows users to modify the generated SDUI using natural language prompts. This iterative process enables users to refine the UI to meet their exact needs. This is fairly easy to do as most LLMs can already do this very well. We just have to use good system prompting to get it right -- Common customisations like layout changes, pagination, and sorting can be added through buttons, making the customisation process seamless for users. +- The generated UI component can be previewed in a Component Preview Window, allowing users to inspect the result. +- The Component Modifier Agent is an optional feature that allows users to modify the generated component using natural language prompts. This iterative process enables users to refine the UI to meet their exact needs. This is fairly easy to do as most LLMs can already do this very well. We just have to use good system prompting to get it right +- Common customizations like layout changes, pagination, and sorting can be added through buttons, making the customization process seamless for users. ```dart -class StacModifierAgent extends APIDashAIAgent { +class ComponentModifierAgent extends APIDashAIAgent { @override - String getSystemPrompt() { - return """You are an intelligent Code Modifier Agent who matches the client's needs"""; - } - //Similar implementation as above but the validator & outputFormatter logic changes -} -``` -#### Agent: STAC2CODE (Conversion from SDUI Code to Framework Code) -![](images/STAC2CODE.png) -- The generated SDUI code cannot be executed on the user's machine, hence we must convert this into actual flutter code. -- This is fairly easy to do as Stac is almost a one-one representation of flutter code just in JSON -- This property also makes it fairly easy to convert to other languages as the LLM can understand the context through the JSON SDUI and just convert it into another framework like NextJS code. This allows future extensibility. -```dart -class Stac2CodeAgent extends APIDashAIAgent { + String get agentName => 'COMPONENT_MODIFIER'; + + @override + Future getInput() async {...} + @override String getSystemPrompt() { - return "You are an expert agent who accepts Stac SDUI code and converts it into Flutter.."; + return """You are an intelligent Code Modifier Agent...."""; } - //Similar implementation as above but the validator & outputFormatter logic changes -} -``` + @override + Future validator(String aiResponse) async { + //Language Specific Validations if needed + } -### Live UI Preview Implementation -One of the most important features of this entire AI UI Designer pipeline is the implementation of the UI Preview Engine. This allows the user to see how the generated component looks and can help them identify what changes may be needed. -Creation of a dynamic UI rendering system from Flutter code is very hard due to flutter's limitations (**reflection** is not allowed, **flutter_eval** has minuscule coverage, **remote flutter widgets** or webviews need external server support etc) + @override + Future outputFormatter(String validatedResponse) { + //perform formatting operations as needed + return { + 'MODIFIED_CODE': validatedResponse.trim() + } + } +} -Hence the last option was to use **SDUI (Server Driven User Interface)** using [Stac](https://pub.dev/packages/stac) -This gives us a lot of flexibility as it is very similar in naming convention to Flutter and is hence easier to convert to Flutter Code using an LLM, it also has much greater coverage. -We will also be using a regular JSON based rendering approach as a fallback incase Stac is very unstable. +``` ## System Architecture & Flow -![image info](images/AI_UI_DES_ARCH_MAIN.png) +![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/SYSRCH.png) + +### Flow Example: Sample API Response -### Sample API Response This is a sample response given when I hit a [DEMO API](https://reqres.in/api/users?page=0) + ```json { + "page": 1, + "per_page": 6, + "total": 12, "total_pages": 2, "data": [ { @@ -362,343 +366,304 @@ This is a sample response given when I hit a [DEMO API](https://reqres.in/api/us "email": "george.bluth@reqres.in", "first_name": "George", "last_name": "Bluth", - "avatar": "https://reqres.in/img/faces/1-image.jpg" + "avatar": "" + }, + { + "id": 2, + "email": "janet.weaver@reqres.in", + "first_name": "Janet", + "last_name": "Weaver", + "avatar": "" }, ... ] } + ``` -This is subsequently sent to our frontend prompt where we accept the target framework, let's assume the user has selected `Flutter` + +This is subsequently sent to our frontend prompt where we accept the target framework, let's assume the user has selected `Flutter` This response will first be sent into our Semantic Agent which tries to pick out what this response means. +--- ----------- - - -### Generated Semantic Response - -*This JSON response represents a paginated list of user data. The 'data' field is an array containing user objects. Each user object includes 'id', 'email', 'first_name', 'last_name', and 'avatar'. The 'page', 'per_page', 'total', and 'total_pages' fields provide pagination information. The 'support' object can be omitted as it is not directly related to the user data itself. A suitable UI would be a List View to display the user information. Details should include the avatar (as an image), first name, last name, and email. The 'id' is useful internally, but may not be needed in the display. Pagination controls (e.g., next/previous page buttons or page number selection) are required to navigate through the pages of data.* - - -### Generated YAML-Based Internal Representation -```yaml -- type: column - elements: - - type: row - elements: - - type: image - src: "https://reqres.in/img/faces/1-image.jpg" - shape: circle - width: 60 - height: 60 - - type: column - elements: - - type: text - data: "George Bluth" - font: "segoe-ui" - color: blue - - type: text - data: "george.bluth@reqres.in" - font: "segoe-ui" - color: gray - ... -``` -### Generated Stac Representation (For SDUI Preview) -```json +### Flow Example: Generated Semantic Response -{ - "type": "scaffold", - "backgroundColor": "#002b36", - "appBar": { - "type": "appBar", - "backgroundColor": "#073642", - "title": { - "type": "text", - "data": "User List", - "style": { - "color": "#839496" - } - } - }, - "body": { - "type": "listView", - "children": [ - { - "type": "card", - "margin": { - "left": 8, - "top": 4, - "right": 8, - "bottom": 4 - }, - ... ``` -Now that the output is in Stac, It can easily be sent to the next bot **STAC_MODIFER**) (optional) which can easily make any changes that you request via *Natural Language* -### Generated Flutter Code -```dart -Scaffold( - backgroundColor: const Color(0xFF002b36), - appBar: AppBar( - backgroundColor: const Color(0xFF073642), - title: const Text( - "User List", - style: TextStyle( - color: Color(0xFF839496), - ), - ), - ), - body: ListView( - children: [ - Card( - margin: const EdgeInsets.fromLTRB(8, 4, 8, 4), - elevation: 2, - color: const Color(0xFF073642), - ... +The response provides paginated user data with information such as the current page (1), number of users per page (6), total users (12), and total pages (2). Each user entry includes an ID, email, first name, last name, and an avatar image URL. For the UI, display the user's name (first and last), email, and avatar, with avatars shown as circular images. Pagination controls should be implemented with "Previous" and "Next" buttons, as well as page numbers based on the current page and total pages. Ensure the UI dynamically adjusts to show 6 users per page and maintain visual clarity with proper spacing for readability. The design should effectively present the user data while allowing easy navigation between pages. ``` -### Sample Output (Flutter) -![image info](images/FLUT_OUTPUT.png) - - -## Sample UI - -![image info](images/AI_UI_DES_SUI_1.png) -![image info](images/AI_UI_DES_SUI_2.png) -![image info](images/AI_UI_DES_SUI_3.png) -If any modifications are made, it will go back to loading screen and come back to this page with the relevant modifications. -When Export is clicked, the relevant generated code is **copied to clipboard** and the dialog closes! - -### Proof of Concept (PoC) -A Simple and elegant prototype has been created for the entire pipeline. It hightlights the whole process of Response Sentiment Analysis, YAML-based Intermediate Representation, JSON based SDUI generation and Final Code Export. -Prototype link: [https://github.com/synapsecode/AI_UI_designer_prototype](https://github.com/synapsecode/AI_UI_designer_prototype) - ---- -# Automated API Tool Generation - -An API Tool Generator basically is a system to automatically generate functions or methods from a given API description which enables AI agents to interact with APIs on their own without any manual coding from our end. - -This functionality is particularly useful for developers working on AI-driven applications, as it simplifies integrating external APIs into agent workflows, enhancing efficiency and reducing development time. - -## Implementation Details - -Since this is an agent application itself, we can reuse the `APIDashAIAgent` abstract class mentioned above to implement our agents needed for this task. This highlights its modular and easy-to-use nature. - -`Supported Agentic Frameworks`: **OpenAI**, **LangChain**, Anthropic, **Gemini**, Mistral, Microsoft Autogen -`Supported Programming Languages`: Python, JavaScript - -![](images/TOOLGENARCH.png) - -- **Step 1: API Request Consolidation:** We accept the incoming API Request and add all of the relevant data (method, url, headers, auth etc) into a single text object named `REQDATA` this will be useful for the previous steps. -- **Step 2: Tool Template Selector:** After doing some research, it appears that *API Tools* have a very limited number of templates. - `example`: Tool Format used by Anthropic, Mistral, Gemini, OpenAI etc for python - ```python - def func(...): - #Provided API being called in the language specific way - - api_tool = { - "function": func, - "definition": { - "name": TOOL_NAME, - "description": TOOL_DESCRIPTION, - "parameters": { - "type": "object", - "properties": { - ARG_NAME: { - "type": ARG_TYPE, - "description": ARG_DESC - }, - ... - }, - "required": [ALL_REQUIRED_ARGUMENT_NAMES], - "additionalProperties": False #Anthropic Specific - } - } - } +### Flow Example: Generated Internal Representation - __all__ = ["api_tool"] #Export the Tool - ``` - example: Tool Format used by Langchain - ```python - from langchain.tools import StructuredTool - class InputSchema(BaseModel): #Pydantic Model - title: str = Field(..., description="...") - ... - def func(...): - #Provided API being called in the language specific way - api_tool = StructuredTool.from_function( - func=func, - name="", - description="", - args_schema=InputSchema, - ) - __all__ = ["api_tool"] #Exporting the Tool - ``` - and so on. Hence, We can easily store these as templates and substitute the variable values as needed. -- **Step 3: Func Gen:** We use the `APIDashAIAgent` class to create another agent called FUNC_GEN which accepts the `REQDATA` & Target language and converts it into a Function that calls the api - example: - ```python - import requests - def func(data): - try: - response = requests.post("...", data=data) - response.raise_for_status() - return response.json() - except requests.exceptions.RequestException as e: - print(f"Error: {e}") - return {"error": "An error occurred while calling API"} - ``` -- **Step 4: Tool Generation:** This simple agent takes the function generated from the previous step along with the selected template and combines them. `REQDATA` is used to fill in the variables with contextual data. -- **Step 5: Tool Export:** The complete tool code can now be exported and used by the user as needed - -## Week-wise Breakdown - -![image_info](images/aiuides_timeline.png) +```json +[{ + "type": "column", + "elements": [ + { + "type": "row", + "elements": [ + { + "type": "image", + "src": "", + "shape": "circle", + "width": "60", + "height": "60" + }, + { + "type": "column", + "elements": [ + { + "type": "text", + "data": "Michael Lawson", + "font": "segoe-ui", + "color": "blue" + }, + { + "type": "text", + "data": "michael.lawson@reqres.in", + "font": "segoe-ui", + "color": "gray" + } + ] + } + ] + }, + { + "type": "vspacer", + "spacing": "20" + }, + ... + ] + }, + { + "type": "vspacer", + "spacing": "30" + }, + { + "type": "row", + "elements": [ + { + "type": "button", + "text": "Previous", + "onclick": "page=1" + }, + ... + ] + }, + ... + { + "type": "link", + "text": "Tired of writing endless social media content? Let Content Caddy generate it for you.", + "url": "" +}] -### Week 1 (Internal AI Service Development) -- Discuss Future Agentic needs for Smarter code structuring -- Implement the APIDashAIService & APIDashAIAgent core classes -- implement the call_ollama and call_provider LLM accessing functions -- Implement the proposed governor and orchestrator logic -- Implement Variable Replacement Feature for dynamic system prompts +``` - **DELIVERABLE**: A Working version of the APIDashAIService with functional calls to both ollama and external LLM providers along with a working retry mechanism +### Generated Flutter Component +```dart +class UserListWidget extends StatelessWidget { + final List> users = [ + { + 'firstName': 'Michael', + 'lastName': 'Lawson', + 'email': 'michael.lawson@reqres.in', + 'avatar': '', + }, + // Add more users here... + ]; -### Week 2 (Response Analyser Agent Development) -- Write a detailed System Prompt to extract Semantics from a Response -- Test Semantic output against various responses & tweak system prompt -- Write a detailed System Prompt to create a YAML-based internal representation UI Schema from a given API Response and test it against various responses -- Enhance the agent to intelligently omit unnecessary parts of the response in UI -- Implement Validation & Formatting rules for the Semantic Analyser and IR-Gen bots + final int currentPage; + final int totalPages; + final Function(int) onPageChange; - **DELIVERABLE**: A Working version of the complete Response Analyser agent that can accept a given API response and give both the complete Semantic Description & the YAML-based internal representation + UserListWidget({ + required this.currentPage, + required this.totalPages, + required this.onPageChange, + }); -### Week 3 (Stac-SDUI Generator Agent Development) + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + children: users.map((user) { + return Row( + children: [ + CircleAvatar( + radius: 30, + backgroundImage: NetworkImage(user['avatar']), + ), + SizedBox(width: 16), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${user['firstName']} ${user['lastName']}', + style: TextStyle(fontFamily: 'segoe-ui', color: Colors.blue), + ), + Text( + user['email'], + style: TextStyle(fontFamily: 'segoe-ui', color: Colors.grey), + ), + ], + ), + ], + ); + }).toList(), + ), + SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: currentPage > 1 ? () => onPageChange(currentPage - 1) : null, + child: Text('Previous'), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: () => onPageChange(1), + child: Text('1'), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: () => onPageChange(2), + child: Text('2'), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: currentPage < totalPages ? () => onPageChange(currentPage + 1) : null, + child: Text('Next'), + ), + ], + ), + SizedBox(height: 10), + TextButton( + onPressed: () { + // Open the link + }, + child: Text( + 'Tired of writing endless social media content? Let Content Caddy generate it for you.', + style: TextStyle(color: Colors.blue), + ), + ), + ], + ); + } +} -- Iterate and decide on a robust system prompt that converts SA & IR into Flutter-SDUI `Stac` Representation for UI Previewing -- Creation of a Stac-Error Detector to aid in retry mechanism for faulty generations -- Write custom validation logic to deal with Stac's nuances (eg: `bold` is not allowed as a fontWeight in Stac) -- Talk to Stac's team and apidash mentors to decide how to support certain widgets that are not available in the SDUI representation (eg: `ClipRRect`) -- Test out multiple API responses to see if the generated SDUI matches mentors' expectations, if not tweak the system prompt iteratively until satisfactory +``` - **DELIVERABLE**: An agent that accepts the SA & IR from the previous step and converts it into an SDUI compatible "Stac" representation which is a JSON Server Driven UI Representation which can be useful for the upcoming Live Preview Feature - -### Week 4 (Natural Language UI Customization & Preview) +### Sample Output (Flutter) -- Implement a bot named `StacModifier` that can make the user-requested changes on the fly to the previously generated `Stac` SDUI code -- Develop the Component Preview View using a custom implementation called `StacRenderer` -- Implement Validation & Formatting rules along with `Content Moderation` & `Guard-railing` rules to prevent unwanted modifications -- Test against various types of modification requests to ensure that the bot is capable of making a wide variety of changes +![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/flutex.jpg) - **DELIVERABLE**: A working agent that accepts the previously generated SDUI representation along with the user's natural language prompt to make any requested changes on the fly. (eg: Convert this UI into a solarized theme, should result in the solarized version appearing as a UI preview) +Now that the output is in Flutter Code, It can easily be into the next bot **Component Editor Agent**) which can easily make any changes that you request via *Natural Language* -### Week 5 (Code Export: SDUI → Flutter Code) +## Sample UI -- Create the System Prompt for an agent named `Stac2Code` to convert SDUI-JSON into valid Flutter Code -- Create the necessary Validation & output formatting code (eg: removing triple backtick notation and so on) to ensure that the generated component is ready to be directly copy-pasted into the user's IDE -- As SDUI is a JSON representation, once the code generation is stable, we can explore the conversion of it into other frameworks like NextJS too. -- Run the Code export action on a varity of generated `Stac` representations to see if the resultant Flutter code works as expected in a reliable manner +![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/framwdiag.png) - **DELIVERABLE**: By the end of the week, we will have an agent that converts the previously generated SDUI code into a fully working Flutter Component (that can be tested on Online IDEs). This will mark the end of the AI-UI-Designer pipeline requirements +![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/LDG.png) -### Week 6 (APIDash Client Specific UI Changes) +![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/compo.png) -- Create the UI & implementation in the settings page to have an option to select your AI Engine (ollama or custom) and in case it is custom, add an option to pass your API Key and select the provider -- Write the implementation to persist this data and use it for all other agentic actions (LLMKeystore Implementation) -- Work on the secure storage of these API Keys to prevent leaks and theft -- Create the UI screens as shown above in the Sample UI Section -- Integrate the whole UI generation pipeline into the frontend and delete any prototype UI screens created during prior development +If any modifications are made, it will go back to loading screen and come back to this page with the relevant modifications. +When Export is clicked, the relevant generated code is **copied to clipboard** and the dialog closes! - **DELIVERABLE**: By the end of the week, we will have a new api dash client with the settings UI modified to include a `AI Engine` selector and a `LLM Api Key` field. - Additionally we will have the UI implemented for the whole UI generation pipeline created and integrated with the described agents. +## Week-wise Breakdown -### Week 7 (Mobile Specific Changes) +![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/timeline.png) -- Since apidash works on mobile too, this week will be used to convert all the existing desktop-styled UI components into mobile friendly UI components -- ollama is not accessible via localhost on a phone, hence on mobile devices we must include a textbox in the settings page so that the user can paste their hosted ollama instance link (they must also be able to select their model of choice) -- Creation of a tab based layout in the UI Previewer screen to ensure that the UI Preview and code display are not too crammed up. -- The export code button should ideally open up the mobile-os specific share sheet to make it easier to share the generated component code -- Additional Tasks that were pending or put into backlog from the earlier weeks can be completed here. +### Week 1 (Community Bonding) - **DELIVERABLE**: By the end of the week, the complete working pipeline that was available on desktop will also be available on a mobile platform (Android, iOS). The UI will be modified to better fit the smaller screen size and the feature will now be ready for next week's complete flow testing +- Get in touch with the developers and the mentor +- Introduction to the community, to the mentor and fix timings to communicate +- Set communication schedules for regular updates. +- Go through the API Dash codebase and dependency packages, especially the structure around melos. +- Complete local setup (macOS platform support) and run the product in DEBUG mode. +- Discuss any suggestions and changes to the project. There could modifications, new additions or amendments; it would be better to go over these early +- Understand the state management approaches and dependency structure. +- Discuss the working of the existing application and the implementation of the new features with the mentor -### Week 8 (Complete Flow Testing & Agent Optimization) +### Week 2 (Internal AI Service Development) -- For each bot created in the pipeline, we must rigorously run manual tests on different types of inputs and check if they match the expectations. The data generated from these tests must be noted down in a separate report -- Discuss with mentors to implement proper code testing strategy to ensure that the whole pipeline is tested both on a unit basis and entire flow basis. This allows us to eventually pass this whole feature into an automated testing suite -- The agents that need tweaking/optimization will have their System prompts modified to yield better results. -- All the code written until now should be carefully inspected for side-effects, possible loopholes and so on. Work with the mentors to make the written code classified as `production-ready` +- Discuss the present and future Agentic AI requirments that apidash might need with the mentors, this allows for smarter code planning. +- Begin extensive implementation of the `APIDashAIService` class along with the other abstract classes such as `APIDashAIAgent` +- Thoroughly implement the `call_ollama` and `call_provider` functions for local and provider-based LLM actions. +- Implement the `governor` and `orchestrator` logic by expanding upon the initial ideas presented above.. - **DELIVERABLE**: By the end of the week, we will have clarity on an automated testing strategy along with written tests as well as a manual testing report. This will give an idea if the entire pipeline is working according to the mentors' expectations or not. +### Week 3 (API Response Analyser Agent Development) -### Week 9 (Request Consolidation & Metadata Extractor) +- Understand how to map API response formats into internal schema representation and translate it into a very verbose and well structured `System Prompt` +- Understand and iterate on ways to make the System Prompt better at Semantic analysis of the response content +- Research on which API response data meaningfully contributes to the final UI and which data is irrelevant. This can help in improving user satisfaction. +- Start testing with real-world API responses to ensure proper parsing and extraction of meaningful data. -- Implement input parsing logic to extract METHOD, URL, HEADERS, BODY, PARAMS, AUTH, etc., and consolidate into `REQDATA` -- Accept inputs `AGENTIC_FRAMEWORK` and `TARGET_LANGUAGE` and implement the UI to do so -- Implement Changes to the AI UI Designer to accomodate pagination, sorting, filtering and other features requested in the project description. This should be easy to do for an LLM agent as they are standard code actions. +### Week 4 (UI Component Generator Agent Development) +- Design a comprehensive and detailed system prompt which can convert the IR and SA into valid code in the target language. +- Implement a basic conversion for a sample API response to UI components (start with JSON → Table). +- Develop a testing plan to ensure the generated UI components match the expected functionality +- Test and validate generated components in a sample frontend - **DELIVERABLE**: A working module that accepts API specifications and user inputs, and outputs a unified REQDATA. +### Week 5 (Customization & Natural Language Interaction) -### Week 10 (Template Selector and FUNC_GEN, TOOL_GEN Implementation) +- Implement options to modify styles such as color, size, and borders for UI components. +- Develop the Component Modifier Agent, which allows the user to modify UI components via natural language prompts. +- allow users to perform advanced modifications on the generated UI components, such as resizing, renaming, or changing themes using plain text +- Develop a testing strategy for different types of modifications users may request. +- Discuss any breaking changes that would arise in the UI because of these integrations -- Design reusable templates for tool definitions across frameworks: OpenAI, Gemini, Mistral, Anthropic, LangChain, and Autogen. -- Implement a simple rule-based tool selector which can select the relevant template based on `AGENTIC_FRAMEWORK` and `TARGET_LANGUAGE` -- Creation of the **FUNC_GEN** Bot that can convert a given API Request into a piece of language-specific code which calls the API and returns its value -- Creation of the **TOOL_GEN** Bot that can convert the selected Template and Generated Function into a fully functional API Tool +### Week 6 (Multi-framework Support & Export Mechanism) - **DELIVERABLE**: A Rule based Tool Selector that can reliably return the relevant template based on inputs and the complete FUNC_GEN and TOOL_GEN working agents +- Extend the UI component generation to support other UI Frameworks/Libraries such as React, Tkinter etc +- Extend the entire system to support XML API Responses +- Implement a One Click Export button that generates the final UI code in the selected framework. +- Test the generated export functionality by testing it in other framework specific codebases -### Week 11 (UI Integration & Tool Testing) +### Week 7 (User interface Updates & Testing) -- Integrate this newly created pipeline with the existing apidash application -- Test with multiple examples to confirm that both of the bots are working properly -- Any pending tasks from the previous weeks can be completed here +- Modify the **API Dash Client**, including changes to the settings page and a new dialog for UI Generation. +- Migrate from the testing mockup screens to actual full fledged designs pre-approved by the mentor +- Conduct tests of the entire flow: from API response analysis, schema generation, UI component generation, customization, and exporting. +- Gather feedback from the mentor and iterate on any issues or improvements. - **DELIVERABLE**: The Entire Product fully working & Tested: AI UI Designer + API Tool Generator +### Week 8 (Final Buffer, Optimization & Report) -### Week 12 (Report & Documentation) -- The Documentation for the entire pipeline to be written in this period along with the final GSoC Report. - - **DELIVERABLE**: Technical Documentation & GSoC Report - ---- +- Use this buffer for any remaining tasks, bug fixing, or addressing missed items. +- Document all the components built, their functionality, and how they can be used. +- Discuss the project progress with the mentor and provide a plan for any future enhancements. +- Write the final project report covering key achievements, challenges faced, and any future plans. -## Relevant Skills and Experience +## 7. Relevant Skills and Experience - -### Skills +### 7.1. Skills - Languages: Dart, Python, Java, Javascript, Typescript, Go - Frameworks / Libraries: Flutter, Flask, FastAPI, SpringBoot, ReactJS, NextJS, Tailwind -- Databases & Tools: PostgreSQL, Supabase, Firebase, MongoDB, Git, Docker, Cloudflare Workers, Dify.ai, n8n - - +- Databases & Tools: PostgreSQL, Supabase, Firebase, MongoDB, Git, Docker, Cloudflare Workers, [Dify.ai](http://dify.ai/), n8n -### Experience - -- Lead Developer at [Crezam](https://crezam.com) (AI Powered assessments and interview platform); Extensive work on Flutter for Mobile, Web and MacOS platforms -- Flutter Developer for [Atlas Knowledge](https://atlas.fm/) which is a bookmark based second-brain -- Flutter Developer at [FitchoiceWorld](https://www.fitchoiceworld.com/) which is a fitness centre booking & wellness application -- I have a lot of experience with Flutter Web and MacOS including creation of platform specific code and then calling them via bridges/interop channels. -- Experience with using Agentic tools such as Dify to handle GenAI specific tasks - +### 7.2 Experience +- Lead Developer at [Crezam](https://crezam.com/) (AI Powered assessments and interview platform); Extensive work on Flutter for Mobile, Web and MacOS platforms +- Flutter Developer for [Atlas Knowledge](https://atlas.fm/) which is a bookmark based second-brain +- Flutter Developer at [FitchoiceWorld](https://www.fitchoiceworld.com/) which is a fitness centre booking & wellness application +- I have a lot of experience with Flutter Web and MacOS including creation of platform specific code and then calling them via bridges/interop channels. -## Projects +## 8. Projects - **Microblogger** - - A full scale social media application built using Flutter and Flask which includes a wide variety of posts, stories and interactive content fully built from scratch. The whole process was thoroughly documented under my 100 days of code challenge - - Github: [https://github.com/synapsecode/MicrobloggerV1](https://github.com/synapsecode/MicrobloggerV1) - - Public Build Log: [https://tulip-quality-7a5.notion.site/Microblogger-Builds-0b6f44eece5b419ca57f8a431e03ad2c](https://tulip-quality-7a5.notion.site/Microblogger-Builds-0b6f44eece5b419ca57f8a431e03ad2c) - - + - A full scale social media application built using Flutter and Flask which includes a wide variety of posts, stories and interactive content fully built from scratch. The whole process was thoroughly documented under my 100 days of code challenge + - Github: [https://github.com/synapsecode/MicrobloggerV1](https://github.com/synapsecode/MicrobloggerV1) + - Public Build Log: [https://tulip-quality-7a5.notion.site/Microblogger-Builds-0b6f44eece5b419ca57f8a431e03ad2c](https://www.notion.so/Microblogger-Builds-0b6f44eece5b419ca57f8a431e03ad2c?pvs=21) - **FFPNS (Flutter Firebase PushNotifications Service)** - - A package that I created to make it extremely simple to integrate firebase push notifications into a fluter app with minimal setup & advanced features such as background message handling and so on - - Github: [https://github.com/synapsecode/ffpns](https://github.com/synapsecode/ffpns) - + - A package that I created to make it extremely simple to integrate firebase push notifications into a fluter app with minimal setup & advanced features such as background message handling and so on + - Github: [https://github.com/synapsecode/ffpns](https://github.com/synapsecode/ffpns) ### More About Me + I’m Manas M. Hejmadi, a third-year Computer Science & Engineering student at DSCE. With a strong foundation in **Mobile & Desktop Development**, **Backend Engineering**, **Microservices** and **AI Integrations**, I am passionate about building good software at a rapid pace. I have nearly **6 years of experience in Flutter**, along with extensive experience in other technologies such as **Flask**, **NodeJS**, **NextJS**, **Celery**, **Docker**, **SpringBoot**, **Java**, and **Svelte**. Currently, I lead a team of 3 developers at [Crezam](https://www.crezam.com/), where we are focused on revolutionizing the hiring process through **AI-powered assessments and interviews.** I also actively participate in hackathons and tech events, earning second place in **IIT Roorkee’s App Innovation Challenge**. I am particularly interested in leveraging AI agents to create more powerful, user-centric applications. From f85189f769693548ec38b3b66335e0e8a3267e5a Mon Sep 17 00:00:00 2001 From: Manas Hejmadi | Developer <67999871+synapsecode@users.noreply.github.com> Date: Sat, 5 Apr 2025 04:16:38 +0530 Subject: [PATCH 007/188] Added ToolGen, SDUI & Increased Timeline --- ...n_Manas Hejmadi_AI UI Designer For APIs.md | 1007 +++++++++-------- 1 file changed, 525 insertions(+), 482 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md b/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md index a2a184cf5..1a4310bdb 100644 --- a/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md +++ b/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md @@ -1,197 +1,221 @@ -# AI-Powered API Response to Dynamic UI Generator ![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/GSOCBANNER.jpg) -### Personal Information +# AI-Based API Response to Dynamic UI and Tool Generator +### Personal Information - **Full Name:** Manas M Hejmadi -- **Contact Info:** [manashejmadi@gmail.com](mailto:manashejmadi@gmail.com) `+918904995101` +- **Contact Info:** manashejmadi@gmail.com `+918904995101` - **Discord Handle:** `synapse_45006` -- **Github Profile:** [https://github.com/synapsecode](https://github.com/synapsecode) +- **Github Profile:** https://github.com/synapsecode - **Resume:** [Link](https://manashejmadi.vercel.app/images/MANAS_RESUME.pdf) - **Website:** [Link](https://manashejmadi.vercel.app/) - **Time Zone**: GMT +05:30 ### University Information - - **University Name:** Dayananda Sagar College of Engineering (DSCE), Bengaluru -- **Program Enrolled In:** BE in Computer Science & Engineering (CSE) -- **Year:** Pre-final Year (Third Year) +- **Program Enrolled In:** BE in Computer Science & Engineering (CSE) +- **Year:** Pre-final Year (Third Year) - **Expected Graduation Date:** May 2026 ### Motivation & Past Experience -1. Have you worked on or contributed to a FOSS project before? Can you attach repo links or relevant PRs? - - **ANS**: Have worked on large codebases but no direct experience with contribution. +1. Have you worked on or contributed to a FOSS project before? Can you attach repo links or relevant PRs? + - **ANS**: Have worked on large codebases but no direct experience with contribution. 2. What is your one project/achievement that you are most proud of? Why? - - **ANS**: I am most proud of my microblogging application, *Microblogger*. It was a large project I undertook with the goal of learning Flutter, and through it, I gained a deep understanding of the framework. What makes me most proud is the Instagram-like story editor that I built within the app. It was a challenging feature that required creative problem-solving, and successfully implementing it taught me a lot. + - **ANS**: I am most proud of my microblogging application, _Microblogger_. It was a large project I undertook with the goal of learning Flutter, and through it, I gained a deep understanding of the framework. What makes me most proud is the Instagram-like story editor that I built within the app. It was a challenging feature that required creative problem-solving, and successfully implementing it taught me a lot. 3. What kind of problems or challenges motivate you the most to solve them? - - **ANS**: I am particularly motivated by challenges that address real pain points for users, as solving these issues has a direct, meaningful impact. I thrive on problems that require creative and efficient solutions, especially when they need to scale effectively. Additionally, I enjoy opportunities that allow me to take ownership of the project and make critical decisions that shape the direction of the solution. + - **ANS**: I am particularly motivated by challenges that address real pain points for users, as solving these issues has a direct, meaningful impact. I thrive on problems that require creative and efficient solutions, especially when they need to scale effectively. Additionally, I enjoy opportunities that allow me to take ownership of the project and make critical decisions that shape the direction of the solution. 4. Will you be working on GSoC full-time? In case not, what will you be studying or working on while working on the project? - - **ANS**: Yes, I will primarily be working full-time on GSoC. Occasionally, I may have exams, course projects, or job/internship responsibilities, but they will not impact my commitment to GSoC. + - **ANS**: Yes, I will primarily be working full-time on GSoC. Occasionally, I may have exams, course projects, or job/internship responsibilities, but they will not impact my commitment to GSoC. 5. Do you mind regularly syncing up with the project mentors? - - **ANS**: I am available after 5PM IST and can get on calls when needed + - **ANS**: I am available after 5PM IST and can get on calls when needed 6. What interests you the most about API Dash? - - **ANS**: It has a clean and minimalist design with a super fast startup time. It does exactly what it's meant to and gets straight to the point! Plus, Flutter and Dart hold a special place for me since they helped me land my first gig. I've really been enjoying using API Dash. + - **ANS**: It has a clean and minimalist design with a super fast startup time. It does exactly what it's meant to and gets straight to the point! Plus, Flutter and Dart hold a special place for me since they helped me land my first gig. I've really been enjoying using API Dash. 7. Can you mention some areas where the project can be improved? - - **ANS**: One feature that I wanted to include is code to collection generation. Where, we just put the flask code (for example) into the textbox and the LLM agent automatically creates relevant Requests and Collections. This would speed up development and testing time. + - **ANS**: One feature that I wanted to include is code to collection generation. Where, we just put the flask code (for example) into the textbox and the LLM agent automatically creates relevant Requests and Collections. This would speed up development and testing time. -## Project Details -- **Project Title:** AI-Powered API Response to Dynamic UI Generator -- **Issue:** [#617](https://github.com/foss42/apidash/issues/617) -- **Issue Description:** Develop an AI Agent which transforms API responses into dynamic, user-friendly UI components, enabling developers to visualize and interact with data effortlessly. By analyzing API response structures—such as JSON or XML—the agent automatically generates UI elements like tables, charts, forms, and cards, eliminating the need for manual UI development. One can connect an API endpoint, receive real-time responses, and instantly generate UI components that adapt to the data format. It must also support customization options, allowing developers to configure layouts, styles, and interactive elements such as filters, pagination, and sorting. Finally, users must be able to easily export the generated UI and integrate it in their Flutter or Web apps. -- **Abstract:** - - This project introduces an AI-powered agent that **automates the transformation of API responses into structured UI schemas and functional UI components** across various frontend frameworks such as ReactJS, Flutter, and HTML. Using Large Language Models (LLMs), the system eliminates the need for manual UI creation by intelligently analyzing API responses and generating corresponding UI structures. Additionally, it enables **dynamic modification of UI components** based on user prompts, allowing for customization in design, layout, and behavior. By seamlessly integrating these capabilities into a unified workflow, the project streamlines frontend development, reducing manual effort and accelerating the UI generation process with a **single-click solution.** - -- **Project Goals:** - - Conversion from API Response (JSON/XML) into functional UI components such as tables, charts and forms. - - Provide customization options for layout, styles, and interactive elements. - - Allow users to **modify UI components through natural language prompts** - - Generate UI components for multiple frontend frameworks such as Flutter/NextJS etc - - **One Click Export** of the generated UI into code - - Seamlessly integrate all features into a single, easy-to-use system. +## Project Details + +- **Project Title:** AI-Based API Response to Dynamic UI ( & API Tool Generator ) +- **Issue:** [#617](https://github.com/foss42/apidash/issues/617) +- **Issue Description:** Develop an AI Agent which transforms API responses into dynamic, user-friendly UI components, enabling developers to visualize and interact with data effortlessly. By analyzing API response structures—such as JSON or XML—the agent automatically generates UI elements like tables, charts, forms, and cards, eliminating the need for manual UI development. One can connect an API endpoint, receive real-time responses, and instantly generate UI components that adapt to the data format. It must also support customization options, allowing developers to configure layouts, styles, and interactive elements such as filters, pagination, and sorting. Finally, users must be able to easily export the generated UI and integrate it in their Flutter or Web apps. + +- **Abstract:** + This project introduces an AI-powered agent that **automates the transformation of API responses into structured UI schemas and functional UI components** across various frontend frameworks. Using Large Language Models (LLMs), the system eliminates the need for manual UI creation by intelligently analyzing API responses and generating corresponding UI structures. Additionally, it enables **dynamic modification of UI components** based on user prompts, allowing for customization in design, layout, and behavior. By seamlessly integrating these capabilities into a unified workflow, the project streamlines frontend development, reducing manual effort and accelerating the UI generation process with a **single-click solution.** + The project also aims to create a simple one-click API Request to Tool Generation pipeline that can enable external AI agents to interact with APIs without any manual coding. + +- **Project Goals:** + - Conversion from API Response into functional UI components such as tables, charts and forms. + - Provide customisation options for layout, styles, and interactive elements. + - Allow users to **modify UI components through natural language prompts** + - **One Click Export** of the generated UI into code + - Creation of an AI-Based Automatic API Tool Generator + ## Proposal Description ### Deliverables -1. **Internal AI Service** A Layer inside the apidash application that acts as a centralised gateway to local LLMs like **ollama** and other LLM Providers like **ChatGPT**, Claude and Gemini. This enables us to have a simple, modular and flexible approach to integrating agents which can help in future AI-driven implementations within the application. -2. **API Response Analyser Agent:** Extracts the meaning of the API response (JSON/XML) and converts it into an internal schema representation for further processing -3. **UI Component Generator Agent:** Converts this generated internal schema along with other data like (language/framework etc) into functional UI component code. -4. **Component Modifier Agent:** This agent accepts UI components generated from the previous step and can make any modification as needed by the user via Natural language prompts -5. **Updated API Dash Client:** The changes needed on the API Dash Client would be modifications to the settings page and creation of a new Dialog with 5-6 new screens (examples provided below) - + 1. **Internal AI Service** A Layer inside the apidash application that acts as a centralised gateway to local LLMs like **ollama** and other LLM Providers like **ChatGPT**, Claude and Gemini. This enables us to have a simple, modular and flexible approach to integrating agents which can help in future AI-driven implementations within the application. + 2. **API Response Analyser Agent:** Extracts the meaning of the API response and converts it into an internal schema representation for further processing + 3. **Stac Generator Agent:** Converts this generated internal schema along with other metadata into SDUI (Server Driven UI) to aid in visualisation. + 4. **Stac Modifier Agent:** This agent accepts Stac Code generated from the previous step and can make any modification as needed by the user via Natural language prompts + 5. **SDUI To Code Agent** This agent accepts Stac Code generated from the previous step and converts it into working framework-specific code (eg: Flutter) + 6. **Updated API Dash Client:** The changes needed on the API Dash Client would be modifications to the settings page and creation of a new Dialog with 5-6 new screens (examples provided below) + 7. **API Tool Generation Agent & UI:** This agent processes an API request and transforms it into a specific tool format that can be utilized by agentic frameworks. + ### Technical Details + ### Internal AI Service Architecture ![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/agentservice_arch.png) Parts of the Centralised Internal AI Agent Service Architecture - 1. **Input Layer:** the input to each agent will be a JSON/XML input along with an Agent Name. The Agent name will be used to specify which agent we want to call internally. -2. **Orchestrator:** this part takes the inputs and looks at settings, preferences and other contextual data to decide whether to send the request to an internal ollama agent or to a LLM provider like chatgpt, claude or gemini. This layer can also include things like rate limiting to prevent overuse. - - ```dart - //Inside the APIDashAIService implementation - static Future orchestrator(APIDashAIAgent agent) async { - final sP = agent.getSystemPrompt(); - final iP = await agent.getInput(); - final customKey = await getUserCustomAPIKey(); - //Implement any Rate limiting logic as needed - if (customKey == null) { - //Use local ollama implementation - return await call_ollama(systemPrompt: sP, input: iP); - } else { - //Use LLMProvider implementation - return await call_provider( - provider: customKey.$1, - apiKey: customKey.$2, - systemPrompt: sP, - input: iP, - ); - } - } - - ``` - +2. **Orchestrator:** this part takes the inputs and looks at settings, preferences and other contextual data to decide whether to send the request to an internal ollama agent or to a LLM provider like chatgpt, claude or gemini. This layer can also include things like rate limiting to prevent overuse. + ```dart + //Inside the APIDashAIService implementation + static Future _orchestrator( + APIDashAIAgent agent, { String? query, Map? variables } + ) async { + String sP = agent.getSystemPrompt(); + //Perform Templating + if (variables != null) { + for (final v in variables.keys) { + sP = sP.substitutePromptVariable(v, variables[v]); + } + } + final customKey = await _getUserCustomAPIKey(); + //Implement any Rate limiting logic as needed + if (customKey == null) { + //Use local ollama implementation + return await _call_ollama(systemPrompt: sP, input: query ?? ''); + } else { + //Use LLMProvider implementation + return await _call_provider( + provider: customKey.$1, + apiKey: customKey.$2, + systemPrompt: sP, + input: query ?? '', + ); + } + } + ``` + 3. **System Prompt:** The System Prompt is a large block of text that sets the overall context and specifies what task the agent must perform. This prompt is unique to each agent and will be constantly iterated upon to ensure the best possible results. *(Regular discussions with mentors needed to fine-tune this)* 4. **Agentic Model**: - - **ollama**: Ollama is a way to run LLMs locally on the host machine. This will be the default option for each agent unless specified otherwise by the user/input. We will be making use of the ollama_dart and ollama pub.dev packages to implement this. - - ```dart - //Inside the APIDashAIService implementation - static Future call_ollama({ - required String systemPrompt, - required String input, - }) async { - String ollamaInput = "$systemPrompt\\nInput:$input"; - var ollama = Ollama(model: 'QwQ32B-distilled'); - var result = (await ollama.chat( - prompt: input, - ))?['text']; - return result; - } - - ``` - - - **API Providers**: LLM services like ChatGPT, Claude and Gemini offer their services via AI endpoints too. By enabling this option in apidash, we are giving power-users the ability to add their favourite LLM's API Keys and use our services via that LLM - - ```dart - //Inside the APIDashAIService implementation - static Future call_provider({ - required LLMProvider provider, - required String apiKey, - required String systemPrompt, - required String input, - }) async { - switch (provider) { - case LLMProvider.gemini: - { - final response = await http.post( - Uri.parse(''), - headers: { - 'Authorization': 'Bearer $apiKey', - 'Content-Type': 'application/json', - }, - body: json.encode({ - 'model': '...', - 'prompt': '$systemPrompt $input', - }), - ); - return response.statusCode == 200 - ? json.decode(response.body)['text'] - : 'Error: ${response.statusCode}'; - } - //Similar Implementations for other Providers - default: - return null; - } - } - - ``` - + - **ollama**: Ollama is a way to run LLMs locally on the host machine. This will be the default option for each agent unless specified otherwise by the user/input. We will be making use of the ollama_dart package to implement this. + ```dart + //Inside the APIDashAIService implementation + static Future ollama( + String systemPrompt, + String input, [ + String model = 'llama3', + ]) async { + //check Ollama Avaiability + final result = await Process.run('curl', ['http://localhost:11434/api/tags']); + if (result.exitCode != 0) { + print('OLLAMA_NOT_ACTIVE'); + return null; + } + final inpS = input == '' ? '' : '\nProvided Inputs:$input'; + final client = OllamaClient(); + final generated = await client.generateCompletion( + request: GenerateCompletionRequest( + model: model, + prompt: "$systemPrompt$inpS", + ), + ); + return generated.response; + } + ``` + + - **API Providers**: LLM services like ChatGPT, Claude and Gemini offer their services via AI endpoints too. By enabling this option in apidash, we are giving power-users the ability to add their favourite LLM's API Keys and use our services via that LLM + ```dart + static Future _call_provider({ + required LLMProvider provider, + required String apiKey, + required String systemPrompt, + required String input, + }) async { + switch (provider) { + case LLMProvider.gemini: + return await APIDashCustomLLMService.gemini( + systemPrompt, input, apiKey); + ... + } + } + + static Future gemini( + String systemPrompt, + String input, + String apiKey, + ) async { + final inpS = input == '' ? '' : '\nProvided Inputs:$input'; + String combinedInput = "$systemPrompt$inpS"; + final url = Uri.parse( + 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=$apiKey'); + final response = await http.post( + url, + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({ + 'contents': [ + { + "parts": [ + {"text": combinedInput} + ] + } + ] + }), + ); + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + return data['candidates']?[0]?['content']?['parts']?[0]?['text']; + } else { + print("GEMINI_ERROR: ${response.statusCode}"); + return null; + } + } + ``` 5. **Validator:** LLMs are prone to hallucinations/wrong results. This layer aims to solve that issue by allowing users to create their own validation schemes. If the response validation is successful, we can move to the next step or else we send it to the governor which takes care of the whole retry mechanisms. -6. **Governor (Retry Mechanism):** If the validation is unsuccessful, the governor attempts to retry the AI request with an **Exponential Backoff** (successive-delays such as 200ms, 400ms, 800ms, 1.6s, 3.2s and so on). Even after 5 retry attempts if the response is not valid, then we return with a `MAX_RETRIES_EXCEEDED` error. - - ```dart - //Inside the APIDashAIService implementation - static Future governor(APIDashAIAgent agent) async { - int RETRY_COUNT = 0; - List backoffDelays = [200, 400, 800, 1600, 3200]; - do { - try { - final res = await orchestrator(agent); - if (res == null) { - RETRY_COUNT += 1; - } else { - if (await agent.validator(res)) { - return agent.outputFormatter(res); - } else { - RETRY_COUNT += 1; - } - } - } catch (e) { - print(e); - } - // Exponential Backoff - if (RETRY_COUNT < backoffDelays.length) { - await Future.delayed(Duration( - milliseconds: backoffDelays[RETRY_COUNT], - )); - } - RETRY_COUNT += 1; - } while (RETRY_COUNT < 5); - } - - ``` - -7. **Output Formatter:** This is a function that allows us to specify in what way we want our agent to return the validated AI response. This is useful for downstream processing and keeps things elegant. +6. **Governor (Retry Mechanism):** If the validation is unsuccessful, the governor attempts to retry the AI request with an **Exponential Backoff** (successive-delays such as 200ms, 400ms, 800ms, 1.6s, 3.2s and so on). Even after 5 retry attempts if the response is not valid, then we return with a `MAX_RETRIES_EXCEEDED` error. + ```dart + //Inside the APIDashAIService implementation + static Future governor(APIDashAIAgent agent) async { + int RETRY_COUNT = 0; + List backoffDelays = [200, 400, 800, 1600, 3200]; + do { + try { + final res = await orchestrator(agent); + if (res == null) { + RETRY_COUNT += 1; + } else { + if (await agent.validator(res)) { + return agent.outputFormatter(res); + } else { + RETRY_COUNT += 1; + } + } + } catch (e) { + print(e); + } + // Exponential Backoff + if (RETRY_COUNT < backoffDelays.length) { + await Future.delayed(Duration( + milliseconds: backoffDelays[RETRY_COUNT], + )); + } + RETRY_COUNT += 1; + } while (RETRY_COUNT < 5); + } + ``` -### Internal AI Service Proposed Implementation +8. **Output Formatter:** This is a function that allows us to specify in what way we want our agent to return the validated AI response. This is useful for downstream processing and keeps things elegant. +### Internal AI Service Proposed Implementation ```dart enum LLMProvider { chatgpt, claude, gemini } @@ -206,42 +230,44 @@ class APIDashAIService { static Future<...> orchestrator(...) async {...} static Future governor(APIDashAIAgent agent) async {...} } - ``` ### Modular AI Agent Blueprint (Abstract Class) - ```dart abstract class APIDashAIAgent { String get agentName; String getSystemPrompt(); - Future getInput(); Future validator(String aiResponse); Future outputFormatter(String validatedResponse); } -//Every Agent used in APIDash must extend and implement this class -//This approach makes everything extremely modular and testable too - +extension SystemPromptTemplating on String { + String substitutePromptVariable(String variable, String value) { + return this.replaceAll(":$variable:", value); + } +} +``` +Calling any agent from the frontend can be done like this: +```dart +final agent = DummyAgent(); //extends APIDashAIAgent +final ans = await APIDashAIService.callAgent( + agent, + variables: { + 'VAR_API_RESPONSE': resp_data, //SystemPrompt Templating + } +); ``` ### Agent Implementations -### Agent: API_RESPONSE_ANALYZER - +#### Agent: API_RESPONSE_ANALYZER ![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/ARANA.png) -- The API response is first parsed correctly according to its type (JSON / XML) and is then sanitized -- The Agent then goes through the whole response and generates relevant semantic context. This context will be very helpful in determining what is actually needed in the final UI -- The semantic context along with the sanitized response is then sent over to the LLM to be converted into intermediate-representation. This step is essential because it allows us to semantically include/exclude response data leading to good UI generation. - +- The API response is first parsed correctly according to its type (JSON / XML) and is then sanitised +- The Agent then goes through the whole response and generates relevant semantic context. This context will be very helpful in determining what is actually needed in the final UI +- The semantic context along with the sanitized response is then sent over to the LLM to be converted into a YAML based intermediate-representation. This step is essential because it allows us to semantically include/exclude response data leading to good UI generation. ```dart class ResponseAnalyzerAgent extends APIDashAIAgent { - @override - String get agentName => 'API_RESPONSE_ANALYZER'; - - @override - Future getInput() async {...} - + ... @override String getSystemPrompt() { return """You are an intelligent response analyzer agent..."""; @@ -249,116 +275,86 @@ class ResponseAnalyzerAgent extends APIDashAIAgent { @override Future validator(String aiResponse) async { - if(!aiResponse.contains('~~~~~')) return false; //No Separator - final sa, ir = aiResponse.split('~~~~~'); - try { - jsonDecode(ir); - } catch (e) { - return false; //Internal Representation was not valid - } + //Specific validations to check if the generation was correct return true; } - + @override Future outputFormatter(String validatedResponse) { //separator specified in system prompt - final sa, ir = validatedResponse.split('~~~~~'); + final sa, ir = validatedResponse.split('~~~~~'); return { 'SEMANTIC_ANALYSIS': sa.trim(), - 'INTERNAL_REPRESENTATION': jsonDecode(ir) + 'INTERNAL_REPRESENTATION': ir.trim() } } } - ``` -### Agent: UI_COMPONENT_GEN (Component Generator) - -![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/UICOMPGEN.png) - -- Using the internal schema and additional context, the UI Component Generator Agent determines the most appropriate UI component for the data. For example, if the data structure is a `List`, it can generate a table. -- The agent can further refine this decision based on factors such as data type, layout, and design preferences. -- This component is flexible and can generate code for different frameworks such as NextJS, HTML, or Flutter, with embedded HTTP calls and relevant data-fetching logic. - +#### Agent: STAC_GEN (SDUI Representation Generator) +![](https://github.com/synapsecode/CustomStorage/raw/main/stacgenimg.png) +- Using the internal schema and additional context, the SDUI Stac Generator Agent determines the most appropriate UI component for the data. For example, if the data structure is a `List`, it can generate a table and so on. This data is converted into a SDUI (Server Driven UI) representation called `Stac` from the [Stac](https://pub.dev/packages/stac) flutter package. +- The agent can further refine this decision based on factors such as data type, layout, and design preferences. +- The generated Stac code is basically a json representation of a flutter component and this can be used to create lightning-fast previews ```dart -class UIComponentGeneratorAgent extends APIDashAIAgent { - @override - String get agentName => 'UI_COMPONENT_GEN'; - - @override - Future getInput() async {...} - +class StacGenBot extends APIDashAIAgent { @override String getSystemPrompt() { - return """You are an intelligent UI Generating Agent...."""; - } - - @override - Future validator(String aiResponse) async { - //Language Specific Validations if needed - } - - @override - Future outputFormatter(String validatedResponse) { - //perform formatting operations as needed - return { - 'CODE': validatedResponse.trim() - } + return """You are an expert agent whose sole job is to generate Flutter-SDUI Stac Code"""; } + //Similar implementation as above but the validator & outputFormatter logic changes } - ``` -### Agent: COMPONENT_MODIFIER (Component Customization) -![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/COMPMOD.png) +#### Agent: STAC_MODIFIER (UI Customization using Natural Language) +![](https://github.com/synapsecode/CustomStorage/raw/main/stacmodifer.png) -- The generated UI component can be previewed in a Component Preview Window, allowing users to inspect the result. -- The Component Modifier Agent is an optional feature that allows users to modify the generated component using natural language prompts. This iterative process enables users to refine the UI to meet their exact needs. This is fairly easy to do as most LLMs can already do this very well. We just have to use good system prompting to get it right -- Common customizations like layout changes, pagination, and sorting can be added through buttons, making the customization process seamless for users. +- The generated SDUI code can be previewed in a Component Preview Window, allowing users to inspect the result. +- The Stac Modifier Agent is an optional feature that allows users to modify the generated SDUI using natural language prompts. This iterative process enables users to refine the UI to meet their exact needs. This is fairly easy to do as most LLMs can already do this very well. We just have to use good system prompting to get it right +- Common customisations like layout changes, pagination, and sorting can be added through buttons, making the customisation process seamless for users. ```dart -class ComponentModifierAgent extends APIDashAIAgent { - @override - String get agentName => 'COMPONENT_MODIFIER'; - - @override - Future getInput() async {...} - +class StacModifierAgent extends APIDashAIAgent { @override String getSystemPrompt() { - return """You are an intelligent Code Modifier Agent...."""; - } - - @override - Future validator(String aiResponse) async { - //Language Specific Validations if needed + return """You are an intelligent Code Modifier Agent who matches the client's needs"""; } - + //Similar implementation as above but the validator & outputFormatter logic changes +} +``` +#### Agent: STAC2CODE (Conversion from SDUI Code to Framework Code) +![](https://github.com/synapsecode/CustomStorage/raw/main/STAC2CODE.png) +- The generated SDUI code cannot be executed on the user's machine, hence we must convert this into actual flutter code. +- This is fairly easy to do as Stac is almost a one-one representation of flutter code just in JSON +- This property also makes it fairly easy to convert to other languages as the LLM can understand the context through the JSON SDUI and just convert it into another framework like NextJS code. This allows future extensibility. +```dart +class Stac2CodeAgent extends APIDashAIAgent { @override - Future outputFormatter(String validatedResponse) { - //perform formatting operations as needed - return { - 'MODIFIED_CODE': validatedResponse.trim() - } + String getSystemPrompt() { + return "You are an expert agent who accepts Stac SDUI code and converts it into Flutter.."; } + //Similar implementation as above but the validator & outputFormatter logic changes } - ``` -## System Architecture & Flow -![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/SYSRCH.png) +### Live UI Preview Implementation +One of the most important features of this entire AI UI Designer pipeline is the implementation of the UI Preview Engine. This allows the user to see how the generated component looks and can help them identify what changes may be needed. +Creation of a dynamic UI rendering system from Flutter code is very hard due to flutter's limitations (**reflection** is not allowed, **flutter_eval** has minuscule coverage, **remote flutter widgets** or webviews need external server support etc) -### Flow Example: Sample API Response +Hence the last option was to use **SDUI (Server Driven User Interface)** using [Stac](https://pub.dev/packages/stac) +This gives us a lot of flexibility as it is very similar in naming convention to Flutter and is hence easier to convert to Flutter Code using an LLM, it also has much greater coverage. +We will also be using a regular JSON based rendering approach as a fallback incase Stac is very unstable. -This is a sample response given when I hit a [DEMO API](https://reqres.in/api/users?page=0) +## System Architecture & Flow + +![image info](https://github.com/synapsecode/CustomStorage/raw/main/newmainarch.png) +### Sample API Response +This is a sample response given when I hit a [DEMO API](https://reqres.in/api/users?page=0) ```json { - "page": 1, - "per_page": 6, - "total": 12, "total_pages": 2, "data": [ { @@ -366,304 +362,351 @@ This is a sample response given when I hit a [DEMO API](https://reqres.in/api/us "email": "george.bluth@reqres.in", "first_name": "George", "last_name": "Bluth", - "avatar": "" - }, - { - "id": 2, - "email": "janet.weaver@reqres.in", - "first_name": "Janet", - "last_name": "Weaver", - "avatar": "" + "avatar": "https://reqres.in/img/faces/1-image.jpg" }, ... ] } - ``` - -This is subsequently sent to our frontend prompt where we accept the target framework, let's assume the user has selected `Flutter` +This is subsequently sent to our frontend prompt where we accept the target framework, let's assume the user has selected `Flutter` This response will first be sent into our Semantic Agent which tries to pick out what this response means. ---- -### Flow Example: Generated Semantic Response +---------- + + +### Generated Semantic Response + +*This JSON response represents a paginated list of user data. The 'data' field is an array containing user objects. Each user object includes 'id', 'email', 'first_name', 'last_name', and 'avatar'. The 'page', 'per_page', 'total', and 'total_pages' fields provide pagination information. The 'support' object can be omitted as it is not directly related to the user data itself. A suitable UI would be a List View to display the user information. Details should include the avatar (as an image), first name, last name, and email. The 'id' is useful internally, but may not be needed in the display. Pagination controls (e.g., next/previous page buttons or page number selection) are required to navigate through the pages of data.* + + +### Generated YAML-Based Internal Representation +```yaml +- type: column + elements: + - type: row + elements: + - type: image + src: "https://reqres.in/img/faces/1-image.jpg" + shape: circle + width: 60 + height: 60 + - type: column + elements: + - type: text + data: "George Bluth" + font: "segoe-ui" + color: blue + - type: text + data: "george.bluth@reqres.in" + font: "segoe-ui" + color: gray + ... +``` +### Generated Stac Representation (For SDUI Preview) +```json +{ + "type": "scaffold", + "backgroundColor": "#002b36", + "appBar": { + "type": "appBar", + "backgroundColor": "#073642", + "title": { + "type": "text", + "data": "User List", + "style": { + "color": "#839496" + } + } + }, + "body": { + "type": "listView", + "children": [ + { + "type": "card", + "margin": { + "left": 8, + "top": 4, + "right": 8, + "bottom": 4 + }, + ... ``` -The response provides paginated user data with information such as the current page (1), number of users per page (6), total users (12), and total pages (2). Each user entry includes an ID, email, first name, last name, and an avatar image URL. For the UI, display the user's name (first and last), email, and avatar, with avatars shown as circular images. Pagination controls should be implemented with "Previous" and "Next" buttons, as well as page numbers based on the current page and total pages. Ensure the UI dynamically adjusts to show 6 users per page and maintain visual clarity with proper spacing for readability. The design should effectively present the user data while allowing easy navigation between pages. +Now that the output is in Stac, It can easily be sent to the next bot **STAC_MODIFER**) (optional) which can easily make any changes that you request via *Natural Language* +### Generated Flutter Code +```dart +Scaffold( + backgroundColor: const Color(0xFF002b36), + appBar: AppBar( + backgroundColor: const Color(0xFF073642), + title: const Text( + "User List", + style: TextStyle( + color: Color(0xFF839496), + ), + ), + ), + body: ListView( + children: [ + Card( + margin: const EdgeInsets.fromLTRB(8, 4, 8, 4), + elevation: 2, + color: const Color(0xFF073642), + ... ``` -### Flow Example: Generated Internal Representation +### Sample Output (Flutter) +![image info](https://github.com/synapsecode/CustomStorage/raw/main/flutout.png) -```json -[{ - "type": "column", - "elements": [ - { - "type": "row", - "elements": [ - { - "type": "image", - "src": "", - "shape": "circle", - "width": "60", - "height": "60" - }, - { - "type": "column", - "elements": [ - { - "type": "text", - "data": "Michael Lawson", - "font": "segoe-ui", - "color": "blue" - }, - { - "type": "text", - "data": "michael.lawson@reqres.in", - "font": "segoe-ui", - "color": "gray" - } - ] - } - ] - }, - { - "type": "vspacer", - "spacing": "20" - }, - ... - ] - }, - { - "type": "vspacer", - "spacing": "30" - }, - { - "type": "row", - "elements": [ - { - "type": "button", - "text": "Previous", - "onclick": "page=1" - }, - ... - ] - }, - ... - { - "type": "link", - "text": "Tired of writing endless social media content? Let Content Caddy generate it for you.", - "url": "" -}] -``` +## Sample UI -### Generated Flutter Component +![image info](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/framwdiag.png) +![image info](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/LDG.png) +![image info](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/compo.png) +If any modifications are made, it will go back to loading screen and come back to this page with the relevant modifications. +When Export is clicked, the relevant generated code is **copied to clipboard** and the dialog closes! -```dart -class UserListWidget extends StatelessWidget { - final List> users = [ - { - 'firstName': 'Michael', - 'lastName': 'Lawson', - 'email': 'michael.lawson@reqres.in', - 'avatar': '', - }, - // Add more users here... - ]; +### Proof of Concept (PoC) +A Simple and elegant prototype has been created for the entire pipeline. It hightlights the whole process of Response Sentiment Analysis, YAML-based Intermediate Representation, JSON based SDUI generation and Final Code Export. +Prototype link: [https://github.com/synapsecode/AI_UI_designer_prototype](https://github.com/synapsecode/AI_UI_designer_prototype) - final int currentPage; - final int totalPages; - final Function(int) onPageChange; +--- +# Automated API Tool Generation + +An API Tool Generator basically is a system to automatically generate functions or methods from a given API description which enables AI agents to interact with APIs on their own without any manual coding from our end. + +This functionality is particularly useful for developers working on AI-driven applications, as it simplifies integrating external APIs into agent workflows, enhancing efficiency and reducing development time. + +Reference: [https://www.postman.com/explore/toolgen](https://www.postman.com/explore/toolgen) + +## Implementation Details + +Since this is an agent application itself, we can reuse the `APIDashAIAgent` abstract class mentioned above to implement our agents needed for this task. This highlights its modular and easy-to-use nature. + +`Supported Agentic Frameworks`: **OpenAI**, **LangChain**, Anthropic, **Gemini**, Mistral, Microsoft Autogen +`Supported Programming Languages`: Python, JavaScript +*(same as Postman's Implementation)* + +![](https://github.com/synapsecode/CustomStorage/raw/main/TOOLGENARCH.png) + +- **Step 1: API Request Consolidation:** We accept the incoming API Request and add all of the relevant data (method, url, headers, auth etc) into a single text object named `REQDATA` this will be useful for the previous steps. +- **Step 2: Tool Template Selector:** After doing some research, it appears that *API Tools* have a very limited number of templates. + `example`: Tool Format used by Anthropic, Mistral, Gemini, OpenAI etc for python + ```python + def func(...): + #Provided API being called in the language specific way + + api_tool = { + "function": func, + "definition": { + "name": TOOL_NAME, + "description": TOOL_DESCRIPTION, + "parameters": { + "type": "object", + "properties": { + ARG_NAME: { + "type": ARG_TYPE, + "description": ARG_DESC + }, + ... + }, + "required": [ALL_REQUIRED_ARGUMENT_NAMES], + "additionalProperties": False #Anthropic Specific + } + } + } - UserListWidget({ - required this.currentPage, - required this.totalPages, - required this.onPageChange, - }); + __all__ = ["api_tool"] #Export the Tool + ``` + example: Tool Format used by Langchain + ```python + from langchain.tools import StructuredTool + class InputSchema(BaseModel): #Pydantic Model + title: str = Field(..., description="...") + ... + def func(...): + #Provided API being called in the language specific way + api_tool = StructuredTool.from_function( + func=func, + name="", + description="", + args_schema=InputSchema, + ) + __all__ = ["api_tool"] #Exporting the Tool + ``` + and so on. Hence, We can easily store these as templates and substitute the variable values as needed. +- **Step 3: Func Gen:** We use the `APIDashAIAgent` class to create another agent called FUNC_GEN which accepts the `REQDATA` & Target language and converts it into a Function that calls the api + example: + ```python + import requests + def func(data): + try: + response = requests.post("...", data=data) + response.raise_for_status() + return response.json() + except requests.exceptions.RequestException as e: + print(f"Error: {e}") + return {"error": "An error occurred while calling API"} + ``` +- **Step 4: Tool Generation:** This simple agent takes the function generated from the previous step along with the selected template and combines them. `REQDATA` is used to fill in the variables with contextual data. +- **Step 5: Tool Export:** The complete tool code can now be exported and used by the user as needed + +## Week-wise Breakdown - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - children: users.map((user) { - return Row( - children: [ - CircleAvatar( - radius: 30, - backgroundImage: NetworkImage(user['avatar']), - ), - SizedBox(width: 16), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '${user['firstName']} ${user['lastName']}', - style: TextStyle(fontFamily: 'segoe-ui', color: Colors.blue), - ), - Text( - user['email'], - style: TextStyle(fontFamily: 'segoe-ui', color: Colors.grey), - ), - ], - ), - ], - ); - }).toList(), - ), - SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: currentPage > 1 ? () => onPageChange(currentPage - 1) : null, - child: Text('Previous'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: () => onPageChange(1), - child: Text('1'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: () => onPageChange(2), - child: Text('2'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: currentPage < totalPages ? () => onPageChange(currentPage + 1) : null, - child: Text('Next'), - ), - ], - ), - SizedBox(height: 10), - TextButton( - onPressed: () { - // Open the link - }, - child: Text( - 'Tired of writing endless social media content? Let Content Caddy generate it for you.', - style: TextStyle(color: Colors.blue), - ), - ), - ], - ); - } -} +![image_info](https://github.com/synapsecode/CustomStorage/raw/main/gsocgantt2.png) -``` +### Week 1 (Internal AI Service Development) +- Discuss Future Agentic needs for Smarter code structuring +- Implement the APIDashAIService & APIDashAIAgent core classes +- implement the call_ollama and call_provider LLM accessing functions +- Implement the proposed governor and orchestrator logic +- Implement Variable Replacement Feature for dynamic system prompts -### Sample Output (Flutter) + **DELIVERABLE**: A Working version of the APIDashAIService with functional calls to both ollama and external LLM providers along with a working retry mechanism -![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/flutex.jpg) -Now that the output is in Flutter Code, It can easily be into the next bot **Component Editor Agent**) which can easily make any changes that you request via *Natural Language* +### Week 2 (Response Analyser Agent Development) +- Write a detailed System Prompt to extract Semantics from a Response +- Test Semantic output against various responses & tweak system prompt +- Write a detailed System Prompt to create a YAML-based internal representation UI Schema from a given API Response and test it against various responses +- Enhance the agent to intelligently omit unnecessary parts of the response in UI +- Implement Validation & Formatting rules for the Semantic Analyser and IR-Gen bots -## Sample UI + **DELIVERABLE**: A Working version of the complete Response Analyser agent that can accept a given API response and give both the complete Semantic Description & the YAML-based internal representation -![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/framwdiag.png) +### Week 3 (Stac-SDUI Generator Agent Development) -![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/LDG.png) +- Iterate and decide on a robust system prompt that converts SA & IR into Flutter-SDUI `Stac` Representation for UI Previewing +- Creation of a Stac-Error Detector to aid in retry mechanism for faulty generations +- Write custom validation logic to deal with Stac's nuances (eg: `bold` is not allowed as a fontWeight in Stac) +- Talk to Stac's team and apidash mentors to decide how to support certain widgets that are not available in the SDUI representation (eg: `ClipRRect`) +- Test out multiple API responses to see if the generated SDUI matches mentors' expectations, if not tweak the system prompt iteratively until satisfactory -![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/compo.png) + **DELIVERABLE**: An agent that accepts the SA & IR from the previous step and converts it into an SDUI compatible "Stac" representation which is a JSON Server Driven UI Representation which can be useful for the upcoming Live Preview Feature + +### Week 4 (Natural Language UI Customization & Preview) -If any modifications are made, it will go back to loading screen and come back to this page with the relevant modifications. -When Export is clicked, the relevant generated code is **copied to clipboard** and the dialog closes! +- Implement a bot named `StacModifier` that can make the user-requested changes on the fly to the previously generated `Stac` SDUI code +- Develop the Component Preview View using a custom implementation called `StacRenderer` +- Implement Validation & Formatting rules along with `Content Moderation` & `Guard-railing` rules to prevent unwanted modifications +- Test against various types of modification requests to ensure that the bot is capable of making a wide variety of changes -## Week-wise Breakdown + **DELIVERABLE**: A working agent that accepts the previously generated SDUI representation along with the user's natural language prompt to make any requested changes on the fly. (eg: Convert this UI into a solarized theme, should result in the solarized version appearing as a UI preview) + +### Week 5 (Code Export: SDUI → Flutter Code) + +- Create the System Prompt for an agent named `Stac2Code` to convert SDUI-JSON into valid Flutter Code +- Create the necessary Validation & output formatting code (eg: removing triple backtick notation and so on) to ensure that the generated component is ready to be directly copy-pasted into the user's IDE +- As SDUI is a JSON representation, once the code generation is stable, we can explore the conversion of it into other frameworks like NextJS too. +- Run the Code export action on a varity of generated `Stac` representations to see if the resultant Flutter code works as expected in a reliable manner + + **DELIVERABLE**: By the end of the week, we will have an agent that converts the previously generated SDUI code into a fully working Flutter Component (that can be tested on Online IDEs). This will mark the end of the AI-UI-Designer pipeline requirements -![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/timeline.png) +### Week 6 (APIDash Client Specific UI Changes) -### Week 1 (Community Bonding) +- Create the UI & implementation in the settings page to have an option to select your AI Engine (ollama or custom) and in case it is custom, add an option to pass your API Key and select the provider +- Write the implementation to persist this data and use it for all other agentic actions (LLMKeystore Implementation) +- Work on the secure storage of these API Keys to prevent leaks and theft +- Create the UI screens as shown above in the Sample UI Section +- Integrate the whole UI generation pipeline into the frontend and delete any prototype UI screens created during prior development -- Get in touch with the developers and the mentor -- Introduction to the community, to the mentor and fix timings to communicate -- Set communication schedules for regular updates. -- Go through the API Dash codebase and dependency packages, especially the structure around melos. -- Complete local setup (macOS platform support) and run the product in DEBUG mode. -- Discuss any suggestions and changes to the project. There could modifications, new additions or amendments; it would be better to go over these early -- Understand the state management approaches and dependency structure. -- Discuss the working of the existing application and the implementation of the new features with the mentor + **DELIVERABLE**: By the end of the week, we will have a new api dash client with the settings UI modified to include a `AI Engine` selector and a `LLM Api Key` field. + Additionally we will have the UI implemented for the whole UI generation pipeline created and integrated with the described agents. -### Week 2 (Internal AI Service Development) +### Week 7 (Mobile Specific Changes & Additional Buffer) -- Discuss the present and future Agentic AI requirments that apidash might need with the mentors, this allows for smarter code planning. -- Begin extensive implementation of the `APIDashAIService` class along with the other abstract classes such as `APIDashAIAgent` -- Thoroughly implement the `call_ollama` and `call_provider` functions for local and provider-based LLM actions. -- Implement the `governor` and `orchestrator` logic by expanding upon the initial ideas presented above.. +- Since apidash works on mobile too, this week will be used to convert all the existing desktop-styled UI components into mobile friendly UI components +- ollama is not accessible via localhost on a phone, hence on mobile devices we must include a textbox in the settings page so that the user can paste their hosted ollama instance link (they must also be able to select their model of choice) +- Creation of a tab based layout in the UI Previewer screen to ensure that the UI Preview and code display are not too crammed up. +- The export code button should ideally open up the mobile-os specific share sheet to make it easier to share the generated component code +- Additional Tasks that were pending or put into backlog from the earlier weeks can be completed here. -### Week 3 (API Response Analyser Agent Development) + **DELIVERABLE**: By the end of the week, the complete working pipeline that was available on desktop will also be available on a mobile platform (Android, iOS). The UI will be modified to better fit the smaller screen size and the feature will now be ready for next week's complete flow testing -- Understand how to map API response formats into internal schema representation and translate it into a very verbose and well structured `System Prompt` -- Understand and iterate on ways to make the System Prompt better at Semantic analysis of the response content -- Research on which API response data meaningfully contributes to the final UI and which data is irrelevant. This can help in improving user satisfaction. -- Start testing with real-world API responses to ensure proper parsing and extraction of meaningful data. +### Week 8 (Complete Flow Testing & Agent Optimization) -### Week 4 (UI Component Generator Agent Development) +- For each bot created in the pipeline, we must rigorously run manual tests on different types of inputs and check if they match the expectations. The data generated from these tests must be noted down in a separate report +- Discuss with mentors to implement proper code testing strategy to ensure that the whole pipeline is tested both on a unit basis and entire flow basis. This allows us to eventually pass this whole feature into an automated testing suite +- The agents that need tweaking/optimization will have their System prompts modified to yield better results. +- All the code written until now should be carefully inspected for side-effects, possible loopholes and so on. Work with the mentors to make the written code classified as `production-ready` -- Design a comprehensive and detailed system prompt which can convert the IR and SA into valid code in the target language. -- Implement a basic conversion for a sample API response to UI components (start with JSON → Table). -- Develop a testing plan to ensure the generated UI components match the expected functionality -- Test and validate generated components in a sample frontend + **DELIVERABLE**: By the end of the week, we will have clarity on an automated testing strategy along with written tests as well as a manual testing report. This will give an idea if the entire pipeline is working according to the mentors' expectations or not. -### Week 5 (Customization & Natural Language Interaction) +### Week 9 (Request Consolidation & Metadata Extractor) -- Implement options to modify styles such as color, size, and borders for UI components. -- Develop the Component Modifier Agent, which allows the user to modify UI components via natural language prompts. -- allow users to perform advanced modifications on the generated UI components, such as resizing, renaming, or changing themes using plain text -- Develop a testing strategy for different types of modifications users may request. -- Discuss any breaking changes that would arise in the UI because of these integrations +- Implement input parsing logic to extract METHOD, URL, HEADERS, BODY, PARAMS, AUTH, etc., and consolidate into `REQDATA` +- Accept inputs `AGENTIC_FRAMEWORK` and `TARGET_LANGUAGE` and implement the UI to do so +- Implement Changes to the AI UI Designer to accomodate pagination, sorting, filtering and other features requested in the project description. This should be easy to do for an LLM agent as they are standard code actions. -### Week 6 (Multi-framework Support & Export Mechanism) -- Extend the UI component generation to support other UI Frameworks/Libraries such as React, Tkinter etc -- Extend the entire system to support XML API Responses -- Implement a One Click Export button that generates the final UI code in the selected framework. -- Test the generated export functionality by testing it in other framework specific codebases + **DELIVERABLE**: A working module that accepts API specifications and user inputs, and outputs a unified REQDATA. -### Week 7 (User interface Updates & Testing) +### Week 10 (Template Creator and Selector) -- Modify the **API Dash Client**, including changes to the settings page and a new dialog for UI Generation. -- Migrate from the testing mockup screens to actual full fledged designs pre-approved by the mentor -- Conduct tests of the entire flow: from API response analysis, schema generation, UI component generation, customization, and exporting. -- Gather feedback from the mentor and iterate on any issues or improvements. +- Design reusable templates for tool definitions across frameworks: OpenAI, Gemini, Mistral, Anthropic, LangChain, and Autogen. +- Implement a simple rule-based tool selector which can select the relevant template based on `AGENTIC_FRAMEWORK` and `TARGET_LANGUAGE` -### Week 8 (Final Buffer, Optimization & Report) + **DELIVERABLE**: The Rule based Tool Selector that can reliably return the relevant template based on inputs -- Use this buffer for any remaining tasks, bug fixing, or addressing missed items. -- Document all the components built, their functionality, and how they can be used. -- Discuss the project progress with the mentor and provide a plan for any future enhancements. -- Write the final project report covering key achievements, challenges faced, and any future plans. +### Week 11 (FUNC_GEN & TOOL_GEN Implementation) -## 7. Relevant Skills and Experience +- Creation of the **FUNC_GEN** Bot that can convert a given API Request into a piece of language-specific code which calls the API and returns its value +- Creation of the **TOOL_GEN** Bot that can convert the selected Template and Generated Function into a fully functional API Tool -### 7.1. Skills + **DELIVERABLE**: Both of the bots in complete working condition with correct code output. + +### Week 12 (UI Integration & Tool Testing) + +- Integrate this newly created pipeline with the existing apidash application +- Test with multiple examples to confirm that both of the bots are working properly +- Any pending tasks from the previous weeks can be completed here + + **DELIVERABLE**: The Entire Product fully working & Tested: AI UI Designer + API Tool Generator + +### Week 13 (Report & Documentation) +- The Documentation for the entire pipeline to be written in this period along with the final GSoC Report. + + **DELIVERABLE**: GSoC Report + +--- + +## Relevant Skills and Experience + + +### Skills - Languages: Dart, Python, Java, Javascript, Typescript, Go - Frameworks / Libraries: Flutter, Flask, FastAPI, SpringBoot, ReactJS, NextJS, Tailwind -- Databases & Tools: PostgreSQL, Supabase, Firebase, MongoDB, Git, Docker, Cloudflare Workers, [Dify.ai](http://dify.ai/), n8n +- Databases & Tools: PostgreSQL, Supabase, Firebase, MongoDB, Git, Docker, Cloudflare Workers, Dify.ai, n8n + + -### 7.2 Experience +### Experience + +- Lead Developer at [Crezam](https://crezam.com) (AI Powered assessments and interview platform); Extensive work on Flutter for Mobile, Web and MacOS platforms +- Flutter Developer for [Atlas Knowledge](https://atlas.fm/) which is a bookmark based second-brain +- Flutter Developer at [FitchoiceWorld](https://www.fitchoiceworld.com/) which is a fitness centre booking & wellness application +- I have a lot of experience with Flutter Web and MacOS including creation of platform specific code and then calling them via bridges/interop channels. +- Experience with using Agentic tools such as Dify to handle GenAI specific tasks + -- Lead Developer at [Crezam](https://crezam.com/) (AI Powered assessments and interview platform); Extensive work on Flutter for Mobile, Web and MacOS platforms -- Flutter Developer for [Atlas Knowledge](https://atlas.fm/) which is a bookmark based second-brain -- Flutter Developer at [FitchoiceWorld](https://www.fitchoiceworld.com/) which is a fitness centre booking & wellness application -- I have a lot of experience with Flutter Web and MacOS including creation of platform specific code and then calling them via bridges/interop channels. -## 8. Projects +## Projects - **Microblogger** - - A full scale social media application built using Flutter and Flask which includes a wide variety of posts, stories and interactive content fully built from scratch. The whole process was thoroughly documented under my 100 days of code challenge - - Github: [https://github.com/synapsecode/MicrobloggerV1](https://github.com/synapsecode/MicrobloggerV1) - - Public Build Log: [https://tulip-quality-7a5.notion.site/Microblogger-Builds-0b6f44eece5b419ca57f8a431e03ad2c](https://www.notion.so/Microblogger-Builds-0b6f44eece5b419ca57f8a431e03ad2c?pvs=21) + - A full scale social media application built using Flutter and Flask which includes a wide variety of posts, stories and interactive content fully built from scratch. The whole process was thoroughly documented under my 100 days of code challenge + - Github: [https://github.com/synapsecode/MicrobloggerV1](https://github.com/synapsecode/MicrobloggerV1) + - Public Build Log: [https://tulip-quality-7a5.notion.site/Microblogger-Builds-0b6f44eece5b419ca57f8a431e03ad2c](https://tulip-quality-7a5.notion.site/Microblogger-Builds-0b6f44eece5b419ca57f8a431e03ad2c) + + - **FFPNS (Flutter Firebase PushNotifications Service)** - - A package that I created to make it extremely simple to integrate firebase push notifications into a fluter app with minimal setup & advanced features such as background message handling and so on - - Github: [https://github.com/synapsecode/ffpns](https://github.com/synapsecode/ffpns) + - A package that I created to make it extremely simple to integrate firebase push notifications into a fluter app with minimal setup & advanced features such as background message handling and so on + - Github: [https://github.com/synapsecode/ffpns](https://github.com/synapsecode/ffpns) + ### More About Me - I’m Manas M. Hejmadi, a third-year Computer Science & Engineering student at DSCE. With a strong foundation in **Mobile & Desktop Development**, **Backend Engineering**, **Microservices** and **AI Integrations**, I am passionate about building good software at a rapid pace. I have nearly **6 years of experience in Flutter**, along with extensive experience in other technologies such as **Flask**, **NodeJS**, **NextJS**, **Celery**, **Docker**, **SpringBoot**, **Java**, and **Svelte**. Currently, I lead a team of 3 developers at [Crezam](https://www.crezam.com/), where we are focused on revolutionizing the hiring process through **AI-powered assessments and interviews.** I also actively participate in hackathons and tech events, earning second place in **IIT Roorkee’s App Innovation Challenge**. I am particularly interested in leveraging AI agents to create more powerful, user-centric applications. From 0bbd09d62f1d32d94c5df7ad2a3db729eaea2b2b Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 03:20:07 +0530 Subject: [PATCH 008/188] Create Application_K_Govind_API_Support.md --- .../gsoc/Application_K_Govind_API_Support.md | 432 ++---------------- 1 file changed, 44 insertions(+), 388 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index b8daccb85..2b41c238c 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -11,8 +11,7 @@ - LinkedIn: https://www.linkedin.com/in/k-govind-226529270/ - **Time Zone:** Thrissur, KL · UTC+5:30 (IST) -- **Resume:** https://drive.google.com/drive/folders/1nHlF1_uSjGRc-DruOZaVZeZT-MF8FBVh?usp=sharing - +- **Resume:** [Publicly accessible link to resume] --- @@ -28,427 +27,84 @@ ## Motivation & Past Experience ### Open Source Contributions -- My open source journey started from APIDash itself. I discovered it towards the end of last year and became a regular user of it. +- My open source journey started from APIDash itself. I discovered it ast year and became a regular user of it. - **Repo Links/PRs:** - Adding codegen for hyper-rust (https://github.com/foss42/apidash/pull/468) :- I thought this to be a comparitively easy contribution when i started but became challenging when i encountered with lack of multipart functionaility in the package. I made some mistakable commits since it was first PR but with all that happened was still able to make a quality PR - SSL Feature (https://github.com/foss42/apidash/pull/512) :- I came across this issue first while testing the hyper codegen i made. I took notice of the issue and when came to learn that the feauture was not in apidash took initiative to make it happen. - Client running in background problem even after removal (https://github.com/foss42/apidash/pull/560) ;- A small bug that came across me while using the app. - - Multipart cancellation error and Request cancellation flow Error :- Both of these bugs came acorss me while trying to implement the web socket protocol in the app. + - Multipart cancellation error and Request cancellation flow Error :- Both of these errors came acorss me while trying to implement the web socket protocol in the app. ### Proud Project/Achievement -- **Project Name:** EKO is a combination of reselling and recycling platforms for electric components which connects the technicians with the common public.The project gave us Kerela state level winner title in the prestigious YIP Programme. This was one of the first applications that i have coded. -- **Why it matters:** Upon uploading the IMEI No(if component has it) ,model no and model name. Using gemini api we produce all the components inside the phone ,computer etc(using gemini api).The data is then stored . Technicians can now search the components seperately and can purchase the component they want. Rather than scraping (were valuable components are lost) this is a better way to go forward. The consumers get the maximum value price for each of their working components. Imagine your phone is lagging due to overuse.The app comes to help you to sell your undamaged screen , phone speaker etc. The application also had computer vision to recognize electric components lying around your house to know how much it is worth. -- **Repo/Link:** Frontend: https://github.com/Apollo-Blaze/Ekov1 - Backend: https://github.com/Clasherzz/eko +- **Project Name:** [Project Title] +- **Why it matters:** [Brief Explanation] +- **Repo/Link:** [Link to Project] ### Motivating Challenges -- I like challenges of two kind, one which demands me to think and implement what i learned. And the other one which makes me learn new things. +- I like challenges of two kind, one which demands me to think and implement what i learned. And the other one is which makes me learn new things. ### Availability for GSoC - **Will you work full-time on GSoC?** No -- **If not, what else will you be working on?** I currently have to meet with my study requirements and have to continue my Btech course. My initial plan is to contribute 2 hours per week day and 5 hours in weekend days which i beleive is enough for achieving the proposals goal. +- **If not, what else will you be working on?** I currently have to meet with my study requirements and have to continue my Btech course. My initial plan is to contribute 2 hours per week days and 5 hours in weekend days which i beleive is enough for achieving the proposals goal. ### Communication & Sync-ups - **Do you mind regularly syncing up with mentors?** I am totally fine with syncing up with mentors for being uptodated on my work and for feedbacks. If its a virtual meet it would be great to have a heads up about the time. ### Interest in API Dash -- **What interests you about API Dash?** - What fascinates me most about APIDash is its elegant Flutter architecture,vibrant community, and its codebase. Despite standing humbly among industry giants, APIDash holds its ground with a well-structured, easy-to-understand codebase that helped me understand how code must be written for a complicated application. The clarity and separation in its design make it a pleasure to explore and contribute to.Apart from that by contributing and going through open issues I was abl to understand a lot of backend concepts.While it may be an underdog today, I have no doubt that it will grow into a formidable contender in the future. I look forward to contributing more features and supporting others along the way! - +- **What interests you about API Dash?** - **Areas for improvement:** I feel like the application would fullfill its goal as it reaches into wider audience of api testers . And for that the application has to tend to broader needs of api testers.Currently APIDash supports on REST APIs and GRAPHQL in its work flow. It would be great to add in other protocols into the app. --- - ### API Testing Support -I would like to introduce some feautures as listed below to enhance the api testing support of the application.All this abstracts are part of a single proposal. - - -### Web Socket Implementation (Issue Number: #15) -Introducing web socket implementation into the APIDash. Introduce a new client and manager to handle the websocket messages and render them in the ui . - -The solution involves providing the users with an option to change ping intervals, number of reconnection attempts, interval between reconnection attenpts. All of this would be managed inside the settings. The ui updates would be made using riverpod provider specifically for websocket messages for each request ids.Finally during onError and onDone the frames are added into the websocket Response Model. All this would be initiated by user clicking on the new APIType (Web Socket). The appropriate changes would be done in codegen related files to give code. -In the proposed approach I am using a web Socket Client Manager similar to the Http Manger to not conflict with the existing services. - -Operations on incoming messages: --Upon the incoming messages we can delete specific ones or whole. --We can also search for keywords withing every message. Which would be highlightned.(Not Submitted in POC) - -I have tested my approach on custom endpoints https://github.com/Clasherzz/testing/blob/main/websock.js , echo websocket and multiple fast incoming web socket message endpoints related to bit coin and stock prices to ensure robustness. -Architecture is as shown below: - ``` - +--------------------------------+ -| WebSocket Server | -| - Manages connections | -| - Handles messages | -+---------------+--------------+ - | - v -+--------------------------------------+ -| Settings Connection Manager | -| - Ping interval handling | -| - Reconnection attempts | -| - Interval between retries | -+----------------+-------------------+ - | - v -+---------------------------------------------------------------+ -| WebSocket Client (web_socket_channel) | -| - Establishes connection | -| - Sends & receives messages | -| - Handles ping & retries | -| - onError → Updates Riverpod WebSocket Response Model State | -| → Saves event history in HistoryModel Provider | -| - onListen → Updates WebSocket Messages Provider | -| - onDone → Updates Riverpod WebSocket Response Model State | -| → Saves event history in HistoryModel Provider | -+----------------------+----------------------+----------------+ - | | - | | -+------------------------------------+ +--------------------------------+ -| WebSocket Messages Riverpod | | Riverpod State Management | -| Provider | | - Stores all messages | -| - Stores incoming messages | | - Updates WebSocket Model | -| - Groups messages by request ID | | - Handles UI reactivity | -| - Provides real-time updates | +--------------------------------+ -+------------------------------------+ | - | | - v | -+------------------------------------------------+ -| HistoryModel Provider | -| - Saves event history upon errors (`onError`)| -| - Saves event history when done (`onDone`) | -| - Stores event metadata & timestamps | -| - Persists disconnected session logs | -+------------------------------------------------+ - | - v -+-----------------------------------------+ -| Flutter WebSocket UI | -| - Search messages | -| - Clear all/one message | -| - Scroll to top/up option | -| - Dynamic UI per request | -| - View WebSocket Message History | -+-----------------------------------------+ - -``` - - -- **Technologies & Tools:** The approach uses the package web_socket_channel(^3.0.1).(https://fluttergems.dev/packages/web_socket_channel/).Supports all platforms. -- **Expected Outcomes:** A clean ui with maximum smoothness satisfying above mentioned solutions. -- **Linked PR for POC:** https://github.com/foss42/apidash/pull/555 (The PR is in no way the final product but simply to show the code structure and my approach) - ![Alt text](./images/websocket(1).png) - ![Alt text](./images/websocket(2).png) - - -### SSE Support(Issue Number #116) -Implementation fo server send events in the Apidash. - -Trying to implement SSE Support into the application. Using a special provider for incoming frames just like in web socket messages . Now the incoming messages would be parsed and decided if it is comment ,data,event,id,retry. Data is the most important one that is needed to be shown. Often comments , id , retry are hidden away. We can provide an option for advanced visibily in settings that helps the developer to see these frames if they want. This can enhance their testing. - -The apiType uses the Http Client Manager itself but generates a streamed reponse and listens into it.Inorder to get the actual request headers send by the client i had to use the http_interceptopr package(https://pub.dev/packages/http_interceptor). - -My approach was tested on a https://sse.dev/test - -Architecture is as shown below: -``` - +--------------------------------+ - | SSE Server | - | - Manages connections | - | - Sends event streams | - +---------------+--------------+ - | - v - +--------------------------------------+ - | Settings Connection Manager | - | - Handles retry intervals | - | - Manages reconnections | - | - Configures event listeners | - | - (Optional) Show Advanced Options| - | - Comments | - | - ID | - | - Retry Interval | - +----------------+-------------------+ - | - v - +-------------------------------------------------------------------+ - | SSE Client (http package / eventsource) | - | - Establishes connection | - | - Listens for events | - | - Handles automatic reconnections | - | - onError → Updates Riverpod SSE Response Model State | - | → Saves event history in HistoryModel Provider | - | - onEvent → Updates SSE Messages Provider | - | - onDone → Updates Riverpod SSE Response Model State | - | → Saves event history in HistoryModel Provider | - | - **HTTP Interceptor** (Inside SSE Client) | - +----------------------+----------------------+--------------------+ - | | - | | - +------------------------------------+ +--------------------------------+ - | SSE Messages Riverpod Provider | | Riverpod State Management | - | - Stores incoming messages | | - Stores all messages | - | - Groups messages by event ID | | - Updates SSE Model | - | - Provides real-time updates | | - Handles UI reactivity | - +------------------------------------+ +--------------------------------+ - | | - | | - +------------------------------------------------+ - | HistoryModel Provider | - | - Saves event history upon errors (`onError`)| - | - Saves event history when done (`onDone`) | - | - Stores event metadata & timestamps | - | - Persists disconnected session logs | - +------------------------------------------------+ - | - v - +-----------------------------------------+ - | Flutter SSE UI | - | - Search messages | - | - Clear all/one message | - | - Scroll to top/up option | - | - Dynamic UI per event type | - | - View Event History | - +-----------------------------------------+ - -``` -POC Link: https://github.com/foss42/apidash/pull/757 -Images of POC: -![SSE](./images/SSE(1).png) -![SSE](./images/SSE(1).png) - - - -### GraphQL Enhancement -Proposing to include graphql variable support and graphql introspection . - -Graphql variable(Issue no #576) :- - -UI Changes:Make a json editor pane as an additional tab in request pane for graphql with enviornment support. -Service changes: And then parse it along the body of request. -The approach was verified in custom endpoint and in https://rickandmortyapi.com/graphql . A partial implementation of this was done in https://github.com/foss42/apidash/pull/588 with a mistake in ui approach which would be rectified. -GraphQL Inspect Schema :- The approach is to use a prebuild introspection query that asks for all the contents necessary that developer would want to know and display the results in GraphQL SDL (Schema Definition Language) . The introspection query asks for all schema details mutation ,subscription, query schema types , directives . Later on we iterate through the received json making the GRAPHQL SDL. -The introspection query would be: -``` -{ - __schema { - queryType { - name - fields { - name - description - args { - name - description - type { - name - kind - } - } - type { - name - kind - } - } - } - mutationType { - name - fields { - name - description - args { - name - description - type { - name - kind - } - } - type { - name - kind - } - } - } - subscriptionType { - name - fields { - name - description - args { - name - description - type { - name - kind - } - } - type { - name - kind - } - } - } - types { - name - kind - description - fields { - name - description - type { - name - kind - } - } - } - directives { - name - description - locations - args { - name - description - type { - name - kind - } - } - } - } -} - -``` -Afterwards if possible i would like to make a package similar to json explorer to render the ui with collapse and expand feauture . This would require some time as SDL is quite different from the standard JSON. - - -### URL Encoded Multipart(Issue $337):- -Sending MultiPart through the body with x-www-form-urlencoded content type. - -Ui change: There would be a toggle button in multipart tab that can switch between multipart and urlencoded multipart . - -Request model change: An additional content type (application/x-www-form-urlencoded). By default the choice would be url encoded multipart content type and would only change to multipart if toggle button is used or if a file is uploaded and selected to send. - -Service level change:- We would make key value pairs as string . This is encoded and passes into the body along with added header . - -### File support(Issue #352):- -Sending Files through octect-stream content type. - -UI Changes: Make a combination of drag and droppable and select file ui as a new tab. Make a progression bar to show the conversion to bytes progress. -To acheive the droppable interface we can use desktop_drop(https://fluttergems.dev/packages/desktop_drop/)(supports windows,linux,macos,android,web).This works along with already existing file_selector.But it doesn't support IOS. -To support IOS we would have to use super_drag_and_drop(https://fluttergems.dev/packages/super_drag_and_drop/) . Currently I have only been able to test the feauture in windows ,android using desktop_drop. - -Model changes: Add another content type application/octet-stream . - -Service changes:We pick the file using file_selector. And adds the filepath into the body. Then stream the content to bytes whenever we need to send the file. Showing and handling error appropriately if file is not present or corrupted. - -Put Content-Type header as application/octet-stream and send the request. - -### Basic Authentication (Issue Number #610) -This would be a straightforward implementation. We take the input password and username and follow the below said transformation. - ``` -+--------------------------------------------------+ -| 1. Client combines them into one string | -| Format: username:password | -| Example: admin:pass123 | -+--------------------------------------------------+ - | - v -+--------------------------------------------------+ -| 2.Client encodes the string using Base64 | -| Result: YWRtaW46cGFzczEyMw== | -+--------------------------------------------------+ - | - v -+--------------------------------------------------+ -| 3. Client adds Authorization header | -| Format: Authorization: Basic | -| Example: Authorization: Basic YWRtaW46... | -+--------------------------------------------------+ - -``` -### API Key authentication. -UI:Upon selecting AuthType as APIKey there would be a text field for entering APIKey and a sliding button to select whther it should be send through header(X-API-Type) or -via query parameter. + +### API Testing Support -Then the transformation occurs depending on the selection. -### Bearer Token authentication: -UI: There would be a text field to enter the bearer token. -The value is added to the Authorization header in the format by the client: - Bearer +### Abstract +A brief summary of the problem and how you plan to tackle it. +### Detailed Description +- **Problem Statement:** [Explain the problem] +- **Proposed Solution:** [Your approach] +- **Technologies & Tools:** [List of technologies you plan to use] +- **Expected Outcomes:** [Deliverables and goals] ### Weekly Timeline -#### **Week 1:** -- Getting to know about the organization and what mentors wants to say about the work I am about to start. Fix the timings of meetings if there are any. Get to know which time do the maintainers be more active and share the approach and queries with them. -- Changing the design and architecture from feedback. -- Add the ui for graphql variables feauture. Change service layer to accomodate the change. - -#### **Week 2:** -- Make additional endpoints to test the graphql variable feauture and test it on them. Make related test files for this feauture. -- Work on graphql introspection query and on its transformation to GraphqlSDL. -- Add the ui for the inspect Schema. -- Work on the endpoints if needed and test files of this feauture. -- Work on the feauture and do improvements if the testing is causing issues. +#### **Week 1-2:** +- [Task 1] +- [Task 2] +- [Task 3] -#### **Week 3:** -- Start working on the initial ui. -- update settings providers for the new feautures of Web Socket (number of reconnection attemps, time interval between number of reconnection attempt,ping interval). -- Add service layer of web sockets(Like adding the functions to listen , catching the error and cancellation) -- Make ui changes to reflect the incoming and outgoing messages. +#### **Week 3-4:** +- [Task 1] +- [Task 2] +- [Task 3] -#### **Week 4:** -- Add Searching through the messages feauture. -- Work on handling the web socket history. -- Make endpoints to test the feauture. And add the ui and service level tests. -- Work on the feauture and do improvements if the testing is causing issues. - -#### **Week 5:** -- Start working on the initial ui and set update settings providers for the new feautures of SSE. -- Add service layer of SSE.(add functions to listen , handle error etc). -- Make ui for incoming frames. - -#### **Week 6:** -- Add Searching through the frames feauture. -- Work on handling the SSE history. -- Make custom endpoints to test the feauture if organization wants it. And add the ui and service level tests. -- Work on the feauture and do improvements if the testing is causing issues. - +#### **Week 5-6:** +- [Task 1] +- [Task 2] +- [Task 3] -#### **Week 7-8:** -- Make way for codegen feautures of sse and WebSocket. -- Work on UI changes for url encoded multipart and make service layer changes to accomadate that. -- Add testing for the ui changes. -- Make endpoints for this feauture if needed and test it upon it generating needed test files. +#### **Week 7-8:** +- [Task 1] +- [Task 2] +- [Task 3] +#### **Week 9-10:** +- [Task 1] +- [Task 2] +- [Task 3] -#### **Week 9** -- Work on UI changes of File Request and implement the service layer changes. -- Add test files to this After testing thoroughly on major file types coming in different file sizes. - -### **Week 10**: -- Make UI changes for Bearer Token. -- Make service level changes needed for it. -- Write test files for this feautre. -- Make UI changes for API Authentication -- Make service level changes for this and write needed test files. - - -#### **Week 11**: -- Make UI changes for Basic Authentication. -- Make service level changes needed for it. -- Write test files for this feautre. -- Final polishing done for any feauture is done if needed. - -#### **Week 12:** -- Adding needed docmentations. -- Submitting the final documentation and work. +#### **Week 11-12:** +- [Finalizing & Testing] +- [Documentation] +- [Submission] --- +This structure provides clear formatting and proper indentation in Markdown for easy readability. Let me know if you need any refinements! + From 5a561deecc14b1da3fe90dfbe6a19ccfddbe3116 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 03:20:42 +0530 Subject: [PATCH 009/188] Add files via upload --- .../2025/gsoc/images/Screenshot (1).png | Bin 0 -> 220545 bytes .../2025/gsoc/images/Screenshot (2).png | Bin 0 -> 299243 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/proposals/2025/gsoc/images/Screenshot (1).png create mode 100644 doc/proposals/2025/gsoc/images/Screenshot (2).png diff --git a/doc/proposals/2025/gsoc/images/Screenshot (1).png b/doc/proposals/2025/gsoc/images/Screenshot (1).png new file mode 100644 index 0000000000000000000000000000000000000000..04ed837efea4b270d7c1b0a041cbe83395a9795b GIT binary patch literal 220545 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

k>a`A_@C9-Cd(pw8nWS3A9c8#NSPhUbXh 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%0A&MwmVRNxgp3dBqZi+y=BIODqxzA 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

h_ImmeXE`jPD z=6;P}FTCPVxWWi*+?hug6JxfG?%pRT6k%PR2M~-aDXaIVAwY+9um(Kp^R1@5PoNP? zEePcjm4g~@6>o%7;D8d`cwiL;NdkR1?aUl2=D;Z3lGwFn%h^R zb)r?+Dbp{UUg12T33bpacQ3*BIi8YcqD755hF*QrA6E#jmbb_O`yE4lhhCnFolI@g zgbN!&MkhD>X&W*IjQ7ACmgBD_Z(S^~)k)d6#>cL9E8RPr}`ET9KGW zL%;sUpVaIWxY}UR84L4MfD%i3jb(1wj6fzkFyr-OTTPC~d(|W2gDxzS42{RFD!%_9 zI29e=+KYV3o{cLy(ftcHD!4^GY^|h^4BMolyJ#WAOV+mfUzLTw{P>`qO%K{T!(-(7 z>MV&7o4b+fjl@m1NA>qg(_};!VrU^{%mR?+AT)=3@PG-)fQ{6PwNW!*9I{${T)z~i zult&7diO@v#0}U@rsnwTsqdYzO&Ar+$uj9j?oj2Ki*XFW0k1c?b-DGENm(b)Sxfs3 zJE}#ZcVtn{CpfI0pPCU_GiD#eq8TN7mRm_jO_xttm@GU0tXysvTUCraS3o4DhwO== z5ek|6$6I?Ifd%e%!gHnv+lb=G{LV>CSz+DkU#xVg9sy@-R=z~PVB=y3s#gav)hF~z z+w0q-t~qVITK{q6is%+3#tcQDwWy{ILb{k=d|TiNu`(5I_orT|H2h(XzA0+ zPF&;CCIRNA@cne-gq>4nzD}_7uZrr69_eGEnc;BFpyRJ)>SJvba~=?Wg=jzT{|Nh; zy?_|_s2>-82SXhjDvf)&pxpiZZ7ISfHa|h-LNK{LUOWOlreh@$2>KU|Lpyyg3;1ASNOCLd3UrJ zeFMIgG5YJ)5SseDqF_4P;_6k;sS9{g@keaF(sbOoQkNLtDtS2G_}jqiN$UyJud!XM zZ~T338QFO*F2lJC-=mfV3v`vZt?jHVYUlecmiU`l(`Qbowu5mjX6$>pXD?Z`29^Ay zgp_PERHa=kZ3XPBCGM)y?sX_0ylbegRRoy;@-SMR9#;@H1I8ukNy zBIQ+uJ4$N&z}cF=-!AG;+O=ckZy=)9YafD34c24<94=<@xovS2W@N`9r%Z0LO_#UF z7ThLQ?=F3+iM(YzwH|YI&Bq-Ogo|m_0b@Z)RfxrN|gIJiaoXbEwnIQoYM)-?ea~p`eA#Jpgxp z>h#RI>zC%1E5R);BNBG=_b8JI!uIEm`U^`Fu1)SjTK&dv{004vf)hiaAPt_LYnwq~ z3cY=f?yK(g3vhi&>)YzqV|E30+++i$bWUW7-|*RxbV?=Ty$68Qy%5du3ocOoW*jYrI*c&o@)lov#UhKCX(L-7!~34-$NXrz zBL*WIR)iZ!JKXl!|ZvUPADWkCNE|>sAI#&dse=8nUKjS zMZ6oBKo5$WgGL!!LC2X~&BiXAZ;3Sb(>JqD^!TWeUk0L#ue_+^FNXJ1iOLq1~Xy@-Y>d&~y8>FY1Y(2}VAYcr?4kl_vitB679&p;xY5B|L>CG8yO zC}&l65v_d%os)jf9d+?@_Nl`7&_WBcb|? zx2bZlJNi_8SZDn_N$Qf!2zfUBB|qxvc0s4058$0y<@1B5TBV4+)hg^Xw_YT5_)Dpw zNo(8t>Uy6*%#$GhH*@r*k}h96i@to=dccObEWW;Il!UJ*#$1e z&i_qs=`Ruuvk+1+A=WCvx+6~`n@28s^ai%|4i;H{)|!#*f4=wVFLf$KEk>6VznZ91HOzKIPq) zn$2-B+l|dcSzuVhdPW}>gt|deUM9DWq2F91Cs3+HUF|hX?xKgMUMzsyQ}_0Nbba)b ze46{o4+6-|@TiLgovfw`UZLsxQ8+#vF#G8~{yn1Z>ko>96<3x$o@$DECI*cJ@r%P( zvN}HGj;DWjP=_$rUD|=>unhksJ)M4V(E@n)VZo|T{3zsd@P=CgW4;gj!=Jurmlvit z#!aAU?cm|%q$=z1oVxSF__{A+m;Fw)ZR1yIquW#t;iS2;4i}=T-&qWc)<$1F5 z_fs|Pa*7wyZ zN9Fb*+c!?u4qR}|pPX!I-CSU;+;|?@D*elZxlGgTuh(7>Mth&N9TRljCC80cuO=_v z8Lvn@mCiT2LzEA)$)c%ju_ICUCbhS;BI9m0>HXW1C2VuEb8S;sZL$VdD>tUH4mf-@ z&Ze{_L7MVKwLk)5RC|nKZ|W?6Wy!wh+iH4K1`$wb8n<%P05@TnZ7vlhNS&5qdM%>D z7_SruOJ2Lof~+6YNTboGWD!Am(JQiu+`MSeBYuUBl#{Ss(GHY7xTQ{CHjhlwC>Gzv zHhtAbcUNQZiuwYN&3%U7EW)dEypNZKEePX^Td|w z75dTI0ILs#13w7fZ2w`*9ouo=;TQzrQ7xsKk+<2r{vXH=wQco z1i+~+RR;*~p-cbBcP9?mrD(=o6YU>7lz9H6@&a5%IpZeF@PCu`=5a}G?Z3Z9#crEY z`RvS0u-av1O6hhMu-xU|yVA;CY7SIR70weX3fg3C$I2mdpt7<-#aR&*%~AtJO%q4Z z6i`u6QIPr2^L)?moY(7|-+BFp|NP5)-HUr&*IMhkKJO2BWD{ramvbAW`qAOmEN|x{ zm_=NSuD)Jbta-guJyX-jPgGvW`bfZ5#48raP*R;YgH@xqzw_XOFYxt({Q-dZ3$kxW z`L}z?WcK5BBS1{Hnx#H6CU%o1gdhLGW8%HQqtiQ7RsQE)yJe$Y&=Tr5d6Zq>B7Af5 zAmd7SpYuV6jXgrx?T8LBxC$wc7&GItTHa{#Mkj^psx5SN^72ZJwnm;tuWDPVi_#RR z!LLH(l#g*YEwv^Cu!QU|+O+r0c90{QSp{q=v_*|}?rONPxWlIF%Dh3uV~wbQ-<217 z5vs23ApOYet2Z5c#WO5@<>X@K;QOCR|mg$b!fwfrY)bZHs_XV1=ro{VRCZ7R~cq z3OFa-=2QA#Z!2?9C+xAbcvN?Jr+gt4tj3(uLa$dADqp%B3E+ z&M~r|;`sI6Mqu91%fhterl>AjF6bZ9(`TLS-_MxHe8pt6-D_+!S3YsMyuMnUKC@db zf_R#9ufCcD?~^d($xM?cYr?5>80xjR?DRCkQKQRcP~e_~lB#fH(9!jeX3kMMIiW@C z(RYKq@tg>=0oHqJ3(_e;MhiCaZ-8?k6U5p!FTFhzQCC7@dhS;(KUBJPq&HkDeBPn* zNh{&fRYVlJ02J?di|}Z!k?3N6yCp>gHcn3VZ%if^?{Ch)vZq2a_=i5Ydb+b>4ZO28zhdHUmM zb(To>b#EbW!9EZ7(<_9HpzU=uZ}0^BSDpECP(lLC5XvyS%(Jo`l{QSP;g}+awMM&u z8?MCbpWT{^nx(Cm`H*xn@p$1bt10LIr$c()b{ z3J*iaOxMf{r8ed%t&Mi#$Nllt>3%mDTA6R> zZ!X$IDjEX>ocDmbD#{ z6>pc%_HBl+q=?ocSZZ!bp<9<{j8I+p#SQfoxTG$fb=^61h7_iT5MatuBfz4+^srWq z(j3_2;<)<&yrcCLD!1>%&_!V06VJD{`)yDPcbSMxVyu-q(^xST>CKP~)kmAhJdGQ?;AlMfCdbTOuY7C4>q7O`SoA(nV(f#I46E;D z*`UPA+m zF0Y?UOx2VfQpsfwtXn2l?uP=wqbp7^S79fhoXI`sMa2;Lthu-6qe-v!|4bCBCKsjF zjj}7KZgiDfOMY}OiuxB-Nkx=fI$=czg@~wBu@(Y18XF5tQ0vFK}vk9f=c=ssuqX0-Eg$gN*HgA2k(%K}pW&>NSZ;JNCk zFvheKaBSBVw!da(lqq-@sQXUCPH&xsIR}5_Sc^T$B4&dJ`{(JIp&O6P%f9g*Mq5?n zRxYIiMtZ@cHGsv|hE;%1fjY6-fApS6E%8YT!tZ*QpXN{(csLPm6)ot`o>H^sCT1no ze$6poNP&cxQQP7ki`+4ay?sN#(bQ8R^2OSn^2nf2(cP$+kS~>bB?Zx3-ThxTqj8zi zZ=3Q$3L-KyAXPa;cYtqUDzF0}c&^f(ENXk&_GzV_K^*JQ%^X9$@rnVdSrOM=R5Lwv zHEkhDbM}spCB#ctE3;bb4_!B-prLhEJ=TJu?iq3COd$nc^;2>G&}e!ryoUz5ws26K93$n*)Fs0Gp97-c0l$Y3YR^L;5I#emQm)A>s$eoa z!WTLYm%zB~r$#4N)w-lSQb_ENDw7jJzIrdCcqenc!?Mx&?m;aV&2Ho1RT+^gbj z0;ZE<`&3R?A_yw&UcjC%tg!Em{tXqRp%>V?PhNI3M_?qdOpHBchx*j9dy+^iKtlG| z9+zZCOt=FJLO;o=H|rY&GKBIn(=&}9a{xSV(szS@0xEdE9}U9vbHU5!8>$88V;bHJ zO5Z&rNAk!yP^>I_!Y)%Za!xE)-XU1MI$O$zt$uLd`1f_t+dJOeL59)6%Tc)f;jb3n zvkSj@->fub~k;*kZHRUV@mVYIPFH(o8Ka zF3Vl~#=y9DdsP7lx05PvF^4s`B zRo5di?$#}3XH!FrecFfXa(nQ^SMlt_$fFS?jDXd>P+v_f+GydkqvK0i8^Uzlt;>Cs zc#-_sIJkd~*m=@6G+_?d5fieno_^LA4_kiqu5zW~=FDr%2Ds&KE=4v?AL4QGug*dv z6tPPYzH6{1Y@zKoX?*!xbeK3-lE6yoK28?RsQYqTPf*lGu=`Ld2=a^Ivf_5udOJNQ zzzLLn=S|%4<^=|hYZBSC^N62ZQnhl~FlSlYsT=`X@JJi*3+B{Q#_M{&VHRYnTveQ! zUJ>F;ckf<2<_b^vayC{nR-X$Aoa^axM}-zC3!3piKAV}?CqBvk^hJ+oB^x#G|IIx* z7&`4hqI}6{YLEX6euEYLuo=$*HWG#C@@dK+rYG$Lw{+O_RTT$X+-Tn((Jzo-}=7PW|) zc|AYL#%p~#(|Ji7`GHf{Olse%5D|B^$Z$|1Qd|%HV1sj!cM1f``2^RjfHE+&p2am) zTtQs}{_?(3YP;;ntU6l|oopI6d1CDuJd)YHdhgD~K?MpP6Xq*NuHPjF9+RM@miE$8hRFdUmUAz25`p zjY8ZT1h#glM?%yDb{}C=Q$woGE|RkeOFb8VSyGviP>RWvw*ui61I^Ky_$!n<7V|%2 zD0AwgvI1j48j2s)b*1YEO-j_9njD$v)?(=u%gQb+T5c^;osK^E!E@~>&NsbRld^$4%QM~4HgGy%RRomNV{|i=iA2>_$ctP|d<#tB(Nd?>!x|uzE505xpO-mT%48o^rO7{_})e zruCo$r|1v;Dm)k1anyCdO0d^I(Z@_@V$>e-X`CohvpvQer6G)~uD8X2&pOK}M;jmwWj{4Bc2&00~v}y!cVjf|ffVgm+t6Zn4F-BqFx#VS7N+o(6w4dWKh^C*}|9mDVR8nvZ1rv*(+y963z47#LXC zA0J$ld4?aN9J2Sm@m}-UREWC6+A%@*T7?IrxRlA=sbO}^mcHp&hDg22BGQi-J?&NB zCBRMg2jnCO$39$aj+O>5EX$3UI?Z3PdQ`?5y14ekL2B1RcGT=*RQvo{4L+~X(2se7 zR7&QV|1IS#bU&ay8$ovwo3%zGjCFy)A@E2=h%Q1{daR`^&rjxN{xU8D6C+F`;Im$@fj5LY7k{I0^q}Oxl)k?mBhpJrXj%oN}p>=O=)T$=IDBk_wovru8v=B{mm_R2i&l~g*YQm5o> zcDU%>l<5@_5#6!#CU-SZ%y}`vdb}(#$6(V{GVMayW!8_K1pHZ)N)K|N=H1st?pE#W z2bKhCAu`~<*25Fj#=-Uu(bE^-idioQEVDE8t6PdT94sZC&qYcv!E3I+ES22DknZGm}t1&4FJDiXGM#eOMD zuk(bB^|K3I{2Ha0aZ7M?f#~4@5Kg;P{YEm9E>adcGG4@svK+IFlkpeI?N)`XG4fKf zU%1l824MiK&>?|rRHe?<1nSH`-vm1Kh_dnKveMD8Q%L59OXr{H=C#A0kMaNNp4{`v zrusiC(H*oWW-@7_h3C%@7$d_VEn&^=bpe&jlHbqU{Z4rf424G=W8kh};x zGEu#ixmdZ@)Ld2C*!zS+anU-WUBCN?wjdzz`^FpPhOW}(63yKGS}zqV90&=+$oG3x z;qb{}L7n^Ckc#+{&pn$TJ)P%(X!x^N8NcT|osCVbacSrNl^|*(PCyrFn@nkvpdh~! zpy&Du!MXtPJ1mm-aB#1bh`rh!Zk}caxxU%TLIso2RJj%uVn7vu>2i^n5S3V)nhRi0 z_WUh#PcwusKh%dUTmu;e6vg_x*O!Yh<`uw-jDomrwc%-r@ZlIM^ytl!Yz$MX%I3(3 zQP1x$G@wlt<6Ljiym!2yI{+T>Vy6n&wfpOv!q};Dj~TJ^)d7<&3yJdh5vuoPC%ffO zyk5SV=$`XI*xaipjce*EZXobx3CQfF3o{`!*!MY%LunWfxv@6?Z2};ePPZ+`l$0?C zthinAg!ayMtMckvRxD_AKVLmjGgEaV#Cb8EOO~;C`s1S;MLQ>__6Z$8u$p{ppWnS5 zrE2s)wLZ1SgPm5!AG+~wwgd={GuI!x*;)@4cc@NmKuV~*JNG#dM33#Fkm|&Lto{&* zO(=HYD2GcTK?%_2)vxKCql4dD&SXCMee2BDZkkQn8r**9z7|7O)khZqmE!|VH|`3= z%Ne*P%%LEyE&<+r@}{Mu?bU;#(cwYcf75o(KAAM>{ur39cUIjuMRno`E;6;_wmY{> z1E>jLad%btDS2DDVx=9={H{H^3%El8II6U2w@orEVN-?{ZK(+P7gh6z5tR3NP)%we zb=Y&&%+p6>LFU6M_aZf>?5hv}Ea1U=ykYG|8yfy-q9j8%l2^}=2b&YdVlbRI@$3zF z%T$wq=CS_Ajc~C8*KSR;$fW&qRx@(`hW7A?5B>L2Z0zW$8)2`_-Ck7fc+^UAd8zt? zZ(1|un_>{ZJ}}eK@&ox)8HOSrSZWzWxXEO))%Hd8$?}1y`r2`5gvTyW@5lr5PCK7! zkf$}~fGNUnz5&$geG@QT^(NwW^u(9i0I!Hlj9S5mC$BZgrD!X#@!yM?kC}16Fa=b^MnGA7TImbY>%dSfL|m&{~K0- zjX5wqmQ0>)0>lVXz(YMxQK6T8<9YWik|tp6Y5bUKY@Zsj3*HWqKOuS6QEA!8F)@x~ z%N&uze!)7ePqJdWaD3;zkiGee=kz?fU%zX#1SGpIt=-T=*r8Cclxr$=; z7Hm*whe5&-8!NrFf6Ohh*FKmQl#X??UhZ?j)O~b#_o52R%x0C7e5AhORv_+ti)`(ksjbrKD&vC1-{Pe|7QJxRk6Pr}=&Xodaf*2SGk0bW?XAA4 z){G?WLPxTbeh?GOZ=Ua|x^wP*ju?7q;?|L6`&%c5z{lCIkYgw9>QF=0tLjYR7qT(~ zf{|9uZ6O@>C}+5vC?sgg!ubh7|MFkO6W$=rOYc3}92n2s@2fKWF*A4E-f^$fu5`kk zdbdlbT`%LjdS|;-^`@%k(p{`+eEsE7@5|{y1PjKdrsmX)g+s;dBnlhqc>%uG5MrDM zF|>E!+`mDYx#POYW#@_g_KbWU#kH{wck%ymC^X{KX#Hd*o@ysg}vRfx|& zidXW^j?|^VjQsGm?X1=Pj#OBP`9-Z=Fv3|JhvnzX3AmLobgr1EoXEw$n;ThaadyrP zP)_rze9$2wPxtqdTWO``hea2P>MVv(ZbN)zv|NG^v4@FesojB4;KXv431{@Yv+u}n zHU7&~c1GgJge}z)A0{%56`!Ju<#@DOwbrEhjxMZ{Jk!lI$^}PvGzDx2;&=#3BcYkF zb&+x9e#b$|FOCyso^eaMYh2lI{3r9{cD7!ZLx^wptgNB4i5R=`1(jz+Y_EAGdsBc( z_on2;h^o!-%Fh{zL%iAxI(dWwh#72NY`GPa9M=^Z0JtgQ-Zw3%y?VyvzGZ6mB@m*t6lv>SBvDW) zTXYtmi4sSBd~41Yge6xA>y;{v3~zF!GUQw?)YHnvWTG49MM=`TPhAFPWSM2`K8(rk z%_j26@3#g?uP@pvw^h2nZXfSE8{6CIPmMq}mn@VsPY-LedGUe>Q-SIP2qwQHD;?-j ziX`cYrZBbGC8_eI=2hx5+LqBMLwZqE&l;nR7j^IV(H@uk0SU4D1e3a*bR3-a)H%QE z@@>L3h+-+3lz}?PxsO$HrcC*}o0xK5{Zj2H(ay*^-=1^rK<~D7+qM?KaD2cCAQ9`6 zwLIdY*J}`?XtmvGRzzOIEPvL4HUXYG-|HHU|M@To<67=H0&8E|t)S!Nt|FQr79!#o z{kXQ|39Xoq{#ZB{rC44j9RekejFoxRM>E^k8NN`fuz1Au6qM;8PI$~Y_{p8GB9ei} z1-GL@NBnZ5Ai0b3kDWL{=u@<_QdRTExZ-tc1b^z;ts36Pwmu6&a6*j-iy?G?-Rud_ z{6Yv;kZ;?(srPlvA6*f@N@I~~nAe(CDWOjGrGmtn(du#{!*;m2Jo67yEM=(_>g)@) zn~t%x<(G;I=h&2oRJ2+>2alMeX#6H*k`?0~qAX5WK?KdI2!|4w{R?L~mVxsx;;O{5 z)}*K!hYqm`2JWxFw02vnv2^U|ZsEq6pH$HwHm^|ok)$H!XTWMAqrpu*QqROthtyv> zIQLHuT|`y~yTpD(HkAdA*SN|LG%K!OXpdc2X_gt3+S78pvX!Fh!L$va{Nn1IBWMw_ z!l&p6k2+++pTzl>&>VeNY{?MM>t7td9IW2qim-^~^L3jD29p{`2tR$F-q8@NW(h*3 zBII+<(9$ifi}y-1;uY8mSd+T=G=i6bC{4w~fH^Ba=u*oEp^T-9{dk)h+ zL8#fEqGAEtRM}K7ZPK;MweHvu&|d^kaz=zUkA8((?)1_k=B^&|CR=}8pBYWN=TU-3 zzSV0lYS5Z&Q5Vs4Ba|Dfq^~Ao4hL)~ZEKNqp9-cQq6d_Q`up5lUfr;0H~*2CH2$n( zAi1HY+ELh76qTL#x!KetsYjA%H!}`Ogh}5z2u)qB6NU$doOcQX5_kz?z`};6kP1{g zp8NQJ9pw*Pz+l=nXHQ@#GZDi{CE_-WBzz5S$}&?Qx{6F-@2f{;am_AURabtDiA#zG zM@@~}2cMgsbMPDuDHY)Ombf~MT6eF`tD~XV`9!)$jda8T^STFVT_3;f48EM6Fi}EX z8f}ilem}B(p!v-3dPSQdRCZ-VW+-~M@>Oac;NvaoYqs3k8hh_3B>OHT20s%O#!MVv zFTEMkAkB{UePoT3b>ck1syZB+@R$vZz?hvW!eN)#0s4&XvJYg*2l~^`&$f}btx&bd zsZ}k;9(7rKtcb+AkP-K{&Y6VFh*yF7J~Nq^k@DWAyUQTldOImY-3Pqb@v$r-sP=z; zcW+m$|Ei96q^NJSiPsd%5%7I`*(4?_!RnN~Xjnu zg6BdSj+r3xSQootpmid(rx^z$LUsy*Wdaq{#g4R49n6oza&?b5_`NTmR*0AF@lwRB(a+%P%5K3IVrP%wD)_P}BLZt|Mn8y4#FMh7506{n- z@hqvsRv8R&Sul(O!*hex4w-|$(zq0Tydmp=wJt%EL8fg7wKV+G1+y|!8NXZ1cFi-| zTcz|pKI$j{7c1|j3h(gGo5*Q?=GRIwOSZq6UYVYUojs}~jG5i_o+d?tH}pE?nAUzs zbvm0<+gW;K|8#_3ye~$Si2gjwmS9-vJfG*{SiDuIhqbRUR^(u#~)uaUC7$agPFT{HwC;bI1fF~ z|JCGrKu@gr)7eWEkALgB`GKpq7Cc#LO@3Zug4tNl%*k0ZG`nk0V|zXO7&StA7Hi9E)#+|-l;Uggd4x-QlENdseBBcgNp-a{Yw zYvKF}+viSussX)ub+zfb4@`vF6|L(uN#0vSLD5G&x(=as%k9RGj8}h({yM18tQZ7nU*U;<+um6E+O40 zm$6V|8et5=bsj`#184|RV8e{S@5ZfwJnRxa2Ub6c3dNPiVfoY5g$G{n09Sio+#vXn z`O5QfHE(jT7f~frXEvv>R6S=QBHCfN)-(RJY}yd--`Tr$u(>BFc;H)|mU8Jqh8rIk z_fne=n8eAXf|E+d*Gu>-3C;7FO~TU<`Rf;*8X z9jc~|(?Tu>Xg{{H-lBE7Z>*hzaqMdg;L;GmOCIxz351;r%cnzn}nyz zC3G?arYegzT7ME$q&ri-Z&J5gW$6FFIEq@A!b z!)w?<)UtTTspo&Bmq>G&Uh77#+N^uxuCAoO+s1lUM-A{X(|xtY%&8&}6coQYK#h^> z79g`uqw*L``GLAl?HjVgZ#AG3?k1#g=Ml4>54$kVfB(OXlohTv5KNL-Bl&_cRg^G>8XKeFy61rJhYngA#_1~u*dOW`go1sS@w-; z3;Ks2UYZkjRiV+Xmr0I35^iZyZ{ zSDpQm+zPBb-MY59gIcz?1Y(`XUyE3zghu*aGp28<+PU`dc!m|`dPH+57qIfsQp<~U z+#a@gxV=!F=Wd@{k^jWPstYW?MT(9$kdK-|UnCGcZ&iaRt$Vs)=1AReO%e#;Ongj#-{6tGh2yTQ`En#8}T?f?QD*h!4op#A&G^yeA4=-3w_B%)YsJ}%SX-$uJ zVDcUIR5uB03C4y6p(k~yq|2q(v`m+quIFVOm#GR7ee#c=KV*qdJ%aqx3`rtVzYI}T z2B%28pw}@Kws_N->wQY|KU4T|*JAgY>ZFeynYqLp${ffj=oU$c(SBACDNC##`al9# z6Bd%cjY&iNQkI*Z{DE`s{EXPQZW&biZUJz0a{Q&2^Qc;ZIfbw&3%P>xCGroG$C`c6 zZ=@u0yI;6}xgGn@lC0wNk&+MSgr%91gcoONWk*ViYF^rXZ?l9l*V#iW&LND8>w2wC z+A$B1m+$*ilucqwD%*(M-(!zUJle@-7y;XJU;D z7G6e*wmr?F93B|Cf2#))U!3Z-^ySvufa%F6RLXvl5h~;997Da^VY2cXSP`tkDUc`| z#feHD#7 zWyGAnTt9zZQizN6O`>gG-)J*K_qg7@UrVpW$NxU%j3_l5P`brjwaV*j^Hsrc_%h;&3+oqKbk4heM&qvpXVjfL3oo{&Ml7zY3A07kZ&UH3 z(tN^)g8@`NSy>e>K}SbJo$y}d$^~Tr?vjb! zpOw3qvsu}$N=%+)2%pkd-z)Qkh1Z?8JQyyi9+O%{?7G1L@FHXj#csWCHd zz28EVh`Y;@LTtL$UNvC`uCYFn?1&5AweJRUpEIh92T0U~bYs8h@MTO$O8*>;RRJXv z&&np7Yyx}UVaQT-=pmPSPsNYQBu%_y)=o_rq8pxreacscDmE`2E-AbKSI#*{hRL>e ziGmad_qN`rwR?e2+Q;*2bC$f?7E`}Z-T@hBU_QD~$g+0kFAVj-K#Zc2=xqf@Rqgzq zCXlqIsn2T%EP8bCx_Utoey1jGGZ7r%T2qiw``w|0G434hVqHPFXr;8CcLYgF)ya;W zqpOANBaR*SVcitv9r|FQ49J5idr=83j%%`4-_rGQZKx7zKBAArPc*Y(*7g{ybW>-7 zgeP>L0*EDSBZq~<$1OjLUFrTo<*xtnCJv1o-^EmBOT`m_NdM}b|1$ga^GD+!Z0hjU z_fgRbJ*+urd||Dghy^k5e_pLNRTVkh4elwDPfr~SV6pWJXS`P)nroeI0*GFU9eTz zE#&2PjjEilBly`#)|r@FEv7TOu|qTj2qzzV-(>%I_opci_PZznmpe0HyzY{Z69(Yw zLJ^O9ZOzoh@x9d{G&-#tz+}oNn2X+STC;6>%Q0$Fifc=_ilzV=pgOK7RQdU)XwILn zy1b?Yzy*1{Q?r9Jn{s49ehPhn8>HcK;1+L#De~T$Nw3zHyiD`kc1pW{&Fp_qgnH!{ zU}v7^|BrwlzYlc*zf16-+R%coQ4jx5Lem2b4iuS=>rNJJirhl7zNuO~{e(JHC-5u5 zSg>O>m-^a5`h4pOUzb#}9$R89+K0TeF8Ylg>Qbn@roW|NvzqqdLruq<{xpM6%f726 znY^L+Xrm}$hWJakEd+-ps_eYJRm{YDUUCg@Kbif`KQ{$eQ9M*|SdX5b(>#vCB_=wwl`aHqL%YN*EM8i!)wp|H6_KV<5kl8b0X7-rhyt<__FP^A;K)^^45+ zz5r?2{g!BRI3nb0MvQO&(82q5Uv|m2C1i~?$c=3d?h^A|K{tBc64TGx6%;YFNB7cwytrzpn!4ZThuVfZ)B`{L!1N=erymNXH9t; z-D7Y2N~`aY7rnMnsriB1E00q8$F7g{I2C@DPD^f^VTNvMvo3&O4MYByTR>~P4?Mn_ zGyQwSe!qzhhsO12e|DHLt&slb%;*y8PNTQLTE8*5&g{3jFZxsM_d|+obXA3nn1r(N z{&?AKn&M);C}Vr~)Yt$g8{ zhpJecH$ zmx>AtO{&*Qdk2|&OG>bIOy%C7vaLz_AglS^cllyYYLmUHTn>oa8VZ~X;@)9JLRNU(pzR$FY3&s-{P$X_Q*T_7E?srJa_ah zU9T^Qz#+kJI1BF0`PH~nl$)Ka{&(FnP#eM%Vb{`+mv5SIlczLE-t~(29h^r(77k%3 zmmkNA-Q+p;?b)4d5PJd=T-YJl}bpz?~xIeHx1J$U*zDP zYpoyp3#?da&BeE^>!#q|vRzm$E7^od{!eV@^kwwY<5_&vmB=DSuEM0eu%DZaFuDSr z#y-U>leOG0J;5N8MXBA{)fw(Cd z6wvL^%l#GD@pDA#iOsX_Ixy>sj5u!>!XvB|kwIbrw{W+7uf`j&->3lO8-`w78>bwRE!D|0Wb1w*?)EaqBGEf1>HhW8hm|k!pjuxDABC zO9o3+jGlhNj{UNXg;dm)JuHI(N}BS?$9;;MYb=D zfMFPQ1dMCKl=Eu(c`;V7xr|c%aM)0-R>f)5nODL3fkAs(Pp2L`?ZMvX)64y&0g7+G z+q@(Q#3&-c<5$fA#Qf{a%!f8B*7W&yQ62*93af zF=|aW7;_>dVDIILqQ}Ic0rpnO-2Q9Tb$zFuyq#k|x>;39`m@)@dZC}#JAiY& zo(&cctzKx2I7%+`;`9t_d<1c$hSPfkJ_maR&C+IMYIz$YhdeEtA>a-si2yu zi*JfVssCR_r)r|91ieB;OpX5f{jQBKWG-M?CU}11&2%j~YAWYUO7!(O>s36Xwz!%; z=zW&eb<>3}X+;MvN;uDwL?scXwoMa0heRFm5wCXUg*fd?(7?1${xX7`MLmit&|ac? zZm>lGKKa4mo1ewcPgWBoRHR~s2eB5f{|Z-RW?b4(0AO6}{t!f7x(AKKDh_HxGX4Nh zPFE<{A&NICQ`w(CnQ1`Qx}nOM1eL1LbLCdFJxDGqJG`=LM!H)zfeY%!(_M;=?Q}Y- z`1Lsxt3pP&BGcpSRW^%5)lrPTWx>^$f2qLSkKHZZ zW#GWZ;6C;wVaSmnA5zK7P+E^2Ug@}a3i;`1UlSrC)uUqVMsRdvv8iRRQInQ6&O7<{ zaw_Vl2dX#bKt4!|jp z2~&pDy^#2uBdg85Q7F>p<%}~Zi>xZF%emd$!2K>yScw9Jo0jA&IlHpZH9D$vY>jjM zrHMl~ANGw$wwRw;%Q8&8r~LuaoW_FOS9J}BL?bxyb7zqAippn*yr~w&+fc2!H!?A9 zJ@KK0l^1b_aIqz>^@g?dX|130Ij`7`JU`LWKP#X4PT7U=pmIH$`!uerS^4c*b_KE) zRTux=W57?;hh1yZ_tcMqaNVcDoTeyI{I`&(8$J1W9RrMF!hvvjT5Z+$wHuMn>yO_(lRXIyoTvU;3qwcz*)ub zpypmXK{s!IDpMh>N8@;#&p#pdYzmF+g3B0R?wn>H0PX^!X<5rjr2~{fLDBZ07FC#N z6vdMN7^3`NQG)27DZkfJlT_MXKsy;v`|JppUVTD#lo^p%j3!cY8~p7aa71B@Fe+(p z<T!(Y(hZ_`sx#|OI+6QPwuRmqD32-E;XMoY8d3|g`y;=Sw>|oySZzG! zdl$VE?9#q#v5U6x3X)bRm<34eCGy+1p%@2u5KU)OM6Je($}9nVx5v$IIx%)IvHJpj zvo4pRzPMv~OVG^rl)&|d5SGquOS}fwni{4z^{6KIN7Q; z{vZ8ItHN6CZrQWnqVA-s3}a7`&Hfrr{o$SRogvMrJdsYHSbp= zf1M#AEv@KlaKsjUmjAf^Qxy-Y`%juvrxY2C|ZFUlwDogO)@xB ztJpCX-@U`5D|Bdh1f#TSSt>`aj(|(*1t+@Ml1a-Bk#gcm)cqH?K7?C3aU8s7s>kYc z@&sh>4hy6k`wa$1`g+AHR0aF6x!j27S!Q`~-DLI0uprnK#s2YIn=Dr#Tru5$C)BLu45ek{_CK2r?7kX=QeUvfef0jcI{j&XMk*aP z&GpXiN2hCM*W+tm1QOz72E17-`mY1Wyy*RBKA_5~4P58trF~RDyj(_Otrg{axZgj0 z+11vJK62#TH_RjFlMalUI#FWlNLx5l`Do0%Bx8^4arG>BQqq+#QdSs<{z)-0K`Ogl zDFKMkU~!jiuL&f!Q%ZGYC=@GG^@Uz@IErPFax#1wVfQ*vId1J0^1wR$;1_diGt}+> zS{X^djF}vk;v}_x-+p11X}*7P0K6fCaMVxE@cTT@3mlr2QHDOJEG^ONUO;2N{8 z0Yz9(gc2=3vObccQ@reSkip7_JdE>6VrV27sfbWKkp*GXF}|b-r+D?-@)}}-IA-?S z6`HmGCE|?h~y>|+0eqE^H0m0^XrFWwz0r@O2QT>z(L!@n1K;vVqe^7C9~ zu0nuNRtqZmvPCb*27%L6K{;QnvpT76Kol+lTNukCx7f~E6@rb)^SM(wl7hamVsage zJTpuk{gMF_4)a7`YPR`Hv9B7fP% z@POAFl9OsHtWma>AT6=kIoiVRhe#SaR92xL!W3JA=m(!*25 zSUo_6-jnF7(Cd=0?*r^-rGVbpv6xH?pNZm57%yz19j7Ryj(BNIhFpjs&yo)JDnA}M z>SSPQr*R0NbNy)JrfRKYxOS{#mMv`QCaVvi!UhVxFK7*;PL0$Azru8yK&HBFV@T+g z%k$ukbo}f(#E;8=sy|e^`!LEiP51!VmQCQifLdo0JX}zJrw7SLm&1p*pcmt8K@s~t z7^=rg+;baIzXK%(3AYnMp$5Kv!j*0B0pI%%pneJ*GOL4_I*+(T1%=vsMYg;py~$c; ztpQ8bDK=^&u-1}-y6zLv&JSN4tg0MVYm{zIy>KwyCe*(+Qs>;JUHgvwi zdHn#&fr05DC6{e|7+v)0camtSo0>O~TLTE1+1vi`Udh%KiFQgl#8>|+{I1t?5~7QU z$fU!sX>tVM3d@%6EQO0h_IQ`e zdfKUMCcG&yVPU&p=NZV*+NSao`ZWUU>ei0Y#7cQ)ET!W;>JC7(ASqIY#82x@bdHP* z>*~EO0u4Cw);pK5Vc*-zRkQvE1QE$V$|aDO7r1X_-UIuvWG8=ybz+6;Ev))?ha?sA zQM;%w;kR87<8#JvIOk92xQil<@l!fpe=Ag869kNT?CoSSS%@MJC4Bl##W$srJ%4Cz z?zzfV)bcWt!}01k{5{~O_`!Y3r8Y+wYea;yrU17dzOfPCY+D%By6CJwZ!E$5#wR3{ zs>dBa^XD;qwE;(AZz+?R;b4#m*t0x+9(aSY;wjg=>^R|XgJ@8K&Rg1rhuGDq%~P()s$vBg>SNq#jUs_rHF?z- zL)e!&R#b5tDXibfYkkf1w;oR_PKj^4O&%eZD6W%8D-*SJ-yW8JTaTPH~mxy)z2z* zFO)yd-p?wm_eKi4&*oH)V&Sh--qqi5*1Dd|(w_cAx2ZOpFx0Qx70Fw zVUfXoZc1Qac`Q)?GEBEwx(UCCm417@!1$K`4x&kibA?csd@Yj*pXLm`TCn+SvsyQ` zPnBvZQV;za(_bId*29}tr}+D@-?j0xR?WdjtN^I6ot#r8mW=`13a!)3A-Cvkg@KDt zUD1|Eq?J1P`nOOqewv=;wdOhav$-}BO^V*5@qs@h!FziPU-|B@q;{cocR@i+}pJ?gmY zi9GclV-W67nG(#fk@I6G%Y_|5B)|3PhBOI?P1>I=Oj7BL?!}&I0Ivc@JfS3S-0U z2K4-!;@s>2@gD9m3|j8XO(w;U4prmj3nm%BN}056F$5>Qm+|P$&R1vmL``=81qt|N zy<$7(=)=<7mKyFI7miB01ns-krcuANydh53@$;?vh_&_%f$UC`;?v7%p!gWz1V7&VcJehe;bsYGGsdu>ZTE)!wF(h+Q(j46X`E&4=RHq9 z4|^VJZ=O==l^txijqBZuGn`m%V@(2va^)6jxy_cTxoO-^$}#l?a*jDyLEBA6n_Sz*LW3OF^?QCYT4*N@N z<%uoq0B@Q1@Lg=f#s~NM%F@bvTMXnw$@RxG*S@7$(np2oD5K#C?yl)fK(5&Szw!2_ zVM(q3zrSWhZA;quEh`6Xx656ol;$jI+g;Xfu(BLcET@Vb2^CS$tgLU2nVN}ESy?&Y zkOL}WS!xboso;p3A_@vBA_5|3wSU7o*ZH3Z=REmemk)g4b-C7k-=Ftd>odIG+WPln z$@ms4P&Xra+XbyswR#LsuQw9>_(8c{oox`nltHERmyk^CD9A3`Vb20uQi z){@KCP5Snb$|rvbYQszmbq4>cH=g+FyvuK^Jjf`ZR`ebywTF6NwQC!m9028+EvaD> zsvFDOm`Cl9DuMGcRj~N~yuvx1F!`M&F9^HEf$3(!h9ie84kA0m0p3W*o0W)AR>jh@qL8xcu@25T93V=NAPh$4ov0?5=@lW$M&d_ z7bBrh@6iHQKZ0>#S9jJT(OY9LXC9&NiFcpy1`&vZDy#Pi_?W9vx^4aGoGDeFC1g2S z=%m7alwjS{khUxENFx`~YbgF?LqR|!zfKb9r_M%5Hu!H5d53@zp6+zx;y?`|u0)yV+n?AfnWjiDYFV|K8NTfln4n4`SXsST;t`B2{S*dEanwf9Y@V}#vid2Qm!j+NoQ z*Jph^S>J6Qji?{yDcYnb8dpG6)FjceUNq&iC+Mtp9vs2kkWapp|6#&n)8xXvYNntu zgwEVO44vBdX1RmkBOH8-(oF|~f)}+m&d!?@4hw4+seZVs**#u}zZJ5JD?QqsUrn^1 z5!}5RqrN+KoCsPyp9`-sK?F=zjM5Rh=;cg z|7nWVyet<9$N=S~%y&oeYuaKitJH=s=mzslZqd zMq~#kGV7V0nv;{sP3}PpxZloe$19Jm0mKq-Ae~^0&|AA1%CNW7d$m}vZPi;@+BTS* zXX~dzvj-|%N%)2#3(2XV2l6*IoYE{~V{%H@dlhr0=oe`{ls=$tF zw$HBcj5VZ-E+n`)L^*#i?qADv*}VD2mp~et`_EntEyq`s>fqTfQ>wx7yB4Vk)BmYm z4MP&3S)Nrt%K9eF&rd^4jeJ}kfj@8_is|&zb76~!lH2G9oG(MG6gA0lVr78y?bvno z*o=y?kH?^YjBOB1E@W`y?JHGQB??+C)A^r5Vg+hPTJ|h6Jln$-U4T_Ex8#F>B{%}{4_TZ|?AjwspgMSsy8H8Y zk<3&~qp5>>4)*YMpZ}a@Mrzc?sY;}wt1Y2VP;{%p2Eb!TaEoq`E*+X`^i>(DYZ@pH z9{t@flaC7V?>_f_-C-4sYJ$1Cen&aRrK)POl`X3d5T;P0WQ&54@{8sghzos~F(92i ze_@x#LRj>x1IfN5Zh^Sw{SLo<0b9X$+)}g5if+!Ob~|;*&_&suoo^`N{V2P*e($_) zQh0HWW#})+{l!^-5kxAtrUxgN+Z#Ij=sNxBsz=*@6Qyf@{*gGVT3oR8T8ULA_BjH` zbs?TYI~nfoJxd#6++d-8WJH8`o(QMQHai^kHAF1H5S#Sb-G%O32IJ!mFUA4HM# zMGu2BP``844`X`wo!m`hLGr4`SIQ<>#3Ls?t|vvD3|0hw0AEM$2MIP~Znza@SE-sj z!Or4df$3DBOKXgN7EW6Wi|g;&Av2{T+iiPmTt9i9{%i$qx?g%OgxuKypYy@{J0w@M z*-ox6U(v^^@UJJjBk7ZR({7P^ZJyhXoP>x8TQNZE0I~zbRlb2KwL_v9730R>+3SjY z$OY!ejB0?|n3LsVcq20+P)$;=%eT_{i5eJvHu` zh!o(>ES_M=3h)s#*RP6R!4>(3XS}ZErOm{y?TZ$MjsqW2S=`e$Ce*#lc0r4cOmgR* zWK~(hQQ2G87iTeBbvR{!^T`kmeta{fqtOcwEoQ|Dg;OA=xuJ0z+eK;E`H_r%orYMp4 z2^$v7{HtSU?(p7+9gx3tq$tx7%8dUAJuL@#RO6jdm9UdoZZIwt7?`u3dt@KvpMr@S z6e^qC1x~ELlY>kX&a;_`AzqEBNkw~UAJz@o#YGNR7o8dSTBv%Mo_);gen+1if-r7q z*6irL&mJXx04eI!V~zgOspKU0&hvktd^Si)kUPL51lYy5Nx>|^NWjgA3X!6AY*i5m z&|(t!YV9{r&TVI~Zo+n1RSeGb+v_evr*=GCe9y$mytcYH`K^--02nZ_=DHHcb7Cyefp%9~=7-?KLAOZZ?`lJ4KrjM{vsVJaLw z+dQ?e^kGpNHhQ3nQL>YFy4N>lopzl$g=zvtRbdtXxo`h1nTtBUNeyJNp$*vIXw$BO zw3vqZJ@dwhwGWyQQ726F6QaNot_g&7TEF@>w;J5ekY}XDzRD|vIZZDoz6BE=f;zx4 z-HHEratcQGM;pAs?rWR)XbORLsWaYTXz~$q55~e|?8Ho_Xf$!|xDIUgUcTq7Ch6~N zs6JP96zL;+w%DCy_d#yScaGz^aDJK}aZx}YxN6g*0gUQ;rQQ$pysX|wB}!v;P_yL^ z^|=dz5_3sAJa|bpxMZ>kwii0b6XZMGk4RI*JhfX%QHW^3GlMPfbc_R@8DF%uYFR`P zf&1jAu}{6$dfa0EuXLNcm>0+Cj&W_4|0T)|k@TxNijeKI{j%V{c~ifJW=5(kQ;ku_D&{LsC|35P7`(L9nA9#0$ShC;_@1)4bCuZErHYR~_Wz8v48 z&uz!NC56+GI=0;ADWXRYCZi;;a>jza*77t7^0LtMowk}P^7rnPD>Wc=`a*r-=Gm6? zoGKTKwCGL#haI92d(q2XGJ2Lm|0+@O-Js2kWLb;c2Z6vUQ-Nav2c5z*4K^b3(liV- z26V*5!8)dO_RLC<&i8D6mQL+m_I9yq1-ftVICZu`5wmYXGTc!<-V+*pJ7(R~5sfW$ zd}xT(5wQJV&Ow(fHm+lydahj*0_N#YL^*5rr|g9y3q$l4wzav(3mX6A&E|>o4dur_ z*~(u#rz7pc!&oQn>|z4*>27C?E_mOG-RR*xdI#U=#QA(Q)vBGC<d1od63XlwloSl$h>!=^O&g7Gzg>ae=d8WqXR*9U21R;Hf{-Ldo=NX+}O z8aj$OEi6V~rdjNuOTNozWbsKEl0ej~sp_0@L8yu;ehwj>?L9lNkO<>Eu7P`)g2%M2qQ5XBgE7G|8vsVRxHY zw|TcOt-R+oSvjK{$7N`L4rlaGL4CE6__UZe3&&rOwu}(H%pp?sfaq&zx0)LAN)8Bh zG)Yufuh{?Evx;k5aMZX z9d+7@`_ehvG*!pA6=I2Jza2rAPi{|Tg{nu{MHUkx-AC#>Mz6F_?x@;`DER9Bw@|V4 z9lI$)S6`C1J3nU^Ijt%6zoQsCzUI!*3p?d?2aX!s9&-l6@Bo4a2Su@Q)QknqGs!yI zb_(XnLIYooiJ#f)=8HjjqH!|)g9y_>dIRhXm8DzyY@3PN-4hy874AmbM`f5pY$@{{ z!Xy#5R+hZ9^NI0R$!eH2Rb^z8)A2;5M7f6ehY-l|Z4Nre%P^SD4a@(GUBhew{WPMB z`02kyG6C}c)ZiEP`l)I9g$7hijTQ73_l1rfFWg?xUtYkUAyQl=dj{%0FX-n*3Ge*F z#`+<3xdM&;s9vYlCKCAkvm|UKuf;zuAR(l<4*Qp`|7zzWVpu-2Yw%qjuoxygpZ7HH z`oERfRFG`gD~J>qDOtC34fb!X@0%u`E+~=0b7P08JZquL88_j`%z10=ySENkv>AuI zosNI-OYQU8g^S@j@C!@(QcaD?-&h+EJJPS7)OEOg_v2esZVetF0thHSOuG)G&XZ z3TfJVYwmEE-0?STd;S@SgZbz{)xa4If(`!}`7l9C8;yz^P_NyYj!s8)m0o>U$2?%p ze^87hRypJe_rwpo{~&0SFl}M+x6V;a!eC8`=WpuDYG9pi*|Wke>5`{bP42#1$6|iJ z+E@-akp!8OtP*embNz1}W9uQGRju0Kmaj%-pFHX7JHHs@rp;~{(EgMyvI&)SqDmV= zRP2v{+!JG%A2;?cs9D}E$=?uyKjrUCLCvVOP1)3&W4|V#g1kjp_e9qULQ0`nRo1!+ zjfm0#v%^&Rg^Uhf68N$YP4BYW>#~f3*t0&(3G(qc zzGnh8EvxrgjTildI0Kcpzh{R zkh`SAS0&q<76SB;U-=u84Nea5ufur0YCPs$$A10b)h|bf*V5aF5!~;f^!*v;F-!i9 z)d7dO#ZEA4PLgp?Vr`Gk|SW)jm2+2~EeU&%wfw164|PObgG)5k*ps zoge$*Cei~ZfzM^_ODgQ>BSFZL6*Q*+!uAYV`?1C@!W&BUSH{@EoTfFSN2U3wI&fG( zy5!%$BIeminyS!JyY#sS&rAaXW8R!S-_?u7)#>EEMLA!#GbUA{WtXksR=53QD|GDc z*bYcrib8MRtSc>H=h^BAe9StaKjJKhm8)vxeW-Pc_Q4UYpJzFwYYOb*eoW5sx zPCT1=XL3~X{5u785(twsI?uD0%}0H71v=2mOc(FY*~=rNdsttymeU}Im2>S;Phh4s z*`v6H_QgbokI#9yx16P1$Yy*#49Pd*5^do)9>uU-0^?t|YM59xshohJf^NP^-onD$ z_lmp>-eOH%s&7F#l{HJtpLY{dVjMi$bG_BDMTlMMK*1ERQ&Cn5-E2md*R`QvYy?u% zsgeiMx3pdX+g_D=c2zx- zhc#1B=2LxBvUt(8iMlCSx{$vLp(?_1?Jf(`X6h{N*_a#Bmf_>Y^%<3|0 zMs2)H>nKqq3UL@kG1cS_ul%E$EJL@Avk77L4W%=c|9`5HNaKuc_X%@^hb7dU_qe0z z9wz6_>;|}cn{`|7829MfSk$ds#g+JXW5?M;KtEz>seCRO;Bn1$G0O5RLIn>PH#n&4 z(q_LOguI%VM(EiP%bJda*&HCij-Nw+)M*-sz^CsIx}UyRcoY?14oH;v)TdZKz}km? z%kH0`DZiMs5^4WH4QQ6c_baYijD3XCT;ikmAF-m%tJAu_xFg&BrALG!lN80P+JhxbKBl$mNs4FTmm?pu}+>9xcj|Di|nv7p> z^>A;tJzH{iOd`tNJGG8}#)vm1i;lv)-d@^^^bCKs6 zbT?Y7wy3o3y+~9&^mBr2gfro{u~dX+d9$P*hOB{ACwtkqk*cYz!QxD78ZdEcW3L?! zv^Y=g1u&+q5KLU9%pR|6swL#mPZ785hjA+n_l9t>@qbODEc>1%dHsylVF<6&Z?NZaWl{UW@7CtD9CnUOp2KC9iO)lI=1?RhIe?~ByG(UT=_Vll}ZezD^W~q39+#%CyR!M7s$<0h5 zas8lGGMa?SPCnrpW$Z`wnO^#8i_Ie`2nU1iYqJ)M^C|rV&;@OOTL;&qq00Py$Y-bq zk?>Yqi;czaZIy+Qx3Pncd9RET&TkE>E<}QsyFrr_3z>YT_smGFyw0f?6~_xxG{weI z5WBgGyCL`o@K^!Gyqo8s{Z~FPn+o}90sx|Cm~~-Z5tV`{92yg$HyK&6Jb${J16luxn&uUtr@6pjE`-p#v7qv|V3mf&<%{1nF)ZbZ%Z5|2M*YY(n*jGF6RIy}ojRmD^zSFL0bxHJ)ZS1v)W?qcA)S z_{yJLZ+o^Fg^!9;V8)o= zsT*!bvH6mPuL}{rquC)Rh3c{nRnc$tN^kbCtan_NAeBYX-q);=ke=@%^z;@SqFPHA zD$uRt5v|@6S*cHK1jK?l^~LP^B3fo^$IjUyfXAx`a8_D9KFVoS7qAV}aTIZ|z%~U)q z@~KD^9Lis-??52L0+Wg#^hGbI&lTJR)-@}q54D^Ul$srChyAen*`oLlnmn$k14{G7 zP`G0|ICL-eh4#tnm>ZUUy+ttO3xW4u-a!58n{O4$vf!P|FxIRNpfW^zv}lO>Qm?$l z17Uvz@lzEr_Al@UxrpnD4S{VAyVouNPBZ6gH?~6I$tD#qE4gYgY_{{GcQgd#d)mzh6A+}fd=EeC2k}~R_+~xe zxO*)$d`*+7$^?bUxjHgUW;~y}7kD$`?k&UrK}4+%kQ5#gk0#qKf%YCkz&^U;VD z4I1?G&IWqi5*F(><8nRX?G>%u}>)4^1I4?<9)~Mrd2J4X2v5c`&*;+d}8slA4{QYf|7xL7xp1i&W082Wsh+lsqs>B8$I)QRS? zzxq%V%$mk3dCVuDdb=OP*yX#Rfrjn`f*X~0Mbs$AuBE)F=F;*D`#xUqoU*k}C#N(T=tmI+%HA327%yyXRMD($ zac#$e!Z#f+95p!zv)|uh-KqH`7t(}>t_cRfl3;nCDX?SNpp$M{MP<9dF5rUU! z`j?us3ZVRi&aS%YsW8zsXWap7W<0Wk7={i=S2*yItiU+r*W>oN(R>v}zbRw{xlvVK z8|gRhk;V@R85}F!8Upc!#lx_>pw2es>6H0dwb{&a<`M9i+VVH@;o_Xg2^HLw#B(>- zR*+@B8g^S!GIw>ZLD<062S}b-m$TMh>}Tb1mwRlJm;q~N6wO)6%-T_H=eqS^7b&MD_Rm$abJB!tp0 zK!K+<>gam-AeSEo2csHp)i)Lc=*@aIee6~z$F5Fe%zAz=wo6)b8W=e-;;6|q``?le z*Vv<`4DnKsNfQX3hOX2*LZ=iz3ZVmxO~L8IpS{k$F zAll&UW?&W{e_}3nVHOuA2lFsRYnN_R3&sIvrD?uxRBPu& z$d(l5qv|&ir9se}(I1xI%57~u@elOgu|gx!rM zT7~C9SN8n=3tINExrg&_8@C(_Nc0KHEgruviQV^2x^k_DT9F0?U~$A@poHXqz4&al zR7v<8k)#GrF%jp$@LzVY7*E9Y^h5UA-G(&U-bz z2({~1toz&FD4uS(75)gRva*OwMn3t+z{Rp|K}FFCKrG+GG&vt;>elFWDv>|#RPEy= z0@FLU(Su2z=_c-1;*{u~H;ajUW)??Wyx)D?i_gko3HvX;xk+Fh;%*R70au$=P|#H8 zJvGS&yhI%CQL>CLh9$fn^et(wlj!fC=V-JmlfE+A2mmVx#qXH?14q?P_K)| zR9rDG-?M?+gsxTMx2GdV<96*CMYytuu~;W=JDNgl(BQSjIxiaSOJHrQbq(`v_Uou_rPiK3jBvTn^C`9c zP(TRpkhlLkF^VBk`wX$-91Q3%MR*)w&3k-2<2R5&<$O42kd={1e_E;YNQ_?#9?_YT zM#ZhvhOCx90ivn%9wDfQP%QaSXkS$9L3=?^($*S>9wq$}WII*7_=vn#wOE4}+4CfG zY}SFGo04v^6s|%&zy)JG40VcbP_$TA1FU{l<0EF9mxG@V-Wb(?6A1w;ysH z1(Q|iwG9J$lgdhPQ&sbkK4Scb8Z)a8n6%;a<7{mwRre}gxlwyoiW$vHXWU}uNYS?u z1o78j)n-rFR!E-XW*fdgN$P6(*34G1sjGU7pHGC$u02&%jN2+_?ZJO3oAJ|%ylvR! z@e_~Z-Ab+{Jt*J!x!DyrIB%YucsqgO=kGCFq{$<-=T_2lBS)OHD>W<38ETF zgJ_H!oc;AWo2-~0&>}5(5&On6uR4&kGJ*f_kOzbJVk58i)7`j zoDv#AWe-Q+gKnGZqrQ5!CpAimaLCkZI2phG?=ib044=qeB z+_Eg7RNt6l=IQQ|>)H7=B{QzPIf0t|zxv(0hU}Wy(q;vDJPbHWmK9rY${WVO`@n*DPE`lW1>>uK9 zZu=_x^lU{xLi)0wxubtZ)r2JrhWM#eP@1BB^_Doc%FVZO4U;py3QjOTFvnKGbo`XQ z3&>qG+99=MQ3^>I)rdfzSx496|>~-u_Zv-fl#`EU0@? z94nG^k~;?AMWW5OtZ4cn(nU_dKn2_JSLBDbXH3~8hVp0HHwv%Y-i+{>%R^_B6#lgL z`JcD@L8AVTRnf4MOv()|F*tAlaDqXKHbk_%Nvol?L-dn3tX|5EnbB;iGPlBC>;S*- z{BvtMb$o?jl|ZEu<8?T9%@&SN#;jQc$DYTuXd3V zSm4<3e_25-C8ot)91wpX9elkO>#l~CvtMt`3P3c}CewS@0Ft7&xbH4o&38F5&c31~ zS#Q|YRY0R0o{{e}bWln{xk#-)wS zJEB8d{!$l05MO4_|bJjCifBE_HE`Zu1~pG;?MTBIlI4=c2=kBT3hYt7!gr zh46wo@02fKKda~bg`GS0tPxW zOl&UGmKR!g>`*X|R47wTOxSX@7tAyP=~&BC6&3G*(|;Ym3_Eeygw<1brY?>sDE|U%#GX{d z^p)e&_cGT8YC2D=V+FDCxNX8G<6Gzxhr`m5Js_C$ptB+0oUl@6r$T(XOL(hv^ASx{ zBD?^3iex2&qX4D?(c9+IeS|yhBUnXw zcT!l$x6;kJF>h%~?v^G?KydueA&&+Tp>turP`~lrpb8S~!wt}2LO5U+WvLcH2i?#* zLsX{MW8Y6zY()Bl{~y#wD3K^{{v)KaVp0>^$JyaLP-y!M%8&y|5k}%gy+aE>4DD*N zqB0go3RS|BoMZl$Y+H%$U&^(3QdI+Okk#gY#0{dd*H?G$X!(fAdP#==%Xauj!U1x~ z7e@0DZJJW&-FIInkTE)N;hJEt-7JL8~ixWY3oF~ zJ${CkU8DTU$Y#V%eUFd>mI>aHvrmFix4Ef*54Cb{@JC=gcH=CNkqQ{fh|s>x9j~AO zT+-^2Bx`B5jm8sxNL@j!EE8gcR~Fj4*)0++n2)~6Pwey!35@5StUYgu=QKgf*gy%p zt5t2arMijUA;aXUumjU`>O^juv0>CIX~03eLceX?=#_%MIX*>_^AG#KZ#)>f@}(3t z?Wg^AJc|b20 z1b8fL!1SxceS0V(X8LJO{^}Q+fu`d7q)8i^0`->vLe^}2)v2}V$9E@N^MKgFcxf>0 zLx5dbwLHTd9ZOf$OBI1HHp}`mGSBp$dTNkcz`%(2dF`9n`&@AL)2>y0?!Vewp7(Uf zKU+mm@93QNkBg`*jhNa+)~L{mEDde>P}Q6HHUoFczY)7Gs-&fk%luc1b|J9>W&)m0 z8Uj+tE#Fyk!1o$kHInw>-2_TwUMO2lOGbmQ$E|drHmnR+KE^DLlb1_RUP*m;B}7U% zyEWoO=@+URee2jkzR1)I6unbK3$* zQ%$e+d;yFHTiu^V&@)%3yHdriC@=8uF zzm`-5-@N%|*jD<~dg!bLqP2)Bw>@v?bvtAVV~^@;y=(_jBUfny8GhUJ*my|<2pwGWSy$;yLnrFupXI1Jvx zf0uXd+;Kk7XsKCl&|#7DO(2Rd3GEthrv-F9TO^&gEx+|_i2qq2LyN?KiJW#T=Fz+l zj_wDOs6;@Pw805dwwbf`kvCp$U%ImgMeWppz&nF0`^D|Z;oC67C;_b=UN=|C`Q&Bq zVHex_h4UUuHk2D^JPGxt7TRL^UhL_VaM}7x z7SHddi@TmyyXN*IvgcOdyX(0;lgQB5-mL6$HLBYd*o9<4{p)15{9eT@k&*Rn&dsQ| zfw_+Lz%tAN6s6krNwhn**Jii6 z3hF3?g&CT$-vsuigW2H|@KRjlW>-E?2!6H7S(PZ8H*jy;l~1p_{T>xpJe~jyyI9$J z1sbzG0$cf#=d!ebiF$lc&dt1csuEKHdy~O5qez{|b&~RRwTgz&IX; zk}QK1T^^k^8Ic$aPb`O z)j3@fD&Cf{%&@9@l*C*8|Azc;id8mo^o!{EqqOxuuFhu?mtGkMa;U5)TUpeiM{`M< zrEx(XHJq2m=6n~o?eulSHu2}IP|0qf_CYFF$w92Wo(1}JuzzGr*Lhrnez@40>#!^u ztioyx|8J62e$sD-uUNZ~k7rX7fl7*88~Z6{utfEs{dZuraN7EAF>_Y&CDmdd z>@5vVc^z&d=jN`xv7@;9+fiG_;TY%+PW${6sJmCAn3H<#Mszl2TY21SBN(8iS? z0%+>|g2OWg#YQ@rS)bD>`m#DA#6{&adtFg}c3%BU`qS21(A<`QYJZqm85!fKFHw1$ z_ox)?3+gw`ai_f%7tL&;?@O*iEU#NB-^~IqCwYNhZ7Ei>N<_~gjeHvy+H!+ZCB>As z7Bx99(a+JG6%)oO&QN$hrTwe3Dw}mW52bQz86Y@q+gI$zPP9@^FIVaj^uUv=d3j;M zElom()Cn{-ZaVsGdRtX8XrO~V=$#{N`vib*wL^=KwiFG4K5aBX*bie;I$|6~0H9Qv z+HCHb*|AkgL`Di|xewHO|5mAZtleXuyy^)4;8SD?FxDM`uFJq! zS__qq20cD;E%zzmpjQp#AhcnE+`-N@8-ccU zXps*F49$51hivk6nsKJ~`4X*b)wbBKDh`z)Ra8V?QRp?DDXdHKs^mN*5B7htHVkV_ z0@_-!YufFT57SD*ue?(xh;EK@EN~TcvzBpw_0_cn-A-1-QaIbb}T(!YwkBT$I^>Wb7#KL=tPmbF+-ApPpMeR7cC~? zM6sdrmt1Sg&W1jS`KlEpf=)JCSTV3IZI3xR<^8&vBPW4l^ka}!fS2j(%PoCHL1ooL-c|~F)#L+ zLaZk-+ozgPkY3Z#xb;&GomUA|4%5-K27Gs`eOnQW1KrnK?y3GX)HY%Yz)>QND;Am>0ZY&Ht|~pa71E!!a|*fvRU!_HRWdMb zEHErx6#9=IR!}yx0=G2GZJi%*)*a(osgRD|v`CSw^%D?%pGi4PPD0)Pi6% z9b14gJak+{Y4XE{&gpFt#Xp6!Hq;cB*)91pXyp4#b72eexw)|Z z^|DLmJGtkajq%}jK=%ZhJw^hkOdzf-#+c@7Uuq zQML9dX{ra*p0cC=X=dJc%y{fae>z+(OaO-vnm>Yil>qQBP%^JI@8;M5)3TFq^5@=Z zI&F}Dxe?%9RLj$YeKu-X!W+K)_S^y-ZDqT9Vx4{t_!GM#;0#5=#@c9CJ*-NK^R8FG z6JzL;DUTQC_Nd_a&m47*?9@K00;!uMP+0Wi8t|tDw+Pl{U^_+2dmG?8+I(u;vVq>D zEMNP3DcHTWPJe=+YQMg$$dP#pyDonZ^N9*&sm&HG+4Jd5h)xB%CwrNI$a6rnx9Bb9 zZ9U%r41T%)A=JGJ@!N28@(;&J%|hMibBn8{kZ{47Pn3nB9*Q{E57B0~Y!14~ePygO zo#;jKTu06SX1Cle94tu**>d5&yN&IHx8ph*x+~nyubkqx(!HPCNsUY$2KlB|g!7v0 zR+hAj1f82_`=2`YlcLl4Q6Z=5?R<6lcal*XSGP5lYIy9M+kE?`yUV)C@B2i(Bgjp8 zb>VHyb6$feT=2TMgWA~cP;<)#rdq_c7Y$K&8Auc%Sr3?F@j36#(t)v7r5v-_B%sI_`c|tdbiE1 zOtV&L@|Tlanv#sI3MKks|1VeF9K9$ShaT-Md$Yc6f2*g%2=)GVGU3A$MdKz*b>H(3 z%zz~p*a~t8?ktj3lUUpE!EUd}OCIGvUP;}Q%EoQ zdO3nyt==K<&S5a-p22Ixd&#h)O*+8$6EZ<9E{yMjTzm8{GW}i-kGx#agZePD6tT46 zY-pMmcQe;Yaxg$fFvyvyBwc<(kG=Mqd@wV}_QhFS*+RgNi)n@vg|O;mQcOs$XH_Y7 ze;Ik*l#xrlOdRx%T@-+7T&dP| zg!cBZTx$jLW(Z7uq^DA9h_XNGm91M#EpDuo%-2+Qiv2Xk(Y`0g%+}Ml-e7@>{k+qW z29P>hh3pdWV{##qy!RHbWv?j;dBz4KNHb5cvorT(M}4Y@5W64L6z@65T6s0_a3+H8 zEpKNC@{B`=qpxB31$tL}5HlcVDQpU`$#SDbONfi|yqYI?Yj=DLsZtHwtl9Y`xqX}b z=b5Og2L}B~rPvrwW5iN+qFGJ5>C(Gnef#8kx`Ctb;o_Tz`9M+$={)LrUN8TxHA1bP z{I_o3`vB$2S!s{REn5;7SX@tg*CWo$t19yNir%yodGhwP@tLSOW~8t;>qWWDETm_I z6xVdj7VVF-lV4)+GlNtj=)*{^KFGN9qWO8-g*Ee&6(3F~D)l}gqc~WVNckzf~ z^{L}6xAzudlPLu=b6!(?cdcmhae8-=OXi^npSC?WoQH8kB^(m)Y?8Kt)?UMJkIQ#{ zbFyC{=Tk}0&GzLN23_zEX5}ttcO?k<5R>%ahI>gt8@8vVb8%KaSSbo(4Y@#uq7AP z9$t$|z=Nu0Q?}Xf^Ij|+uIbJ^>^-peKP%D}r!w`2cA>^>qBE_WNB?I*?kXMvUTWO? zANXp_a^p73peC^DEHB;cJJF%=zS({UW0;9E5KCq6w3BX)e0UA}ubWf^CTfPl^bK~l zW1ae2-H&gvYTqXPd;{c@`I>G|l0MVViJG0PypHMHOJ+}e)D*jgu3eB=(YQn9qr5kdYC7BU#R(Cjg%F2VG85EN zv}6z{hA;(1MGFN>OPLAC5H*a6VG0Bg1ucUFK@kZAoX{eG%=4@vG9)M%Aj|>@66TPE zDe%73z4f|p_r2YB_3D0W{eCZhXMN8;d+)Q)+0$o-F1LZN4y_Il7|d#h|8QO8M&q#t z24DZa{m-7Uz2F|H=^Z)R>T>fP$mxVrMlZzd-*d}`gPFiocO zZBlQ^jmse~fFMes#5@4`v8`fKCFUn+MngW zbA0twzI>m9-}G+7XxAt=@DcAdAM+_Mi{Xd`WzR@H$Rb+yI^ew0oL~%tGD!5;-XUi1 zOuz7vv;NF`VQzrydj3bO$HU<53TQ17|ox zs{$M{0JnI7-DT@rzZz!ilFV&Vnb~!m<&+6=6Q}>NL?wbd%ocC|Va{3{5e;|Asl{G3 zrmrkboL{X=;n!onD0+u_oi_IrsHNwLBnp1dovkE=J?)CcnUpEG&W6Ho%|F426M*Ry zbIbG3UBV@ONhW-OvrbA-11~;-Obp2|cTV7`owt+;=o+Xk#lCuR>}u)@Wy8mclXsF^ z+gFoYm#SKO{4~4J-0OlJ{P)enX`_XDn@>JjEajC(ZXXcui3DD@v0Q`bONXUxi3@<^ zse`DNuO5KteWJawFx%Fm! zZm)fc&DF$|SSx+O`WK5!wE;PY54Yu42d63*)VvzNjb%*C4_QsjFaHX^75ZGo?5AIZ z(}-XFt0Jw#n2(K8Ho5tJ6JY&h@8`mj0{*8i#c`TM7(w=>L={qyAk;{`Y+@$MK<6 zM3=Jq6lJ-E6W>bROFPWxC!z*7(Y7H!ebYB^b}J!-gmD%lvCs(K^kcq6Sk>qJTKx&Z zCh9`i@B{#nd8$A|zeqcDs@ux8`nqS8;v|CGW&4*?093x_L4F=Li4{{(bJne*&7_0) zwig8iykqs4iMAu`_stvEZ+;DQ6o2aJ=wV{NJ~MNs)Am~d03JBuzV*W4nQ}kv^1*Oy zrGS&(%5ko9@L&0wkg>BqlCPDKC)iC19!qG9;$JY*{0vp;MbsnIdnN|jcY?mUpz2GN z;_dPiYpABbGMV!^(3C#AS#4{L<*8pb?d!F^9J}&%BjinS_iC-mQUS8Cpl0OGp!Z9{ z@K--d{LKzM7i?~wsNvR1v52a@x&G~Y19S;b6wQeGlA-MQQp*#A#MEA-f$-N3YK#rS z#!DU;(%?%sLe%j0U336#UmQ;E73`HMM*>6f!V^>Y!+)Ob&5M~)H@z(AK!09wYYt)%57FdAOB9q$XeF-%161e;6YQ-M-C;};%s?s?cbPA{`}muUWx2@D zAHJ}Mu6E>vpXZxw|Jo&xk0iW)godJ}){Gw->|o{+5rVgN9R>S6EP}m`;LFWCofMjV z^M{SSMhq~7X?obqfHHoRxxtcTJG%q7yxf3+Q(M^hjw$_QG)8XpJS)dgZ$Yt5DKD|3 zv?(D-&BL#f02jP@fNXv0l0%4`EvBO-?%^&hQ$Zy!_69rz8W_(_B4xQS*If{S=_ZM= zqX9dYmq$^WVs)Vc!gvr4cp6?GyhRLlW^j>s!(*q|r8YM+)Kasy30Lf~5Ywfyzi8gK`;9)7J zff1inbQ4N8OEn`DTVj=Ob292BWwYqoU?DK?V<4$W_;ZO#cxY6>!zte}4v_aSt8^VA zcZ5k4|G>n3=bJBuXF!8wBOI<<1dG6FX5&v#=Vd%6xsJMWilGT)m?_51bk62KVOEhS zqmcS(wb$j?fC6_n>j^LQa$?o`?kt?%;hy_CYZc4`1t9e&uzR80XFqJ1?|2mvHQAYM zrMh2VM?Cp>jYDN*Wg144(WjX!{WvAG&O48p%Qt;%%Zdd~z(sTaEt29BYMZ-s6GQaV0 zZTlpox6B_l7n$cRoNWv^q&5)zUI`sJo(4~IG<{YKAc;=oyVdTC0G-Om8@lp*>+qvB zRlzI}nfZSN)Lo<&G*x-kN71zA1$jXSaph`2ZN<(Dsx+rfBbV1|;%>c4;rsB&Fr|jr zn?8PdgLC5sAz=zCyLNl4%|@gV?zZlvpBW&;Y@b10h& z-!GpIr>DtucS)1p#;CTl3UIx;8nDG z)6Pxcyjjj|@D1CX@SJ!YER*Eo!sbIv zoydbdg}&piv+u6-CUumCwCTUTtGk4qq3s9H$#YvCQZqtDG`HpA`qF6OzTSStJa9+? z3!@fxdo|2F1JD)0+l2EGG)`^oKW~VjUskM{D+{1?K)QI4FD-@|fVijP9iAogH47-b zk)L!f_n!gs?N=+91Yt4P%$(BA0x@h5J(*aEf+W5v(#>>>q&!(ind<>DKqSN@FoOPH zqG{Rq^3dHU%qk8+w_n0bH6N;#at%ne>q?J z9QD$zoxk(lks2&3L3dZv!%Rajql@XmBzS|%^i*pWQbif93LxeBrh$tP>gB`d^S_^O zbsFj?BfSR*1~mEVyMpo8tqwED zC9&-w&NX{`Y4KMe)lR{cWOp^Y7$bg?0V<|>-(j3b%CFL%i>zqi@FVE~Nc3wM#&6FC zd9dab(Dsv-E0<$^Tg8`_r7ZI;KKDV2pcJ<%I7l-umo^Mz{bDV&_!-$6?SOW&u8@n) z2&av`u$B2*l&Q`t-JL9+Xr(=OhPGD5Sa8q68n|T4H5Z89ok2A=>At?kj5;>DviHm5 z6ZIAGtLp{fpO{bjlDr4!ZlHd|)xU|0ALu*wMXxr#vby|nQtps*t7v`od4*w!1%dI< ztO$#B+*lE5K1*6zjZrvTj|RZ8-~>C2O#iVkC}MURwb}I+GUNJQYuRo;|qQa z85Y-J+Bo%^rn8lAe1{jZF_WEng07uJ->uVx$PDN47uzgUwn zut-IqP`e9lTY$`IY5gR~N$I=7o09KZ75IO3uk{5&Sy#JtMrv?8-`afd#j}o;Yck#w z4{cf(2YVj(rF~p+W5hgMzew2xTEv2*E--a8sD8dIcqVyup#fP2AT&H~8P}KL{bFaV zL4fP#gB6=*t4j9oMLql#YTsAsTQN|DDe-mFbt%(Q{tO?|Np=b)JJmi!YQzQoe7tZu>PavR{~lX*`tM>WnDKuMvIzG9gsOy&iJyk70`~ zk$7;AB*x8CkLA*Hcr+xTp(eX8t;km-@a`K*Z*!P9C~~`eCd94(W8l&R-v3BjuxrmZ z^I?O*l^51cb;6S&!E5l;s-@_BWwbPUA40l?wYqv=sf`aiCaj)Q z{ZXDZ=Mk_`k8Z>3I6Ifkmub;Po$9(RW)(fbxS|Tb*tW)Y}qtp>}NSW$kPX|u8Ku z-h$AHna7j`A}S>Zm<~IU#*FaM;O~C9CY<3duC!0KAT|~z>7nM<*sUW|TLo^zN9Z=unum`6FjbxW^wW;WEtKyOEcXr;d9 z=F@VEoP7b}b}W1h|F5nr3tdd}S<;0r9)J}{TWp78sWKrCsakJPs$)wB zH$TC|zl31<@?d+HpwlR>_oP|qM8Uy(qld-;gyjBeNU_<#R>k=YW~)7P3!r@30)RfI zaT@1g8>6sbqc6br-p}fMx17BW%wBN~=?q0K0JwIzs2eAuEXWz@%Ui9am7{UWRk0^! z-4@zTfR{M@+xJ0BjXH0}2!&_hhCSa1pq>nMhC?W=s(f{%o59;264xI80*t;0H9q(* zPVbHmA7mIIfmi#f=jnn(EoE>+C@J*!IFISfKLH>*->l_=V)6*v@EP)Rt@xDAz+BH`3$xyD4IHr=rMmM zI1yldCB7UQnADb+n!1P}ls-&nq{^8u6ke6 z$}#)==?!xaYUF@U^iY%9^0ZX@U0$pyl4ESeY5o*r@CHKO?O>(Yl)56JY!N+ZE}5@8 z1`0=1i)W;!2b-RbXSv2&to7|xF4AqEshJD}Ml1H(cl}1S1E#f^qaB^!G-pWlB4%j6 zaFYld@o}V_ALsI}Tx6Q`%CGhm>;1}WXW=nrq8l$}RA^{$UadXagQ@P0G&U~VJgF9V z=GGkO7TtvPzx?V{(*H98dp_*cIi}ynN&$0gvQ*4?VctGpUa#&IURUH~&vl0?W|%pg z@j2YK_%_!23(M}wXb0s8&>dS9@6-@R)tL^^ai7~q`vEeewA9DUT=!i`Myw?Bp|?L= zS_FgUc75p2S!rpDc4`MPOHDo53HT$7&9h%}i`#8&A5U~sBVJ}2J+F=z9BjU8S4dIK z6J1IT@|DG(q%ZLaahvJuYm^(QSHdo2tSuGl;b(u-!+uxryUNi3_*xuhs`20iYsPcp zGcWbAO+NC9P)*ajXl?rQ2l1gE9&r+YkPhrH^dIProNI`Kx`!l2qe~uI&Y{ZdYolu$ zr{}N3*DF>Hxc7&z#Mx*3`eb-1yGY2EY5lYSj<|`H@5*ls7Xp)5XD@GF{H-#>UR6Cg znb*4d9z525I@mu;E@WSHhs&05jtSQgo4L+IeLY zdN`+JG*I|F;_I`*#kXktl!M95-Z$f()dDy+@qCVwWv*%b0^1U89am!2P#5Tqtb94F z_^B4sxu557xnu?7D-22mh0EYaisg^X=R;>B(#CeCL9HRIEI`QavfkT~9I)@QM6hUk zi74sM-8m~HtCXNSQ3(7sE5o=ysNb9VSL#d4F2|O4+gVUgABtigz#YC_zqwXFEUqT4PBXVaMng{2y!atJ(0!cmX1BjAQ>p#`9& zhdl|DS38isG4iw@(j}Fz+ff>FV44E%>c%NkzRH#r|}fGp=H3E~+_=0P?2#bT+0Z4=Pu&Z5N(kL znyvg0fs&#hz)s(NNChVSA|tu5{}Sn&?#zF{t;Us*e?VY;vIo;a#)5<^N(^hA@}vAI zgUtXw(m>~Ti(Jp_`p#ry54e52285oM*n&Y)V2EXBEmQSKI zn32+s&a+j5#{I0@P_K+>&l(KpAew3N4UHEi-xUK@bt03U|JfG{miSEAzD4y8$d%<# ziFUA&+Ry}UB)eA>IV{3K8KDxy4)a)vs!nO#^-tEa^oP&94HPGYNBtc6dGe5gboC;=a2fuJYV! z`)Plj-&`n#w+XDD73_36885Uk{Bc&vRT=RP%vuP}dF4PXa-I z+ng%c_`POSHegcicuv90pxr(p3OcGN(|w;$Tabx0T(8uwTuU~Xk!`W_Whb5H^vb9@ zW;kxt5*mN5zi1R^9;Dh337*Z+=WOio5}m7FxWw4z5F05-doYmyOFq+Kw4L_d*f%M5 zSod8lxY8i+%Siy%@TOvbj(T~jh4Fin`50<)B+;G`;Wgt306fhRiryjc+qaIF%aBoT z=_nKTagaj25nxQX*7Z0XT8nNzQFfF31;Ij|r>bKdqtw5GZVaL3^JRj#lSHE2P`Tn9*n762q( zY2Tp?ul`jkWMz8Zjl6xUd&e)eh2lY#wM&3T#{3W+gq6YeS-d$LdoH3hfs*D+U#=4n} zIH6riESD$s+WlfZfQ?(G2$7>x|UJ!dJF!ITnZor&|& z2EF!_O8Sh_#47Q7hA;Jardw8qwX3la$+^o(Ye|Ft>dAso5`+I1Yefaa=q7zFp%d-a ztW4bJ9^tHp%vy2$JW*=u{X}G}tMZ0R>VWv;Ghy7AhT+U#jUq!6cr^Wwt$h#}iHrzf zSinw66iJO}A9&DsG-cu9(DwIWPpXG%sDGTwE*W+vW+tuRf~%J-is9e~hjmg+=q}uD z4Wh2S>&whSxN@{rJ>Rsxt5$DWD+7BI`lSSRBwp+gUkiO&1?ylP@Wq060 z8%Hm>qN>8y=$euncFKAKYlG;=b$2S?kJpY814TPvx_wXunRh2>5@RLhE9@_VHgmhx z6D;7{cMVF-C)oPP^eVa%gIvhQSY#n^q)NtHU@S|1)md{-tf55wyK2@Fh+k8X*Ldt_ zP}Ggy0{{U1$>owm?S;tJ16b0Rge%gF9eRd-^uW20f-9?gID3*sar#Yz8WHck5bl}{ z(u!bW*ANG7-=iZk0}n+6t#>DowQT+%EwOar9I8n zzQpV$%Qozv&Nql`Fq=c8BYmX-?EZ+Esnv;04Y4{JxbK>t{S(6e=h#dRxd?X4{SQQ( zNwE0Z$bSiY3jXh1_ z?ER1UX2sFjy%yJsV8{2l#}Yxb8v*>%TprLov=mD<04^8wO?+!9s?HFXWI%>{mQF-i zMop<>GcHq((_C;#;WUn~gq%l6-ZwBO3~9R8(op*ZgATy8R6`p4et9!|;iku<)0}HP zIkiaYf+gIu_CDK*O_qW4KTF?hyI}RYY{2X*siPy*L$kA>!{O!ubg^3C+~<5|+MU7? zd>t2G#nj-W-iexrL|v3qGc!g6u+EOV>?$orjH?}V))A|7JN;c~V8NXQ+IG*{mT5{M zeWsmir3fP{x!K(EAUJnr`cWJF9|ldijZxpiOapyH@+AW5D6Y^@WzW>E1Ms z!OM>Yo^@pl;K|4LDZ~y7jQ=2y1iyhzOv+0obTUASsSB3o9yPj64s*I|C{2v1KJOw8 zk`vdn$Mxwjs|2Nd>|MGi*+ks$wps{A1xKB5W-7VKqnr0<%k74aE6OmPgy25q$q+3z z(SEkvjuj|Ps_Yfqn3G4FU1T_j^7CHhxuR${!0#vOs(dQL-tSLOUubg3LZ%mXpJMr# zj|{zXrReYQjL}_oP2~F|oMPNpEvjLsI8wZ~YRLcIPB_k=Cdg~8qtfj2|7KHXjM zyMQk%l&pr+-r^IAUlXPq()iDWxh?hFt?>9to^1RblT⋛*%%lSAY`CX@3#%Gt)b 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 literal 0 HcmV?d00001 diff --git a/doc/proposals/2025/gsoc/images/Screenshot (2).png b/doc/proposals/2025/gsoc/images/Screenshot (2).png new file mode 100644 index 0000000000000000000000000000000000000000..58d8d334b96efee646596fffc9c7166c9c948870 GIT binary patch literal 299243 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∾p4KsPlN2-%6lgciF4?am-uL{# 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

C-ai@@} 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

anaB@@rEj#O*kpK!6LHs7i_* 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 Date: Tue, 1 Apr 2025 03:26:51 +0530 Subject: [PATCH 010/188] Delete doc/proposals/2025/gsoc/images/Screenshot (2).png --- .../2025/gsoc/images/Screenshot (2).png | Bin 299243 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/proposals/2025/gsoc/images/Screenshot (2).png diff --git a/doc/proposals/2025/gsoc/images/Screenshot (2).png b/doc/proposals/2025/gsoc/images/Screenshot (2).png deleted file mode 100644 index 58d8d334b96efee646596fffc9c7166c9c948870..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299243 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∾p4KsPlN2-%6lgciF4?am-uL{# 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

C-ai@@} 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

k>a`A_@C9-Cd(pw8nWS3A9c8#NSPhUbXh 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%0A&MwmVRNxgp3dBqZi+y=BIODqxzA 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

h_ImmeXE`jPD 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⋛*%%lSAY`CX@3#%Gt)b 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 From 6f4524356560fc09962a7e668e0a2e39d9048f9b Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 03:29:14 +0530 Subject: [PATCH 012/188] Add files via upload --- .../images/Screenshot 2025-04-01 031655.png | Bin 0 -> 92356 bytes .../images/Screenshot 2025-04-01 031809.png | Bin 0 -> 92937 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 031655.png create mode 100644 doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 031809.png diff --git a/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 031655.png b/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 031655.png new file mode 100644 index 0000000000000000000000000000000000000000..aa27edf40359a9a597c0765bef7bbf95dd8ed758 GIT binary patch literal 92356 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�P4Uh~HNS6*LfpGS|GJpL^e+@juBE^cs9po8H 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

anaB@@rEj#O*kpK!6LHs7i_* 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 Date: Tue, 1 Apr 2025 03:27:07 +0530 Subject: [PATCH 011/188] Delete doc/proposals/2025/gsoc/images/Screenshot (1).png --- .../2025/gsoc/images/Screenshot (1).png | Bin 220545 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/proposals/2025/gsoc/images/Screenshot (1).png diff --git a/doc/proposals/2025/gsoc/images/Screenshot (1).png b/doc/proposals/2025/gsoc/images/Screenshot (1).png deleted file mode 100644 index 04ed837efea4b270d7c1b0a041cbe83395a9795b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 220545 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

1A^yGEew9P@NVm%+-S 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 I@AUA00c_KDnE(I) literal 0 HcmV?d00001 From af60b829a94c052cbad4823e52806e660e42e4db Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 03:48:15 +0530 Subject: [PATCH 013/188] Update Application_K_Govind_API_Support.md --- .../gsoc/Application_K_Govind_API_Support.md | 68 +++++++++++++++++-- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 2b41c238c..7fa53ff5f 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -60,17 +60,71 @@ ### API Testing Support +I would like to intoroduce some feautures as listed above to enhance the api testing support of the application. + + +### Abstract 1 +Introducing web socket implementation into the APIDash. Introduce a new client and manager to handle the websocket messages and render them in the ui . + ``` + + +-------------------------+ + | WebSocket Server | + | - Manages connections | + | - Handles messages | + +-----------+-------------+ + | + v + +------------------------------------+ + | WebSocket Client (web_socket_channel) | + | - Establishes connection | + | - Sends & receives messages | + | - Handles ping & retries | + | - onError → Update Riverpod state | + | - onDone → Update Riverpod state | + +------------------------------------+ + | + v + +-------------------------------+ + | WebSocket Connection Manager | + | - Ping interval handling | + | - Reconnection attempts | + | - Interval between retries | + +-------------------------------+ + | + v + +-----------------------------+ + | Riverpod State Management | + | - Stores all messages | + | - Updates WebSocket Model | + | - Handles UI reactivity | + +-----------------------------+ + | + v + +--------------------------------+ + | Flutter WebSocket UI | + | - Search messages | + | - Clear all/one message | + | - Scroll to top/up option | + | - Dynamic UI per request | + +--------------------------------+ +``` + +### Detailed Description +- **Problem Statement:** [Explain the problem] +- **Proposed Solution:** + The + +- **Technologies & Tools:** The approach uses the package web_socket_channel(^3.0.1) +- **Expected Outcomes:** A clean ui with maximum smoothness satisfying above mentioned solutions. +- **Linked PR for POC:** https://github.com/foss42/apidash/pull/555 + +### Abstract 2 + + -### Abstract -A brief summary of the problem and how you plan to tackle it. -### Detailed Description -- **Problem Statement:** [Explain the problem] -- **Proposed Solution:** [Your approach] -- **Technologies & Tools:** [List of technologies you plan to use] -- **Expected Outcomes:** [Deliverables and goals] ### Weekly Timeline From 70ea4bf4ab8579f9117fa5d683e4945935deecea Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:49:57 +0530 Subject: [PATCH 014/188] Update Application_K_Govind_API_Support.md --- .../gsoc/Application_K_Govind_API_Support.md | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 7fa53ff5f..4726b99d7 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -63,8 +63,9 @@ I would like to intoroduce some feautures as listed above to enhance the api testing support of the application. -### Abstract 1 +### Abstract 1(Web Socket Implementation) Introducing web socket implementation into the APIDash. Introduce a new client and manager to handle the websocket messages and render them in the ui . +The solution involves providing the users with an option to change ping intervals, number of reconnection attempts, interval between reconnection attenpts. All of this would be managed inside the settings. The ui updates would be made using river pod provider specifically for websocket messages for each request ids. All this would be initiated by user clicking on the new APIType (Web Scoket). The appropriate changes would be done in codegen related files to give code. ``` +-------------------------+ @@ -109,16 +110,25 @@ Introducing web socket implementation into the APIDash. Introduce a new client a +--------------------------------+ ``` -### Detailed Description -- **Problem Statement:** [Explain the problem] -- **Proposed Solution:** - The - +### Detailed Description - **Technologies & Tools:** The approach uses the package web_socket_channel(^3.0.1) - **Expected Outcomes:** A clean ui with maximum smoothness satisfying above mentioned solutions. -- **Linked PR for POC:** https://github.com/foss42/apidash/pull/555 +- **Linked PR for POC:** https://github.com/foss42/apidash/pull/555 (The PR is in no way the final product but simply to show the code structure and my approach) + +### Abstract 2(SSE Support) +Trying to implement SSE Support into the application. The approach is still similar to + + + +### Abstract 3(GraphQL Enhancement) +Proposing to include graph ql variable support and graphql introspection + + + + +### Abstract 4(URL Encoded Multipart) + -### Abstract 2 From eab65adb70a52818ded2b3b8dc4034f42fe1210a Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 15:30:28 +0530 Subject: [PATCH 015/188] Update Application_K_Govind_API_Support.md with graphql --- .../gsoc/Application_K_Govind_API_Support.md | 111 ++++++++++++++++-- 1 file changed, 104 insertions(+), 7 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 4726b99d7..429303771 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -40,7 +40,7 @@ - **Repo/Link:** [Link to Project] ### Motivating Challenges -- I like challenges of two kind, one which demands me to think and implement what i learned. And the other one is which makes me learn new things. +- I like challenges of two kind, one which demands me to think and implement what i learned. And the other one which makes me learn new things. ### Availability for GSoC - **Will you work full-time on GSoC?** No @@ -60,12 +60,12 @@ ### API Testing Support -I would like to intoroduce some feautures as listed above to enhance the api testing support of the application. +I would like to introduce some feautures as listed below to enhance the api testing support of the application. This is part of a single proposal. ### Abstract 1(Web Socket Implementation) Introducing web socket implementation into the APIDash. Introduce a new client and manager to handle the websocket messages and render them in the ui . -The solution involves providing the users with an option to change ping intervals, number of reconnection attempts, interval between reconnection attenpts. All of this would be managed inside the settings. The ui updates would be made using river pod provider specifically for websocket messages for each request ids. All this would be initiated by user clicking on the new APIType (Web Scoket). The appropriate changes would be done in codegen related files to give code. +The solution involves providing the users with an option to change ping intervals, number of reconnection attempts, interval between reconnection attenpts. All of this would be managed inside the settings. The ui updates would be made using river pod provider specifically for websocket messages for each request ids. All this would be initiated by user clicking on the new APIType (Web Socket). The appropriate changes would be done in codegen related files to give code. ``` +-------------------------+ @@ -110,23 +110,120 @@ The solution involves providing the users with an option to change ping interval +--------------------------------+ ``` -### Detailed Description + - **Technologies & Tools:** The approach uses the package web_socket_channel(^3.0.1) - **Expected Outcomes:** A clean ui with maximum smoothness satisfying above mentioned solutions. - **Linked PR for POC:** https://github.com/foss42/apidash/pull/555 (The PR is in no way the final product but simply to show the code structure and my approach) ### Abstract 2(SSE Support) -Trying to implement SSE Support into the application. The approach is still similar to +Trying to implement SSE Support into the application. The approach is similar to web_sockets in many ways. ### Abstract 3(GraphQL Enhancement) -Proposing to include graph ql variable support and graphql introspection +Proposing to include graphql variable support and graphql introspection . + +Graphql variable :- Make a json editor pane as an additional tab in request pane for graphql with enviornment support. And then parse it along the body of request. The approach was verified in custom endpoint and in https://rickandmortyapi.com/graphql . A partial implementation of this was done in https://github.com/foss42/apidash/pull/588 with a mistake in ui approach which would be rectified. + + +GraphQL Introspection :- The approach is to use a prebuild introspection query that asks for all the contents necessary that developer would want to know and display the results in GraphQL SDL (Schema Definition Language) . The introspection query asks for all schema details mutation ,subscription, query schema types , directives . Later on we iterate through the received json making the GRAPHQL SDL. +The introspection query would be: +``` +{ + __schema { + queryType { + name + fields { + name + description + args { + name + description + type { + name + kind + } + } + type { + name + kind + } + } + } + mutationType { + name + fields { + name + description + args { + name + description + type { + name + kind + } + } + type { + name + kind + } + } + } + subscriptionType { + name + fields { + name + description + args { + name + description + type { + name + kind + } + } + type { + name + kind + } + } + } + types { + name + kind + description + fields { + name + description + type { + name + kind + } + } + } + directives { + name + description + locations + args { + name + description + type { + name + kind + } + } + } + } +} + +``` +Afterwards if possible i would like to make a package similar to json explorer to render the ui with collapse and expand feauture . This would require some time as SDL is quite different from the standard JSON. +### Abstract 4(URL Encoded Multipart nd File Support) -### Abstract 4(URL Encoded Multipart) From ad5a3f3c86cfef7b97671710d0f6b716ab48a5cb Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:04:24 +0530 Subject: [PATCH 016/188] Update Application_K_Govind_API_Support.md added the timeline --- .../gsoc/Application_K_Govind_API_Support.md | 72 +++++++++++++------ 1 file changed, 49 insertions(+), 23 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 429303771..b31ae4380 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -66,6 +66,8 @@ I would like to introduce some feautures as listed below to enhance the api test ### Abstract 1(Web Socket Implementation) Introducing web socket implementation into the APIDash. Introduce a new client and manager to handle the websocket messages and render them in the ui . The solution involves providing the users with an option to change ping intervals, number of reconnection attempts, interval between reconnection attenpts. All of this would be managed inside the settings. The ui updates would be made using river pod provider specifically for websocket messages for each request ids. All this would be initiated by user clicking on the new APIType (Web Socket). The appropriate changes would be done in codegen related files to give code. +I have tested my approach on custom endpoints https://github.com/Clasherzz/testing/blob/main/websock.js , echo websocket and multiple fast incoming web socket message endpoints related to bit coin and stock prices to ensure robustness. + ``` +-------------------------+ @@ -123,7 +125,12 @@ Trying to implement SSE Support into the application. The approach is similar to ### Abstract 3(GraphQL Enhancement) Proposing to include graphql variable support and graphql introspection . -Graphql variable :- Make a json editor pane as an additional tab in request pane for graphql with enviornment support. And then parse it along the body of request. The approach was verified in custom endpoint and in https://rickandmortyapi.com/graphql . A partial implementation of this was done in https://github.com/foss42/apidash/pull/588 with a mistake in ui approach which would be rectified. +Graphql variable :- +UI Changes: +Make a json editor pane as an additional tab in request pane for graphql with enviornment support. +And then parse it along the body of request. + +The approach was verified in custom endpoint and in https://rickandmortyapi.com/graphql . A partial implementation of this was done in https://github.com/foss42/apidash/pull/588 with a mistake in ui approach which would be rectified. GraphQL Introspection :- The approach is to use a prebuild introspection query that asks for all the contents necessary that developer would want to know and display the results in GraphQL SDL (Schema Definition Language) . The introspection query asks for all schema details mutation ,subscription, query schema types , directives . Later on we iterate through the received json making the GRAPHQL SDL. @@ -221,7 +228,24 @@ The introspection query would be: Afterwards if possible i would like to make a package similar to json explorer to render the ui with collapse and expand feauture . This would require some time as SDL is quite different from the standard JSON. -### Abstract 4(URL Encoded Multipart nd File Support) +### Abstract 4(URL Encoded Multipart and File Support) + +Url Encoded Multipart(Issue #352):- +Ui change: There would be a toggle button in multipart tab that can switch between multipart and urlencoded multipart . +Request model change: An additional content type (application/x-www-form-urlencoded). By default the choice would be url encoded multipart content type and would only change to multipart if toggle button is used or if a file is uploaded and selected to send. +Service level change:- We would make key value pairs as string . This is encoded and passes into the body along with added header . + +File support(Issue #337);- +UI Chnages: Make a combination of drag and droppable and select file ui as a new tab. +Model changes: Add another content type application/octet-stream . +Service changes: Stream the file as bytes + + List fileBytes = await file.readAsBytes(); +Then change it to base64 format: + + String base64String = base64Encode(fileBytes); +We can run this + @@ -236,34 +260,36 @@ Afterwards if possible i would like to make a package similar to json explorer t ### Weekly Timeline #### **Week 1-2:** -- [Task 1] -- [Task 2] -- [Task 3] +- Getting to know about the organization and what mentors wants to say about the work I am about to start. Fix the timings of meetings if there are any. Get to know which time do the maintainers be more active and share the approach and queries with them. +- Changing the design and architecture from feedback. +- Start with the graphql variable and inspect schema feautures. + +#### **Week 3-4:** (Web Socket) +- Coninuing work on graphql feautures and add testing. +- Start working on the initial ui and set update settings providers for the new feautures of Web Socket. +- Add service layer of web sockets and make way for codegen feauture for web socket. +- Work on handling the web socket history. Add testing for the ui and service code. + +#### **Week 5-6:** (SSE) +- Work on handling the web socket history. Add testing for the ui and service code. +- Start working on the initial ui and set update settings providers for the new feautures of SSE. +- Add service layer of web sockets and make way for codegen feauture for SSE. -#### **Week 3-4:** -- [Task 1] -- [Task 2] -- [Task 3] -#### **Week 5-6:** -- [Task 1] -- [Task 2] -- [Task 3] +#### **Week 7-8:** +- Work on handling the SSE history.Add testing for ui and service code. +- Work on UI changes for url encoded multipart and make service layer changes to accomadate that. +- Add testing for the ui changes. -#### **Week 7-8:** -- [Task 1] -- [Task 2] -- [Task 3] #### **Week 9-10:** -- [Task 1] -- [Task 2] -- [Task 3] +- Work on UI changes of File Request and implement the service layer changes. +- Add test files to this. after testing thoroughly on all file types. #### **Week 11-12:** -- [Finalizing & Testing] -- [Documentation] -- [Submission] +- Final polishing done if needed. +- Adding needed docmentations. +- Submitting the final documentation and work. --- From 3459e3129202ee0cdb16980f6321b5d2017aebec Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:19:22 +0530 Subject: [PATCH 017/188] Update Application_K_Govind_API_Support.md --- .../gsoc/Application_K_Govind_API_Support.md | 86 ++++++++++--------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index b31ae4380..61a8fa1bc 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -69,47 +69,51 @@ The solution involves providing the users with an option to change ping interval I have tested my approach on custom endpoints https://github.com/Clasherzz/testing/blob/main/websock.js , echo websocket and multiple fast incoming web socket message endpoints related to bit coin and stock prices to ensure robustness. ``` - - +-------------------------+ - | WebSocket Server | - | - Manages connections | - | - Handles messages | - +-----------+-------------+ - | - v - +------------------------------------+ - | WebSocket Client (web_socket_channel) | - | - Establishes connection | - | - Sends & receives messages | - | - Handles ping & retries | - | - onError → Update Riverpod state | - | - onDone → Update Riverpod state | - +------------------------------------+ - | - v - +-------------------------------+ - | WebSocket Connection Manager | - | - Ping interval handling | - | - Reconnection attempts | - | - Interval between retries | - +-------------------------------+ - | - v - +-----------------------------+ - | Riverpod State Management | - | - Stores all messages | - | - Updates WebSocket Model | - | - Handles UI reactivity | - +-----------------------------+ - | - v - +--------------------------------+ - | Flutter WebSocket UI | - | - Search messages | - | - Clear all/one message | - | - Scroll to top/up option | - | - Dynamic UI per request | - +--------------------------------+ + +--------------------------------+ + | WebSocket Server | + | - Manages connections | + | - Handles messages | + +---------------+--------------+ + | + v + +--------------------------------------+ + | Settings Connection Manager | + | - Ping interval handling | + | - Reconnection attempts | + | - Interval between retries | + +----------------+-------------------+ + | + v + +---------------------------------------------------------------+ + | WebSocket Client (web_socket_channel) | + | - Establishes connection | + | - Sends & receives messages | + | - Handles ping & retries | + | - onError → Updates Riverpod WebSocket Response Model State | + | - onListen → Updates WebSocket Messages Provider | + | - onDone → Updates Riverpod WebSocket Response Model State | + +----------------------+----------------------+----------------+ + | | + | | + +------------------------------------+ +--------------------------------+ + | WebSocket Messages Riverpod | | Riverpod State Management | + | Provider | | - Stores all messages | + | - Stores incoming messages | | - Updates WebSocket Model | + | - Groups messages by request ID | | - Handles UI reactivity | + | - Provides real-time updates | +--------------------------------+ + +------------------------------------+ + | + v + +-----------------------------------------+ + | Flutter WebSocket UI | + | - Search messages | + | - Clear all/one message | + | - Scroll to top/up option | + | - Dynamic UI per request | + +-----------------------------------------+ + + + ``` From 83b0d3db87dae7b77f6a83044c675a902ad4dc6e Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 18:52:23 +0530 Subject: [PATCH 018/188] Update Application_K_Govind_API_Support.md --- .../gsoc/Application_K_Govind_API_Support.md | 80 ++++++++++++++++--- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 61a8fa1bc..97f55453e 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -35,9 +35,10 @@ - Multipart cancellation error and Request cancellation flow Error :- Both of these errors came acorss me while trying to implement the web socket protocol in the app. ### Proud Project/Achievement -- **Project Name:** [Project Title] -- **Why it matters:** [Brief Explanation] -- **Repo/Link:** [Link to Project] +- **Project Name:** EKO is a combination of reselling and recycling platforms platform for electric components which connects the technicians with the common public.The project gave us state level winner title in the prestigious YIP Programme. This was one of the first applications that i have coded. +- **Why it matters:** Upon uploading the IMEI No(if component has it) ,model no and model name. Using gemini api we produce all the components inside that phone ,computer etc(using gemini api).The data is then stored . Technicians can now search the components seperately and can purchase the component they want. Rather than scraping (were valuable components are lost) this is a better way to go forward. The consumers get the maximum value price for each of their working components. Imagine your phone is lagging dueto overuse.The app comes to help you to sell your undamages screen , phone speaker etc. The application also had computer vision to recognize electric components lying around your house to know how much it is worth. +- **Repo/Link:** Frontend: https://github.com/Apollo-Blaze/Ekov1 + Backend: https://github.com/Clasherzz/eko ### Motivating Challenges - I like challenges of two kind, one which demands me to think and implement what i learned. And the other one which makes me learn new things. @@ -65,9 +66,9 @@ I would like to introduce some feautures as listed below to enhance the api test ### Abstract 1(Web Socket Implementation) Introducing web socket implementation into the APIDash. Introduce a new client and manager to handle the websocket messages and render them in the ui . -The solution involves providing the users with an option to change ping intervals, number of reconnection attempts, interval between reconnection attenpts. All of this would be managed inside the settings. The ui updates would be made using river pod provider specifically for websocket messages for each request ids. All this would be initiated by user clicking on the new APIType (Web Socket). The appropriate changes would be done in codegen related files to give code. +The solution involves providing the users with an option to change ping intervals, number of reconnection attempts, interval between reconnection attenpts. All of this would be managed inside the settings. The ui updates would be made using riverpod provider specifically for websocket messages for each request ids. All this would be initiated by user clicking on the new APIType (Web Socket). The appropriate changes would be done in codegen related files to give code. I have tested my approach on custom endpoints https://github.com/Clasherzz/testing/blob/main/websock.js , echo websocket and multiple fast incoming web socket message endpoints related to bit coin and stock prices to ensure robustness. - +Architecture is as shown below: ``` +--------------------------------+ | WebSocket Server | @@ -101,9 +102,9 @@ I have tested my approach on custom endpoints https://github.com/Clasherzz/testi | - Stores incoming messages | | - Updates WebSocket Model | | - Groups messages by request ID | | - Handles UI reactivity | | - Provides real-time updates | +--------------------------------+ - +------------------------------------+ - | - v + +------------------------------------+ | + | | + v | +-----------------------------------------+ | Flutter WebSocket UI | | - Search messages | @@ -122,10 +123,71 @@ I have tested my approach on custom endpoints https://github.com/Clasherzz/testi - **Linked PR for POC:** https://github.com/foss42/apidash/pull/555 (The PR is in no way the final product but simply to show the code structure and my approach) ### Abstract 2(SSE Support) -Trying to implement SSE Support into the application. The approach is similar to web_sockets in many ways. +Trying to implement SSE Support into the application. Using a special provider for incoming frames just like in web socket messages . Now the incoming messages would be parsed and decided if it is comment ,data,event,id,retry. Data is the most important one that is needed to be shown. But we keep an advanced option in settings for dev's who need advanced testing they can turn on it to all other kind of frames. This would be helpful for testers for advanced testing.Additionally the ui would render message perfectly if it is a json. +Architecture is as shown below: +``` + +--------------------------------+ + | SSE Server | + | - Manages connections | + | - Sends event streams | + +---------------+--------------+ + | + v + +--------------------------------------+ + | Settings Connection Manager | + | - Handles retry intervals | + | - Manages reconnections | + | - Configures event listeners | + | - (Optional) Show Advanced Options| + | - Comments | + | - ID | + | - Retry Interval | + +----------------+-------------------+ + | + v + +-------------------------------------------------------------------+ + | SSE Client (http package / eventsource) | + | - Establishes connection | + | - Listens for events | + | - Handles automatic reconnections | + | - onError → Updates Riverpod SSE Response Model State | + | → Saves event history in HistoryModel Provider | + | - onEvent → Updates SSE Messages Provider | + | - onDone → Updates Riverpod SSE Response Model State | + | → Saves event history in HistoryModel Provider | + | - **HTTP Interceptor** (Inside SSE Client) | + +----------------------+----------------------+--------------------+ + | | + | | + +------------------------------------+ +--------------------------------+ + | SSE Messages Riverpod Provider | | Riverpod State Management | + | - Stores incoming messages | | - Stores all messages | + | - Groups messages by event ID | | - Updates SSE Model | + | - Provides real-time updates | | - Handles UI reactivity | + +------------------------------------+ +--------------------------------+ + | | + | | + +------------------------------------------------+ + | HistoryModel Provider | + | - Saves event history upon errors (`onError`)| + | - Saves event history when done (`onDone`) | + | - Stores event metadata & timestamps | + | - Persists disconnected session logs | + +------------------------------------------------+ + | + v + +-----------------------------------------+ + | Flutter SSE UI | + | - Search messages | + | - Clear all/one message | + | - Scroll to top/up option | + | - Dynamic UI per event type | + | - View Event History | + +-----------------------------------------+ +``` ### Abstract 3(GraphQL Enhancement) Proposing to include graphql variable support and graphql introspection . From c83b6029a6e85f5fbeec210df592ce2886a19ea2 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 18:53:27 +0530 Subject: [PATCH 019/188] Update Application_K_Govind_API_Support.md --- doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 97f55453e..95ef2b9ca 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -302,7 +302,7 @@ Request model change: An additional content type (application/x-www-form-urlenco Service level change:- We would make key value pairs as string . This is encoded and passes into the body along with added header . File support(Issue #337);- -UI Chnages: Make a combination of drag and droppable and select file ui as a new tab. +UI Changes: Make a combination of drag and droppable and select file ui as a new tab. Model changes: Add another content type application/octet-stream . Service changes: Stream the file as bytes From 5110097b561f3e7b1c77494d76a897c7463f3018 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:11:32 +0530 Subject: [PATCH 020/188] Update Application_K_Govind_API_Support.md --- .../2025/gsoc/Application_K_Govind_API_Support.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 95ef2b9ca..127d5e729 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -11,7 +11,8 @@ - LinkedIn: https://www.linkedin.com/in/k-govind-226529270/ - **Time Zone:** Thrissur, KL · UTC+5:30 (IST) -- **Resume:** [Publicly accessible link to resume] +- **Resume:** https://drive.google.com/drive/folders/1nHlF1_uSjGRc-DruOZaVZeZT-MF8FBVh?usp=sharing +- --- @@ -45,7 +46,7 @@ ### Availability for GSoC - **Will you work full-time on GSoC?** No -- **If not, what else will you be working on?** I currently have to meet with my study requirements and have to continue my Btech course. My initial plan is to contribute 2 hours per week days and 5 hours in weekend days which i beleive is enough for achieving the proposals goal. +- **If not, what else will you be working on?** I currently have to meet with my study requirements and have to continue my Btech course. My initial plan is to contribute 2 hours per week day and 5 hours in weekend days which i beleive is enough for achieving the proposals goal. ### Communication & Sync-ups @@ -61,7 +62,7 @@ ### API Testing Support -I would like to introduce some feautures as listed below to enhance the api testing support of the application. This is part of a single proposal. +I would like to introduce some feautures as listed below to enhance the api testing support of the application.All this abstracts are part of a single proposal. ### Abstract 1(Web Socket Implementation) From e05797f42a7c2ef2934294d36fdc5a980b63d844 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:18:35 +0530 Subject: [PATCH 021/188] Rename Screenshot 2025-04-01 031655.png to SSE(1).png --- .../images/Screenshot 2025-04-01 031655.png | Bin 92356 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 031655.png diff --git a/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 031655.png b/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 031655.png deleted file mode 100644 index aa27edf40359a9a597c0765bef7bbf95dd8ed758..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92356 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�P4Uh~HNS6*LfpGS|GJpL^e+@juBE^cs9po8H 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 Date: Tue, 1 Apr 2025 20:18:51 +0530 Subject: [PATCH 022/188] Update Application_K_Govind_API_Support.md --- doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 127d5e729..e718af458 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -189,6 +189,12 @@ Architecture is as shown below: ``` +POC Link: +Images of POC: +![SSE](/images/SSE(1).png) + + + ### Abstract 3(GraphQL Enhancement) Proposing to include graphql variable support and graphql introspection . From 7addbf8aae9435a07980a250edfc2109ba76c83d Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:21:54 +0530 Subject: [PATCH 023/188] Rename Screenshot 2025-04-01 031809.png to SSE(2).png --- .../images/Screenshot 2025-04-01 031809.png | Bin 92937 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 031809.png diff --git a/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 031809.png b/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 031809.png deleted file mode 100644 index ec5b20f949725def3b63fd47688aa1c3a6c9f865..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92937 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

baN%6-$^FCbns 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 z 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

-{SPwJ+plR~9C&1Yx0sjgH zq-U=ew(aT;n8>sOEaF(VZ|6z<9bpk=Fv8lnn1HDO*6ugfA|9gqOfs>Yp`doT8Tex1 zlJi*X-$^+}q}+`$L6yLA-W)HFcDrC5!3xUKa?1+WiSl2=1ZJJQx)1LCyNUWU(!;QB z@6y6Jr2_+l3&Mm%sP0H0zRFxvuR!sM32#_LTKRDiU9|46u zUYC^++}5`4&TydU1y@jrkTpi=q1Dc3B)LT_EaIF4HEwb zh2pC+NrK|IZ&<0+NR#hvpOypKm^-s%q8P-96>< zUDV$&xsqCdC2~*v@Ba|#fr~U^SW6wBo&|xjHM)luBsi`S5rN?);XHg*0YO0}AWD$X ze4npppf4e77j-cB_c^k7zCa+($UzJ!5o><_+{OlsA&TMQ!_c|Z#=4qLjK3eV^PV^V zvK2EqGTfc9|Ge~6W8Hs$@xSIg{^{etg3|vhLGu5-vGl?f%AlZa%1%nm@C|TDh|7xQ Ii)efO53*D_nE(I) literal 0 HcmV?d00001 From 63c72ed528b35bb0f5ddba78701ea78c70a900a8 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:25:33 +0530 Subject: [PATCH 026/188] Rename Screenshot 2025-04-01 095628.png to websocket(1).png --- .../images/Screenshot 2025-04-01 095628.png | Bin 115056 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 095628.png diff --git a/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 095628.png b/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 095628.png deleted file mode 100644 index 94a371933c43e46644c2c551138c4c691bf4a887..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115056 zcmcF~1y>x~)-4d+H4rpFaCdhL5}e@f?ry=I#@*dLxVvj`cW>P7^*QI>bH6|E#wh5~ z)TmvXs=e2mYtFgCz`&3szl$n@fk9G&fkBAFLWBO2D&BAg`T%!Sln@51 zn#4Z_eStI;`XK}cRtrFQ)rSIohqL>x?g$2k-1pZH_>gUh5g6DqRrL;X=Atv>-qO0rEDG&&Ru=d*l0;?oL+bBLV(jUGC2Eu})HxsQN zoL5{-0>}9r52K?(W8} z_i`R;`Tv~_ci2$y6aL@e2{+O3nE#F(o|oaOoFo6g!S~zqA%gzztqJ~r-r9=S%m13L zYYGu>Y}4XbM<&7b^>u7=wyC5-LRm;<6dgW3eoS^Y;Oxw5FWvXwNn<05$z(`jP+Far z0a${Qi?NwmSYl&S_5Z#qKcCcMzJko1jhPzV-JQCG>OYq`=ywM%y|%~i!=CT_e|mTc z7J-$ObgjB`XZ_4q4912ADJ|qXA@7NAzxJf z^R@~p8!$AQEW(O@;Px#pO3a$IS#7jqWpcZ8m)o|7H8eO$GDOG3tdtNDMo&+p{wYEG z1|cHZKRCEOz1_Fe<|^OAO+;jC+x~og^LV*C6*-}(N6KoufLK~un!;)iQI+DxhK`Ow z*1<@y(j;=4M7MQ#VJb;QbbS0}so5N3p>p%rbPTFY2N3u5@unKbX+)4#vlgP>WD0Gy zu%bjzUkEDv#1FJ$&d$z7GdMHLT-j^kv6R87er8Dj-+4tuAP{rYAC`FPfQH> zIv(gBoPE$zB?7v-h_c*wm}J`aU8Lcs3mqx0%5eA`I0vJ%LmvYn$I!oXw*|PlHESwP zW+c+65)-k{R_@&}boH9*dPOxfu#U~vRiL$~ogt{;tqirF=a+?{BAM}(7aPVC<_Y%S zHE%Dn%gw{1iHm_Cr>?GUs*+J735f~LOPj*Ua36lcCi+K)l8XoSb~g{u7D z&?lup2`e8{qEcV;&&dlFb?;9-Ns9qxE$Q_i?c z1U%+vY7+TB)+^u`K#xzILc~c47)whlUpXjX<^*1dlExp=xt7T!==THaYkw#D@C2g# z-pVa05xNT5z`z<34=nJy5mw7zURgGa%iicD8hd|&FF$ATLzyg#_LZfjJaID-8ILXR z$o6-tTSD(o{(ZRmL#;u>6C-sM&R4xN+-GNJw2u2jGs{>H?L642<|CoJHQLOtORG9U zZ%NvlZZe{oJx<6{jtz_$dFS-^~a+W{h0mtAUt( zjdR!PR{8eMi607Kc4+oSD@uCul&FnMk(&vt**w9WrjuW?E)rxAt6<*(AFU?D?-RD< zx7KENDUX}U za7v_m1kdM@q01Rh+p%eW;|}rvrqYDEUIkuZVGGTJNg`xq}B;Yrh9d|Xei-n&gps10DZG{Z^l+FZnSl1IevaN zo7L5f^fsId-fCwAABnZvz9c%iZXh(0hO>xWCb?SIBg5HhD=ykgQ{M*8km|-Aw8kAy z_(3L2_?5TWBD75VgSC|oJBB$GT*`AlwjJxK^N|2@CbSOmi}{=*C$g1pRKcTR5~;GT z+3D))a`MR01+Q72+jmid6=O-N?u{O_4)Kj&{Y&5fAle3>mmlP~FOJ2;#|OP>{?wb^ zgPED>C|I>U{2sOvnRk3KnL;Ai1K0sn;?3A2}Wu0ux(q;i(hWX3#OfKZJ`mb%X- z;!uV*PBAjl2A`&TS2247mBmotJrLKfdl{NvD%G1`CQ_N#@2|6G!})GkB%+H)j22R_9}h`>W>|t! zLE%0hWkZi-d`MH*2qIL|wbKC`AESx)1Fk_n@=mLSK;NC5H!0n_%*WF@%+=eetNutN z!`8VjMycf0@aT7u?|nbfKS*+Zaz+C;$=9)bUbdL(-$e{+*pH{D>K;yHj|t)OZQTi7 zJKnP0;t?~SzX9ELbsKG!ois@?rn(O((6eU)?>HHF0o$ z7+iVk3NsZFD=C42YEzLf$RW;5kB*PfstL1K&Kl_$jF^&40YUnu;DQW>p+GOj-w0LK?xJu|xkM^J&ZO0fKOO&mi zwRx$4iyL5h$f>#6Gw&s)d>gnQqPyvfS$^>PP25A)<@3SEJvfBLyhwUhUyc5Z?rff1 zu0fA2(atHh?O^egmg)UflkvqoUMFPXdfySLr@h&sM*;cZ=tW-}2MHJdO5H7Fy9AA? z55)$WP%e@fZ+HZE;M$p0{5xHz?;Wz2A#k&IQ=tE(MS!^3Mv1evSsw_n4TnXYtwCa^ zdjW+vGQc27f)L|YJ+IeZXe5FbIX1<=;gWf_!5qWrm63;lIdxh5%7v3ju8TT_s8J{ihTYPq? zQ|vdge7B`E8RwC}w&~grP9u|okrG0<8Hqwon97F<8WZB3Ze%NsXA}=WS};=+FQB%m z{Z|Y$9UW*HT>|&9z8DmJ`}}sr6b)QANaX>~7BUepgC(E}V@i3Nk04Q$mzRo;=#6G` zoYC)gi%hv^4c7iBaKZ>*YQi!fxmv2XFXbK5KE);`rGpD`slu>Li5uqlxE?*4nJ!eK zxINmyVzYz>K|w;yo{Y@Q?d@&izGEF)jOt`VAxmuBf1XKU`?TZo#5__~R@T4*TltBo z?deZCjqfOab3eE!kK(qscPMY#X5X%6tH?QH_B}^i3xC+$yAO}oGq|M2=g&#%Tp{88 z;rL+4{;EjU>c+SLx%;W+B@heuC_U*dASWic1Z_b+QtmV%J~M3p!-q83SZr&eTn!rl zPJVNZZ@qsr26Ua&+dO0X3Drl~6V{BB+T?H75QvwSFLyJY|`!^SDy zm`U0T5zSQ4@euNPoOc4`4a%-|0`$#kcw+hGE<;y1Hq@uCq{M5NtF575r##%8Bs@H; zd)fHQ_Xa7jo0h8Bsx81-VB+5es+4m85>o}yETMh|8;|GB>Yop4?&a0&C;m~EE4Pa3 z&*Rz4_FUW~*3}RUXW@%4%_!^+3MpGY1<2M`{W~Y=D)u`q$Y+~!xD7YgH&PkwqnRa65uO0x~DgV^pS4BBr7vg+dav{PIH47%5+o!&38 z85tyF;f2Gy^;eg0WeI8RmD&TE^e?(TG|PYzh4rdcnBa|e8UpQ%4ezX=X@o=X~D1V*)MlrZ`^YJ5* z9#-lp_v9ymMi0x64XSb&F)@25C(+8Z-`}dm#l^CIqHYhdefb_8c`U}Q;0y(r7Q>LjWGn*blb z(8dzkLb4Lz>m#K6+b5EBzEhu!ZAR!Mrc$NozkwM3MnNe*rH69Q7TR9 z1xkp4V;nZ2W&6Hcd)QsrhS|BwdrGwJnR#diFW%a^UI^K~kr}p9ie3jV9UoHtM|#A( z<$^|17UB(IP-L&Nx!+F2p>4u~Ei#)1jtQZMT+Lk@iLz*p-Wq}noShxFw{Lo6gOMY! zwD_DbCjl{kikmBI7)Axn=k*aSaKQQ4O5vxU{|=-;#V!7Kxh}hQb82`5Tx?V9eu{qG z2H~i`m&`oT!j;){$b2cFxf$mh*`PFE12dxepcw*kRg~cni3~xVQT6s{Aar@QFDeNs zvg#-srdz^N-`E(84UjOD(*t&Ske}A_4e-E0$p|}?2T+l=_h+?S!4U~TY#dJsG%SOUJTw7#&c3-sJ4(WOCPb4P{G^`nrk{6p+XdG zNB%A81Y&KA$tq5vQEK_ifok=7a%3W|3g{LnUjbh<%0Cf*qp*Y%SSUy$T>(yRPGWNM zvRSb}1fd*C#5PPwNJvr^non0(wdgE0tV<;WRWp(!lJgD7nSjYm?p)Vu!lb8gd&+ez zqH}~~8$3VR)X+&M^uvr9HkAA**8yQ_HGM6>k93aRp=@6!M`H}ivIYh|6}mD5L(rL_hOv8Ks9j zF@YzsQOkBdQ5>p8YgYX1RNUW!0sUXnS`B6JR$;}Q#G4F`J}O9T!*an;NH?+n2^{`+ z*hECSH;X@cDH_1b&MtMx`iH`Ec27=pEVN_RQOoY$*ot+&)V?S>o{Pnl9R5Tdo}FSs zV9T6~O;Ca(Jjddc_=^sK6QEq?;u;?QjxE+8_=WR<5f<%}fx`|$k$Ck#44Oluo+=c! zDhGZ*R&xM9s(?$y$_0zR2n4M9b) zh*`Bb$df^agVHuBP5D{R8z;VzY|lLRNbF$B60>556}8wQf^mErFUwIsE5rm*QC-vtm_L-C+qdi z4eA&B?+~UxgF}Q=g4q?-K|n@G*yV~4ZT&TGTR%IhWya6VMFQj1qk;X*`o`W|zQ8yt zUUKR;2628x0!L`qe{0#mDC>c!roa@~v-Ng-P|6%yP;oR`>GU8ZK>_yH_O;pTy{zmW zXZ!`C3%(6V9+bmQ%1-XoP1zeJmZ1c}qtRU7#HJ^!GB(VrXHb5}bV931+|6Rp_wT!L zbD*S(S5;C}+RF$JAktA)RV^qg(omF@Z*#F;j?Ry+`B06*WfM(34Do-d*6re-ot+Iw zB6okWcKzBd0LJ5b-cW$qjmCWPr!)Xph;3{Cob2TwWyTakXS0$+E}kjD(7;f-9s4KF zZRnxOGI&Y_@5dTK-Nh#ulnf{OWI!AK`AXwZa1mc+?U|zq&^g-3=Xk#B zf}GH7=v`qem*HcWt8=;9+u!dK_zG+8;9b1n=#B_|zUPjuigJH-B_uD8%FN7c#C5vb z+1crUWZJf&Nw~JYHeDcTIHoo7`b8a~i>Q(=-w~5-_1yY;W`RdXhS25CY%9y3={K^p zDj?5}G2p^5&Cs|4hWoicfQeid=4K)~lc&~p^F7YGjHImuJx)>9h5X)DGhn~Ivk!i( z;kt_mt=5;v#a7FMcY?c+jN*tf3!9W&>Bi8ZstCc4hC-%)7>#t_P`Gw?y=ilxxi)!> z`Llm@`)wH}>HrqHY;<%nTm`XCLXZ!hJB6q z9NOz`fWmC9b)HMLkL!AhL)Y6d^^=b88yl@|tuTC}s*aC}xB~GjY+GxV52XQ(*5a~N zHMlR0`g1M9TPYWj=olc>|lEY^qTJyw{pg8I_R%620UaNv=qN;n`m z%Ba<9*lx|`u%mahzHlswnyfGB~!}FPm_v+{q^V{`}>*WnfOe|0;0BMQ7R;-wtI)70p~WignQ2UZ?{Sy)HkZD}^VM&&SKbdB099RY zsE>&!l!(s^!XDzSn3$hnbo@`NGW~V&IDjzfPXgwRD`KnDv40YsCNVWL`uqEP zUAFV-V!n9PS6;i#PtE;nNC&2Fw&g$G4{gnyZ2o%tEzSDt zBV(&Dsp!6yUUv`+PyrT@yM8d)jCZ^o_&lG3{xm4lYS(?|1q%-tqREB{>Zs7|()kqO z{j+AGN}H$FtcSPMHBnrhmvZUxW(}b_%v2<^b^CF@_yPp~zHM~%^svn|s3Xv7aLV!i zk`A~psE;ULzW?Lw{#JaGW44q-d=j)MvdVs55*1D)Sf~E-vX0KHM!#3bR{X zfqjn(trm!}2~lu=C@{@?s)M(?#q6a{LmnNUW2~pgO6@ji!@)=~8pDK}-ARCD{NNA9 zey`Avrw3a!I&vL%tXHqMz5Mk?Yo1rGH@We5+eWYUu@|O|I_vaMjIpHZZfn<#C{tax z|%O)Ig~ve9(TRK^|q@i0pEO)7Z97$8r5QZGcm+LB=B*~ zLP8Rq=YI3~^5&s$@CRN@a(c)Y->L50z7Qkb{-Gf}+xC0%pVjJ7Eslp<(W^kz+JFt( zGs2ry+pciE9;v{;133JtvW*SlzKx)9X7Elwf$K&{a1?HSY-#-YdK=6|`&}8%eInFp z5S?bDxS(E))A<%ADymuJF1wuz*G(>c<Z{HjSeN~l%+$3cArTolWe}BKhA~18d zMpR@K{?tL~$GZE*t9uI9jjnl(#YP1e#Oqt3Q1soo@5dVq2sw&?L()$^I7BP$Xcx=r)*9be13=iKX;~KB_e*I&W_=N#iaE7g!cCKNiBcL4S8DK{X+v0 zJJR69-&iq0atJfgiuT|o@K+L@hQUqJ#L;X^1STy3j|(~g{%Y9HJ{AQTYu9)zSs&Qm zLgF-?<8$9)TI+s89*NHdFUR{{(BFc?@5Px!t8P_9rq*fD^7Mv9`L zt<|aYEWjIcOxnr{f1yf~VU_&bH-puCf&uFFM6n90NR!F5FqLxEFbcS8+tWsCMSZmv z3o>*JjN@yvQ$lXH?;4biHtQt*0a&`a0xh~ALR`F9Hd7||Ahc$h@%C&Pq3iuVVlDr8 z?t>XC#AxjE-zY|FZ7DQF$VX=x&w)}F5_-P-9Q2OFqteq(Bnca>SHn|3WF=&roC+6` z&GuHYnP)4P(i}OJh;ylXEl0;k`(KFK0acl8QXR`uc2Rm){%UnjVXWiU9C!WXKhlQl zL|{;)R>Bi$3XmtAVQx=tftej{+S38-CE`eE2GkMfJTB@!iIHvIewT@A!a3u!dKF;} zw@&!cp>f`fCFBDkv$#mE7aK~K`S|LhAQ`shm%h;^pXdE|RV3WUmx-a}z%b6jfo%M%}o{6S4Wz^RhE4ga$UWjNY@(!XnrMRp0ht2I{(=2pD z@1w2US&E61vca&`+L+(2@|73}VBsWMU7TL+9TQI+F*mojx2!}SHwsN| z{K_dLM3OwfsW@>C!B(p9;t;V=N`}URw79sq%WVmwQKy?q4*jdRfjB~ z`rjE+!3?7AU15Pc+D#U6zI*+FKTtd>Eu~epiZ!ME<=!$hJe;?3)jKg{oaBB-RuVqJN@UL zo}Qg|2|xXTa4fU65;Wd5zOB8ZZ+=Y6);(UPGwtq<$7$0Q>W<4v7~1nGgWm^rbaX-^ z0P${^-X0G^>#fdcxaib#x5sm}7V}bC?)#$Z*<&CC#q?O$qnN|o2@T>MbVLtjGiN%z zJ;%Oe%YgXV%lB1~q7ZT5cD{djH7Y%oD*QCguT3kc1`dmaB(~1&tWf^bdW@nIa{RfS z;?_T=JGY$+`m^9GBMdb3$eo&`j8uXOCp`SFHb|Nb8?JX**NDX9nOp;KAEf-=%cA*v zBh=W$rNu#dzSRA;dwG1`-hd$#>+nD-h^}=Ua%sOgpaB)A1}pN}>453S=-P_yMQ1_OrGN;k_l--cxo0`Q^aj7YG?kMC4 zZ?aj8Od+?R@vBUL5xQr6_M~>J-utUNr`^^sFSFGK^N6mZ53+T>C5Ffi6#Sn~M=``^ ze@d8sR_oB|U`IzIqs!%q-5fMObb7|>@iWWuJtf@!ZZql@RIuuLex`Y%RH|uyDoUUE zZOIX)S?e)@`^BbSwR)rZIvnmGe*;gm(YmKZA>TMKCLc}0em*h2#^~4p^r{mtZ?DmX z@+l)_@d(|pn`Qs=)uxDHruXZsos*&6>lCL` z&2}TWET0o|Eth3IC$mkR)+jrHx0kyiNJoJs-`KL(jFyiPmY;tW%lkXQ!0W0in_)`w z5<>+e%&49|vB;$+TNEANJ1Eh#Ruy%9zG7Y>W~vOQ(+z0@xf*1X%vqT?$NLK3QOt%q zS|R3TI+x3sZM8Z#a1)sif^-h+{)Gv=TJv2GS{CO)cSqCo^Y}5R$qoL_;qhTXm-|w1 zUJ!agi2I_*1IcCqX*iP?RF_e^5{yU~E*61pusawP=Q2juWFYtfj;`q+s%y43MuMX`&#m|V^%@hxc`_zu34)*Ucnr0 zLwlJ}=~+1kZ_b<6t@z(>L?4GI_`GvPv3UnwOA=7J{ze3&T)&)KaeIE--wyXEo`!92 z-`Vf0EPh}7W83wvg{wUJDL@B0-H?Fiv;K*6q31XGoTJ%7yk``ABS|W~iL}HNz8l-C zIyxkH2%mSM&`3@#gN}veWhjRB+u+bI>guUO)~fJG7hMnJAokHzl8eu(8vzc9 z(*7!s`GE*KHwa|Pji)=63+h^n-mFZ>+2;dSM)i(%(_ZnGCSyVR6wT@ zkN6aL3g}6q38@=BVOnY+;XfgHk4=SP43d5;Xfy_{3AgQ+S9ggx$wZE;iso=yGEtgc z0`NS!I^TeJoay77)+yob0ViK05hqwuyD(rptvZ)cchj^^u=5n=keU9R9LHWTug zC@Zosm=46tgz_^GQhb+*BHUxay~1A@72QfAaWJt-3kUvV!zxYN!}Fw^!@2e|osaNu zRB<BPux`J;}~>`2R?Pj^Wj{i$QeDC*!erkIGdKzIuL^Iad%?K-WMvD=M7#N@*jAsTQ zpYw#q^vu9Xd2XkKd3$@2sjV-2C}0+HrfkN^^z0t(Q5y88c6(K1{8~4=_5M4K6}I*~ zBkW4abHXAcGdZ`jl#}YnENK|HfzbNbxuuW;6OE({_zRXcEnQA$vjrc7?~;-OC<1Q| zvK{4Ax36Z=Ew-CJsmYGDwM-99F%F92lP4JZrB={k40%Hky8g=Q)7kfl8Qbf69m%=On)NbxC@$^3~R0*9gm0EUA6js9*`g`aTzEPO(=9ItBs0gRk#8Jg5dGJfDMGd z3T=flmPn)?8^Wxt4fHlYCMq?e0=^%+7c%6HR&D7XCK|YHu#VXS)vBo&0YB>^U^+y` z=6SZ0EEhe>Q~fUso>(m>VmTFNjkg#~fyg<5snJ=kx?>qVn|FBVTD3Kxymn>Ij6 z{18jtt+r%HgD<(aU=X&u4^E*)AxgK zL=eDxO_}Ap&2@Xaih_j49v5G@g=)-28FPHf@3{09}TTUyN#Xy zex?{bVm!0^ObY5RB|R1vaEFGjyywCQ5vM=PyXYPxdr@E9)F z6{fIxE=Urh2L%|-!tU`t=;CmoFd*W(e#)4TR>p5J@pb%oMOTo3UsHn@u@8Q;#?kzB zsj&=mQb*i(H72)dxb_&PR__6I4_wx^z)qN(sZ~;iD`<9`@sb`LfU540>+)khxCy?q zq${0A;o#s{N=xSCB3q7A6jTS{D4&uqILsUrs`rlMPi3vW7l>UvUKd{?4-ML3{wb#1 z)S~Pu2I(E`0^>_hFE8w+8z694i0+Y*N&GWtb~Em@!!fxA*}2hXYavp8P>C9u6k+3Z zfBE7-fK~cZ+j)L|PUkGK5`i5ouV0nBC4lfWD0?aPP?ZS+)hyC0sY0IKBePgbzP}nT z!y>=i`>)3>*-26S%B%ev(QABYg^7auZuP}Pe7j#TftT|`p>&6!9#^p)tV-axOmYb{ z#AiC4Z^6)WDGFvewa&|9W74w~)Y$8pCGRuo{;Bc7aDcwM7Ct~vk|{lnm*FUhd~`SpEO&TOvn zQxcta;83>LXWzFcJa%g>fHX8xPditAtd`$bPEzsc#9IHZ51{m44_quqGQPTRqr=na zb^k=l7#1^-1S+6I5hveOUW|Ws9&1Cu$F^?bWsqJMTeODs?=6r(B443*%6jUJ&0?^m z(fXK8RpHpyhQ%rs@m+|tPNUD$@JZ6GnpWWL?R7d&%#qc6O}@1$0cPXooX6E*?4WYV z_wtuy?l_rm#YaGm+p%>T&pv-bZpmyL^23Sk}MG;3=M+ zS?Go|YJJN56g`#xk^1!K4=bEei^IGHhzP_9law4DEkH6LZG^ElB)ON6f+r!(ud#fv zsnBS_{TR;(0+EI!8%rK*0v|)`pJg0Sl_;jfEHuiNj=f}!h3}pHLC8-zP$UEySA=hY z+4Y_>LQqH%l!;<90-+~X`esJNW|ve9_x-=5yqy*g!ZLIUAn}PVy%~ITcI<=?giP=Y zF6wX~-5kETYUO~?9csS!P_%VuWT>E~Vadbss%|?Mx9d5*_-U>P%w@L-AR;y^MGx_+ z!*je7zO`IFXUk(|M#0Y6aj1l8x7KAsO3Ql!$>c**OfLt_z}A!jnwJIc#r#L72Xz{| zrC+`TAV;OV5f-^BXd<0GM(|;G7yK|pkX9KLZRNoIueHsd3g}l zM1Gd0D#fbGv@v67$sPmMnN|@J8AKtSt^5{=9(p-7Jh1K9B}6lA_x7@1HtwtOS~fsz zcVdJ_A`H$r!*{H@3Jv9?-k@qXjju|jJ{*6QulD(L^;x~qiK$4(mu1EIC@|&7@Sspq z{CtI&S{~Ey^y-sz56~dht;6^ominLEE=8>7Yck%ubCNpmGFmIE&YbP+m8FDiU|?wU zeKsnc!*HNXMXkXI4iv)tgCaf~_T-7FH5x!I*e-nq87yikjc_&h`|pe1y9-GKo3vgP;ocsyiV<1oM+cB+7TW^;2aaWV3wwDiB8 z1gYM^*P*Sv>vGf#9{16)vECN_w~ z%0k%;Ow6tj(a%%A>X_T_(v25{Z_;s%%T3KoFJi5Q^nxXFDXDxghe?D z)<$3#x4G}E&V}T@mh4`Ic$OL;AJG?rEaeIv4le;9OiDp0>~TXoqZYO&rL91ootqOC z)k{Wm$yTfNqN=My==YDykq0ep5j3*S*B!%SabfJtx;pn8ai^mt$;pSKSklm_Fn)UO zQ0&oBV`hK2HXFv1nrz))Zdz&=<_ZjDyemmI|?{NAGmHuzq7Z`PJ+K|c0~C4z7N+GxIsWbFg>N6 zUTyGHkAy-5uG1amUjD&A+1?(?8r+=wh}lYYU3&v-@9?_3OU@=?q5@adWx*mO)X|F> z0g(~XE&rtvBZY@PtVl2}UQx8~*ZO0WeNCzV~fNE1_ z3_T!lT?cqxw2JZ4XZhS4J8oouKMTbxS|`s>meM+`8rVB8uBj+nD#~f zG#;gqZ+3UnKTCkw0&WKgbh%@|JXrmvr6OWQe?qPBtu!w<;-$(kdDBklJ;kV!K+^+J zJj^Qt51Za}21Ca+z^-QRkvjh`k~CiQV!oQSMOa1s-q`h3iFLMpcTyX2D#dkB@ch2W z98xpk+WI&R2Tl*_|J{~_B-8wcyKFEfdaADldj-s^bPWp1IgA-3@^0=UjM-#8={k(# zl%GN{5i>PZauqz^8Z^nG`)L^7QDS4!EHZxD8?71R;RDX~3rQfL0@9x!A48XVa$PFT z2>;t|R9Hu~R&us;MnbAVPDU11sf|l5uLfcPARl4Ao~ZqS((h*qU|jh0;m{8#qb;I@ znu>r>7Z(qf38JbN6q=u%nnJ0yuifFw&HG2l0fp&9_!agHGhK>rzP+~F9M^v}Zq ztk29`Wr0j543Lj8EX=cHYDM7~Dn#%`E{!fb$j;=KBB(exYG%sVSQLLP_`eP=*J_4lb^z0=@iy z62)KhNNa!PEif&RR=vLt$Hex=Mxnt!nd@5@@o`kQ(W!dFpkJgJEs zUG8rW208u{Unv-j;TWvcq2N6XAQxc=w@E|j{@Vru8UlMTPu?#NG2xp$rIbn@F9ejX zPt|{|{ZhmQ+v<4P(&X7ve*3QbI~Rux;IAqws2nxQC`+sdQ2?ThWo=aTZeKZAJ_+0FKKd?^t*Nx86n?{J@lhU{}~TQaFY@goQV7iz#` z_6&k~z7H&-VPHgo)S=J_Npq)*8JL%Q;GmKmZ{(jp3I6>KpouFI$p70|Cl#X2<#p;B z93RqEYsa?HpBfLyh*a)B*YT_PJ;yU@&yVj72mC`!Y&!To%gF7&f+B(LE!(y%wOZ<1I zij}&^XI#SC6zue?_fGaeb#4=UYaD73{y$xTg)91pTaXVq9!Xq7bQ8h6dC1}HSEjczPW+x>gF!yZxTLVf0yOfkbp*uQ9Q&29%6f0%s^njk+^K5%&K->ZoUx#1`*lS z*=rJ=fFOOSm@7wWyp428)gQeju@sXEpx4rJS;MrSU9T#Xs-7wz*sMS6w&uC(+Gl|%Znrt1w0(LG#Q^H^I+{o75%Np=UH*9jkFC35M74Wlc1eLM#bLf@9^sz;uxO@W z^v@cBjM%{Pz<{#enpEbQ)j6#uRk-4M4wFho&wvvy7Z7N}>2kJ2)#K#l2gSFPCM(0uG zmO;lbPc%7lc(dyE*D_-uq)gW#(DQ050A+hY)20stz-O=JMGTVMdple=O;gLYGBPuy z@8tiAKxG{4>@GE~yx!VnBp}mH==WOtRb<|WgBQ1#xoXkKE}LF8P!J#?V?&x@s=d6r z??sBQ>i*QU@yl`$AU=WXcgb=$Po)~2*gzwW#O92x>14Br>$>Si2W;QQWvwDYhJo>P zx>!P1)#Zn8(s9KCc9_F^I3K(5zLnLI+6~>Q?rx+P=Sjb?JJ1sr`SAKY>aDqsDMu1` zP_4=E#qIl!6@gSR9*;}16h;qD4OVkX5;3*&v?Q7`NSThlnF)#aEbFrjt+ni$SAJ5f z@qh|gucbF=t?=#~T(i|ikQ?MIh_K^1!v!85UR-offmVYNn~RGpi7%1hr&daf*%>H$ z2MwkahZOINq$uX0iS~sJ+E`eWk?oJ;a2u5dYXfzA!?-xyVBb-QM>sw;lovxRVW3^a9eR;SLFmK2}BNaE;2@F%~@c11mtBB)+ zU}jXM-SpXVvFdlh=kDG5`TGms_d$>qV_)YoZ3xf>k&K8R>SDc`3TPsY1|jG%vxd9l zbr{gz5W{up@h{^jZi0y)p3W{+CC2FCHk_< z=DRm@9V)^v`ph=Hy-|Tl2{s+5MndH8T5`&H?|; zD+vjyQQN{GYVpiUldS-&%@XVj3O?i5oB~5TVK5Q_v@L^eOQ*~E%Gi^7wh&0N{qgK( z`1L_zu4uxJl>~x{YJ>zDB+`NZWp)86e_K0e{(Tb2$((tSSQ`vkeV6*A)A^pRj#J+D zN7F)2IyarVAmtblpHaS@5SfsZ5&{uPrwG(oB7FT;n2aoZV=_@|`B1exR?Rro)-~cf zrb?Uq(&HMK*Uueq+WB~mP@&eTm36$`7qZVQ`TZPq&3!2Ix*bF$Gnr2M#>uAW7VB_8 zfx=5@WMo47&?_MpR#?}^J6`$EYG!vH@LdKX07weBDLA!RYi8nhISS17c~_`%{WCnX z)M{OhvyX|JA{T;05DjYJ1yS$$mdRBI%aP(BwS!EKkk@VN+jiKH_fc-h)kKr-*wfAP z^X)OIJM*`1qW!1`$sObXx70(<^Jl#2QYF*CG~Qmzv!z-Lkh8<#Rp;(xUXvHLHT`f9 zh_e=bD4DBwzQX1}2a-M|Kt30)zxwe22HSVp+vFVem0GJsB~_c^)-kBI=;_@rXEYfue6-zG z)bgF*hrPTJ*{d~Ea3sS#UZah~&0rK`jjJojdlHyVHI>?FbMrG2pA~ficX4QD z*wAdgz0}R|Tgvmp#G)+gT?*~D;(|10)=$pDmc*+Gw$iGVeSV}8u{yzM%wrH2c0V;B z>f#LH(MMrVPia@>q+?I6-S2X8W?~aFgFv-$GW?JDqKO{^1uCUVflR{h&u^+qxehT> z^FfHZ%{$MWb9Onk@(ZV?2YQ9S&1iD+Fbk8IvgldF+1r%Y++Q+}Od?@hO&^Xnl&+_& z2fN`A5c)uRMgL==g2>T2>v=F<6xvx3-JpEll}^|D$lLr19%+&BSW=BEC)=jO<7Sn1 z>vr|AuezeFphyxlbd*YY?9v9#@?XY9Ja!wwK&Fz)Lm|o(NZkC8LqUBKV&bdj-2LNy zDg%V|8-O@|qV>&$Kx6Bs^WwTeh3#tRkJi*RhnvcxX*<-bfKv$xqCS4ZrAT}xMNu40 zJWY`ANE&e#%-2`TegM**qupXh#n^*mwb9xVJ{gk9-er+3Ki z1>L40{MW@V`Dx;;0*@eBp~OpIgzOUontqtD#FS3QG|1Gr-1zf+WpKDtII{H<400$M z*JB;A!Pboj5qoegps5Mxs_KA8q0NPExk^7E*kDZ}<)!~Li~G)l)yC;L9IWfq`{j0^ zl4H|4U_x(dgVzUvsiZ*>&uNf1Xog&wk*+?t^oSwJuLpEqs#Ej6p1 zF+w}b%YXIq_NMcsew@5{4){TMSet5F$r!am8iLm8an0rs+0GkZJz?+e-L7+hh5PnX zs3O_@URADD14zclOv#Gw$>WLUUg2JE9&chcR~Chzy|Y-^v_?G!5_zbnm{?hr7xHtQ z%iXabc?}H=3Z)|x8yb{3Q!Mx2sN5FOqg6#8&mV98A6IV~6j!vZ(I&V{XbA4^F2NzV zC6M4wu;AW!aEIXT7TkinyA#~q8+Un|bMHN`-b+#Zpo&z}z4zK{%{jg?$Axz?>ddES zOH%_^cbCB#0^Xq(_Ak53d>R!?^qgPPcn&&?*nEadE$&kAJIxlHCIeE=)CtkgSM&kVH2>~3aZ%e zd$5#dJr99^@QxRtCdWTPSsr&7zH0Rvq=_6$5Wz`izbWUr>J||)m z_(SE-6mMiih^U8l8g7|@s?6D9MdO2J%j0VE?y5J__~ySkT}+DF^2UBqAJI*!4kORo zmHPe3)*#RK_?9%VRk zt-hWK4&E-`0DmeIm}kUjtEzN7s-4OKw;}EEnjK>EQ z3NqE{>k`7K)rNApI~s4*es|&EaUVZeUxIvL_48-wHk)%#eW{%_`%S z_C}*a&My(mQNBs4n}xlMc!B37Nc*6}I^WCHu+n^;RqC>^8~EyQK`VoxRR)ddlYd3q z3zBfE(`+gPedGKfLX<5?m+Q)8gU$VFpX~lKa7-)eOR^^uhWP*S=GIRhtS)i3Ma3jX zx}2O7*1tb=n#$ii9F|J?r1&fD`+0~AIb~jINCx(mrM@677n9E6_^NKfs>$JRbCZng z!{ao|?c|d@ZSK5eH%>v826RU(<-pE~W1=xH*zRw8KdMy+@0Y&a z@9@0ZD6pWJaMqPqVb3Bll#d*P$ap886U@Kus2k)kuxf#mC57(Rp*8wu{{V+r+S6NY zNf*6>OWe||MlD|`O@>TmJf5SkBl@O(_hrqvCaCzFaYL^k`w$8z z&jCPHw!~qr&9j%ZDylBBR8e=|Q(m%XoB^*~X?AcCnaAS5IL6T0b-1y_BcX~%f` zi-+N}2Ro?jeK) zjuL}ZFrySU3Y$1{>D;FLUJIu)dQZI5e`z55PS}C=^l?_pCf=I!>f2rK@^IH&qQxm+ zCZ>T4PPyJ0kv+Bz%2Dgi3*Y1)lSGURtN^s8XO%mB9{eM&i^uK;GS1Vo%C&;j=G0)7 z*{w_0%TIU~!OX8CMwF|#d9dRp>$)15t=a_jT!XWZ11IL{n$6Z2<=tD$PUGp+-X6Z_ zLLPdWyL6OnYmSHW8ZBQ=8;^83t(RdGQaO9KWny=dju){AwN(sNXNooCT3nLg?^m|H z=(3h?J3I}S+wY#_lyx;W9y_dALA(fp@AvV()?4e$l<1)o@L0up78DkeQGb&3_m?8^ z{#vYI7GqUQf5_=y= zF4RlZ<*pfHK~-O4E$6dwPD7HoH+)JTC4A%WXhn7vxmab?PX{T(K5}4?jpW;NyFN^= z?$_}z28FWFROfxUTSl${SjWo@oMkO)!9M2qC5&P@KZNPVZC@EedLKa9&}-E<6{W04Jap!=fC*8v zH15+b|5!49W@8)?mizf=Zkp88+tWdpwLC~_nlMjzy4>> za^Xu*3Kud|j;&rr>qQfctC2+7=|0dcy3)^jFJlz;+;<=SL(jJ9JBM&}7@W~Nfyd?7 zv)v^dSr7Ook7?&?#I^R`3YItD&IwQs;b{@rKv z)o!g_jrPY?{&`Cjsgm=4YgS0xbA}^9WhhIF-qBoH_lnC3#p6~FFEmKcd}4Y&-)_)- zzRW_6O>Dt9o0B$9;NmP7eBYZGMH0rEZWnNOysyw@vjrrfDACuv_N>5X+3;siVdDmc zbPRPiYk@ZHuc~#;FM&ni&7%JE<;(HVvnu<4NF!-nDi?@Gac3=!khC;Q^V&+WY+oI7 zoMe}7{KsFFkCM585j~IVqdaRJW%a9pI?XO}B?!&tmB`bknckuOnP9@B*^2J`L~VN4 z_&IWlfF$p5DrHU^H!ygp6^R`?0yu_}-2Pm5gVl-t8EiIsxg%&Y665)JV*tQvG4)Af z)&c@J_1Q@sEnVu2$8JUe@pwuOkyj1i&B6Zje`F zuUq@RXRi&;C0p~jdMg86@q`?Zej)LG!UURHPM2QoewkhmU;U|ou1G$<(P02zay*t_ z8EdJHNwWPP*R8+?$Sz&OUnTSr-Ab7DXF;u@0)T4xVPa}9@T={^-sppYn2wcsae*I8^2q&f0>K6YQ;(a$6l;71S z%kvmF9pMsC^tpzLd2FisbGD!L2A4e5b-+ixj)bz*^9gA5VLOtRSF)DtF|ExHqehxq ztDqgniTzI=`@b8srg01Td9+&1E2sALORj<--jrfX>A=D2{&H`)>G%Vy-R5VHvzH+7 zc|f9V6x$a(!U|SSF*6wK8`{lgk#56BAaPT7#oh-Ei)m59Z_s8s7hAvPCgtCvrtA7W zoYr}~5}(o1ybydlwgegK5!&;I6`f?J-UecO;D_YG&%E1SYu*rO`9|yF7QZV2X+@a? zM@3@d(}5~nB~JTl_L{se*~lcQ%H=j(n}~wF^Vj*d3pSgs^r(dC9*Yb;S6Dv`}_SFtO5x?8{&OOR} z(If6%FZTdUVXcpb#5?qM)fL^Tt7}^w#bik6G1K#+v2olc)fn>8(P|@TM5F=(Kao?m z%)Eu!Idh}+b>OL9k2%VP z@m9I{e-)_=Efb(NifXyql(R1lBJqHMw7o{+mW;?f4VS-TV`GbvT4?q4c11a2S3E~N zC(mGwI(Jps-)z0RiXA^20(xR=PJ4Dn$vQsT+B)0(qtEKurWnmHmWwSi;kIzR&!f1L z9;)H)UFrlNhr7~){7WDWKXCJ;s?_$g+A)Im=kJ;l5M)b=#Ea1tycLMr$NDOfaK7OA za&JoR(em;@aM!V|*4IvI(|TRtn16pfJ-)rL;=FVcGv-lgGWye@UAM0p(nfQ;L|@I@ z%)6D4Y7_18`gF+>TsUZ&=}A3&M|}C>ZMJHq3k|KqPMV1AY^Twn|6b_z7PRDz#p4x80qq9dw;TmL=}aN=UZV?GRNy-eabbs zZ$$WZ7k<9=^>g9~N7wRNcn|HiNjibST3$UA%8kOA z#HlVyeM!WfhsvZsblczx`puc^P&RBEce~$)Z=-8;j4DVqhPdHX)Z=Nef4<#2^V_1= z^F5EYS1&Icxvs~(1lv1WR)Mn)Kk#XzsolTtYBD=1*nD-t&u7n3xGwkV2ONqnkGE^y zN}9#Q+<~C_!Z1OtER7=5fb`S9bakAA#xXd^SnD= zC?0FU$HS&0Dc;o2$k~HxMI6KCl%}hkJj+#Ao&RD~|L*nOt6}bT4e|#X{GeH@q{IWo zmoa{uuz@eQ_+bFRgMHd?t7ra{8zv3=)~bPKsu67HYa5D()TLKI}A z#i%TiwqwO*inz=<*VJMin| zu*#sIT^v@s8E(tn6mk+PAjE9AKmzDDZB4sT3KsQFQrDb2k8IkVz<6&o6`A^*91=o& zcuwn3jykifk6iR-f7l181aU*B>J+pP_oeIaHvs@Oh=frwjg(L*hC(Iodm8C7&B{J< z7?%Iuhy`;6uP7~X{9mGg-$Ng05Q1X!q(o$3MP(|H$4>>Dm4F}Jii`eQMVIJB?6P1O zy9`)5Pgef(sh6VzH%OkP!bfS`f3pWs3_bAew{LqraayB5$VoKA?%s%(d}|S1B0;=TBfgI|4~`-6I@Ks$PF z2NEG%UGq%HI83}dI@?YhHk150jJI+920}Zv*!4oNJuzd^N0;u|n*j1L6d>H}6*fQsG4f(FzT(gfZ8>>>GXbx4M^6|LuaZb<%ZKEY2@KF6f*0 zWU1lR?y1tSIl~Mpm8VLTy}q;Cnr!-RA)m~C{Z|u?;ftfWi5vk%)JJL-7QH}C%c+LJ z^Oq}5>I4=&Y6?f&8Q8um{dU7hCCL6ZrD!K^m~Zz;Rle-kV*I6gn|_9hHjm|hEv!KA ztQR<~eJiey%}Ha><%HvjSgyvMHue!4oqly+WOvyRjXq?FRK1*zQg5vOV$1dhpA?~l zzfZ^Xh8e^(u9TkD?E__6_J~vgd!Od{fyHQk@bV4gw8%UHeh85<#VJk z#b42)!=1p5-@!TW5Diq~VoBAA@^rgT9BxG2a4S92c%9<+eI(^HqjR&(I9QH6Voi@# z1(m>m=Y#t{_hs7G$54`J`F8fHr6RTUMg+OSUO&|MedKf3?l_ZP^OCF?&4+ycP+M1*56!Cnu;t zhuKb8uh}G`p;*N>3&Rf5dK)6@^gKhr>U^wDNlBfhF>PWtS0@PMSR^XfAJ7Hw&g@u1 zygVPtSa?1v37-E&X_vONvYf1yQ$xXbOg@Hqzq+e_jt2CLV#@aHzJ7kS#u|-T8(}0Q zB{adDvI7Se^@28c7I~ScScIs1`o1bc3Y(?HO z#@U|mH7^miPIlN3R3#P``36HMpwp^Jwj=qq80#u0OID_W9GmWcKOPA_k_nt_1+l&N zZmx-m022@X)dr9&~!xHmuyuvoo-5i<~# zV2*-}Otel;NvR)bc~hJ-6ML+aEd%A1oY`fky_NmdR`{EfwF+X;ki`47bfd}s^}}5x z;B#|Dzl)xbp-Fd~`tP?-saUOzt*tFoLX-rG=1Xe+^jstRjbeagyBPW#$uRqfncc+n zz{;o&R8O%;;s3gDhcr4j8%8(87A$$@p!`Eo9y-Z(W8Iwro7nJwQwU z`qb1^1S}BV^76{rU9OTe#$+|?&uv=ZycY_i`0f8L6(BYrhy*hJCD~dN#sO%Gsb&LX z!(vDH`BK}<$JqIJKY^C^GMAc~nq()$x}2V#o_Z;KzbYOqU|?VX%~J1%h6ZWD+rCF6iGFslj$ClH{eLw7%lL6IE*be-Gyg z+kD#Q+b`GY?4b5HY;SdL^&cZoq!kr$3^oB)s5=~Q`g}%3Dc5Srd4Hw~3&1+i0C;V| zWF)ETk4_1gq{1eb0K;H@n z1HKtUAiXTJJ@X8IV>A?3gv;kasmW;**{6^~N=8OJoWLZm7}>Y_2knd56z+dj*xHrA z6Y65J$pIPc=B_`>;!tUQAlcyFMDbS_!P$B-0+oT=01^p3AU)o3(Z~?SMQZ+ID%NKe zS#_V!0O-J*oj;^jY;7qCi6t`Y*lq8B1y&R2x40|;wa)D2P2bD&10W3o z+Nsh$rs7^g)ipIcE}EJt#!RIAG+NYn#9S0x4Y%;k=`$s2BwlS^xDyq^hXFxbfNb7u z{tsD9Ozh`(*-2II9e3D z=qz=bi8$IaL);>c4CfLArGphZ>aO`G9KP_tz}#=lM;pzzI@yGf!wQlCmBYx#(?)b4 z$0yLLR>Kk2*Ofr+F-Z(A|--uh<4Orp@RQ!H31&v zOmFiN=z9%!_Hq6J^k9W=`G;gYAT){`1ZM@v2f`mPvGJ%nS?9{N7(|)qbiaVt{}8^3 zm0I2F!J2=~CdGO}fL(c(XAl|?kjMuc{54w@yJ>V;)4hAVH@eMOAs6tULzM#XuxJY} z5+M)V#}WHTjHw1YEWlAn&d#pfB8W!FOG+NBe7#{?Q~Pr^1~`WrXXqi4|Bnjo?T7@# z$gRCr2krfVArs0H|AVvg&8av0;hcIEzpbC9(Acz$z~1Q_{21VN4F3Qa8u3K?@GIVS z7Fu8qIjWQ>F1OtMWhK?Vmp)0B*{>;)8oc z$;I?tkOlZCA!`SAW^#$^L(qL}*l8z5?fp6jL?5q+X{J$56!E1T>`To?O;!^qA z4x~CL!P%T`7xtHxrV!?q%h?iXMcEkDolqdn_r_^AfhWp_#>bTc6TVj#P=lgLo%Yb5;^@RI8L%sDUERc!>@MaKz z@aCG=>^5x2bOuL7bq}*Vqf4QY6jw?E!XafM+7c16RjiDRfsqM;GTI@D;E1v!zxnR5 zPQ05j;!3u578AfJ_&YV(q~IF>Zl{LoEgZ@IPi{XxAT}0jS$ie#$W>-XCLV`&c}39n z))So!2orfdoNF;e3{PcwvF*}dzG6r@Xdc35o8%sSMTy$ zY;ifm_p?M1=Y@oNZp?t}_5tv+It8>i8mJ>B3BN;tH-H&mou$&{+&@f-iPsgjvva|Z zzmQ)fHT;syQfNZ3M+cM@4>i#4o{R6xD{}~iPkDM_5{^fmeWJq}iltgA5 zwC#!0G^_WE_uX#E_zxH;;BnG&HM9i2@s8S>=!7;5j=;+ege9p!HfF5k9DO`0ge$!C?DSN@g2iox*x1xoYsk# zF>WH6sk3DRXNsY=DOhOW5A1?|dpPpjS5JLzp+)@9Nh5BkXKkDWh&}9lpe>dUSq0Kd zY#pkMdi0wxRW7!_qADPEoxg??nKfuXPI`Bc^78V^x*3ZYO)L#d^&;++0)G8n9UB(C z!L4{vYd&o-a9I>{)H$P_p;^H~DrYNQ>{aYG|IT;J&R8r%b#Ki5;V0B?)z7_^dt0WZ zg4Oi+_#pY9Aky>8ODPc%Qeepg?kvU?!Tk@)wu0QYc6POnjqh^EDF6sLEGvsNR+X=C zylb($J4~37F4X?UfcW;}3TK@3?do)VJ7{4qilJCD)eHyuh0(mR?&+~5$byY zW6j2(5BPzJdhQ=l3Am!OKWDn$U|;PoXu*UmFB4MI&};x%ABmb>CgX4Dq1dId6T>;x z0_a49rED~yon=D{BV_pgp-UKm8E*VyVB{NS!_)vfTLWk~dshdEGHO#hC65H&RxIqD z7Gw~AolQ6J`2{2;nc-D!9aRW*9&5ak($K)u(a|-$W-L`&pU^M6zfzDJ`Un1tCQZ<) zw@z>x1JxPwfBK}c%S;Sj*)eZqfjk)dJl34fHQ9`*Fanmoj{>{+CoC7imv4(l=h!6d zYIYfu!yd6kvXf9zxc&S;9vJ3Tu2GijoVQ5KP~f~ppHNA-{U6S;7tEoKI-rdJn1nX- zKW22MEpQ-1>7~fN^E2t`?cKZ_7wYo7T@Lhgzg-^+Q42He9m1B2PER*p=uS3pXT$B1 z(EpeWSR6EWWobA$0eK%v<;eH6uK4D!xsrotN!LenQUL1NBOd7UH3*#`Fhork5diFm zQ%o~2H4a*6{c(Zif}_)eCS3s9;K=rd02JyK%Qs%+GN)*3=UkVUfj8@^Sy&A){Xs8+ z&6>Mo?;HfwURgsFz7o-m3)U#FUL&h4p3~b48Fx0V4XrusbY3>&9!-QQfAU~KQZml| z*(T>Pj>k~}8O&2EN3?6ytkAC!6AB~!JT z@klK842Q~QvAYIrLTp1OMdGIr5fDTGNP)>AU!}%G!SQHzlwFN2DVpEu*s+Bl6&4-o zLz;`7UGxqxk#xo62Joio=xhJ*)Zmt`YE; zPj_}Cm3xsTG)^HRorym;86@y^bgmLB5d34gCRl6xjx6B#$T_V2K5E4ic`*sn999zx z6g@*sMF7$pe9^XaK7@t7c`tR^S%bcUs+KqvfDSCIOZ8PF>3E76FA3@()4-Av~uHum?`PS8S!7M6(pTcE^%;?)$0cbKK&$ZdXZMW|7Q4} zo`+@;*qyHs91kZ}*>oi?=bjUG_H{YaF-t<&%~lVEo)3~ID+ z+S@x&cJn5O+ciys(yYuy7CXkY$!W$KlQ?NEm)3+x%w4$)x!5#*0oXOZ)8UNh7kyIT zJOXHKErd?9&6XRgf?tzK%cZ)yx+Z!`wE#`*3<6qUD_x$a zr)Mu#j`hl?52=2SW-G<<$rTK+JoVQX7crjy%D>A^rKhC<^LHrwk7sL3P$&S`lwd>Z zmS$A%rRDOU=(NI2zQ71q=BK4f195<4Qkt!r%8@|E6Dsg}cB@vjw`T`7#T$SjVGi(z zk>Bz%F`=p{WrWMY>5|(?0Pr~*_)MQsy#YfbS;NG&xB=DacplSdH>J*cDHJ$ReeUlW zsM%~hBc`OLCML0ty{*=$F+pIv^$#$L0b)~A4MY`1ks6ugI1psm1i)k4gXMpl{$3Re zD|UMs_{aLQ2f^cdl*j(m|8c8kz2EOP#r4ure9^6;dFy0Ow`W}rels(c3234{#d)%8cT{pDppz>=fN~w;7}2QYcS+To-~?SmChQZ zN2WJZrWeS z6vCuc^+B-7sj~$(OqLKy=PNU}$CHKr+0OKr3Ue(^t7&Y{Ae{hLZ%->rB>3=_2W!Ve zL68AzO0Hse!hDSr6O=`u;h|>*X_h3yA@|RSJb&Dt&&1WoCr@GoKZX+&`n-_3H@e}0 zsKR@WWE=1|n3s$f_((#bZ->oF1XO~ppyof)(c1YiNI3S*&L-q38Mt(8;>?Xh`e!NWQ1{2odJ0Vo3{LB9BQe@-Sr2>GTldwdv3FXlrVl@_Aldf zLuTF zNYD9mMmkWr_32_*0WSE1=!UnFa769VY&IYV-CwO`xFO+vv)v|%Ss)M+68(@tnY!mh2$^}6D9~`R(^n62bs4X^JK+gRM#&r;%aiHYbY>LNDXbU8zGF<5lq6t z!t#50eh6K!GT7?d5)%!&IgxNgJVWoeJ5Mw^pUO7_il3)Vk7H0({onM##{h(#>^_S+ zr!AoO7-gd~Ucfmo%A+X+zJhz?V{b3vxWIOv5 zQmgDPXSep$asIQZh9JN;F3hc|7F#Dlk@;g7RYceixVfS^_^^y5r5)yKzs7=w4LTs3pJan zQec?joNF~2U#%txDOin(jrJeuF-q`<0_^wOsnvG%JK)HI$U3WjMVU-+FK?-KE!^~L zGS%F_Vq*tOc4P7*j8s7Sggm*sDwVQl5MbD;z$4#@R)dc8o(jm7(~$QsK|F)MTg)0B zTmyncJ3p3@Bx?V< z9E)gKO6R=kAw5WkHb0|xTL6BVP=tKK zc$3_ix;5#(d0oHSUBINZJ^wnI&Jsb>LM_y8L8BAGU8ZFaR@6KhYt*%4)Vt(&T?$w= z2fZpX?IQ%Bb!i}enVA(u7R0RAq{7-R*Pl@c?2~7>(1<@V9@Su4O!=SV@|#emr0!Ka zJ)$+5dPwrwVjh?$qGq~(1AQ2|E9k~rLK=+ac+7>H+3F??gmWWK2ojQS8Ia$1 zW+T|l;FGl4e&jAsquKoI#kK~!dARFiptchE(oI9b%8T+Wr3=l2ZbLZVssR(lP#_qZ z;m7=uZF_za+T;}$wZhc5rfSE!?V5Q84N=hi)6e5EAZwIzadXA3BmomjKi%tBX(%Gj z)TMpqZqptqMbZS1zUoaw>~_Z7ayxc3Ut>Cl7lwsxpccrq*u0Y!VS&bcIn+m+$w)X< z9iPf0yzst6CoFu* z9syvA?j?ndNYC>+p3YJ-Z0{X%Jz$a5Pp12=v$C?o1DM7VSi1Kzy$!<8^6mA2%A^ml zA2(Wic}~byKk2c3zDcz?8?IhK0JiyQ8g;xI$r;GgcAYZ;mmmcGff! zcqwqek=lD_8{O(PCa8vnhHR#%+NJ7^AD$nsqLEbEy`Im4_-JtAR8>=*_E^;Q>4|nT zl19dT|ILo!RGB>5v_Inrzg)#42!z{o8UtJEUKimf{nKt1>L{Y#UoX!)^H-D_#;0-{ zvQV&;V=D4FtkKEQ<{HWZAj7^0B6Fl`Oj|m?lJYkNhVu~nAA#^ZsPe4>|6IH@x`R5yGZpy(}Ci^f{TdS&HV`nFyqs~s@GlR3O%4`6Q!uk@#yKCsX1)EzB>kretq^1$? zyNgsv9@-v6YdvQ4`Al2A&=u0HB++tqbHVNe`Y7MvLD+VGDmC(8!z>^m$m(#D7JDcr zQN`15dW+(gQyuMphW|E5v^<@EGj_baPi@V^=0dp~fxXE504TuRQrUc^-E4e)I)ac_ zKi%x1G|t{5}z*Q?a?+^Tj*!c7l#&> z^WM3&WOw2BPK?Cjy^{?F;(x6dlp}9PU4*3tzkX$=Hmfu>HI>!CAXG*#!I4kAot$1G z0W=rA4Mpf1hx{2*fZU5cMbAr@d0C*v_RPK@_bL>alcCu92HRvvhzLrbmvL?>3n&7Suh)194BHB5J-m1 z))d7iO|5gwS88SeeW6`V(T&EWV;}w7BJ2;L*452GXEdXhz{dk$xUt5D83wH;FAzKf z|K;9z{~N8BYSV;RqE?o0id<{)udfptzG@100~v?8jz zPZ4lpt)KEkdVt6G*^*8B;HMT6%Z)7h-f;Wtyul;^*J?)^1FBF~mHbrCe0+-3$2|2; zC&cX{k}Q``%4s*=?9IlPb%9 zpNVJ)l(#vcG4^!T&u^&S@HEkIyF0^F;gRNNMoNq9l}p?TDa^!* z3YhMEX4Y!^P|)ZlBspv*a~ zZ(5Jx+$(@X6alVb5t{4hcz4Wt&Ey82aB#fMfMS6WgA5o^r|YJ_-{s>-zYg9{Qg_uY zH5xc=q_`w$l{S=bJqdQbxzg5u*%0QQorsMc|3pSsHQ)3b8;KR9qgfBkd|mY@B%QPz zJ}G<^R2hrD=BoP}7!Y9i?~uzr61dRVZnKJxQ|B_QE>H!*u4Mw&5gnwMHO{B26cJyJ z6o_;Fh}q>E_&r<*kL82gR3Jvlqa2oK{$R@MD+@1<9^@sfesD!m=E;v9cgu?iJzXtAVy zIVzgzlA%|%pb7OSee+O3_Dd_)c?M)I!V3(Wgv4(>l^Dp}Bp1Lwf8!C--VX%N=>j_5 z8vT0^hE))wG!Xd?0+{PC@cPC9T?Tw2DslKTbvpSwL8g7CvxPl9X&!hSe}RJQ2JLpa z#Id_!+-tc6M$B3r6ldrN*FM{IJskSkmlVd9(trwV{By=|vzeOSUsVK1-Io@Nc_Coa z-ud8!Bf?2kTyRX~!!yA7g10`hKdMRabY;;yMSJ zFBL>+j{`Wlb)@NOYOT|#Hph7Kv%ag+&r~F2LD~5WWxx_%L};raEp2t)q5ttW(7N6O zGr+Y9(`tg2n`0QzBsQ$(t&kqemXMao-s<&36g&*A@^D+r2nPv;6Y?A1WpQp?w zxJuPNVo9R+1~M(3J=<{P{cEP&$FB4^ag)dA`H(uoBRH+xNcl7?MUXqbG>iowj@brT zH^9WgO(pNdEls>S@S*DC&9^08MUU=}bkWjpl!Ty28C8B|wBzaHghl8xRj+qV4F-kl zh#L6oh(>9;HL`l6D?dBn}Sy{o8oRJKYaD1bSdS*VF9m+I(hHDsCa&Low~KCN*W*>U#?sA0t=KE0Jt)=W}&N zFLSwe)Jj-`0MKV@+O^GiJiECP(|icI4Z79IDBICh4!<{gKsi61H@(<#Qd5dyW(Sy= zPG+3iLryXIZ38)^N~e<#mxA(AGn3D}ICdh2kn{BFnp!CjN0=$)m^`h(=Dh>xyP?8v z`Qx81Iqnid&Ne!JkTrMw;oTRX1R^&I-n=$rjccIE&;C2Z)bBQuPX@48m^_d!hTKCr z#1NhtrBUEe@pnu$*du|E_TOQt7NYe<;`#AI8pI75fVntbd5fEU1lAp#9BgP}Vq(5v zEntfFCk}<^tR!H9Moo{szDqdqN^_X#^O+xdP61n;2I(Y&Zzbzui!nzaI*B#PgN*Xo zM2D$Q_s~LK2$o&<^_-k#6cE;yA^Q=b(c!eu)cKc53;7|GJ+Q}VPM0A7&A?#nTEmRZ zOFU6Aoie>Y-xS8cCq6nENS{X;9!weAj~o>$#vd8=`mIRck?T5K|5M)Ah#DLsZRJy+ z-%WCL)591WPveq%P*HZ=aE%}o5(|_vcEPh2-Uwx2weLYtX~5|jthDdG=2vK zHK)Hd-{Rj8!fNGBMh7Z!7HBtVkH;guzJ{o1&U=s4sJnKDkDEt&FL4bZD%e5Ft8J|o zLsa~KC1DWnOoO2WD5Xzsw3UocClHMfX)#cJ+rv_!NgYnRjGFY4RxxahYNbp1L*5^c zg*~<@H-44f2tS?kz}=ii9#MyP|J#x>dJ68`^r`Z^)z#*6!=;WOqBGt;4v?$#7=@!O zEJ(hVG_dh3Q$Xa&QnvTnDnL5bI_9rGTTcDScH{>}z*`Rum49fMlx?0ZW8IH=nIhVBtQ7j! zZbPEYZ%!VFhDT}q30m!RZJa%0Mc+lHQqipe2*vhSe;3v9b)ro;PzCaoU8 z%v7?wyg<1UC+d6>F|_z{r#})5sJwIaGFK5QwBV6(n4%qC4-$ptTdzK8f3kf?t;hA= zfQ+ml@~_6BS?-9Z;Vljv%!=^^{l+y5=MsD10B+6l+?*6lG4QIhs$6%&5vy%p->cM5 z2NLXTb`N@l#cV3Uz!Vhvho#cEX|tmx>UdGySnAQ}Vnk>fw%*YFq) zH?fdte6`EXa|JWl)eRf>wlB$KV{79S(KL~R#JYr6v(}O>S54q`GfcViZ4^qGg2592 z?#m!UVVgUAxHvZ@>yt%9Hh9_LtF}!MIG`_b9L;zpxg-HRJkB1k-RM)--J0O)-&-#Y zOdOb~o;W{I8U{O3D_S0kLE-XS53{oj5b?N--1>? zu3sSNb|doWd~!C#P!Swfa*Zp%Q{EqPDF9d4*7}$Q;I9CoQXg$WuYc^-Wl`{MCd&)j z<6@z(x~z6ege9zt!mpt@`B9&q+(XratuzBWuOYF9l|3)TRj7rflr;{B`)_aQG6ifr zmw&Gj2@VYp74V9b*m%whLT#P|dW71+^AjD&#qdDq@(qF*{??ZiT+^^o6K>C8teWr* zZDHt`(hw%mIK|aDcbglfQUfE2Lr;K=om~mlmvN`1Bn+n91UBj%h1U>%uTk%Ys^GT) zm;Azz(pCa<3nP3PF!j`RV_$u}Yj`WM-;bQ*0N2okNnCfVXb)(st75Pmb@+T37Ic#b zMdEd;Iyh61hNn*awKGC|r=c&uC+q1qr%yE(7ag80I{ynW%%C}+E>i61$P93>KK{|G z$lz1bs(G<)R1%P~IHn23VC2_esp{GjgUB2;Vw{EiyXgi)V09P zdUqYoM2jS;Mj@BWMF&1Ev{@uNVs6+a&54VPZ}SjjBSn4dn`dN7;Sq0=S*o#V7eHUF zp;o|8elF0@lrehw78i!-_GO|j?_IPJCT~bd72gq;xnF}3W^kkcB{4PtOr4+Ih`07x zRod)Ho)^&4dBGTcv0d{}8yz+oN|Z-TOO64df4rRI$)ffZ@2WDImOX$I6Wx-}*16lB zW=Vo*WZN{{TYKIdz|R9yz0M&&WhTNhcf@6O5T`ZTuq9!URFcvZe8^j@b?|zPMi04^ zF*l@v(MY3xfF3~3Gs_?Ecdm|YPv{CTq-Q7ldxOKwj(=sJq;|pbKHL^d(n2#9P9bbBta{8GRXt7zI=}_ ze;Xffv?{%}OfVVoIsXKac)utZ^2e#xzqQ;mCvfuThrV)^oi)d&xQSf}648H&iv~E~ zk`jXDWdTMxhL3%c`V4-}Qk^dx9PB?zk1s$Ejz&L@VPA}UeVL%YK_P*Be0&DD_C^f8D(yvD$^}MT0mY(ZU=A!Vq!>!V=aLD9_5We&Er8+%+x2hU z-D%O{?(R_BrMQ&h?(SBK7b&jA-QC^2xGe7O&KKVIob&%?G7K}>-E5L)lPC9mUDuD- zsX)98NMVG())VK~0XpZ55?4-6XpLSJtQfg+`4W2Dqmjj6D(SzLnz5Gf_vgt}fMcmV z((8ds&7E!nkW0ktg$$Hxr4*Mcj9HHSFi| z!oGmX0m|(g$)!#|As`Yf=EDInQm^{m?%dymF0p%Z0hdf7vTp>N?xIgsapRCG>t{xL z`!5(p!Ph)@{Z;N$iR%SSApMtOaSGSKH`-!Bu{YhrsmfxOax_*k9}q#tvowmml_sqG zr53M>gLCcxEuFx$!m0r7GPD67MgH@CNgkUw*6TsdrCu0Nmul~6*KOLHY#_r6Bv zf9~98J}vm%$CgX3zyyS3&I$i#cwPzT1itIFZ3San7#I6D{g3J^MeUR%Wv_|9x%I3A z({0U(%lG-UFH~BZ2vKQ#0Dxe^2%idViOwa8l1#GX6OknsH_*kUFrP~J&RM0+RKUbTb08xV^ItgRDGg^USRx}Vy zu>`C0hen%Tw+#*m@?-0G+oMxuwCXPV`0W2Kb74+6ti>}Ttdo>L8^0TfOsg^)bbaBwHjuR( zHb?ph1ZDY;aL*}h;6FGN+V~Q}pe=d~AVN@&4{s3AOMQ&!k0s$~5SmnJs$PJT6X!o+ zh9&T=4ep+vGL6`871oPa%77FsJv|-J9sTcahN}oPkwAslBIiS`h(ac5?T<#Zw+q-d zy}{g^&FS<(DPUpZAtj7bYhN(!r%dcZuK=o#Zjl)tmW*SEAAZWVgY1CxYt?5#07L1< z8jj@ueXe5zx0wiQDaI~*oCWIVhZSzJ#Q|vPA#`!7p_jWx2ho-kH)a|}({gJpV&X0Y zXhl-T95!pEPZUj)vz#no;18J zN0ES>C&vKP@hdDWpfZMOAQC?UD33^kG872;3^)7(X6AO^o{757{az}3Bujb5F#qSU zYy96~m-;Ptn@t@;>RLYqYJh>TNmm995N6oCP7iXz$y*Ds3=0yt*sM+U=PmsKV#tkZ z?Xm>9?d?1H^MEYP&3s(_fpBcPuX37=zJ#96=K--mT;KqRt;+cjga7Wp2w-XbgaRkz zG{!TXc)^vGdxAver+&7Y$qSS7zQ~n~>OSHqHMzkgCbol5Ez0jL_ZWE>K}zE`rohxza651)m7?$wGN?r_;xwv<%CD`$b;!TPI(k<4!M*7V;6Rh zDH(VYO#-HL7^I{*nw^FM0J@M~v@cV@3(az-h*my}g%Ie__C(^d{Z5Gm*Wu%{UT?z( zTu|uXWI#G7knt67lwWA}3kn{O(}mshr|LFv#foTm;=5fhJA=D5KJ{0c5KA(2eJ9 zUu|H|fIm3o$IlbqfB1Vcd{`J@P!-z3+%j2$CH5uRf?ja3cmD132dnm@a{E6^$)``` zwOBK;5py2Znu)kU7xZ}w z7ruOhJvUsrvtcbO2@R#{={??R1o>g22XFOf%fb#fgtW6H9EHLRf0z{}DE>Q?i{)rB z$W@`kRW@G#Qi(UDJS1HcRFieT_(}TKyfi2PHp38$g^Ko*kV}dKa2BsSdid~Wmr-?N zV^b;J@RV9p8R0RY7fK?;M0`5V8Hz#-@CQ3==QE-|;@NP278Vx%LXQ-$2FQEdyu4c- zUgv{=m*68vauof~(MSlT(!|N* zkozeRA?;rD$O)Bs3_OEj?W?KXSO%yY^I$$id}a!qt=pdffug zbL%CMlU&q}B5u+z; zRcv>S&9oBL^-c&0Hg`3|CO}K$kOa(Gpy`@~KhX+|c!Ia^C!h`(oC89q?JQI5okdhjz zaIbmXL)tTAmu;EPwJ_MWJ)(RF01Fgap)o1-ZPsDEMpbr2yVpih7X{ewOdAsL%7H)KxH_*exS(kJYzlM8vAhH`{((J4=}-yX$)2pC)N` zx<>uNcG@5NT16A-d2oeuH!PpQX=zNCp^oaT#}mG4U4TF&|5KtaW*SA1ku`?Qz+=UF6Q_FyRDKx zqp`Ll1JM~R*{X#V0G1|`h8Q(mYPr-+sLp0ZfBiOB^6~81sLNl(e7+3Y+uNI>+#^E$ zV7VL?VAGJEyT{SATK$9L1pU(AYn}ddd&DISa6qn3&22l#^jhzJv-|2GB+bns19#8N z7I{9I%;)>GB!};VpPWDQ!-w8mC!PzZ>Gw|ed4CS<{j|LpD`4;FAhsfyJJn>P2o=Kq zHT`jb-4+DPA^JJhPD&`4!!l^+!Q42H0=LYn!%lwrUOf9Tv0t2s-J;^a@$kPX@ zA#Adp9_ClVWs@T?4|9-R*^(VoZA^odbR-ZJNBOC4=iVy0ED6gGDaC@uaf06fHB0!h z;`MqiVzJp5{V2_ahX3mch!ND~+Ac|2XGphWYL;S?yJu%HiY^3$p%IeDM#8*-^CZoTE)0B`&rK(Xz3 zg_T*04uU49qzG@+Jr{hPDv{T@7GNxjudY}f@CLOepSx*h*t4^{bOJewB&@6u06=m4 z=~cPYkc>Rl$k;f~$iH}&r?OpMSOdN7sgi5zUn8&PmF!f42) z(@V$#fxRrQt5B*NOW~JYfidsV;^H|Vf7wXG85M2aP-J3cMMWm#WYojH(h3n=BWLoy z!JH(TDbe1eA1}eBeXV4ad&y0tjzS|#p$vbzUDC1@;_q1J;A}_^%ghZt?mt(#IZN0o z;QUndA(*Hjnt>(ByM~&6i;Y z`Wo5&@o{do#^Ajp4kgzjKHxdwtu1GPt6}@zJ5S(qvrp)z$`ch60bpHk`>Sk!tUIZT zKE7SAS+D=)U~G2&MMHZi^iVeC48f?yAEGpzyL(`gxaJAHFM#K{MCDx1@Z;bVV1$|S5ukpuVmH{Zw>D1 zGDDA|ru;R^0?i6sn9hYJ`_HchT`WN-i z214h%Zf}OJKW=0{miPrXV>TxLrNouc`Hy=zOS|DQ(U;^Wz*;?7WFf@@B29E_*yF=p zFN^0{k23A7-eO?cxLTp#_k=LRb%R>4h+Mwa?i!!f=nLywzGXgXkrbaYS>m%E*}k}{ z3~=?4$Q3C)llm1V{EDNOmj)~Tdm>5Bzce%^#SMsbgO$V`UB}M9s^YpWnX2M7H}04Q za;VYIVSPt4GBWI+GwypGL13wHY-MZLbLfZ&b*rDA#=rKVM3-JkKiysu-Kw=wQE0GcMyZ`^}ko1OUc ze5D8gLHUZd^G0n=uTxVrCK~Q&s`Tr7#EZ$>xh}E~wVTsLVU0 zJ3Bp-4pOh_mO-U!y(L)GEU-RtLebefpZ@rWwZR``sNZL2{D8SWB`6}Ft)r{t-M8U{mi+oh|d1N#s;~++Shvykn^7X6^Tro%#YNoACw=DG)a-QJ@Q%8MFLg^F+gHM#7 z5aVyP%bedYAy-X)u7fa}WzT2C6S^;QKP=N9nyZ5=MF_=20LS zn~^@OL-uSg2UA)d*O~h37uSerB0b}4dL#Qd4BBLDY>)t|m)WIR>Z_z=7?riTIpN8A zJtX4ZWPhjMJB7Y&o-e>dTNqy(Pe6^Hao@G-IK3+%VtJi3V7Uez4h;H^ePOEOcaQdcGBQoG=2;IZdmI+4Z-q&O342m$=_%&=}%-7mJG0! z?pC|@@GReldAzcQrsGW5e!9SJwsIDxtFew5wwYaSc3s={SA8I;L_NX9mAE=Ru_?RS zA0z2(ha8<(vpR27XyI|U@cb(rzSrZQrtLG>Jj5`^yJ1%B7Xy$NRF;reyPT7sZ{zm> z59*Vv~hSsPQzeAJaXL$$VxwnVv5q=p=rxyIwb^Txj87&uBWR3X|^4hm{fXn z*b6|2hXn1Q))!G6@6x@=j3|e_wa!s*qXX79w@F6s)bKMvb0|KPehrf2cLx#f@x_tu zXX|QCr5TQnyl?ChM~>5(rd*Lr@?%i*mg!tjCB9yB_W8RE8Q7$6Ph3rlE4lVgo@qme+=;?fE!Mao+GRlm{va=pJx zC*Y4A5_~B4-7A*tla1CnU96Um787|MU(m<0i|{)4``YqIvk&{>Y~!J-+KprMvOdg9 zBYMV}F$$6v$&<-|kM=j|4$LHqqaG8y=?s|9e zZ`edatG{V2nc^bhaxpzX%@3?*t#<=I&wg$aXVnU=!Z5Y5481jmfGz7Pt8JBkMYEc; z2dk12o|l*UtnyEhlUeaN)67PA_By*SV!bwl4duz+w`$g)j$3znqUf{kM_!knUB2FM zb8l~glT&$$+aYv7acXrnZn4mjtHt%1PJ_Ppb7d#B1I^0|BsukMmfuSx_q9!RxNf~z zPnG4IBmd)K(8H{E>6Kgv@JOn?wTVSQ1`ZDoA4mKLG0L0ejK=iscIllNxQmKQWmAb9 z6ZPgF!?bECG3R#^SNnbT_1yT2`#UrvVkk_(#ds(^Bq!uq2RCo)-*1iGi*4Qo zc{C%O(x)gbX@ z1NA(#BrbF^(yiAyR28s(-YU$~0lZGwu@pAU+v7C1Z#+yNwXNd6y5=IR`0lnRyO`6J z?-U1XZeV-k8BqYXu4`Rwb=mEJUXK#5%6w&hyy-e0@kHdG`iG{>SAXNc|Iwq1(|4UJ^rq`^if?`kLz2;$^( zJ)>m|ghf4(bd;%qqef~P^coSC1L7&M$?|-Af5y$XdB{e77D5*oe+1}`Xr)orJ#W89 zmV&UKaP%1a3ryaVW!wW`p`Nv$=iqwpIIe$N)9&_G_&}XLMPfAWx*)#)jum=}?m(Ac zzQwk~a~;kmMOP{%f{E-zmgl=ky0>VQUa58!JYu(ukgaHfJbjM-*=Qh7uv)FIjkqxO zy4zIkG3z9hBi6}s0bdm2(b+~d@XPV-ySO8O2Yc`uAjYt6toJzYeBMJur@-H^z8%?J zY_}?Wv~?aBirpq>rVM2>9mQ)W$BSfOBZHiMe?eYQNTFoxC**VaFdB+ColMs@_GMbE zbmTgV*zpFg z?x45Wz+bJww}<;Q4Kx>f+&f$S=R-pF}D$Kv=`iL zps`PeQT;(tyTw4C0xPD~?c4<50)D;N4~9r^C(m>fmGJ5LdRVC8*nf1DuNC9tET)f2&nhxA9ayf5u6SlMU0%er8ANP z(Q5SHd);MRx5B!z4 zkQT)H)tm#13T`q(oSE?TIko^A< zwSc-LA8&Xl@HL&WQ1ff5I6V5gyYV@%G8$;4)EF7@DENdF1J_s%GX7!>7U#Ny0j^4b zR+cnPjkWuCuniM~o1G8yk>cMdu)8AS5|E~R) z6&#j`2*>XtAW;AbC7SU91tB1#J(ohfc`;0|39`9)DUnBeR z>w`D{^i|!w!a}lz7X}o<{@lp_WLNw*DgbyJIyJSz2lA|@u8v){1lWKh%MZH|WDQkH zr98loT9cQV{RJZA|85sG*?vX zpsuj;U;f+5EoS_}!ZHn@Ul|!0RfwutwN;w)fc?@Xr~>EUCu-9_MQ$b?`|#%+;)G}3UH63XdhTh;%Mdk3sexQ_mh)B#xipC#Sx|K9nr z54&yu?csl{O&7=e{txirv3@xG6M{dkSNXxURrRHU+M!8>xU6h!v9T$J(m;+i!HyWdBhQB*y*rjoT~jvi&jH3C_vb6*R`}W*wSS?s+SL z0G=AHeouLRy9zOPYunye2O%Kn#@^X6;d%5Gdz|d5OwM;?cs%x1^Ii+FTaZTZ9;T0!c8Vz-UOIMG!jpkBkH$bS6@L)5_ks z6nZ?s)Mj!&*o0;AKCs_Ot+{+Z$$*_m3j|2=49?ym@qp)GbEdu^t(!k0vPGc z=%{RJqj|x8qvg`_iH~{3U|j9J{HM>wB-%i`uUNUg;Njs(ioeqR>R!ldCoKgeHGqWA zL5NlZKJxdy%eO!d)nXawdQu6xd9z0cg`x}%DU;RfKHVMCE!CLD1D$;7IC5#@#S6o$ z^9jFvrEbxg=2H^k*>vXsK6}Ck4>h_YuMMw;=^3%?h zsK=*rSXw-8Ed@R!^q2#9yL^T6WyiN3=;7a_?dbwu>ZTK^#g`Dl@3(b_H8(L7Gj$GZ z7PA%giKEZwekA*Hc%i7oC^TXi9KtKn)X?HL)3j)!aHPS%8!_Ol(e|>9{il!vU~>1} z&Yy;Xyzo8{h>}FK%DeOBnfBLQk-k_ZBp!vJ=S|V-WwTL5ZPZB;!*fLf!*xaUoqmV+ z5?n7=6tkP`M9KvM4mEYTPJQA)7H8tq?Wg3Xb|Cg!G>*wm@UdxRP^HjS519;ZlO0S-ZeAd)2@fXm625c`DwH|4P=BpDQ?e4N_7*ieWtyx~K@`}REM#!hj( zP?DuKYemuo1nx@$jRuM=m&39V zB&4RkXhNMD(_zYSInDBwhQ#xIze*RdoL0RR*OK2wI-SusXS1VlE7&f~uz||gdkn@8 zzYJ9x^$F$O&h>q6QjMshr59}VID|3;kE!w5OxZ=fWf-jJvg(f)ub<9Q7=mI1OQ>7# zPgq3V5iQnhQfMKj?y@%?E_zYx-`<|>3TdxH_;pXCl2_I^VNn`N}Ppxeh_0}0O13% z1M-4@a2V1`6_lxns9{8yx<$D8bRs3okW1T;e>%QRKmB{ItmoDrh>H9_3qH4=JTFs90QXgU%!~gzd$j`_6%BXn3c^xvy`B_>ZE7WkK89@( z43zjNmUj#~2`Zr=d0L~jdJcA=+iZcn3pp`_!9;qq@yS8;lm1GRMXvQqhaTWwrD}CO zpK<#!yhV}cez`?!oWbv*@{v4Ns!=)5{c`ryyT}Gk_?OZdFYtW{$Y>Ny)M zky@-ViAY?CB@5j(A_MEZp5U_-05ch}IbWf57iDHpFH26K{if|`JXvuVIM?6t`s{op zo7v{EyXH1pjD{QmPn~wQ-f9&*RT3wl1(n)9-mlmbfJ zIqQH9x4@#d%cWsb5;WZz6f$f;r*5vN^(fIa32__o(gi_nHj(a0cos8QH5hUwBFLC> zHIYJ>boyHW8E4*(Iw(51WvR&_mtL#o>%CUCPxh)pIyWiZn>J|&FhnO4C=%F}%(Ha^ zA&BKeNbPXcl+dnl{3oQgzJ^g41_oPtKGq@cZ^p|Y0M}DxXzE2L<4ICqiH!y*0=T0p z9B!AR1X^;EuZc8z^Keph#`kAyHJ;QORGp89V}}4VUyGhIQffZlyByCtXtI5F;6qQNsOVwUJ8G zcT(&v5S_+Eo?Y5l8kfeNkC0+77+T(EzvZvk=Vi$Z9nZQ|7GHt=^c@g_u%UP1Wgs&E(6-CYEV^iw&;pPJ+CGej$D+RRK%6$$@EULkTJXF9JULI4t&lkwVUMwt=M3$+7t@K_1SjUS zO1;_!cJnEvqtq6I7U$x1vdw@)2HU-dqfQ%yCR}hEV}L;BY2d?RT;$Dn%=b+#+a?%# zqSNFmUdT@HIkQTu(Kq7+)Tvl*2{5ity=+LL=rqdzRrx*`#wJ2yX`4?fRc)+?`k`K# zib6w^+JPUW8&;5dYw0$7?xuWZKXo^hWIqtz2*F9b>UE{x+9uy|!O(w(QRKTTA<86^W6fQxME zwK177+hO?{>(e0nR;y28H0%B0iew`?S?G$}3e=BalU-;Jac$8GAA|$0uE@9Z&V}H} z8nSc>iN&+I(!wXGi+ev38=b~)vf+jXNDHxNfYoFK2=C1^i_xe3x}6I6qGc-A1@Zx+ zcg;44P&)>WII9XL!0)HDDy4MRx(~TW%?|G$GvUsFnN~X{0T?UD2Pc2b={HDNsd;)W zlc}z5l`>6-Z+OtqX*H}`H3ZBM3ANuAz$~oS>za+nQ@{OTk5)y<9x`7k$e{gJ_L}S& z&moIrF;&#ze7+>lTX#;K!fG~eGzqy?zl?kYz5<{Hb^|Q~!xc$!3kv{P&6}ok$=DLBTrm%{tOVi)(IQ@LWk=hidU_5hTM=lY zy$U_OG9h}g1h+wNh!_hj5*ol5+IVI@_Hr;m=&QhEP_m-7UF<^*+=f%}IPT4(=$cP~ z4`=q8qCBww@NiE?DH`vA)Um!+LOzC;miijR8O}+N2?6%ZW<2)g5|g~{+#{X;5yAY~ z=M!`BT{!N0H8=CoecAEv27V7jsm(9_6gDmPyxBWZ-Q!E*p4ikhvm@$>M2``&`~2d! z&rNaI_ceQ_Ep3TEy~ zshoBtxs^F0EgQO1SNP~S4fi=@F}~ZL+);9-P)8zJs0qY4Oo=P&jm|<)OmGv!@l*@S zK5e<*GvLtnidCti&^q;BNr7Ui`s}aiog@lo5~XrPW`-V>reNscoBfQ&7>iFuwYQWR zXlSua{(yPx{`xllc%kCB7S@nbAx@LiX1OR4@_u1-Wr7qh-BAtf9I^BIHO5%3})Bl}w^Xb{Me}UgpWBDa@vj7>a%?6P_AmkKrs|DMAec%I!*4P>9qnIS0byw(B)o z&mv&6l-cG1CMfjI%=HDzL4z5I9dJc%bz@n`j*>Jx872iLq=H$)@U9xW9@g zAEnReLWXF$3#a6vhzXVCodR+7T+;EBbDrQ2!Lv#yl7v5c-Bt}Q#2xQW@P}U&GJm@o zf|GKo4#<^^X?1)#ITT|;96{qEp*ICvkDvc%;vHJ6oA>#1g6_#@L6oQU12!u8)G_`> zm7%Ul$)RM2c<0Xowly9o_39K>U#`nM)w8zWqiS{NC%f;?D}x#DG3C3`ncDcxKYjhn z>OM3Wg{iSZtAF(L9R-p<;qnT(6w)*xl!F)TfFu|<8oH-<6megh>1bbMpK@=60sQT4 zZ2PSpj_eD3&)^{NiwxG8c)?3{j|02#zFX% z`qnl@ft5FTGvLnebQ${+^@3{>Qn6iVC@n1Bf=o%F%duy+4ppN}?)!3Pnul2*+iTit zSlo!;0^@K-VO`S2ltKr(3~5;iXEV&fg$~62H3YbQ*M^}=$xUmG>216(b|!9H>y%xo zL1hQM?#)gKJlOdk_sh0C$6eaEA-JN)tKImXkno{s!j&xdeG(?T?!ghf&rx02I2VP# z0aZTU;m_?cehG6o&V}x3@)1(*ha9y^eLd*rm&fbFe~@th(#9QxuJr@iq%_gH_MyD- z)A`&Ke8_d#0tLKHgnxPNXRIF08s4AjS*~rs9YKaea38JKJvx{r;vBLWD-9+|$5Bpm z|K8W(vMYuhn#w+*RWHliHJl@TLj6PcEtc3%yQ~xZ38~%lZpH%pqTk;PeWcW3oASzb zsm784!Yo2O9OqA%fx_?7z2=|OH%OEW7y00w7@3*XbGYCHv406z%|`Nkxl7cF|583D z6UEVKFpm4pkW1&8$Nq>T_3=z$G5YzGSfpK_O6y`z*wb4=ffnhsJC=_^8iCEg_$RU| z$^usXQ+&!Bc)AXdb|doo`=$8=^_%1O-B?sQ?>v`eP8(hq=cBW;f{{2)iLTT6`ZUIO zgm-dvVw!IwY6ZHW_=n4Ft4|TBZ4((>8WRRpx5`Rf)Na%&cf^a8r-(8kt~M*BtPD3! zM7s+GE;{mlssAkgX$&Edbs3d}8FeCU;?Sskg}Eap?_4fK4}n4OSIL(-{FnLih-m)U zcNv+BLvqjxUZ+(@n1FwVop(N#GJAA_vG7+HG1WB;c{b0U5KC;xDw=>N&pM^XGWK$u zi|s~hs%X0Ks{0w6ywbwpqCc>hL$y#INDH!)8c(hN2X76$`nIKHuB6z}?Dffo^c3l_ zCxLLabyGlS>TJWB!>8qjmynLVXQN_k;+OM-oJ!V1WbG1g5Hs?LupU9Zr9#VCj?TAl~=1vIYMqSwO?KQJT zRn$8hR~7+>^*#9YusB{9OPNj#2Wtlt=~%#=aR~*FA%fs-`6RUyn5mwgY&*kX;P6c< z7-j^Kn!)xNl;p!@t%{%zRk#03Ly22DB!>2w@1SzmZ}Gl^`xh=*K7hAZHN-e?n>uG( zg$zX@no-to6PuEyLGr2KKtKeaz6wK2tu>oC;)4i>gOl^!Ylq9h$#4V9Ck+;a zYsUK%Y5>kn7u+kyZ5PZOVHVhUZ&T06?fcTi`w;XUnkuPc&-bSagdomFo@xdouRnhR zy|O6q*iq5rt4U(xPPE2#&v2%3a&9R z2HyXk<_eSSXZ)VU@C?Z0{q2>qO!zf8A%MFgZGV!f^X=~&>>M`VC&W+OD`iMTkR8<{C;c+zrRugE5B!{;m<;@7I%{ zBlp=$f?%JQzOdhO^dv91^h#2bAud5=LsTQ+llF4rOKm2#wmH}~+^D-T4KRh}h5@e8 ztfG3k>zmF_XZKFdGO^GzL1uykm0RBaR#i2y>V3ip!F!CZ*Pz*Xs_6l1$%}&ngzW~jwPP+$JdUnz#`?&Z=ue@?Nmq%+@YJ_ ztL(JqFwH1?yEB8&_`O5m2ytIrcqIxb-9>U=#{XQtb>vRLRdVhgW%LjBbw|Va(-i?G zYnD^~mko0rk`UF{eB`nQ8MV#(Qe|+Ir_G*z3Ot1KlWA&>LHcHZ^1AN%4kuGHW*nyt z43D>YXTfJCEzNIW$0^Zt^>vPVJJ&OQ34nwGI&1*Txa$>nJbuo$Qq_nyy=lPJ12Q!=~hI>^~iom&sh z@0Qy}I_@94pUG`f>q>qFsSZ!w!)I1YK|;muL!Y|!9fUnC<*R;vUQdFYe4Wl6D4Jq% zJ;C$g>KNY8nr&7!TWWQ)YeGQ4aPiL{i29tob@hx$bNLHdvvlL_Y@xG~*5?akn*nGz zLj$y}RJYj-o$m`&pNx-j(sFeWee?@tD}$h6Q5VbXW(wphgOD`e9u8u~NJRw;@^^>R z6k(yJ&IR1RchXv;NyyCFb%DY5fVmHtjwgzNE)OKP)BWDp3Z5@Jetui7cy~Q8%jprX zuZ(`UJ6pQT_~X5KxxyqgqA0|GFspK7K~kO^hp=0;-3qC0{2N@fQ<1)k zScXmVASe*+YQ(+oPH7WW6oaS?4Z(hE!?VlqP@HxPHLFI@vJi4sgY zi!@ia#a)E>0?(lf4vnaW+!In#o6qem!Ee4|4msDxo)TAE^_@RYHcMHOCI`*m1x1bm z9LpFljN9iZxS^77Jw}2dhn~;tDUY_t{>6i@Ov!fRO_;B*j%xH1#64oPVM6bIbR+G* zxHK>1jKIPlw&;;%ItKzq?_rMxs6?v^D9{pl%mQ>;S6B@}qViU~u*cXY<*}PMtQKb7oC5;Io&CZ0^Q7YyVBy(JN35S9sPa9dc;jfl zSt272vg|^rZV-rMVfoCZaaG9X(|(&Xn4tP*RU*36w1gT0n}94H@dxQpQV6vPC0w6c zre}p&$xJI*{G-Yxst?cqUA3@5z`J(eU6Rm*QX##M5-J6jfOlkm^pj~X&Ow*xvDIu5 zFOdi5LET!Lhu(sin{(n%6efvK%+EO}4x(CH55K-KlrJ&!{^E|6AaEOq7=X(Nizio= zMM;MJAe$|n^TM`5k9jtDku?2N){JVc&SYy7Pgk~MKF<{{NslL?m3PqkjxO^WxchGHY zbmQN!QA>uPi|(uH?*n8B9V%}g5njJ}=|)C9p?HvAW4YveTQ(*{$ja_$lJUyCd_k0* z%B-R$(LuW9tiqP(QW>GHZhC#Mp~GaINSz`i)`$R5P23+~0ZB)CUcO}nz{301nNebu!Uc-@Z=N&=$?!z-G zHOBa-7nq__#*h@?-a=Y$DPUMR$#I4O_h)%bgUjCT$!^{x>yvk8ECrZivC4Vo@EMHo zCbTa+^pB@zr*7Fy{$}g-r5bbSuHB;Ia|Q?nx?4Q>+Bp%2l;?g*%H_XVB4Men@u)pI zdn8qg9ltgEoo8CFwJ8^d+?@yrgU;;`yy-rEO!t~+v~Z%NU>E*_1afdLXpzjYsMn8I zxYYFUr&hj3LCyRHT03+~Qdhf7Me}I(mN)ch1@&tkE~=7o^$WI5j{T-rD1MkNe}8!l zZ}6x4EB2Lk-p(#lrAlZj?u=_Faq*ulSx|xn3aEp+uqbqNdZ?EeGI)1>@M1FWeti~` zgq-KI?=qzkX3oMw2zh~yI6xp#V%Id}N364PH*vf98-;?7g$c)`xc(ByE3e&db}~)l z+aXw>iYYlR%Vsug3feDK*u27}w?2!4;DsJZwG5#l8&U+1_bnp|^x8L(J0CuV3=f;+ ze4!Y$1EKlNQbNe5A$a1Ibx>!g<6VO&dAyHXUQZS#YlQrg@@0L$z0KB zNM|78xZ^FE_<|B5UZrq6lqgWBur`#+(E60#<)1P4-Z%5y>iSyh1FLED{lez{EW`0& z1HAq&WEUnK?0ltFRv|_4p%acyZyKFT85odFhU4t;p}w8W5JL? zYdJ2@={r24Wa98D=N(F<4MXAy^Vjj(-f&Ed;4Sd*?y4DZGo7KJdS3Xjr|;M?Tf?fd z6HwCR1&_Y-gF2l0{B%j@9c{O`>s08Q+NQ> zzes}7FgT_Qn>l`%dO9)fjqgi;kr4q$Q;$k@nX@1$fY4rG(0huYPrhGcj|EjYyte@?QMBcU>I#08!4mr)a)WQb_J!u%EJbGj>J&fhQQ zFH~U@W@`t=kk4+E0#Q(;mT^ca%*GcV#Y%1XqOY9~*9l*9E-G2|1hoD1+9XEfkUcdy z-=Ia2>+FRfOwAxssRV6vR1o*TYRIhN;fZlk<0>EjxlS4)kNdE~j?)k!?QXNbltv!G z#HQkS214)xjg;(9x8ro5oHvBGVSr-VMH9Pr;^>|q+e%* zWcy6n7IZ$n)NKZ>+`%N#crJcl0hJS#Z?B>q5-Z!U% z8%t?Z#YTE@uXTA%O=hqM883c|_CmXtEF}`)g*;dL7zGbcE*sLuPMu#j#HVaT7{{h~ z*U8F=6k(mKsE{oZ>ax2m15^iQan@U-6vFY8>VVsZbrs#uPQT4rh_R*aJD(T8!{h(6 zt00(sd%V@beJGBn0^aC{Mwk8pjUV`w!3X0vgD&cd@bD4J5|r}sJZYypyb?e&&!_1u z*Q$zrf%>!N0}doTFK8VW0g_Fm65JGvEoo7ck(sCQ}IY^fcqH`+)WBbt0#AEP|!c0fj`7yfbaJA zw+NS#+`rpP$rd~V3ew-dQIkD}3jh8FfrQkl4@5mGOpl*<((7PgD8zyDpo%s@wtcWZ(xqa`6HRf6wz{acw1_piNlwJ4xGpkR+{v22&VRh96wD7@=(W!MQ zcn>~vc6G&ecjs^Rf9UWXFUO`)zAeD!((M}(yxtLNTlX3Qss7FT;CD>VrS2(A5}7R{ z6Bo5OjGHUSdTRoj?LR(`*c?t`ITrS|M&P(ffS4{zGPDLcBu0M6Wis{sn-C8N^o!+nTBZK^V-B z%uIq+JA;w5H!Rq_OxJC>-+z3kV`P+t3C`wuMnzi|5N-Bdz`3FF`uZx& z+q_pdEj&DY43!>yA0vV`m37a4+$lpmKn>m)f!%Lz1-yTAxGZ{8?Sw?PJ|Rc{_by5O zmwSe@`}dAK&Iz*N%3R11AOCC}J2w<1Ff_fjWnkFf|N1mR4qOfjfEGczR*dhqLJ_r( zJ(R&0{|5bM1hgq{mLUQ1J1_ zk@4|pg(Mz_2{_0JBje-EpPJJ6{k_?IUq@XKj1Wh*nxePdT&Q?+pfFUW4HsM+Ns50) zd5UumJ%QHyq%C^@Z``lO?-VgA)W-^q)cf|90f#>-%c`R{(o})-b=kA1^u~ z;-A@XGwCec^gNquJU3J%=S9zCbU3@ z26mV$X$~cIifTg-@I*?6gM*vD5JR4PwMAEPEASKAl={PEbm5(Wp<;9DX#c{Cear!K zTfv1fWDap8%sG9Qx>cdm1nd-i`mt_5lYXnh?r%@Ci+Z!A0|aU*ZYu01zL0L9?2`nn zI5;>^&A}lG_L)PrfZB8cyH7&e%ZsE^?JS#-6gF|ZDxxnEV{D!`w>U!SVibola2IVN zK8b* zPxy;Hnaq7Gd#3qJgn6o#E+so@9V9^I)-B3rua8P}9;*nyqd_p4rs{pwWxE7s1tN2g{5)54Q<-@00>wt9Aw& zbUGUXVS;*pwmJz|_3ryIn)l$c=MW;9o$b%0d@gzr^c<$WS!NhY=Pe&H=1uEd^|qPL zjRTd6J$`*(5CLenJ6TM4xU@{K6bM;5?38P)0ASk&J(Vjy5f&xzR*v<)IFuVDX|Ncc z9(sfhuU`G+kC}M_Cw0m_+^KE!W^0&^M&`rxo%4EMoRO$^I8y)E&o+sK2eo{ey|WJ_ zeDMH7%(x6vB0h-A@xPKA834~AXdcQmlt7mtEG7ooP>I?C!Ul;lD6OZ{9y3_%sB!$= zCRkPy*zwNwnmtc`n%|Ii--ma{ zP;B5~{+YyZ`Xlm!tS%fxEdcBFX8)XxX1?I%3qRn@^kFrr&n+2yI2Wsi?-pT4pja+J zn(NgdB|xJle0NlzVFh(De}tIhGSgE?`HG$i<>(y10RgpfkaIog#S zPFluf*TgW`DNu$La|kRNXk}wnKc$aP+&1lt%jK}QX9r`;A=v>u*C7Y7;W4=H?=EL^JD9 zZZZIvb?V_l+v-5Sj1rZtKko>uz#HH}wP6-6rUlV7iwZmn4;Gr?Gnc(wD-rw6~f zx?vYzHKu#l`7}egenpuszQUjoD!56>aEoXJTp#lty5%zM=Ms^`JAgW3~#N2GWuz$qmGR1u5 z;gdz0PX@mq{v7JLirP%c@k=s$5mm|=jNR5?H#giLm2#?w6b78S;-2OY*j_?I2fo>z zd?Dxw2|+O=a^}!1s|TIcwCLaGP(IQKaYr0sK6$3&x37{>7Kq4<+ID2F+-93dD5RU9 z@feJK1?BwiuK3gVFTC-~B+yzvT3*jhS*``WfZS9oOh-cX5GG;}zMpUh-OrnSZyFz& zOW18tPs?qkBK}I+_vDMxBX;G$7_~oFz0dRNy*whId{mZpEQ>L+EGD%RGqFgtTxbz-+mDfM+=6AQfjB&+`3C;~K&(bc`!jX9K;lJMm$mA7J>f0>%NHjW zlbdpo7&-7JBeZJ~6*1elY9jyf(&K)>fZMdkLz6&YDM_y61T=`Uxg`MLrh1u$jqYVH zH5;Lhlf_87VG=^LgeT}}XrGlHbSp)^=J_@wN92)VjG zCq;o`d=}<&A4)3fHBFj9f3Vb;!c&xq=qsi=#l5lbXwg@)MwR>oYAuYQ4rVHFFq}|*ZXjTXW2}I&C zF{AJECuX;M?_A~j-651cC+6|U?c+ayljmie;7mf%QY1j_Fs9u25~ERw^zHy#9|4nu zmEU0*DNQJZOF~fds^HB#ahT1KW8&a8!Jr$23nDQ!E!o~pxdhAK-v!GIy04<=5(eQ9 z68?-uu!IFE6votal{6krrLRwMP$}!-;FsV;U#HH-K7A(5q*If7y8A=>vDyWukZv`P z=KTpL32L_ku=x_Qhgs#1)>URb{vkwh<3#F5c>e1L8{iaB)yxu~!W_P@wi>+kJlSf$ z7%rC+LLmBNIZ{Ock9D4_DCo2r5QZvQsdK!({%f7}k@x&<*czoR!XcL+^x=!C{X2dh zSxkuONU;3`%uTL{^+H)_{p%l6UKhLgpH}3Xk}mFUF3PuHJIs5Zo+yoDqKBYg3JSQL zmSl>!s$Wo%>QL6(Ea`!c=!|KKS&58BQt+~g!aigs(@!)sxfY9QD5G8X=M?t&DlL9~ zbp*EGcDFX>Y{IGM&zj#J>}fhjt`)eV(II5INHxP*j*#IRMJ6)2GDG!T10Y-zo6Bb& ziTJ5cLEncb7i#2Y^N`9^%=#w`b+mFpI#^=gi}b}ia_b;cD2CG^};}H>8}N& z#M9cSr*U5hYZi&X@Nvdu2J0Q`$|InXHtivw1=#P+X#h0T4RW24S&pS$ZwdMJIn2>+33Qt$VfD zWTlL@%|c5dsARy8P|baZgjUalP@~&J5D>IAQr(eQP4COJ!4RDS>f}q9ZboocDRRYl zYD7o%JLurVF^Q1bbaHcxMn0w1LZIzJjs0&lK@>rQ4?w_et!k}j(lKN-<^vz%0FbB< zC-uT*6K~FB4`CR6ip#_hC?`ysC_FabUlu86P;bz`PhkHcra=dXNB$y;AZe3HT^8`{ z7VnWAcP2P2Hs7h09d#v!%-ZYQh=9q@VG@_|9`jbILvc}2P+|vBfG|ibM3)hh^9=Ex zg@84+dbef|Yd64B@evG{iTRzb{VRAw?b0*g1x67;S2|oIyg~Szu}r4AFR<{)cu%lW za}cUPommEt&7{uK@lISmomF(^qOz1ZOI>kz_(sV-VAY0V#2>7OyN!W*Xd-@1TjEWT z;J|3qLp@LG?(A8whwAiFbmy!HMDcbwVmeN?xM1|j6RKJN3u($#wPdc@N0oc2f-n~+<}QUZ{)NLr$k#7d8)KmPeKtg zkR|+(Bn6LJ78_NHbQB6rxpP-{?zp>DT&>(BA;d}jpws1sMX-*3utLdGYu2pL6B6MP z3?H5}I{}J2D5H zMnWTAI?WZm$h!*$ek;H$q#v$IGQGyI&EljtHyO{(4uv!vKR5{k z=?3e)+PHR=QJmdeb(-Dk_-f0`+mesUPfJzvg221&d8Ia36qHBO3fzT^qgKpAKfv1x zeygn$TDyHSnbP9IRFqD63rqzD!4l3j6rx? z&#+GjyBO$@NVkx(J}T)~t1O8brHqNc&0r1WrU6?1crwgWY-dtfcE zw?B!`RLd`XI!v!;m_0Rr*1W!0zdI?n_pnOBRUy&y9Sv2Cthrm zqyx9g7K;=`vnF)d^;DM%p4h5Q*+mW$T0JlaXyed|GctnawhE&pyqY!|Q(~0e!j{iJJ<|)()`VR}1?Cx(}`XcARA11RE!_bmyym~4{d5a(7 z7fxM$wl}LG`MNjk3}pL*=@eNsD@smI`0c-!z|bWh*C&_sy~)wRFHkQWR&UdB*1aB3 zKO3!a-PbxpO%nBXD^)LzWFJMc3gE0>sk4g76mpf{KG!aLDVO5bw}-vYU*S!y!YC}T z_Ij@Ihq(f$P>>Ch>ZdLURE{cC*Hcp+ztZ7kPwh9`WBJi2YFbbltY8|wy2OcLA8S#E zPi6`2L9`5qNO_uFX(-xtU-X7!?yG0l2uUomXjfFgIK}n6u$W2L{N#9O1dxyRs(Yvv zvO!eR4COQ~-R;uez*lFsGiO9T&mJA8ijNF9w?Z`8%CsuWZcHmVJ@;r01z~3K?e!YI z?ebh4t|S0%z6@8M5kHzt9H@t-a!cOv^GFLvQBdNzZ2I+FL2ut=F{^)i&ZHz9!YlCp z*L+8Q@>?UC*u2}}RLC=uF=K)1VN6Eynt16()&+~PHbFfCryV45Qq>?l=9jWrcI!SB zub}-YA1Vx=n7Nt-LhR-m+kGH%nhli5T)SEPj#cM}q}`sOsQAgYn&suj5j6ogxfaLq z>TMp*o^y#d$R}hS!nv0cf>20Dphz}JDTCj}kmkox1`NwPJzhII;@nCTc+&3_B{3sK zqdZsjwe0&5Lej#^ALCI{3c4?D^u4MxT34YVUV*~Zf2Eo_i8Y()&5(^eT z+&}w^-yYG7Cd`~m!S)YyK?l3&jm721+}M|#KIUeA8Eo26oPUO%@M7Z zPe|9j*2UJrzn+N09EfNnS6qJ$hCzf-gn`D?% zhB9Ad^pgVeKN)K^JnP_H+5`~n>;r}9om)I|;g@&caL?)#iuumlIS{2I{cqP~(zxBL zSntduP&D%-$t&V~0^Aux5+^(*#VE1Xd{Tdf!!P$96$iXsyT!@DqGZcM?EH2mE#M(z zE`BbV+v`{W>-UsZoLJzM5`S$~uNabN%{@}(r6hSeuf4(zs1G0!zL&C#TIhdAJbF^oD#Y5xv&ZH=fS-+*<%``rpNe`eI^9tD*M($C929uf-U zB^T27=e<4sFp&&8Qj~15$fS~2dC{c%J+=&5?fK}rK3McVY?qhczlk(iFH7M&C(!$! zg-C>*_6SiHWw4QzP-Noj=?r-4F%+@ZN-613iZUW5G9^Z6c?!=84rAJUd5N|;B(*;@ z%^xkvc-mBr0pk?&@#}HKo4Q5p$&i)-*UMs*-wN7<+DfQZt^S7{%N)_Gfc=yd zuG?ebK~`GuJ=@ae^67IrE5{+wx}zC?N8uWH?s2$mSZS3AI~igrS9@!_4vLN{B#dyvB0;n(9!-`Az7l<)%(&cD8f<>pZB>{ttmnh#9c@SsBdlJRa-m zm*lBdR_nBKo2?7nE?34EzqA14Kiiaf60`Qq=T&PrU|8sNH`Vnr_uEDX*hXXVLS!f=iZJzdJRz4b(*~$~ zDA)grcGxh|SQ^E0EKd&z(DEd=pF~EZ5!;s&tzMi2MYEEUMn4fh|Ddf5^?uc?0gajA zBccv;$Z$l;cJw$|)+b!zVPq`8=WY(BDQHJ`zeVdpM6a;VMT7D%YOtM>6i=X6U>}I4 z&{QC?vLgJUULeb~KU*0$lEP7HDYx7PU!b03YYSa#y=FjjJFZ=h!pP+0mo0KdYrp8S z=hqbn2+YBYw^k#c#FJ$sa4e^#=;nl*XT~Elfy+v2#?bo@;{rLk1|A=&T4p7FR6}(=ljq zQFD8}QYQ8cDzRW?EW2t&C0l@M$B_O)8>j`%S^P>hZRspVJM4KxTFplH{Bl?QZXKxy{?xD;Rp~smy+nbo%GOi~ys#HT> zM@T}@`D*gTTT8TshJ9n#vUE0YjJs1vBm;$31vRJ?i;piHE?bFOsO!+83orBt@TYv8 z%Ju2(fE$xR{pY^OEh>;OP5@OLyMm4|2Couh8Byv>z|X%g1D#iRS#Mc2QfOq@nY7wM zybfayh-Kb@f*wNl%!F?=oi*;0$W*kp(4k$0QG}$|Gf!Q-zJR_sr&lSUmQc|9XMMY| zL-ddKaxv#sB%EaID+vC}PW%kMDqO1*#ZVY%mSGbckfBJB2Sm!|m(eG0)-Rt5x}U!{ z=}biLd){`FvL*krs%aF6bS zsq10<2476wDs9dAs7q|2)3JJJC5kCVjSorubdMrdO{&`V1TOD6Y1KNYg$OP8coa^v zo^LXf3t(bwi0#knb6Jg*nPv&ON`c;ERD@$WsM@>AV2~qc8+IKNhx*Ps4jfXRK8>2# zUW(Qou-76ah~752$TvCK`qr#Pz-@I-%$UDOMU9{EZ*LL-Of90d zJkwBhg69aCf6n=z6qz!$Z6tW%>T@0#Ho1McZTBkt-r>9748R%@bk>_xeq(6Q)kBVp z-;0@8FsE6ZNk|<~H8}?#Vr5qSy5b9mGzt>IXx@vYi(Z;x(Y9E~=`82SBZNOhP(Jp6 z7w8XujtKsF909b{ATFsdriaal-Aq|D=P%AV=+cgC6&? zbW*laZbnRuN}}qX+P_!>QUsq# zQi$tAMcfP2?b7&<3Kz=|n*@UO{r&Q2o$(xT1t5BV10cXZ@-o%8$eWA9p~X(^5tS%H zS+ES7=JADSo4a0pIr=(;Or=KgzqZ6+WYVhE%S*;f=pc4g1rsW zQ12c5o!C6-B_+Gp_x6~dmK1;~Wj&y-t`1JzhY@r1yzFnfKU+>!HP%fGnDzWoyW0)~ z1pBev`T3i@FRB2=A^9IzL{<6n`bzHCJUI^cD@8qZa47xggoFeSkG|yGRZ2}W8MvqG zeJa;`ZbS$S+dKLDN0Iwm%r!Th~+frWD>vCZ+9_+ahs?mnl9flVlT9)UBI#G?Jb zg!X!yN%^B*6Cm&^elBVe;7D$5?Q@FEn-T+8r_C2s;4y_Ih#XxI`qzb?jmf5R=6!GT zPE08Bj|X?%=Q^?RS4Q~?Kn6m4nfm#QKRe@L8vA`k|SkAG&} z?EVo(#z)5e@9p?+Hvo)pFw^}RmY!K{U-2-jHXm^aj&U!Q3B>-lfUSwZ_;YDDT_Myc zD2((Vy6T&mnek~lqz%D>%m!#?QWgV~G!PE}%HqF~7R-!CevqRhEdK|@SEwFk)Q^u+ z2J$lTZwH5;S;wlsm)oBdRY<}Ax5J2_py(*}MITVP|Go^lF`@>CIbVHcK`_Ynht1&s z9EtdcExGCpupz4z73muoNO{CEs3QF(0^ktfck4Uf{e!%K|EUOA586Q&soF=p21*O- zkrnx^{C5y2Jz8XSX=LVszj_J>YLdNje%?2%{Qqh6;y&_0rX2FuK>~s@6o%p21{|J8 ztAXGBomM)+V@nWcah21k3ma!fwAsU33!cnYsw6VT88pA??+6Ds$J6;pOfG}I5>6Z~ zncHeiq}D++IW`!!{^o+F`XR)fMhmyQ6+6d+Ur%`fn0}pcv&On@B1VhB!os@Nb%}+9 zI!$9+O9WKC#93~+zrKjK*bJcO9f`Bp`ZJGrh}#>V zH5)%;P@UW*TNbo3x*aO5y;G4iUFkn`RKeqaM>qj`$AMrpwA>P6Un56B%vd zR9DASnt0VGU}Z3hMm9q;``tj#qN5=yR!Ut0(?zbthb88f2}*~MaFu|Xid!hs-ZNQ_ zv2N#I&pfG(yO)Dbyo!@!#8FdF5%|GuO+;k$$DxbfgaER&iIUQMn|tVz-it* zfD#Am%ri#9p-cM0Gj_&I)au1Xs^NaxP$OsUO? z>mL+D^7|teQL0fa3uNI!t7>wv2eB$ht;JBfro-}Q8@OJHv`&+bwuF5$(K#I3xLM*A z#W^8chz;(eOy~0ZH0?bZ|Ib>lU`}Z0ql6goI1x{X{GnL8i}>|mZx}izHglX?$cK&3 z9fnKi@l?+c*nm3rkRJ zgrX;|pnT44RJ--u{@HNtvm1=cc4W-oyeG$;PpVRKOz_4U~;~o&UBGhX`1k``(-;JfbEf(~gY2VVFrvO^*9d z>n}hMB{y(RW;r-PHCburP7LYahKvaxS`$+$?L6_yII;5Q*9VFxeJ}z>GKk9}qnP%I zp5~SyC$<)Szdqe%!@Q-C1r8yKy7iMP2HxwJ&ct?*L~bkPsSdGkx4Mx*n9C0mAc$hs zQ1q@}lm|*zgE9xOetE0(BR^9HgMujnhhg^9v#Wc+EL>NkBg zrs5M6K6|UrHYxaGm#XXr0vtAz1#v+49eI>Z+o0^XPP(4~KZhBTFB7K5)}l0IBrzb- z_#B;DT@u^39_7z_c(al=>?(>vyrbUchZcNV`CL_*>aQ()75Gm&bR}lwd!0-*jk@+AE&Ua*sDUD?d+QGdfq`54dxl~hr2fN!HRxddbzG~3; zwKX1yOWtVI7w;u7;luHlTU_nV3=C3)G;Ki`nfNvH>cgWiL8(z?7N0HkXZmMpq;y{IB4AmpDjh?TFNHnqJh(cp=_Gxv zHl8KyHfzVMS;or$!AaO|R#7v+<=4F2651qG;ds}5u!{tet4ZzaD@ZJtM2p%5;14F- z=#|nH$Ff8jG!6zpLFs9PBy9oWc3yTIL4Xj_4=pSQk*P3TIz`$c#dNcl#YK{67HJ2- z&A02V(kjNP#yo?Dh3{)x8BHeUE*e>*3Ag-OQoD#jz?$he$)o9K3~Bw|?ymhRU1TE@ zL@`5gG{ruGYYJo&*FQ6e)2oAv(Bd4w<1)e)e1nlk+=Si@QO80_1Q^fo>8|leqy5t6 zY1$MC!{UN5%fsX+(61z zeS^G#bN<3V;CxuXe6udSrseqyAy^dx9R93kPG~-KE#nxeKy_1qq!g&9YA7} z&X}t<=0LLvKnkEcJKLYz9Q;PZxBuj18>mUM-J#4U#{ zm<(I$e?OQWQ~yyIMJg}tLTAPC_+p4IKwe3kvGZm#lVkzD=-N!7C}cKv%R*=&k*o^BjiOZbt*xO{2(%+Jnr zzziYX?bk}XH>iKCXx!S~E?@fagx_K$nawt99cdU|3%^{^Fh|f^dc$_|b41&TvO@#c z-f}}LGtf2v;C}zGvn=gy zdT44h!8T{mkYq|;&U0%{RvN&_dP()I>Y2Xpwq8)9 zt9{IG(bGlnLLDytKvK^8rArdlW!n2_b$ko>RdM-#nVT$pODGphnTJNsCEJ@NdZM3I zR&&ysMH2mD2ZE?&F#D8I4B?c5v8mlLBkSX??T^MJ^5C8?LYO(ab+0re9|8oG;RE0a!=~S$YRYTa;=+}?B9VZhztppHlQVKZ{q4AnLObb;G^MEf z?@W|p!xrBn&j2nYjpruF&?6yv&%$i3+YUWOz7p4MZj@>A7z40RbxSqNt3gu|Ha7WU zq>}^q1f-1RxZ(h4{71}ZY=>a!GhV}J38u+ncjJWLBYM@c`oY)e^=PZYuzvI=wKYEd zkbxo(oin&2vboW0L45*N1IkCEDG@61dD8KI$AV&)(n;pb;E6M&a&pCsF5h=6jB}nV z<1;mEfFgLcR~DyS_!%tx^wJ~*dEd_Rmti@<9X}(UtMPlbyU-e*jmi0eF2jb~y0^wG zjmxua+5M5FW%gnv@adHJ@_~YI(l1YdNW`gz`K9!G(!86Au?M{l^)erI!9I#`^NzqI z8Pv@u_9NHt3C9aeCoQtHLOvn;ibWWxs(>8DpXKke2j!rIY7iX^9E}3jbaZ7ef+@~D z+Hl8BReI1Pgd_Zw-HqXW^qj%ssfQOGw%V(z^iap{hylx~+trs#xOm!R3QLfhrB#r%Y+ZB`tmDa_fA z)l`Jnrr6g+9f!+x`g^mL<{`&Ilb}K54R*%Y{+3fHsP30?&m~{dJR3NPArodsJh+07 zUJF6k4Ov0U#I(c@I1)R&@l*%>BQ=#`u6Kg0idxl1 zmC2a|ssbhtX5Ea5#k@?IVh@3cFEPl|`4*Oh8jS%R6{dq?BO+sJyb& z0GyAY0{iD}wdM8u-N|ZWsl@*AJQZ7!283H%*^{!x#n^Ut)9 zz23VF9O9S^Z-c)?47qgZYG%Y`#LLZO)T&Pffu-~-e}0A8O5+QIEvON=%boc;t0v_a zP@gfbYFTTaDnTEpX*YSG$gThvg@UzW^vxJcf7u)(Us}Mxm^NDW9ap$T;47-msn+Kn zbPZXj*V`P(GYc^2jS3x#HQ?VkSdfp1$8Gr8&?!hVEINt<(qJKh#2++`6AOff1=qJHmk0{aFec z38(D3SARp{H>7=286L-3 zgFB+GH9ELg1(-0rTK*EGPJ23q^7su-0cKD^%)`Unjl~7ZSwhcEK>XQMw9 zO#qKXIE;ST_wImLe*{KDuFcU~0Tl4K2G{IcjsX*zI@E7Z8@@V^7U%G3tq`U51ny$! zgu)ZBIkmjQXTTsADH+DQq}%=0UJP6fIY`7Zoq!A5w^Pv%fasN77V&i5gWE%wIF18u zpC4ei(BaXGFeOc77O~u{!(*Vf!kOeto?O2B?6Uh&NEjV`kuURAPvFiJz(2O2P4%K* zJVmDy$yL4b9dcAJd&y*|Nn;aVkdFAeB-{=xOE6|JvV z@U@*czI?k5@kdVmnV&`N7ZTbsP)3V%@~gS-d>)vkVY6nhqVce*BB~B|$uY7fIXNVJ zQF^Qz&^y=ZhA(d|P)t)tz`Xumz#Dic52Grr`uuC^S-a2OsDaIoqTrVmrx89ckInB$ zg?uU&#AWDJGT49!((u2N4}DH3OMMK?FflD2gQoUoQXiQ;R5!NXH}36kmt)@4bd8{K zoK0Y4KL)sj-8j)Evi76n?r=dUA=nB!-sAv((7D3(Rk>@p#k^T2L_BV3Zrv?mOP+Jd zJai1apeuTf$6+K@Mgon0PfTfTzua~e;m!!rtJLGgKjL#(v|hOpAUdlygR{NN>M9XV z_Icjsgok9po+IMPRjQfa7(L;C^P`Em%QQ4NijbqMOS@c)jY+S*L?}3f^Mef?)`SMo z#{kQdh`C&yJB7qoVO$-JK_yyIkke-WWZCZZ+k)^s>M zJ0lEdAshk1XgV)Hj3>@e>eB>0Yf8n6RG9EMD0@^4Uek!xdFA2GhYmgEu9Z-8Pt%3z z^clO79)>vV={2jh^};AOankm!oQ4OHwPN_KxfV;iDRNtd1ujd?D_-~I?^yn51hNYE zw^uXN@UtkU(4i_rICQ+D)*Gr_CZX|wct)yi3xHt&7M?8-)X8X{?gzx8eM)qlb=Q9HO6la&+9N;NHfb~{=oAv;XUesP92NYg_TygFBTv&Lle~BmbfQ+j@P-~eH9Pny zl8}W8SX-+PlrRc|Y7>rnSJq?OBuneu`9L%13nJDcso=J}Tk-3fkDGiKUjdp{SY#vq zSw%JFC=WWku%n~H%dt|gfvYbEp>{?RM4e{$>3`&OGh#Ay3Ubd1-&zP_7Z&h~*> z0lB7LJJR)gx3}J3*HKm%C|a__B^j~*DZ!JNl4D2%S%W{)b$x;&5+04T%JevU)*|qD zA|D0&Jd`n(E4|8ni_L zOU@}AuOs~wY*+Uwcb;=|3Os>#m~+$^q;(-&cAU^XsmTml6`WG!!#wrthy(h>dFO$G ze7#8Prhx(#JTQImrj!+>IIa8CCN>j19)^JDMMoXkk@;h`|^El zy!C66l16c8fg|(<4Z0InA+@y69x2f`hrQ8Iv+>qcSr>vQd*f1?N{WNoO z7?~2SW*q=uSSiS$;dWO3s;JkuUN$sP%6P-?3K>)hqZt%O&}Umfb+t)qf?t;uG|4e1 zosT))Ac91m>f7Vo)Qe>ro)Vih&=d^S{x;ri2My`{98V^?A*O~`M8WF)+61P%5D+p1>xC>Yz%TEw%$YD zUMKIxnd(dC)8J^umsfe)lF`4i!b} zBcq4f#G{<^4QJZ~E4BXS@B z$JB=``Qqk-X{I9+A*&?uw(g-_rXyLeYxJDnKn+!NO$c9Nns%GVV^s)#V{gw>QWo|C zDpnqJ`u6BMWR{rdRSz8-sn19&k%-Rv^F5v}U*%!prUUL=FD|^h{ljn=vXK*el_(-&TElO5C&_?dcaCY&#kqZFOt`G#7thEHx?;ZWX$UpH3QHnDB}L^Jhq_z7xu7Vq ze7@6YWm*X9yeiERS`w+%jq;RwV7!N&J_1oWo1v7sDFC{A3blONd{A=H$ErbD>(o~~Mwz@e3;#b@zl z4aZz4qA+FFt6V&zlNocj82|miL-vjam|Um>n~x!za|Y%3mmq^i156L&f3hu=O&*4L z-c#LUWVNK7^oua8k2KfuSv9J_YE+j2cOH;u))eP3wP#Q!P4M-0&*1W{g*|3^f*^B;5x>s;#g`BJ*RNrI|I~H8|Gp5r;$hTXN-IY7 z_3(SMAwI6EgG62`NHPl6_*VnrRs;UzyC-BM9G|6pqQkmdM7IGa8Tp9TykB>fcXn9 zOEKzIgaC_M72D~u;-7=jI>QkvWi#R>%E6nBK znpZBBGtL57c=9>wQGB|_l$fs(2h`XG1JiStt1~>itCrpUvhjAbK70P&#Di|^|%~Cgp3QnvPnk~qQK6#ef6 z2brTIv9N7PrjVzBSc$5dnEiLDZN>av!&Zl9R|U38kK#TW@wCkCFJ&}QhY?}@|1kl`xb3VWYq+3j6Kg9%_FHxfhplj z6mS4+zdz*l-{l1vMh$U%ClB5BSN4V-jjx*_@l@uobU7b{aS=0Z7Rf%QAI?K%m)cAf z&6QF_;Pk5W@ACijln3v#=W0h29}^To0q3F*8ueZmde)(0 zTHWwa!hmLApyx91#+V6!SsUIOi6IqE|9z3flJY3yT;KM-#ZvgLcLxCp_~FV7ewR|i z%oHXKf|I*2<%^n3*XzsWIN>Hog1XaJRI`QwelD6_6qAH^x7BgX_I zo~LbJ7$tNLzKtKUlm^A}65PY!Cs2V$Xc>uZC-Hy4(7*ZvjOKjan=zyPC0gakZW#Z{>>u${VOq^^v;djs;{4Wq&XS-rlT(k~ zNWf7%7z>B&KR^V|fydEm$LpmQxmV-2di4H+%43;gmf`^6s`_7tPwt90+X!oTvp0?W z|1W8Qh&Td}XJBYR{vSgFWE7N1jFPCRC{XYYKp!#&1_scBD5dq{_rgUR$=~(1RZ!g1 z&Q^CiU{UJ)HcCzb*im?n1OoSA?nZzPl;8+czr$wmo78UV<@_JDbD6KHPwD=`78Utg zrqKV7q{Xat8kpnYUc}0AfD_vZ<=*r^x$JbbND2rD;9P-6#-&dLuZpmn?5;o6%lSb; z0E74!oft3Ag{sL8_z3fajR_cA9BkxyYR?zlhdxy02CWPJaZdTe!2kyi>^~#{g!O;( zi+GgY{%?I804GTsDfP!)!ia%mFF0uIDR`Vk4h$t3(*6&CA~*FN@LKRxY2ItyCH6uC zkEbK0vK!sm42U2n|zgwQ9uw zFh*dy{cmm{|06>L@rUUUuDLa;(5d~y)r81A8i-226Gr{Z{NaPjj%`|1Z|wGAhfp-}KZ)7L@K55s*$P=?3YN?yl#!yw_U$zW1}AJ;po6>jypo*NJBx$A8ZGn-DEfz+io< zJRJv6bBGx?^MmW+ISeb0Fvot?+39MNp9myTOv+wE}6RBo88=`2y5N*9jIdR3Iavjko}F&$wTglx^%x8f_k~t0RC{AG6=C{)om^ zdnHpE8ygD;4{w#BN()X`n$dbUb{-ul@>tzjWqeg>FzE||RUIK|Zvr@_Q{4}KjSkm4 zt-VWh7Zc0+Rqtfb+T$Ecli?)PJ=>=KyKBT?ZJA zWn6vS#iARF5*YRQ=+C=9ou$8O?NNb)N1W&RQm7u`7N<+u7{`tYF)=gA;;h)wKuj#a zW9iE`+;!}YXd#^qcVI;;_rCkVEn{SC9LLZz??pWA@#6w7c82tZXqoVAvWy)nQT3oe z;ZEVZclT*LnD5NS3SsmkUc=ENPJm+&BZTCRt)xEjIQ$Y-vrXb(CxIp zz@2RA3E+;}v3l2-N0=iw(jqFcNQhSApNgJ#MK-j5F&-y1<(vIh@SUWzL;`#64P#ZY21&bjlwAlpw(7D25HB;am$>hB@|FM@qTYL!3Q@aRAS*BeVrY=i0F;y@FErtxBfU8KTi z0nN}wfr2QTsiOS;Cuf;)TMOQ9HU>%Fm%`gK*}0HN4MOTYl;_@De(Cx^x={?NFy(Bu zc}i001LwwN`67gpkvuHmK57Pj4)gn)uA>i(iIUN6DtyP`CoP*!-0md+&^N6(36~`tredKky z#{#7HalR^QYPilfENODsxNkad(DN-0Nz>R=fW@URnL8a)uQA^}tzKBVhiBOVslVg0 zn$!02@qIlSM5Z7B(>*;rT=UdDd}D{m6`7~R2={E-Gz^dwS|Kb^J-T#Q}fMPVq^+yiSeGDKqo|oHo@F7P6Pmo zcRVXqc7llc{lZkA4loEb!<@71Nz%oLEJK*0f{dEIx{kKO)eK9IQBQBmst|Wf>>rkdun2yK%F_h^;_wAC1ngN{`Iw?cd z14t{+KE9iT4yi|!DnjFdX=^-8PC$@C3Ox!_y`C!99k{z*nwP8#Y;xcEr3z}V8OrBt z;zb|y83FSGxNs^4K)DbFi(XwE&%UmFm=a0%ZEc;8{)U;NH;6QugJk?z4^p3za%Mpg zOGuI=BBpFgMW$>i56d9;zGf<$UO`zh^w{a(PXbP3WB=`i#uSuWtPs_(tHt8Cblaj? ztv^3;k0;(s>!xJ#STjGj7*6D|)04l!#x1x50eF;0EWO9@_U3{2caoe9?8X zUkf;0ckry{YKj?albLmLZdK`eEf5n(??ha8^6nqblR<~{{9H8^ZTjWGQVJN&vR`&_ zYyJkb6g_~`e7jfzXv4(%{#bRPh`h#eUo zsZ(}qj1{Xb|Hpoyk*h$XOhE~IU#JDz0tW))r}hpQl=ysq<|Igo)a}O7D|j(}Dxc{} zJ)>H?CE8m|Vs;5z3dOIAscck=M7#;W0MdtzJG>kiOygQIA!R8@Bg*_B&H@E#!-D7v z2WhR-y*KE9%yGN#{W6|fiCHt+F$OSaL`Qsq?IJGI??$zZWnJJ&gG{82i`wWgmg1QZj^#-%Kp%rV;-PnqVA>{7&+{sNY?x zum#WEJsB`Esxu)^V1^h*&E!^?ctY7AAPvWmFQ}MmmT31O-ErqjUuIN$oinl!Nkb~s zL{oXfPcomo$JB-|Hs7Q!`JDjWw11_OC!1L7spVUMO-TXOP|0A!p6(+c`|gauc7OK{ zUXxPXihI~ccS>GmU4zT22vD{(Ng=f&mtlxI)i1*1t8D@M=b~(I4q-(!mK*QH=l8Ps zXAziEoEowTRqf?PvGg!_tz*;feeX_$japE9)*R~(JXgMz=n6~i!j9Kt<`A!DUu!He zU%gRbOx8k~&eu)Zd<0Lkzn9A|bjVXXGCRCC`b$6Z!ZA7iKBAy{PpWQ09K_;-jK`?2 z?Y4iTyj79O=cF&4n_b_=)eFpDgcRrX#I>ePbMf+0GBdFl$3haeDw`s`k0&1i+o|clVcs!eqIl zsZ&)ZYY6xP!=FVhLVjz<)7U&A`LI}PS1NmJF#9^BYNTzt17OZvy%6DaySw&HR^?M@ z%azJyK5`~)A|?Y<_o;g?ckf|PDC%p_CYmWv4^QTM^`UP`SP@rDWQ|TH>j@d~*{Yi8 zhBcr=d==q>`2Z0i4OmC7Pnsl1uS7>|mT#e_A;!HzBJJ}!w*}qvS4XoJncEupNX13G ziQ6z#iCQ2ks-*ngNDRb8O^HAtsWklOU4A)%n*Jl&EWx9os%y@3GB zfcq5Ef)&qhLP>R1;U_VRMc9=Dvhi;Xc2FdyfVYp9xKwXe#28ANv zDovaOXJOJ{_rqr*A&!IISiAg{Uyc65pBm-LF^&6784$8OWYYzrU-u`+fR?u0+>X$I2&x#py@=Po@NlN&%s0_A&&$BZLO2fKEp} zfEkOf%+8Um;`xTha1HXO$diyD_y}P}`bGV}cU-Dz)Zqy-z$&9TQ^!nCe#UQ)mAWi* z^4WcH^ldivOqEG93Mb!zNA&rEY89F<+7ju~)4Npm2vvX#@nwP#cS51yOzF|c(SAgd z87ZDRvGQocK_S(#`zn8;|DY=b-cz{zZg=%4HL)Ie7M8^0xv7UBHT0p9)}JQf=f%P& z>C-E+{P_#3{0+|l%?@cYEe&}*dNrtik_~>7RS$qS0pKK8xw%@a(s8DD=p?KFZ9)ec z?owWgGEXp|_0p}**ZF5E+&Kp+ackS}VDRGB+Wdz5yM4dY&mxYr0V)KXcazX^*`5s=Ob*_z{p>= zHuW8Zu}@7&+=URI5RSKgWChK935Lzy#X*H)fk79!%BGX|g5RCzFNI>vW=x>-2gmZ| zU37b^Ryy;{Zew&2Rq8_#{0x>ODPM#?!HBs=Ve+#v`fawk$39FqjF};s%9=uH4uJIq zG9T5-eR5F#o_su_VoiaipS?gkkI9~S0DI7fw?8j?xB`!)>^Uf7lHhCY7if*p?j_!# zkx&MQp=mJ%jbsI5z)N<(Aw&EIFb0+!y~plzHCCFbp9Sn9%D94rR>NRlAf3EoHA)5; zi6W@582^a%zH`Em@KGarW~ms$>=qaVe-W97=RvyzLF^P_rrvW!oOfno^!}vA`B={j zN^KXLbULLyDaf?+kHWD!C(0uuOLdE~h%feLbE)p9=!J>p1Pp@*dx)h!wuw{uaM~_? zF{tEszIv~VXIujR$*E%%e(1TXd#Y&yg_%?{2G#bFEC&eY@_Srmy^Ad-6{lT7B5Pq2 zJ6UINY0X?%G1JZ>$@VpEKwAxLMJ~gE-AZ2xbQbfQqbgH5=P;tM*Q93Bop8x zG-06gC}Ii=btNnp(!Hb_rTE2H;Ed?E$MP|CWV>Ko6WNUX`Yd;BfEWIv85_31Fq`-T zX}}Fo%v*G0mIawTMmFQZz(dILW1MtW6ni;~e6npkw;l$8@C6gWbNlKj0#UHjN~c`# zI!oH)cTV$WN$C1k>4m4`2<9k2536$iGf(+5VD`etL}E5VBQgu&v)G=@P4GR84E8v# z&;#OW@tqS5{wE38>=IDKx+8N4-0ZZX+ESd!0x==v+el;FrZY(Qi@C2b@6iQZ9H#S_A^I^K8X?@XzI)!$HgEMp=PBu*ai_% zz^f_dq_RmayK#wHbYiSP!jBS z+{Ap2V*Y|Kh1re;NQH@7*X^AmS{7AW$58Y=+v5cT%A${X&<8VewP@|!<(UbkXJ7Lz?MTAHq1zQt4mN+Sfu(h-VMI)>UGmgRtZFC9Cw)N9dlEBal;fH75Wny zn0#-~N0(AtPA9u$=h!+m5`TSnwwaZj1uz*SGy=wZ#Itb*>6k196Z0SulKvK#Vzsg1 zPl)+3W{()x0g}VD<@2&h%8c`uA{b*61&Vsj(yF}L&@^om-heqdxdq<~po3Pt(UAJ% z2Fy!LJZGw~*!UOWxZ5tsSCMTji>2<$&2?Yq-Ye;RcN90L;jK%ZWBrfVp{0uKw+Ai6tfZE~aU&KqMs62IYOo)_4PJ^N9_Ki3<* z%gZGSuyk*kbvVl{qJB%Vhp50rqIX3)OL+%zu66a{>bhfHsA6C#V3TUmi5nrfMtfu( z1y;jYg;ruGVkTd9{pK5c3pRS;TS2wb`1J~Wv(*;QtsE!u*~sfxbxTRiCefHRy>EWz z-i#mZO`*tie#z~bI1HXNRB7q$1c`)m-W|9HsjMqbXd4msmx!em6D6wh5%_EsOjaTJ zw*3GT#QGT}5X-4;E1afHkDLs7l`f9UYP<-G^ofi*;oD4sboLL=YuIY|)0MG^R9cL6 z6W4r$xX9ToFRR`oN%;ai@k`7AXP6$-h9498Vk=+aytB|S4WH+x~MiWg>a?rJy z$>I0IZV=nfk!i8D#EtMLfDXD0F} zXigBU1Upp)kloMsGz722bY)-xW&nQaz8~i{iXyfV5<+^SS5@(hnOVPdZifla@8c@P zD4e)g_7=X%V-U|2ZmkleQ(JK|=u}7P+U_shXM(-T3lQO9^gP8^*;oV76Bk_!JoHSB zat25VflcPiy_pP7pLGw}w4Xd1%*=r8hn5Xp_fl#FFEeEg>2yB=FPeGo2@Q;%B z9<35DXTRAL-pta2vP4GPU0z$t&nTRyMw#=)!=&ij`{jNCC*BqN~qD} z;nVtnkqn@smkr=G_MF6NzX@LYfWG@UdIvxAxio{%Poo?W5J0JbUc6xE1Mz` zyzWI8yEpe;l7%5_neXjQ9>5DKa9Z>g48v=?8hfts{tf7s2?4|bsmBJF{c@`JM~KGI zp)Y?Ze9W`C6afnYrM^V|0aPXsrCxu8#tTYqLD+M@& zkLf1Qzau?jJJO=qR6{*N@w-^IDs8pZ@G8K!TNAyENRFF*-kN;4HJBU)T9N(enS_4w zJFR*(SHTYaM)=34;Bm#St@ZFtp(R~dQu;xJj8m;?wL78S@Ho$V*stI%*|bYTYtn{4 zYqoO`n7gzkqW4&7RwcKUutRBKg5{LD4KmgpE z+ob1&K8ys<-y}O9`erfxsIebc<$TjmsaZN551Lj0Iey{w;emmGZ+Y`|hJY*KY?D>9 z%S~CB3lCB9ZIjP-Jh7nH3xTw??ue8`;J~xI!&Gd-JZSYCkAUQdRq+7%0GvrzoDZcQ zUF;<1kh-ELEc2nMteqN!{o&^RcNMJ=kzFKY7(r1L`B_$G3fJtaNv zJyZ;==CpBs3>p~4JK6$g2|P)6z0<0--ud|{ z`sI%wuOy@oYoSxMEm^c-&ih7shpijcyo8kLANds)< zGPQ|h&$M0PI8hF!3vqUM^8a0$ePjcID>nL#RHOTTB3O;f_Qjh=8LzWW+Dv=^?@9pi zefzpQLex-qgmPU`I_XDyyRv;{)<~obJiPBe;ygN59`0rvup=1xtu zWxKx47oTFdtJ`s}pg7BaxcqUJ@Wj^(4ZiX}qHZMI4t}wMf~>3&Ku0^h=d5vbZB zLLJ>rL?BAO-}-O;1cC6Ov9Xn17e{f=E=D;aVlyv?Swlp6dit+c|0N*${=am3ERIka zP*I%9VJ6p)$MCJ(u*F$N@SREEhqC{Th>%*)prVlVoNz!m`63qv28OCD4@k%|XB~Zg zvYlza8R>#v3sn^;dWI2>4HqD6 zdwRDEQnItF)crpSFgl7!A)@$6*<}|XBYavg^~9+4_op%Cdztw(u`#_-zzYQprRI== zdMEHd>MzXj2L6WJ)fKQIwXg!8$?g8~b1GQby5bMYU- zHdq*dgBO87IhvFVyAa`hmBIHxD|^2hk=^`H8Or{9BNh25$|EB z_QAo0aOK~xgY}Pmg8bpFt*!S@!QX8Pt1h&BLP1GPum0CEC?k5?;~j5oD#Rn8_@bHg z?{OFS#Q&w^<-Z&Py^qLz(VZvWU(9-H55XtkkV5zzgcJwN%io?iEaOkhr}Q>F2H1Ec z6v_X)U*s7}o^B1#E1?A^GN@ZW#L?-$-5#U4#U2lHb&kjD zQ%^;D0SUFsr?79!AaU{w7xQ};{tK<2OCZyQ`T2Y$~pA$lWF?ws%Y z^i$Z$`NhT5k;;V(ug~pLqrdWah;uaXml&>XZrbnAXjd3DvVzR(^wCJAxZeksyxaBS z1g~g41>WW#on22Y{UA9?&?8b~M}7bTXrauZkv5JjJHHn42z(j+-=g=!EZ^DAR&Top zG_5XykZO&O>Z6Re3f4i`xR=uJlXiF|B@WP+l!0ES5&frDGIwl-b? zVn8qtaTfO7se#0$tKSU1>7Q0y>)2ddE8T975Cy9-o@tuNqfd%n!8L5RD!_%2eS9r? zH0^~n;(d~nf#^8=dgzl*g;6W3tfGL@C!uWD2mx{L7kcEXs1pQpo1X>UYe=-SG8{mp zV@5=}W&uvrFA6*Za_Ij&usuJ6H)s+*9#)0l?FR2`oquZ-p|F~@MrF2y7Krd^WV2hH zQ14Sqrb-wdE!K?9)jQ$##fday?^JH{Clo5+fLpPVoKCGy$>5k1#7NlQG9K+sXv-Wp0B8CKB9T6R`2>7h>#+Y<+f3u#NI-@cSa2JZ9U`DbMZx>f< z>skP1t^|ViWiI<;5R_QimTs24fZJXkOk0buxA``ie7?&z=jN{p0B>>%g_QEXQ7jOK5VOBZ>vNhHnq0hy?@dG_Q6g3?XBgB% zj^a z8qURQg`U7m9Z3u_bK6!vm6nu zts@hv^=Tvj545!RKs_C=A1!bszJ4a^7F2UMqtw}Xobxh5ZoYPZ9gy60Q+_io0+SE$ zZO|_?xB;YCf(%G@(O~0YYz-B!n*N*_CEVHy4okfy4`!s)*SFjMs=1r2))g{4QgaFZ zn?mSycha4QfSsV0B=P*G!y79jTog=twlp4_9F&-ssD3Imqa>Mvt1BxMfZP9p>xWzg!U2yf(4pvUDvp-9N9 z8Q9k|4d&ntU?!0H3VOd2g$&efxJHf4Dwa(;#oWA4MgClvD3_R>a|DuS*r6CwA5GG9U&Q8TH&fWUjr`tUUZ=}^B3*WpEB^Yx++kir->kn?0wr;ji26;|vFuFBw+*OE=+ zD)#X5Dj`SPkeseC(BMDT*&5cs4r!vSppo~VZm0`TL%ps!LCGK9;V#uo?ci4FB_EE- zJ;&UP6T|~s62bqo4d zq>v)nmi#Wum4F<5ZnMzpb#4tzN0GL(4KI^fj0)sJmW@;M<~!!CW9;T@6`!!^7PY$H zH5y3YQ&bIoI*&$CN!1+zlo9KU2T+Hh@xG(z+8K9|cj@Pd6(t!V1p&;0_hC{>tVkJs z#h2tCtY0ufv3k~^JV@Hou+F;Pk)lX>|+b;L=e=QtFLc{A#S^Yn(gs3A8GPs zihW3b{ll$8p>JgovQH-hdVb(+)(dDbf`7gx_IYFXHeK4 zEVU{RW1`X{_ow^V{;E+er#5~G6DpP~6+JNqG&vRHtR@xAa=qHAyH~{;FO$TvELH7Q z=D2KUWFR$8o+s;&UMC&+!&b$6iiM=X>-s0-&+Hdk@`EnGQ(%H{8F&@qKtB$!wfIpL z)OHQ7ue2=Sz->T>;uiSw2{snxcLATyxsbLaHT$ubN7s}UmG8~DYGiPgm|mMpe18(- zK>hLB1SbLtfxej{A~GsH2~Z;GjFi%@X3j7iS2|uQKJVY(tt_-k4=~-mIyU1p+e8B$ zIVi_~RDaItu&Do8i|e>FyoC7p`&#Z3MoS}=7e01#9VCZ>B|kkKmy$293ZkbgH1!C% zZ0W&6W|RS|SK32t8)CKVXjvht2HYD+Yz`O%LPR@%sq;E4Bs%=O2QH&?qA9C%-J2d9ohswN zA20vyVtayQh>S~}2v`fgw*6E-XvTg*Wensarf?!4GcBbp$nZtTl)z-O_Vv#=Rq0WR zyv2G?NqG0DMbkX5o2beSBBT~6mEZuR>?a-gen!CX6)K;aMJDQ`ANuC29t#X;JIKmy zf$DJFnXsBjBnc8N;05l@MButA`quU?n&@=`_R-^mMRW$He7Np6>%Qc2JZfk7|BjxNSwzP7VtdTY&-M?xgE&+!?W_NpG?`u@15 z+uHZ2N!`UZce741k)mXR!$gp%ojCW=&(B@5AgE0Lo7z2kjm^B0TD}rXdY!%MMhFep z8X>(&mPQ*F4%FL6C@OMZtFad-Pnf9zzI~jt2VwjH6!d&Nnj6qmZ>_><|CTzY+Vabr z@s7u4PJ#2y&pZN^bH8jitPZII&QXv(L1Gyy*PZbHoS|D(5EjVC+^OVqT zdE2Asw!fHf9?ig9lmO}+0Tq^pMrZ6b}B7QKI(e z>ul!5=4&M>87CJ%;V8i%EPU%4X_pY8E zJrPp{2BO!74p#-Vg)oph_9L$FfaEEJGa_Ol@=)D2w;JJlMgYSomAK_2D9wVtT)5l< zI<{y7awzTf*}mI_c%vdsM_}i1RL9mlVbIsOXKGXKc!`J@$QFXV47fIIZ#RcV_fw4g zzmR!wv#&n1BjT@DCi!pw<_^GWdArqWy)pXD`ze*jVKR^1T#>PmOE3G!he4NOt=u%Q z%QAr20D|gK>ZD^!@TD_bnkT(->~`GC!fA7pX1JVUospk5~n&}h+|Nv{pZ5MxaIzK2ao8A z9T{^%5;Wl4kbs8-3~2Ac9QeG+lD=Dmt-afZh=xa#9XPHsJo82JE(g&0=6peThg$N7 zLOh4ro4z=T1V-oFe^>GGzP=ft;CF5@TRL48^XPcb;Dcj~(ePD%hN|wFL9^R_38)m2 zqxIt-wZ#PmeUZ>EBOE7DlUN2COcbOD5Z-!a;=NN-oc045m#!#i&0PGNT;B#2^U6BlZRgg?LiJ(4B<|D& z$vn5u`qPMKOsqa$p54zq(WmaZBQ{t8vu#uW02pF3f{USg3(an@Fz!r^N>LwyR6U=JG(1~*ypBpkCDyDR*VRCzHM zP|?-N2(k+eLl8__ZteNsRt(CmS#4MZ@}SF;(DN8moHEnAhab8&=T!J)^ zQw|Ihfpo7QfzQNX_CqllJD0ZC@oV%mVbz+SV zo}lueBr3kA7|huAz|c8+DWU|Lw_K8<0Mn>H(Mo+n+)P0oAy+zfYz=Gf$>UKj$}|$9 zKbFu+50pC3vK~w_cIsjP#v}0WX;!^kT`dhySnudQWppvAA&5aVIXOgvGoeSR5_s9E zOf(06_jl(**;sqw^%_vkra+K-zt60R6H<`dhY4q-&1sB+6yX29vQPrY8u&1j*rz^u2!n?R5T@}POv%NeNT1As z7gO!CglV6=!npYNoU-MF>^IT6)&>D?Lcmz6(`awks}TTN6Pba%nR}Zsrxp5B=1|6Y zxxnh(?O|)C<*AJSM^5v607p<@+wQf0qZyTKv_I~?`gDD$ zl*u(~@e7yT_#4H^SOl%(wh7!C2K}&$=l<=NJKo)l-s`)S8%+8AbV;r^(%Q?{Fg>*@ zoTiLwmRPJ~u?(8tmj|PoqEcvN6w(T}fs`kpj4b|eYxud{Za@_0`ACCZspEZ2Q*_oH zA-6p%2+NIH;QOV&GZ96BPa9nQ9rz)(^)tW0--;tM@xP`926wG^ zc+Wm}0yW-l4|27lisAv(qyhgwEq;Cs*lg z=V+>%^bhWPWh@qOP!}SPx}Gf+ofa>w=VtJQf|(r47+I#Yq-qX2B(RDZ5lC{ccV=W` z^~}RYZ@Of0L}_~|e5+X7J(JnPPkJ;k&ayca;g7`wLi1tU=z024sdg-RvDCd?tb*? ztR`a`oX!cmDrB+@re`&nyN(XInlQA_PU3HlEKi$Pq0$MLFFDm`xv zTOtt;YN8-r~{KZpDCJ6phL&On=f(udBJW!NA{@O3jT0j zxB4#eMr?IkGx`SUSfFwuO9piPq2!Xf!}FrYs{#XG?Jp5i79M-E_nG!bp{JDpv$&3w zD|s6e&tjtmo@zz9vQ2$iWKg^BTea?K9v^Niec8fGz;;x3C_}K&kCr(f%F{2)<$Cor zd#rGY3N>>YJu%|j&O{3LW`>~WN&R9lBu}oEzm)XxdBMzZ^~&lhBY>l5+T_r*K9&s+ z}@tqm1p<-qzOFHS|xhq_4Mr7Ch4z zG7~Mi`jW>pY$}wHeRq8@Pp}GV4jG@6XjRfcNZ1DekKST0VmBty0@9L|zE8dg3!8WK zBFA&-INxT_U4z{BdSN$Pi=5mGM*5sst-wFi^%qtn-(&CxB$JR=Ok7_(9Yh9vP)$9% z+!@yszG>YS6Kfj-MV<3nXcVgHfDD$vZKI{me;nUN2tTAoCC?9S-qxdOBNLHWOjF^T zPoPaK#t&@~4>ept$i17UZ1Js0Hl4MZUo?r4sVd50)HMI~cD&J~KV%Y1^LDdLtzgf) zf2jw&++HO!#Rxw3Om_b*h z`Ysb_S}sutgxT;6+(5&lL}F?hyx8;mV)6Ecok4$ zv(Q-SFgc)dHoILJ!71;=Q@*R58pP!?ts6%6eX9`_8NYR7is@d{EIz^tE489v{w zJ)_vm^uKAZY3x<~eJA?2HueI^5#b|;`Wvh1?8t2u3r_{vz(fAG8_6-B8%wX9bEswa zK(>(DIZy&?yo7|sc$w^n6M>FGo#FK~=RF$#w1_J-(+xG(u&6>eNUSIGIW@1JZ6};8 zDb33PZj94}1Z?XH5*|vpnRIM#;Jvq|73#oq>E-9NDV~NBAD}ZSA%F^B$z=iU*W?OU zw;s6vbyy(qp6vPdO>u7Pkb#$6$%K`yyj>cL{M6o9i`lDveSco>cXyYE7mk102sB{( zp%sR!%1DT1KTzfam2kHvitH&{TobTn03%3AP3Q@Xx}@Vmt6*)hvp9)nn(pwa&sVXzNjP-f$ z8xiyD;y_S)wL6y0sd=@y112|QX)uHJ@vb@I4?chGq4^ePxe5juthOj<+7fGkgk6G( z5@7WN^X`5!e)Z=^^#&-(%!f?*=9_5qU{4H*FHoh(d|e^1gYM2-NvsRNp2s}99*ly2 zem#?|&O2M}8LWhDJy9PD&`ENgte_d8%&7547g<)s(uyQvUlDfZz?-)RWex>a4eaIA zW%%AXg8F|5;!>%9u-tLeFJDxw2A)PdVPhZ>aGTt~fc^xIm|O8d=c}AQq!`_|&D!j& zzLMJWB=JI68D=*YRGi4Fj6`0ytS|y)4(Mgn{Xt4wQ-eh2V<~Gc!oD75nZke4qv6b? zt8BzC#gV-32)6Dr|2d6(zmXh3f?5iLj1u=p^n?{j$P2itk#?Lo6BY}i`SJ0GXslJF zf>2;u?gzs99huO`q-;iU*Oh1|11TT`{K<;{i}24EWK%V|1s6_V6=k=@D*XEN$N5QT z&Y`QPat#L`!QO&H)vqQeN$^3$)o|1lT^G!5PEJXnLzj@hbFk$STf5_WJsfxEa`k0+ zF9Ye2{TCs``PNn(j+_s4s-T|T^$pHd&|4c&w2+nij8{qDuC&FkWo2>3gS<`8>vbiG zBKXt2f-hx-Dm|~QbOjOF#c1`rAJg}K&mk1vosalYvy znR!d7AHhIIx_`t>$jKW;KvH+VzXQFJxf@5>!DbSf^*KTd572cbFJI%Kzzse%VQD}XNj1I=hxE0X+Gxhc?uhA+2OyZ zT5`(L2(Ayy@kA2=0GkIv2#!D>_%1b6Qt z@^c+>6DGiqn~S_mnl|ZU{5$(Dcn*6@hVb8nDWTY)4wxr+DjLR;bV(4Y_NAj^92^{+ zoQjG{{UItNeFivz|K;g>s;Z)`!VjD@e;NatqJld9-B02v{=M;gX#pKO{m35#zCeW2 zTGu25ol*KllWc|Q~J-d z0rKU)hwDL-{)OtFii6(W^_RXXBad)E>t47M^-%%Q-G8$QXsU>@V0Zqp7MvTr6ov=i zNyE<0{_7I{|7tLxr#I$a+1xCc2SuU^JzRGi8xYa{d)t@l^bfB-8R$>w zh!Wq1y+euE3PC}^A^Bsr07v18hi7lkp~=6phWoIi1`c|WRNugW29!TsU5Q*9dJw?F ze}4ThF0w_?Xf=RihF4aS{ZZc&fu4WNOO%L*4gJ?Wu>P^0PjFX%?TGmX^5LJwWUt;@nO3ON%Z-M^<2&|J$NR30q_#SMSC53nRCN&dO#fNcK1MehHlsPDeu|DPHR z{v%pXTrurZrk6>tN(rpC5fJ(V*2(I!Z#<8pF0$VYB4Rf5dbewy@+c=T)G>01$tvTo$l%h;vY!N0~QZ?*lyWkJ9aYWnr zy|$Zs8>gkLJRApbU%0;5Wy?YJkOyYi=J1keTjF_TZU^OPw$j303tE+RA2&Pq>z?I4E7c$MQ?ya5XnlqaBT zU27jhg%Awrht=ABGh!mu4?YwR6bCtWw!Btz`NqZq;hFWmh=B;i9;b@RN{3$VC-pz% z!WtVJGjN}PZs35bIIZ=`e$1*QN#wHo^3R;DOqaP|S_ z7?{pdmFs;=1||=gVN&6IyW3h&hrsBkp^Adf%FJ7_q!MU1^y?53gdHo_yTmd_fcpWs zKmD+eh+`FY-rMtav1bPh((inZugy@1^B4@9JZKHjsliBUY=|@k{EH&xmbxW}%G5Tj z_3gpfB(wnO@q}cm2?}tLB_ZSX$OrfbyzWgEhlmQtO{>M^QJlYOebZ4+>~!mrfak&L z-k{z?yfUyYNJL3c{U98dhR2RYTdP8sLH<#@#I6jeG8w4p9BF}y=syDdpQszp~)!VcYR{>oub8F=!X$sH;RVw}+Ia&0av7?-zt zGbGcv>`N}8`uFx`pL5QvGsRk6ezTcOrjQhwu7xeps4$(Aa)YStjFD;FR=`KS$-Un; z`HP8nqg2*t=dEdf9>c2nTs&htOz`^Aht6upQJC^zWd&ia5^dSjcyd3Oa?D?)pu-T1 zshJ2kU&jrkunsg`pK=)n)N;E0dtrU&J5Oew5JV;|1I@LI9(8Px+1DhxdlIkn`~k87+6N0tzNmk^3nalRfA zr0Qq+p=~Y?cb>=WtpGXC)_A!$HM&+|(9rL_v6#S@;lxG!DRvEsh{P!*@cwGwJa(K= z_6J?ZkL&liFd6UyCn|83go92|duEvl!cCxZfy*I#w#BRw<@51sPr?B0BNiWa&ma+R z^zxj1orqLhXaG_-I>I_&kyz=HAFb%yUZJ;pTvcJmQ@%ve#L^b~eK_4(`EIgNne7?~ zD^kYabhzOV;QA|DA3%T9Ik&|A7HCA}V0bk23^(cb>#F*|sBp2rBn{dP7|L~Pr$&AB z-2u{KTeK>3_NFul8isRhygTs_0QO%mXF;FryDt$!e~(E<9Mc9Q&W}94X$2cYBo5mX z#b1*Z0k9XskZgFYTk=n64@R)L_DVe=VK>gQojGc~7=jUrSc_bX4S5!_4Cg+*iqXBW z5P62#txK=+E5BZlg*$76($0z@RD$6E{KJt!y4$)ucoMS~R6yN!hE;DDqxbmpnp5&)L*)M(w3AC)8`5%gklvoQ$zgw&UL1q5OG;_SzC zb|T%(@z@`vkq)gn_^b+v5v>W{1gC_fUcTTl7c=xaKd^$kWIfq+XqBt0SolUJ`2m*Z z$IKTqP>=8&bXmLGv3(jy<4!NG6gj!9&L8WZP<^EycKYuYkm5O&I^w72)~#X zof;Wz2J4Ja(q^x;h8=1nAie`4MDZ}*++JyA>)1^I4&pG?4&469?IG&EZ}Hb!rX6g5 zN!8GXV@}tQT1hfzIIEn>weQiSR1H(8Cu6NkP@rf%u9oNq%aix-{ZEKDd6#i5X+K~D{AtG!D8?`c=~CyD3E8*9<^~b$ zfb#y?3BmmU&aw$hjlv}f6rlr1MAp|<7Tp?^P>IvWZz{V%$7XbvcUXJQuZJl>+9?qu z)1Ot;M^rLYEHCWip_O_;Ulm2C`B7GT$#1B7I@OLBC;MlI8ck_p&=IYBZZPR4>VDG+dx5?Zzo_{Di?OpAZ+e|aEmXH&}js#0GyR04!6 zUaxH`mdt2>ee4kBT_2)=%w|0k+nnm)lVvMP&*UK5C?jy4|Iou?O8aNNN92 zLr0y!$D``K-Jf^wVk^nqMBjc@tTuU;q<(Tamfx@ks|8>5e0?K~V5w9Su^%=ck=a2- zOFGVB9bN7-l{984`$C=*?nmnzIA*3FQG`rDi5YSG?y()jdM`c?YBp))`}cI-a*_U& z$`tVI&m+o`<7Zxjs>k#U3>}X7ENFXvzZ_VLeKX8`YMbSg!e*pq6Na=&R`wDQRA~H} zZzS>c`9?B^Vebk05$JtwhkxPEf|i6$=&Y8CbT_HY_EYXRd(ZZzRs6Drkfa~QA6P%$ zEjyfjD5W(@Q6MSj+^=_A5QrXtamwIxl6cTLjwKfH(cW#5+hC0Et?>sMO}_X&LP;;h zA+0_r?mR& zE3sZ^h{k?se!W-!r2&m1qO$UgXha!)*WRFk0Q&S3SVG8px$I__9)J$(vP-4ULW-c2 z-YTT#8{yUfRJZgnX|t^ewL;!j^wD&U4({zN9i(r8bfy)ozc&Jw<)Y8N|XYt zmQn94`kS+_wF0%`-fjmIFTvZ|ViifP-EHB<)!H|X&cX0ovY4hF+_9@$PgP{Y2o`B)=dh~&p0BKY zK+kGRyi)~uqxLr440Ht8l*wGQ|WGy?glC8mTm;;E+wVAq@+V>Q9!z3 z(%sE@=URL36W953&aZW`(CM7t{NjDbxX1mR7m7gAj0sA*Vtlf4I08?|Qx|~;qnGTI zx==nf@kNfuq<8=LOPyhp!EtfTu9=^a&7J=3&p4fa!)P*h-I|;aaBzB$loTgpmsBsb zoXJ8f>*`yv-a;k%P1yyCNm?uhxw*Y+c*!@_G*RH(7kN(@|CCXb9Uk|xYWOQm1+hv1 zPnB_6knxL2_VcJ8QIF$RSn^TYuwz%vwd;^Cj>W@Z^!gx=x-EAMCGFq1#CQ)&-n!i(kt1n(6L{%*qzkC%m_%%#P#7QipFv$5Tu1qjv^4wi8rLJVHH!q zbx(hHc?1Fp_DTXn?&2>K3l%oDT14cCsO%)LLO{XrLVH(X?aR)xyz)ecEb$8gLoBss zsv6D`mD;w+Qf>Xzr+S0=KE0`$UgXnbO4yI6$t-}ApY}01P%U<_FV$==vqo zD};6)B>ZqI3qr^td)5RwD)D`zo{VSke-L+Uhai*h zR%2uh!t~mWZNK_jU-4cCK@)MOTX-ItOMNGs6L9I1jjhhb5tA^6gD9SzmqrH~fe}wC z`l*Dc*H1cD@wK5aH$SiuZ3z%o(ygweU-H|P_}U#>k5@z_)9V^ob@t-a*vO_bw=nBB zzD;3{6;-VTEzS&{fGSq!pqqe0gBJ5S?FsY8V6X8)eX9l-cig$W5>et`*$?^P^pt+E zOJ*>X#v=nLH%JQ#FRjrBA?geU%{BhvnuBa$F(u@4p-~)PGNxIsE!Y0cz0frO*?7Ij zn2ds`311)$r3x#IBe|mxlIfFIZhH+bWb8-OB!^9bY=WXe(04cZo1b3Hd4V}tG7bxE zlnSY=noQC3s`vYORbnLlv83vSJ=?YUZaYA8e>3T_?jG1<1(rQ!+z|vK9YW{Uzb%g! zRf{wTeFlF!}TST{&n3S(dx_k8%p zb$W#3-?9c)K3On!3y|_35N5V#YcV1=ubvZg*M$o{_hko&?b}hv>cAMGU2%lxc!5%7 z{WN>Hj2`Aj4FaWc$Nl$paj5U>eZp673~_4lbhJ8T9hGN*>IWIjM?pk&CQ(Agol9cNR0UdMY%?` zk;J3nO*I07sMxUzxG$HyU^s(}T8$r1)ol(Y$+)E7=fB{x8e)zi@`LX1RJ(~fNNcDR zB9j>qwXjtm?zt6tjYFyACmGg;(uzWTY~w9j`H^ zQpdkaK>fUBu+y&TgFk4-X3!Ks>~Md4m;iYVPjhK zgHRvNg0b9-csdV<2G%Z@XOm2)=pR3F)yQ@$5gsc-gHjBYf}1ld1y!yix#2MfrX_m9=~BE4sl1u3|lX^TAB1;g4JJLrEY=t~{8kbEker@!3$) zc;V0h;IJlNA=}4&7%>@2V=$uy&#s{TC9^XLEXv?7)QYKZ?~Jnry$Y3&I$TBt1^N&C zwxq#bd%|QCzGAi)#=HB{nMjf zTiKwBPT)hpiKuR@B{E9fibWW~i`z2(SWY%fZSX@{MSd?cw3=tpeTQH>Ldh+*?eYu` zA=DB%k}a||qTYPRWo>nbA&Q{=dkW0>4<&dV`9Kyt!_jDoh+69jX*}=YG^XmkT_Hh1 zi+mJU|L|8G7$Wy7fLq7UM=g_#(uDT$+q@?;^cK6>=k2Nh|b8AD`+Ycqi}6^WM#0eSNf-QuY*9hbHi*2+iA=!Q#s`84@R!M9d z7!;(*NEQyWUke=uZyzw0g~|xf`hxCM$h(GG%WJbVty1gdttxzVZ$4Bwf?u2=yT0vh z5vuTpi?yh~gBxg&o>ox|6smZ@_TBX;U^#)P2$_>x0kw!sE?N) zy={gZqH$!q3`b~>Je*?{)a;utnPBAi4CFyO@8>DkA2%V^nJg(6J8{zB)hQtUffa4bRf{a!JuW4-+*e>}t86KA9q!ZEUm^VF zC64F)dxnc$Kb?w!Qc;MC9gk~;+M{i=;!NLPCa|G!Wj51iAXAH z_Cr?9qBgLOYk`i@H%a`!Z`8q8z3zQ?I(Axkdl$}5@Ay?Fs%i_c3(WIPrVP==01nq+ zkpV5?!ugUOMkIN3dfqaP%DmykL1hxJWsVM1>-?T4lE2h_+CXtlGG>>|(Lm8ZBweRjt^8O1J*jxcy02lv1b4mnH&_ zDVA}|fb{O_AR$Yj@r#>blbLL;L+(f|ZajYVJ2A0;yvv}|s?o(v{_HGW%I)K?wHs-U zkbP83_Pxr0PWqQ6QOKpl@+j^gNg)$@-aj?`{=F5p1k|M8o!ds>MU~ zt<|X1QdsM|gqH1xev#@!s(%E?*66t}CgC#JzYrY*R0BR{hR6UTpUb8eQgLb`taC4c zq`9$isZPshjcQ$OJnE~EkRQt;Rpukbaezdad@KfmLzDbhbEh z9<>j4%b>}%m84}qzlWbCyd*r{Z0)M4w7*cR2oxps9uCXUQXF;_GHU2pLccsR^I2d6 zd>|Z*Fg3FjOvOv)fS1RJA(eu)W}qr2w*jJ}6B%6bFW9g^1jhvdhwWw)_Mct}JS?bm z!F*6%J}f%puQToa>ZIe0&f0ZqXY7Di-P1)-Oa>BB&v8bYhLfq#&_SV9wJZ+}UHjO;zPRnhIRlg4-(a>l!-j}50k=BrC^D5^@b7)4Jp7v)Es(~h&1 zRf&c}mBPTNUcEZFsF<3*#o_*@riVLQtnPz@8_MI$)5h0b3N~TLUP&gj!)%c`mzwEe ztKq$Po>*patMeb!xuL6DO;GmIn|yEi{#FHyg*ssPp*Jqov;6fuIl-xbnXWKmDA%rx zZ}rHjClID&Lw@_0Nwc)-0j=m9f9AHPC(T=p;SPX?bBrPwI~3Hnr$d(U=4vC#?F zO7`vXTZlh7e{&9ru$xN1A|9hw%A7m#P*v8vg~?MkHZE?#G^V(nC{|4GefqovS;(?4 zJ%^E0Z8&dBU&y}*#pzNlD79zs`X}pMfanxs5AhO>mhY1#n##bILnqO(f2Woz9NNZa z(t|Vh^C364IMxK`k#Q*Eci4YQ0pbJt@Fdpx)Bw$cCIZou#3+5h*ifuBP8{;#yi z$bSp!&`)prKL7>%gyQ<&8|@ebR1DTOf96qv*D8)MdGa;9PW`vfK1RdKt0O2VNG%@v zns@CDX;}B@!-1Ea)J2pH<4HD{ zW1c1xm%Z)4v|{_$QJ9Fb0F*tU7?c+ks(#8720~ZFKh{ft&IP zrlNaoAG{W>aDwmoXO)_5Xly_#>~O@L*cIe5OMtPs+1siwnou3|7O>auB$p{MvIS+p zGH4a|pRc7&&PYQHb|vhUI0DaYK1FE!ReC%w4p3jTQD~^dHvtJ0IN0D1N&j9uUlpV6RrXbOE=D8)__|Nu zi2l0}8;oM4&rR1bz?j zo?L_Adi(qJ4;NY_egLWk6Hy-cAZqs6xc{g1Aka}-!1zwSmM8&E+atPTi697B>e>goG8`xwCzD~P=!~?JtO9e4lOJvY1vrW)5hgvREl1$K-qCbv2 zz=lbcjg8Gc+3U(-CMKt*xTCl@4alC<+uGaReFW?RP-`BnWhG-tbcaK-WRH!0me zWG2fd&#N4BwC`*vFbTRa-Qu4$i>B-^*dwKN=8I$8cR8CG#m_V>go-&0)!AGAH6wI*ud|@oM@!Fwx z_Jk45l^wyC<8M$p9OuX$_B()=1)}QPOkn@XGoEJ>{`3fa7yd-|0G=erxFD#Gf^37} z0UbiMN+8I#jrLx@|3eJOn>PTgPXxOR=Hqd9(iXU)wHt`3&k(7jAMHz=wm%0?-Fw&e}&34(vN^AaI<)Gs=4rc99u zPgsxpsV_SJfjSsV8k)BT-$_ye=MF7>f>Mq*Il_*yzaa%Q_Z_T%q z3He+?Gle{~)Qa9Rkn`N-L@dkT5!+5xB;@#K=kI*&03FrR;A42Iz&H{iH6WUxgpv%F zqry|p7(=`V<)wA5TS^Dz4C+ORW5N$_@90ApDJ78hW(IbrpYE;$TyBOe2nEe+ih}C$ zjLkj&I1- zc^qZaA#6{UP7Dx`3dpsN%@3}yg$w>o#aC%W-xg8vzjsdMwx4^;?{OpkkuT7t;Ei96 ze$(MN=ris7sVw&WQ4L7p#tBVQ?>74z+IuO*?H9S zvTf6Br3+TA#RaeZjn}EU%g^&d^0xtvqCV)PoNw{j9@T9=`?>?Oe-*%Q7}N_UJH|L1 zuc^54a6u;%+^fJN={2U)S3`s>ELqhA;SGq&Ss{@QR zxs%znjrQQKH0h1SekzmDwJZ7-#~jbyujA{{K@qTZ>Ei>_)8l-s;Q4?lqj?y}&b5~e z-uu?8?W$nc;#lMNH_&0~<@{~;gngO@h7A*;K<@AEdyL=)nmRV`S9K9`d35cCq}ybg zHN{ZX-7w$*dYTphVbGId(z>StQVbt+%6A|-lqzwMco*41##ruS$2;NnymT?+=#?T+ zg{r+QO=ftVy@J$jlM#IO3C&BIyu^w0Sjog4)N0FYx z@26(fLpcH&NKnT^!yFn)Whf8Ud+@%P{GRJECOlrD%QdR}{o77~M0l)Pr42MHt-^dP z)o@}sUN|J0Sioqm@kS#KT8SSHbjw>w=l3e(w!N*za%fd(VqMhO?qjA3mDjN|xtt|T zzPUcZak)OG#7ku{ISF><4TcMGvn1tutZ@p|>`I^tzc2u(+Xh$qK zqq2jrJNAhe5Li)un+I%P;X>AN?-%RT%PS#RU)?>cFf)VM_15_5>v!DxHMhGu-M;rCK`hZX0B0CnsIU= zT^EsqRH*AZN-*q&@EfJdnI!&+7OYW?`u|Cs00@6X06_ni|oS)4k1+f3A03--V6b zHkOATWb(#qh#Kk&VwC(DO==&i?~!4C%4@msUbo%5;pZ7AE{onk&d4~QK3JuUlh&oM{^=}9}cOZ)~zVmo~wy^tC> zTU_sAefd1&_ZJ$}_qf%6zJ=0SSz^oeR!vh>xCl7?%89eC{>ea;ZyT_^Sl)ELJu8vc z{PxRjibjQo*hHV*T{O9+{OH_k#jKgRrgtABzbC^^k>jWT&EfKi-2`gH^0`LLE}wM% zAE(dGpEGkn*%i0#^A<)d=3~L=m%?gqUc%n>&(^!O;bU*ZNPXW2p3tl374JvPV zMa*Cz${?7OM98}jfF3okg5Qm*ZLWBc@;Fp5>wcH)g*i3qG=euw4Yoooy2_V{O~~sF z9{?7|3nH{h?ZX8xH$JpfHJvg13KJkrE60Cbv%5XtTFvOJRb>E{CW@*SsN*@-yM$Vb zUz+Tu?sDxUa@rhro#U-}0&fl5w%JNmO52vM7(oq3F~xt`SHLip)zAimW)+cs7o1f8 z)EnB)L<5K7DMQ|72x?mX$~^JJpG85AhN6;l4Yprwi)G`;8fnn0X#cR&|71QLroXD4 z0yk)(vP>9E>UqC8rH<@3h8-l_I+3^xWn81kaloQRBH|@}7CF1T-fxeO^|B90AB;FH zh6>>J0BJ(4wXb7?cr2d|#-!nr+5dxhu9n#dzj_$-O2c*6>NZz>_1mHOXF5Zpt35`F<> zP-FJb*B^9@$m<;-iDr1rH6RsA8PB=x6o66YIZGT`MU_s%iOxr9VaMg}4aOd`?tb6g zYMuAZ?yLx4+gIsT4j7L6+HDU_e6ay>fl9-AX+Ivfohhj|_Dl3Ek5*??LEI{q$yB11 z?3;q>e6YEFJAYf#z`}dvJgpiaT4A4KnVjrphfvU%CCUt&K;Vv^+ykHQyIoe-qLIt` zs-Rc!!%_wzL-hd->(_LlN;-Mxy+0#EDhMJBndF;&T?!GRebKjjxh5L9aqDJ5LhL&9H zD&(9gRD+kDS+ndLlwFzg2YB$|0wueTjDKx!P(J%M#QKWCbVHOL70l4EJ|}ve?0vB( zU%Mvd9wG1fO7*McrY!yNlWrAUJ(Y?E5LL@OUaxyV1K=6)fTx*xdF)!r>g4YHfQ7@X zPX!GRZRDF-Dat*$Nd#IlrpmVO-6GF-1cX!?oouic*KI)UW+hPxqwu?a4TZckAAz}U zc2eI@lO9lI+J{TeRcz7w;u^(_0+p%B7hB*2Vgxb15g;4`ETlXqK~^{++R1Nvfw;sE zbt~c7DSFK}7zK+JX51k(?J2=u6tNRlN?+P|*l!MtI}}mzm`UCl`>W>O2TLKO8eR8a zfM(wFIm{tm-U>Iw54XoLB)TkS%qz&RXT%6UeduT9&i@%b4k7q7todmA7j2J5hb-Y3 zW$qxKcC^r^`f)se8fIsyzH@`^`1XP0+kd}{dpeV@JIL=poOYr1iVx**u#O;vp0R-c zSE2x1C;E&Z{N~Zk89{t7BC0&-m%11ICIru1>@HO^*{(SXzgzU(xE^dJQWW+cjAnT|h%$v6r^ zwYGirpRlOHQVC?pk_gf@TEBj%ocli6Gn<#sMGwDg@po6#WjEPg4t^J{gSRG076eXQ z;GD4Norkk(qj18p?h-4|52dgaPcdU=qv0_ZKqN@l1X80bj2h%e?`xSPvKPE)0Oz3> z4)PG7bG&=3L{27pzYTI|6z~~L)`WboYo?eolV4Q;4bbdtwfP3ba{l8 zLbT@12n~pv-)w)(!>Th4g6W1;K+fF5TZchjoB0FRQ_JKVlZ&eELoc&}%c=Nz(q&@+ z>q2Vv?YQ}%M*JdRDW>p5@2C}04WP`?{Cv?qLlH4Zy0rG3Fc= zuAP;2R3u-+v4q<5HY1cQg9S*b%d5&#k?XsjsR9+jZe!$3FY#`~kKN0|sYAvJkDL!b z6~T71P~YYq&kH-LNT1aju1-1P;dd*ccR!-7Z!a5V?EOHxmAaKJjZZ8|;z3A- z4J-o9pw`4w-pY+F$VW1&2n8!R#U_L-4%e^>-B!Iy_1_;pqT-Eb>95?u54|`CXh%mH zrxfaFR6r(go#)BB3{LkU90frY*2$Dn)l%CW8_y3bPM4ux!pHZ#A>VQ*#UE>kM`_i! zR5T2@Om&P9au`yBQC3y3zp@OP)x2h{umD-fG@yyb^J(hwEEY#_=N65W}a^@pf); z=s(RC=alxCUS`f-{-zTn`83=!6rO@4!(P?pmHeX&Bboe404=E*f12;7SqjotrDuhA z?JnvG-zMzpC-N(pP+j-j9Z%0P^i@XT4cJ^GCyXkuWTZ0;oS5dNTIOb?6aFQ$pu517 zFwGCXa9ci^6XF?8EZOi@DghwzPNP3wC3mrj4Y5u%J05=sQ3d1E7)JoUmLWfyH-6ly zMYTHbOUWl^JEngz4T*WGuhtlMeRXC&yi-VrwR4y!I#(HGDo(%3WG2Hk!<)xyNlAn1 zP%T_EEn(=Y)_+DPjS2Wyq0(yWSjdoQc_oMxg!@t$!%VNVo^f3WzWRJ>=7OmZp8HK~ zVI~+4pL2%vC!wJlZnxs$jECyUMa)-u37r8^=XVgKzDQD;vrnW(HD=E{WH>k(#+%*G zio#%-6m`T`1(bD|bZt%|XEW_a zWKh}FQaIs?0M?J0!*&Ri3Zx{;Eu@;V=)<1uBNjI zMGJ=bkaRD{S~UE=pT`sUbyG9m@Qd%~PG*tW0(iX~SA?x>#5LXe4>7D5&a81CQ!oso zj;3HSWA7?lLUCneSFS4M8}!sVP0jfH_dh|M^2pP*z!K|_DBuXI+hG@R^Wdr;M4YM% z8WPE4a{-i41mj@7@l$beIJd)`ffAVrEsa8oy4a#>txAVDUW3N_Kd0(^o=%;>Of0<~ z{;E#C;d7nC0plprzobS6XMjnS2y0N#74@}$4eMZ6V?Ag&s-jhZ^!9l?`9rnpZkalt z=~&FWY{cYOg+s|n@Be%iQM=?N2Yf|hmW})7cTx$sqi20b6*@k6Xpv49mw~}IH&Q9L z&p*qK3B&(2MI!NVJ6Mu2c1YDq{%6AwD)K5Li1*p}jKc)pluor}r8hs}1?M_HG6^sW zYvw&zRPZioQJ9PAOt47GH6!5*Z*(@tzEJhYlBH$|G%1|F(WJ!uRW=->69Xm62{n-8<@O;scsz}`)fOuqGdm~Bf5x%zQo}gPS{uW*GBG_ftQgy^dx`6Q(*^PUr`o(=3E3` zkHZQF4)k1@>kqdY4{iPjpIO&yD(+XAas5f;Q&`zJ%zjK+yaGAbDKr5rYK3Ubf?&B( z5t&|ZH6{yWkKlnQu2%52a+pi7-A*AKlZTnErf;qHD@}n|Z;{Px;606erUvI{-bVY` zf@$gKZA!?HcIxZ$r@v=MsH@UqtB`|AdC&(n8{#omR&|Y+*DNcC^!=d;S*MF{empUsOGc^l{NtENXAFBsIA3SKp9-#V!i?8v`kF>vz)g5r zS$8~rQ&!TYBpe^UIMNXBS6g5{xF(`ex=Qf+Gxh7~WL9+}{HGQbJUW#_Z!a%-lBCU8 z()oRccH&TCL@D&M@nFIc(vqrt#x^%T1a_u{RDnba;#!n#EuO z27DT7Us(0^>SD!2cT4m%K3A=qZw~~!>7#UN)YemNDH|ghU~j35^A*#M570`|fCya^M-V99aly?~l4$Q;&Ryh$sz_cQxkcNVY764 z{MfUWYWyqTzT`Q*QJMh91wBxG_^!L#W%`l$;|cGE+I47P(#T7WciRa3KOxtOp-J-l zb3qmqI(Q;!osuJ#wKm{8IE@f=zC9-tmR|dzV1nUAw7+t)K5|6PV(6u+ z(g+|Es{89)b~aYK`cQkVxo7?)6oFqNkG4T}z zuMT7eq_>pz%G0wrJGU>$5{f>U6G z4<=F%Cf%V>z+(kS+a#5}lS;vPLl*W;oW=ml0T&nP^5Cz%R${SisosZui7%WMHo7Nc z%XQAZde0RSnKKqRo}aMt!9w3S01>)P{N^T6PLCoXKu(03!S9v%Vg$v@kb5^p>Upi{; zPd2yKS>DgxKirJ}K)Y1I!oW~2Rci=?t{ptYk*)V72kww@ac89Q6x2;(oNv!d>A+-M z<6vUvqc4p%swWSHkzf)Ko_JOL&73eJa`07w;l;iwfE0hf7+T1&+_@#+r;Iv~`NC~q zg~w+(`Fnb4D@B+doY()Pg?It*2m@98QDMA5k?mkEAP;|0N-f@4I0}yJ43L zLPTFxaM?S;{|9dZWWo|Y1(&GYiSC`!+B&J+&uB8nopGyVRJ`(C=HnGHKOkrR&IwPs z@x#*!4;Lm|1<>&OmZ2Y_Fp^GkDeTsw_Q6n5s(Z0(+3-ve~yby z7fYU$y~w}&3)SapKNYL@nJS;yfWA_*`hQg`180G~Q=r!<*STZ}D%hs{-EzoYt^dPm zPPO2S-GLAywOqS;1}IV-x&D9g2miHVLC@*`AtM8>Uf0KY&8^PcvS4v1_e$~;1=jwu zF+qpLzwh4_<;4qp3}p<`Pf*(iJ{UiCS#JA(AUUX!T|Pl;z_;@+k^>JxSVDt^g}scQ zhB9c-*EtnOSZHS=Bg3L`*^piP34tbvEWz3GL;WQe7b!lQi!d}F-eW=miRq?Z_U4vO zA-2*Ie^m%5phDx8eUK?vF_e{+Q|gv79!!SD(IkiRF=t z>*VcRm7o3=T*sif7ON57%QJl?H!vK$#VfZ)eY&y=&*y*d`RM_3tfiPRSvrzr(7^0W z5xTa0=vf?i5)%`1xw|AiBkP*^;olPkBr`)POxohU$`to`gipLsF9;Odk@f5!9W{Kt zr6Q-of#tno5wj(jDp#x(zOf z`kf@k-^}prG0Qb{qd76LAZ(JJdS%)7XLbJ^u?^j}$l%~8BxryVyNZ&h3EQ9 z)+x0&*Q@?f8hTXYul@rDv12V#shRkP=%7;agAgQeXSjor5d{A=LI&2P2mnz%u$>mw zB;#H~e~IjsmItoM9tM!2(nD;NZB#9obhNrtnbUn0^48e@#J*%Yppw{+uY4ry0$GTV*(0M|p<+H}ZBv}@5o~LF?Xa<@ zDeC*@-H)^{+HA622gH}Ze{bd8ASuu0HGCwXtnwrz$Mwu^c7>v4%Q^yaAJ02-lSckW zB%vsq0J4Q@q?3`*B`xtAU(`@f_3co5;QZz9Eu3DFgvlLtE&m*0(b%KK^FFi}!C1*S zEJ$NXSoZQmF~eK?R+5n#jx^SF1PP^!w#0dl9bq@pQ8W$nF^S}1-;!HDScqqh^$(>& zTp?Xkc$6K2Ia1Nh5ruMNj^?Y=59Z57kjvjs&+NpAUS8{3R9(_YJEP$$Yt5b(p%4mFL zP0v$p^etRChvbK?{Es-F;nl1?^Plm`8~!UoRJbis;Z1S=Kcmw$K4V;~hBYuVHdPzr zUwz8P?nYMp$jFn1n{hUn?C_fAx0^($}BbS5fUU>=eaQNg3aGF5z66^Ad-!M)F^>HlR)=F;BC(r8kc z*3aXt&M!BWsQKsOq&|X9c6t*_g{aYAJ@}jNw*DC0uf!kWjU>fjA)$bbS!gjMj)!7- z0o?-K{qt{v-03J*O0S;%i^}E%vPc=@^HA-Jt~4<`UpTKp+0VYc5zGx7P^}2d6mC;r zqkcu9ad;4KvyK z+ZK!-8`D_c7bH?D%QYOo7dj`1AwW%;hUJ#d+0c+yOT?mZ0IGc!+Wc+z&xBt>^h@O1iFM|VqVY?TYPFtUSJth;Thrg6-8#u$B| zch)*WHfvOL^&p#AG)#$-XqDV%LA=eGfA7?;&N7{JcBFf5Ka_^nX`YvMW zLRgoTsQ1A`5dVp)@r$U1#E8)fI|~7TtSnblj+~*2enJ zBjpCelYW{tGZE*mY2d_9e`BtY3)_=NREd14Fd#?7nL^T4B+891DnfgMUaiAg`Qjp; zU*9xhgIIBmT8Spyqe8Xt$E}D}NzyR=pLgYP5q@7%2Y+zMKsJKxX&;Nzq$o#Y6s(3n zFL!eEH+?Y1#iABFMaO3H=WK|kU#zM3^pqL5pf0V)OSsLV8|r_sj8NVv+0~d3QMKW7 zb&E6L+}Ss}N<&ygQ>r>a(Vn+PoikUPF~jd=uUm{YA1;;1F>k@t_>PjA|^mYuZ3h z_5lG!!bA=a*Ib`d3fwPsx*~Y)Q%pG_?kH)fs1@yO`c+@giP<&0LEIaIdSPP@I3U<> z1obmk&~agrnkth6HkINbB5@N(uuWCNWy>AoozraDCnJl3H5+hBtQdB>l&#ZjsafR# z(^-7u5JuLk!-Ygye-0D1TzSMcbjRi9idN6AL|EO6(-ZfHMDClp#&2AukD9xw85 zBVdBA;5PREv~%t!y&A7peM7iZS8^?NrL7Q|A)!Cm`~r{sVBGoW&>kni+|VOJ`O9)7 z5;n3_UuWk~V4REm@-y;*YbDKnfiS_}ZaKyAUS!Qr?&}mA7oWO>!IQj?Chy_{vNmcY z$O(n+o2hLk-zRcu8%3BuD#cSI0Cqy&*VmU?i6o%aixNCac;uXFZ0Rb;;82lyL;GNh z6EG*q5tgSrO)rj_Ik(t~m69Is9cuV%?|m-c^SPQgQ7fjYH$L1&nX~CBrl};C%p3U` znmpeq4Vf?Wogh=IyhuA0%ZsGNpVsqzfAiXymQ8wNy5==nag^g^`gqAaw_%uC^@OLU zsO0(iWFceB_W+uu1m6oidR{dv7V6i+q?E5kNUL^w<=+jTdL;^bpfoghIZ#U3HrC$s zBTIR)Qd_k^pJbFraQ|Ijqj@R@kgjOttECW)uO&6AHOo3j;0t|c)@_qTij#^;OT~yv zjpu|g4mGlTfozTvR~L3)U7k+C?)u$LxrT;<;OeuewPL@EsRMn=OD|;AgjZ6D^G(LS zd(z@Y`1CJV3{L-gl_+p3ku5xmx9Uhn;VH)+%+eRf?N4U09WG-e@1_0Pb&0HG+8kcg z=b@H=8=(8fD%wEU#~^lJj}BZJ_}3kQn!Sm-RFK_|>YI;->_RnCM3lil-6svuq$2P> z_%2%-Ryn&}d7s3P$v7BxGT*&no3>YE%MoJaxW^ezg@_DuxGqLK=tK5v+lhF5ZT58@ zT8%vHjx71wTq|21(^+4)c<$ENaozMy74uXZj~BAe5N@~!y1crEJsou?+vtQnN_wxF zzcaIUd$D)?K%|KtaVPz{askPw3?2tsjM2MKxHHk4-CA=1eyI` zrt{-CQ$*c1G^Rmm#jWAR4tO^0&q*i5e9J;PrcG_t?3i{P#`{vgid`NkVukHFFX#KH z(k8vhR@G5A(Z(ovZh?$Ti)FHU5)65P(npSqOW$3Mg+w{F?lm9%?l@v=+x@2rIB%AO z*oC99X(AVzPYNiz_1j0c|1zanY+Y6+^1JH+{x}tvO;x(eu+;{2mrl2SYjWi7Ryu*@ z3r`q*36?nWjbqK|XW`{jN@_Q`k61Ov8nV+C6iUL36*N>jjBo)syUX2uNK%0pYrP&i zx}te2C7CeIn;j@k$v9mcr1V%TQE(?^4IwRL&L;gYGAG>jZ~Fb3U!Y;v&}sTWKU9Kgo!^%3wr)TL}9HS%u{2m zB^LVl4}eLh?(pJyJR5TXfi@Y8$O&vU&g;?-Zc3fpzTQbiNn= zp=H4Tr}P}RH4Pv%X>-Jip2L}gasEVhG4a7KJV*JB-vdN+-q;ybn}vh^;gAT^W5Pnq zDRi7TdyWkMB9%|wGQxiCem!paHM#|>;rD3InSd=-?PIZT=SMVp7+z-^DWD#@>P}(x zD?0(&+I07~w967P76$u^zu%hdW?xy2W#O?Yvkd}X>NY{bpeE6%#ikG;gR|?(0Q5z< zJ3v(hu$*Lkpb2UO+AQ*NX{=fP{n%Q`@F%YVT3jJhf=CN3R8Lj++G$j){FzKf70nE3 zO(?CjOpk_n7SfB7k+`#y;nrqnS99*Rug7$ZV)+%b_!}A%QeNC5AByR^Y>v=?w)$`3 zxNqzN(VU>zHM1d5wiM!anBK-4JMr#|f9QGY7k|A1**`KDd+2kyCPBe2+rKGU*v4y8 z_rJbP_xqJo9e8=Q;B`8Hn$!stai94|8L$+y|GXHOPp$j{0sVZnI!U>#A7M0<`0&E) z1PfeSPkN_mQ^x1p9N!*Q-q7CxU7F3@htj?%0%hPKpaT2J)ZDwqLF7qA00TC3u+vHz z1CMb=vy{`sX%%Qe9kH3+qR}S*9ZN0^;j;QQ0CE!=$R>&v*mfq0WvoYLx((+EGah|r zmlGK^Z7Rn~{QZf@4{HMrKGl&3x~V{C`=|4be*ct(x1d~&@)!?>BW(p$&!Ui}RM2hO z1MluzD%hb9fSH<62@mN9HH{JtO+_E*uE%u5gkjr1m#{+EdRFvN;CSstsLR^x@ceO=D&YAV2AMZPTKP$JG6b+ zPTrFjr%c^W2i$LoY{veetOww~51NR1v@u_ZC`*%He5cG^_1=IDel3iJ)U(Qlx@4od;3`jk#D1E0k+hLX8QRxONxe zycO3VLyz{kqF^&Lq(*IF(!D3t$s|Yo`-hz}RP2p>z=W%Pd(XjQmVgl@d-vg>ffS(D zuYkT~TA2js>k>ddp)J|&bUE8=+sVX|KVM7XN_)3R8%CXst#HT@)altk=9pDgIRhEs z%Trs(>_gqq@ad_u_}#`YMQ`-{$K}_R(%FN!ZO#*t-z08#}*9AoJsKg@w1^53^*jYwJ^)-5!bm$?Zq=r%&q@`0pP(Z=~ zX%tBb=^7g8E~!C6azMJI1q4A_3F$^sI`8KH-ur&I>#q0mVb)?eYi6BU%s%_<{rsLE z?FN$9AFNv{R~0<1p$E-&b= z%pn+Sk3b+c=E_@FADMm(is#bf-~MYUefWTFSVDr~;zM7uDup@hQsQOvOUTA!0 zp1*6~2cZ&x^feY46jg$7ZFkqW!jf#J_tdcfaEKu%N{&k7++rMZk!_tM%ob{oyjH98 z)U#D6uC7ZsSV0W|7Z~1z=maxC^uUnx!LwqU0PueugiXT2x1J?NO{7BeOb(*@C`VQN zyLf!;33VhHV}`7Ix~SF1!QS7(7^D`NC14rsffyN4TD(8vz2M* z#2;>rs^1}^%JT>?!>hQ%`I=27YxgJB@n8knF80zki8)@KMN-6i z+42iiV2aoQbLUR)x@k)BS zrT`YKf2>FgG#~!<HUM*8tk(|tE*62PW%X2r6J>k`AX&S%%wf8OW zG@+?C$}@Mf_vN-XvW1pkIoOa53ag;#HI@k>q_G~18d1lot#EC#XXip+9Za{`T=*0S^2?`a7Qo zpNpW$5E{EJmL2@P=q*Fa{c@OSJ$Kz+uhJV9c*mEWIVAn!9IBlo4OXgZPc)gg44QnO z{>6$&a5V3>kpM1K>o_#d#+S#gSd_d*iS{j-C_5?VQ;jfs?neE5KxEbFi&#R&uG~kH z6P`{EeJar@dpkQ%0xH*e_|3W;sQI1W*RK7QnaxqFHyu%$=~N<0*^z9r$|!t!Y9KeA z3tAMBypZC`K45LqH->p1rVd+cKJ@Jny`q^AN{s}PoyyXetaK zK*$)m?aR!tMjqq zFdjKAzbDy==>u1&*FA%Dp3+>{V7gGMieHh39dJEi_wvZcb8#8>n|aXKobQEB%ZKYE zyl`y1yF+?A`(zKO+C8(3+!i{pC#&4*xQts2lE{DYAU=gLtZoG3Q6#rqo%e!oK)!xb z@%_PsN{_$yi=H=xlbnVtgXX5!O9f9yYHUPqwIX;$;MzLk&iG4DV2q;hJ3;WR-;+WYSrjC;r~j(0}Yxuj(z!I&!R zd+re}nbgyE4+45DB;+?HVOEe-Y+tpgnZa!>1$7NHGrj=s4DTSk+P;$k9to2m$+sww1 zhJH$pycgxVh7Uk zyZRabmBaQWvgI=t#+pc)@Z(u<#t-ljH&1;tu5wESd=?jjBlO+Y$utN|i?9>H=5sO* ze9*sDbAr zvuN$pg%p;9FvuopG^=XL`VlPQjAkF1ogwaKWc{4NRU z&Paw=Aj{OKJ!dUXdW>XP5NY=~BYbsN0{Ww?h`KWh8=L-Mj*=(#INOKG$I~Sa>?EXx z^2B&}bftiOPJZ5uA9-4#!cLc|+aRh*OzJwX803#`p|O)Di{YB;C$Ai|?_MKM=k%em zYsv}!Od0EMxbTr$?*);sJq&)myL3}TY#)WmYuvyE8uGMdNg--&TKUh)0*KVQA4V`R zTM&>J!w%)&2Ul3@-Tj^=@N#t^yW|JMogw%XdNAg7w-9k2jB1ho^pngfXjgijL~`yx zcMvRS@+2mol=y&@<$XEk0dUn7nzh~cqZ8=JeaaT1LAXVL(?q-oIR8(o^)Tnfle+;N z2LY3wZ!~yttVKg@$P*w2F%q&=HCS?qGnFrEV<4(q-clyVF zew#yu^augbzH$LhMw!NV6CZ!d1km(Ou5r*sD`hLrJyD;0yPclSYLgs|I)?`l4 z@YlwzS?p)Mjx)=b7FFsijtl`p0cYx}88Xe3cUGwAPsDi$rSeZ1xT&G19If}!$2i0z$c*UM{M^dkZ?pi3PPuSI5mAPkQH9f;F zI_j&E@u;4uvzHY57Si%=I4?{cj*)}Xfb6vfY~|F;{EmGAu?uanr9(~g+v14e;rTJJ zrt-F@^>@sz_Ge5AEcfWm^`i{M4=;5_+VQ~<+LAc}w#k;kh|l9D_5SZh=NDU% z-A|?B)u(HcfXuMC>iPG}&J(4j0tOWzwh_$4U3y@dRS{%z&5N&XA>&~`(`?#e?)E9v zn%a(?MWc>5KKntJ+@4{BQ~ssI$s5CP3>#c7i2n#qzq*O$yZL$a$vXRTaPw+7+TO$i z5)hu!eWn42bf;Xg#$067(vBHX&m`ie?}$BGf_rO1r!{Jn=jh8kR@I- zR~nE7``oL>JcnJ1)Q8jH(XBc3v}&YiX#33OeDNMB4hFzaCHB!Dg1hn$)##*3cw30z z1!fDsK9$u%ew4JHkdNVS;@Ty`OqsrY;0~o{cs0NywJOs8d^6bPD!8A7n0^s7P4q5G zQ}&IpBKp4K&t&?6%{|^)bd2VZriO);?xhu|0L)#GZN4qMSsZCk$*uc5oW}pJj=u&@ z#I03Vc|k7N5Kj?p0wdL~n}@g$T+sS6VN^1Z4y5LqTsRbHzU5Xna(C(N5 zCghVhp29%Ar@a-oJ#8n>iEIY9!ehH=E8YB9r>rtwG8O(e1c`LwNUS;Ngj45(_t=Yj zA})8n5~7rRWEZd&D1q}vZoUcgXB+>WWTryiW%T}LN9jZo_<6;A3ihT1!v?76o+ZSI z1w+cF3m7yu$JjGnptyHPQ&1`ItyJi)y2yo%6rdDcUi>$1`m~yN^ z(@R-OYVtME9gRzQzMjk_;=ugD9Y334T{ipW-nhdI=Jj0%VLT-;%4}DLh5#Rw<(ouG zB((jEdkSstGZy|8VSm)T045&y0hPq3$#=`iz`+8NHAk;{D;|z~d4rI=^*vlc2$DY3 zY^mn=je`~tmgA<55QnOP_4J^Bmf8B9M}{Sa$WJw+={ReRZWr>JIoz{@j={dvUFfA# z&`#U&*&FWd9A4;1Fz!VL10w!_ykGJ)DUr7F!vMo+VY?_8b)M8fg&K?-+d_$2^8^ zc7pmnFt@Xs@w8CC&i9KLZsZoj3ow&LQGl7q>~7g~_45QbC5@~_(0Y~79hI+3H+_Kd zCY+r6zG0KI);o7J&Sl4?o}xL7H&Pde0*1wC2%=;iEUX_OZA9ar-w0^p!8zZ*nQ9rL zvUOh+q-A{COQqF!X2iT=%0zgLTbRnGYoF(QRyxVDzDDeKU^BCHYrjba8Gz@;v=yoK zo=4#d;k0^n-G4@EB6!BzcPNrLbSGG8HFVoZ^W2Pq0EQRh%Rc;3@#kxi!gBkTG@b|w z-Vs)oUwRm4kEIppA8N%k${F5PQIo?oB!I+L%f|~2R^S@F_u3&Nb;ib5G~(vol6H+> zc;=M_Ld5KQShuBY-W>R-UXh#DMS{}$!p>(UrMH^=Y#8R#)cibIgxzRk)Hzp!<5GcsM%%De zi7lIWfhpe!-JZg#Hvwy9a zZU?lf)*Cc1Q_mJ#$T;~ck@oid8`#Nzy*^|L*rtNwJSO2Gh|NzsHV~)ifEL%6d|dk+ zfZKYp9s##L@2oTK4a{(e2?aAuX^@lm^ z_*hJ9l`ttB2@RNu>m<25bm`tqgr3VxplrR4? z6bE(&xutHV{t1i$HATA?cYnk+KL}HF4Q~G)-(~((0!DPG6ZFz9YO73GA#+ixN7y-< z@+b)N=Z4R!1RvtVML^ArD5L~G@3p;Idcqs67lW?@?Mc~U9AY?4uJ~LJ;X1ZI>gebcRH=R0)-E_cmuJq6A_G`eScd|HhU}yaQ7OJ#0Ov` z6gs@3ecr@8qbE!9)^*=K0pM;2!hV$V5q}oZ_da$(KOd1YfyHej3IaZPFG^^a-Yr>d z&}^9_c{twYMI@(LcCThk?_-I?qM;R8>Ac_2Y7ifbGuLTk<$N=1-on-9rstLfQAVeb z*t{_0I;2yn=P!kBs>|%SIQ@}fjyHCi`0g8_8aM2bKnDxD_ndj3(MEa}Qj>Lz2kXO? zM)7haqFsVJiy>VL?oc95pNw||ZYc4dce+M`nHRNu21}_*Y@XHhf|BW4T1pP(OGl!+ zsUhZKZJ*Vmk1T$_8Eqbij6~un1VI9)PJ}Y(Q|ha=-94i+KT8rTy;P{e$B;p5N!nX_e&8xMSBoF`1Jg zLBKa6cwU(Hsy9}}?b^{LL(=Q9>-Nm`$Wf>p=4QX9W(a><$C{pM+1*chv-DLEx%aAj zm>BQGEDOLgSZsyDn~m=mI07g}e_5L`%i+VR6!d@|5DjJlBe^fHS{eDVG zKNP|e3eC`4%};4sJ*-9w5z1B)D6GOowF|<&;Azz%I4IQXyEq}AF1k6OPW>K{;pIBPhu;38K+Jq^8D6b z25L2G=Zsp%IwT4(#4yI~A~( z#~CA4CurX?A7uBtkuudh|5)w4uV$z)W4n4%6}=Wd}nupXTP z9L?Xt$a+8#8=)srGVWt*N@M0~^Al>eSHMDxROQjVZn_+{XTw7={^jC>52Uol3R+Fbd3!Sj7x95@+vs0_?nvu34>sAm zx>&5oCq_*+m`^X+aI_doO5iK!5!|3xRPkH8dl>~f1N0Y1ikOK(Diy6E-Q!FS}pj6)) z#w**Q0GcSmdQKoy8UBlI6!0@w7nm8_3kM(N&~1^k$^9T_q=CbSa6)!iLUXYq3E*{r zb|$TWa`I~@^;Qr1Y$UgmT=~Z@fDJqF^*;%WXge5C|bJ$D5Ne0Gpz(W78B zffF0gmkoVy%Dw}MY>Uh-FE0n(hWf6%Q&vr>;I4xN8=o>RSBdCbQ9|wSeUo;LJUFi2 z=@v$k8>eI=Xf(NabXPRpOS-opI{%oQ z3RoFO4^4#7L(`#A-hTNk>B1<)rpPokuRU*(K~2FawVo);{E}IJ0UBxKkAf@HS$chTVwBm8^R|Jp2_w|-D$O+VsOTGD}vi5p~oP+Jp zKd_h4i~rUf0ydHS1s>ixz|e1x1rN%p|0ipC`!~|nf4HK59r+(^YPdCakC_$To7_-b z1JzRO{JhEAY2^-~w{PDrE-mHy=+t!)Cfzp7{qv3;kYCngaT@<7O2L!O9RDL@0l)mm zcltMI3SR4fdE4adD517tr?te7oL=4T{36?6fW`cu^PIg4-QR6TR1XxUUrgsB-)~at zfNkLiZo9v004Dd}$1OkI-&+{P!}~`__WyrA-S00MYVJ=DQ>_zjpn)G1#b*jAIg^0@ E0+!jy@Bjb+ From cdc1758ceff92844ca854e82b8022ef4e38e96bb Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:26:19 +0530 Subject: [PATCH 027/188] Rename Screenshot 2025-04-01 095640.png to websocket(2).png --- .../images/Screenshot 2025-04-01 095640.png | Bin 102620 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 095640.png diff --git a/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 095640.png b/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 095640.png deleted file mode 100644 index 0c0f2c8886d29238c9cae3ec5071ace537c3e791..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102620 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

baN%6-$^FCbns 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 z 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

-{SPwJ+plR~9C&1Yx0sjgH zq-U=ew(aT;n8>sOEaF(VZ|6z<9bpk=Fv8lnn1HDO*6ugfA|9gqOfs>Yp`doT8Tex1 zlJi*X-$^+}q}+`$L6yLA-W)HFcDrC5!3xUKa?1+WiSl2=1ZJJQx)1LCyNUWU(!;QB z@6y6Jr2_+l3&Mm%sP0H0zRFxvuR!sM32#_LTKRDiU9|46u zUYC^++}5`4&TydU1y@jrkTpi=q1Dc3B)LT_EaIF4HEwb zh2pC+NrK|IZ&<0+NR#hvpOypKm^-s%q8P-96>< zUDV$&xsqCdC2~*v@Ba|#fr~U^SW6wBo&|xjHM)luBsi`S5rN?);XHg*0YO0}AWD$X ze4npppf4e77j-cB_c^k7zCa+($UzJ!5o><_+{OlsA&TMQ!_c|Z#=4qLjK3eV^PV^V zvK2EqGTfc9|Ge~6W8Hs$@xSIg{^{etg3|vhLGu5-vGl?f%AlZa%1%nm@C|TDh|7xQ Ii)efO53*D_nE(I) From 33fafde23cc51891f478931a2e960c7a0f8a7391 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:27:59 +0530 Subject: [PATCH 028/188] Update Application_K_Govind_API_Support.md --- doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index b88a29a30..38c9ba598 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -122,6 +122,9 @@ Architecture is as shown below: - **Technologies & Tools:** The approach uses the package web_socket_channel(^3.0.1) - **Expected Outcomes:** A clean ui with maximum smoothness satisfying above mentioned solutions. - **Linked PR for POC:** https://github.com/foss42/apidash/pull/555 (The PR is in no way the final product but simply to show the code structure and my approach) + ![Alt text](./images/websocket(1).png) + ![Alt text](./images/websocket(2).png) + ### Abstract 2(SSE Support) Trying to implement SSE Support into the application. Using a special provider for incoming frames just like in web socket messages . Now the incoming messages would be parsed and decided if it is comment ,data,event,id,retry. Data is the most important one that is needed to be shown. But we keep an advanced option in settings for dev's who need advanced testing they can turn on it to all other kind of frames. This would be helpful for testers for advanced testing.Additionally the ui would render message perfectly if it is a json. From 0e75a6f3c32ef57a9273b453d8e48923d0e872cc Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 21:53:16 +0530 Subject: [PATCH 029/188] Update Application_K_Govind_API_Support.md --- .../gsoc/Application_K_Govind_API_Support.md | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 38c9ba598..1fc9a0986 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -28,16 +28,16 @@ ## Motivation & Past Experience ### Open Source Contributions -- My open source journey started from APIDash itself. I discovered it ast year and became a regular user of it. +- My open source journey started from APIDash itself. I discovered it towards the end of last year and became a regular user of it. - **Repo Links/PRs:** - Adding codegen for hyper-rust (https://github.com/foss42/apidash/pull/468) :- I thought this to be a comparitively easy contribution when i started but became challenging when i encountered with lack of multipart functionaility in the package. I made some mistakable commits since it was first PR but with all that happened was still able to make a quality PR - SSL Feature (https://github.com/foss42/apidash/pull/512) :- I came across this issue first while testing the hyper codegen i made. I took notice of the issue and when came to learn that the feauture was not in apidash took initiative to make it happen. - Client running in background problem even after removal (https://github.com/foss42/apidash/pull/560) ;- A small bug that came across me while using the app. - - Multipart cancellation error and Request cancellation flow Error :- Both of these errors came acorss me while trying to implement the web socket protocol in the app. + - Multipart cancellation error and Request cancellation flow Error :- Both of these bugs came acorss me while trying to implement the web socket protocol in the app. ### Proud Project/Achievement -- **Project Name:** EKO is a combination of reselling and recycling platforms platform for electric components which connects the technicians with the common public.The project gave us state level winner title in the prestigious YIP Programme. This was one of the first applications that i have coded. -- **Why it matters:** Upon uploading the IMEI No(if component has it) ,model no and model name. Using gemini api we produce all the components inside that phone ,computer etc(using gemini api).The data is then stored . Technicians can now search the components seperately and can purchase the component they want. Rather than scraping (were valuable components are lost) this is a better way to go forward. The consumers get the maximum value price for each of their working components. Imagine your phone is lagging dueto overuse.The app comes to help you to sell your undamages screen , phone speaker etc. The application also had computer vision to recognize electric components lying around your house to know how much it is worth. +- **Project Name:** EKO is a combination of reselling and recycling platforms for electric components which connects the technicians with the common public.The project gave us Kerela state level winner title in the prestigious YIP Programme. This was one of the first applications that i have coded. +- **Why it matters:** Upon uploading the IMEI No(if component has it) ,model no and model name. Using gemini api we produce all the components inside the phone ,computer etc(using gemini api).The data is then stored . Technicians can now search the components seperately and can purchase the component they want. Rather than scraping (were valuable components are lost) this is a better way to go forward. The consumers get the maximum value price for each of their working components. Imagine your phone is lagging due to overuse.The app comes to help you to sell your undamaged screen , phone speaker etc. The application also had computer vision to recognize electric components lying around your house to know how much it is worth. - **Repo/Link:** Frontend: https://github.com/Apollo-Blaze/Ekov1 Backend: https://github.com/Clasherzz/eko @@ -53,21 +53,26 @@ - **Do you mind regularly syncing up with mentors?** I am totally fine with syncing up with mentors for being uptodated on my work and for feedbacks. If its a virtual meet it would be great to have a heads up about the time. ### Interest in API Dash -- **What interests you about API Dash?** +- **What interests you about API Dash?** + What fascinates me most about APIDash is its elegant Flutter architecture,vibrant community, and its codebase. Despite standing humbly among industry giants, APIDash holds its ground with a well-structured, easy-to-understand codebase that helped me understand how code must be written for a complicated application. The clarity and separation in its design make it a pleasure to explore and contribute to.Apart from that by contributing and going through open issues I was abl to understand a lot of backend concepts.While it may be an underdog today, I have no doubt that it will grow into a formidable contender in the future. I look forward to contributing more features and supporting others along the way! + - **Areas for improvement:** I feel like the application would fullfill its goal as it reaches into wider audience of api testers . And for that the application has to tend to broader needs of api testers.Currently APIDash supports on REST APIs and GRAPHQL in its work flow. It would be great to add in other protocols into the app. --- - - - - -### API Testing Support + ### API Testing Support I would like to introduce some feautures as listed below to enhance the api testing support of the application.All this abstracts are part of a single proposal. ### Abstract 1(Web Socket Implementation) Introducing web socket implementation into the APIDash. Introduce a new client and manager to handle the websocket messages and render them in the ui . -The solution involves providing the users with an option to change ping intervals, number of reconnection attempts, interval between reconnection attenpts. All of this would be managed inside the settings. The ui updates would be made using riverpod provider specifically for websocket messages for each request ids. All this would be initiated by user clicking on the new APIType (Web Socket). The appropriate changes would be done in codegen related files to give code. + +The solution involves providing the users with an option to change ping intervals, number of reconnection attempts, interval between reconnection attenpts. All of this would be managed inside the settings. The ui updates would be made using riverpod provider specifically for websocket messages for each request ids.Finally during onError and onDone the frames are added into the websocket Response Model. All this would be initiated by user clicking on the new APIType (Web Socket). The appropriate changes would be done in codegen related files to give code. +In the proposed approach I am using a web Socket Client Manager similar to the Http Manger to not conflict with the existing services. + +Operations on incoming messages: +-Upon the incoming messages we can delete specific ones or whole. +-We can also search for keywords withing every message. Which would be highlightned.(Not Submitted in POC) + I have tested my approach on custom endpoints https://github.com/Clasherzz/testing/blob/main/websock.js , echo websocket and multiple fast incoming web socket message endpoints related to bit coin and stock prices to ensure robustness. Architecture is as shown below: ``` @@ -127,8 +132,12 @@ Architecture is as shown below: ### Abstract 2(SSE Support) +Implementation fo server send events in the Apidash. + Trying to implement SSE Support into the application. Using a special provider for incoming frames just like in web socket messages . Now the incoming messages would be parsed and decided if it is comment ,data,event,id,retry. Data is the most important one that is needed to be shown. But we keep an advanced option in settings for dev's who need advanced testing they can turn on it to all other kind of frames. This would be helpful for testers for advanced testing.Additionally the ui would render message perfectly if it is a json. +The apiType uses the Http Client Manager itself but generates a streamed reponse and listens into it. + Architecture is as shown below: ``` +--------------------------------+ @@ -203,9 +212,9 @@ Images of POC: Proposing to include graphql variable support and graphql introspection . Graphql variable :- -UI Changes: -Make a json editor pane as an additional tab in request pane for graphql with enviornment support. -And then parse it along the body of request. + +UI Changes:Make a json editor pane as an additional tab in request pane for graphql with enviornment support. +Service changes: And then parse it along the body of request. The approach was verified in custom endpoint and in https://rickandmortyapi.com/graphql . A partial implementation of this was done in https://github.com/foss42/apidash/pull/588 with a mistake in ui approach which would be rectified. @@ -307,29 +316,20 @@ Afterwards if possible i would like to make a package similar to json explorer t ### Abstract 4(URL Encoded Multipart and File Support) -Url Encoded Multipart(Issue #352):- +Url Encoded Multipart(Issue #337):- +Sending MultiPart through the body with x-www-form-urlencoded content type. + Ui change: There would be a toggle button in multipart tab that can switch between multipart and urlencoded multipart . Request model change: An additional content type (application/x-www-form-urlencoded). By default the choice would be url encoded multipart content type and would only change to multipart if toggle button is used or if a file is uploaded and selected to send. Service level change:- We would make key value pairs as string . This is encoded and passes into the body along with added header . -File support(Issue #337);- +File support(Issue #352):- +Sending Files through octect-stream content type. UI Changes: Make a combination of drag and droppable and select file ui as a new tab. Model changes: Add another content type application/octet-stream . -Service changes: Stream the file as bytes - - List fileBytes = await file.readAsBytes(); -Then change it to base64 format: - - String base64String = base64Encode(fileBytes); -We can run this - - - - - - - - +Service changes:We would first stream the file as bytes , add it into the body . +Put Content-Type header as application/octet-stream and send the request. + From 866e24943fcbc1224d63e95f68bbe985d68384e3 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 22:07:55 +0530 Subject: [PATCH 030/188] Update Application_K_Govind_API_Support.md --- doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 1fc9a0986..b6cacda0b 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -136,7 +136,7 @@ Implementation fo server send events in the Apidash. Trying to implement SSE Support into the application. Using a special provider for incoming frames just like in web socket messages . Now the incoming messages would be parsed and decided if it is comment ,data,event,id,retry. Data is the most important one that is needed to be shown. But we keep an advanced option in settings for dev's who need advanced testing they can turn on it to all other kind of frames. This would be helpful for testers for advanced testing.Additionally the ui would render message perfectly if it is a json. -The apiType uses the Http Client Manager itself but generates a streamed reponse and listens into it. +The apiType uses the Http Client Manager itself but generates a streamed reponse and listens into it.Inorder to get the actual request headers send by the client i had to use the http_interceptopr package(https://pub.dev/packages/http_interceptor). Architecture is as shown below: ``` @@ -201,7 +201,7 @@ Architecture is as shown below: ``` -POC Link: +POC Link: https://github.com/foss42/apidash/pull/757 Images of POC: ![SSE](./images/SSE(1).png) ![SSE](./images/SSE(1).png) From 62a0cb5635b1ee32ee350d3d83b09ac8d6a0e091 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 22:08:12 +0530 Subject: [PATCH 031/188] Update Application_K_Govind_API_Support.md --- doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index b6cacda0b..62e0eab5c 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -12,7 +12,7 @@ - **Time Zone:** Thrissur, KL · UTC+5:30 (IST) - **Resume:** https://drive.google.com/drive/folders/1nHlF1_uSjGRc-DruOZaVZeZT-MF8FBVh?usp=sharing -- + --- From 4d7e4eadfad789ebbad44601d13db0bb2f672149 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 22:10:40 +0530 Subject: [PATCH 032/188] Update Application_K_Govind_API_Support.md --- doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 62e0eab5c..8939b1fa6 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -138,6 +138,8 @@ Trying to implement SSE Support into the application. Using a special provider f The apiType uses the Http Client Manager itself but generates a streamed reponse and listens into it.Inorder to get the actual request headers send by the client i had to use the http_interceptopr package(https://pub.dev/packages/http_interceptor). +My approach was tested on a https://sse.dev/test + Architecture is as shown below: ``` +--------------------------------+ From 95270528ccd4c75698a90a505421258549072f0b Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 22:11:38 +0530 Subject: [PATCH 033/188] Update Application_K_Govind_API_Support.md --- doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 8939b1fa6..fbca42c39 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -372,5 +372,3 @@ Put Content-Type header as application/octet-stream and send the request. --- -This structure provides clear formatting and proper indentation in Markdown for easy readability. Let me know if you need any refinements! - From ff9b7bfa08e92ea346306033988044de7a63cf0b Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Wed, 2 Apr 2025 01:29:16 +0530 Subject: [PATCH 034/188] Update Application_K_Govind_API_Support.md --- doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index fbca42c39..4de34a407 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -134,7 +134,7 @@ Architecture is as shown below: ### Abstract 2(SSE Support) Implementation fo server send events in the Apidash. -Trying to implement SSE Support into the application. Using a special provider for incoming frames just like in web socket messages . Now the incoming messages would be parsed and decided if it is comment ,data,event,id,retry. Data is the most important one that is needed to be shown. But we keep an advanced option in settings for dev's who need advanced testing they can turn on it to all other kind of frames. This would be helpful for testers for advanced testing.Additionally the ui would render message perfectly if it is a json. +Trying to implement SSE Support into the application. Using a special provider for incoming frames just like in web socket messages . Now the incoming messages would be parsed and decided if it is comment ,data,event,id,retry. Data is the most important one that is needed to be shown. Often comments , id , retry are hidden away. We can provide an option for advanced visibily in settings that helps the developer to see these frames if they want. This can enhance their testing. The apiType uses the Http Client Manager itself but generates a streamed reponse and listens into it.Inorder to get the actual request headers send by the client i had to use the http_interceptopr package(https://pub.dev/packages/http_interceptor). From eb40bbbe22a55b37f9b3866643a52ac540da4be7 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Wed, 2 Apr 2025 01:33:45 +0530 Subject: [PATCH 035/188] Update Application_K_Govind_API_Support.md --- .../gsoc/Application_K_Govind_API_Support.md | 112 +++++++++--------- 1 file changed, 54 insertions(+), 58 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 4de34a407..c07da54ae 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -142,64 +142,60 @@ My approach was tested on a https://sse.dev/test Architecture is as shown below: ``` - +--------------------------------+ - | SSE Server | - | - Manages connections | - | - Sends event streams | - +---------------+--------------+ - | - v - +--------------------------------------+ - | Settings Connection Manager | - | - Handles retry intervals | - | - Manages reconnections | - | - Configures event listeners | - | - (Optional) Show Advanced Options| - | - Comments | - | - ID | - | - Retry Interval | - +----------------+-------------------+ - | - v - +-------------------------------------------------------------------+ - | SSE Client (http package / eventsource) | - | - Establishes connection | - | - Listens for events | - | - Handles automatic reconnections | - | - onError → Updates Riverpod SSE Response Model State | - | → Saves event history in HistoryModel Provider | - | - onEvent → Updates SSE Messages Provider | - | - onDone → Updates Riverpod SSE Response Model State | - | → Saves event history in HistoryModel Provider | - | - **HTTP Interceptor** (Inside SSE Client) | - +----------------------+----------------------+--------------------+ - | | - | | - +------------------------------------+ +--------------------------------+ - | SSE Messages Riverpod Provider | | Riverpod State Management | - | - Stores incoming messages | | - Stores all messages | - | - Groups messages by event ID | | - Updates SSE Model | - | - Provides real-time updates | | - Handles UI reactivity | - +------------------------------------+ +--------------------------------+ - | | - | | - +------------------------------------------------+ - | HistoryModel Provider | - | - Saves event history upon errors (`onError`)| - | - Saves event history when done (`onDone`) | - | - Stores event metadata & timestamps | - | - Persists disconnected session logs | - +------------------------------------------------+ - | - v - +-----------------------------------------+ - | Flutter SSE UI | - | - Search messages | - | - Clear all/one message | - | - Scroll to top/up option | - | - Dynamic UI per event type | - | - View Event History | - +-----------------------------------------+ + +--------------------------------+ +| WebSocket Server | +| - Manages connections | +| - Handles messages | ++---------------+--------------+ + | + v ++--------------------------------------+ +| Settings Connection Manager | +| - Ping interval handling | +| - Reconnection attempts | +| - Interval between retries | ++----------------+-------------------+ + | + v ++---------------------------------------------------------------+ +| WebSocket Client (web_socket_channel) | +| - Establishes connection | +| - Sends & receives messages | +| - Handles ping & retries | +| - onError → Updates Riverpod WebSocket Response Model State | +| → Saves event history in HistoryModel Provider | +| - onListen → Updates WebSocket Messages Provider | +| - onDone → Updates Riverpod WebSocket Response Model State | +| → Saves event history in HistoryModel Provider | ++----------------------+----------------------+----------------+ + | | + | | ++------------------------------------+ +--------------------------------+ +| WebSocket Messages Riverpod | | Riverpod State Management | +| Provider | | - Stores all messages | +| - Stores incoming messages | | - Updates WebSocket Model | +| - Groups messages by request ID | | - Handles UI reactivity | +| - Provides real-time updates | +--------------------------------+ ++------------------------------------+ | + | | + v | ++------------------------------------------------+ +| HistoryModel Provider | +| - Saves event history upon errors (`onError`)| +| - Saves event history when done (`onDone`) | +| - Stores event metadata & timestamps | +| - Persists disconnected session logs | ++------------------------------------------------+ + | + v ++-----------------------------------------+ +| Flutter WebSocket UI | +| - Search messages | +| - Clear all/one message | +| - Scroll to top/up option | +| - Dynamic UI per request | +| - View WebSocket Message History | ++-----------------------------------------+ ``` From e5e7a8cca045ca9295c6a1dd430bde3df477ff11 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Wed, 2 Apr 2025 01:36:19 +0530 Subject: [PATCH 036/188] Update Application_K_Govind_API_Support.md --- .../gsoc/Application_K_Govind_API_Support.md | 149 ++++++++++-------- 1 file changed, 81 insertions(+), 68 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index c07da54ae..c999fa69e 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -76,73 +76,7 @@ Operations on incoming messages: I have tested my approach on custom endpoints https://github.com/Clasherzz/testing/blob/main/websock.js , echo websocket and multiple fast incoming web socket message endpoints related to bit coin and stock prices to ensure robustness. Architecture is as shown below: ``` - +--------------------------------+ - | WebSocket Server | - | - Manages connections | - | - Handles messages | - +---------------+--------------+ - | - v - +--------------------------------------+ - | Settings Connection Manager | - | - Ping interval handling | - | - Reconnection attempts | - | - Interval between retries | - +----------------+-------------------+ - | - v - +---------------------------------------------------------------+ - | WebSocket Client (web_socket_channel) | - | - Establishes connection | - | - Sends & receives messages | - | - Handles ping & retries | - | - onError → Updates Riverpod WebSocket Response Model State | - | - onListen → Updates WebSocket Messages Provider | - | - onDone → Updates Riverpod WebSocket Response Model State | - +----------------------+----------------------+----------------+ - | | - | | - +------------------------------------+ +--------------------------------+ - | WebSocket Messages Riverpod | | Riverpod State Management | - | Provider | | - Stores all messages | - | - Stores incoming messages | | - Updates WebSocket Model | - | - Groups messages by request ID | | - Handles UI reactivity | - | - Provides real-time updates | +--------------------------------+ - +------------------------------------+ | - | | - v | - +-----------------------------------------+ - | Flutter WebSocket UI | - | - Search messages | - | - Clear all/one message | - | - Scroll to top/up option | - | - Dynamic UI per request | - +-----------------------------------------+ - - - -``` - - -- **Technologies & Tools:** The approach uses the package web_socket_channel(^3.0.1) -- **Expected Outcomes:** A clean ui with maximum smoothness satisfying above mentioned solutions. -- **Linked PR for POC:** https://github.com/foss42/apidash/pull/555 (The PR is in no way the final product but simply to show the code structure and my approach) - ![Alt text](./images/websocket(1).png) - ![Alt text](./images/websocket(2).png) - - -### Abstract 2(SSE Support) -Implementation fo server send events in the Apidash. - -Trying to implement SSE Support into the application. Using a special provider for incoming frames just like in web socket messages . Now the incoming messages would be parsed and decided if it is comment ,data,event,id,retry. Data is the most important one that is needed to be shown. Often comments , id , retry are hidden away. We can provide an option for advanced visibily in settings that helps the developer to see these frames if they want. This can enhance their testing. - -The apiType uses the Http Client Manager itself but generates a streamed reponse and listens into it.Inorder to get the actual request headers send by the client i had to use the http_interceptopr package(https://pub.dev/packages/http_interceptor). - -My approach was tested on a https://sse.dev/test - -Architecture is as shown below: -``` - +--------------------------------+ + +--------------------------------+ | WebSocket Server | | - Manages connections | | - Handles messages | @@ -195,8 +129,87 @@ Architecture is as shown below: | - Scroll to top/up option | | - Dynamic UI per request | | - View WebSocket Message History | -+-----------------------------------------+ ++-----------------------------------------+ + +``` + + +- **Technologies & Tools:** The approach uses the package web_socket_channel(^3.0.1) +- **Expected Outcomes:** A clean ui with maximum smoothness satisfying above mentioned solutions. +- **Linked PR for POC:** https://github.com/foss42/apidash/pull/555 (The PR is in no way the final product but simply to show the code structure and my approach) + ![Alt text](./images/websocket(1).png) + ![Alt text](./images/websocket(2).png) + +### Abstract 2(SSE Support) +Implementation fo server send events in the Apidash. + +Trying to implement SSE Support into the application. Using a special provider for incoming frames just like in web socket messages . Now the incoming messages would be parsed and decided if it is comment ,data,event,id,retry. Data is the most important one that is needed to be shown. Often comments , id , retry are hidden away. We can provide an option for advanced visibily in settings that helps the developer to see these frames if they want. This can enhance their testing. + +The apiType uses the Http Client Manager itself but generates a streamed reponse and listens into it.Inorder to get the actual request headers send by the client i had to use the http_interceptopr package(https://pub.dev/packages/http_interceptor). + +My approach was tested on a https://sse.dev/test + +Architecture is as shown below: +``` + +--------------------------------+ + | SSE Server | + | - Manages connections | + | - Sends event streams | + +---------------+--------------+ + | + v + +--------------------------------------+ + | Settings Connection Manager | + | - Handles retry intervals | + | - Manages reconnections | + | - Configures event listeners | + | - (Optional) Show Advanced Options| + | - Comments | + | - ID | + | - Retry Interval | + +----------------+-------------------+ + | + v + +-------------------------------------------------------------------+ + | SSE Client (http package / eventsource) | + | - Establishes connection | + | - Listens for events | + | - Handles automatic reconnections | + | - onError → Updates Riverpod SSE Response Model State | + | → Saves event history in HistoryModel Provider | + | - onEvent → Updates SSE Messages Provider | + | - onDone → Updates Riverpod SSE Response Model State | + | → Saves event history in HistoryModel Provider | + | - **HTTP Interceptor** (Inside SSE Client) | + +----------------------+----------------------+--------------------+ + | | + | | + +------------------------------------+ +--------------------------------+ + | SSE Messages Riverpod Provider | | Riverpod State Management | + | - Stores incoming messages | | - Stores all messages | + | - Groups messages by event ID | | - Updates SSE Model | + | - Provides real-time updates | | - Handles UI reactivity | + +------------------------------------+ +--------------------------------+ + | | + | | + +------------------------------------------------+ + | HistoryModel Provider | + | - Saves event history upon errors (`onError`)| + | - Saves event history when done (`onDone`) | + | - Stores event metadata & timestamps | + | - Persists disconnected session logs | + +------------------------------------------------+ + | + v + +-----------------------------------------+ + | Flutter SSE UI | + | - Search messages | + | - Clear all/one message | + | - Scroll to top/up option | + | - Dynamic UI per event type | + | - View Event History | + +-----------------------------------------+ ``` POC Link: https://github.com/foss42/apidash/pull/757 From ed7b35e671f2bf672703fff4c9ddb5600506bf94 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Fri, 4 Apr 2025 18:30:41 +0530 Subject: [PATCH 037/188] Update Application_K_Govind_API_Support.md removed abstract --- .../2025/gsoc/Application_K_Govind_API_Support.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index c999fa69e..b2263a4d5 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -63,7 +63,7 @@ I would like to introduce some feautures as listed below to enhance the api testing support of the application.All this abstracts are part of a single proposal. -### Abstract 1(Web Socket Implementation) +###Web Socket Implementation (Issue Number: #15) Introducing web socket implementation into the APIDash. Introduce a new client and manager to handle the websocket messages and render them in the ui . The solution involves providing the users with an option to change ping intervals, number of reconnection attempts, interval between reconnection attenpts. All of this would be managed inside the settings. The ui updates would be made using riverpod provider specifically for websocket messages for each request ids.Finally during onError and onDone the frames are added into the websocket Response Model. All this would be initiated by user clicking on the new APIType (Web Socket). The appropriate changes would be done in codegen related files to give code. @@ -141,7 +141,7 @@ Architecture is as shown below: ![Alt text](./images/websocket(2).png) -### Abstract 2(SSE Support) +###SSE Support(Issue Number #116) Implementation fo server send events in the Apidash. Trying to implement SSE Support into the application. Using a special provider for incoming frames just like in web socket messages . Now the incoming messages would be parsed and decided if it is comment ,data,event,id,retry. Data is the most important one that is needed to be shown. Often comments , id , retry are hidden away. We can provide an option for advanced visibily in settings that helps the developer to see these frames if they want. This can enhance their testing. @@ -219,10 +219,10 @@ Images of POC: -### Abstract 3(GraphQL Enhancement) +### GraphQL Enhancement Proposing to include graphql variable support and graphql introspection . -Graphql variable :- +Graphql variable(Issue no #576) :- UI Changes:Make a json editor pane as an additional tab in request pane for graphql with enviornment support. Service changes: And then parse it along the body of request. @@ -325,16 +325,14 @@ The introspection query would be: Afterwards if possible i would like to make a package similar to json explorer to render the ui with collapse and expand feauture . This would require some time as SDL is quite different from the standard JSON. -### Abstract 4(URL Encoded Multipart and File Support) - -Url Encoded Multipart(Issue #337):- +###URL Encoded Multipart(Issue $337):- Sending MultiPart through the body with x-www-form-urlencoded content type. Ui change: There would be a toggle button in multipart tab that can switch between multipart and urlencoded multipart . Request model change: An additional content type (application/x-www-form-urlencoded). By default the choice would be url encoded multipart content type and would only change to multipart if toggle button is used or if a file is uploaded and selected to send. Service level change:- We would make key value pairs as string . This is encoded and passes into the body along with added header . -File support(Issue #352):- +###File support(Issue #352):- Sending Files through octect-stream content type. UI Changes: Make a combination of drag and droppable and select file ui as a new tab. Model changes: Add another content type application/octet-stream . From 128d2433ddf47fa7f6cb4d1668e27c0cd1089e1b Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Fri, 4 Apr 2025 18:33:12 +0530 Subject: [PATCH 038/188] Update Application_K_Govind_API_Support.md --- .../2025/gsoc/Application_K_Govind_API_Support.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index b2263a4d5..4a4040cb1 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -63,7 +63,7 @@ I would like to introduce some feautures as listed below to enhance the api testing support of the application.All this abstracts are part of a single proposal. -###Web Socket Implementation (Issue Number: #15) +### Web Socket Implementation (Issue Number: #15) Introducing web socket implementation into the APIDash. Introduce a new client and manager to handle the websocket messages and render them in the ui . The solution involves providing the users with an option to change ping intervals, number of reconnection attempts, interval between reconnection attenpts. All of this would be managed inside the settings. The ui updates would be made using riverpod provider specifically for websocket messages for each request ids.Finally during onError and onDone the frames are added into the websocket Response Model. All this would be initiated by user clicking on the new APIType (Web Socket). The appropriate changes would be done in codegen related files to give code. @@ -141,7 +141,7 @@ Architecture is as shown below: ![Alt text](./images/websocket(2).png) -###SSE Support(Issue Number #116) +### SSE Support(Issue Number #116) Implementation fo server send events in the Apidash. Trying to implement SSE Support into the application. Using a special provider for incoming frames just like in web socket messages . Now the incoming messages would be parsed and decided if it is comment ,data,event,id,retry. Data is the most important one that is needed to be shown. Often comments , id , retry are hidden away. We can provide an option for advanced visibily in settings that helps the developer to see these frames if they want. This can enhance their testing. @@ -325,14 +325,14 @@ The introspection query would be: Afterwards if possible i would like to make a package similar to json explorer to render the ui with collapse and expand feauture . This would require some time as SDL is quite different from the standard JSON. -###URL Encoded Multipart(Issue $337):- +### URL Encoded Multipart(Issue $337):- Sending MultiPart through the body with x-www-form-urlencoded content type. Ui change: There would be a toggle button in multipart tab that can switch between multipart and urlencoded multipart . Request model change: An additional content type (application/x-www-form-urlencoded). By default the choice would be url encoded multipart content type and would only change to multipart if toggle button is used or if a file is uploaded and selected to send. Service level change:- We would make key value pairs as string . This is encoded and passes into the body along with added header . -###File support(Issue #352):- +### File support(Issue #352):- Sending Files through octect-stream content type. UI Changes: Make a combination of drag and droppable and select file ui as a new tab. Model changes: Add another content type application/octet-stream . From bf6f098f59a963eb93b86ff8a7baf0e3853e8a66 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Fri, 4 Apr 2025 23:54:26 +0530 Subject: [PATCH 039/188] Update Application_K_Govind_API_Support.md --- .../gsoc/Application_K_Govind_API_Support.md | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 4a4040cb1..bcb938263 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -230,7 +230,7 @@ Service changes: And then parse it along the body of request. The approach was verified in custom endpoint and in https://rickandmortyapi.com/graphql . A partial implementation of this was done in https://github.com/foss42/apidash/pull/588 with a mistake in ui approach which would be rectified. -GraphQL Introspection :- The approach is to use a prebuild introspection query that asks for all the contents necessary that developer would want to know and display the results in GraphQL SDL (Schema Definition Language) . The introspection query asks for all schema details mutation ,subscription, query schema types , directives . Later on we iterate through the received json making the GRAPHQL SDL. +GraphQL Inspect Schema :- The approach is to use a prebuild introspection query that asks for all the contents necessary that developer would want to know and display the results in GraphQL SDL (Schema Definition Language) . The introspection query asks for all schema details mutation ,subscription, query schema types , directives . Later on we iterate through the received json making the GRAPHQL SDL. The introspection query would be: ``` { @@ -348,22 +348,32 @@ Put Content-Type header as application/octet-stream and send the request. #### **Week 1-2:** - Getting to know about the organization and what mentors wants to say about the work I am about to start. Fix the timings of meetings if there are any. Get to know which time do the maintainers be more active and share the approach and queries with them. - Changing the design and architecture from feedback. -- Start with the graphql variable and inspect schema feautures. - -#### **Week 3-4:** (Web Socket) -- Coninuing work on graphql feautures and add testing. -- Start working on the initial ui and set update settings providers for the new feautures of Web Socket. -- Add service layer of web sockets and make way for codegen feauture for web socket. -- Work on handling the web socket history. Add testing for the ui and service code. +- Add the ui for graphql variables feauture. Change service layer to accomodate the change. +- Make additional endpoints to test the feauture and test it on them. Make related test files for this feauture. +- Work on graphql introspection query and on its transformation to GraphqlSDL. +- Add the ui for the inspect Schema. +- Work on the endpoints if needed and test files of this feauture. +- Work on the feauture and do improvements if the testing is causing issues. + +#### **Week 3-4:** (Web Socket) +- Start working on the initial ui. +- update settings providers for the new feautures of Web Socket (number of reconnection attemps, time interval between number of reconnection attempt,ping interval). +- Add service layer of web sockets. +- Make ui changes to reflect the incoming and outgoing messages.' +- Add Searching through the messages feauture. +- Work on handling the web socket history. +- Make endpoints to test the feauture. And add the ui and service level tests. +- Work on the feauture and do improvements if the testing is causing issues. #### **Week 5-6:** (SSE) - Work on handling the web socket history. Add testing for the ui and service code. - Start working on the initial ui and set update settings providers for the new feautures of SSE. - Add service layer of web sockets and make way for codegen feauture for SSE. +- #### **Week 7-8:** -- Work on handling the SSE history.Add testing for ui and service code. +- Work on handling the SSE history. Add testing for ui and service code. - Work on UI changes for url encoded multipart and make service layer changes to accomadate that. - Add testing for the ui changes. From efad455c36cfb2fdcad80786386fdeb64e49e705 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Sat, 5 Apr 2025 01:57:52 +0530 Subject: [PATCH 040/188] Update Application_K_Govind_API_Support.md --- .../gsoc/Application_K_Govind_API_Support.md | 81 ++++++++++++++++--- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index bcb938263..be55dd522 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -338,52 +338,107 @@ UI Changes: Make a combination of drag and droppable and select file ui as a new Model changes: Add another content type application/octet-stream . Service changes:We would first stream the file as bytes , add it into the body . Put Content-Type header as application/octet-stream and send the request. + +### Basic Authentication (Issue Number #610) +This would be a straightforward implementation. We take the input password and username and follow the below said transformation. + ++--------------------------------------------------+ +| 1. Client combines them into one string | +| Format: username:password | +| Example: admin:pass123 | ++--------------------------------------------------+ + | + v ++--------------------------------------------------+ +| 2.Client encodes the string using Base64 | +| Result: YWRtaW46cGFzczEyMw== | ++--------------------------------------------------+ + | + v ++--------------------------------------------------+ +| 3. Client adds Authorization header | +| Format: Authorization: Basic | +| Example: Authorization: Basic YWRtaW46... | ++--------------------------------------------------+ +### API Key authentication. +UI:Upon selecting AuthType as APIKey there would be a text field for entering APIKey and a sliding button to select whther it should be send through header(X-API-Type) or +via query parameter. + +Then the transformation occurs depending on the selection. + +### Bearer Token authentication: +UI: There would be a text field to enter the bearer token. +The value is added to the Authorization header in the format by the client: + Bearer ### Weekly Timeline -#### **Week 1-2:** +#### **Week 1:** - Getting to know about the organization and what mentors wants to say about the work I am about to start. Fix the timings of meetings if there are any. Get to know which time do the maintainers be more active and share the approach and queries with them. - Changing the design and architecture from feedback. - Add the ui for graphql variables feauture. Change service layer to accomodate the change. -- Make additional endpoints to test the feauture and test it on them. Make related test files for this feauture. + +#### **Week 2:** +- Make additional endpoints to test the graphql variable feauture and test it on them. Make related test files for this feauture. - Work on graphql introspection query and on its transformation to GraphqlSDL. - Add the ui for the inspect Schema. - Work on the endpoints if needed and test files of this feauture. - Work on the feauture and do improvements if the testing is causing issues. -#### **Week 3-4:** (Web Socket) +#### **Week 3:** - Start working on the initial ui. - update settings providers for the new feautures of Web Socket (number of reconnection attemps, time interval between number of reconnection attempt,ping interval). - Add service layer of web sockets. -- Make ui changes to reflect the incoming and outgoing messages.' +- Make ui changes to reflect the incoming and outgoing messages. + +#### **Week 4:** - Add Searching through the messages feauture. - Work on handling the web socket history. - Make endpoints to test the feauture. And add the ui and service level tests. - Work on the feauture and do improvements if the testing is causing issues. -#### **Week 5-6:** (SSE) -- Work on handling the web socket history. Add testing for the ui and service code. +#### **Week 5:** - Start working on the initial ui and set update settings providers for the new feautures of SSE. -- Add service layer of web sockets and make way for codegen feauture for SSE. -- +- Add service layer of SSE. +- Make ui for incoming frames. +#### **Week 6:** +- Add Searching through the messages feauture. +- Work on handling the SSE history. +- Make custom endpoints to test the feauture if organization wants it. And add the ui and service level tests. +- Work on the feauture and do improvements if the testing is causing issues. + #### **Week 7-8:** -- Work on handling the SSE history. Add testing for ui and service code. +- Make way for codegen feautures of sse and WebSocket. - Work on UI changes for url encoded multipart and make service layer changes to accomadate that. - Add testing for the ui changes. +- Make endpoints for this feauture if needed and test it upon it generating needed test files. -#### **Week 9-10:** +#### **Week 9** - Work on UI changes of File Request and implement the service layer changes. -- Add test files to this. after testing thoroughly on all file types. +- Add test files to this After testing thoroughly on major file types coming in different file sizes. + +### **Week 10**: +- Make UI changes for Bearer Token. +- Make service level changes needed for it. +- Write test files for this feautre. +- Make UI changes for API Authentication +- Make service level changes for this and write needed test files. + + +#### **Week 11**: +- Make UI changes for Basic Authentication. +- Make service level changes needed for it. +- Write test files for this feautre. +- Final polishing done for any feauture is done if needed. -#### **Week 11-12:** -- Final polishing done if needed. +#### **Week 12:** - Adding needed docmentations. - Submitting the final documentation and work. From eadd008fb1c1276d0b0c78e88e7f1fbf437c0dbb Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Sat, 5 Apr 2025 02:02:14 +0530 Subject: [PATCH 041/188] Update Application_K_Govind_API_Support.md --- .../2025/gsoc/Application_K_Govind_API_Support.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index be55dd522..0532b4292 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -329,14 +329,20 @@ Afterwards if possible i would like to make a package similar to json explorer t Sending MultiPart through the body with x-www-form-urlencoded content type. Ui change: There would be a toggle button in multipart tab that can switch between multipart and urlencoded multipart . + Request model change: An additional content type (application/x-www-form-urlencoded). By default the choice would be url encoded multipart content type and would only change to multipart if toggle button is used or if a file is uploaded and selected to send. + Service level change:- We would make key value pairs as string . This is encoded and passes into the body along with added header . ### File support(Issue #352):- Sending Files through octect-stream content type. -UI Changes: Make a combination of drag and droppable and select file ui as a new tab. + +UI Changes: Make a combination of drag and droppable and select file ui as a new tab. Make a progression bar to show the uploading progress. + Model changes: Add another content type application/octet-stream . + Service changes:We would first stream the file as bytes , add it into the body . + Put Content-Type header as application/octet-stream and send the request. ### Basic Authentication (Issue Number #610) @@ -407,7 +413,7 @@ The value is added to the Authorization header in the format by the client: - Make ui for incoming frames. #### **Week 6:** -- Add Searching through the messages feauture. +- Add Searching through the frames feauture. - Work on handling the SSE history. - Make custom endpoints to test the feauture if organization wants it. And add the ui and service level tests. - Work on the feauture and do improvements if the testing is causing issues. From aaca77a97facff6a3db1879e696ce6968322b8df Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Sat, 5 Apr 2025 02:03:08 +0530 Subject: [PATCH 042/188] Update Application_K_Govind_API_Support.md --- doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 0532b4292..7deaa13be 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -347,7 +347,7 @@ Put Content-Type header as application/octet-stream and send the request. ### Basic Authentication (Issue Number #610) This would be a straightforward implementation. We take the input password and username and follow the below said transformation. - + ``` +--------------------------------------------------+ | 1. Client combines them into one string | | Format: username:password | @@ -367,7 +367,7 @@ This would be a straightforward implementation. We take the input password and u | Example: Authorization: Basic YWRtaW46... | +--------------------------------------------------+ - +``` ### API Key authentication. UI:Upon selecting AuthType as APIKey there would be a text field for entering APIKey and a sliding button to select whther it should be send through header(X-API-Type) or via query parameter. From 1fad51bc4ff8bd02ffa4b91f5f0f3334c1150b31 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Sat, 5 Apr 2025 02:10:53 +0530 Subject: [PATCH 043/188] Update Application_K_Govind_API_Support.md --- doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index 7deaa13be..ed8b35bda 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -398,7 +398,7 @@ The value is added to the Authorization header in the format by the client: #### **Week 3:** - Start working on the initial ui. - update settings providers for the new feautures of Web Socket (number of reconnection attemps, time interval between number of reconnection attempt,ping interval). -- Add service layer of web sockets. +- Add service layer of web sockets(Like adding the functions to listen , catching the error and cancellation) - Make ui changes to reflect the incoming and outgoing messages. #### **Week 4:** @@ -409,7 +409,7 @@ The value is added to the Authorization header in the format by the client: #### **Week 5:** - Start working on the initial ui and set update settings providers for the new feautures of SSE. -- Add service layer of SSE. +- Add service layer of SSE.(add functions to listen , handle error etc). - Make ui for incoming frames. #### **Week 6:** From 3a82284d225e1b6624a1fb6a964f9b41310fd0eb Mon Sep 17 00:00:00 2001 From: Shree Meher Date: Wed, 2 Apr 2025 22:17:53 -0500 Subject: [PATCH 044/188] Initial GSoC 2025 Proposal: AI UI Designer by Shree Meher --- .../application_ShreeMeher_AI-UI-Designer.md | 196 +----------------- 1 file changed, 4 insertions(+), 192 deletions(-) diff --git a/doc/proposals/2025/gsoc/application_ShreeMeher_AI-UI-Designer.md b/doc/proposals/2025/gsoc/application_ShreeMeher_AI-UI-Designer.md index ca7217614..8af3df7c8 100644 --- a/doc/proposals/2025/gsoc/application_ShreeMeher_AI-UI-Designer.md +++ b/doc/proposals/2025/gsoc/application_ShreeMeher_AI-UI-Designer.md @@ -23,196 +23,8 @@ This will streamline API testing, visualization, and UI generation — saving de - UI export options (Flutter/Web) - Documentation + tests +## Timeline +Will be finalized after initial feedback. -## Motivation -As a developer passionate about user experience and automation, I’ve always found API tools fascinating — they bridge human interaction and technical infrastructure. When I explored API Dash, its focus on speed, clarity, and minimalism stood out from other tools I’ve used. - -What excites me most about this project is the challenge of turning raw API data into something meaningful and visual, especially through dynamic UI generation. I enjoy frontend development, but also love working under the hood with JSON, schema parsing, and smart layout logic — this project sits at the perfect intersection of those interests. - -Beyond the technical scope, I’m driven by the potential impact on accessibility and education. By making complex APIs easier to understand and interact with, we empower students, developers from non-traditional backgrounds, and contributors around the world — especially those in international or low-resource settings. - -Contributing to a tool that’s free, open-source, and community-driven means a lot to me. GSoC is not just an opportunity to build something great, but to grow through mentorship and collaboration, while helping shape a more inclusive and impactful developer ecosystem. - - -## ✨ Benefits to the Community - -This project directly strengthens the core value of API Dash — speed, clarity, and accessibility — by making API responses instantly visual and interactive. - -By enabling automatic UI generation from JSON or XML, we reduce the friction for developers to test, debug, and understand APIs. This is especially helpful for beginners, educators, and teams building internal tools. - -It enhances API Dash’s flexibility across diverse use cases — from prototyping and documentation to integration testing — and lowers the barrier to entry for developers across experience levels. - -This project also supports accessibility goals: visualizing APIs helps neurodiverse learners and non-native English speakers better grasp complex structures. - -Finally, well-tested, reusable UI components and improved docs will help API Dash scale better, attract more contributors, and evolve as a leading open-source dev tool. - - -## 📦 Deliverables - -### Phase 1 (Before Midterm Evaluation) -- ✅ **UI Parsing Engine (PoC):** Initial parser that analyzes API responses (JSON/XML) and extracts structure. -- ✅ **Component Generator v1:** Basic generation of UI components like tables, key-value cards, and simple forms. -- ✅ **Live Preview Panel:** Preview generated UI in real-time as API response is received. -- ✅ **Customization Panel (Basic):** Allow user to toggle between different UI layouts (table, card, raw JSON view). -- ✅ **Docs & Tests:** Documentation for codebase and test coverage for core modules. - -### Phase 2 (Post Midterm to Final Evaluation) -- ✅ **Advanced Component Generator:** Support nested data, arrays, empty states, pagination, filters, and dynamic layout resizing. -- ✅ **Export Feature:** One-click export to Dart (Flutter) or HTML code snippets for integration in user apps. -- ✅ **Theming & Styling Options:** Let users configure color schemes, borders, spacing, and responsive breakpoints. -- ✅ **Schema-aware Enhancements:** Support for OpenAPI/Swagger schema to pre-generate interface and validation hints. -- ✅ **Accessibility Improvements:** Keyboard navigation and screen-reader support for generated UIs. -- ✅ **Community UI Library (optional):** Start a curated set of reusable UI templates contributed by users. - - -## 📆 Timeline - -### Community Bonding (May 20 – June 16) -- Set up development environment and finalize UI generation strategy. -- Connect with mentors to discuss API patterns and styling preferences. -- Plan component architecture and UI preview interface. - ---- - -### Phase 1: (June 17 – July 15) - -**Week 1–2** -- Develop core parser for API responses (JSON/XML). -- Build prototype UI Generator v1 (tables, cards, basic form fields). - -**Week 3–4** -- Add live preview panel for dynamic UI rendering. -- Begin customization options (toggle views: table, card, raw). - -**Week 5** -- Basic test suite for generator module. -- Write clear documentation for PoC and parser. - -🧪 **Midterm Evaluation** - ---- - -### Phase 2: (July 15 – August 12) - -**Week 6–7** -- Improve component generation (nested JSON, arrays, filters, pagination). -- Integrate theming/styling options panel (light/dark mode, spacing, etc). - -**Week 8–9** -- Add export options (Flutter/Dart code, HTML snippets). -- Optimize layout rendering for responsiveness and mobile. - -**Week 10–11** -- Add accessibility features (keyboard support, screen reader labels). -- Add schema-aware UI hints using OpenAPI/Swagger files. - -**Week 12** -- Final polish + documentation. -- Submit screencast demo + final report. - -🎓 **Final Evaluation** - - - -## 🔭 Future Work & Stretch Goals - -- **OpenAPI Integration**: Add deeper support for parsing OpenAPI specs to auto-generate full UI forms and authentication flows. -- **Community Templates**: Let users contribute and share UI templates for common APIs (e.g., weather, finance, AI). -- **AI Enhancements**: Train a basic model to suggest UI layouts based on API response types. -- **Plugin Support**: Modularize the generator so new components (e.g., chart, maps) can be added easily by the community. -- **Real-time Collaboration**: Explore syncing API workspace editing in real-time (like a Google Docs for APIs). - -These goals can be taken up post-GSoC as future enhancements or extended during GSoC depending on the project pace. - - - - -## 🛠 Technical Approach - -The core goal is to build an intelligent system that takes raw API responses (JSON/XML) and transforms them into dynamic UI components, with customization and export support. - -### 1. **Parsing API Responses** -- Analyze structure of JSON/XML data (using `json_serializable`, `xml`, or similar Dart packages). -- Identify key-value pairs, arrays, nested objects to determine appropriate UI widgets. - -### 2. **Component Mapping Engine** -- Build a mapping system that links data types → UI components: - - String → TextField - - Boolean → Toggle/Switch - - List → Dropdown - - Object → Card or Nested Form -- Handle nested data recursively. - -### 3. **Dynamic UI Generation** -- Generate Flutter widgets using a tree structure. -- Allow instant preview and live editing (padding, styling, layout). -- Scaffold reusable components: `DataCard`, `AutoForm`, `APIGrid`, etc. - -### 4. **Customization Support** -- Allow users to tweak: - - Layout: vertical/horizontal/grid - - Visibility toggles - - Labels, styles, colors -- Provide real-time preview and export to Dart code. - -### 5. **Export Functionality** -- Convert the generated UI into clean, production-ready Flutter code. -- Package as a `.dart` file, downloadable or savable to project workspace. - -### 6. **Optional AI Assist** -- Use basic ML heuristics or an LLM API to recommend layout structures or group similar fields (as a stretch goal). - - - - - -## 📦 Deliverables (Milestone Breakdown) - -### 🔹 Milestone 1: Core Parsing + Component Mapping (Weeks 1–2) -- Build a robust parser to handle JSON/XML structures. -- Map data types to UI widgets (string → input, bool → toggle, etc). -- Scaffold reusable component structure (`DataCard`, `AutoForm`, etc). - -### 🔹 Milestone 2: UI Generator v1 + Preview Panel (Weeks 3–4) -- Generate dynamic UI based on parsed response. -- Render a real-time preview of the generated UI. -- Add layout toggles (card, table, raw view). -- Begin work on customization options. - -### 🔹 Milestone 3: Customization + Code Export (Weeks 5–7) -- Add theming and layout customization (color, grid/flex, spacing). -- Build export-to-Flutter button with clean Dart code output. -- Improve handling of nested objects and array rendering. - -### 🔹 Milestone 4: Advanced UX + Accessibility (Weeks 8–10) -- Add support for pagination, filters, form validation. -- Integrate accessibility features (ARIA labels, keyboard navigation). -- Add schema-aware layout hints (OpenAPI preview if time permits). - -### 🔹 Milestone 5: Polish, Docs & Community Readiness (Week 11–12) -- Write developer and user documentation. -- Record final demo screencast of the end-to-end workflow. -- Submit final report and guide for future contributors. - - -## 👋 About Me - -I’m Shree Meher, a Computer Science student with a strong interest in frontend development, UI/UX, and open-source collaboration. I enjoy building tools that make technical work more visual, accessible, and intuitive. - -I discovered API Dash while exploring lightweight alternatives to heavy API clients, and the project immediately resonated with me — especially the emphasis on clarity, customization, and minimalism. This project is a perfect fit for my skills in JavaScript, Python, and JSON parsing, while giving me the opportunity to dive deeper into Flutter and Dart. - -I’m currently learning Flutter and contributing to open-source platforms like CircuitVerse and API Dash. Through GSoC, I hope to make meaningful contributions, grow through mentorship, and help create tools that empower developers everywhere. - - - - -## 📅 Commitment - -- I will be fully available for the entire duration of GSoC 2025. -- I can dedicate **18–22 hours per week**, and more if needed. -- I do not have any other academic, job, or internship commitments during this time. -- I'm based in IST (UTC+5:30) and flexible with mentor availability and sync-ups. - -I’m committed to collaborating closely with mentors, incorporating feedback quickly, and delivering clean, maintainable code aligned with API Dash’s long-term vision. - - +## About Me +I’m a CS student skilled in Python, JavaScript, HTML/CSS, and working with APIs. I'm excited to contribute to API Dash and help make dev tools smarter and more intuitive. From 8afc1ec5c67a9916709f8bfa4b9804a17014edc2 Mon Sep 17 00:00:00 2001 From: Shree <107465077+s-meher@users.noreply.github.com> Date: Thu, 3 Apr 2025 23:09:53 -0500 Subject: [PATCH 045/188] Update application_ShreeMeher_AI-UI-Designer.md --- .../application_ShreeMeher_AI-UI-Designer.md | 196 +++++++++++++++++- 1 file changed, 192 insertions(+), 4 deletions(-) diff --git a/doc/proposals/2025/gsoc/application_ShreeMeher_AI-UI-Designer.md b/doc/proposals/2025/gsoc/application_ShreeMeher_AI-UI-Designer.md index 8af3df7c8..ca7217614 100644 --- a/doc/proposals/2025/gsoc/application_ShreeMeher_AI-UI-Designer.md +++ b/doc/proposals/2025/gsoc/application_ShreeMeher_AI-UI-Designer.md @@ -23,8 +23,196 @@ This will streamline API testing, visualization, and UI generation — saving de - UI export options (Flutter/Web) - Documentation + tests -## Timeline -Will be finalized after initial feedback. -## About Me -I’m a CS student skilled in Python, JavaScript, HTML/CSS, and working with APIs. I'm excited to contribute to API Dash and help make dev tools smarter and more intuitive. +## Motivation +As a developer passionate about user experience and automation, I’ve always found API tools fascinating — they bridge human interaction and technical infrastructure. When I explored API Dash, its focus on speed, clarity, and minimalism stood out from other tools I’ve used. + +What excites me most about this project is the challenge of turning raw API data into something meaningful and visual, especially through dynamic UI generation. I enjoy frontend development, but also love working under the hood with JSON, schema parsing, and smart layout logic — this project sits at the perfect intersection of those interests. + +Beyond the technical scope, I’m driven by the potential impact on accessibility and education. By making complex APIs easier to understand and interact with, we empower students, developers from non-traditional backgrounds, and contributors around the world — especially those in international or low-resource settings. + +Contributing to a tool that’s free, open-source, and community-driven means a lot to me. GSoC is not just an opportunity to build something great, but to grow through mentorship and collaboration, while helping shape a more inclusive and impactful developer ecosystem. + + +## ✨ Benefits to the Community + +This project directly strengthens the core value of API Dash — speed, clarity, and accessibility — by making API responses instantly visual and interactive. + +By enabling automatic UI generation from JSON or XML, we reduce the friction for developers to test, debug, and understand APIs. This is especially helpful for beginners, educators, and teams building internal tools. + +It enhances API Dash’s flexibility across diverse use cases — from prototyping and documentation to integration testing — and lowers the barrier to entry for developers across experience levels. + +This project also supports accessibility goals: visualizing APIs helps neurodiverse learners and non-native English speakers better grasp complex structures. + +Finally, well-tested, reusable UI components and improved docs will help API Dash scale better, attract more contributors, and evolve as a leading open-source dev tool. + + +## 📦 Deliverables + +### Phase 1 (Before Midterm Evaluation) +- ✅ **UI Parsing Engine (PoC):** Initial parser that analyzes API responses (JSON/XML) and extracts structure. +- ✅ **Component Generator v1:** Basic generation of UI components like tables, key-value cards, and simple forms. +- ✅ **Live Preview Panel:** Preview generated UI in real-time as API response is received. +- ✅ **Customization Panel (Basic):** Allow user to toggle between different UI layouts (table, card, raw JSON view). +- ✅ **Docs & Tests:** Documentation for codebase and test coverage for core modules. + +### Phase 2 (Post Midterm to Final Evaluation) +- ✅ **Advanced Component Generator:** Support nested data, arrays, empty states, pagination, filters, and dynamic layout resizing. +- ✅ **Export Feature:** One-click export to Dart (Flutter) or HTML code snippets for integration in user apps. +- ✅ **Theming & Styling Options:** Let users configure color schemes, borders, spacing, and responsive breakpoints. +- ✅ **Schema-aware Enhancements:** Support for OpenAPI/Swagger schema to pre-generate interface and validation hints. +- ✅ **Accessibility Improvements:** Keyboard navigation and screen-reader support for generated UIs. +- ✅ **Community UI Library (optional):** Start a curated set of reusable UI templates contributed by users. + + +## 📆 Timeline + +### Community Bonding (May 20 – June 16) +- Set up development environment and finalize UI generation strategy. +- Connect with mentors to discuss API patterns and styling preferences. +- Plan component architecture and UI preview interface. + +--- + +### Phase 1: (June 17 – July 15) + +**Week 1–2** +- Develop core parser for API responses (JSON/XML). +- Build prototype UI Generator v1 (tables, cards, basic form fields). + +**Week 3–4** +- Add live preview panel for dynamic UI rendering. +- Begin customization options (toggle views: table, card, raw). + +**Week 5** +- Basic test suite for generator module. +- Write clear documentation for PoC and parser. + +🧪 **Midterm Evaluation** + +--- + +### Phase 2: (July 15 – August 12) + +**Week 6–7** +- Improve component generation (nested JSON, arrays, filters, pagination). +- Integrate theming/styling options panel (light/dark mode, spacing, etc). + +**Week 8–9** +- Add export options (Flutter/Dart code, HTML snippets). +- Optimize layout rendering for responsiveness and mobile. + +**Week 10–11** +- Add accessibility features (keyboard support, screen reader labels). +- Add schema-aware UI hints using OpenAPI/Swagger files. + +**Week 12** +- Final polish + documentation. +- Submit screencast demo + final report. + +🎓 **Final Evaluation** + + + +## 🔭 Future Work & Stretch Goals + +- **OpenAPI Integration**: Add deeper support for parsing OpenAPI specs to auto-generate full UI forms and authentication flows. +- **Community Templates**: Let users contribute and share UI templates for common APIs (e.g., weather, finance, AI). +- **AI Enhancements**: Train a basic model to suggest UI layouts based on API response types. +- **Plugin Support**: Modularize the generator so new components (e.g., chart, maps) can be added easily by the community. +- **Real-time Collaboration**: Explore syncing API workspace editing in real-time (like a Google Docs for APIs). + +These goals can be taken up post-GSoC as future enhancements or extended during GSoC depending on the project pace. + + + + +## 🛠 Technical Approach + +The core goal is to build an intelligent system that takes raw API responses (JSON/XML) and transforms them into dynamic UI components, with customization and export support. + +### 1. **Parsing API Responses** +- Analyze structure of JSON/XML data (using `json_serializable`, `xml`, or similar Dart packages). +- Identify key-value pairs, arrays, nested objects to determine appropriate UI widgets. + +### 2. **Component Mapping Engine** +- Build a mapping system that links data types → UI components: + - String → TextField + - Boolean → Toggle/Switch + - List → Dropdown + - Object → Card or Nested Form +- Handle nested data recursively. + +### 3. **Dynamic UI Generation** +- Generate Flutter widgets using a tree structure. +- Allow instant preview and live editing (padding, styling, layout). +- Scaffold reusable components: `DataCard`, `AutoForm`, `APIGrid`, etc. + +### 4. **Customization Support** +- Allow users to tweak: + - Layout: vertical/horizontal/grid + - Visibility toggles + - Labels, styles, colors +- Provide real-time preview and export to Dart code. + +### 5. **Export Functionality** +- Convert the generated UI into clean, production-ready Flutter code. +- Package as a `.dart` file, downloadable or savable to project workspace. + +### 6. **Optional AI Assist** +- Use basic ML heuristics or an LLM API to recommend layout structures or group similar fields (as a stretch goal). + + + + + +## 📦 Deliverables (Milestone Breakdown) + +### 🔹 Milestone 1: Core Parsing + Component Mapping (Weeks 1–2) +- Build a robust parser to handle JSON/XML structures. +- Map data types to UI widgets (string → input, bool → toggle, etc). +- Scaffold reusable component structure (`DataCard`, `AutoForm`, etc). + +### 🔹 Milestone 2: UI Generator v1 + Preview Panel (Weeks 3–4) +- Generate dynamic UI based on parsed response. +- Render a real-time preview of the generated UI. +- Add layout toggles (card, table, raw view). +- Begin work on customization options. + +### 🔹 Milestone 3: Customization + Code Export (Weeks 5–7) +- Add theming and layout customization (color, grid/flex, spacing). +- Build export-to-Flutter button with clean Dart code output. +- Improve handling of nested objects and array rendering. + +### 🔹 Milestone 4: Advanced UX + Accessibility (Weeks 8–10) +- Add support for pagination, filters, form validation. +- Integrate accessibility features (ARIA labels, keyboard navigation). +- Add schema-aware layout hints (OpenAPI preview if time permits). + +### 🔹 Milestone 5: Polish, Docs & Community Readiness (Week 11–12) +- Write developer and user documentation. +- Record final demo screencast of the end-to-end workflow. +- Submit final report and guide for future contributors. + + +## 👋 About Me + +I’m Shree Meher, a Computer Science student with a strong interest in frontend development, UI/UX, and open-source collaboration. I enjoy building tools that make technical work more visual, accessible, and intuitive. + +I discovered API Dash while exploring lightweight alternatives to heavy API clients, and the project immediately resonated with me — especially the emphasis on clarity, customization, and minimalism. This project is a perfect fit for my skills in JavaScript, Python, and JSON parsing, while giving me the opportunity to dive deeper into Flutter and Dart. + +I’m currently learning Flutter and contributing to open-source platforms like CircuitVerse and API Dash. Through GSoC, I hope to make meaningful contributions, grow through mentorship, and help create tools that empower developers everywhere. + + + + +## 📅 Commitment + +- I will be fully available for the entire duration of GSoC 2025. +- I can dedicate **18–22 hours per week**, and more if needed. +- I do not have any other academic, job, or internship commitments during this time. +- I'm based in IST (UTC+5:30) and flexible with mentor availability and sync-ups. + +I’m committed to collaborating closely with mentors, incorporating feedback quickly, and delivering clean, maintainable code aligned with API Dash’s long-term vision. + + From 58cfa145e70d4b52835f3a50f650580bb6463e8b Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Fri, 4 Apr 2025 21:21:54 +0530 Subject: [PATCH 046/188] Gsoc Proposal application_AmanGupta_dahbot.md For review --- .../2025/gsoc/application_AmanGupta_dahbot.md | 275 ++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 doc/proposals/2025/gsoc/application_AmanGupta_dahbot.md diff --git a/doc/proposals/2025/gsoc/application_AmanGupta_dahbot.md b/doc/proposals/2025/gsoc/application_AmanGupta_dahbot.md new file mode 100644 index 000000000..70b6051aa --- /dev/null +++ b/doc/proposals/2025/gsoc/application_AmanGupta_dahbot.md @@ -0,0 +1,275 @@ +# API Dash Intelligent Assistant (DashBot) + +### **Proposal by Aman Gupta, IIT Mandi(175005)** +**Email:** aman07112006@gmail.com +**Phone:** +91-7597759385 +**GitHub:** [Click Here](https://github.com/Aman071106) +**LinkedIn:** [Click Here](https://www.linkedin.com/in/aman-gupta-730694323/) +**Discord Handle:** amanguptacd_46920 +#### **Institute:** IIT- Mandi(B.Tech in Computer Science and Engineering) +**Resume Link**: [Click Here](https://github.com/Aman071106) +--- + +## 1. Executive Summary + +**DashBot Pro** represents a paradigm shift in API development ecosystems by seamlessly integrating an advanced AI-powered assistant directly into API Dash. This intelligent system goes beyond traditional development tools by providing contextual understanding, proactive debugging, automated documentation, and intelligent code generation—all while continuously learning from developer interactions to deliver increasingly personalized support. + +By harnessing cutting-edge AI agent architecture and machine learning pipelines, DashBot Pro will transform API Dash into a comprehensive development companion that anticipates needs, solves complex issues, and accelerates development workflows across the entire API lifecycle. + +--- + +## 2. Technical Expertise & Qualifications + +### Core Technical Competencies: + +| Domain | Technologies & Skills | +|--------|----------------------| +| **Full-Stack Development** | Flutter, Kotlin, Node.js, Express, TypeScript | +| **Blockchain & Smart Contracts** | Solidity| +| **Cloud & Backend** | Firebase, MongoDB, PostGreSQL, mySQL, AWS| +| **Data Science & ML** | Python, NumPy, Pandas, Scikit-learn | +| **AI/NLP & Intelligent Agents** | LangChain, Transformers, NLTK, spaCy, GPT integration, Pathway, FetchAI | +| **API Development** | REST, OpenAPI, OAuth | +| **Competitive Programming and DSA** | C++, Python, Advanced DSA | + +### Relavant and Notable Achievements and Projects: + +- **🏆 Third Place, Fetch.ai AI Agent Hackathon (2025)**: Developed an autonomous rag powered finance assitant in 48 hours showcasing exceptional ai agents and api handling +- **🚀 Developer in Institute App-Vertex**: Integrated external APIs with Flutter app + + +--- + +## 3. Project Vision & Innovative Approach + +### Transformative Impact: + +DashBot Pro will revolutionize the API development experience by: + +1. **Reducing Debug Time** through intelligent pattern recognition and proactive error detection +2. **Automation of Documentation Tasks** via contextual understanding and dynamic example generation +3. **Accelerating Integration Speed** through framework-specific code generation with best practices +4. **Enabling Data-Driven API Design** through visual insights and interactive flow diagrams +5. **Providing Continuous Learning** that adapts to individual developer styles and preferences + +### Conceptual Innovation: + +The core innovation lies in DashBot Pro's **Multi-Agent Collective Intelligence System**. Unlike traditional assistants that use a single large model, DashBot Pro employs specialized AI agents that collaborate to handle specific aspects of API development—from semantic understanding to code generation—creating a comprehensive, adaptive system greater than the sum of its parts. + +--- + +## 4. Technical Architecture & System Design + +### Core AI Agent Ecosystem: + +![DashBot Pro AI Agent Ecosystem](https://raw.githubusercontent.com/Aman071106/Issues/main/system-architecture-diagram.svg) + +DashBot Pro is built on a multi-agent architecture where specialized agents collaborate to deliver comprehensive API assistance: + +1. **Perception Agent**: Analyzes API requests/responses, extracting semantic meaning and identifying patterns +2. **Reasoning Agent**: Processes information from the Perception Agent to draw conclusions and formulate responses +3. **Memory Agent**: Maintains contextual information about past interactions and API behaviors +4. **Action Agent**: Executes specific tasks based on reasoning outputs (code generation, documentation, etc.) +5. **Learning Agent**: Continuously improves system performance by analyzing interaction outcomes + +### Architectural Components: + +#### 1. **Cognitive Understanding Engine** +- **Semantic Parser**: Extracts intent and entities from API requests and responses +- **Context Manager**: Maintains session state and historical knowledge +- **Intent Classifier**: Determines the developer's goals (debug, document, test, etc.) +- **Entity Recognition**: Identifies API endpoints, parameters, and response patterns + +#### 2. **Advanced Debugging System** +- **Pattern Recognition Module**: Identifies common error signatures across similar APIs +- **Root Cause Analyzer**: Traces errors to their source through dependency chains +- **Solution Generator**: Provides targeted fixes based on error patterns and context +- **Learning Feedback Loop**: Improves solutions based on resolution success rates + +#### 3. **Intelligent Documentation Engine** +- **Schema Extractor**: Automatically derives API structures from usage patterns +- **Example Generator**: Creates contextually relevant request/response examples +- **Markdown Processor**: Produces clean, structured documentation +- **Interactive Visualizer**: Generates dynamic API maps and relationship diagrams + +#### 4. **Smart Code Generation System** +- **Framework Detector**: Identifies target frameworks and languages +- **Code Template Engine**: Maintains optimized patterns for different ecosystems +- **Best Practices Enforcer**: Ensures generated code follows security and performance guidelines +- **Adaptation Module**: Learns from developer code modifications to improve future generations + +#### 5. **Visual Analytics Dashboard** +- **Performance Metrics Engine**: Tracks API latency, error rates, and usage patterns +- **Network Flow Visualizer**: Creates interactive diagrams of API dependencies +- **Anomaly Detection**: Highlights unusual patterns or potential issues +- **Trend Analysis**: Provides insights on API usage evolution over time + +--- + +## 5. Technical Workflow & Component Interaction + +### System Interaction Flow: + +![DashBot Pro System Interaction Flow ](https://raw.githubusercontent.com/Aman071106/Issues/refs/heads/main/worflow.svg) + +The diagram above illustrates the sophisticated workflow of DashBot Pro: + +1. **Input Processing Stage**: + - API requests/responses are intercepted by the system + - The Perception Agent processes raw data to extract meaningful patterns + - Context is established by the Memory Agent for consistent understanding + +2. **Analysis & Decision Stage**: + - The Reasoning Agent evaluates the processed information + - Pattern matching against known API behaviors and error signatures + - Decision trees determine optimal response strategies + +3. **Execution Stage**: + - The Action Agent implements the selected strategy + - Tasks are distributed to specialized modules (debugging, documentation, etc.) + - Results are formatted for developer consumption + +4. **Learning Stage**: + - The Learning Agent records interaction outcomes + - Feedback loops improve future performance + - Model weights are adjusted based on success metrics + +### Agent Communication Protocol: + +![DashBot Pro Agent Communication Protocol](https://raw.githubusercontent.com/Aman071106/Issues/refs/heads/main/agentCommunicationProtocol.svg) + +The multi-agent system employs a sophisticated communication protocol: + +1. **Message Passing System**: Structured JSON objects enable seamless agent-to-agent communication +2. **Priority Queue**: Ensures critical tasks (like error resolution) receive immediate attention +3. **Shared Memory Space**: Maintains global context accessible to all agents +4. **Task Delegation Framework**: Dynamically assigns responsibilities based on agent specialization +5. **Conflict Resolution Mechanism**: Mediates when agents produce contradictory outputs + +--- + +## 6. Technology Stack & Implementation Details + +### Core Technologies: + +| Category | Technologies | +|----------|-------------| +| **AI/ML Foundation** | PyTorch, TensorFlow, Hugging Face Transformers, LangChain | +| **Backend Infrastructure** | Node.js, FastAPI| +| **Frontend Integration** | Flutter | +| **API Processing** | OpenAPI Tools, GraphQL Codegen, REST Clients | +| **DevOps & Monitoring** | GitHub Actions | +| **Testing Framework** | Jest, Pytest, Cypress, k6 | + +### Implementation Architecture: + +The system implements a modular architecture with clear separation of concerns: + +1. **Core Engine Layer**: + - AI model orchestration + - Agent communication protocols + - State management system + +2. **Service Layer**: + - API analysis services + - Documentation generation + - Code synthesis modules + - Debugging pipelines + +3. **Integration Layer**: + - API Dash plugin system + - Event listeners and hooks + - UI/UX components + - Data visualization systems + +4. **Infrastructure Layer**: + - Model serving infrastructure + - Data persistence mechanisms + - Caching and performance optimization + - Security and access control + +--- + +## 7. Enhanced Project Timeline (12 Weeks, 175 Hours) + +| Week | Focus Area | Key Deliverables | Technical Goals | +|------|------------|------------------|-----------------| +| 1 | **Project Foundation** | - Repository structure
- CI/CD pipeline
- Development environment | - Establish architecture fundamentals
- Configure AI agent frameworks
- Set up data collection pipeline | +| 2 | **Cognitive Engine (Alpha)** | - Base semantic parser
- Initial context management
- Agent communication protocol | - Implement entity recognition for API components
- Create basic intent classification system
- Establish multi-agent message passing | +| 3 | **Debugging System (Alpha)** | - Error pattern recognition
- Basic root cause analysis
- Solution template engine | - Train initial error classification models
- Implement dependency chain analysis
- Develop solution retrieval system | +| 4 | **Debugging System (Beta)** | - Advanced pattern detection
- Interactive debugging flows
- Solution effectiveness tracking | - Enhance error signature database
- Implement correction suggestion engine
- Create feedback collection system | +| 5 | **Documentation Engine (Alpha)** | - Schema extraction system
- Basic example generation
- Markdown rendering | - Develop API structure analyzer
- Create documentation templates
- Implement content organization system | +| 6 | **Documentation Engine (Beta)** | - Interactive diagram generation
- Context-aware examples
- Documentation versioning | - Integrate graph visualization library
- Enhance example relevance algorithms
- Implement documentation comparison tools | +| 7 | **Visualization System** | - Performance metrics dashboard
- Network flow diagrams
- Anomaly detection | - Develop metrics collection system
- Create interactive visualization components
- Implement statistical anomaly detection | +| 8 | **Code Generation (Alpha)** | - Framework detection
- Base template system
- Language-specific generators | - Create language detection models
- Develop code template framework
- Implement syntax validation | +| 9 | **Code Generation (Beta)** | - Multi-framework support
- Best practices enforcement
- Code optimization | - Expand framework-specific libraries
- Implement security validation rules
- Create performance optimization suggestions | +| 10 | **AI/NLP Enhancement** | - Advanced NLP processing
- Context refinement
- Learning system integration | - Fine-tune language models on API datasets
- Enhance contextual understanding
- Implement reinforcement learning from feedback | +| 11 | **Performance Optimization** | - System benchmarking
- Latency reduction
- Resource utilization | - Implement model quantization
- Optimize inference pipelines
- Create caching strategies | +| 12 | **Final Integration** | - Comprehensive testing
- Documentation
- Deployment package | - Perform end-to-end testing
- Create user documentation
- Prepare release package | + +### Weekly Check-in Process: +- **Code Review**: Monday submissions for mentor feedback +- **Progress Reports**: Wednesday updates with metrics and blockers +- **Planning Sessions**: Friday meetings for next week's objectives + +--- + +## 8. Evaluation Metrics & Success Criteria + +### Quantitative Metrics: + +| Dimension | Target Metrics | +|-----------|---------------| +| **Performance** | - Response time < 500ms for 90% of queries
- Memory usage < 250MB for primary components
- Support for processing 100+ API endpoints simultaneously | +| **Accuracy** | - >85% correct error diagnosis
- >90% documentation completeness
- >95% syntactically correct code generation | +| **Usability** | - <5 minutes setup time
- >80% positive user feedback
- >70% reduction in documentation time | +| **Learning** | - >15% improvement in accuracy after 1000 interactions
- Adaptation to user-specific patterns within 20 uses | + +### Qualitative Goals: + +1. **Intuitive Integration**: Seamless workflow that feels like a natural extension of API Dash +2. **Developer Trust**: Build confidence through accurate suggestions and transparent reasoning +3. **Knowledge Transfer**: Enable learning through contextual explanations, not just solutions +4. **Workflow Enhancement**: Create a development experience that anticipates needs and eliminates friction + +--- + +## 9. Beyond GSoC: Future Roadmap + +### Post-GSoC Development: + +1. **Enterprise Feature Expansion**: + - Team collaboration features + - Custom model training for organization-specific APIs + - Advanced security validation tools + +2. **Ecosystem Integration**: + - CI/CD pipeline integration + - IDE plugins (VS Code, IntelliJ) + - Slack/Discord/Teams integrations + +3. **Advanced Capabilities**: + - API evolution recommendations + - Automated performance optimization + - Security vulnerability scanning + +### Community Development Plan: + +- **Open Source Model Contributions**: Releasing specialized API-focused models +- **Developer Education**: Creating comprehensive guides for API best practices +- **Contributor Program**: Establishing framework for community extensions + +--- + +## 10. Conclusion & Personal Commitment + +**DashBot Pro** represents more than just a technical solution—it's a fundamental rethinking of how developers interact with APIs. By creating an intelligent ecosystem of AI agents that work together to understand, debug, document, and generate code, this project promises to transform API development from a technical challenge into a creative partnership between developer and machine. + +As the architect of this vision, I bring not only technical expertise across the full development stack but also a proven track record in AI agent development, competitive algorithm design, and real-world API integration. My experience winning the Fetch.ai AI Agent Hackathon demonstrates my ability to create sophisticated agent systems that deliver practical value. + +I am fully committed to making DashBot Pro a flagship project for GSoC 2025, with the goal of creating a tool that becomes an indispensable part of the modern developer's toolkit. Beyond the technical implementation, I am deeply passionate about improving developer experience and eliminating the friction points that slow innovation. +I can contribute around 15-18 hours per week. + +With your support, DashBot Pro will become a transformative addition to the API Dash ecosystem and a showcase of what's possible when cutting-edge AI meets practical development needs. + +--- From 80f6c4298032bc4d9a13dfb097fc031c7fabda9d Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 5 Apr 2025 05:17:48 +0530 Subject: [PATCH 047/188] Update application_AmanGupta_dahbot.md --- doc/proposals/2025/gsoc/application_AmanGupta_dahbot.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/proposals/2025/gsoc/application_AmanGupta_dahbot.md b/doc/proposals/2025/gsoc/application_AmanGupta_dahbot.md index 70b6051aa..078acaaef 100644 --- a/doc/proposals/2025/gsoc/application_AmanGupta_dahbot.md +++ b/doc/proposals/2025/gsoc/application_AmanGupta_dahbot.md @@ -62,7 +62,7 @@ The core innovation lies in DashBot Pro's **Multi-Agent Collective Intelligence ### Core AI Agent Ecosystem: -![DashBot Pro AI Agent Ecosystem](https://raw.githubusercontent.com/Aman071106/Issues/main/system-architecture-diagram.svg) +![DashBot Pro AI Agent Ecosystem](images/system-architecture-diagram.svg) DashBot Pro is built on a multi-agent architecture where specialized agents collaborate to deliver comprehensive API assistance: @@ -110,7 +110,7 @@ DashBot Pro is built on a multi-agent architecture where specialized agents coll ### System Interaction Flow: -![DashBot Pro System Interaction Flow ](https://raw.githubusercontent.com/Aman071106/Issues/refs/heads/main/worflow.svg) +![DashBot Pro System Interaction Flow ](images/worflow.svg) The diagram above illustrates the sophisticated workflow of DashBot Pro: @@ -136,7 +136,7 @@ The diagram above illustrates the sophisticated workflow of DashBot Pro: ### Agent Communication Protocol: -![DashBot Pro Agent Communication Protocol](https://raw.githubusercontent.com/Aman071106/Issues/refs/heads/main/agentCommunicationProtocol.svg) +![DashBot Pro Agent Communication Protocol](images/agentCommunicationProtocol.svg) The multi-agent system employs a sophisticated communication protocol: From b2c833fbf023b9ce75a0cb241313022f130ac37d Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 5 Apr 2025 05:18:04 +0530 Subject: [PATCH 048/188] Rename application_AmanGupta_dahbot.md to application_AmanGupta_dashbot.md --- .../2025/gsoc/application_AmanGupta_dahbot.md | 275 ------------------ 1 file changed, 275 deletions(-) delete mode 100644 doc/proposals/2025/gsoc/application_AmanGupta_dahbot.md diff --git a/doc/proposals/2025/gsoc/application_AmanGupta_dahbot.md b/doc/proposals/2025/gsoc/application_AmanGupta_dahbot.md deleted file mode 100644 index 078acaaef..000000000 --- a/doc/proposals/2025/gsoc/application_AmanGupta_dahbot.md +++ /dev/null @@ -1,275 +0,0 @@ -# API Dash Intelligent Assistant (DashBot) - -### **Proposal by Aman Gupta, IIT Mandi(175005)** -**Email:** aman07112006@gmail.com -**Phone:** +91-7597759385 -**GitHub:** [Click Here](https://github.com/Aman071106) -**LinkedIn:** [Click Here](https://www.linkedin.com/in/aman-gupta-730694323/) -**Discord Handle:** amanguptacd_46920 -#### **Institute:** IIT- Mandi(B.Tech in Computer Science and Engineering) -**Resume Link**: [Click Here](https://github.com/Aman071106) ---- - -## 1. Executive Summary - -**DashBot Pro** represents a paradigm shift in API development ecosystems by seamlessly integrating an advanced AI-powered assistant directly into API Dash. This intelligent system goes beyond traditional development tools by providing contextual understanding, proactive debugging, automated documentation, and intelligent code generation—all while continuously learning from developer interactions to deliver increasingly personalized support. - -By harnessing cutting-edge AI agent architecture and machine learning pipelines, DashBot Pro will transform API Dash into a comprehensive development companion that anticipates needs, solves complex issues, and accelerates development workflows across the entire API lifecycle. - ---- - -## 2. Technical Expertise & Qualifications - -### Core Technical Competencies: - -| Domain | Technologies & Skills | -|--------|----------------------| -| **Full-Stack Development** | Flutter, Kotlin, Node.js, Express, TypeScript | -| **Blockchain & Smart Contracts** | Solidity| -| **Cloud & Backend** | Firebase, MongoDB, PostGreSQL, mySQL, AWS| -| **Data Science & ML** | Python, NumPy, Pandas, Scikit-learn | -| **AI/NLP & Intelligent Agents** | LangChain, Transformers, NLTK, spaCy, GPT integration, Pathway, FetchAI | -| **API Development** | REST, OpenAPI, OAuth | -| **Competitive Programming and DSA** | C++, Python, Advanced DSA | - -### Relavant and Notable Achievements and Projects: - -- **🏆 Third Place, Fetch.ai AI Agent Hackathon (2025)**: Developed an autonomous rag powered finance assitant in 48 hours showcasing exceptional ai agents and api handling -- **🚀 Developer in Institute App-Vertex**: Integrated external APIs with Flutter app - - ---- - -## 3. Project Vision & Innovative Approach - -### Transformative Impact: - -DashBot Pro will revolutionize the API development experience by: - -1. **Reducing Debug Time** through intelligent pattern recognition and proactive error detection -2. **Automation of Documentation Tasks** via contextual understanding and dynamic example generation -3. **Accelerating Integration Speed** through framework-specific code generation with best practices -4. **Enabling Data-Driven API Design** through visual insights and interactive flow diagrams -5. **Providing Continuous Learning** that adapts to individual developer styles and preferences - -### Conceptual Innovation: - -The core innovation lies in DashBot Pro's **Multi-Agent Collective Intelligence System**. Unlike traditional assistants that use a single large model, DashBot Pro employs specialized AI agents that collaborate to handle specific aspects of API development—from semantic understanding to code generation—creating a comprehensive, adaptive system greater than the sum of its parts. - ---- - -## 4. Technical Architecture & System Design - -### Core AI Agent Ecosystem: - -![DashBot Pro AI Agent Ecosystem](images/system-architecture-diagram.svg) - -DashBot Pro is built on a multi-agent architecture where specialized agents collaborate to deliver comprehensive API assistance: - -1. **Perception Agent**: Analyzes API requests/responses, extracting semantic meaning and identifying patterns -2. **Reasoning Agent**: Processes information from the Perception Agent to draw conclusions and formulate responses -3. **Memory Agent**: Maintains contextual information about past interactions and API behaviors -4. **Action Agent**: Executes specific tasks based on reasoning outputs (code generation, documentation, etc.) -5. **Learning Agent**: Continuously improves system performance by analyzing interaction outcomes - -### Architectural Components: - -#### 1. **Cognitive Understanding Engine** -- **Semantic Parser**: Extracts intent and entities from API requests and responses -- **Context Manager**: Maintains session state and historical knowledge -- **Intent Classifier**: Determines the developer's goals (debug, document, test, etc.) -- **Entity Recognition**: Identifies API endpoints, parameters, and response patterns - -#### 2. **Advanced Debugging System** -- **Pattern Recognition Module**: Identifies common error signatures across similar APIs -- **Root Cause Analyzer**: Traces errors to their source through dependency chains -- **Solution Generator**: Provides targeted fixes based on error patterns and context -- **Learning Feedback Loop**: Improves solutions based on resolution success rates - -#### 3. **Intelligent Documentation Engine** -- **Schema Extractor**: Automatically derives API structures from usage patterns -- **Example Generator**: Creates contextually relevant request/response examples -- **Markdown Processor**: Produces clean, structured documentation -- **Interactive Visualizer**: Generates dynamic API maps and relationship diagrams - -#### 4. **Smart Code Generation System** -- **Framework Detector**: Identifies target frameworks and languages -- **Code Template Engine**: Maintains optimized patterns for different ecosystems -- **Best Practices Enforcer**: Ensures generated code follows security and performance guidelines -- **Adaptation Module**: Learns from developer code modifications to improve future generations - -#### 5. **Visual Analytics Dashboard** -- **Performance Metrics Engine**: Tracks API latency, error rates, and usage patterns -- **Network Flow Visualizer**: Creates interactive diagrams of API dependencies -- **Anomaly Detection**: Highlights unusual patterns or potential issues -- **Trend Analysis**: Provides insights on API usage evolution over time - ---- - -## 5. Technical Workflow & Component Interaction - -### System Interaction Flow: - -![DashBot Pro System Interaction Flow ](images/worflow.svg) - -The diagram above illustrates the sophisticated workflow of DashBot Pro: - -1. **Input Processing Stage**: - - API requests/responses are intercepted by the system - - The Perception Agent processes raw data to extract meaningful patterns - - Context is established by the Memory Agent for consistent understanding - -2. **Analysis & Decision Stage**: - - The Reasoning Agent evaluates the processed information - - Pattern matching against known API behaviors and error signatures - - Decision trees determine optimal response strategies - -3. **Execution Stage**: - - The Action Agent implements the selected strategy - - Tasks are distributed to specialized modules (debugging, documentation, etc.) - - Results are formatted for developer consumption - -4. **Learning Stage**: - - The Learning Agent records interaction outcomes - - Feedback loops improve future performance - - Model weights are adjusted based on success metrics - -### Agent Communication Protocol: - -![DashBot Pro Agent Communication Protocol](images/agentCommunicationProtocol.svg) - -The multi-agent system employs a sophisticated communication protocol: - -1. **Message Passing System**: Structured JSON objects enable seamless agent-to-agent communication -2. **Priority Queue**: Ensures critical tasks (like error resolution) receive immediate attention -3. **Shared Memory Space**: Maintains global context accessible to all agents -4. **Task Delegation Framework**: Dynamically assigns responsibilities based on agent specialization -5. **Conflict Resolution Mechanism**: Mediates when agents produce contradictory outputs - ---- - -## 6. Technology Stack & Implementation Details - -### Core Technologies: - -| Category | Technologies | -|----------|-------------| -| **AI/ML Foundation** | PyTorch, TensorFlow, Hugging Face Transformers, LangChain | -| **Backend Infrastructure** | Node.js, FastAPI| -| **Frontend Integration** | Flutter | -| **API Processing** | OpenAPI Tools, GraphQL Codegen, REST Clients | -| **DevOps & Monitoring** | GitHub Actions | -| **Testing Framework** | Jest, Pytest, Cypress, k6 | - -### Implementation Architecture: - -The system implements a modular architecture with clear separation of concerns: - -1. **Core Engine Layer**: - - AI model orchestration - - Agent communication protocols - - State management system - -2. **Service Layer**: - - API analysis services - - Documentation generation - - Code synthesis modules - - Debugging pipelines - -3. **Integration Layer**: - - API Dash plugin system - - Event listeners and hooks - - UI/UX components - - Data visualization systems - -4. **Infrastructure Layer**: - - Model serving infrastructure - - Data persistence mechanisms - - Caching and performance optimization - - Security and access control - ---- - -## 7. Enhanced Project Timeline (12 Weeks, 175 Hours) - -| Week | Focus Area | Key Deliverables | Technical Goals | -|------|------------|------------------|-----------------| -| 1 | **Project Foundation** | - Repository structure
- CI/CD pipeline
- Development environment | - Establish architecture fundamentals
- Configure AI agent frameworks
- Set up data collection pipeline | -| 2 | **Cognitive Engine (Alpha)** | - Base semantic parser
- Initial context management
- Agent communication protocol | - Implement entity recognition for API components
- Create basic intent classification system
- Establish multi-agent message passing | -| 3 | **Debugging System (Alpha)** | - Error pattern recognition
- Basic root cause analysis
- Solution template engine | - Train initial error classification models
- Implement dependency chain analysis
- Develop solution retrieval system | -| 4 | **Debugging System (Beta)** | - Advanced pattern detection
- Interactive debugging flows
- Solution effectiveness tracking | - Enhance error signature database
- Implement correction suggestion engine
- Create feedback collection system | -| 5 | **Documentation Engine (Alpha)** | - Schema extraction system
- Basic example generation
- Markdown rendering | - Develop API structure analyzer
- Create documentation templates
- Implement content organization system | -| 6 | **Documentation Engine (Beta)** | - Interactive diagram generation
- Context-aware examples
- Documentation versioning | - Integrate graph visualization library
- Enhance example relevance algorithms
- Implement documentation comparison tools | -| 7 | **Visualization System** | - Performance metrics dashboard
- Network flow diagrams
- Anomaly detection | - Develop metrics collection system
- Create interactive visualization components
- Implement statistical anomaly detection | -| 8 | **Code Generation (Alpha)** | - Framework detection
- Base template system
- Language-specific generators | - Create language detection models
- Develop code template framework
- Implement syntax validation | -| 9 | **Code Generation (Beta)** | - Multi-framework support
- Best practices enforcement
- Code optimization | - Expand framework-specific libraries
- Implement security validation rules
- Create performance optimization suggestions | -| 10 | **AI/NLP Enhancement** | - Advanced NLP processing
- Context refinement
- Learning system integration | - Fine-tune language models on API datasets
- Enhance contextual understanding
- Implement reinforcement learning from feedback | -| 11 | **Performance Optimization** | - System benchmarking
- Latency reduction
- Resource utilization | - Implement model quantization
- Optimize inference pipelines
- Create caching strategies | -| 12 | **Final Integration** | - Comprehensive testing
- Documentation
- Deployment package | - Perform end-to-end testing
- Create user documentation
- Prepare release package | - -### Weekly Check-in Process: -- **Code Review**: Monday submissions for mentor feedback -- **Progress Reports**: Wednesday updates with metrics and blockers -- **Planning Sessions**: Friday meetings for next week's objectives - ---- - -## 8. Evaluation Metrics & Success Criteria - -### Quantitative Metrics: - -| Dimension | Target Metrics | -|-----------|---------------| -| **Performance** | - Response time < 500ms for 90% of queries
- Memory usage < 250MB for primary components
- Support for processing 100+ API endpoints simultaneously | -| **Accuracy** | - >85% correct error diagnosis
- >90% documentation completeness
- >95% syntactically correct code generation | -| **Usability** | - <5 minutes setup time
- >80% positive user feedback
- >70% reduction in documentation time | -| **Learning** | - >15% improvement in accuracy after 1000 interactions
- Adaptation to user-specific patterns within 20 uses | - -### Qualitative Goals: - -1. **Intuitive Integration**: Seamless workflow that feels like a natural extension of API Dash -2. **Developer Trust**: Build confidence through accurate suggestions and transparent reasoning -3. **Knowledge Transfer**: Enable learning through contextual explanations, not just solutions -4. **Workflow Enhancement**: Create a development experience that anticipates needs and eliminates friction - ---- - -## 9. Beyond GSoC: Future Roadmap - -### Post-GSoC Development: - -1. **Enterprise Feature Expansion**: - - Team collaboration features - - Custom model training for organization-specific APIs - - Advanced security validation tools - -2. **Ecosystem Integration**: - - CI/CD pipeline integration - - IDE plugins (VS Code, IntelliJ) - - Slack/Discord/Teams integrations - -3. **Advanced Capabilities**: - - API evolution recommendations - - Automated performance optimization - - Security vulnerability scanning - -### Community Development Plan: - -- **Open Source Model Contributions**: Releasing specialized API-focused models -- **Developer Education**: Creating comprehensive guides for API best practices -- **Contributor Program**: Establishing framework for community extensions - ---- - -## 10. Conclusion & Personal Commitment - -**DashBot Pro** represents more than just a technical solution—it's a fundamental rethinking of how developers interact with APIs. By creating an intelligent ecosystem of AI agents that work together to understand, debug, document, and generate code, this project promises to transform API development from a technical challenge into a creative partnership between developer and machine. - -As the architect of this vision, I bring not only technical expertise across the full development stack but also a proven track record in AI agent development, competitive algorithm design, and real-world API integration. My experience winning the Fetch.ai AI Agent Hackathon demonstrates my ability to create sophisticated agent systems that deliver practical value. - -I am fully committed to making DashBot Pro a flagship project for GSoC 2025, with the goal of creating a tool that becomes an indispensable part of the modern developer's toolkit. Beyond the technical implementation, I am deeply passionate about improving developer experience and eliminating the friction points that slow innovation. -I can contribute around 15-18 hours per week. - -With your support, DashBot Pro will become a transformative addition to the API Dash ecosystem and a showcase of what's possible when cutting-edge AI meets practical development needs. - ---- From 1b32a4f675e5b34faa04e8aebc425c4f32afba6c Mon Sep 17 00:00:00 2001 From: Manas Hejmadi | Developer <67999871+synapsecode@users.noreply.github.com> Date: Sat, 5 Apr 2025 13:52:43 +0530 Subject: [PATCH 049/188] Minor Updates to Proposal --- .../gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md b/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md index 1a4310bdb..f6e80e816 100644 --- a/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md +++ b/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md @@ -479,15 +479,12 @@ An API Tool Generator basically is a system to automatically generate functions This functionality is particularly useful for developers working on AI-driven applications, as it simplifies integrating external APIs into agent workflows, enhancing efficiency and reducing development time. -Reference: [https://www.postman.com/explore/toolgen](https://www.postman.com/explore/toolgen) - ## Implementation Details Since this is an agent application itself, we can reuse the `APIDashAIAgent` abstract class mentioned above to implement our agents needed for this task. This highlights its modular and easy-to-use nature. `Supported Agentic Frameworks`: **OpenAI**, **LangChain**, Anthropic, **Gemini**, Mistral, Microsoft Autogen `Supported Programming Languages`: Python, JavaScript -*(same as Postman's Implementation)* ![](https://github.com/synapsecode/CustomStorage/raw/main/TOOLGENARCH.png) From 1b106e6d52162822bfebde95e6337bfb81f5c898 Mon Sep 17 00:00:00 2001 From: Manas Hejmadi Date: Sat, 5 Apr 2025 15:01:27 +0530 Subject: [PATCH 050/188] Added Local Images to Proposal + Timeline Changes --- ...n_Manas Hejmadi_AI UI Designer For APIs.md | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md b/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md index f6e80e816..588fa9145 100644 --- a/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md +++ b/doc/proposals/2025/gsoc/Application_Manas Hejmadi_AI UI Designer For APIs.md @@ -1,5 +1,5 @@ -![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/GSOCBANNER.jpg) +![](images/GSOCBANNER_APIDASH.jpg) # AI-Based API Response to Dynamic UI and Tool Generator @@ -71,7 +71,7 @@ ### Internal AI Service Architecture -![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/agentservice_arch.png) +![](images/agentservice_arch.png) Parts of the Centralised Internal AI Agent Service Architecture 1. **Input Layer:** the input to each agent will be a JSON/XML input along with an Agent Name. The Agent name will be used to specify which agent we want to call internally. @@ -260,7 +260,7 @@ final ans = await APIDashAIService.callAgent( ### Agent Implementations #### Agent: API_RESPONSE_ANALYZER -![](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/ARANA.png) +![](images/AI_RESP_ANA.png) - The API response is first parsed correctly according to its type (JSON / XML) and is then sanitised - The Agent then goes through the whole response and generates relevant semantic context. This context will be very helpful in determining what is actually needed in the final UI @@ -292,7 +292,7 @@ class ResponseAnalyzerAgent extends APIDashAIAgent { ``` #### Agent: STAC_GEN (SDUI Representation Generator) -![](https://github.com/synapsecode/CustomStorage/raw/main/stacgenimg.png) +![](images/STAC_GEN.png) - Using the internal schema and additional context, the SDUI Stac Generator Agent determines the most appropriate UI component for the data. For example, if the data structure is a `List`, it can generate a table and so on. This data is converted into a SDUI (Server Driven UI) representation called `Stac` from the [Stac](https://pub.dev/packages/stac) flutter package. - The agent can further refine this decision based on factors such as data type, layout, and design preferences. - The generated Stac code is basically a json representation of a flutter component and this can be used to create lightning-fast previews @@ -308,7 +308,7 @@ class StacGenBot extends APIDashAIAgent { #### Agent: STAC_MODIFIER (UI Customization using Natural Language) -![](https://github.com/synapsecode/CustomStorage/raw/main/stacmodifer.png) +![](images/STAC_MODIFIER.png) - The generated SDUI code can be previewed in a Component Preview Window, allowing users to inspect the result. - The Stac Modifier Agent is an optional feature that allows users to modify the generated SDUI using natural language prompts. This iterative process enables users to refine the UI to meet their exact needs. This is fairly easy to do as most LLMs can already do this very well. We just have to use good system prompting to get it right @@ -324,7 +324,7 @@ class StacModifierAgent extends APIDashAIAgent { } ``` #### Agent: STAC2CODE (Conversion from SDUI Code to Framework Code) -![](https://github.com/synapsecode/CustomStorage/raw/main/STAC2CODE.png) +![](images/STAC2CODE.png) - The generated SDUI code cannot be executed on the user's machine, hence we must convert this into actual flutter code. - This is fairly easy to do as Stac is almost a one-one representation of flutter code just in JSON - This property also makes it fairly easy to convert to other languages as the LLM can understand the context through the JSON SDUI and just convert it into another framework like NextJS code. This allows future extensibility. @@ -349,7 +349,7 @@ We will also be using a regular JSON based rendering approach as a fallback inca ## System Architecture & Flow -![image info](https://github.com/synapsecode/CustomStorage/raw/main/newmainarch.png) +![image info](images/AI_UI_DES_ARCH_MAIN.png) ### Sample API Response This is a sample response given when I hit a [DEMO API](https://reqres.in/api/users?page=0) @@ -457,14 +457,14 @@ Scaffold( ``` ### Sample Output (Flutter) -![image info](https://github.com/synapsecode/CustomStorage/raw/main/flutout.png) +![image info](images/FLUT_OUTPUT.png) ## Sample UI -![image info](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/framwdiag.png) -![image info](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/LDG.png) -![image info](https://raw.githubusercontent.com/synapsecode/CustomStorage/main/compo.png) +![image info](images/AI_UI_DES_SUI_1.png) +![image info](images/AI_UI_DES_SUI_2.png) +![image info](images/AI_UI_DES_SUI_3.png) If any modifications are made, it will go back to loading screen and come back to this page with the relevant modifications. When Export is clicked, the relevant generated code is **copied to clipboard** and the dialog closes! @@ -486,7 +486,7 @@ Since this is an agent application itself, we can reuse the `APIDashAIAgent` abs `Supported Agentic Frameworks`: **OpenAI**, **LangChain**, Anthropic, **Gemini**, Mistral, Microsoft Autogen `Supported Programming Languages`: Python, JavaScript -![](https://github.com/synapsecode/CustomStorage/raw/main/TOOLGENARCH.png) +![](images/TOOLGENARCH.png) - **Step 1: API Request Consolidation:** We accept the incoming API Request and add all of the relevant data (method, url, headers, auth etc) into a single text object named `REQDATA` this will be useful for the previous steps. - **Step 2: Tool Template Selector:** After doing some research, it appears that *API Tools* have a very limited number of templates. @@ -552,7 +552,7 @@ Since this is an agent application itself, we can reuse the `APIDashAIAgent` abs ## Week-wise Breakdown -![image_info](https://github.com/synapsecode/CustomStorage/raw/main/gsocgantt2.png) +![image_info](images/aiuides_timeline.png) ### Week 1 (Internal AI Service Development) - Discuss Future Agentic needs for Smarter code structuring @@ -612,7 +612,7 @@ Since this is an agent application itself, we can reuse the `APIDashAIAgent` abs **DELIVERABLE**: By the end of the week, we will have a new api dash client with the settings UI modified to include a `AI Engine` selector and a `LLM Api Key` field. Additionally we will have the UI implemented for the whole UI generation pipeline created and integrated with the described agents. -### Week 7 (Mobile Specific Changes & Additional Buffer) +### Week 7 (Mobile Specific Changes) - Since apidash works on mobile too, this week will be used to convert all the existing desktop-styled UI components into mobile friendly UI components - ollama is not accessible via localhost on a phone, hence on mobile devices we must include a textbox in the settings page so that the user can paste their hosted ollama instance link (they must also be able to select their model of choice) @@ -640,21 +640,16 @@ Since this is an agent application itself, we can reuse the `APIDashAIAgent` abs **DELIVERABLE**: A working module that accepts API specifications and user inputs, and outputs a unified REQDATA. -### Week 10 (Template Creator and Selector) +### Week 10 (Template Selector and FUNC_GEN, TOOL_GEN Implementation) - Design reusable templates for tool definitions across frameworks: OpenAI, Gemini, Mistral, Anthropic, LangChain, and Autogen. - Implement a simple rule-based tool selector which can select the relevant template based on `AGENTIC_FRAMEWORK` and `TARGET_LANGUAGE` - - **DELIVERABLE**: The Rule based Tool Selector that can reliably return the relevant template based on inputs - -### Week 11 (FUNC_GEN & TOOL_GEN Implementation) - - Creation of the **FUNC_GEN** Bot that can convert a given API Request into a piece of language-specific code which calls the API and returns its value - Creation of the **TOOL_GEN** Bot that can convert the selected Template and Generated Function into a fully functional API Tool - **DELIVERABLE**: Both of the bots in complete working condition with correct code output. + **DELIVERABLE**: A Rule based Tool Selector that can reliably return the relevant template based on inputs and the complete FUNC_GEN and TOOL_GEN working agents -### Week 12 (UI Integration & Tool Testing) +### Week 11 (UI Integration & Tool Testing) - Integrate this newly created pipeline with the existing apidash application - Test with multiple examples to confirm that both of the bots are working properly @@ -662,10 +657,10 @@ Since this is an agent application itself, we can reuse the `APIDashAIAgent` abs **DELIVERABLE**: The Entire Product fully working & Tested: AI UI Designer + API Tool Generator -### Week 13 (Report & Documentation) +### Week 12 (Report & Documentation) - The Documentation for the entire pipeline to be written in this period along with the final GSoC Report. - **DELIVERABLE**: GSoC Report + **DELIVERABLE**: Technical Documentation & GSoC Report --- From e50dbe2b93ebb54a723246949ed92757bccd89c6 Mon Sep 17 00:00:00 2001 From: Udhay-Adithya Date: Sat, 29 Mar 2025 02:09:45 +0530 Subject: [PATCH 051/188] add dashbot application --- .../gsoc/application_udhay_adithya_dashbot.md | 249 ++++-------------- 1 file changed, 54 insertions(+), 195 deletions(-) diff --git a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md index 5dcb43a0a..4fbccbb29 100644 --- a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md +++ b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md @@ -22,7 +22,7 @@ ### 1. Have you worked on or contributed to a FOSS project before? Can you attach repo links or relevant PRs? -I haven't contributed to open-source projects before this, so API Dash marks my first practical experience with open-source contribution. So far, I have made a total of four PRs—three of which have been merged, while the other one got rejected. Here are the relevant PRs: +I haven't contributed to open-source projects before this, so API Dash marks my first practical experience with open-source contribution. So far, I have made a total of four PRs—two of which have been merged, while the other two are currently under review. Here are the relevant PRs: ([#698](https://github.com/foss42/apidash/pull/698)): docs(codegen): add detailed instructions for ureq @@ -68,20 +68,15 @@ What excites me the most about API Dash is its aim to streamline the testing and ### 8. Can you mention some areas where the project can be improved? +DashBot's UI/UX can be significantly enhanced by implementing several modern features to improve user interaction and satisfaction. One key improvement is the introduction of a **Resizable and Draggable Chat Window**, allowing users to adjust the chat interface according to their preferences and screen space, creating a more personalized and efficient workspace. -DashBot's UI/UX can be significantly enhanced by implementing several modern features to improve user interaction and satisfaction. One key improvement is the introduction of a **Resizable and Draggable Chat Window**, allowing users to adjust the chat interface according to their preferences and screen space, creating a more personalized and efficient workspace. +**Integrating Voice Input** can also change the way of how users interact with DashBot. By enabling voice commands, users can perform tasks hands-free, which is particularly beneficial in scenarios where typing is inconvenient or impossible. This feature not only adds convenience but also makes DashBot more accessible to users with disabilities, aligning with inclusive design principles and expanding its utility in diverse environments. -**Integrating Voice Input** can also change the way users interact with DashBot. By enabling voice commands, users can perform tasks hands-free, which is particularly beneficial in scenarios where typing is inconvenient or impossible. This feature not only adds convenience but also makes DashBot more accessible to users with disabilities, aligning with inclusive design principles and expanding its utility in diverse environments. - -Additionally, a **Security & Compliance Advisor** can enhance DashBot’s capabilities by providing: -- **Vulnerability Scanning:** Automatically identifying security risks such as missing authentication, exposed sensitive data, and other vulnerabilities. -- **Auto-Remediation:** Offering actionable suggestions to fix issues like CORS misconfigurations or insecure headers. - -### **PROJECT TITLE : DashBot and API Authentication** +### **PROJECT TITLE : DashBot** ### **ABSTRACT:** -This proposal seeks to develop DashBot - the AI assistant for API Dash which supercharges developer productivity by helping developers automate tedious tasks, follow best practices, interact & obtain contextual suggestions, all via natural-language input. DashBot will be designed in a modular and extensible manner and provide the following list of features: +This proposal seeks to develop DashBot - the AI assistant for API Dash which supercharges developer productivity by helping developers automate tedious tasks, follow best practices, interact & obtain contextual suggestions, all via natural-language input. DashBot will be designed in a modular and extensible manner and provide the following list of features (suggestive, not exhaustive): - Explain responses & identify any discrepancy - Debug requests based on Status codes & Error messages @@ -93,8 +88,7 @@ This proposal seeks to develop DashBot - the AI assistant for API Dash which sup For each of the tasks you are benchmark evaluations will also be done so that it is easier for end users to choose the right backend LLM. Upon successful completion of this project, we will have -a fully function DashBot integrated with API Dash [#621](https://github.com/foss42/apidash/issues/621) and a new tab “Authorization” in the Home Page of API Dash to handle different API Authentication Methods. - +a fully function DashBot integrated with API Dash [#621](https://github.com/foss42/apidash/issues/621) ### **DETAILED DESCRIPTION** @@ -106,131 +100,36 @@ For core features like debugging API requests, generating documentation, and cre API response visualizations will be built using the `fl_chart` library, which supports customizable line, bar, pie, scatter, and radar charts. This integration will allow users to interactively analyze API data and identify trends. -**Benchmark evaluations** for each LLM provider will be conducted. Here are the key metrics that will be considered for benchmarking different models, - -- **Explain responses** - - Discrepancy Detection Rate: Does the LLM correctly identify discrepancies? - - Explainability Score: Are explanations understandable to developers? - - Edge Case Handling: Can it handle ambiguous or incomplete responses? - -- **Debug errors** - - Error Classification Accuracy: Does the LLM correctly identify the error source? - - Fix Implementation Rate: Are fixes actionable and contextually appropriate? - - Ambiguity Handling Test: Can it categorize errors (e.g., syntax vs. logic)? - -- **Generate Documentation** - - Coverage Score: Does the documentation cover all endpoints, parameters, and responses? - - Readability Assessment: Is the documentation clear and well-structured? - - Standard Compliance Check: Does it adhere to standards? - -- **Generate Tests** - - Test Coverage Report: Percentage of API endpoints and edge cases covered - - Test Execution Success Rate: Do tests pass/fail correctly when run? - - Adaptability Test: Are tests structured for easy updates? - -- **Generate Visualizations** - - Data Fidelity Check: Does the plot correctly represent the data? - - Aesthetic Scoring: Is the visualization clear and professional? - - Customization Flexibility Test: Can users tweak parameters? +The UI/UX will be optimized for developer productivity, featuring a responsive design, dynamic theming and real-time feedback. Benchmark evaluations for each LLM provider will be conducted using predefined datasets to compare accuracy, response time, and cost efficiency, empowering users to choose the optimal backend. -- **Generate Frontend Code** - - Code Execution Test: Does the code compile and function as expected? - - Best Practice Adherence: Does it follow best practices? - - Maintainability Score: Is the code clean and maintainable? - -### **Implementing other essential features:** - -Adding Support for various API Authentication Methods such as, -- **Basic authentication:** Sending a verified username and password with API request -- **API Auth:** Basic authentication [#610](https://github.com/foss42/apidash/issues/610) -- **API key:** Sending a key-value pair to the API either in the request headers or query parameters Add API Auth: API key [#611](https://github.com/foss42/apidash/issues/611) -- **Bearer token:** Authenticate using an access key, such as a JSON Web Token (JWT) Add API Auth: Bearer token [#612](https://github.com/foss42/apidash/issues/612) -- **JWT Bearer:** Generate JWT bearer tokens to authorize requests Add API Auth: JWT Bearer [#613](https://github.com/foss42/apidash/issues/613) -- **Digest Auth:** Client must send two requests. First request sent to the server receives a nonce value, which is then used to produce a one-time-use hash key to authenticate the request Add API Auth: Digest Auth [#614](https://github.com/foss42/apidash/issues/614) -- **OAuth 1.0** Add API Auth: OAuth 1.0 [#615](https://github.com/foss42/apidash/issues/615) -- **OAuth 2.0:** Implement OAuth 2.0 authentication [#481](https://github.com/foss42/apidash/issues/481) - -Required dependencies, +Key dependencies are, - [anthropic_sdk_dart](https://pub.dev/packages/anthropic_sdk_dart) - [googleai_dart](https://pub.dev/packages/googleai_dart/versions) - [openai_dart](https://pub.dev/packages/openai_dart) - [ollama_dart](https://pub.dev/packages/ollama_dart) - [flutter_riverpod](https://pub.dev/packages/flutter_riverpod) -- [fpdart](https://pub.dev/packages/fpdart) - [fl_charts](https://pub.dev/packages/fl_chart) -- [riverpod_annotation](https://pub.dev/packages/riverpod_annotation) -- [riverpod_lint](https://pub.dev/packages/riverpod_lint) -- [riverpod_generator](https://pub.dev/packages/riverpod_generator) -- [custom_lint](https://pub.dev/packages/custom_lint) -- [oauth1](https://pub.dev/packages/oauth1) -- [oauth2](https://pub.dev/packages/oauth2) - -## **FOLDER STRUCTURE** -``` -lib/ -│── features/ -│ ├── feature_1/ -│ │ ├── repository/ # Data layer for feature_1 -│ │ │ ├── feature_1_repository.dart -│ │ ├── viewmodel/ # Business logic for feature_1 -│ │ │ ├── feature_1_viewmodel.dart -│ │ ├── view/ # UI layer for feature_1 -│ │ │ ├── pages/ # Reusable widgets for this feature -│ │ │ │ ├── feature_1_screen.dart -│ │ │ ├── widgets/ # Reusable widgets for this feature -``` + ## **USAGE** DashBot can be accessed from the home screen of API Dash using a floating action button at the bottom right corner. +![api explorer](images/dashbot_on_screen.png) +

-
- DashBot Pop-up Window -
- -![DashBot On Screen](images/dashbot_on_screen.png) -
- DashBot Home Screen View -
- -![DashBot Settings](images/dashbot_settings_1.png) -
- DashBot Settings -
- -![DashBot Debug](images/dashbot_explain.png) -
- Access Dashbot Through Context Menu -
- -![DashBot Debug](images/dashbot_debug.png) -
- Debug Suggestion by DashBot -
- -![DashBot Debug](images/dashbot_debug_2.png) -
- Quick Fix API Errors -
- -![DashBot Debug](images/api_authentication.png) -
- Basic Authentication -
- -![DashBot Debug](images/api_authentication_1.png) -
- JWT Bearer Authentication -
+ **** +

+ More design files at Figma +

## **MILESTONES AND DELIVERABLES** @@ -240,35 +139,27 @@ I propose to divide the project into four milestones/deliverables to produce a s #### **Milestone #1: Architecture Setup & Multi-LLM Integration.** -This milestone will lay the foundation of `DashBot`, building an interactive, draggable and responsive interface for DashBot. +This milestone will lay the foundation of `DashBot`, building a interactive, draggable and responsive interface for DashBot. -Integrate support for multiple LLM providers with the option to switch between LLMs. +Integrate support for multiple LLM providers with the option to switch between LLM's. #### **Milestone #2: Core Feature Implementation** -This milestone focuses on integrating the major features and is expected to be the most time-intensive phase of development. +This milestone focuses on integrating three major features and is expected to be the most time-intensive phase of development. - Implement error debugging based on status codes and error messages. -- Documentation generator supporting Markdown and HTML formats. +- Develop a documentation generator supporting Markdown and HTML formats. - Build a test generator to create unit and integration tests from API specifications. - Enhance LLM responses using Few-shot prompting and ReAct techniques. -- API response visualizations (line, bar, pie, scatter, radar charts) -- Generating API integration frontend code for frontend frameworks. -#### **Milestone #3: Benchmarking & Optimization** +#### **Milestone #3: Visualization & UI/UX Development** -This milestone will involve running benchmark evaluations for all LLM providers by comparing the result of key metrics from each model mentioned in the [Detailed Description](#detailed-description) section. +This Milestone is divided into two parts. The first part will aim at adding integrations for for API response visualizations (line, bar, pie, scatter, radar charts). +The second part will involve generating API integration frontend code for frontend frameworks. -#### **Milestone #4: API Authentication Methods** +#### **Milestone #4: Benchmarking & Optimization** -This milestone will add a new tab "Authorization" in the home page of API Dash where users can perform various API Authentication Methods such as, -- Basic authentication -- API Auth -- API key -- Bearer token -- JWT Bearer -- Digest Auth -- OAuth 1.0 -- OAuth 2.0 +This milestone will invole in running benchmark evaluations for all LLM providers using predefined datasets. +Comparing accuracy, response time, and cost efficiency across providers. ## **[GSOC 2025 TIMELINE](https://developers.google.com/open-source/gsoc/timeline) FOR REFERENCE** @@ -298,61 +189,42 @@ their final mentor evaluation (standard coding period) ## **PREDICTED PROJECT TIMELINE** * **Community Bonding Period (May 8 - June 1)** - This is the period where I will get to know my mentors better. I will also ask questions and attempt to clarify the doubts and queries in my mind, to get a clear understanding of the project. Although Google recommends this 3-week bonding period to be entirely for the introduction of GSoC Contributors into their projects, since we are going to build a brand new feature, I propose to begin coding from the 2nd or 3rd week of this period, thus adding a head start. + This is the period where I will get to know my mentors better. I will also ask questions and attempt to clarify the doubts and queries in my mind, to get a clear understanding of the project. Although Google recommends this 3-week bonding period to be entirely for the introduction of GSoC Contributors into their projects, since we are going to build a brand new feature, I propose to begin coding from the 2nd or 3rd week of this period, thus adding a headstart. * **Coding Period (June 2 - July 14)** * **Week 1 (June 2 - June 8)** - - Build an interactive, draggable and responsive interface for DashBot. - - Add support for Local and Remote LLM providers along with integrated support for switching between local and remote LLM providers. - - **Deliverables:** - A fully interactive, draggable, and responsive UI for DashBot, with support for switching between providers. - + M#1 is delivered, comprising of a function DashBot. + + Work on M#2 begins in the latter half of the week. * **Week 2 (June 9 - June 15)** - - Implement API Response explanation generation. - - Implement error debugging based on status codes and error messages. - - **Deliverables:** - Dashbot capable of providing explanations for API Responses and debugging errors based on status code and error messages. + Start of M#2 Implement error debugging based on status codes and error messages. * **Week 3 (June 16 - June 22)** - - Develop a documentation generator supporting Markdown and HTML formats. - - Develop a Test generator to create unit and integration tests from API specifications is done. - - **Deliverables:** - Dashbot capable of generating documentation and tests from API Specifications. - + Building upon the previous week, the integration for documentation generator and test generator to create unit and integration tests from API specifications is done. * **Week 4 (June 16 - June 22)** - - Adding integration for API response visualization. - - Adding integration for frontend code generation for frontend frameworks. - - **Deliverables:** - Dashbot capable of visualizing api responses and code generation for frontend frameworks. + Based on the implementation experience and testing feedback from the previous weeks, refinements will be made by enhance LLM responses using Few-shot prompting and ReAct techniques to ensure robustness and efficiency. * **Week 5 (June 23 - June 29)** - - A significant portion of the week will be dedicated to testing all integrations thoroughly and addressing any bugs or issues identified. - - Mentor Reviews are requested. - - **Deliverables:** - Ensure that Dashbot is capable of performing all action mentioned in the previous weeks and ready for running benchmarks. + A significant portion of the week will be dedicated to testing all integrations thoroughly and addressing any bugs or issues identified. + + This week will also contain several steps of documenting DashBot. + Mentor Reviews are requested. * **Week 6 (June 30 - July 6)** - - Changes follow, from Mentor Review, if required. - - Running benchmark evaluation evaluating LLMs for API Response Explanation and API Error Debugging, Documentation generation, Test Generation, API Visualization and Frontend code generation for frontend frameworks. - - - Final Mentor Review before Mid-term Evaluation is submitted. - - **Deliverables:** - Benchmarking results of various LLMs for Response Explanation and API Error Debugging, Documentation generation, Test Generation, API Visualization and Frontend code generation for frontend frameworks. + Changes follow, from Mentor Review, if required. + + Start of M#3 by adding integrations for for API response visualizations. + + Final Mentor Review before Mid-term Evaluation is submitted. * **Midterm Evaluation Submission (July 14 - July 18)** * Projects are submitted to the mentors and the GSoC portal. @@ -360,45 +232,32 @@ their final mentor evaluation (standard coding period) * **Work Period (July 14 - August 25)** * **Week 7 (July 14 - July 20)** - - Extending support for Dashbot in Android/iOS devices. - - Finishing up Dashbot. - - **Deliverables:** - A fully functional Dashbot across all platforms. + Continuation of the work done in Week 6. * **Week 8 (July 21 - July 27)** - - Start developing basic API Authentication methods: Basic authentication, API Auth, API key. - - **Deliverables:** - Defined API authentication parameters and initial implementation of basic authentication methods. + A significant portion of the week will be dedicated to testing all integrations thoroughly and addressing any bugs or issues identified. + Documentation is enhanced in the if no issues arise. + * **Week 9 (July 28 - August 3)** - - Implement API Authentication methods: Bearer token, JWT Bearer. - - Develop a mechanism to allow switching authentication parameters between the request header and body. - - Mentor Reviews are requested. - - **Deliverables:** - Fully implemented Bearer Token and JWT Bearer authentication, along with a configurable authentication parameter placement (header/body). + Running benchmark evaluations for all LLM providers. * **Week 10 (August 4 - August 10)** - - Develop OAuth API Authentication methods: OAuth 1.0 and OAuth 2.0. - - **Deliverables:** - A Fully working “Authorization” tab in the Home Page of API Dash with support for various API Authntication Methods. + Continuation of the work done in Week 9. + Mentor Reviews are requested. * **Week 11 (August 11 - August 17)** - - Implement test cases for different authentication methods, including Basic Auth, API Key, Bearer Token, and JWT and OAuth. - - **Deliverables:** - Comprehensive test cases covering unit, integration, functional, and end-to-end authentication scenarios. + The former half of the week acts as a buffer period in case any issues are confronted. + Documentation is enhanced in the buffer period if no issues arise. + Milestone #4 is delivered. * **Week 12 (August 18 - August 24)** - Final checks are made, and any supporting documents (such as example usage markdown files, benchmarkings, ) are written. + Final checks are made, and any supporting documents (such as example usage markdown files) are written. The project Report is written and all tracking issues are labelled appropriately. * **Final Week (August 25 - September 1)** From 77bd54a7a406276ad9cc66a206054dc6ad0a4f5d Mon Sep 17 00:00:00 2001 From: Udhay-Adithya Date: Sat, 29 Mar 2025 02:17:51 +0530 Subject: [PATCH 052/188] add mvvm folder structure --- .../gsoc/application_udhay_adithya_dashbot.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md index 4fbccbb29..d7c0f97fa 100644 --- a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md +++ b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md @@ -112,6 +112,23 @@ Key dependencies are, - [fl_charts](https://pub.dev/packages/fl_chart) + +Here's a well-structured **Flutter MVVM (Model-View-ViewModel) project** using a **feature-first approach**, ensuring scalability and maintainability: + +## **FOLDER STRUCTURE** +``` +lib/ +│── features/ +│ ├── feature_1/ +│ │ ├── repository/ # Data layer for feature_1 +│ │ │ ├── feature_1_repository.dart +│ │ ├── viewmodel/ # Business logic for feature_1 +│ │ │ ├── feature_1_viewmodel.dart +│ │ ├── view/ # UI layer for feature_1 +│ │ │ ├── feature_1_screen.dart +│ │ │ ├── widgets/ # Reusable widgets for this feature +``` + ## **USAGE** DashBot can be accessed from the home screen of API Dash using a floating action button at the bottom right corner. From ccb1a85217605d89e2a89df4b26c8d2ae1b63a0c Mon Sep 17 00:00:00 2001 From: Udhay-Adithya Date: Sun, 30 Mar 2025 00:08:37 +0530 Subject: [PATCH 053/188] improve proposal based on mentor feedback --- .../gsoc/application_udhay_adithya_dashbot.md | 61 ++++++++++++++----- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md index d7c0f97fa..419960fb3 100644 --- a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md +++ b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md @@ -68,9 +68,14 @@ What excites me the most about API Dash is its aim to streamline the testing and ### 8. Can you mention some areas where the project can be improved? -DashBot's UI/UX can be significantly enhanced by implementing several modern features to improve user interaction and satisfaction. One key improvement is the introduction of a **Resizable and Draggable Chat Window**, allowing users to adjust the chat interface according to their preferences and screen space, creating a more personalized and efficient workspace. -**Integrating Voice Input** can also change the way of how users interact with DashBot. By enabling voice commands, users can perform tasks hands-free, which is particularly beneficial in scenarios where typing is inconvenient or impossible. This feature not only adds convenience but also makes DashBot more accessible to users with disabilities, aligning with inclusive design principles and expanding its utility in diverse environments. +DashBot's UI/UX can be significantly enhanced by implementing several modern features to improve user interaction and satisfaction. One key improvement is the introduction of a **Resizable and Draggable Chat Window**, allowing users to adjust the chat interface according to their preferences and screen space, creating a more personalized and efficient workspace. + +**Integrating Voice Input** can also change the way users interact with DashBot. By enabling voice commands, users can perform tasks hands-free, which is particularly beneficial in scenarios where typing is inconvenient or impossible. This feature not only adds convenience but also makes DashBot more accessible to users with disabilities, aligning with inclusive design principles and expanding its utility in diverse environments. + +Additionally, a **Security & Compliance Advisor** can enhance DashBot’s capabilities by providing: +- **Vulnerability Scanning:** Automatically identifying security risks such as missing authentication, exposed sensitive data, and other vulnerabilities. +- **Auto-Remediation:** Offering actionable suggestions to fix issues like CORS misconfigurations or insecure headers. ### **PROJECT TITLE : DashBot** @@ -100,7 +105,38 @@ For core features like debugging API requests, generating documentation, and cre API response visualizations will be built using the `fl_chart` library, which supports customizable line, bar, pie, scatter, and radar charts. This integration will allow users to interactively analyze API data and identify trends. -The UI/UX will be optimized for developer productivity, featuring a responsive design, dynamic theming and real-time feedback. Benchmark evaluations for each LLM provider will be conducted using predefined datasets to compare accuracy, response time, and cost efficiency, empowering users to choose the optimal backend. +Benchmark evaluations for each LLM provider will be conducted. Here are the key metrics that will be considered for benchmarking different models, + +- **Explain responses** + - Discrepancy Detection Rate: Does the LLM correctly identify discrepancies? + - Explainability Score: Are explanations understandable to developers? + - Edge Case Handling: Can it handle ambiguous or incomplete responses? + +- **Debug errors** + - Error Classification Accuracy: Does the LLM correctly identify the error source? + - Fix Implementation Rate: Are fixes actionable and contextually appropriate? + - Ambiguity Handling Test: Can it categorize errors (e.g., syntax vs. logic)? + +- **Generate Documentation** + - Coverage Score: Does the documentation cover all endpoints, parameters, and responses? + - Readability Assessment: Is the documentation clear and well-structured? + - Standard Compliance Check: Does it adhere to standards? + +- **Generate Tests** + - Test Coverage Report: Percentage of API endpoints and edge cases covered + - Test Execution Success Rate: Do tests pass/fail correctly when run? + - Adaptability Test: Are tests structured for easy updates? + +- **Generate Visualizations** + - Data Fidelity Check: Does the plot correctly represent the data? + - Aesthetic Scoring: Is the visualization clear and professional? + - Customization Flexibility Test: Can users tweak parameters? + +- **Generate Frontend Code** + - Code Execution Test: Does the code compile and function as expected? + - Best Practice Adherence: Does it follow best practices? + - Maintainability Score: Is the code clean and maintainable? + Key dependencies are, @@ -112,9 +148,6 @@ Key dependencies are, - [fl_charts](https://pub.dev/packages/fl_chart) - -Here's a well-structured **Flutter MVVM (Model-View-ViewModel) project** using a **feature-first approach**, ensuring scalability and maintainability: - ## **FOLDER STRUCTURE** ``` lib/ @@ -133,12 +166,14 @@ lib/ DashBot can be accessed from the home screen of API Dash using a floating action button at the bottom right corner. -![api explorer](images/dashbot_on_screen.png) +![DashBot On Screen](images/dashbot_on_screen.png) +![DashBot Settings](images/dashbot_settings_1.png) +![DashBot Debug](images/dashbot_debug.png)
- GSoC Logo - API Dash Logo + dashbot_default_dark + dashbot_default_dark
@@ -175,8 +210,7 @@ The second part will involve generating API integration frontend code for fronte #### **Milestone #4: Benchmarking & Optimization** -This milestone will invole in running benchmark evaluations for all LLM providers using predefined datasets. -Comparing accuracy, response time, and cost efficiency across providers. +This milestone will invole in running benchmark evaluations for all LLM providers by comparing the result of key metrics from each model mentioned in the [Detailed Description](#detailed-description) section. ## **[GSOC 2025 TIMELINE](https://developers.google.com/open-source/gsoc/timeline) FOR REFERENCE** @@ -255,7 +289,6 @@ their final mentor evaluation (standard coding period) A significant portion of the week will be dedicated to testing all integrations thoroughly and addressing any bugs or issues identified. Documentation is enhanced in the if no issues arise. - * **Week 9 (July 28 - August 3)** @@ -269,12 +302,12 @@ their final mentor evaluation (standard coding period) * **Week 11 (August 11 - August 17)** The former half of the week acts as a buffer period in case any issues are confronted. - Documentation is enhanced in the buffer period if no issues arise. + Documentation and benchmarking evaluations are enhanced in the buffer period if no issues arise. Milestone #4 is delivered. * **Week 12 (August 18 - August 24)** - Final checks are made, and any supporting documents (such as example usage markdown files) are written. + Final checks are made, and any supporting documents (such as example usage markdown files, bechmarkings, ) are written. The project Report is written and all tracking issues are labelled appropriately. * **Final Week (August 25 - September 1)** From b3afe690c766f97819399639585aba6f652397c9 Mon Sep 17 00:00:00 2001 From: Udhay-Adithya Date: Sun, 30 Mar 2025 13:13:42 +0530 Subject: [PATCH 054/188] fix: correct typos and enhance clarity --- .../gsoc/application_udhay_adithya_dashbot.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md index 419960fb3..444a0b2fc 100644 --- a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md +++ b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md @@ -191,9 +191,9 @@ I propose to divide the project into four milestones/deliverables to produce a s #### **Milestone #1: Architecture Setup & Multi-LLM Integration.** -This milestone will lay the foundation of `DashBot`, building a interactive, draggable and responsive interface for DashBot. +This milestone will lay the foundation of `DashBot`, building an interactive, draggable and responsive interface for DashBot. -Integrate support for multiple LLM providers with the option to switch between LLM's. +Integrate support for multiple LLM providers with the option to switch between LLMs. #### **Milestone #2: Core Feature Implementation** This milestone focuses on integrating three major features and is expected to be the most time-intensive phase of development. @@ -205,12 +205,12 @@ This milestone focuses on integrating three major features and is expected to be #### **Milestone #3: Visualization & UI/UX Development** -This Milestone is divided into two parts. The first part will aim at adding integrations for for API response visualizations (line, bar, pie, scatter, radar charts). +This Milestone is divided into two parts. The first part will aim at adding integrations for API response visualizations (line, bar, pie, scatter, radar charts). The second part will involve generating API integration frontend code for frontend frameworks. #### **Milestone #4: Benchmarking & Optimization** -This milestone will invole in running benchmark evaluations for all LLM providers by comparing the result of key metrics from each model mentioned in the [Detailed Description](#detailed-description) section. +This milestone will involve running benchmark evaluations for all LLM providers by comparing the result of key metrics from each model mentioned in the [Detailed Description](#detailed-description) section. ## **[GSOC 2025 TIMELINE](https://developers.google.com/open-source/gsoc/timeline) FOR REFERENCE** @@ -240,12 +240,12 @@ their final mentor evaluation (standard coding period) ## **PREDICTED PROJECT TIMELINE** * **Community Bonding Period (May 8 - June 1)** - This is the period where I will get to know my mentors better. I will also ask questions and attempt to clarify the doubts and queries in my mind, to get a clear understanding of the project. Although Google recommends this 3-week bonding period to be entirely for the introduction of GSoC Contributors into their projects, since we are going to build a brand new feature, I propose to begin coding from the 2nd or 3rd week of this period, thus adding a headstart. + This is the period where I will get to know my mentors better. I will also ask questions and attempt to clarify the doubts and queries in my mind, to get a clear understanding of the project. Although Google recommends this 3-week bonding period to be entirely for the introduction of GSoC Contributors into their projects, since we are going to build a brand new feature, I propose to begin coding from the 2nd or 3rd week of this period, thus adding a head start. * **Coding Period (June 2 - July 14)** * **Week 1 (June 2 - June 8)** - M#1 is delivered, comprising of a function DashBot. + M#1 is delivered, consisting of a function DashBot. Work on M#2 begins in the latter half of the week. @@ -259,7 +259,7 @@ their final mentor evaluation (standard coding period) * **Week 4 (June 16 - June 22)** - Based on the implementation experience and testing feedback from the previous weeks, refinements will be made by enhance LLM responses using Few-shot prompting and ReAct techniques to ensure robustness and efficiency. + Based on the implementation experience and testing feedback from the previous weeks, refinements will be made by enhancing LLM responses using Few-shot prompting and ReAct techniques to ensure robustness and efficiency. * **Week 5 (June 23 - June 29)** @@ -273,7 +273,7 @@ their final mentor evaluation (standard coding period) Changes follow, from Mentor Review, if required. - Start of M#3 by adding integrations for for API response visualizations. + Start of M#3 by adding integrations for API response visualizations. Final Mentor Review before Mid-term Evaluation is submitted. @@ -288,7 +288,7 @@ their final mentor evaluation (standard coding period) * **Week 8 (July 21 - July 27)** A significant portion of the week will be dedicated to testing all integrations thoroughly and addressing any bugs or issues identified. - Documentation is enhanced in the if no issues arise. + Documentation is enhanced in if no issues arise. * **Week 9 (July 28 - August 3)** @@ -307,7 +307,7 @@ their final mentor evaluation (standard coding period) * **Week 12 (August 18 - August 24)** - Final checks are made, and any supporting documents (such as example usage markdown files, bechmarkings, ) are written. + Final checks are made, and any supporting documents (such as example usage markdown files, benchmarkings, ) are written. The project Report is written and all tracking issues are labelled appropriately. * **Final Week (August 25 - September 1)** From 47989544e54606a6c12596912fec28da9541ab7f Mon Sep 17 00:00:00 2001 From: Udhay-Adithya Date: Fri, 4 Apr 2025 18:09:53 +0530 Subject: [PATCH 055/188] update title and add more details in weekly timeline --- .../gsoc/application_udhay_adithya_dashbot.md | 133 +++++++++++++----- 1 file changed, 96 insertions(+), 37 deletions(-) diff --git a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md index 444a0b2fc..c943b77be 100644 --- a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md +++ b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md @@ -22,7 +22,7 @@ ### 1. Have you worked on or contributed to a FOSS project before? Can you attach repo links or relevant PRs? -I haven't contributed to open-source projects before this, so API Dash marks my first practical experience with open-source contribution. So far, I have made a total of four PRs—two of which have been merged, while the other two are currently under review. Here are the relevant PRs: +I haven't contributed to open-source projects before this, so API Dash marks my first practical experience with open-source contribution. So far, I have made a total of four PRs—three of which have been merged, while the other one is currently under review. Here are the relevant PRs: ([#698](https://github.com/foss42/apidash/pull/698)): docs(codegen): add detailed instructions for ureq @@ -81,7 +81,7 @@ Additionally, a **Security & Compliance Advisor** can enhance DashBot’s capabi ### **ABSTRACT:** -This proposal seeks to develop DashBot - the AI assistant for API Dash which supercharges developer productivity by helping developers automate tedious tasks, follow best practices, interact & obtain contextual suggestions, all via natural-language input. DashBot will be designed in a modular and extensible manner and provide the following list of features (suggestive, not exhaustive): +This proposal seeks to develop DashBot - the AI assistant for API Dash which supercharges developer productivity by helping developers automate tedious tasks, follow best practices, interact & obtain contextual suggestions, all via natural-language input. DashBot will be designed in a modular and extensible manner and provide the following list of features: - Explain responses & identify any discrepancy - Debug requests based on Status codes & Error messages @@ -105,7 +105,7 @@ For core features like debugging API requests, generating documentation, and cre API response visualizations will be built using the `fl_chart` library, which supports customizable line, bar, pie, scatter, and radar charts. This integration will allow users to interactively analyze API data and identify trends. -Benchmark evaluations for each LLM provider will be conducted. Here are the key metrics that will be considered for benchmarking different models, +**Benchmark evaluations** for each LLM provider will be conducted. Here are the key metrics that will be considered for benchmarking different models, - **Explain responses** - Discrepancy Detection Rate: Does the LLM correctly identify discrepancies? @@ -137,8 +137,19 @@ Benchmark evaluations for each LLM provider will be conducted. Here are the key - Best Practice Adherence: Does it follow best practices? - Maintainability Score: Is the code clean and maintainable? +### **Implementing other essential features:** -Key dependencies are, +Adding Support for various API Authentication Methods such as, +- **Basic authentication:** Sending a verified username and password with API request +- **API Auth:** Basic authentication [#610](https://github.com/foss42/apidash/issues/610) +- **API key:** Sending a key-value pair to the API either in the request headers or query parameters Add API Auth: API key [#611](https://github.com/foss42/apidash/issues/611) +- **Bearer token:** Authenticate using an access key, such as a JSON Web Token (JWT) Add API Auth: Bearer token [#612](https://github.com/foss42/apidash/issues/612) +- **JWT Bearer:** Generate JWT bearer tokens to authorize requests Add API Auth: JWT Bearer [#613](https://github.com/foss42/apidash/issues/613) +- **Digest Auth:** Client must send two requests. First request sent to the server receives a nonce value, which is then used to produce a one-time-use hash key to authenticate the request Add API Auth: Digest Auth [#614](https://github.com/foss42/apidash/issues/614) +- **OAuth 1.0** Add API Auth: OAuth 1.0 [#615](https://github.com/foss42/apidash/issues/615) +- **OAuth 2.0:** Implement OAuth 2.0 authentication [#481](https://github.com/foss42/apidash/issues/481) + +Required dependencies, - [anthropic_sdk_dart](https://pub.dev/packages/anthropic_sdk_dart) - [googleai_dart](https://pub.dev/packages/googleai_dart/versions) @@ -146,7 +157,12 @@ Key dependencies are, - [ollama_dart](https://pub.dev/packages/ollama_dart) - [flutter_riverpod](https://pub.dev/packages/flutter_riverpod) - [fl_charts](https://pub.dev/packages/fl_chart) - +- [riverpod_annotation](https://pub.dev/packages/riverpod_annotation) +- [riverpod_lint](https://pub.dev/packages/riverpod_lint) +- [riverpod_generator](https://pub.dev/packages/riverpod_generator) +- [custom_lint](https://pub.dev/packages/custom_lint) +- [oauth1](https://pub.dev/packages/oauth1) +- [oauth2](https://pub.dev/packages/oauth2) ## **FOLDER STRUCTURE** ``` @@ -158,7 +174,8 @@ lib/ │ │ ├── viewmodel/ # Business logic for feature_1 │ │ │ ├── feature_1_viewmodel.dart │ │ ├── view/ # UI layer for feature_1 -│ │ │ ├── feature_1_screen.dart +│ │ │ ├── pages/ # Reusable widgets for this feature +│ │ │ │ ├── feature_1_screen.dart │ │ │ ├── widgets/ # Reusable widgets for this feature ``` @@ -196,21 +213,30 @@ This milestone will lay the foundation of `DashBot`, building an interactive, dr Integrate support for multiple LLM providers with the option to switch between LLMs. #### **Milestone #2: Core Feature Implementation** -This milestone focuses on integrating three major features and is expected to be the most time-intensive phase of development. +This milestone focuses on integrating the major features and is expected to be the most time-intensive phase of development. - Implement error debugging based on status codes and error messages. -- Develop a documentation generator supporting Markdown and HTML formats. +- Documentation generator supporting Markdown and HTML formats. - Build a test generator to create unit and integration tests from API specifications. - Enhance LLM responses using Few-shot prompting and ReAct techniques. +- API response visualizations (line, bar, pie, scatter, radar charts) +- Generating API integration frontend code for frontend frameworks. -#### **Milestone #3: Visualization & UI/UX Development** +#### **Milestone #3: Benchmarking & Optimization** -This Milestone is divided into two parts. The first part will aim at adding integrations for API response visualizations (line, bar, pie, scatter, radar charts). -The second part will involve generating API integration frontend code for frontend frameworks. +This milestone will involve running benchmark evaluations for all LLM providers by comparing the result of key metrics from each model mentioned in the [Detailed Description](#detailed-description) section. -#### **Milestone #4: Benchmarking & Optimization** +#### **Milestone #4: API Authentication Methods** -This milestone will involve running benchmark evaluations for all LLM providers by comparing the result of key metrics from each model mentioned in the [Detailed Description](#detailed-description) section. +This milestone will a new section in the home page of API Dash "Authorization" where users can perform various API Authentication Methods such as, +- Basic authentication +- API Auth +- API key +- Bearer token +- JWT Bearer +- Digest Auth +- OAuth 1.0 +- OAuth 2.0. ## **[GSOC 2025 TIMELINE](https://developers.google.com/open-source/gsoc/timeline) FOR REFERENCE** @@ -245,37 +271,55 @@ their final mentor evaluation (standard coding period) * **Coding Period (June 2 - July 14)** * **Week 1 (June 2 - June 8)** - M#1 is delivered, consisting of a function DashBot. - - Work on M#2 begins in the latter half of the week. + - Build an interactive, draggable and responsive interface for DashBot. + - Add support for Local and Remote LLM providers along with integrated support for switching between local and remote LLM providers. + + **Deliverables:** + A fully interactive, draggable, and responsive UI for DashBot, with support for switching between providers. + * **Week 2 (June 9 - June 15)** - Start of M#2 Implement error debugging based on status codes and error messages. + - Implement API Response explanation generation. + - Implement error debugging based on status codes and error messages. + + **Deliverables:** + Dashbot capable of providing explanations for API Responses and debugging errors based on status code and error messages. * **Week 3 (June 16 - June 22)** - Building upon the previous week, the integration for documentation generator and test generator to create unit and integration tests from API specifications is done. + - Develop a documentation generator supporting Markdown and HTML formats. + - Develop a Test generator to create unit and integration tests from API specifications is done. + + **Deliverables:** + Dashbot capable of generating documentation and tests from API Specifications. + * **Week 4 (June 16 - June 22)** - Based on the implementation experience and testing feedback from the previous weeks, refinements will be made by enhancing LLM responses using Few-shot prompting and ReAct techniques to ensure robustness and efficiency. + - Adding integration for API response visualization. + - Adding integration for frontend code generation for frontend frameworks. + + **Deliverables:** + Dashbot capable of visualizing api responses and code generation for frontend frameworks. * **Week 5 (June 23 - June 29)** - A significant portion of the week will be dedicated to testing all integrations thoroughly and addressing any bugs or issues identified. - - This week will also contain several steps of documenting DashBot. + - A significant portion of the week will be dedicated to testing all integrations thoroughly and addressing any bugs or issues identified. + - Mentor Reviews are requested. + + **Deliverables:** + Ensure that Dashbot is capable of performing all action mentioned in the previous weeks and ready for running benchmarks. - Mentor Reviews are requested. * **Week 6 (June 30 - July 6)** - Changes follow, from Mentor Review, if required. - - Start of M#3 by adding integrations for API response visualizations. - - Final Mentor Review before Mid-term Evaluation is submitted. + - Changes follow, from Mentor Review, if required. + - Setting up a benchmarking environment and evaluating LLMs for API Response Explanation and API Error Debugging. + - Final Mentor Review before Mid-term Evaluation is submitted. + + **Deliverables:** + Benchmarking results of various LLMs for Response Explanation and API Error Debugging. * **Midterm Evaluation Submission (July 14 - July 18)** * Projects are submitted to the mentors and the GSoC portal. @@ -283,27 +327,42 @@ their final mentor evaluation (standard coding period) * **Work Period (July 14 - August 25)** * **Week 7 (July 14 - July 20)** - Continuation of the work done in Week 6. + - Running benchmark evaluation for remaining features, Documentation generation, Test Generation, API Visualization and Frontend code generation for frontend frameworks. + + **Deliverables:** + Benchmarking results of different LLMs for Documentation generation, Test Generation, API Visualization and Frontend code generation for frontend frameworks. * **Week 8 (July 21 - July 27)** - A significant portion of the week will be dedicated to testing all integrations thoroughly and addressing any bugs or issues identified. - Documentation is enhanced in if no issues arise. + - Extending support for Dashbot in Android/iOS devices. + - Documentation and benchmarking evaluations are enhanced if time permits. + - Finishing up Dashbot. + + **Deliverables:** + A fully functional Dashbot across all platforms. * **Week 9 (July 28 - August 3)** - Running benchmark evaluations for all LLM providers. + - Start developing basic API Authentication methods: Basic authentication, API Auth, API key. + + **Deliverables:** + Defined API authentication parameters and initial implementation of basic authentication methods. * **Week 10 (August 4 - August 10)** - Continuation of the work done in Week 9. - Mentor Reviews are requested. + - Implement API Authentication methods: Bearer token, JWT Bearer. + - Develop a mechanism to allow switching authentication parameters between the request header and body. + - Mentor Reviews are requested. + + **Deliverables:** + Fully implemented Bearer Token and JWT Bearer authentication, along with a configurable authentication parameter placement (header/body). * **Week 11 (August 11 - August 17)** - The former half of the week acts as a buffer period in case any issues are confronted. - Documentation and benchmarking evaluations are enhanced in the buffer period if no issues arise. - Milestone #4 is delivered. + - Implement test cases for different authentication methods, including Basic Auth, API Key, Bearer Token, and JWT. + + **Deliverables:** + Comprehensive test cases covering unit, integration, functional, and end-to-end authentication scenarios. * **Week 12 (August 18 - August 24)** From fcd43fa3cddaedf525ad98f0d97f8ef648d1230e Mon Sep 17 00:00:00 2001 From: Udhay-Adithya Date: Sat, 5 Apr 2025 16:20:17 +0530 Subject: [PATCH 056/188] dedicate a week for oauth --- .../gsoc/application_udhay_adithya_dashbot.md | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md index c943b77be..f6019ec38 100644 --- a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md +++ b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md @@ -315,11 +315,12 @@ their final mentor evaluation (standard coding period) * **Week 6 (June 30 - July 6)** - Changes follow, from Mentor Review, if required. - - Setting up a benchmarking environment and evaluating LLMs for API Response Explanation and API Error Debugging. + - Running benchmark evaluation evaluating LLMs for API Response Explanation and API Error Debugging, Documentation generation, Test Generation, API Visualization and Frontend code generation for frontend frameworks. + - Final Mentor Review before Mid-term Evaluation is submitted. **Deliverables:** - Benchmarking results of various LLMs for Response Explanation and API Error Debugging. + Benchmarking results of various LLMs for Response Explanation and API Error Debugging, Documentation generation, Test Generation, API Visualization and Frontend code generation for frontend frameworks. * **Midterm Evaluation Submission (July 14 - July 18)** * Projects are submitted to the mentors and the GSoC portal. @@ -327,28 +328,21 @@ their final mentor evaluation (standard coding period) * **Work Period (July 14 - August 25)** * **Week 7 (July 14 - July 20)** - - Running benchmark evaluation for remaining features, Documentation generation, Test Generation, API Visualization and Frontend code generation for frontend frameworks. - - **Deliverables:** - Benchmarking results of different LLMs for Documentation generation, Test Generation, API Visualization and Frontend code generation for frontend frameworks. - - * **Week 8 (July 21 - July 27)** - - Extending support for Dashbot in Android/iOS devices. - - Documentation and benchmarking evaluations are enhanced if time permits. + - Documentation and benchmarking evaluations are enhanced in the buffer period if no issues arise. - Finishing up Dashbot. **Deliverables:** A fully functional Dashbot across all platforms. - * **Week 9 (July 28 - August 3)** + * **Week 8 (July 21 - July 27)** - Start developing basic API Authentication methods: Basic authentication, API Auth, API key. **Deliverables:** Defined API authentication parameters and initial implementation of basic authentication methods. - * **Week 10 (August 4 - August 10)** + * **Week 9 (July 28 - August 3)** - Implement API Authentication methods: Bearer token, JWT Bearer. - Develop a mechanism to allow switching authentication parameters between the request header and body. @@ -357,9 +351,16 @@ their final mentor evaluation (standard coding period) **Deliverables:** Fully implemented Bearer Token and JWT Bearer authentication, along with a configurable authentication parameter placement (header/body). + * **Week 10 (August 4 - August 10)** + + - Develop OAuth API Authentication methods: OAuth 1.0 and OAuth 2.0. + + **Deliverables:** + A Fully working “Authorization” tab in the Home Page of API Dash with support for various API Authntication Methods. + * **Week 11 (August 11 - August 17)** - - Implement test cases for different authentication methods, including Basic Auth, API Key, Bearer Token, and JWT. + - Implement test cases for different authentication methods, including Basic Auth, API Key, Bearer Token, and JWT and OAuth. **Deliverables:** Comprehensive test cases covering unit, integration, functional, and end-to-end authentication scenarios. From 5be5c6678c63af634262615f4fd4f0f83b55e69d Mon Sep 17 00:00:00 2001 From: nb923 <139726680+nb923@users.noreply.github.com> Date: Tue, 25 Mar 2025 14:58:00 -0400 Subject: [PATCH 057/188] Create application_nideesh_bharath_kumar_ai_api_evaluator.md --- ...ation_nideesh_bharath_kumar_ai_api_evaluator.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/doc/proposals/2025/gsoc/application_nideesh_bharath_kumar_ai_api_evaluator.md b/doc/proposals/2025/gsoc/application_nideesh_bharath_kumar_ai_api_evaluator.md index 73cd6424f..14bd5363e 100644 --- a/doc/proposals/2025/gsoc/application_nideesh_bharath_kumar_ai_api_evaluator.md +++ b/doc/proposals/2025/gsoc/application_nideesh_bharath_kumar_ai_api_evaluator.md @@ -5,8 +5,6 @@ **Email:** [bknideesh@gmail.com](mailto:bknideesh@gmail.com) -**Phone Number:** +1-224-333-1045 - **GitHub profile link:** [https://github.com/nb923](https://github.com/nb923) **Location:** New Jersey, United States of America @@ -121,7 +119,7 @@ This project is to develop a Dart-centered evaluation framework designed to simp **Architecture:** -![architecture](images/nb923-proposal-architecture.png) +![architecture](images/nb923-proposal-architecture) - Frontend Layer: This layer will be the main API Dash app UI. It will use Flutter/Dart to build a UI for users to select the AI evaluation test specifications and obtain details such as API key, model name, API link, and other details. This layer will also display the real-time charts of the evaluations and final metrics. @@ -144,23 +142,23 @@ This prototype contains a custom UI implementation of the AI evaluation layer, l The top right corner has a new button for API evaluations as show in the picture below: -![prototype-image-one](images/nb923-proposal-prototype-one.png) +![prototype-image-one](images/nb923-proposal-prototype-one) When selected, it prompts a selection of tests: -![prototype-image-two](images/nb923-proposal-prototype-two.png) +![prototype-image-two](images/nb923-proposal-prototype-two) Hellaswag is the only implemented test currently. When selected, it prompts a menu with model name, API URL, API key, and limit of dataset rows being tested. I recommend setting the limit to 20 to reduce API usage. -![prototype-image-three](images/nb923-proposal-prototype-three.png) +![prototype-image-three](images/nb923-proposal-prototype-three) When run is selected, it prompts a loading screen as the lm-evaluation-harness processes this request through a custom implementation of the provided models. -![prototype-image-four](images/nb923-proposal-prototype-four.png) +![prototype-image-four](images/nb923-proposal-prototype-four) After the evaluation is finished, it provides a quick value for the accuracy. This is a simple prototype and a limit of rows on the test is set; so, this metric should be taken with a grain of salt. -![prototype-image-five](images/nb923-proposal-prototype-five.png) +![prototype-image-five](images/nb923-proposal-prototype-five) Key changes are: From 38b245dcbcb89989f2d059c5726b5fa55f54d87e Mon Sep 17 00:00:00 2001 From: nb923 <139726680+nb923@users.noreply.github.com> Date: Tue, 25 Mar 2025 15:08:25 -0400 Subject: [PATCH 058/188] Update application_nideesh_bharath_kumar_ai_api_evaluator.md to support images --- ...ication_nideesh_bharath_kumar_ai_api_evaluator.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/proposals/2025/gsoc/application_nideesh_bharath_kumar_ai_api_evaluator.md b/doc/proposals/2025/gsoc/application_nideesh_bharath_kumar_ai_api_evaluator.md index 14bd5363e..c4f24c3ff 100644 --- a/doc/proposals/2025/gsoc/application_nideesh_bharath_kumar_ai_api_evaluator.md +++ b/doc/proposals/2025/gsoc/application_nideesh_bharath_kumar_ai_api_evaluator.md @@ -119,7 +119,7 @@ This project is to develop a Dart-centered evaluation framework designed to simp **Architecture:** -![architecture](images/nb923-proposal-architecture) +![architecture](images/nb923-proposal-architecture.png) - Frontend Layer: This layer will be the main API Dash app UI. It will use Flutter/Dart to build a UI for users to select the AI evaluation test specifications and obtain details such as API key, model name, API link, and other details. This layer will also display the real-time charts of the evaluations and final metrics. @@ -142,23 +142,23 @@ This prototype contains a custom UI implementation of the AI evaluation layer, l The top right corner has a new button for API evaluations as show in the picture below: -![prototype-image-one](images/nb923-proposal-prototype-one) +![prototype-image-one](images/nb923-proposal-prototype-one.png) When selected, it prompts a selection of tests: -![prototype-image-two](images/nb923-proposal-prototype-two) +![prototype-image-two](images/nb923-proposal-prototype-two.png) Hellaswag is the only implemented test currently. When selected, it prompts a menu with model name, API URL, API key, and limit of dataset rows being tested. I recommend setting the limit to 20 to reduce API usage. -![prototype-image-three](images/nb923-proposal-prototype-three) +![prototype-image-three](images/nb923-proposal-prototype-three.png) When run is selected, it prompts a loading screen as the lm-evaluation-harness processes this request through a custom implementation of the provided models. -![prototype-image-four](images/nb923-proposal-prototype-four) +![prototype-image-four](images/nb923-proposal-prototype-four.png) After the evaluation is finished, it provides a quick value for the accuracy. This is a simple prototype and a limit of rows on the test is set; so, this metric should be taken with a grain of salt. -![prototype-image-five](images/nb923-proposal-prototype-five) +![prototype-image-five](images/nb923-proposal-prototype-five.png) Key changes are: From f4eb5f1beda6fd25b6bedc34575b6960a8db442a Mon Sep 17 00:00:00 2001 From: nb923 <139726680+nb923@users.noreply.github.com> Date: Sun, 30 Mar 2025 11:24:05 -0400 Subject: [PATCH 059/188] Add phone number to application --- .../gsoc/application_nideesh_bharath_kumar_ai_api_evaluator.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/proposals/2025/gsoc/application_nideesh_bharath_kumar_ai_api_evaluator.md b/doc/proposals/2025/gsoc/application_nideesh_bharath_kumar_ai_api_evaluator.md index c4f24c3ff..73cd6424f 100644 --- a/doc/proposals/2025/gsoc/application_nideesh_bharath_kumar_ai_api_evaluator.md +++ b/doc/proposals/2025/gsoc/application_nideesh_bharath_kumar_ai_api_evaluator.md @@ -5,6 +5,8 @@ **Email:** [bknideesh@gmail.com](mailto:bknideesh@gmail.com) +**Phone Number:** +1-224-333-1045 + **GitHub profile link:** [https://github.com/nb923](https://github.com/nb923) **Location:** New Jersey, United States of America From 347a19adf07deae5cdc63d04fe260794d7a1aa6b Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 5 Apr 2025 20:44:07 +0530 Subject: [PATCH 060/188] share response body in Mobile --- lib/widgets/response_widgets.dart | 524 ++++++++++++++++++++++++++++++ 1 file changed, 524 insertions(+) create mode 100644 lib/widgets/response_widgets.dart diff --git a/lib/widgets/response_widgets.dart b/lib/widgets/response_widgets.dart new file mode 100644 index 000000000..9488e5de8 --- /dev/null +++ b/lib/widgets/response_widgets.dart @@ -0,0 +1,524 @@ +import 'dart:async'; +import 'package:apidash_core/apidash_core.dart'; +import 'package:apidash_design_system/apidash_design_system.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:lottie/lottie.dart'; +import 'package:apidash/utils/utils.dart'; +import 'package:apidash/widgets/widgets.dart'; +import 'package:apidash/models/models.dart'; +import 'package:apidash/consts.dart'; + +import 'button_share.dart'; + +class NotSentWidget extends StatelessWidget { + const NotSentWidget({super.key}); + + @override + Widget build(BuildContext context) { + final color = Theme.of(context).colorScheme.secondary; + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.north_east_rounded, + size: 40, + color: color, + ), + Text( + kLabelNotSent, + style: + Theme.of(context).textTheme.titleMedium?.copyWith(color: color), + ), + ], + ), + ); + } +} + +class SendingWidget extends StatefulWidget { + final DateTime? startSendingTime; + const SendingWidget({ + super.key, + required this.startSendingTime, + }); + + @override + State createState() => _SendingWidgetState(); +} + +class _SendingWidgetState extends State { + int _millisecondsElapsed = 0; + Timer? _timer; + + @override + void initState() { + super.initState(); + if (widget.startSendingTime != null) { + _millisecondsElapsed = + (DateTime.now().difference(widget.startSendingTime!).inMilliseconds ~/ + 100) * + 100; + _timer = Timer.periodic(const Duration(milliseconds: 100), _updateTimer); + } + } + + void _updateTimer(Timer timer) { + setState(() { + _millisecondsElapsed += 100; + }); + } + + @override + void dispose() { + if (_timer != null && _timer!.isActive) _timer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Center( + child: Lottie.asset(kAssetSendingLottie), + ), + Padding( + padding: kPh20t40, + child: Visibility( + visible: _millisecondsElapsed >= 0, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.alarm, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + const SizedBox( + width: 10, + ), + Text( + 'Time elapsed: ${humanizeDuration(Duration(milliseconds: _millisecondsElapsed))}', + textAlign: TextAlign.center, + overflow: TextOverflow.fade, + softWrap: false, + style: kTextStyleButton.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ), + ), + ], + ); + } +} + +class ResponsePaneHeader extends StatelessWidget { + const ResponsePaneHeader({ + super.key, + this.responseStatus, + this.message, + this.time, + this.onClearResponse, + }); + + final int? responseStatus; + final String? message; + final Duration? time; + final VoidCallback? onClearResponse; + + @override + Widget build(BuildContext context) { + final bool showClearButton = onClearResponse != null; + return Padding( + padding: kPv8, + child: SizedBox( + height: kHeaderHeight, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + kHSpacer10, + Expanded( + child: Text( + "$responseStatus: ${message ?? '-'}", + softWrap: false, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontFamily: kCodeStyle.fontFamily, + color: getResponseStatusCodeColor( + responseStatus, + brightness: Theme.of(context).brightness, + ), + ), + ), + ), + kHSpacer10, + Text( + humanizeDuration(time), + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontFamily: kCodeStyle.fontFamily, + color: Theme.of(context).colorScheme.secondary, + ), + ), + kHSpacer10, + showClearButton + ? ClearResponseButton( + onPressed: onClearResponse, + ) + : const SizedBox.shrink(), + ], + ), + ), + ); + } +} + +class ResponseTabView extends StatefulWidget { + const ResponseTabView({ + super.key, + this.selectedId, + required this.children, + }); + + final String? selectedId; + final List children; + @override + State createState() => _ResponseTabViewState(); +} + +class _ResponseTabViewState extends State + with TickerProviderStateMixin { + late final TabController _controller; + + @override + void initState() { + super.initState(); + _controller = TabController( + length: 2, + animationDuration: kTabAnimationDuration, + vsync: this, + ); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + TabBar( + key: Key(widget.selectedId!), + controller: _controller, + labelPadding: kPh2, + overlayColor: kColorTransparentState, + onTap: (index) {}, + tabs: const [ + TabLabel( + text: kLabelResponseBody, + ), + TabLabel( + text: kLabelHeaders, + ), + ], + ), + Expanded( + child: TabBarView( + controller: _controller, + physics: const NeverScrollableScrollPhysics(), + children: widget.children, + ), + ), + ], + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} + +class ResponseHeadersHeader extends StatelessWidget { + const ResponseHeadersHeader({ + super.key, + required this.map, + required this.name, + }); + + final Map map; + final String name; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: kHeaderHeight, + child: Row( + children: [ + Expanded( + child: Text( + "$name (${map.length} $kLabelItems)", + style: Theme.of(context).textTheme.labelMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ), + if (map.isNotEmpty) + CopyButton( + toCopy: kJsonEncoder.convert(map), + ), + ], + ), + ); + } +} + +class ResponseHeaders extends StatelessWidget { + const ResponseHeaders({ + super.key, + required this.responseHeaders, + required this.requestHeaders, + }); + + final Map responseHeaders; + final Map requestHeaders; + + @override + Widget build(BuildContext context) { + return Padding( + padding: kPh20v5, + child: ListView( + children: [ + ResponseHeadersHeader( + map: responseHeaders, + name: kLabelResponseHeaders, + ), + if (responseHeaders.isNotEmpty) kVSpacer5, + if (responseHeaders.isNotEmpty) + MapTable( + map: responseHeaders, + colNames: kHeaderRow, + firstColumnHeaderCase: true, + ), + kVSpacer10, + ResponseHeadersHeader( + map: requestHeaders, + name: kLabelRequestHeaders, + ), + if (requestHeaders.isNotEmpty) kVSpacer5, + if (requestHeaders.isNotEmpty) + MapTable( + map: requestHeaders, + colNames: kHeaderRow, + firstColumnHeaderCase: true, + ), + ], + ), + ); + } +} + +class ResponseBody extends StatelessWidget { + const ResponseBody({ + super.key, + this.selectedRequestModel, + }); + + final RequestModel? selectedRequestModel; + + @override + Widget build(BuildContext context) { + final responseModel = selectedRequestModel?.httpResponseModel; + if (responseModel == null) { + return const ErrorMessage( + message: '$kNullResponseModelError $kUnexpectedRaiseIssue'); + } + + var body = responseModel.body; + var formattedBody = responseModel.formattedBody; + if (body == null) { + return const ErrorMessage( + message: '$kMsgNullBody $kUnexpectedRaiseIssue'); + } + if (body.isEmpty) { + return const ErrorMessage( + message: kMsgNoContent, + showIcon: false, + showIssueButton: false, + ); + } + + final mediaType = + responseModel.mediaType ?? MediaType(kTypeText, kSubTypePlain); + // Fix #415: Treat null Content-type as plain text instead of Error message + // if (mediaType == null) { + // return ErrorMessage( + // message: + // '$kMsgUnknowContentType - ${responseModel.contentType}. $kUnexpectedRaiseIssue'); + // } + + var responseBodyView = getResponseBodyViewOptions(mediaType); + var options = responseBodyView.$1; + var highlightLanguage = responseBodyView.$2; + + if (formattedBody == null) { + options = [...options]; + options.remove(ResponseBodyView.code); + } + + return BodySuccess( + key: Key("${selectedRequestModel!.id}-response"), + mediaType: mediaType, + options: options, + bytes: responseModel.bodyBytes!, + body: body, + formattedBody: formattedBody, + highlightLanguage: highlightLanguage, + ); + } +} + +class BodySuccess extends StatefulWidget { + const BodySuccess( + {super.key, + required this.mediaType, + required this.body, + required this.options, + required this.bytes, + this.formattedBody, + this.highlightLanguage}); + final MediaType mediaType; + final List options; + final String body; + final Uint8List bytes; + final String? formattedBody; + final String? highlightLanguage; + @override + State createState() => _BodySuccessState(); +} + +class _BodySuccessState extends State { + int segmentIdx = 0; + + @override + Widget build(BuildContext context) { + var currentSeg = widget.options[segmentIdx]; + var codeTheme = Theme.of(context).brightness == Brightness.light + ? kLightCodeTheme + : kDarkCodeTheme; + final textContainerdecoration = BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainerLow, + border: Border.all( + color: Theme.of(context).colorScheme.surfaceContainerHighest, + ), + borderRadius: kBorderRadius8, + ); + + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + var showLabel = showButtonLabelsInBodySuccess( + widget.options.length, + constraints.maxWidth, + ); + return Padding( + padding: kP10, + child: Column( + children: [ + Row( + children: [ + (widget.options == kRawBodyViewOptions) + ? const SizedBox() + : SegmentedButton( + style: SegmentedButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 8), + ), + selectedIcon: Icon(currentSeg.icon), + segments: widget.options + .map>( + (e) => ButtonSegment( + value: e, + label: Text(e.label), + icon: constraints.maxWidth > + kMinWindowSize.width + ? Icon(e.icon) + : null, + ), + ) + .toList(), + selected: {currentSeg}, + onSelectionChanged: (newSelection) { + setState(() { + segmentIdx = + widget.options.indexOf(newSelection.first); + }); + }, + ), + const Spacer(), + kCodeRawBodyViewOptions.contains(currentSeg) + ? CopyButton( + toCopy: widget.formattedBody ?? widget.body, + showLabel: showLabel, + ) + : const SizedBox(), + kIsMobile + ? ShareButton( + toShare: widget.formattedBody ?? widget.body, + showLabel: showLabel, + ) + : SaveInDownloadsButton( + content: widget.bytes, + mimeType: widget.mediaType.mimeType, + showLabel: showLabel, + ), + ], + ), + kVSpacer10, + switch (currentSeg) { + ResponseBodyView.preview || ResponseBodyView.none => Expanded( + child: Container( + width: double.maxFinite, + padding: kP8, + decoration: textContainerdecoration, + child: Previewer( + bytes: widget.bytes, + body: widget.body, + type: widget.mediaType.type, + subtype: widget.mediaType.subtype, + hasRaw: widget.options.contains(ResponseBodyView.raw), + ), + ), + ), + ResponseBodyView.code => Expanded( + child: Container( + width: double.maxFinite, + padding: kP8, + decoration: textContainerdecoration, + child: CodePreviewer( + code: widget.formattedBody ?? widget.body, + theme: codeTheme, + language: widget.highlightLanguage, + textStyle: kCodeStyle, + ), + ), + ), + ResponseBodyView.raw => Expanded( + child: Container( + width: double.maxFinite, + padding: kP8, + decoration: textContainerdecoration, + child: SingleChildScrollView( + child: SelectableText( + widget.formattedBody ?? widget.body, + style: kCodeStyle, + ), + ), + ), + ), + } + ], + ), + ); + }, + ); + } +} From 40ebd3f27879fa65b5209856954afe421c732c93 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 5 Apr 2025 21:14:34 +0530 Subject: [PATCH 061/188] Refactor response widgets --- lib/widgets/response_body_success.dart | 3 +- lib/widgets/response_widgets.dart | 524 ------------------------- 2 files changed, 1 insertion(+), 526 deletions(-) delete mode 100644 lib/widgets/response_widgets.dart diff --git a/lib/widgets/response_body_success.dart b/lib/widgets/response_body_success.dart index 3774b606c..cb68b86d1 100644 --- a/lib/widgets/response_body_success.dart +++ b/lib/widgets/response_body_success.dart @@ -83,8 +83,7 @@ class _ResponseBodySuccessState extends State { }, ), const Spacer(), - ((widget.options == kPreviewRawBodyViewOptions) || - kCodeRawBodyViewOptions.contains(currentSeg)) + kCodeRawBodyViewOptions.contains(currentSeg) ? CopyButton( toCopy: widget.formattedBody ?? widget.body, showLabel: showLabel, diff --git a/lib/widgets/response_widgets.dart b/lib/widgets/response_widgets.dart deleted file mode 100644 index 9488e5de8..000000000 --- a/lib/widgets/response_widgets.dart +++ /dev/null @@ -1,524 +0,0 @@ -import 'dart:async'; -import 'package:apidash_core/apidash_core.dart'; -import 'package:apidash_design_system/apidash_design_system.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:lottie/lottie.dart'; -import 'package:apidash/utils/utils.dart'; -import 'package:apidash/widgets/widgets.dart'; -import 'package:apidash/models/models.dart'; -import 'package:apidash/consts.dart'; - -import 'button_share.dart'; - -class NotSentWidget extends StatelessWidget { - const NotSentWidget({super.key}); - - @override - Widget build(BuildContext context) { - final color = Theme.of(context).colorScheme.secondary; - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.north_east_rounded, - size: 40, - color: color, - ), - Text( - kLabelNotSent, - style: - Theme.of(context).textTheme.titleMedium?.copyWith(color: color), - ), - ], - ), - ); - } -} - -class SendingWidget extends StatefulWidget { - final DateTime? startSendingTime; - const SendingWidget({ - super.key, - required this.startSendingTime, - }); - - @override - State createState() => _SendingWidgetState(); -} - -class _SendingWidgetState extends State { - int _millisecondsElapsed = 0; - Timer? _timer; - - @override - void initState() { - super.initState(); - if (widget.startSendingTime != null) { - _millisecondsElapsed = - (DateTime.now().difference(widget.startSendingTime!).inMilliseconds ~/ - 100) * - 100; - _timer = Timer.periodic(const Duration(milliseconds: 100), _updateTimer); - } - } - - void _updateTimer(Timer timer) { - setState(() { - _millisecondsElapsed += 100; - }); - } - - @override - void dispose() { - if (_timer != null && _timer!.isActive) _timer?.cancel(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Stack( - children: [ - Center( - child: Lottie.asset(kAssetSendingLottie), - ), - Padding( - padding: kPh20t40, - child: Visibility( - visible: _millisecondsElapsed >= 0, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.alarm, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - const SizedBox( - width: 10, - ), - Text( - 'Time elapsed: ${humanizeDuration(Duration(milliseconds: _millisecondsElapsed))}', - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - softWrap: false, - style: kTextStyleButton.copyWith( - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ), - ), - ], - ); - } -} - -class ResponsePaneHeader extends StatelessWidget { - const ResponsePaneHeader({ - super.key, - this.responseStatus, - this.message, - this.time, - this.onClearResponse, - }); - - final int? responseStatus; - final String? message; - final Duration? time; - final VoidCallback? onClearResponse; - - @override - Widget build(BuildContext context) { - final bool showClearButton = onClearResponse != null; - return Padding( - padding: kPv8, - child: SizedBox( - height: kHeaderHeight, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - kHSpacer10, - Expanded( - child: Text( - "$responseStatus: ${message ?? '-'}", - softWrap: false, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontFamily: kCodeStyle.fontFamily, - color: getResponseStatusCodeColor( - responseStatus, - brightness: Theme.of(context).brightness, - ), - ), - ), - ), - kHSpacer10, - Text( - humanizeDuration(time), - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontFamily: kCodeStyle.fontFamily, - color: Theme.of(context).colorScheme.secondary, - ), - ), - kHSpacer10, - showClearButton - ? ClearResponseButton( - onPressed: onClearResponse, - ) - : const SizedBox.shrink(), - ], - ), - ), - ); - } -} - -class ResponseTabView extends StatefulWidget { - const ResponseTabView({ - super.key, - this.selectedId, - required this.children, - }); - - final String? selectedId; - final List children; - @override - State createState() => _ResponseTabViewState(); -} - -class _ResponseTabViewState extends State - with TickerProviderStateMixin { - late final TabController _controller; - - @override - void initState() { - super.initState(); - _controller = TabController( - length: 2, - animationDuration: kTabAnimationDuration, - vsync: this, - ); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - TabBar( - key: Key(widget.selectedId!), - controller: _controller, - labelPadding: kPh2, - overlayColor: kColorTransparentState, - onTap: (index) {}, - tabs: const [ - TabLabel( - text: kLabelResponseBody, - ), - TabLabel( - text: kLabelHeaders, - ), - ], - ), - Expanded( - child: TabBarView( - controller: _controller, - physics: const NeverScrollableScrollPhysics(), - children: widget.children, - ), - ), - ], - ); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } -} - -class ResponseHeadersHeader extends StatelessWidget { - const ResponseHeadersHeader({ - super.key, - required this.map, - required this.name, - }); - - final Map map; - final String name; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: kHeaderHeight, - child: Row( - children: [ - Expanded( - child: Text( - "$name (${map.length} $kLabelItems)", - style: Theme.of(context).textTheme.labelMedium?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - ), - if (map.isNotEmpty) - CopyButton( - toCopy: kJsonEncoder.convert(map), - ), - ], - ), - ); - } -} - -class ResponseHeaders extends StatelessWidget { - const ResponseHeaders({ - super.key, - required this.responseHeaders, - required this.requestHeaders, - }); - - final Map responseHeaders; - final Map requestHeaders; - - @override - Widget build(BuildContext context) { - return Padding( - padding: kPh20v5, - child: ListView( - children: [ - ResponseHeadersHeader( - map: responseHeaders, - name: kLabelResponseHeaders, - ), - if (responseHeaders.isNotEmpty) kVSpacer5, - if (responseHeaders.isNotEmpty) - MapTable( - map: responseHeaders, - colNames: kHeaderRow, - firstColumnHeaderCase: true, - ), - kVSpacer10, - ResponseHeadersHeader( - map: requestHeaders, - name: kLabelRequestHeaders, - ), - if (requestHeaders.isNotEmpty) kVSpacer5, - if (requestHeaders.isNotEmpty) - MapTable( - map: requestHeaders, - colNames: kHeaderRow, - firstColumnHeaderCase: true, - ), - ], - ), - ); - } -} - -class ResponseBody extends StatelessWidget { - const ResponseBody({ - super.key, - this.selectedRequestModel, - }); - - final RequestModel? selectedRequestModel; - - @override - Widget build(BuildContext context) { - final responseModel = selectedRequestModel?.httpResponseModel; - if (responseModel == null) { - return const ErrorMessage( - message: '$kNullResponseModelError $kUnexpectedRaiseIssue'); - } - - var body = responseModel.body; - var formattedBody = responseModel.formattedBody; - if (body == null) { - return const ErrorMessage( - message: '$kMsgNullBody $kUnexpectedRaiseIssue'); - } - if (body.isEmpty) { - return const ErrorMessage( - message: kMsgNoContent, - showIcon: false, - showIssueButton: false, - ); - } - - final mediaType = - responseModel.mediaType ?? MediaType(kTypeText, kSubTypePlain); - // Fix #415: Treat null Content-type as plain text instead of Error message - // if (mediaType == null) { - // return ErrorMessage( - // message: - // '$kMsgUnknowContentType - ${responseModel.contentType}. $kUnexpectedRaiseIssue'); - // } - - var responseBodyView = getResponseBodyViewOptions(mediaType); - var options = responseBodyView.$1; - var highlightLanguage = responseBodyView.$2; - - if (formattedBody == null) { - options = [...options]; - options.remove(ResponseBodyView.code); - } - - return BodySuccess( - key: Key("${selectedRequestModel!.id}-response"), - mediaType: mediaType, - options: options, - bytes: responseModel.bodyBytes!, - body: body, - formattedBody: formattedBody, - highlightLanguage: highlightLanguage, - ); - } -} - -class BodySuccess extends StatefulWidget { - const BodySuccess( - {super.key, - required this.mediaType, - required this.body, - required this.options, - required this.bytes, - this.formattedBody, - this.highlightLanguage}); - final MediaType mediaType; - final List options; - final String body; - final Uint8List bytes; - final String? formattedBody; - final String? highlightLanguage; - @override - State createState() => _BodySuccessState(); -} - -class _BodySuccessState extends State { - int segmentIdx = 0; - - @override - Widget build(BuildContext context) { - var currentSeg = widget.options[segmentIdx]; - var codeTheme = Theme.of(context).brightness == Brightness.light - ? kLightCodeTheme - : kDarkCodeTheme; - final textContainerdecoration = BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainerLow, - border: Border.all( - color: Theme.of(context).colorScheme.surfaceContainerHighest, - ), - borderRadius: kBorderRadius8, - ); - - return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - var showLabel = showButtonLabelsInBodySuccess( - widget.options.length, - constraints.maxWidth, - ); - return Padding( - padding: kP10, - child: Column( - children: [ - Row( - children: [ - (widget.options == kRawBodyViewOptions) - ? const SizedBox() - : SegmentedButton( - style: SegmentedButton.styleFrom( - padding: const EdgeInsets.symmetric(horizontal: 8), - ), - selectedIcon: Icon(currentSeg.icon), - segments: widget.options - .map>( - (e) => ButtonSegment( - value: e, - label: Text(e.label), - icon: constraints.maxWidth > - kMinWindowSize.width - ? Icon(e.icon) - : null, - ), - ) - .toList(), - selected: {currentSeg}, - onSelectionChanged: (newSelection) { - setState(() { - segmentIdx = - widget.options.indexOf(newSelection.first); - }); - }, - ), - const Spacer(), - kCodeRawBodyViewOptions.contains(currentSeg) - ? CopyButton( - toCopy: widget.formattedBody ?? widget.body, - showLabel: showLabel, - ) - : const SizedBox(), - kIsMobile - ? ShareButton( - toShare: widget.formattedBody ?? widget.body, - showLabel: showLabel, - ) - : SaveInDownloadsButton( - content: widget.bytes, - mimeType: widget.mediaType.mimeType, - showLabel: showLabel, - ), - ], - ), - kVSpacer10, - switch (currentSeg) { - ResponseBodyView.preview || ResponseBodyView.none => Expanded( - child: Container( - width: double.maxFinite, - padding: kP8, - decoration: textContainerdecoration, - child: Previewer( - bytes: widget.bytes, - body: widget.body, - type: widget.mediaType.type, - subtype: widget.mediaType.subtype, - hasRaw: widget.options.contains(ResponseBodyView.raw), - ), - ), - ), - ResponseBodyView.code => Expanded( - child: Container( - width: double.maxFinite, - padding: kP8, - decoration: textContainerdecoration, - child: CodePreviewer( - code: widget.formattedBody ?? widget.body, - theme: codeTheme, - language: widget.highlightLanguage, - textStyle: kCodeStyle, - ), - ), - ), - ResponseBodyView.raw => Expanded( - child: Container( - width: double.maxFinite, - padding: kP8, - decoration: textContainerdecoration, - child: SingleChildScrollView( - child: SelectableText( - widget.formattedBody ?? widget.body, - style: kCodeStyle, - ), - ), - ), - ), - } - ], - ), - ); - }, - ); - } -} From 52d7220eb1f69bf74a469e1ce33d1920e2289ea4 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 5 Apr 2025 22:43:46 +0530 Subject: [PATCH 062/188] Copy button for JSON & CSV previewer --- lib/widgets/response_body_success.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/widgets/response_body_success.dart b/lib/widgets/response_body_success.dart index cb68b86d1..3774b606c 100644 --- a/lib/widgets/response_body_success.dart +++ b/lib/widgets/response_body_success.dart @@ -83,7 +83,8 @@ class _ResponseBodySuccessState extends State { }, ), const Spacer(), - kCodeRawBodyViewOptions.contains(currentSeg) + ((widget.options == kPreviewRawBodyViewOptions) || + kCodeRawBodyViewOptions.contains(currentSeg)) ? CopyButton( toCopy: widget.formattedBody ?? widget.body, showLabel: showLabel, From 7d7b1313c8306e8863839882f8da6c439f6986d2 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 5 Apr 2025 23:00:35 +0530 Subject: [PATCH 063/188] Update previewer_json.dart --- lib/widgets/previewer_json.dart | 54 +++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/lib/widgets/previewer_json.dart b/lib/widgets/previewer_json.dart index 3f8e2b190..914df2af5 100644 --- a/lib/widgets/previewer_json.dart +++ b/lib/widgets/previewer_json.dart @@ -177,31 +177,39 @@ class _JsonPreviewerState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Expanded( - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - border: Border.all( - color: Theme.of(context) - .colorScheme - .surfaceContainerHighest), - borderRadius: kBorderRadius8, - ), - child: Row( - children: [ - const Padding( - padding: kPh4, - child: Icon( - Icons.search, - size: 16, + mainAxisAlignment: MainAxisAlignment.end, + children: constraints.minWidth > kMinWindowSize.width + ? [ + TextButton( + onPressed: state.areAllExpanded() + ? null + : state.expandAll, + child: const Text( + 'Expand All', + style: kTextStyleButtonSmall, + ), + ), + TextButton( + onPressed: state.areAllCollapsed() + ? null + : state.collapseAll, + child: const Text( + 'Collapse All', + style: kTextStyleButtonSmall, ), ), - Expanded( - child: JsonSearchField( - controller: searchController, - onChanged: (term) => state.search(term), + ] + : [ + IconButton( + tooltip: "Expand All", + color: Theme.of(context).colorScheme.primary, + visualDensity: VisualDensity.compact, + onPressed: state.areAllExpanded() + ? null + : state.expandAll, + icon: const Icon( + Icons.unfold_more, + size: 16, ), ), const SizedBox( From 56a4b098c8f5511c3bb199bb382ed403c3b9c8d6 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 5 Apr 2025 23:03:56 +0530 Subject: [PATCH 064/188] Update app.dart --- lib/app.dart | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/app.dart b/lib/app.dart index 3cdbe150a..33411f805 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -139,15 +139,20 @@ class DashApp extends ConsumerWidget { !kIsLinux && !kIsMobile ? const App() : context.isMediumWindow - ? (kIsMobile && !userOnboarded) - ? OnboardingScreen( - onComplete: () async { - await setOnboardingStatusToSharedPrefs( - isOnboardingComplete: true, - ); - ref - .read(userOnboardedProvider.notifier) - .state = true; + ? (kIsMobile + ? FutureBuilder( + future: getOnboardingStatusFromSharedPrefs(), + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + final showOnboarding = + snapshot.data ?? false; + return showOnboarding + ? const MobileDashboard() + : const OnboardingScreen(); + } + return const Center( + child: CircularProgressIndicator()); }, ) : const MobileDashboard() From fdf21fc9a935fa440029f00b1d5ed10145109399 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 6 Apr 2025 02:01:15 +0530 Subject: [PATCH 065/188] Fix mobile app rebuilding issue --- lib/app.dart | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/app.dart b/lib/app.dart index 33411f805..3cdbe150a 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -139,20 +139,15 @@ class DashApp extends ConsumerWidget { !kIsLinux && !kIsMobile ? const App() : context.isMediumWindow - ? (kIsMobile - ? FutureBuilder( - future: getOnboardingStatusFromSharedPrefs(), - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - final showOnboarding = - snapshot.data ?? false; - return showOnboarding - ? const MobileDashboard() - : const OnboardingScreen(); - } - return const Center( - child: CircularProgressIndicator()); + ? (kIsMobile && !userOnboarded) + ? OnboardingScreen( + onComplete: () async { + await setOnboardingStatusToSharedPrefs( + isOnboardingComplete: true, + ); + ref + .read(userOnboardedProvider.notifier) + .state = true; }, ) : const MobileDashboard() From bf61134608dd0678b9598a3e4262ff8cd78eb6e2 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 6 Apr 2025 03:08:35 +0530 Subject: [PATCH 066/188] Refactor JSON Previewer buttons --- lib/widgets/previewer_json.dart | 75 ++------------------------------- 1 file changed, 4 insertions(+), 71 deletions(-) diff --git a/lib/widgets/previewer_json.dart b/lib/widgets/previewer_json.dart index 914df2af5..74d69e91e 100644 --- a/lib/widgets/previewer_json.dart +++ b/lib/widgets/previewer_json.dart @@ -177,75 +177,11 @@ class _JsonPreviewerState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( - mainAxisAlignment: MainAxisAlignment.end, - children: constraints.minWidth > kMinWindowSize.width - ? [ - TextButton( - onPressed: state.areAllExpanded() - ? null - : state.expandAll, - child: const Text( - 'Expand All', - style: kTextStyleButtonSmall, - ), - ), - TextButton( - onPressed: state.areAllCollapsed() - ? null - : state.collapseAll, - child: const Text( - 'Collapse All', - style: kTextStyleButtonSmall, - ), - ), - ] - : [ - IconButton( - tooltip: "Expand All", - color: Theme.of(context).colorScheme.primary, - visualDensity: VisualDensity.compact, - onPressed: state.areAllExpanded() - ? null - : state.expandAll, - icon: const Icon( - Icons.unfold_more, - size: 16, - ), - ), - const SizedBox( - width: 8, - ), - if (state.searchResults.isNotEmpty) - Text(_searchFocusText(), - style: - Theme.of(context).textTheme.bodySmall), - if (state.searchResults.isNotEmpty) - IconButton( - visualDensity: VisualDensity.compact, - onPressed: () { - store.focusPreviousSearchResult(); - _scrollToSearchMatch(); - }, - icon: const Icon(Icons.arrow_drop_up), - ), - if (state.searchResults.isNotEmpty) - IconButton( - visualDensity: VisualDensity.compact, - onPressed: () { - store.focusNextSearchResult(); - _scrollToSearchMatch(); - }, - icon: const Icon(Icons.arrow_drop_down), - ), - ], - ), - ), - ), + mainAxisAlignment: MainAxisAlignment.end, + children: [ ADTextButton( icon: Icons.unfold_more, - showLabel: - (constraints.minWidth > kMinWindowSize.width) && - !kIsMobile, + showLabel: constraints.minWidth > kMinWindowSize.width, label: 'Expand All', labelTextStyle: kTextStyleButtonSmall, onPressed: @@ -253,9 +189,7 @@ class _JsonPreviewerState extends State { ), ADTextButton( icon: Icons.unfold_less, - showLabel: - (constraints.minWidth > kMinWindowSize.width) && - !kIsMobile, + showLabel: constraints.minWidth > kMinWindowSize.width, label: 'Collapse All', labelTextStyle: kTextStyleButtonSmall, onPressed: @@ -263,7 +197,6 @@ class _JsonPreviewerState extends State { ), ], ), - kVSpacer6, Expanded( child: JsonExplorer( nodes: state.displayNodes, From 1d25c393bc2c981f583d3f261e9ff98f948192b0 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 6 Apr 2025 03:40:14 +0530 Subject: [PATCH 067/188] Fixes #644 --- lib/widgets/previewer_json.dart | 63 +++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/lib/widgets/previewer_json.dart b/lib/widgets/previewer_json.dart index 74d69e91e..3f8e2b190 100644 --- a/lib/widgets/previewer_json.dart +++ b/lib/widgets/previewer_json.dart @@ -179,9 +179,65 @@ class _JsonPreviewerState extends State { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + border: Border.all( + color: Theme.of(context) + .colorScheme + .surfaceContainerHighest), + borderRadius: kBorderRadius8, + ), + child: Row( + children: [ + const Padding( + padding: kPh4, + child: Icon( + Icons.search, + size: 16, + ), + ), + Expanded( + child: JsonSearchField( + controller: searchController, + onChanged: (term) => state.search(term), + ), + ), + const SizedBox( + width: 8, + ), + if (state.searchResults.isNotEmpty) + Text(_searchFocusText(), + style: + Theme.of(context).textTheme.bodySmall), + if (state.searchResults.isNotEmpty) + IconButton( + visualDensity: VisualDensity.compact, + onPressed: () { + store.focusPreviousSearchResult(); + _scrollToSearchMatch(); + }, + icon: const Icon(Icons.arrow_drop_up), + ), + if (state.searchResults.isNotEmpty) + IconButton( + visualDensity: VisualDensity.compact, + onPressed: () { + store.focusNextSearchResult(); + _scrollToSearchMatch(); + }, + icon: const Icon(Icons.arrow_drop_down), + ), + ], + ), + ), + ), ADTextButton( icon: Icons.unfold_more, - showLabel: constraints.minWidth > kMinWindowSize.width, + showLabel: + (constraints.minWidth > kMinWindowSize.width) && + !kIsMobile, label: 'Expand All', labelTextStyle: kTextStyleButtonSmall, onPressed: @@ -189,7 +245,9 @@ class _JsonPreviewerState extends State { ), ADTextButton( icon: Icons.unfold_less, - showLabel: constraints.minWidth > kMinWindowSize.width, + showLabel: + (constraints.minWidth > kMinWindowSize.width) && + !kIsMobile, label: 'Collapse All', labelTextStyle: kTextStyleButtonSmall, onPressed: @@ -197,6 +255,7 @@ class _JsonPreviewerState extends State { ), ], ), + kVSpacer6, Expanded( child: JsonExplorer( nodes: state.displayNodes, From 40d27b315968ec5793fa0a413d7ba4fc3b39bfb0 Mon Sep 17 00:00:00 2001 From: Udhay-Adithya Date: Sun, 6 Apr 2025 13:46:00 +0530 Subject: [PATCH 068/188] docs: add missing details from previous PR --- .../gsoc/application_udhay_adithya_dashbot.md | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md index f6019ec38..a3a5b1a4e 100644 --- a/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md +++ b/doc/proposals/2025/gsoc/application_udhay_adithya_dashbot.md @@ -22,7 +22,7 @@ ### 1. Have you worked on or contributed to a FOSS project before? Can you attach repo links or relevant PRs? -I haven't contributed to open-source projects before this, so API Dash marks my first practical experience with open-source contribution. So far, I have made a total of four PRs—three of which have been merged, while the other one is currently under review. Here are the relevant PRs: +I haven't contributed to open-source projects before this, so API Dash marks my first practical experience with open-source contribution. So far, I have made a total of four PRs—three of which have been merged, while the other one got rejected. Here are the relevant PRs: ([#698](https://github.com/foss42/apidash/pull/698)): docs(codegen): add detailed instructions for ureq @@ -77,7 +77,7 @@ Additionally, a **Security & Compliance Advisor** can enhance DashBot’s capabi - **Vulnerability Scanning:** Automatically identifying security risks such as missing authentication, exposed sensitive data, and other vulnerabilities. - **Auto-Remediation:** Offering actionable suggestions to fix issues like CORS misconfigurations or insecure headers. -### **PROJECT TITLE : DashBot** +### **PROJECT TITLE : DashBot and API Authentication** ### **ABSTRACT:** @@ -93,7 +93,8 @@ This proposal seeks to develop DashBot - the AI assistant for API Dash which sup For each of the tasks you are benchmark evaluations will also be done so that it is easier for end users to choose the right backend LLM. Upon successful completion of this project, we will have -a fully function DashBot integrated with API Dash [#621](https://github.com/foss42/apidash/issues/621) +a fully function DashBot integrated with API Dash [#621](https://github.com/foss42/apidash/issues/621) and a new tab “Authorization” in the Home Page of API Dash to handle different API Authentication Methods. + ### **DETAILED DESCRIPTION** @@ -156,6 +157,7 @@ Required dependencies, - [openai_dart](https://pub.dev/packages/openai_dart) - [ollama_dart](https://pub.dev/packages/ollama_dart) - [flutter_riverpod](https://pub.dev/packages/flutter_riverpod) +- [fpdart](https://pub.dev/packages/fpdart) - [fl_charts](https://pub.dev/packages/fl_chart) - [riverpod_annotation](https://pub.dev/packages/riverpod_annotation) - [riverpod_lint](https://pub.dev/packages/riverpod_lint) @@ -183,22 +185,52 @@ lib/ DashBot can be accessed from the home screen of API Dash using a floating action button at the bottom right corner. -![DashBot On Screen](images/dashbot_on_screen.png) -![DashBot Settings](images/dashbot_settings_1.png) -![DashBot Debug](images/dashbot_debug.png) -
dashbot_default_dark dashbot_default_dark
+
+ DashBot Pop-up Window +
+ +![DashBot On Screen](images/dashbot_on_screen.png) +
+ DashBot Home Screen View +
+ +![DashBot Settings](images/dashbot_settings_1.png) +
+ DashBot Settings +
+ +![DashBot Debug](images/dashbot_explain.png) +
+ Access Dashbot Through Context Menu +
+ +![DashBot Debug](images/dashbot_debug.png) +
+ Debug Suggestion by DashBot +
+ +![DashBot Debug](images/dashbot_debug_2.png) +
+ Quick Fix API Errors +
+ +![DashBot Debug](images/api_authentication.png) +
+ Basic Authentication +
+![DashBot Debug](images/api_authentication_1.png) +
+ JWT Bearer Authentication +
**** -

- More design files at Figma -

## **MILESTONES AND DELIVERABLES** @@ -329,7 +361,6 @@ their final mentor evaluation (standard coding period) * **Week 7 (July 14 - July 20)** - Extending support for Dashbot in Android/iOS devices. - - Documentation and benchmarking evaluations are enhanced in the buffer period if no issues arise. - Finishing up Dashbot. **Deliverables:** From 6fa2c4b60e5c00f8c22ecdfd4a3b9531912e4d84 Mon Sep 17 00:00:00 2001 From: Rupamthxt Date: Fri, 8 Nov 2024 02:58:32 +0530 Subject: [PATCH 069/188] Added shortcut key "ctrl + s" for saving projects on the go. --- .../common_widgets/sidebar_save_button.dart | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/lib/screens/common_widgets/sidebar_save_button.dart b/lib/screens/common_widgets/sidebar_save_button.dart index d92b88b02..73a62400f 100644 --- a/lib/screens/common_widgets/sidebar_save_button.dart +++ b/lib/screens/common_widgets/sidebar_save_button.dart @@ -10,21 +10,9 @@ class SaveButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final savingData = ref.watch(saveDataStateProvider); - final hasUnsavedChanges = ref.watch(hasUnsavedChangesProvider); + return TextButton.icon( - onPressed: (savingData || !hasUnsavedChanges) - ? null - : () async { - await saveAndShowDialog(context, onSave: () async { - await ref - .read(collectionStateNotifierProvider.notifier) - .saveData(); - await ref - .read(environmentsStateNotifierProvider.notifier) - .saveEnvironments(); - }); - }, + onPressed: () {saveData(context, ref);} , icon: const Icon( Icons.save, size: 20, @@ -35,4 +23,30 @@ class SaveButton extends ConsumerWidget { ), ); } + + static void saveData(BuildContext context, WidgetRef ref) async { + final savingData = ref.watch(saveDataStateProvider); + final hasUnsavedChanges = ref.watch(hasUnsavedChangesProvider); + final overlayWidget = OverlayWidgetTemplate(context: context); + (savingData || !hasUnsavedChanges) + ? null + :{ + overlayWidget.show( + widget: const SavingOverlay(saveCompleted: false)), + + await ref + .read(collectionStateNotifierProvider.notifier) + .saveData(), + await ref + .read(environmentsStateNotifierProvider.notifier) + .saveEnvironments(), + overlayWidget.hide(), + overlayWidget.show( + widget: const SavingOverlay(saveCompleted: true)), + await Future.delayed(const Duration(seconds: 1)), + overlayWidget.hide(), + }; + } } + + From 101566bad9b4995480988bef1bd515a1e7e6dd6e Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Sun, 10 Nov 2024 18:07:19 +0530 Subject: [PATCH 070/188] update save data --- .../common_widgets/sidebar_save_button.dart | 37 ++++--------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/lib/screens/common_widgets/sidebar_save_button.dart b/lib/screens/common_widgets/sidebar_save_button.dart index 73a62400f..6084651f1 100644 --- a/lib/screens/common_widgets/sidebar_save_button.dart +++ b/lib/screens/common_widgets/sidebar_save_button.dart @@ -3,16 +3,21 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:apidash/consts.dart'; import 'package:apidash/providers/providers.dart'; -import 'package:apidash/utils/utils.dart'; +import '../../common/utils.dart'; class SaveButton extends ConsumerWidget { const SaveButton({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - + final savingData = ref.watch(saveDataStateProvider); + final hasUnsavedChanges = ref.watch(hasUnsavedChangesProvider); return TextButton.icon( - onPressed: () {saveData(context, ref);} , + onPressed: (savingData || !hasUnsavedChanges) + ? null + : () async { + await saveData(context, ref); + }, icon: const Icon( Icons.save, size: 20, @@ -23,30 +28,4 @@ class SaveButton extends ConsumerWidget { ), ); } - - static void saveData(BuildContext context, WidgetRef ref) async { - final savingData = ref.watch(saveDataStateProvider); - final hasUnsavedChanges = ref.watch(hasUnsavedChangesProvider); - final overlayWidget = OverlayWidgetTemplate(context: context); - (savingData || !hasUnsavedChanges) - ? null - :{ - overlayWidget.show( - widget: const SavingOverlay(saveCompleted: false)), - - await ref - .read(collectionStateNotifierProvider.notifier) - .saveData(), - await ref - .read(environmentsStateNotifierProvider.notifier) - .saveEnvironments(), - overlayWidget.hide(), - overlayWidget.show( - widget: const SavingOverlay(saveCompleted: true)), - await Future.delayed(const Duration(seconds: 1)), - overlayWidget.hide(), - }; - } } - - From 9cb1c03ff8fe9086ba60f2e566a85d1b9bb5623d Mon Sep 17 00:00:00 2001 From: Rupamthxt Date: Mon, 11 Nov 2024 18:27:22 +0530 Subject: [PATCH 071/188] Implemented Save Shortcut. --- lib/screens/dashboard.dart | 175 ++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 81 deletions(-) diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index 428ffaebc..832435674 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -17,87 +17,100 @@ class Dashboard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final railIdx = ref.watch(navRailIndexStateProvider); - return Scaffold( - body: SafeArea( - child: Row( - children: [ - Column( - children: [ - SizedBox( - height: kIsMacOS ? 32.0 : 16.0, - width: 64, - ), - Column( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - isSelected: railIdx == 0, - onPressed: () { - ref.read(navRailIndexStateProvider.notifier).state = 0; - }, - icon: const Icon(Icons.auto_awesome_mosaic_outlined), - selectedIcon: const Icon(Icons.auto_awesome_mosaic), - ), - Text( - 'Requests', - style: Theme.of(context).textTheme.labelSmall, - ), - kVSpacer10, - IconButton( - isSelected: railIdx == 1, - onPressed: () { - ref.read(navRailIndexStateProvider.notifier).state = 1; - }, - icon: const Icon(Icons.laptop_windows_outlined), - selectedIcon: const Icon(Icons.laptop_windows), - ), - Text( - 'Variables', - style: Theme.of(context).textTheme.labelSmall, - ), - kVSpacer10, - IconButton( - isSelected: railIdx == 2, - onPressed: () { - ref.read(navRailIndexStateProvider.notifier).state = 2; - }, - icon: const Icon(Icons.history_outlined), - selectedIcon: const Icon(Icons.history_rounded), - ), - Text( - 'History', - style: Theme.of(context).textTheme.labelSmall, - ), - ], - ), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.end, + return Shortcuts( + shortcuts: { + LogicalKeySet(LogicalKeyboardKey.controlLeft, LogicalKeyboardKey.keyS): const SaveIntent(), + }, + child: Actions( + actions: { + SaveIntent: CallbackAction(onInvoke: (intent) => saveData(context, ref)) + }, + child: FocusScope( + autofocus: true, + child: Scaffold( + body: SafeArea( + child: Row( + children: [ + Column( children: [ - Padding( - padding: const EdgeInsets.only(bottom: 16.0), - child: NavbarButton( - railIdx: railIdx, - selectedIcon: Icons.help, - icon: Icons.help_outline, - label: 'About', - showLabel: false, - isCompact: true, - onTap: () { - showAboutAppDialog(context); - }, - ), + SizedBox( + height: kIsMacOS ? 32.0 : 16.0, + width: 64, ), - Padding( - padding: const EdgeInsets.only(bottom: 16.0), - child: NavbarButton( - railIdx: railIdx, - buttonIdx: 3, - selectedIcon: Icons.settings, - icon: Icons.settings_outlined, - label: 'Settings', - showLabel: false, - isCompact: true, + Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + isSelected: railIdx == 0, + onPressed: () { + ref.read(navRailIndexStateProvider.notifier).state = 0; + }, + icon: const Icon(Icons.auto_awesome_mosaic_outlined), + selectedIcon: const Icon(Icons.auto_awesome_mosaic), + ), + Text( + 'Requests', + style: Theme.of(context).textTheme.labelSmall, + ), + kVSpacer10, + IconButton( + isSelected: railIdx == 1, + onPressed: () { + ref.read(navRailIndexStateProvider.notifier).state = 1; + }, + icon: const Icon(Icons.laptop_windows_outlined), + selectedIcon: const Icon(Icons.laptop_windows), + ), + Text( + 'Variables', + style: Theme.of(context).textTheme.labelSmall, + ), + kVSpacer10, + IconButton( + isSelected: railIdx == 2, + onPressed: () { + ref.read(navRailIndexStateProvider.notifier).state = 2; + }, + icon: const Icon(Icons.history_outlined), + selectedIcon: const Icon(Icons.history_rounded), + ), + Text( + 'History', + style: Theme.of(context).textTheme.labelSmall, + ), + ], + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: NavbarButton( + railIdx: railIdx, + selectedIcon: Icons.help, + icon: Icons.help_outline, + label: 'About', + showLabel: false, + isCompact: true, + onTap: () { + showAboutAppDialog(context); + }, + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: NavbarButton( + railIdx: railIdx, + buttonIdx: 3, + selectedIcon: Icons.settings, + icon: Icons.settings_outlined, + label: 'Settings', + showLabel: false, + isCompact: true, + ), + ), + ], ), ), ], @@ -121,8 +134,8 @@ class Dashboard extends ConsumerWidget { SettingsPage(), ], ), - ) - ], + ), + ), ), ), // TODO: Release DashBot From 399a0f4be62600316c544cc93c5465e7767b48d8 Mon Sep 17 00:00:00 2001 From: Rupamthxt Date: Tue, 12 Nov 2024 23:03:29 +0530 Subject: [PATCH 072/188] Update --- lib/screens/dashboard.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index 832435674..44a60f983 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -19,11 +19,11 @@ class Dashboard extends ConsumerWidget { final railIdx = ref.watch(navRailIndexStateProvider); return Shortcuts( shortcuts: { - LogicalKeySet(LogicalKeyboardKey.controlLeft, LogicalKeyboardKey.keyS): const SaveIntent(), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyS): const SaveIntent(), }, child: Actions( actions: { - SaveIntent: CallbackAction(onInvoke: (intent) => saveData(context, ref)) + SaveIntent : CallbackAction(onInvoke: (intent) => saveData(context, ref)) }, child: FocusScope( autofocus: true, From bdc67ac4329a95c0d2b4415c6f089fd2889640a7 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 6 Apr 2025 14:00:33 +0530 Subject: [PATCH 073/188] Update dashboard.dart --- lib/screens/dashboard.dart | 175 +++++++++++++++++-------------------- 1 file changed, 81 insertions(+), 94 deletions(-) diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index 44a60f983..428ffaebc 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -17,100 +17,87 @@ class Dashboard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final railIdx = ref.watch(navRailIndexStateProvider); - return Shortcuts( - shortcuts: { - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyS): const SaveIntent(), - }, - child: Actions( - actions: { - SaveIntent : CallbackAction(onInvoke: (intent) => saveData(context, ref)) - }, - child: FocusScope( - autofocus: true, - child: Scaffold( - body: SafeArea( - child: Row( - children: [ - Column( + return Scaffold( + body: SafeArea( + child: Row( + children: [ + Column( + children: [ + SizedBox( + height: kIsMacOS ? 32.0 : 16.0, + width: 64, + ), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + isSelected: railIdx == 0, + onPressed: () { + ref.read(navRailIndexStateProvider.notifier).state = 0; + }, + icon: const Icon(Icons.auto_awesome_mosaic_outlined), + selectedIcon: const Icon(Icons.auto_awesome_mosaic), + ), + Text( + 'Requests', + style: Theme.of(context).textTheme.labelSmall, + ), + kVSpacer10, + IconButton( + isSelected: railIdx == 1, + onPressed: () { + ref.read(navRailIndexStateProvider.notifier).state = 1; + }, + icon: const Icon(Icons.laptop_windows_outlined), + selectedIcon: const Icon(Icons.laptop_windows), + ), + Text( + 'Variables', + style: Theme.of(context).textTheme.labelSmall, + ), + kVSpacer10, + IconButton( + isSelected: railIdx == 2, + onPressed: () { + ref.read(navRailIndexStateProvider.notifier).state = 2; + }, + icon: const Icon(Icons.history_outlined), + selectedIcon: const Icon(Icons.history_rounded), + ), + Text( + 'History', + style: Theme.of(context).textTheme.labelSmall, + ), + ], + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, children: [ - SizedBox( - height: kIsMacOS ? 32.0 : 16.0, - width: 64, - ), - Column( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - isSelected: railIdx == 0, - onPressed: () { - ref.read(navRailIndexStateProvider.notifier).state = 0; - }, - icon: const Icon(Icons.auto_awesome_mosaic_outlined), - selectedIcon: const Icon(Icons.auto_awesome_mosaic), - ), - Text( - 'Requests', - style: Theme.of(context).textTheme.labelSmall, - ), - kVSpacer10, - IconButton( - isSelected: railIdx == 1, - onPressed: () { - ref.read(navRailIndexStateProvider.notifier).state = 1; - }, - icon: const Icon(Icons.laptop_windows_outlined), - selectedIcon: const Icon(Icons.laptop_windows), - ), - Text( - 'Variables', - style: Theme.of(context).textTheme.labelSmall, - ), - kVSpacer10, - IconButton( - isSelected: railIdx == 2, - onPressed: () { - ref.read(navRailIndexStateProvider.notifier).state = 2; - }, - icon: const Icon(Icons.history_outlined), - selectedIcon: const Icon(Icons.history_rounded), - ), - Text( - 'History', - style: Theme.of(context).textTheme.labelSmall, - ), - ], + Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: NavbarButton( + railIdx: railIdx, + selectedIcon: Icons.help, + icon: Icons.help_outline, + label: 'About', + showLabel: false, + isCompact: true, + onTap: () { + showAboutAppDialog(context); + }, + ), ), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 16.0), - child: NavbarButton( - railIdx: railIdx, - selectedIcon: Icons.help, - icon: Icons.help_outline, - label: 'About', - showLabel: false, - isCompact: true, - onTap: () { - showAboutAppDialog(context); - }, - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 16.0), - child: NavbarButton( - railIdx: railIdx, - buttonIdx: 3, - selectedIcon: Icons.settings, - icon: Icons.settings_outlined, - label: 'Settings', - showLabel: false, - isCompact: true, - ), - ), - ], + Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: NavbarButton( + railIdx: railIdx, + buttonIdx: 3, + selectedIcon: Icons.settings, + icon: Icons.settings_outlined, + label: 'Settings', + showLabel: false, + isCompact: true, ), ), ], @@ -134,8 +121,8 @@ class Dashboard extends ConsumerWidget { SettingsPage(), ], ), - ), - ), + ) + ], ), ), // TODO: Release DashBot From ace377d3c15de9e11ed0c07c74c0250ad22b4d96 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 6 Apr 2025 15:43:57 +0530 Subject: [PATCH 074/188] update --- lib/screens/common_widgets/sidebar_save_button.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/screens/common_widgets/sidebar_save_button.dart b/lib/screens/common_widgets/sidebar_save_button.dart index 6084651f1..d92b88b02 100644 --- a/lib/screens/common_widgets/sidebar_save_button.dart +++ b/lib/screens/common_widgets/sidebar_save_button.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:apidash/consts.dart'; import 'package:apidash/providers/providers.dart'; -import '../../common/utils.dart'; +import 'package:apidash/utils/utils.dart'; class SaveButton extends ConsumerWidget { const SaveButton({super.key}); @@ -16,7 +16,14 @@ class SaveButton extends ConsumerWidget { onPressed: (savingData || !hasUnsavedChanges) ? null : () async { - await saveData(context, ref); + await saveAndShowDialog(context, onSave: () async { + await ref + .read(collectionStateNotifierProvider.notifier) + .saveData(); + await ref + .read(environmentsStateNotifierProvider.notifier) + .saveEnvironments(); + }); }, icon: const Icon( Icons.save, From 4e77af7ea0788384135a247ca62cd2b8cad28367 Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Mon, 7 Apr 2025 01:20:59 +0530 Subject: [PATCH 075/188] Update Application_K_Govind_API_Support.md --- .../2025/gsoc/Application_K_Govind_API_Support.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index ed8b35bda..b8daccb85 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -134,7 +134,7 @@ Architecture is as shown below: ``` -- **Technologies & Tools:** The approach uses the package web_socket_channel(^3.0.1) +- **Technologies & Tools:** The approach uses the package web_socket_channel(^3.0.1).(https://fluttergems.dev/packages/web_socket_channel/).Supports all platforms. - **Expected Outcomes:** A clean ui with maximum smoothness satisfying above mentioned solutions. - **Linked PR for POC:** https://github.com/foss42/apidash/pull/555 (The PR is in no way the final product but simply to show the code structure and my approach) ![Alt text](./images/websocket(1).png) @@ -337,11 +337,13 @@ Service level change:- We would make key value pairs as string . This is encoded ### File support(Issue #352):- Sending Files through octect-stream content type. -UI Changes: Make a combination of drag and droppable and select file ui as a new tab. Make a progression bar to show the uploading progress. +UI Changes: Make a combination of drag and droppable and select file ui as a new tab. Make a progression bar to show the conversion to bytes progress. +To acheive the droppable interface we can use desktop_drop(https://fluttergems.dev/packages/desktop_drop/)(supports windows,linux,macos,android,web).This works along with already existing file_selector.But it doesn't support IOS. +To support IOS we would have to use super_drag_and_drop(https://fluttergems.dev/packages/super_drag_and_drop/) . Currently I have only been able to test the feauture in windows ,android using desktop_drop. Model changes: Add another content type application/octet-stream . -Service changes:We would first stream the file as bytes , add it into the body . +Service changes:We pick the file using file_selector. And adds the filepath into the body. Then stream the content to bytes whenever we need to send the file. Showing and handling error appropriately if file is not present or corrupted. Put Content-Type header as application/octet-stream and send the request. From ee202a155e582fa699446cc30c4f7fc378da4136 Mon Sep 17 00:00:00 2001 From: Loghadhith Date: Sun, 6 Apr 2025 22:38:17 +0530 Subject: [PATCH 076/188] [ADD]: GSOC Proposal --- .../2025/gsoc/application_Loghadhith_Dashbot.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md b/doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md index e7f05c7df..5b1ca9c7f 100644 --- a/doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md +++ b/doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md @@ -1,5 +1,5 @@ # GSoC Proposal -## DashBot - AI-Powered API Assistant +## DashBot - AI-Powered API Assistant for API Dash Making LLM powered insights as plugins for each different api's ## About @@ -73,11 +73,9 @@ DashBot will include: The entire system will be modular and extensible, ensuring future contributions and new features can plug into the core easily. --- -### 4. Architecture Diagram -![1743958514](https://github.com/user-attachments/assets/ef3d1d7c-5c58-4183-864a-8711bcd1f62e) ---- -### 5. Weekly Timeline (12 Weeks) + +### 4. Weekly Timeline (12 Weeks) | Week | Activities | |------|------------| @@ -175,7 +173,7 @@ The entire system will be modular and extensible, ensuring future contributions --- -## 6.Techstack +## 5.Techstack #### 1. **NLP Module (Intent + Entity Extraction)** From ef1bbb2cb09692edfd471e62550c3ed5b913858a Mon Sep 17 00:00:00 2001 From: Loghadhith <120036835+Loghadhith@users.noreply.github.com> Date: Sun, 6 Apr 2025 22:44:14 +0530 Subject: [PATCH 077/188] Update application_Loghadhith_Dashbot.md --- doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md b/doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md index 5b1ca9c7f..cfa88e796 100644 --- a/doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md +++ b/doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md @@ -72,10 +72,12 @@ DashBot will include: The entire system will be modular and extensible, ensuring future contributions and new features can plug into the core easily. +--- +### 4. Architecture Diagram +![1743958514](https://github.com/user-attachments/assets/ef3d1d7c-5c58-4183-864a-8711bcd1f62e) --- - -### 4. Weekly Timeline (12 Weeks) +### 5. Weekly Timeline (12 Weeks) | Week | Activities | |------|------------| @@ -173,7 +175,7 @@ The entire system will be modular and extensible, ensuring future contributions --- -## 5.Techstack +## 6.Techstack #### 1. **NLP Module (Intent + Entity Extraction)** From 529cc93973efaea5f8f64df16ad55111c0db7b34 Mon Sep 17 00:00:00 2001 From: Loghadhith <120036835+Loghadhith@users.noreply.github.com> Date: Sun, 6 Apr 2025 22:46:50 +0530 Subject: [PATCH 078/188] Update application_Loghadhith_Dashbot.md --- doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md b/doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md index cfa88e796..e7f05c7df 100644 --- a/doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md +++ b/doc/proposals/2025/gsoc/application_Loghadhith_Dashbot.md @@ -1,5 +1,5 @@ # GSoC Proposal -## DashBot - AI-Powered API Assistant for API Dash Making LLM powered insights as plugins for each different api's +## DashBot - AI-Powered API Assistant ## About From bd1b8fed37fd99471cbfd022ae5e9d4d991efb9b Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:48:40 +0530 Subject: [PATCH 079/188] feat: add stress test config model --- .../stress_test/stress_test_config.dart | 20 ++ .../stress_test_config.freezed.dart | 309 ++++++++++++++++++ .../stress_test/stress_test_config.g.dart | 35 ++ 3 files changed, 364 insertions(+) create mode 100644 lib/models/stress_test/stress_test_config.dart create mode 100644 lib/models/stress_test/stress_test_config.freezed.dart create mode 100644 lib/models/stress_test/stress_test_config.g.dart diff --git a/lib/models/stress_test/stress_test_config.dart b/lib/models/stress_test/stress_test_config.dart new file mode 100644 index 000000000..ff30a0132 --- /dev/null +++ b/lib/models/stress_test/stress_test_config.dart @@ -0,0 +1,20 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:apidash_core/apidash_core.dart'; + +part 'stress_test_config.freezed.dart'; +part 'stress_test_config.g.dart'; + +@freezed +class StressTestConfig with _$StressTestConfig { + const factory StressTestConfig({ + required String url, + required String method, + Map? headers, + dynamic body, + required int concurrentRequests, + Duration? timeout, + @Default(false) bool useIsolates, + }) = _StressTestConfig; + + factory StressTestConfig.fromJson(Map json) => _$StressTestConfigFromJson(json); +} diff --git a/lib/models/stress_test/stress_test_config.freezed.dart b/lib/models/stress_test/stress_test_config.freezed.dart new file mode 100644 index 000000000..2671a5dfe --- /dev/null +++ b/lib/models/stress_test/stress_test_config.freezed.dart @@ -0,0 +1,309 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'stress_test_config.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +StressTestConfig _$StressTestConfigFromJson(Map json) { + return _StressTestConfig.fromJson(json); +} + +/// @nodoc +mixin _$StressTestConfig { + String get url => throw _privateConstructorUsedError; + String get method => throw _privateConstructorUsedError; + Map? get headers => throw _privateConstructorUsedError; + dynamic get body => throw _privateConstructorUsedError; + int get concurrentRequests => throw _privateConstructorUsedError; + Duration? get timeout => throw _privateConstructorUsedError; + bool get useIsolates => throw _privateConstructorUsedError; + + /// Serializes this StressTestConfig to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $StressTestConfigCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $StressTestConfigCopyWith<$Res> { + factory $StressTestConfigCopyWith( + StressTestConfig value, $Res Function(StressTestConfig) then) = + _$StressTestConfigCopyWithImpl<$Res, StressTestConfig>; + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + int concurrentRequests, + Duration? timeout, + bool useIsolates}); +} + +/// @nodoc +class _$StressTestConfigCopyWithImpl<$Res, $Val extends StressTestConfig> + implements $StressTestConfigCopyWith<$Res> { + _$StressTestConfigCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? concurrentRequests = null, + Object? timeout = freezed, + Object? useIsolates = null, + }) { + return _then(_value.copyWith( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value.headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + concurrentRequests: null == concurrentRequests + ? _value.concurrentRequests + : concurrentRequests // ignore: cast_nullable_to_non_nullable + as int, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + useIsolates: null == useIsolates + ? _value.useIsolates + : useIsolates // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$StressTestConfigImplCopyWith<$Res> + implements $StressTestConfigCopyWith<$Res> { + factory _$$StressTestConfigImplCopyWith(_$StressTestConfigImpl value, + $Res Function(_$StressTestConfigImpl) then) = + __$$StressTestConfigImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + int concurrentRequests, + Duration? timeout, + bool useIsolates}); +} + +/// @nodoc +class __$$StressTestConfigImplCopyWithImpl<$Res> + extends _$StressTestConfigCopyWithImpl<$Res, _$StressTestConfigImpl> + implements _$$StressTestConfigImplCopyWith<$Res> { + __$$StressTestConfigImplCopyWithImpl(_$StressTestConfigImpl _value, + $Res Function(_$StressTestConfigImpl) _then) + : super(_value, _then); + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? concurrentRequests = null, + Object? timeout = freezed, + Object? useIsolates = null, + }) { + return _then(_$StressTestConfigImpl( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value._headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + concurrentRequests: null == concurrentRequests + ? _value.concurrentRequests + : concurrentRequests // ignore: cast_nullable_to_non_nullable + as int, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + useIsolates: null == useIsolates + ? _value.useIsolates + : useIsolates // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$StressTestConfigImpl implements _StressTestConfig { + const _$StressTestConfigImpl( + {required this.url, + required this.method, + final Map? headers, + this.body, + required this.concurrentRequests, + this.timeout, + this.useIsolates = false}) + : _headers = headers; + + factory _$StressTestConfigImpl.fromJson(Map json) => + _$$StressTestConfigImplFromJson(json); + + @override + final String url; + @override + final String method; + final Map? _headers; + @override + Map? get headers { + final value = _headers; + if (value == null) return null; + if (_headers is EqualUnmodifiableMapView) return _headers; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); + } + + @override + final dynamic body; + @override + final int concurrentRequests; + @override + final Duration? timeout; + @override + @JsonKey() + final bool useIsolates; + + @override + String toString() { + return 'StressTestConfig(url: $url, method: $method, headers: $headers, body: $body, concurrentRequests: $concurrentRequests, timeout: $timeout, useIsolates: $useIsolates)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$StressTestConfigImpl && + (identical(other.url, url) || other.url == url) && + (identical(other.method, method) || other.method == method) && + const DeepCollectionEquality().equals(other._headers, _headers) && + const DeepCollectionEquality().equals(other.body, body) && + (identical(other.concurrentRequests, concurrentRequests) || + other.concurrentRequests == concurrentRequests) && + (identical(other.timeout, timeout) || other.timeout == timeout) && + (identical(other.useIsolates, useIsolates) || + other.useIsolates == useIsolates)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + url, + method, + const DeepCollectionEquality().hash(_headers), + const DeepCollectionEquality().hash(body), + concurrentRequests, + timeout, + useIsolates); + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => + __$$StressTestConfigImplCopyWithImpl<_$StressTestConfigImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$StressTestConfigImplToJson( + this, + ); + } +} + +abstract class _StressTestConfig implements StressTestConfig { + const factory _StressTestConfig( + {required final String url, + required final String method, + final Map? headers, + final dynamic body, + required final int concurrentRequests, + final Duration? timeout, + final bool useIsolates}) = _$StressTestConfigImpl; + + factory _StressTestConfig.fromJson(Map json) = + _$StressTestConfigImpl.fromJson; + + @override + String get url; + @override + String get method; + @override + Map? get headers; + @override + dynamic get body; + @override + int get concurrentRequests; + @override + Duration? get timeout; + @override + bool get useIsolates; + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/stress_test_config.g.dart b/lib/models/stress_test/stress_test_config.g.dart new file mode 100644 index 000000000..6193507c3 --- /dev/null +++ b/lib/models/stress_test/stress_test_config.g.dart @@ -0,0 +1,35 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stress_test_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$StressTestConfigImpl _$$StressTestConfigImplFromJson( + Map json) => + _$StressTestConfigImpl( + url: json['url'] as String, + method: json['method'] as String, + headers: (json['headers'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + body: json['body'], + concurrentRequests: (json['concurrentRequests'] as num).toInt(), + timeout: json['timeout'] == null + ? null + : Duration(microseconds: (json['timeout'] as num).toInt()), + useIsolates: json['useIsolates'] as bool? ?? false, + ); + +Map _$$StressTestConfigImplToJson( + _$StressTestConfigImpl instance) => + { + 'url': instance.url, + 'method': instance.method, + 'headers': instance.headers, + 'body': instance.body, + 'concurrentRequests': instance.concurrentRequests, + 'timeout': instance.timeout?.inMicroseconds, + 'useIsolates': instance.useIsolates, + }; From 27574f8d6b850d6aafa6718d97e2d5f2db1d53a9 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:49:38 +0530 Subject: [PATCH 080/188] feat: add api request result model --- .../stress_test/api_request_result.dart | 16 ++ .../api_request_result.freezed.dart | 224 ++++++++++++++++++ .../stress_test/api_request_result.g.dart | 25 ++ 3 files changed, 265 insertions(+) create mode 100644 lib/models/stress_test/api_request_result.dart create mode 100644 lib/models/stress_test/api_request_result.freezed.dart create mode 100644 lib/models/stress_test/api_request_result.g.dart diff --git a/lib/models/stress_test/api_request_result.dart b/lib/models/stress_test/api_request_result.dart new file mode 100644 index 000000000..2cbf40251 --- /dev/null +++ b/lib/models/stress_test/api_request_result.dart @@ -0,0 +1,16 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'api_request_result.freezed.dart'; +part 'api_request_result.g.dart'; + +@freezed +class ApiRequestResult with _$ApiRequestResult { + const factory ApiRequestResult({ + required int statusCode, + required String body, + required Duration duration, + String? error, + }) = _ApiRequestResult; + + factory ApiRequestResult.fromJson(Map json) => _$ApiRequestResultFromJson(json); +} diff --git a/lib/models/stress_test/api_request_result.freezed.dart b/lib/models/stress_test/api_request_result.freezed.dart new file mode 100644 index 000000000..6bdee80a3 --- /dev/null +++ b/lib/models/stress_test/api_request_result.freezed.dart @@ -0,0 +1,224 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'api_request_result.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +ApiRequestResult _$ApiRequestResultFromJson(Map json) { + return _ApiRequestResult.fromJson(json); +} + +/// @nodoc +mixin _$ApiRequestResult { + int get statusCode => throw _privateConstructorUsedError; + String get body => throw _privateConstructorUsedError; + Duration get duration => throw _privateConstructorUsedError; + String? get error => throw _privateConstructorUsedError; + + /// Serializes this ApiRequestResult to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $ApiRequestResultCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ApiRequestResultCopyWith<$Res> { + factory $ApiRequestResultCopyWith( + ApiRequestResult value, $Res Function(ApiRequestResult) then) = + _$ApiRequestResultCopyWithImpl<$Res, ApiRequestResult>; + @useResult + $Res call({int statusCode, String body, Duration duration, String? error}); +} + +/// @nodoc +class _$ApiRequestResultCopyWithImpl<$Res, $Val extends ApiRequestResult> + implements $ApiRequestResultCopyWith<$Res> { + _$ApiRequestResultCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? statusCode = null, + Object? body = null, + Object? duration = null, + Object? error = freezed, + }) { + return _then(_value.copyWith( + statusCode: null == statusCode + ? _value.statusCode + : statusCode // ignore: cast_nullable_to_non_nullable + as int, + body: null == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as String, + duration: null == duration + ? _value.duration + : duration // ignore: cast_nullable_to_non_nullable + as Duration, + error: freezed == error + ? _value.error + : error // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$ApiRequestResultImplCopyWith<$Res> + implements $ApiRequestResultCopyWith<$Res> { + factory _$$ApiRequestResultImplCopyWith(_$ApiRequestResultImpl value, + $Res Function(_$ApiRequestResultImpl) then) = + __$$ApiRequestResultImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({int statusCode, String body, Duration duration, String? error}); +} + +/// @nodoc +class __$$ApiRequestResultImplCopyWithImpl<$Res> + extends _$ApiRequestResultCopyWithImpl<$Res, _$ApiRequestResultImpl> + implements _$$ApiRequestResultImplCopyWith<$Res> { + __$$ApiRequestResultImplCopyWithImpl(_$ApiRequestResultImpl _value, + $Res Function(_$ApiRequestResultImpl) _then) + : super(_value, _then); + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? statusCode = null, + Object? body = null, + Object? duration = null, + Object? error = freezed, + }) { + return _then(_$ApiRequestResultImpl( + statusCode: null == statusCode + ? _value.statusCode + : statusCode // ignore: cast_nullable_to_non_nullable + as int, + body: null == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as String, + duration: null == duration + ? _value.duration + : duration // ignore: cast_nullable_to_non_nullable + as Duration, + error: freezed == error + ? _value.error + : error // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ApiRequestResultImpl implements _ApiRequestResult { + const _$ApiRequestResultImpl( + {required this.statusCode, + required this.body, + required this.duration, + this.error}); + + factory _$ApiRequestResultImpl.fromJson(Map json) => + _$$ApiRequestResultImplFromJson(json); + + @override + final int statusCode; + @override + final String body; + @override + final Duration duration; + @override + final String? error; + + @override + String toString() { + return 'ApiRequestResult(statusCode: $statusCode, body: $body, duration: $duration, error: $error)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ApiRequestResultImpl && + (identical(other.statusCode, statusCode) || + other.statusCode == statusCode) && + (identical(other.body, body) || other.body == body) && + (identical(other.duration, duration) || + other.duration == duration) && + (identical(other.error, error) || other.error == error)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => + Object.hash(runtimeType, statusCode, body, duration, error); + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => + __$$ApiRequestResultImplCopyWithImpl<_$ApiRequestResultImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$ApiRequestResultImplToJson( + this, + ); + } +} + +abstract class _ApiRequestResult implements ApiRequestResult { + const factory _ApiRequestResult( + {required final int statusCode, + required final String body, + required final Duration duration, + final String? error}) = _$ApiRequestResultImpl; + + factory _ApiRequestResult.fromJson(Map json) = + _$ApiRequestResultImpl.fromJson; + + @override + int get statusCode; + @override + String get body; + @override + Duration get duration; + @override + String? get error; + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/api_request_result.g.dart b/lib/models/stress_test/api_request_result.g.dart new file mode 100644 index 000000000..4182a8e58 --- /dev/null +++ b/lib/models/stress_test/api_request_result.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'api_request_result.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$ApiRequestResultImpl _$$ApiRequestResultImplFromJson( + Map json) => + _$ApiRequestResultImpl( + statusCode: (json['statusCode'] as num).toInt(), + body: json['body'] as String, + duration: Duration(microseconds: (json['duration'] as num).toInt()), + error: json['error'] as String?, + ); + +Map _$$ApiRequestResultImplToJson( + _$ApiRequestResultImpl instance) => + { + 'statusCode': instance.statusCode, + 'body': instance.body, + 'duration': instance.duration.inMicroseconds, + 'error': instance.error, + }; From 530d97932788440c36636ce70b442dfbaa3dc943 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:50:18 +0530 Subject: [PATCH 081/188] feat: add isolate model for stress test --- lib/models/stress_test/isolate_message.dart | 17 ++ .../stress_test/isolate_message.freezed.dart | 264 ++++++++++++++++++ lib/models/stress_test/isolate_message.g.dart | 30 ++ 3 files changed, 311 insertions(+) create mode 100644 lib/models/stress_test/isolate_message.dart create mode 100644 lib/models/stress_test/isolate_message.freezed.dart create mode 100644 lib/models/stress_test/isolate_message.g.dart diff --git a/lib/models/stress_test/isolate_message.dart b/lib/models/stress_test/isolate_message.dart new file mode 100644 index 000000000..61bc33121 --- /dev/null +++ b/lib/models/stress_test/isolate_message.dart @@ -0,0 +1,17 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'isolate_message.freezed.dart'; +part 'isolate_message.g.dart'; + +@freezed +class IsolateMessage with _$IsolateMessage { + const factory IsolateMessage({ + required String url, + required String method, + Map? headers, + dynamic body, + Duration? timeout, + }) = _IsolateMessage; + + factory IsolateMessage.fromJson(Map json) => _$IsolateMessageFromJson(json); +} diff --git a/lib/models/stress_test/isolate_message.freezed.dart b/lib/models/stress_test/isolate_message.freezed.dart new file mode 100644 index 000000000..c3abc6857 --- /dev/null +++ b/lib/models/stress_test/isolate_message.freezed.dart @@ -0,0 +1,264 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'isolate_message.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +IsolateMessage _$IsolateMessageFromJson(Map json) { + return _IsolateMessage.fromJson(json); +} + +/// @nodoc +mixin _$IsolateMessage { + String get url => throw _privateConstructorUsedError; + String get method => throw _privateConstructorUsedError; + Map? get headers => throw _privateConstructorUsedError; + dynamic get body => throw _privateConstructorUsedError; + Duration? get timeout => throw _privateConstructorUsedError; + + /// Serializes this IsolateMessage to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $IsolateMessageCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $IsolateMessageCopyWith<$Res> { + factory $IsolateMessageCopyWith( + IsolateMessage value, $Res Function(IsolateMessage) then) = + _$IsolateMessageCopyWithImpl<$Res, IsolateMessage>; + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + Duration? timeout}); +} + +/// @nodoc +class _$IsolateMessageCopyWithImpl<$Res, $Val extends IsolateMessage> + implements $IsolateMessageCopyWith<$Res> { + _$IsolateMessageCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? timeout = freezed, + }) { + return _then(_value.copyWith( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value.headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$IsolateMessageImplCopyWith<$Res> + implements $IsolateMessageCopyWith<$Res> { + factory _$$IsolateMessageImplCopyWith(_$IsolateMessageImpl value, + $Res Function(_$IsolateMessageImpl) then) = + __$$IsolateMessageImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + Duration? timeout}); +} + +/// @nodoc +class __$$IsolateMessageImplCopyWithImpl<$Res> + extends _$IsolateMessageCopyWithImpl<$Res, _$IsolateMessageImpl> + implements _$$IsolateMessageImplCopyWith<$Res> { + __$$IsolateMessageImplCopyWithImpl( + _$IsolateMessageImpl _value, $Res Function(_$IsolateMessageImpl) _then) + : super(_value, _then); + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? timeout = freezed, + }) { + return _then(_$IsolateMessageImpl( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value._headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$IsolateMessageImpl implements _IsolateMessage { + const _$IsolateMessageImpl( + {required this.url, + required this.method, + final Map? headers, + this.body, + this.timeout}) + : _headers = headers; + + factory _$IsolateMessageImpl.fromJson(Map json) => + _$$IsolateMessageImplFromJson(json); + + @override + final String url; + @override + final String method; + final Map? _headers; + @override + Map? get headers { + final value = _headers; + if (value == null) return null; + if (_headers is EqualUnmodifiableMapView) return _headers; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); + } + + @override + final dynamic body; + @override + final Duration? timeout; + + @override + String toString() { + return 'IsolateMessage(url: $url, method: $method, headers: $headers, body: $body, timeout: $timeout)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$IsolateMessageImpl && + (identical(other.url, url) || other.url == url) && + (identical(other.method, method) || other.method == method) && + const DeepCollectionEquality().equals(other._headers, _headers) && + const DeepCollectionEquality().equals(other.body, body) && + (identical(other.timeout, timeout) || other.timeout == timeout)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + url, + method, + const DeepCollectionEquality().hash(_headers), + const DeepCollectionEquality().hash(body), + timeout); + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => + __$$IsolateMessageImplCopyWithImpl<_$IsolateMessageImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$IsolateMessageImplToJson( + this, + ); + } +} + +abstract class _IsolateMessage implements IsolateMessage { + const factory _IsolateMessage( + {required final String url, + required final String method, + final Map? headers, + final dynamic body, + final Duration? timeout}) = _$IsolateMessageImpl; + + factory _IsolateMessage.fromJson(Map json) = + _$IsolateMessageImpl.fromJson; + + @override + String get url; + @override + String get method; + @override + Map? get headers; + @override + dynamic get body; + @override + Duration? get timeout; + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/isolate_message.g.dart b/lib/models/stress_test/isolate_message.g.dart new file mode 100644 index 000000000..e5f403ea1 --- /dev/null +++ b/lib/models/stress_test/isolate_message.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'isolate_message.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$IsolateMessageImpl _$$IsolateMessageImplFromJson(Map json) => + _$IsolateMessageImpl( + url: json['url'] as String, + method: json['method'] as String, + headers: (json['headers'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + body: json['body'], + timeout: json['timeout'] == null + ? null + : Duration(microseconds: (json['timeout'] as num).toInt()), + ); + +Map _$$IsolateMessageImplToJson( + _$IsolateMessageImpl instance) => + { + 'url': instance.url, + 'method': instance.method, + 'headers': instance.headers, + 'body': instance.body, + 'timeout': instance.timeout?.inMicroseconds, + }; From 8f223865a7f53fdeb67bac85a820889e3db40a84 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:51:01 +0530 Subject: [PATCH 082/188] feat: add stress test summer model --- .../stress_test/stress_test_summary.dart | 19 ++ .../stress_test_summary.freezed.dart | 266 ++++++++++++++++++ .../stress_test/stress_test_summary.g.dart | 30 ++ 3 files changed, 315 insertions(+) create mode 100644 lib/models/stress_test/stress_test_summary.dart create mode 100644 lib/models/stress_test/stress_test_summary.freezed.dart create mode 100644 lib/models/stress_test/stress_test_summary.g.dart diff --git a/lib/models/stress_test/stress_test_summary.dart b/lib/models/stress_test/stress_test_summary.dart new file mode 100644 index 000000000..3beaa3a18 --- /dev/null +++ b/lib/models/stress_test/stress_test_summary.dart @@ -0,0 +1,19 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +import 'package:apidash/models/stress_test/api_request_result.dart'; + +part 'stress_test_summary.freezed.dart'; +part 'stress_test_summary.g.dart'; + +@freezed +class StressTestSummary with _$StressTestSummary { + const factory StressTestSummary({ + required List results, + required Duration totalDuration, + required double avgResponseTime, + required int successCount, + required int failureCount, + }) = _StressTestSummary; + + factory StressTestSummary.fromJson(Map json) => _$StressTestSummaryFromJson(json); +} diff --git a/lib/models/stress_test/stress_test_summary.freezed.dart b/lib/models/stress_test/stress_test_summary.freezed.dart new file mode 100644 index 000000000..b5fd37df1 --- /dev/null +++ b/lib/models/stress_test/stress_test_summary.freezed.dart @@ -0,0 +1,266 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'stress_test_summary.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +StressTestSummary _$StressTestSummaryFromJson(Map json) { + return _StressTestSummary.fromJson(json); +} + +/// @nodoc +mixin _$StressTestSummary { + List get results => throw _privateConstructorUsedError; + Duration get totalDuration => throw _privateConstructorUsedError; + double get avgResponseTime => throw _privateConstructorUsedError; + int get successCount => throw _privateConstructorUsedError; + int get failureCount => throw _privateConstructorUsedError; + + /// Serializes this StressTestSummary to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $StressTestSummaryCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $StressTestSummaryCopyWith<$Res> { + factory $StressTestSummaryCopyWith( + StressTestSummary value, $Res Function(StressTestSummary) then) = + _$StressTestSummaryCopyWithImpl<$Res, StressTestSummary>; + @useResult + $Res call( + {List results, + Duration totalDuration, + double avgResponseTime, + int successCount, + int failureCount}); +} + +/// @nodoc +class _$StressTestSummaryCopyWithImpl<$Res, $Val extends StressTestSummary> + implements $StressTestSummaryCopyWith<$Res> { + _$StressTestSummaryCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? results = null, + Object? totalDuration = null, + Object? avgResponseTime = null, + Object? successCount = null, + Object? failureCount = null, + }) { + return _then(_value.copyWith( + results: null == results + ? _value.results + : results // ignore: cast_nullable_to_non_nullable + as List, + totalDuration: null == totalDuration + ? _value.totalDuration + : totalDuration // ignore: cast_nullable_to_non_nullable + as Duration, + avgResponseTime: null == avgResponseTime + ? _value.avgResponseTime + : avgResponseTime // ignore: cast_nullable_to_non_nullable + as double, + successCount: null == successCount + ? _value.successCount + : successCount // ignore: cast_nullable_to_non_nullable + as int, + failureCount: null == failureCount + ? _value.failureCount + : failureCount // ignore: cast_nullable_to_non_nullable + as int, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$StressTestSummaryImplCopyWith<$Res> + implements $StressTestSummaryCopyWith<$Res> { + factory _$$StressTestSummaryImplCopyWith(_$StressTestSummaryImpl value, + $Res Function(_$StressTestSummaryImpl) then) = + __$$StressTestSummaryImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {List results, + Duration totalDuration, + double avgResponseTime, + int successCount, + int failureCount}); +} + +/// @nodoc +class __$$StressTestSummaryImplCopyWithImpl<$Res> + extends _$StressTestSummaryCopyWithImpl<$Res, _$StressTestSummaryImpl> + implements _$$StressTestSummaryImplCopyWith<$Res> { + __$$StressTestSummaryImplCopyWithImpl(_$StressTestSummaryImpl _value, + $Res Function(_$StressTestSummaryImpl) _then) + : super(_value, _then); + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? results = null, + Object? totalDuration = null, + Object? avgResponseTime = null, + Object? successCount = null, + Object? failureCount = null, + }) { + return _then(_$StressTestSummaryImpl( + results: null == results + ? _value._results + : results // ignore: cast_nullable_to_non_nullable + as List, + totalDuration: null == totalDuration + ? _value.totalDuration + : totalDuration // ignore: cast_nullable_to_non_nullable + as Duration, + avgResponseTime: null == avgResponseTime + ? _value.avgResponseTime + : avgResponseTime // ignore: cast_nullable_to_non_nullable + as double, + successCount: null == successCount + ? _value.successCount + : successCount // ignore: cast_nullable_to_non_nullable + as int, + failureCount: null == failureCount + ? _value.failureCount + : failureCount // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$StressTestSummaryImpl implements _StressTestSummary { + const _$StressTestSummaryImpl( + {required final List results, + required this.totalDuration, + required this.avgResponseTime, + required this.successCount, + required this.failureCount}) + : _results = results; + + factory _$StressTestSummaryImpl.fromJson(Map json) => + _$$StressTestSummaryImplFromJson(json); + + final List _results; + @override + List get results { + if (_results is EqualUnmodifiableListView) return _results; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_results); + } + + @override + final Duration totalDuration; + @override + final double avgResponseTime; + @override + final int successCount; + @override + final int failureCount; + + @override + String toString() { + return 'StressTestSummary(results: $results, totalDuration: $totalDuration, avgResponseTime: $avgResponseTime, successCount: $successCount, failureCount: $failureCount)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$StressTestSummaryImpl && + const DeepCollectionEquality().equals(other._results, _results) && + (identical(other.totalDuration, totalDuration) || + other.totalDuration == totalDuration) && + (identical(other.avgResponseTime, avgResponseTime) || + other.avgResponseTime == avgResponseTime) && + (identical(other.successCount, successCount) || + other.successCount == successCount) && + (identical(other.failureCount, failureCount) || + other.failureCount == failureCount)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(_results), + totalDuration, + avgResponseTime, + successCount, + failureCount); + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => + __$$StressTestSummaryImplCopyWithImpl<_$StressTestSummaryImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$StressTestSummaryImplToJson( + this, + ); + } +} + +abstract class _StressTestSummary implements StressTestSummary { + const factory _StressTestSummary( + {required final List results, + required final Duration totalDuration, + required final double avgResponseTime, + required final int successCount, + required final int failureCount}) = _$StressTestSummaryImpl; + + factory _StressTestSummary.fromJson(Map json) = + _$StressTestSummaryImpl.fromJson; + + @override + List get results; + @override + Duration get totalDuration; + @override + double get avgResponseTime; + @override + int get successCount; + @override + int get failureCount; + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/stress_test_summary.g.dart b/lib/models/stress_test/stress_test_summary.g.dart new file mode 100644 index 000000000..5bf4d5b48 --- /dev/null +++ b/lib/models/stress_test/stress_test_summary.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stress_test_summary.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$StressTestSummaryImpl _$$StressTestSummaryImplFromJson( + Map json) => + _$StressTestSummaryImpl( + results: (json['results'] as List) + .map((e) => ApiRequestResult.fromJson(e as Map)) + .toList(), + totalDuration: + Duration(microseconds: (json['totalDuration'] as num).toInt()), + avgResponseTime: (json['avgResponseTime'] as num).toDouble(), + successCount: (json['successCount'] as num).toInt(), + failureCount: (json['failureCount'] as num).toInt(), + ); + +Map _$$StressTestSummaryImplToJson( + _$StressTestSummaryImpl instance) => + { + 'results': instance.results, + 'totalDuration': instance.totalDuration.inMicroseconds, + 'avgResponseTime': instance.avgResponseTime, + 'successCount': instance.successCount, + 'failureCount': instance.failureCount, + }; From 4c5dc916e3074ff3b901d6665a4f1e6a1fcd9145 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:51:38 +0530 Subject: [PATCH 083/188] chore: create stress test models file for export --- lib/models/stress_test/stress_test_models.dart | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 lib/models/stress_test/stress_test_models.dart diff --git a/lib/models/stress_test/stress_test_models.dart b/lib/models/stress_test/stress_test_models.dart new file mode 100644 index 000000000..10c1bcacd --- /dev/null +++ b/lib/models/stress_test/stress_test_models.dart @@ -0,0 +1,3 @@ +export 'stress_test_config.dart'; +export 'api_request_result.dart'; +export 'stress_test_summary.dart'; From de88a436470a5d31a96bb8d343029e9bb154eefe Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:55:34 +0530 Subject: [PATCH 084/188] chore: create stress test service with isolates and future.wait() --- .../stress_test/stress_test_service.dart | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 lib/services/stress_test/stress_test_service.dart diff --git a/lib/services/stress_test/stress_test_service.dart b/lib/services/stress_test/stress_test_service.dart new file mode 100644 index 000000000..1de78e70c --- /dev/null +++ b/lib/services/stress_test/stress_test_service.dart @@ -0,0 +1,202 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:isolate'; + +import 'package:http/http.dart' as http; + +import 'package:apidash/models/stress_test/isolate_message.dart'; +import '../../models/stress_test/stress_models.dart'; + +/// Service for executing parallel API requests following the Single Responsibility Principle +class StressTestService { + + static Future _executeRequest({ + required String url, + required String method, + Map? headers, + dynamic body, + Duration? timeout, + }) async { + final stopwatch = Stopwatch()..start(); + + try { + final http.Response response; + final client = http.Client(); + + try { + switch (method.toUpperCase()) { + case 'GET': + response = await client.get( + Uri.parse(url), + headers: headers, + ).timeout(timeout ?? const Duration(seconds: 30)); + case 'POST': + response = await client.post( + Uri.parse(url), + headers: headers, + body: body is String ? body : jsonEncode(body), + ).timeout(timeout ?? const Duration(seconds: 30)); + case 'PUT': + response = await client.put( + Uri.parse(url), + headers: headers, + body: body is String ? body : jsonEncode(body), + ).timeout(timeout ?? const Duration(seconds: 30)); + case 'DELETE': + response = await client.delete( + Uri.parse(url), + headers: headers, + body: body is String ? body : jsonEncode(body), + ).timeout(timeout ?? const Duration(seconds: 30)); + case 'PATCH': + response = await client.patch( + Uri.parse(url), + headers: headers, + body: body is String ? body : jsonEncode(body), + ).timeout(timeout ?? const Duration(seconds: 30)); + default: + throw Exception('Unsupported HTTP method: $method'); + } + } finally { + client.close(); + } + + stopwatch.stop(); + return ApiRequestResult( + statusCode: response.statusCode, + body: response.body, + duration: stopwatch.elapsed, + error: null, + ); + } catch (e) { + stopwatch.stop(); + return ApiRequestResult( + statusCode: -1, + body: '', + duration: stopwatch.elapsed, + error: e.toString(), + ); + } + } + + /// Isolate worker function + static Future _isolateWorker(SendPort sendPort) async { + final receivePort = ReceivePort(); + sendPort.send(receivePort.sendPort); + + await for (final message in receivePort) { + if (message is IsolateMessage) { + final result = await _executeRequest( + url: message.url, + method: message.method, + headers: message.headers, + body: message.body, + timeout: message.timeout, + ); + sendPort.send(result); + } else if (message == 'close') { + break; + } + } + + Isolate.exit(); + } + + /// Execute a parallel API test with the given configuration + static Future runTest(StressTestConfig config) async { + final totalStopwatch = Stopwatch()..start(); + final List results = []; + + if (config.useIsolates) { + final isolates = []; + final receivePorts = []; + final workerPorts = []; + + try { + for (int i = 0; i < config.concurrentRequests; i++) { + final receivePort = ReceivePort(); + receivePorts.add(receivePort); + + final isolate = await Isolate.spawn(_isolateWorker, receivePort.sendPort); + isolates.add(isolate); + + final workerPort = await receivePort.first as SendPort; + workerPorts.add(workerPort); + } + + final responseReceivePorts = []; + for (final workerPort in workerPorts) { + final responsePort = ReceivePort(); + responseReceivePorts.add(responsePort); + + // Send the request to the isolate + workerPort.send(IsolateMessage( + url: config.url, + method: config.method, + headers: config.headers, + body: config.body, + timeout: config.timeout, + )); + } + + final futures = responseReceivePorts.map((port) => port.first); + final responses = await Future.wait(futures); + + for (final response in responses) { + if (response is ApiRequestResult) { + results.add(response); + } + } + + for (final workerPort in workerPorts) { + workerPort.send('close'); + } + } finally { + for (final isolate in isolates) { + isolate.kill(priority: Isolate.immediate); + } + for (final port in receivePorts) { + port.close(); + } + } + } else { + // without isolates + final futures = >[]; + + for (int i = 0; i < config.concurrentRequests; i++) { + futures.add(_executeRequest( + url: config.url, + method: config.method, + headers: config.headers, + body: config.body, + timeout: config.timeout, + )); + } + + results.addAll(await Future.wait(futures)); + } + + // Calculating statistics + totalStopwatch.stop(); + final totalDuration = totalStopwatch.elapsed; + + final successCount = results.where((r) => r.error == null && r.statusCode >= 200 && r.statusCode < 300).length; + final failureCount = results.length - successCount; + + final totalResponseTime = results.fold( + Duration.zero, + (prev, result) => prev + result.duration + ); + final avgResponseTime = results.isEmpty + ? 0.0 + : totalResponseTime.inMicroseconds / results.length / 1000; // in milliseconds + + return StressTestSummary( + results: results, + totalDuration: totalDuration, + avgResponseTime: avgResponseTime, + successCount: successCount, + failureCount: failureCount, + ); + } +} From bd1ba6437f0e81e5d29cdf0318856bb9721589d6 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 05:02:33 +0530 Subject: [PATCH 085/188] chore: create input field and result card widget for stress testing --- .../stress_test/number_input_field.dart | 59 ++++ .../stress_test/stress_test_result_card.dart | 291 ++++++++++++++++++ 2 files changed, 350 insertions(+) create mode 100644 lib/widgets/stress_test/number_input_field.dart create mode 100644 lib/widgets/stress_test/stress_test_result_card.dart diff --git a/lib/widgets/stress_test/number_input_field.dart b/lib/widgets/stress_test/number_input_field.dart new file mode 100644 index 000000000..2505ae696 --- /dev/null +++ b/lib/widgets/stress_test/number_input_field.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class NumberInputField extends StatelessWidget { + final TextEditingController controller; + final String labelText; + final String? helperText; + final int min; + final int max; + final bool enabled; + + const NumberInputField({ + Key? key, + required this.controller, + required this.labelText, + this.helperText, + required this.min, + required this.max, + this.enabled = true, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + + return TextField( + controller: controller, + decoration: InputDecoration( + labelText: labelText, + helperText: helperText, + border: const OutlineInputBorder(), + contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + helperStyle: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.grey[700], + ), + ), + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + enabled: enabled, + onChanged: (value) { + final numValue = int.tryParse(value) ?? min; + + if (numValue < min) { + controller.text = min.toString(); + controller.selection = TextSelection.fromPosition( + TextPosition(offset: controller.text.length), + ); + } else if (numValue > max) { + controller.text = max.toString(); + controller.selection = TextSelection.fromPosition( + TextPosition(offset: controller.text.length), + ); + } + }, + ); + } +} diff --git a/lib/widgets/stress_test/stress_test_result_card.dart b/lib/widgets/stress_test/stress_test_result_card.dart new file mode 100644 index 000000000..beb1ecf47 --- /dev/null +++ b/lib/widgets/stress_test/stress_test_result_card.dart @@ -0,0 +1,291 @@ +import 'package:flutter/material.dart'; +import 'package:apidash/models/stress_test/stress_test_models.dart'; + +class StressTestResultCard extends StatelessWidget { + final StressTestSummary summary; + + const StressTestResultCard({Key? key, required this.summary}) : super(key: key); + + @override + Widget build(BuildContext context) { + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + final successColor = isDarkMode ? Colors.green[400] : Colors.green[700]; + final errorColor = isDarkMode ? Colors.red[400] : Colors.red[700]; + final cardColor = isDarkMode ? Colors.grey[850] : Colors.grey[200]; + final textColor = isDarkMode ? Colors.grey[400] : Colors.grey[800]; + + return Card( + color: cardColor, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: _buildSummaryItem( + context, + 'Total Requests', + '${summary.results.length}', + Icons.http, + textColor, + ), + ), + Expanded( + child: _buildSummaryItem( + context, + 'Total Duration', + '${(summary.totalDuration.inMilliseconds / 1000).toStringAsFixed(2)}s', + Icons.timer, + textColor, + ), + ), + Expanded( + child: _buildSummaryItem( + context, + 'Avg Response Time', + '${summary.avgResponseTime.toStringAsFixed(2)}ms', + Icons.speed, + textColor, + ), + ), + ], + ), + const SizedBox(height: 16), + Row( + children: [ + Expanded( + child: _buildStatusItem( + context, + 'Success', + summary.successCount, + summary.results.length, + successColor!, + ), + ), + Expanded( + child: _buildStatusItem( + context, + 'Failed', + summary.failureCount, + summary.results.length, + errorColor!, + ), + ), + ], + ), + const SizedBox(height: 24), + Text( + 'Response Time Distribution', + style: Theme.of(context).textTheme.titleSmall, + ), + const SizedBox(height: 8), + _buildResponseTimeDistribution(context, summary.results), + const SizedBox(height: 16), + Text( + 'Status Code Distribution', + style: Theme.of(context).textTheme.titleSmall, + ), + const SizedBox(height: 8), + _buildStatusCodeDistribution(context, summary.results), + ], + ), + ), + ); + } + + Widget _buildSummaryItem( + BuildContext context, + String label, + String value, + IconData icon, + Color? textColor, + ) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon(icon, size: 14, color: textColor), + const SizedBox(width: 4), + Text( + label, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: textColor, + ), + ), + ], + ), + const SizedBox(height: 4), + Text( + value, + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ); + } + + Widget _buildStatusItem( + BuildContext context, + String label, + int count, + int total, + Color color, + ) { + final percentage = total > 0 ? (count / total * 100).toStringAsFixed(1) : '0.0'; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 4), + Row( + children: [ + Text( + '$count', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: color, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 4), + Text( + '($percentage%)', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: color, + ), + ), + ], + ), + const SizedBox(height: 4), + ClipRRect( + borderRadius: BorderRadius.circular(2), + child: LinearProgressIndicator( + value: total > 0 ? count / total : 0, + backgroundColor: Colors.grey[400], + valueColor: AlwaysStoppedAnimation(color), + minHeight: 4, + ), + ), + ], + ); + } + + Widget _buildResponseTimeDistribution(BuildContext context, List results) { + final successfulResults = results.where((r) => r.error == null).toList(); + if (successfulResults.isEmpty) { + return const Text('No successful responses to analyze'); + } + + successfulResults.sort((a, b) => a.duration.compareTo(b.duration)); + + final p50Index = (successfulResults.length * 0.5).floor(); + final p75Index = (successfulResults.length * 0.75).floor(); + final p90Index = (successfulResults.length * 0.9).floor(); + final p95Index = (successfulResults.length * 0.95).floor(); + final p99Index = (successfulResults.length * 0.99).floor(); + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Column( + children: [ + _buildPercentileRow(context, 'Min', + '${successfulResults.first.duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P50', + '${successfulResults[p50Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P75', + '${successfulResults[p75Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P90', + '${successfulResults[p90Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P95', + '${successfulResults[p95Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P99', + '${successfulResults[p99Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'Max', + '${successfulResults.last.duration.inMilliseconds} ms'), + ], + ), + ); + } + + Widget _buildPercentileRow(BuildContext context, String percentile, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2.0), + child: Row( + children: [ + SizedBox( + width: 40, + child: Text( + percentile, + style: Theme.of(context).textTheme.bodySmall, + ), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + value, + style: Theme.of(context).textTheme.bodyMedium, + ), + ), + ], + ), + ); + } + + Widget _buildStatusCodeDistribution(BuildContext context, List results) { + final statusCodeCounts = {}; + for (final result in results) { + if (result.statusCode != -1) { // Skip errors + statusCodeCounts[result.statusCode] = + (statusCodeCounts[result.statusCode] ?? 0) + 1; + } + } + + if (statusCodeCounts.isEmpty) { + return const Text('No status codes to analyze'); + } + + final sortedEntries = statusCodeCounts.entries.toList() + ..sort((a, b) => a.key.compareTo(b.key)); + + return Wrap( + spacing: 8, + runSpacing: 8, + children: sortedEntries.map((entry) { + final statusCode = entry.key; + final count = entry.value; + final isSuccess = statusCode >= 200 && statusCode < 300; + final isRedirect = statusCode >= 300 && statusCode < 400; + final isClientError = statusCode >= 400 && statusCode < 500; + final isServerError = statusCode >= 500; + + Color chipColor; + if (isSuccess) { + chipColor = Colors.green; + } else if (isRedirect) { + chipColor = Colors.blue; + } else if (isClientError) { + chipColor = Colors.orange; + } else if (isServerError) { + chipColor = Colors.red; + } else { + chipColor = Colors.grey; + } + + return Chip( + label: Text('$statusCode ($count)'), + backgroundColor: chipColor.withOpacity(0.2), + labelStyle: TextStyle(color: chipColor), + ); + }).toList(), + ); + } +} From 22dcb3c61a9579068ac29c7aa635b674f6eebbcd Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 13:42:00 +0530 Subject: [PATCH 086/188] chore: update stress test service for handling timeout exceptions --- .../stress_test/stress_test_service.dart | 224 ++++++++++++------ 1 file changed, 150 insertions(+), 74 deletions(-) diff --git a/lib/services/stress_test/stress_test_service.dart b/lib/services/stress_test/stress_test_service.dart index 1de78e70c..1186e23f0 100644 --- a/lib/services/stress_test/stress_test_service.dart +++ b/lib/services/stress_test/stress_test_service.dart @@ -4,10 +4,12 @@ import 'dart:isolate'; import 'package:http/http.dart' as http; +import 'package:apidash/models/stress_test/api_request_result.dart'; +import 'package:apidash/models/stress_test/stress_test_config.dart'; +import 'package:apidash/models/stress_test/stress_test_summary.dart'; import 'package:apidash/models/stress_test/isolate_message.dart'; -import '../../models/stress_test/stress_models.dart'; -/// Service for executing parallel API requests following the Single Responsibility Principle +// Stress Test Service Class class StressTestService { static Future _executeRequest({ @@ -18,10 +20,11 @@ class StressTestService { Duration? timeout, }) async { final stopwatch = Stopwatch()..start(); + final client = http.Client(); try { - final http.Response response; - final client = http.Client(); + http.Response response; + final defaultTimeout = const Duration(seconds: 30); try { switch (method.toUpperCase()) { @@ -29,53 +32,77 @@ class StressTestService { response = await client.get( Uri.parse(url), headers: headers, - ).timeout(timeout ?? const Duration(seconds: 30)); + ).timeout(timeout ?? defaultTimeout); case 'POST': response = await client.post( Uri.parse(url), headers: headers, body: body is String ? body : jsonEncode(body), - ).timeout(timeout ?? const Duration(seconds: 30)); + ).timeout(timeout ?? defaultTimeout); case 'PUT': response = await client.put( Uri.parse(url), headers: headers, body: body is String ? body : jsonEncode(body), - ).timeout(timeout ?? const Duration(seconds: 30)); + ).timeout(timeout ?? defaultTimeout); case 'DELETE': response = await client.delete( Uri.parse(url), headers: headers, body: body is String ? body : jsonEncode(body), - ).timeout(timeout ?? const Duration(seconds: 30)); + ).timeout(timeout ?? defaultTimeout); case 'PATCH': response = await client.patch( Uri.parse(url), headers: headers, body: body is String ? body : jsonEncode(body), - ).timeout(timeout ?? const Duration(seconds: 30)); + ).timeout(timeout ?? defaultTimeout); default: throw Exception('Unsupported HTTP method: $method'); } - } finally { - client.close(); - } - stopwatch.stop(); - return ApiRequestResult( - statusCode: response.statusCode, - body: response.body, - duration: stopwatch.elapsed, - error: null, - ); - } catch (e) { - stopwatch.stop(); - return ApiRequestResult( - statusCode: -1, - body: '', - duration: stopwatch.elapsed, - error: e.toString(), - ); + stopwatch.stop(); + return ApiRequestResult( + statusCode: response.statusCode, + body: response.body, + duration: stopwatch.elapsed, + error: null, + ); + } on TimeoutException { + stopwatch.stop(); + return ApiRequestResult( + statusCode: -1, + body: '', + duration: stopwatch.elapsed, + error: 'Request timed out after ${(timeout ?? defaultTimeout).inSeconds} seconds', + ); + } on http.ClientException catch (e) { + stopwatch.stop(); + return ApiRequestResult( + statusCode: -1, + body: '', + duration: stopwatch.elapsed, + error: 'HTTP client error: ${e.message}', + ); + } on FormatException catch (e) { + stopwatch.stop(); + return ApiRequestResult( + statusCode: -1, + body: '', + duration: stopwatch.elapsed, + error: 'Format error: ${e.message}', + ); + } catch (e) { + stopwatch.stop(); + return ApiRequestResult( + statusCode: -1, + body: '', + duration: stopwatch.elapsed, + error: e.toString(), + ); + } + } finally { + client.close(); } } @@ -102,7 +129,7 @@ class StressTestService { Isolate.exit(); } - /// Execute a parallel API test with the given configuration + /// Execute a parallel API test static Future runTest(StressTestConfig config) async { final totalStopwatch = Stopwatch()..start(); final List results = []; @@ -110,57 +137,87 @@ class StressTestService { if (config.useIsolates) { final isolates = []; final receivePorts = []; - final workerPorts = []; + final sendPorts = []; + final completers = >[]; try { - for (int i = 0; i < config.concurrentRequests; i++) { + for (var i = 0; i < config.concurrentRequests; i++) { final receivePort = ReceivePort(); - receivePorts.add(receivePort); + final completer = Completer(); - final isolate = await Isolate.spawn(_isolateWorker, receivePort.sendPort); - isolates.add(isolate); + final isolate = await Isolate.spawn( + _isolateWorker, + receivePort.sendPort, + errorsAreFatal: false, + onExit: receivePort.sendPort, + onError: receivePort.sendPort, + ); - final workerPort = await receivePort.first as SendPort; - workerPorts.add(workerPort); - } - - final responseReceivePorts = []; - for (final workerPort in workerPorts) { - final responsePort = ReceivePort(); - responseReceivePorts.add(responsePort); + isolates.add(isolate); + receivePorts.add(receivePort); + completers.add(completer); - // Send the request to the isolate - workerPort.send(IsolateMessage( - url: config.url, - method: config.method, - headers: config.headers, - body: config.body, - timeout: config.timeout, - )); + receivePort.listen((message) { + if (message is SendPort) { + sendPorts.add(message); + if (sendPorts.length == config.concurrentRequests) { + for (var j = 0; j < config.concurrentRequests; j++) { + sendPorts[j].send(IsolateMessage( + url: config.url, + method: config.method, + headers: config.headers, + body: config.body, + timeout: config.timeout, + )); + } + } + } else if (message is ApiRequestResult) { + if (!completer.isCompleted) { + completer.complete(message); + } + } else if (message is List && message.length >= 2) { + if (!completer.isCompleted) { + completer.complete(ApiRequestResult( + statusCode: -1, + body: '', + duration: Duration.zero, + error: 'Isolate error: ${message[0]}', + )); + } + } + }); } - final futures = responseReceivePorts.map((port) => port.first); - final responses = await Future.wait(futures); - - for (final response in responses) { - if (response is ApiRequestResult) { - results.add(response); + for (var completer in completers) { + try { + final result = await completer.future.timeout( + (config.timeout ?? const Duration(seconds: 30)) + const Duration(seconds: 10) + ); + results.add(result); + } on TimeoutException { + results.add(ApiRequestResult( + statusCode: -1, + body: '', + duration: Duration.zero, + error: 'Isolate communication timed out', + )); } } - - for (final workerPort in workerPorts) { - workerPort.send('close'); - } } finally { - for (final isolate in isolates) { - isolate.kill(priority: Isolate.immediate); + for (var i = 0; i < isolates.length; i++) { + try { + if (sendPorts.length > i) { + sendPorts[i].send('close'); + } + isolates[i].kill(priority: Isolate.immediate); + } catch (_) { /*We need to ignore once termination is done*/ } } - for (final port in receivePorts) { + + for (var port in receivePorts) { port.close(); } } } else { - // without isolates final futures = >[]; for (int i = 0; i < config.concurrentRequests; i++) { @@ -173,27 +230,46 @@ class StressTestService { )); } - results.addAll(await Future.wait(futures)); + try { + final overallTimeout = (config.timeout ?? const Duration(seconds: 30)) + const Duration(seconds: 10); + final futureResults = await Future.wait(futures).timeout(overallTimeout); + results.addAll(futureResults); + } on TimeoutException { + final completedResults = results.length; + final remainingCount = config.concurrentRequests - completedResults; + + for (int i = 0; i < remainingCount; i++) { + results.add(ApiRequestResult( + statusCode: -1, + body: '', + duration: Duration.zero, + error: 'Operation timed out', + )); + } + } } - // Calculating statistics totalStopwatch.stop(); - final totalDuration = totalStopwatch.elapsed; + + final successCount = results.where((r) => + r.statusCode >= 200 && r.statusCode < 300 && r.error == null + ).length; - final successCount = results.where((r) => r.error == null && r.statusCode >= 200 && r.statusCode < 300).length; final failureCount = results.length - successCount; - final totalResponseTime = results.fold( - Duration.zero, - (prev, result) => prev + result.duration + final validResults = results.where((r) => r.error == null); + final totalResponseTime = validResults.fold( + 0, + (prev, result) => prev + result.duration.inMicroseconds ); - final avgResponseTime = results.isEmpty - ? 0.0 - : totalResponseTime.inMicroseconds / results.length / 1000; // in milliseconds + + final avgResponseTime = validResults.isEmpty + ? 0.0 + : totalResponseTime / validResults.length / 1000; return StressTestSummary( results: results, - totalDuration: totalDuration, + totalDuration: totalStopwatch.elapsed, avgResponseTime: avgResponseTime, successCount: successCount, failureCount: failureCount, From 7f3f27cdb32ee9b803546fa5deff8ca6965c88d9 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 13:44:43 +0530 Subject: [PATCH 087/188] chore: create request details card --- lib/widgets/card_request_details.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/widgets/card_request_details.dart b/lib/widgets/card_request_details.dart index f953435c0..44c97a8e5 100644 --- a/lib/widgets/card_request_details.dart +++ b/lib/widgets/card_request_details.dart @@ -6,7 +6,6 @@ class RequestDetailsCard extends StatelessWidget { final Widget? child; @override - @override Widget build(BuildContext context) { return Card( color: kColorTransparent, @@ -18,7 +17,10 @@ class RequestDetailsCard extends StatelessWidget { borderRadius: kBorderRadius12, ), elevation: 0, - child: child, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: child ?? const SizedBox.shrink(), + ), ); } } From 9243e379f81b3695cf98a6e69e8ecc59a4b77dff Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 13:51:15 +0530 Subject: [PATCH 088/188] feat: implement stress testing in the UI --- .../request_pane/request_stress_test.dart | 363 ++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 lib/screens/home_page/editor_pane/details_card/request_pane/request_stress_test.dart diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_stress_test.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_stress_test.dart new file mode 100644 index 000000000..d7773d4f8 --- /dev/null +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_stress_test.dart @@ -0,0 +1,363 @@ +import 'package:apidash/providers/providers.dart'; +import 'package:apidash/services/stress_test/stress_test_service.dart'; +import 'package:apidash/models/stress_test/stress_test_config.dart'; +import 'package:apidash/models/stress_test/stress_test_summary.dart'; +import 'package:apidash/models/stress_test/api_request_result.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class RequestStressTestPane extends ConsumerStatefulWidget { + const RequestStressTestPane({super.key}); + + @override + ConsumerState createState() => _RequestStressTestPaneState(); +} + +class _RequestStressTestPaneState extends ConsumerState { + final _concurrentRequestsController = TextEditingController(text: '10'); + final _timeoutController = TextEditingController(text: '30'); + bool _isRunning = false; + bool _useIsolates = true; + StressTestSummary? _summary; + + @override + void dispose() { + _concurrentRequestsController.dispose(); + _timeoutController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + final accentColor = isDarkMode ? Colors.tealAccent : Colors.teal; + + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Parallel API Testing', + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 8), + Text( + 'Test your API endpoint with multiple concurrent requests to evaluate performance under load.', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: isDarkMode ? Colors.grey[400] : Colors.grey[700], + ), + ), + const SizedBox(height: 16), + Row( + children: [ + Expanded( + child: NumberTextField( + controller: _concurrentRequestsController, + labelText: 'Number of Concurrent Requests', + helperText: 'How many requests to send in parallel', + min: 1, + max: 500, + ), + ), + const SizedBox(width: 16), + Expanded( + child: NumberTextField( + controller: _timeoutController, + labelText: 'Timeout (seconds)', + helperText: 'Maximum time to wait for each request', + min: 1, + max: 300, + ), + ), + ], + ), + const SizedBox(height: 16), + CheckboxListTile( + title: const Text('Use Isolates'), + subtitle: const Text('Improves performance for high request volumes but uses more memory'), + value: _useIsolates, + contentPadding: EdgeInsets.zero, + controlAffinity: ListTileControlAffinity.leading, + onChanged: _isRunning ? null : (value) { + setState(() { + _useIsolates = value ?? true; + }); + }, + ), + const SizedBox(height: 16), + SizedBox( + width: 200, + child: ElevatedButton.icon( + onPressed: _isRunning ? null : _runStressTest, + icon: _isRunning + ? const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator(strokeWidth: 2) + ) + : Icon(Icons.bolt, color: accentColor), + label: Text(_isRunning ? 'Running Test...' : 'Run Stress Test'), + ), + ), + if (_summary != null) ...[ + const SizedBox(height: 24), + const Divider(), + const SizedBox(height: 16), + Text( + 'Test Results', + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 16), + _buildResultsCard(context), + ], + ], + ), + ), + ); + } + + Future _runStressTest() async { + final requestModel = ref.read(selectedRequestModelProvider); + if (requestModel == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('No request selected')), + ); + return; + } + + final httpRequest = requestModel.httpRequestModel; + if (httpRequest == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Invalid request configuration')), + ); + return; + } + + setState(() { + _isRunning = true; + _summary = null; + }); + + try { + final collectionNotifier = ref.read(collectionStateNotifierProvider.notifier); + + final substitutedRequest = collectionNotifier.getSubstitutedHttpRequestModel(httpRequest); + + final headers = {}; + if (substitutedRequest.headers != null) { + for (var header in substitutedRequest.headers!) { + if (header.value != null) { + headers[header.name] = header.value ?? ''; + } + } + } + + String fullUrl = substitutedRequest.url; + + final concurrentRequests = int.tryParse(_concurrentRequestsController.text) ?? 10; + final timeoutSeconds = int.tryParse(_timeoutController.text) ?? 30; + + final config = StressTestConfig( + url: fullUrl, + method: substitutedRequest.method.toString().split('.').last, + headers: headers, + body: substitutedRequest.body, + concurrentRequests: concurrentRequests, + timeout: Duration(seconds: timeoutSeconds), + useIsolates: _useIsolates, + ); + + final result = await StressTestService.runTest(config); + + if (mounted) { + setState(() { + _summary = result; + _isRunning = false; + }); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Error: $e')), + ); + setState(() => _isRunning = false); + } + } + } + + Widget _buildResultsCard(BuildContext context) { + final summary = _summary; + if (summary == null) return const SizedBox.shrink(); + + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + final cardColor = isDarkMode + ? Colors.grey[850] + : Colors.grey[50]; + + return Card( + color: cardColor, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildResultRow('Total Requests', '${summary.results.length}'), + _buildResultRow('Total Duration', '${summary.totalDuration.inMilliseconds}ms'), + _buildResultRow('Avg Response Time', '${summary.avgResponseTime.toStringAsFixed(2)}ms'), + _buildResultRow( + 'Success Rate', + '${(summary.successCount / summary.results.length * 100).toStringAsFixed(1)}% (${summary.successCount}/${summary.results.length})' + ), + + const SizedBox(height: 16), + const Text('Response Status Breakdown:'), + const SizedBox(height: 8), + + ..._groupResponsesByStatus(summary.results).entries.map((entry) { + final statusText = 'Status ${entry.key}'; + final countText = '${entry.value} (${(entry.value / summary.results.length * 100).toStringAsFixed(1)}%)'; + + return _buildResultRow(statusText, countText); + }), + + const SizedBox(height: 16), + ExpansionTile( + title: const Text('Detailed Results'), + backgroundColor: Colors.transparent, + children: [ + ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 250), + child: ListView.builder( + shrinkWrap: true, + itemCount: summary.results.length, + itemBuilder: (context, index) { + final result = summary.results[index]; + final isSuccess = result.statusCode >= 200 && result.statusCode < 300; + + return ListTile( + dense: true, + title: Row( + children: [ + Text( + '#${index + 1}', + style: TextStyle( + fontWeight: FontWeight.bold, + color: isDarkMode ? Colors.grey[400] : Colors.grey[700], + ), + ), + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: isSuccess + ? Colors.green.withValues(alpha: 0.2) + : Colors.red.withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: isSuccess ? Colors.green : Colors.red, + width: 1, + ), + ), + child: Text( + 'Status: ${result.statusCode}', + style: TextStyle( + fontSize: 12, + color: isSuccess ? Colors.green : Colors.red, + ), + ), + ), + const SizedBox(width: 8), + Text('${result.duration.inMilliseconds}ms') + ], + ), + subtitle: result.error != null + ? Text( + 'Error: ${result.error}', + style: const TextStyle(color: Colors.red, fontSize: 12), + ) + : null, + ); + }, + ), + ), + ], + ), + ], + ), + ), + ); + } + + Widget _buildResultRow(String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: Row( + children: [ + Text('$label: ', style: const TextStyle(fontWeight: FontWeight.bold)), + Text(value), + ], + ), + ); + } + + Map _groupResponsesByStatus(List results) { + final statusMap = {}; + + for (final result in results) { + statusMap.update( + result.statusCode, + (count) => count + 1, + ifAbsent: () => 1, + ); + } + + return statusMap; + } +} + +class NumberTextField extends StatelessWidget { + final TextEditingController controller; + final String labelText; + final String? helperText; + final int min; + final int max; + + const NumberTextField({ + super.key, + required this.controller, + required this.labelText, + this.helperText, + required this.min, + required this.max, + }); + + @override + Widget build(BuildContext context) { + return TextField( + controller: controller, + decoration: InputDecoration( + labelText: labelText, + helperText: helperText, + border: const OutlineInputBorder(), + contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + ), + keyboardType: TextInputType.number, + onChanged: (value) { + // To resolve range error + final parsedValue = int.tryParse(value); + if (parsedValue != null) { + if (parsedValue < min) { + controller.text = min.toString(); + } else if (parsedValue > max) { + controller.text = max.toString(); + } + } else if (value.isNotEmpty) { + controller.text = min.toString(); + } + }, + ); + } +} From 3499341176b27439f4356268f6a706b96efa1bf2 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 13:52:56 +0530 Subject: [PATCH 089/188] chore: add label for stress testing in consts.dart --- lib/consts.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/consts.dart b/lib/consts.dart index 10ae65ea9..7ee6b32c8 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -443,6 +443,7 @@ const kLabelViewCode = "View Code"; const kLabelURLParams = "URL Params"; const kLabelHeaders = "Headers"; const kLabelBody = "Body"; +const kLabelStressTest = 'Stress Test'; const kLabelQuery = "Query"; const kNameCheckbox = "Checkbox"; const kNameURLParam = "URL Parameter"; From d8e5807906c3e6c8a94be07cdf4a906c3558f72f Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 13:53:49 +0530 Subject: [PATCH 090/188] chore: implement the stress test UI in request pane --- .../api_request_result.freezed.dart | 224 ------------- .../stress_test/api_request_result.g.dart | 25 -- .../stress_test/isolate_message.freezed.dart | 264 --------------- lib/models/stress_test/isolate_message.g.dart | 30 -- .../stress_test_config.freezed.dart | 309 ------------------ .../stress_test/stress_test_config.g.dart | 35 -- .../stress_test_summary.freezed.dart | 266 --------------- .../stress_test/stress_test_summary.g.dart | 30 -- .../request_pane/request_pane_rest.dart | 12 +- lib/widgets/request_pane.dart | 2 +- 10 files changed, 9 insertions(+), 1188 deletions(-) delete mode 100644 lib/models/stress_test/api_request_result.freezed.dart delete mode 100644 lib/models/stress_test/api_request_result.g.dart delete mode 100644 lib/models/stress_test/isolate_message.freezed.dart delete mode 100644 lib/models/stress_test/isolate_message.g.dart delete mode 100644 lib/models/stress_test/stress_test_config.freezed.dart delete mode 100644 lib/models/stress_test/stress_test_config.g.dart delete mode 100644 lib/models/stress_test/stress_test_summary.freezed.dart delete mode 100644 lib/models/stress_test/stress_test_summary.g.dart diff --git a/lib/models/stress_test/api_request_result.freezed.dart b/lib/models/stress_test/api_request_result.freezed.dart deleted file mode 100644 index 6bdee80a3..000000000 --- a/lib/models/stress_test/api_request_result.freezed.dart +++ /dev/null @@ -1,224 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'api_request_result.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -ApiRequestResult _$ApiRequestResultFromJson(Map json) { - return _ApiRequestResult.fromJson(json); -} - -/// @nodoc -mixin _$ApiRequestResult { - int get statusCode => throw _privateConstructorUsedError; - String get body => throw _privateConstructorUsedError; - Duration get duration => throw _privateConstructorUsedError; - String? get error => throw _privateConstructorUsedError; - - /// Serializes this ApiRequestResult to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ApiRequestResultCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ApiRequestResultCopyWith<$Res> { - factory $ApiRequestResultCopyWith( - ApiRequestResult value, $Res Function(ApiRequestResult) then) = - _$ApiRequestResultCopyWithImpl<$Res, ApiRequestResult>; - @useResult - $Res call({int statusCode, String body, Duration duration, String? error}); -} - -/// @nodoc -class _$ApiRequestResultCopyWithImpl<$Res, $Val extends ApiRequestResult> - implements $ApiRequestResultCopyWith<$Res> { - _$ApiRequestResultCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? statusCode = null, - Object? body = null, - Object? duration = null, - Object? error = freezed, - }) { - return _then(_value.copyWith( - statusCode: null == statusCode - ? _value.statusCode - : statusCode // ignore: cast_nullable_to_non_nullable - as int, - body: null == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as String, - duration: null == duration - ? _value.duration - : duration // ignore: cast_nullable_to_non_nullable - as Duration, - error: freezed == error - ? _value.error - : error // ignore: cast_nullable_to_non_nullable - as String?, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$ApiRequestResultImplCopyWith<$Res> - implements $ApiRequestResultCopyWith<$Res> { - factory _$$ApiRequestResultImplCopyWith(_$ApiRequestResultImpl value, - $Res Function(_$ApiRequestResultImpl) then) = - __$$ApiRequestResultImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({int statusCode, String body, Duration duration, String? error}); -} - -/// @nodoc -class __$$ApiRequestResultImplCopyWithImpl<$Res> - extends _$ApiRequestResultCopyWithImpl<$Res, _$ApiRequestResultImpl> - implements _$$ApiRequestResultImplCopyWith<$Res> { - __$$ApiRequestResultImplCopyWithImpl(_$ApiRequestResultImpl _value, - $Res Function(_$ApiRequestResultImpl) _then) - : super(_value, _then); - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? statusCode = null, - Object? body = null, - Object? duration = null, - Object? error = freezed, - }) { - return _then(_$ApiRequestResultImpl( - statusCode: null == statusCode - ? _value.statusCode - : statusCode // ignore: cast_nullable_to_non_nullable - as int, - body: null == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as String, - duration: null == duration - ? _value.duration - : duration // ignore: cast_nullable_to_non_nullable - as Duration, - error: freezed == error - ? _value.error - : error // ignore: cast_nullable_to_non_nullable - as String?, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$ApiRequestResultImpl implements _ApiRequestResult { - const _$ApiRequestResultImpl( - {required this.statusCode, - required this.body, - required this.duration, - this.error}); - - factory _$ApiRequestResultImpl.fromJson(Map json) => - _$$ApiRequestResultImplFromJson(json); - - @override - final int statusCode; - @override - final String body; - @override - final Duration duration; - @override - final String? error; - - @override - String toString() { - return 'ApiRequestResult(statusCode: $statusCode, body: $body, duration: $duration, error: $error)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ApiRequestResultImpl && - (identical(other.statusCode, statusCode) || - other.statusCode == statusCode) && - (identical(other.body, body) || other.body == body) && - (identical(other.duration, duration) || - other.duration == duration) && - (identical(other.error, error) || other.error == error)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => - Object.hash(runtimeType, statusCode, body, duration, error); - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => - __$$ApiRequestResultImplCopyWithImpl<_$ApiRequestResultImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$ApiRequestResultImplToJson( - this, - ); - } -} - -abstract class _ApiRequestResult implements ApiRequestResult { - const factory _ApiRequestResult( - {required final int statusCode, - required final String body, - required final Duration duration, - final String? error}) = _$ApiRequestResultImpl; - - factory _ApiRequestResult.fromJson(Map json) = - _$ApiRequestResultImpl.fromJson; - - @override - int get statusCode; - @override - String get body; - @override - Duration get duration; - @override - String? get error; - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/api_request_result.g.dart b/lib/models/stress_test/api_request_result.g.dart deleted file mode 100644 index 4182a8e58..000000000 --- a/lib/models/stress_test/api_request_result.g.dart +++ /dev/null @@ -1,25 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'api_request_result.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$ApiRequestResultImpl _$$ApiRequestResultImplFromJson( - Map json) => - _$ApiRequestResultImpl( - statusCode: (json['statusCode'] as num).toInt(), - body: json['body'] as String, - duration: Duration(microseconds: (json['duration'] as num).toInt()), - error: json['error'] as String?, - ); - -Map _$$ApiRequestResultImplToJson( - _$ApiRequestResultImpl instance) => - { - 'statusCode': instance.statusCode, - 'body': instance.body, - 'duration': instance.duration.inMicroseconds, - 'error': instance.error, - }; diff --git a/lib/models/stress_test/isolate_message.freezed.dart b/lib/models/stress_test/isolate_message.freezed.dart deleted file mode 100644 index c3abc6857..000000000 --- a/lib/models/stress_test/isolate_message.freezed.dart +++ /dev/null @@ -1,264 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'isolate_message.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -IsolateMessage _$IsolateMessageFromJson(Map json) { - return _IsolateMessage.fromJson(json); -} - -/// @nodoc -mixin _$IsolateMessage { - String get url => throw _privateConstructorUsedError; - String get method => throw _privateConstructorUsedError; - Map? get headers => throw _privateConstructorUsedError; - dynamic get body => throw _privateConstructorUsedError; - Duration? get timeout => throw _privateConstructorUsedError; - - /// Serializes this IsolateMessage to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $IsolateMessageCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $IsolateMessageCopyWith<$Res> { - factory $IsolateMessageCopyWith( - IsolateMessage value, $Res Function(IsolateMessage) then) = - _$IsolateMessageCopyWithImpl<$Res, IsolateMessage>; - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - Duration? timeout}); -} - -/// @nodoc -class _$IsolateMessageCopyWithImpl<$Res, $Val extends IsolateMessage> - implements $IsolateMessageCopyWith<$Res> { - _$IsolateMessageCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? timeout = freezed, - }) { - return _then(_value.copyWith( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value.headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$IsolateMessageImplCopyWith<$Res> - implements $IsolateMessageCopyWith<$Res> { - factory _$$IsolateMessageImplCopyWith(_$IsolateMessageImpl value, - $Res Function(_$IsolateMessageImpl) then) = - __$$IsolateMessageImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - Duration? timeout}); -} - -/// @nodoc -class __$$IsolateMessageImplCopyWithImpl<$Res> - extends _$IsolateMessageCopyWithImpl<$Res, _$IsolateMessageImpl> - implements _$$IsolateMessageImplCopyWith<$Res> { - __$$IsolateMessageImplCopyWithImpl( - _$IsolateMessageImpl _value, $Res Function(_$IsolateMessageImpl) _then) - : super(_value, _then); - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? timeout = freezed, - }) { - return _then(_$IsolateMessageImpl( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value._headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$IsolateMessageImpl implements _IsolateMessage { - const _$IsolateMessageImpl( - {required this.url, - required this.method, - final Map? headers, - this.body, - this.timeout}) - : _headers = headers; - - factory _$IsolateMessageImpl.fromJson(Map json) => - _$$IsolateMessageImplFromJson(json); - - @override - final String url; - @override - final String method; - final Map? _headers; - @override - Map? get headers { - final value = _headers; - if (value == null) return null; - if (_headers is EqualUnmodifiableMapView) return _headers; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(value); - } - - @override - final dynamic body; - @override - final Duration? timeout; - - @override - String toString() { - return 'IsolateMessage(url: $url, method: $method, headers: $headers, body: $body, timeout: $timeout)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$IsolateMessageImpl && - (identical(other.url, url) || other.url == url) && - (identical(other.method, method) || other.method == method) && - const DeepCollectionEquality().equals(other._headers, _headers) && - const DeepCollectionEquality().equals(other.body, body) && - (identical(other.timeout, timeout) || other.timeout == timeout)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - url, - method, - const DeepCollectionEquality().hash(_headers), - const DeepCollectionEquality().hash(body), - timeout); - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => - __$$IsolateMessageImplCopyWithImpl<_$IsolateMessageImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$IsolateMessageImplToJson( - this, - ); - } -} - -abstract class _IsolateMessage implements IsolateMessage { - const factory _IsolateMessage( - {required final String url, - required final String method, - final Map? headers, - final dynamic body, - final Duration? timeout}) = _$IsolateMessageImpl; - - factory _IsolateMessage.fromJson(Map json) = - _$IsolateMessageImpl.fromJson; - - @override - String get url; - @override - String get method; - @override - Map? get headers; - @override - dynamic get body; - @override - Duration? get timeout; - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/isolate_message.g.dart b/lib/models/stress_test/isolate_message.g.dart deleted file mode 100644 index e5f403ea1..000000000 --- a/lib/models/stress_test/isolate_message.g.dart +++ /dev/null @@ -1,30 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'isolate_message.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$IsolateMessageImpl _$$IsolateMessageImplFromJson(Map json) => - _$IsolateMessageImpl( - url: json['url'] as String, - method: json['method'] as String, - headers: (json['headers'] as Map?)?.map( - (k, e) => MapEntry(k, e as String), - ), - body: json['body'], - timeout: json['timeout'] == null - ? null - : Duration(microseconds: (json['timeout'] as num).toInt()), - ); - -Map _$$IsolateMessageImplToJson( - _$IsolateMessageImpl instance) => - { - 'url': instance.url, - 'method': instance.method, - 'headers': instance.headers, - 'body': instance.body, - 'timeout': instance.timeout?.inMicroseconds, - }; diff --git a/lib/models/stress_test/stress_test_config.freezed.dart b/lib/models/stress_test/stress_test_config.freezed.dart deleted file mode 100644 index 2671a5dfe..000000000 --- a/lib/models/stress_test/stress_test_config.freezed.dart +++ /dev/null @@ -1,309 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'stress_test_config.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -StressTestConfig _$StressTestConfigFromJson(Map json) { - return _StressTestConfig.fromJson(json); -} - -/// @nodoc -mixin _$StressTestConfig { - String get url => throw _privateConstructorUsedError; - String get method => throw _privateConstructorUsedError; - Map? get headers => throw _privateConstructorUsedError; - dynamic get body => throw _privateConstructorUsedError; - int get concurrentRequests => throw _privateConstructorUsedError; - Duration? get timeout => throw _privateConstructorUsedError; - bool get useIsolates => throw _privateConstructorUsedError; - - /// Serializes this StressTestConfig to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $StressTestConfigCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $StressTestConfigCopyWith<$Res> { - factory $StressTestConfigCopyWith( - StressTestConfig value, $Res Function(StressTestConfig) then) = - _$StressTestConfigCopyWithImpl<$Res, StressTestConfig>; - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - int concurrentRequests, - Duration? timeout, - bool useIsolates}); -} - -/// @nodoc -class _$StressTestConfigCopyWithImpl<$Res, $Val extends StressTestConfig> - implements $StressTestConfigCopyWith<$Res> { - _$StressTestConfigCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? concurrentRequests = null, - Object? timeout = freezed, - Object? useIsolates = null, - }) { - return _then(_value.copyWith( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value.headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - concurrentRequests: null == concurrentRequests - ? _value.concurrentRequests - : concurrentRequests // ignore: cast_nullable_to_non_nullable - as int, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - useIsolates: null == useIsolates - ? _value.useIsolates - : useIsolates // ignore: cast_nullable_to_non_nullable - as bool, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$StressTestConfigImplCopyWith<$Res> - implements $StressTestConfigCopyWith<$Res> { - factory _$$StressTestConfigImplCopyWith(_$StressTestConfigImpl value, - $Res Function(_$StressTestConfigImpl) then) = - __$$StressTestConfigImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - int concurrentRequests, - Duration? timeout, - bool useIsolates}); -} - -/// @nodoc -class __$$StressTestConfigImplCopyWithImpl<$Res> - extends _$StressTestConfigCopyWithImpl<$Res, _$StressTestConfigImpl> - implements _$$StressTestConfigImplCopyWith<$Res> { - __$$StressTestConfigImplCopyWithImpl(_$StressTestConfigImpl _value, - $Res Function(_$StressTestConfigImpl) _then) - : super(_value, _then); - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? concurrentRequests = null, - Object? timeout = freezed, - Object? useIsolates = null, - }) { - return _then(_$StressTestConfigImpl( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value._headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - concurrentRequests: null == concurrentRequests - ? _value.concurrentRequests - : concurrentRequests // ignore: cast_nullable_to_non_nullable - as int, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - useIsolates: null == useIsolates - ? _value.useIsolates - : useIsolates // ignore: cast_nullable_to_non_nullable - as bool, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$StressTestConfigImpl implements _StressTestConfig { - const _$StressTestConfigImpl( - {required this.url, - required this.method, - final Map? headers, - this.body, - required this.concurrentRequests, - this.timeout, - this.useIsolates = false}) - : _headers = headers; - - factory _$StressTestConfigImpl.fromJson(Map json) => - _$$StressTestConfigImplFromJson(json); - - @override - final String url; - @override - final String method; - final Map? _headers; - @override - Map? get headers { - final value = _headers; - if (value == null) return null; - if (_headers is EqualUnmodifiableMapView) return _headers; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(value); - } - - @override - final dynamic body; - @override - final int concurrentRequests; - @override - final Duration? timeout; - @override - @JsonKey() - final bool useIsolates; - - @override - String toString() { - return 'StressTestConfig(url: $url, method: $method, headers: $headers, body: $body, concurrentRequests: $concurrentRequests, timeout: $timeout, useIsolates: $useIsolates)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$StressTestConfigImpl && - (identical(other.url, url) || other.url == url) && - (identical(other.method, method) || other.method == method) && - const DeepCollectionEquality().equals(other._headers, _headers) && - const DeepCollectionEquality().equals(other.body, body) && - (identical(other.concurrentRequests, concurrentRequests) || - other.concurrentRequests == concurrentRequests) && - (identical(other.timeout, timeout) || other.timeout == timeout) && - (identical(other.useIsolates, useIsolates) || - other.useIsolates == useIsolates)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - url, - method, - const DeepCollectionEquality().hash(_headers), - const DeepCollectionEquality().hash(body), - concurrentRequests, - timeout, - useIsolates); - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => - __$$StressTestConfigImplCopyWithImpl<_$StressTestConfigImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$StressTestConfigImplToJson( - this, - ); - } -} - -abstract class _StressTestConfig implements StressTestConfig { - const factory _StressTestConfig( - {required final String url, - required final String method, - final Map? headers, - final dynamic body, - required final int concurrentRequests, - final Duration? timeout, - final bool useIsolates}) = _$StressTestConfigImpl; - - factory _StressTestConfig.fromJson(Map json) = - _$StressTestConfigImpl.fromJson; - - @override - String get url; - @override - String get method; - @override - Map? get headers; - @override - dynamic get body; - @override - int get concurrentRequests; - @override - Duration? get timeout; - @override - bool get useIsolates; - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/stress_test_config.g.dart b/lib/models/stress_test/stress_test_config.g.dart deleted file mode 100644 index 6193507c3..000000000 --- a/lib/models/stress_test/stress_test_config.g.dart +++ /dev/null @@ -1,35 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'stress_test_config.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$StressTestConfigImpl _$$StressTestConfigImplFromJson( - Map json) => - _$StressTestConfigImpl( - url: json['url'] as String, - method: json['method'] as String, - headers: (json['headers'] as Map?)?.map( - (k, e) => MapEntry(k, e as String), - ), - body: json['body'], - concurrentRequests: (json['concurrentRequests'] as num).toInt(), - timeout: json['timeout'] == null - ? null - : Duration(microseconds: (json['timeout'] as num).toInt()), - useIsolates: json['useIsolates'] as bool? ?? false, - ); - -Map _$$StressTestConfigImplToJson( - _$StressTestConfigImpl instance) => - { - 'url': instance.url, - 'method': instance.method, - 'headers': instance.headers, - 'body': instance.body, - 'concurrentRequests': instance.concurrentRequests, - 'timeout': instance.timeout?.inMicroseconds, - 'useIsolates': instance.useIsolates, - }; diff --git a/lib/models/stress_test/stress_test_summary.freezed.dart b/lib/models/stress_test/stress_test_summary.freezed.dart deleted file mode 100644 index b5fd37df1..000000000 --- a/lib/models/stress_test/stress_test_summary.freezed.dart +++ /dev/null @@ -1,266 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'stress_test_summary.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -StressTestSummary _$StressTestSummaryFromJson(Map json) { - return _StressTestSummary.fromJson(json); -} - -/// @nodoc -mixin _$StressTestSummary { - List get results => throw _privateConstructorUsedError; - Duration get totalDuration => throw _privateConstructorUsedError; - double get avgResponseTime => throw _privateConstructorUsedError; - int get successCount => throw _privateConstructorUsedError; - int get failureCount => throw _privateConstructorUsedError; - - /// Serializes this StressTestSummary to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $StressTestSummaryCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $StressTestSummaryCopyWith<$Res> { - factory $StressTestSummaryCopyWith( - StressTestSummary value, $Res Function(StressTestSummary) then) = - _$StressTestSummaryCopyWithImpl<$Res, StressTestSummary>; - @useResult - $Res call( - {List results, - Duration totalDuration, - double avgResponseTime, - int successCount, - int failureCount}); -} - -/// @nodoc -class _$StressTestSummaryCopyWithImpl<$Res, $Val extends StressTestSummary> - implements $StressTestSummaryCopyWith<$Res> { - _$StressTestSummaryCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? results = null, - Object? totalDuration = null, - Object? avgResponseTime = null, - Object? successCount = null, - Object? failureCount = null, - }) { - return _then(_value.copyWith( - results: null == results - ? _value.results - : results // ignore: cast_nullable_to_non_nullable - as List, - totalDuration: null == totalDuration - ? _value.totalDuration - : totalDuration // ignore: cast_nullable_to_non_nullable - as Duration, - avgResponseTime: null == avgResponseTime - ? _value.avgResponseTime - : avgResponseTime // ignore: cast_nullable_to_non_nullable - as double, - successCount: null == successCount - ? _value.successCount - : successCount // ignore: cast_nullable_to_non_nullable - as int, - failureCount: null == failureCount - ? _value.failureCount - : failureCount // ignore: cast_nullable_to_non_nullable - as int, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$StressTestSummaryImplCopyWith<$Res> - implements $StressTestSummaryCopyWith<$Res> { - factory _$$StressTestSummaryImplCopyWith(_$StressTestSummaryImpl value, - $Res Function(_$StressTestSummaryImpl) then) = - __$$StressTestSummaryImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {List results, - Duration totalDuration, - double avgResponseTime, - int successCount, - int failureCount}); -} - -/// @nodoc -class __$$StressTestSummaryImplCopyWithImpl<$Res> - extends _$StressTestSummaryCopyWithImpl<$Res, _$StressTestSummaryImpl> - implements _$$StressTestSummaryImplCopyWith<$Res> { - __$$StressTestSummaryImplCopyWithImpl(_$StressTestSummaryImpl _value, - $Res Function(_$StressTestSummaryImpl) _then) - : super(_value, _then); - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? results = null, - Object? totalDuration = null, - Object? avgResponseTime = null, - Object? successCount = null, - Object? failureCount = null, - }) { - return _then(_$StressTestSummaryImpl( - results: null == results - ? _value._results - : results // ignore: cast_nullable_to_non_nullable - as List, - totalDuration: null == totalDuration - ? _value.totalDuration - : totalDuration // ignore: cast_nullable_to_non_nullable - as Duration, - avgResponseTime: null == avgResponseTime - ? _value.avgResponseTime - : avgResponseTime // ignore: cast_nullable_to_non_nullable - as double, - successCount: null == successCount - ? _value.successCount - : successCount // ignore: cast_nullable_to_non_nullable - as int, - failureCount: null == failureCount - ? _value.failureCount - : failureCount // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$StressTestSummaryImpl implements _StressTestSummary { - const _$StressTestSummaryImpl( - {required final List results, - required this.totalDuration, - required this.avgResponseTime, - required this.successCount, - required this.failureCount}) - : _results = results; - - factory _$StressTestSummaryImpl.fromJson(Map json) => - _$$StressTestSummaryImplFromJson(json); - - final List _results; - @override - List get results { - if (_results is EqualUnmodifiableListView) return _results; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_results); - } - - @override - final Duration totalDuration; - @override - final double avgResponseTime; - @override - final int successCount; - @override - final int failureCount; - - @override - String toString() { - return 'StressTestSummary(results: $results, totalDuration: $totalDuration, avgResponseTime: $avgResponseTime, successCount: $successCount, failureCount: $failureCount)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$StressTestSummaryImpl && - const DeepCollectionEquality().equals(other._results, _results) && - (identical(other.totalDuration, totalDuration) || - other.totalDuration == totalDuration) && - (identical(other.avgResponseTime, avgResponseTime) || - other.avgResponseTime == avgResponseTime) && - (identical(other.successCount, successCount) || - other.successCount == successCount) && - (identical(other.failureCount, failureCount) || - other.failureCount == failureCount)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_results), - totalDuration, - avgResponseTime, - successCount, - failureCount); - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => - __$$StressTestSummaryImplCopyWithImpl<_$StressTestSummaryImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$StressTestSummaryImplToJson( - this, - ); - } -} - -abstract class _StressTestSummary implements StressTestSummary { - const factory _StressTestSummary( - {required final List results, - required final Duration totalDuration, - required final double avgResponseTime, - required final int successCount, - required final int failureCount}) = _$StressTestSummaryImpl; - - factory _StressTestSummary.fromJson(Map json) = - _$StressTestSummaryImpl.fromJson; - - @override - List get results; - @override - Duration get totalDuration; - @override - double get avgResponseTime; - @override - int get successCount; - @override - int get failureCount; - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/stress_test_summary.g.dart b/lib/models/stress_test/stress_test_summary.g.dart deleted file mode 100644 index 5bf4d5b48..000000000 --- a/lib/models/stress_test/stress_test_summary.g.dart +++ /dev/null @@ -1,30 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'stress_test_summary.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$StressTestSummaryImpl _$$StressTestSummaryImplFromJson( - Map json) => - _$StressTestSummaryImpl( - results: (json['results'] as List) - .map((e) => ApiRequestResult.fromJson(e as Map)) - .toList(), - totalDuration: - Duration(microseconds: (json['totalDuration'] as num).toInt()), - avgResponseTime: (json['avgResponseTime'] as num).toDouble(), - successCount: (json['successCount'] as num).toInt(), - failureCount: (json['failureCount'] as num).toInt(), - ); - -Map _$$StressTestSummaryImplToJson( - _$StressTestSummaryImpl instance) => - { - 'results': instance.results, - 'totalDuration': instance.totalDuration.inMicroseconds, - 'avgResponseTime': instance.avgResponseTime, - 'successCount': instance.successCount, - 'failureCount': instance.failureCount, - }; diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_pane_rest.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_pane_rest.dart index e323f83b8..d582a5296 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_pane_rest.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_pane_rest.dart @@ -6,6 +6,7 @@ import 'package:apidash/widgets/widgets.dart'; import 'request_headers.dart'; import 'request_params.dart'; import 'request_body.dart'; +import 'request_stress_test.dart'; class EditRestRequestPane extends ConsumerWidget { const EditRestRequestPane({super.key}); @@ -44,16 +45,19 @@ class EditRestRequestPane extends ConsumerWidget { paramLength > 0, headerLength > 0, hasBody, + false, //default false ], tabLabels: const [ kLabelURLParams, kLabelHeaders, kLabelBody, + kLabelStressTest, // label from consts.dart ], - children: const [ - EditRequestURLParams(), - EditRequestHeaders(), - EditRequestBody(), + children: [ + const EditRequestURLParams(), + const EditRequestHeaders(), + const EditRequestBody(), + const RequestStressTestPane(), ], ); } diff --git a/lib/widgets/request_pane.dart b/lib/widgets/request_pane.dart index 48535bf41..7243d1080 100644 --- a/lib/widgets/request_pane.dart +++ b/lib/widgets/request_pane.dart @@ -14,7 +14,7 @@ class RequestPane extends StatefulHookWidget { this.onTapTabBar, required this.tabLabels, required this.children, - this.showIndicators = const [false, false, false], + this.showIndicators = const [false, false, false, false], this.showViewCodeButton, }); From 97ca55c6e2963bc8dad360107ac2da1c3293e9d5 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:48:40 +0530 Subject: [PATCH 091/188] feat: add stress test config model --- .../stress_test_config.freezed.dart | 309 ++++++++++++++++++ .../stress_test/stress_test_config.g.dart | 35 ++ 2 files changed, 344 insertions(+) create mode 100644 lib/models/stress_test/stress_test_config.freezed.dart create mode 100644 lib/models/stress_test/stress_test_config.g.dart diff --git a/lib/models/stress_test/stress_test_config.freezed.dart b/lib/models/stress_test/stress_test_config.freezed.dart new file mode 100644 index 000000000..2671a5dfe --- /dev/null +++ b/lib/models/stress_test/stress_test_config.freezed.dart @@ -0,0 +1,309 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'stress_test_config.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +StressTestConfig _$StressTestConfigFromJson(Map json) { + return _StressTestConfig.fromJson(json); +} + +/// @nodoc +mixin _$StressTestConfig { + String get url => throw _privateConstructorUsedError; + String get method => throw _privateConstructorUsedError; + Map? get headers => throw _privateConstructorUsedError; + dynamic get body => throw _privateConstructorUsedError; + int get concurrentRequests => throw _privateConstructorUsedError; + Duration? get timeout => throw _privateConstructorUsedError; + bool get useIsolates => throw _privateConstructorUsedError; + + /// Serializes this StressTestConfig to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $StressTestConfigCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $StressTestConfigCopyWith<$Res> { + factory $StressTestConfigCopyWith( + StressTestConfig value, $Res Function(StressTestConfig) then) = + _$StressTestConfigCopyWithImpl<$Res, StressTestConfig>; + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + int concurrentRequests, + Duration? timeout, + bool useIsolates}); +} + +/// @nodoc +class _$StressTestConfigCopyWithImpl<$Res, $Val extends StressTestConfig> + implements $StressTestConfigCopyWith<$Res> { + _$StressTestConfigCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? concurrentRequests = null, + Object? timeout = freezed, + Object? useIsolates = null, + }) { + return _then(_value.copyWith( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value.headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + concurrentRequests: null == concurrentRequests + ? _value.concurrentRequests + : concurrentRequests // ignore: cast_nullable_to_non_nullable + as int, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + useIsolates: null == useIsolates + ? _value.useIsolates + : useIsolates // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$StressTestConfigImplCopyWith<$Res> + implements $StressTestConfigCopyWith<$Res> { + factory _$$StressTestConfigImplCopyWith(_$StressTestConfigImpl value, + $Res Function(_$StressTestConfigImpl) then) = + __$$StressTestConfigImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + int concurrentRequests, + Duration? timeout, + bool useIsolates}); +} + +/// @nodoc +class __$$StressTestConfigImplCopyWithImpl<$Res> + extends _$StressTestConfigCopyWithImpl<$Res, _$StressTestConfigImpl> + implements _$$StressTestConfigImplCopyWith<$Res> { + __$$StressTestConfigImplCopyWithImpl(_$StressTestConfigImpl _value, + $Res Function(_$StressTestConfigImpl) _then) + : super(_value, _then); + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? concurrentRequests = null, + Object? timeout = freezed, + Object? useIsolates = null, + }) { + return _then(_$StressTestConfigImpl( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value._headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + concurrentRequests: null == concurrentRequests + ? _value.concurrentRequests + : concurrentRequests // ignore: cast_nullable_to_non_nullable + as int, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + useIsolates: null == useIsolates + ? _value.useIsolates + : useIsolates // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$StressTestConfigImpl implements _StressTestConfig { + const _$StressTestConfigImpl( + {required this.url, + required this.method, + final Map? headers, + this.body, + required this.concurrentRequests, + this.timeout, + this.useIsolates = false}) + : _headers = headers; + + factory _$StressTestConfigImpl.fromJson(Map json) => + _$$StressTestConfigImplFromJson(json); + + @override + final String url; + @override + final String method; + final Map? _headers; + @override + Map? get headers { + final value = _headers; + if (value == null) return null; + if (_headers is EqualUnmodifiableMapView) return _headers; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); + } + + @override + final dynamic body; + @override + final int concurrentRequests; + @override + final Duration? timeout; + @override + @JsonKey() + final bool useIsolates; + + @override + String toString() { + return 'StressTestConfig(url: $url, method: $method, headers: $headers, body: $body, concurrentRequests: $concurrentRequests, timeout: $timeout, useIsolates: $useIsolates)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$StressTestConfigImpl && + (identical(other.url, url) || other.url == url) && + (identical(other.method, method) || other.method == method) && + const DeepCollectionEquality().equals(other._headers, _headers) && + const DeepCollectionEquality().equals(other.body, body) && + (identical(other.concurrentRequests, concurrentRequests) || + other.concurrentRequests == concurrentRequests) && + (identical(other.timeout, timeout) || other.timeout == timeout) && + (identical(other.useIsolates, useIsolates) || + other.useIsolates == useIsolates)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + url, + method, + const DeepCollectionEquality().hash(_headers), + const DeepCollectionEquality().hash(body), + concurrentRequests, + timeout, + useIsolates); + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => + __$$StressTestConfigImplCopyWithImpl<_$StressTestConfigImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$StressTestConfigImplToJson( + this, + ); + } +} + +abstract class _StressTestConfig implements StressTestConfig { + const factory _StressTestConfig( + {required final String url, + required final String method, + final Map? headers, + final dynamic body, + required final int concurrentRequests, + final Duration? timeout, + final bool useIsolates}) = _$StressTestConfigImpl; + + factory _StressTestConfig.fromJson(Map json) = + _$StressTestConfigImpl.fromJson; + + @override + String get url; + @override + String get method; + @override + Map? get headers; + @override + dynamic get body; + @override + int get concurrentRequests; + @override + Duration? get timeout; + @override + bool get useIsolates; + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/stress_test_config.g.dart b/lib/models/stress_test/stress_test_config.g.dart new file mode 100644 index 000000000..6193507c3 --- /dev/null +++ b/lib/models/stress_test/stress_test_config.g.dart @@ -0,0 +1,35 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stress_test_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$StressTestConfigImpl _$$StressTestConfigImplFromJson( + Map json) => + _$StressTestConfigImpl( + url: json['url'] as String, + method: json['method'] as String, + headers: (json['headers'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + body: json['body'], + concurrentRequests: (json['concurrentRequests'] as num).toInt(), + timeout: json['timeout'] == null + ? null + : Duration(microseconds: (json['timeout'] as num).toInt()), + useIsolates: json['useIsolates'] as bool? ?? false, + ); + +Map _$$StressTestConfigImplToJson( + _$StressTestConfigImpl instance) => + { + 'url': instance.url, + 'method': instance.method, + 'headers': instance.headers, + 'body': instance.body, + 'concurrentRequests': instance.concurrentRequests, + 'timeout': instance.timeout?.inMicroseconds, + 'useIsolates': instance.useIsolates, + }; From df7d747852b27f8135323bbe6a44a290946c0ca7 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:49:38 +0530 Subject: [PATCH 092/188] feat: add api request result model --- .../api_request_result.freezed.dart | 224 ++++++++++++++++++ .../stress_test/api_request_result.g.dart | 25 ++ 2 files changed, 249 insertions(+) create mode 100644 lib/models/stress_test/api_request_result.freezed.dart create mode 100644 lib/models/stress_test/api_request_result.g.dart diff --git a/lib/models/stress_test/api_request_result.freezed.dart b/lib/models/stress_test/api_request_result.freezed.dart new file mode 100644 index 000000000..6bdee80a3 --- /dev/null +++ b/lib/models/stress_test/api_request_result.freezed.dart @@ -0,0 +1,224 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'api_request_result.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +ApiRequestResult _$ApiRequestResultFromJson(Map json) { + return _ApiRequestResult.fromJson(json); +} + +/// @nodoc +mixin _$ApiRequestResult { + int get statusCode => throw _privateConstructorUsedError; + String get body => throw _privateConstructorUsedError; + Duration get duration => throw _privateConstructorUsedError; + String? get error => throw _privateConstructorUsedError; + + /// Serializes this ApiRequestResult to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $ApiRequestResultCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ApiRequestResultCopyWith<$Res> { + factory $ApiRequestResultCopyWith( + ApiRequestResult value, $Res Function(ApiRequestResult) then) = + _$ApiRequestResultCopyWithImpl<$Res, ApiRequestResult>; + @useResult + $Res call({int statusCode, String body, Duration duration, String? error}); +} + +/// @nodoc +class _$ApiRequestResultCopyWithImpl<$Res, $Val extends ApiRequestResult> + implements $ApiRequestResultCopyWith<$Res> { + _$ApiRequestResultCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? statusCode = null, + Object? body = null, + Object? duration = null, + Object? error = freezed, + }) { + return _then(_value.copyWith( + statusCode: null == statusCode + ? _value.statusCode + : statusCode // ignore: cast_nullable_to_non_nullable + as int, + body: null == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as String, + duration: null == duration + ? _value.duration + : duration // ignore: cast_nullable_to_non_nullable + as Duration, + error: freezed == error + ? _value.error + : error // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$ApiRequestResultImplCopyWith<$Res> + implements $ApiRequestResultCopyWith<$Res> { + factory _$$ApiRequestResultImplCopyWith(_$ApiRequestResultImpl value, + $Res Function(_$ApiRequestResultImpl) then) = + __$$ApiRequestResultImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({int statusCode, String body, Duration duration, String? error}); +} + +/// @nodoc +class __$$ApiRequestResultImplCopyWithImpl<$Res> + extends _$ApiRequestResultCopyWithImpl<$Res, _$ApiRequestResultImpl> + implements _$$ApiRequestResultImplCopyWith<$Res> { + __$$ApiRequestResultImplCopyWithImpl(_$ApiRequestResultImpl _value, + $Res Function(_$ApiRequestResultImpl) _then) + : super(_value, _then); + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? statusCode = null, + Object? body = null, + Object? duration = null, + Object? error = freezed, + }) { + return _then(_$ApiRequestResultImpl( + statusCode: null == statusCode + ? _value.statusCode + : statusCode // ignore: cast_nullable_to_non_nullable + as int, + body: null == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as String, + duration: null == duration + ? _value.duration + : duration // ignore: cast_nullable_to_non_nullable + as Duration, + error: freezed == error + ? _value.error + : error // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ApiRequestResultImpl implements _ApiRequestResult { + const _$ApiRequestResultImpl( + {required this.statusCode, + required this.body, + required this.duration, + this.error}); + + factory _$ApiRequestResultImpl.fromJson(Map json) => + _$$ApiRequestResultImplFromJson(json); + + @override + final int statusCode; + @override + final String body; + @override + final Duration duration; + @override + final String? error; + + @override + String toString() { + return 'ApiRequestResult(statusCode: $statusCode, body: $body, duration: $duration, error: $error)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ApiRequestResultImpl && + (identical(other.statusCode, statusCode) || + other.statusCode == statusCode) && + (identical(other.body, body) || other.body == body) && + (identical(other.duration, duration) || + other.duration == duration) && + (identical(other.error, error) || other.error == error)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => + Object.hash(runtimeType, statusCode, body, duration, error); + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => + __$$ApiRequestResultImplCopyWithImpl<_$ApiRequestResultImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$ApiRequestResultImplToJson( + this, + ); + } +} + +abstract class _ApiRequestResult implements ApiRequestResult { + const factory _ApiRequestResult( + {required final int statusCode, + required final String body, + required final Duration duration, + final String? error}) = _$ApiRequestResultImpl; + + factory _ApiRequestResult.fromJson(Map json) = + _$ApiRequestResultImpl.fromJson; + + @override + int get statusCode; + @override + String get body; + @override + Duration get duration; + @override + String? get error; + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/api_request_result.g.dart b/lib/models/stress_test/api_request_result.g.dart new file mode 100644 index 000000000..4182a8e58 --- /dev/null +++ b/lib/models/stress_test/api_request_result.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'api_request_result.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$ApiRequestResultImpl _$$ApiRequestResultImplFromJson( + Map json) => + _$ApiRequestResultImpl( + statusCode: (json['statusCode'] as num).toInt(), + body: json['body'] as String, + duration: Duration(microseconds: (json['duration'] as num).toInt()), + error: json['error'] as String?, + ); + +Map _$$ApiRequestResultImplToJson( + _$ApiRequestResultImpl instance) => + { + 'statusCode': instance.statusCode, + 'body': instance.body, + 'duration': instance.duration.inMicroseconds, + 'error': instance.error, + }; From 779e7e9258eae6a6612336f2629b501365401372 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:50:18 +0530 Subject: [PATCH 093/188] feat: add isolate model for stress test --- .../stress_test/isolate_message.freezed.dart | 264 ++++++++++++++++++ lib/models/stress_test/isolate_message.g.dart | 30 ++ 2 files changed, 294 insertions(+) create mode 100644 lib/models/stress_test/isolate_message.freezed.dart create mode 100644 lib/models/stress_test/isolate_message.g.dart diff --git a/lib/models/stress_test/isolate_message.freezed.dart b/lib/models/stress_test/isolate_message.freezed.dart new file mode 100644 index 000000000..c3abc6857 --- /dev/null +++ b/lib/models/stress_test/isolate_message.freezed.dart @@ -0,0 +1,264 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'isolate_message.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +IsolateMessage _$IsolateMessageFromJson(Map json) { + return _IsolateMessage.fromJson(json); +} + +/// @nodoc +mixin _$IsolateMessage { + String get url => throw _privateConstructorUsedError; + String get method => throw _privateConstructorUsedError; + Map? get headers => throw _privateConstructorUsedError; + dynamic get body => throw _privateConstructorUsedError; + Duration? get timeout => throw _privateConstructorUsedError; + + /// Serializes this IsolateMessage to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $IsolateMessageCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $IsolateMessageCopyWith<$Res> { + factory $IsolateMessageCopyWith( + IsolateMessage value, $Res Function(IsolateMessage) then) = + _$IsolateMessageCopyWithImpl<$Res, IsolateMessage>; + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + Duration? timeout}); +} + +/// @nodoc +class _$IsolateMessageCopyWithImpl<$Res, $Val extends IsolateMessage> + implements $IsolateMessageCopyWith<$Res> { + _$IsolateMessageCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? timeout = freezed, + }) { + return _then(_value.copyWith( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value.headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$IsolateMessageImplCopyWith<$Res> + implements $IsolateMessageCopyWith<$Res> { + factory _$$IsolateMessageImplCopyWith(_$IsolateMessageImpl value, + $Res Function(_$IsolateMessageImpl) then) = + __$$IsolateMessageImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + Duration? timeout}); +} + +/// @nodoc +class __$$IsolateMessageImplCopyWithImpl<$Res> + extends _$IsolateMessageCopyWithImpl<$Res, _$IsolateMessageImpl> + implements _$$IsolateMessageImplCopyWith<$Res> { + __$$IsolateMessageImplCopyWithImpl( + _$IsolateMessageImpl _value, $Res Function(_$IsolateMessageImpl) _then) + : super(_value, _then); + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? timeout = freezed, + }) { + return _then(_$IsolateMessageImpl( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value._headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$IsolateMessageImpl implements _IsolateMessage { + const _$IsolateMessageImpl( + {required this.url, + required this.method, + final Map? headers, + this.body, + this.timeout}) + : _headers = headers; + + factory _$IsolateMessageImpl.fromJson(Map json) => + _$$IsolateMessageImplFromJson(json); + + @override + final String url; + @override + final String method; + final Map? _headers; + @override + Map? get headers { + final value = _headers; + if (value == null) return null; + if (_headers is EqualUnmodifiableMapView) return _headers; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); + } + + @override + final dynamic body; + @override + final Duration? timeout; + + @override + String toString() { + return 'IsolateMessage(url: $url, method: $method, headers: $headers, body: $body, timeout: $timeout)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$IsolateMessageImpl && + (identical(other.url, url) || other.url == url) && + (identical(other.method, method) || other.method == method) && + const DeepCollectionEquality().equals(other._headers, _headers) && + const DeepCollectionEquality().equals(other.body, body) && + (identical(other.timeout, timeout) || other.timeout == timeout)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + url, + method, + const DeepCollectionEquality().hash(_headers), + const DeepCollectionEquality().hash(body), + timeout); + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => + __$$IsolateMessageImplCopyWithImpl<_$IsolateMessageImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$IsolateMessageImplToJson( + this, + ); + } +} + +abstract class _IsolateMessage implements IsolateMessage { + const factory _IsolateMessage( + {required final String url, + required final String method, + final Map? headers, + final dynamic body, + final Duration? timeout}) = _$IsolateMessageImpl; + + factory _IsolateMessage.fromJson(Map json) = + _$IsolateMessageImpl.fromJson; + + @override + String get url; + @override + String get method; + @override + Map? get headers; + @override + dynamic get body; + @override + Duration? get timeout; + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/isolate_message.g.dart b/lib/models/stress_test/isolate_message.g.dart new file mode 100644 index 000000000..e5f403ea1 --- /dev/null +++ b/lib/models/stress_test/isolate_message.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'isolate_message.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$IsolateMessageImpl _$$IsolateMessageImplFromJson(Map json) => + _$IsolateMessageImpl( + url: json['url'] as String, + method: json['method'] as String, + headers: (json['headers'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + body: json['body'], + timeout: json['timeout'] == null + ? null + : Duration(microseconds: (json['timeout'] as num).toInt()), + ); + +Map _$$IsolateMessageImplToJson( + _$IsolateMessageImpl instance) => + { + 'url': instance.url, + 'method': instance.method, + 'headers': instance.headers, + 'body': instance.body, + 'timeout': instance.timeout?.inMicroseconds, + }; From d22d5a2e196b20fb30debfa2509ec114d26e2b59 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:51:01 +0530 Subject: [PATCH 094/188] feat: add stress test summer model --- .../stress_test_summary.freezed.dart | 266 ++++++++++++++++++ .../stress_test/stress_test_summary.g.dart | 30 ++ 2 files changed, 296 insertions(+) create mode 100644 lib/models/stress_test/stress_test_summary.freezed.dart create mode 100644 lib/models/stress_test/stress_test_summary.g.dart diff --git a/lib/models/stress_test/stress_test_summary.freezed.dart b/lib/models/stress_test/stress_test_summary.freezed.dart new file mode 100644 index 000000000..b5fd37df1 --- /dev/null +++ b/lib/models/stress_test/stress_test_summary.freezed.dart @@ -0,0 +1,266 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'stress_test_summary.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +StressTestSummary _$StressTestSummaryFromJson(Map json) { + return _StressTestSummary.fromJson(json); +} + +/// @nodoc +mixin _$StressTestSummary { + List get results => throw _privateConstructorUsedError; + Duration get totalDuration => throw _privateConstructorUsedError; + double get avgResponseTime => throw _privateConstructorUsedError; + int get successCount => throw _privateConstructorUsedError; + int get failureCount => throw _privateConstructorUsedError; + + /// Serializes this StressTestSummary to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $StressTestSummaryCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $StressTestSummaryCopyWith<$Res> { + factory $StressTestSummaryCopyWith( + StressTestSummary value, $Res Function(StressTestSummary) then) = + _$StressTestSummaryCopyWithImpl<$Res, StressTestSummary>; + @useResult + $Res call( + {List results, + Duration totalDuration, + double avgResponseTime, + int successCount, + int failureCount}); +} + +/// @nodoc +class _$StressTestSummaryCopyWithImpl<$Res, $Val extends StressTestSummary> + implements $StressTestSummaryCopyWith<$Res> { + _$StressTestSummaryCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? results = null, + Object? totalDuration = null, + Object? avgResponseTime = null, + Object? successCount = null, + Object? failureCount = null, + }) { + return _then(_value.copyWith( + results: null == results + ? _value.results + : results // ignore: cast_nullable_to_non_nullable + as List, + totalDuration: null == totalDuration + ? _value.totalDuration + : totalDuration // ignore: cast_nullable_to_non_nullable + as Duration, + avgResponseTime: null == avgResponseTime + ? _value.avgResponseTime + : avgResponseTime // ignore: cast_nullable_to_non_nullable + as double, + successCount: null == successCount + ? _value.successCount + : successCount // ignore: cast_nullable_to_non_nullable + as int, + failureCount: null == failureCount + ? _value.failureCount + : failureCount // ignore: cast_nullable_to_non_nullable + as int, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$StressTestSummaryImplCopyWith<$Res> + implements $StressTestSummaryCopyWith<$Res> { + factory _$$StressTestSummaryImplCopyWith(_$StressTestSummaryImpl value, + $Res Function(_$StressTestSummaryImpl) then) = + __$$StressTestSummaryImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {List results, + Duration totalDuration, + double avgResponseTime, + int successCount, + int failureCount}); +} + +/// @nodoc +class __$$StressTestSummaryImplCopyWithImpl<$Res> + extends _$StressTestSummaryCopyWithImpl<$Res, _$StressTestSummaryImpl> + implements _$$StressTestSummaryImplCopyWith<$Res> { + __$$StressTestSummaryImplCopyWithImpl(_$StressTestSummaryImpl _value, + $Res Function(_$StressTestSummaryImpl) _then) + : super(_value, _then); + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? results = null, + Object? totalDuration = null, + Object? avgResponseTime = null, + Object? successCount = null, + Object? failureCount = null, + }) { + return _then(_$StressTestSummaryImpl( + results: null == results + ? _value._results + : results // ignore: cast_nullable_to_non_nullable + as List, + totalDuration: null == totalDuration + ? _value.totalDuration + : totalDuration // ignore: cast_nullable_to_non_nullable + as Duration, + avgResponseTime: null == avgResponseTime + ? _value.avgResponseTime + : avgResponseTime // ignore: cast_nullable_to_non_nullable + as double, + successCount: null == successCount + ? _value.successCount + : successCount // ignore: cast_nullable_to_non_nullable + as int, + failureCount: null == failureCount + ? _value.failureCount + : failureCount // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$StressTestSummaryImpl implements _StressTestSummary { + const _$StressTestSummaryImpl( + {required final List results, + required this.totalDuration, + required this.avgResponseTime, + required this.successCount, + required this.failureCount}) + : _results = results; + + factory _$StressTestSummaryImpl.fromJson(Map json) => + _$$StressTestSummaryImplFromJson(json); + + final List _results; + @override + List get results { + if (_results is EqualUnmodifiableListView) return _results; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_results); + } + + @override + final Duration totalDuration; + @override + final double avgResponseTime; + @override + final int successCount; + @override + final int failureCount; + + @override + String toString() { + return 'StressTestSummary(results: $results, totalDuration: $totalDuration, avgResponseTime: $avgResponseTime, successCount: $successCount, failureCount: $failureCount)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$StressTestSummaryImpl && + const DeepCollectionEquality().equals(other._results, _results) && + (identical(other.totalDuration, totalDuration) || + other.totalDuration == totalDuration) && + (identical(other.avgResponseTime, avgResponseTime) || + other.avgResponseTime == avgResponseTime) && + (identical(other.successCount, successCount) || + other.successCount == successCount) && + (identical(other.failureCount, failureCount) || + other.failureCount == failureCount)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(_results), + totalDuration, + avgResponseTime, + successCount, + failureCount); + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => + __$$StressTestSummaryImplCopyWithImpl<_$StressTestSummaryImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$StressTestSummaryImplToJson( + this, + ); + } +} + +abstract class _StressTestSummary implements StressTestSummary { + const factory _StressTestSummary( + {required final List results, + required final Duration totalDuration, + required final double avgResponseTime, + required final int successCount, + required final int failureCount}) = _$StressTestSummaryImpl; + + factory _StressTestSummary.fromJson(Map json) = + _$StressTestSummaryImpl.fromJson; + + @override + List get results; + @override + Duration get totalDuration; + @override + double get avgResponseTime; + @override + int get successCount; + @override + int get failureCount; + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/stress_test_summary.g.dart b/lib/models/stress_test/stress_test_summary.g.dart new file mode 100644 index 000000000..5bf4d5b48 --- /dev/null +++ b/lib/models/stress_test/stress_test_summary.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stress_test_summary.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$StressTestSummaryImpl _$$StressTestSummaryImplFromJson( + Map json) => + _$StressTestSummaryImpl( + results: (json['results'] as List) + .map((e) => ApiRequestResult.fromJson(e as Map)) + .toList(), + totalDuration: + Duration(microseconds: (json['totalDuration'] as num).toInt()), + avgResponseTime: (json['avgResponseTime'] as num).toDouble(), + successCount: (json['successCount'] as num).toInt(), + failureCount: (json['failureCount'] as num).toInt(), + ); + +Map _$$StressTestSummaryImplToJson( + _$StressTestSummaryImpl instance) => + { + 'results': instance.results, + 'totalDuration': instance.totalDuration.inMicroseconds, + 'avgResponseTime': instance.avgResponseTime, + 'successCount': instance.successCount, + 'failureCount': instance.failureCount, + }; From 8f54f7a5a10d0c3e64f68f4000bbf43286b754b1 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 13:53:49 +0530 Subject: [PATCH 095/188] chore: implement the stress test UI in request pane --- .../api_request_result.freezed.dart | 224 ------------- .../stress_test/api_request_result.g.dart | 25 -- .../stress_test/isolate_message.freezed.dart | 264 --------------- lib/models/stress_test/isolate_message.g.dart | 30 -- .../stress_test_config.freezed.dart | 309 ------------------ .../stress_test/stress_test_config.g.dart | 35 -- .../stress_test_summary.freezed.dart | 266 --------------- .../stress_test/stress_test_summary.g.dart | 30 -- 8 files changed, 1183 deletions(-) delete mode 100644 lib/models/stress_test/api_request_result.freezed.dart delete mode 100644 lib/models/stress_test/api_request_result.g.dart delete mode 100644 lib/models/stress_test/isolate_message.freezed.dart delete mode 100644 lib/models/stress_test/isolate_message.g.dart delete mode 100644 lib/models/stress_test/stress_test_config.freezed.dart delete mode 100644 lib/models/stress_test/stress_test_config.g.dart delete mode 100644 lib/models/stress_test/stress_test_summary.freezed.dart delete mode 100644 lib/models/stress_test/stress_test_summary.g.dart diff --git a/lib/models/stress_test/api_request_result.freezed.dart b/lib/models/stress_test/api_request_result.freezed.dart deleted file mode 100644 index 6bdee80a3..000000000 --- a/lib/models/stress_test/api_request_result.freezed.dart +++ /dev/null @@ -1,224 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'api_request_result.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -ApiRequestResult _$ApiRequestResultFromJson(Map json) { - return _ApiRequestResult.fromJson(json); -} - -/// @nodoc -mixin _$ApiRequestResult { - int get statusCode => throw _privateConstructorUsedError; - String get body => throw _privateConstructorUsedError; - Duration get duration => throw _privateConstructorUsedError; - String? get error => throw _privateConstructorUsedError; - - /// Serializes this ApiRequestResult to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ApiRequestResultCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ApiRequestResultCopyWith<$Res> { - factory $ApiRequestResultCopyWith( - ApiRequestResult value, $Res Function(ApiRequestResult) then) = - _$ApiRequestResultCopyWithImpl<$Res, ApiRequestResult>; - @useResult - $Res call({int statusCode, String body, Duration duration, String? error}); -} - -/// @nodoc -class _$ApiRequestResultCopyWithImpl<$Res, $Val extends ApiRequestResult> - implements $ApiRequestResultCopyWith<$Res> { - _$ApiRequestResultCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? statusCode = null, - Object? body = null, - Object? duration = null, - Object? error = freezed, - }) { - return _then(_value.copyWith( - statusCode: null == statusCode - ? _value.statusCode - : statusCode // ignore: cast_nullable_to_non_nullable - as int, - body: null == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as String, - duration: null == duration - ? _value.duration - : duration // ignore: cast_nullable_to_non_nullable - as Duration, - error: freezed == error - ? _value.error - : error // ignore: cast_nullable_to_non_nullable - as String?, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$ApiRequestResultImplCopyWith<$Res> - implements $ApiRequestResultCopyWith<$Res> { - factory _$$ApiRequestResultImplCopyWith(_$ApiRequestResultImpl value, - $Res Function(_$ApiRequestResultImpl) then) = - __$$ApiRequestResultImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({int statusCode, String body, Duration duration, String? error}); -} - -/// @nodoc -class __$$ApiRequestResultImplCopyWithImpl<$Res> - extends _$ApiRequestResultCopyWithImpl<$Res, _$ApiRequestResultImpl> - implements _$$ApiRequestResultImplCopyWith<$Res> { - __$$ApiRequestResultImplCopyWithImpl(_$ApiRequestResultImpl _value, - $Res Function(_$ApiRequestResultImpl) _then) - : super(_value, _then); - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? statusCode = null, - Object? body = null, - Object? duration = null, - Object? error = freezed, - }) { - return _then(_$ApiRequestResultImpl( - statusCode: null == statusCode - ? _value.statusCode - : statusCode // ignore: cast_nullable_to_non_nullable - as int, - body: null == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as String, - duration: null == duration - ? _value.duration - : duration // ignore: cast_nullable_to_non_nullable - as Duration, - error: freezed == error - ? _value.error - : error // ignore: cast_nullable_to_non_nullable - as String?, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$ApiRequestResultImpl implements _ApiRequestResult { - const _$ApiRequestResultImpl( - {required this.statusCode, - required this.body, - required this.duration, - this.error}); - - factory _$ApiRequestResultImpl.fromJson(Map json) => - _$$ApiRequestResultImplFromJson(json); - - @override - final int statusCode; - @override - final String body; - @override - final Duration duration; - @override - final String? error; - - @override - String toString() { - return 'ApiRequestResult(statusCode: $statusCode, body: $body, duration: $duration, error: $error)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ApiRequestResultImpl && - (identical(other.statusCode, statusCode) || - other.statusCode == statusCode) && - (identical(other.body, body) || other.body == body) && - (identical(other.duration, duration) || - other.duration == duration) && - (identical(other.error, error) || other.error == error)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => - Object.hash(runtimeType, statusCode, body, duration, error); - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => - __$$ApiRequestResultImplCopyWithImpl<_$ApiRequestResultImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$ApiRequestResultImplToJson( - this, - ); - } -} - -abstract class _ApiRequestResult implements ApiRequestResult { - const factory _ApiRequestResult( - {required final int statusCode, - required final String body, - required final Duration duration, - final String? error}) = _$ApiRequestResultImpl; - - factory _ApiRequestResult.fromJson(Map json) = - _$ApiRequestResultImpl.fromJson; - - @override - int get statusCode; - @override - String get body; - @override - Duration get duration; - @override - String? get error; - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/api_request_result.g.dart b/lib/models/stress_test/api_request_result.g.dart deleted file mode 100644 index 4182a8e58..000000000 --- a/lib/models/stress_test/api_request_result.g.dart +++ /dev/null @@ -1,25 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'api_request_result.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$ApiRequestResultImpl _$$ApiRequestResultImplFromJson( - Map json) => - _$ApiRequestResultImpl( - statusCode: (json['statusCode'] as num).toInt(), - body: json['body'] as String, - duration: Duration(microseconds: (json['duration'] as num).toInt()), - error: json['error'] as String?, - ); - -Map _$$ApiRequestResultImplToJson( - _$ApiRequestResultImpl instance) => - { - 'statusCode': instance.statusCode, - 'body': instance.body, - 'duration': instance.duration.inMicroseconds, - 'error': instance.error, - }; diff --git a/lib/models/stress_test/isolate_message.freezed.dart b/lib/models/stress_test/isolate_message.freezed.dart deleted file mode 100644 index c3abc6857..000000000 --- a/lib/models/stress_test/isolate_message.freezed.dart +++ /dev/null @@ -1,264 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'isolate_message.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -IsolateMessage _$IsolateMessageFromJson(Map json) { - return _IsolateMessage.fromJson(json); -} - -/// @nodoc -mixin _$IsolateMessage { - String get url => throw _privateConstructorUsedError; - String get method => throw _privateConstructorUsedError; - Map? get headers => throw _privateConstructorUsedError; - dynamic get body => throw _privateConstructorUsedError; - Duration? get timeout => throw _privateConstructorUsedError; - - /// Serializes this IsolateMessage to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $IsolateMessageCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $IsolateMessageCopyWith<$Res> { - factory $IsolateMessageCopyWith( - IsolateMessage value, $Res Function(IsolateMessage) then) = - _$IsolateMessageCopyWithImpl<$Res, IsolateMessage>; - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - Duration? timeout}); -} - -/// @nodoc -class _$IsolateMessageCopyWithImpl<$Res, $Val extends IsolateMessage> - implements $IsolateMessageCopyWith<$Res> { - _$IsolateMessageCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? timeout = freezed, - }) { - return _then(_value.copyWith( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value.headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$IsolateMessageImplCopyWith<$Res> - implements $IsolateMessageCopyWith<$Res> { - factory _$$IsolateMessageImplCopyWith(_$IsolateMessageImpl value, - $Res Function(_$IsolateMessageImpl) then) = - __$$IsolateMessageImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - Duration? timeout}); -} - -/// @nodoc -class __$$IsolateMessageImplCopyWithImpl<$Res> - extends _$IsolateMessageCopyWithImpl<$Res, _$IsolateMessageImpl> - implements _$$IsolateMessageImplCopyWith<$Res> { - __$$IsolateMessageImplCopyWithImpl( - _$IsolateMessageImpl _value, $Res Function(_$IsolateMessageImpl) _then) - : super(_value, _then); - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? timeout = freezed, - }) { - return _then(_$IsolateMessageImpl( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value._headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$IsolateMessageImpl implements _IsolateMessage { - const _$IsolateMessageImpl( - {required this.url, - required this.method, - final Map? headers, - this.body, - this.timeout}) - : _headers = headers; - - factory _$IsolateMessageImpl.fromJson(Map json) => - _$$IsolateMessageImplFromJson(json); - - @override - final String url; - @override - final String method; - final Map? _headers; - @override - Map? get headers { - final value = _headers; - if (value == null) return null; - if (_headers is EqualUnmodifiableMapView) return _headers; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(value); - } - - @override - final dynamic body; - @override - final Duration? timeout; - - @override - String toString() { - return 'IsolateMessage(url: $url, method: $method, headers: $headers, body: $body, timeout: $timeout)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$IsolateMessageImpl && - (identical(other.url, url) || other.url == url) && - (identical(other.method, method) || other.method == method) && - const DeepCollectionEquality().equals(other._headers, _headers) && - const DeepCollectionEquality().equals(other.body, body) && - (identical(other.timeout, timeout) || other.timeout == timeout)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - url, - method, - const DeepCollectionEquality().hash(_headers), - const DeepCollectionEquality().hash(body), - timeout); - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => - __$$IsolateMessageImplCopyWithImpl<_$IsolateMessageImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$IsolateMessageImplToJson( - this, - ); - } -} - -abstract class _IsolateMessage implements IsolateMessage { - const factory _IsolateMessage( - {required final String url, - required final String method, - final Map? headers, - final dynamic body, - final Duration? timeout}) = _$IsolateMessageImpl; - - factory _IsolateMessage.fromJson(Map json) = - _$IsolateMessageImpl.fromJson; - - @override - String get url; - @override - String get method; - @override - Map? get headers; - @override - dynamic get body; - @override - Duration? get timeout; - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/isolate_message.g.dart b/lib/models/stress_test/isolate_message.g.dart deleted file mode 100644 index e5f403ea1..000000000 --- a/lib/models/stress_test/isolate_message.g.dart +++ /dev/null @@ -1,30 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'isolate_message.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$IsolateMessageImpl _$$IsolateMessageImplFromJson(Map json) => - _$IsolateMessageImpl( - url: json['url'] as String, - method: json['method'] as String, - headers: (json['headers'] as Map?)?.map( - (k, e) => MapEntry(k, e as String), - ), - body: json['body'], - timeout: json['timeout'] == null - ? null - : Duration(microseconds: (json['timeout'] as num).toInt()), - ); - -Map _$$IsolateMessageImplToJson( - _$IsolateMessageImpl instance) => - { - 'url': instance.url, - 'method': instance.method, - 'headers': instance.headers, - 'body': instance.body, - 'timeout': instance.timeout?.inMicroseconds, - }; diff --git a/lib/models/stress_test/stress_test_config.freezed.dart b/lib/models/stress_test/stress_test_config.freezed.dart deleted file mode 100644 index 2671a5dfe..000000000 --- a/lib/models/stress_test/stress_test_config.freezed.dart +++ /dev/null @@ -1,309 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'stress_test_config.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -StressTestConfig _$StressTestConfigFromJson(Map json) { - return _StressTestConfig.fromJson(json); -} - -/// @nodoc -mixin _$StressTestConfig { - String get url => throw _privateConstructorUsedError; - String get method => throw _privateConstructorUsedError; - Map? get headers => throw _privateConstructorUsedError; - dynamic get body => throw _privateConstructorUsedError; - int get concurrentRequests => throw _privateConstructorUsedError; - Duration? get timeout => throw _privateConstructorUsedError; - bool get useIsolates => throw _privateConstructorUsedError; - - /// Serializes this StressTestConfig to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $StressTestConfigCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $StressTestConfigCopyWith<$Res> { - factory $StressTestConfigCopyWith( - StressTestConfig value, $Res Function(StressTestConfig) then) = - _$StressTestConfigCopyWithImpl<$Res, StressTestConfig>; - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - int concurrentRequests, - Duration? timeout, - bool useIsolates}); -} - -/// @nodoc -class _$StressTestConfigCopyWithImpl<$Res, $Val extends StressTestConfig> - implements $StressTestConfigCopyWith<$Res> { - _$StressTestConfigCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? concurrentRequests = null, - Object? timeout = freezed, - Object? useIsolates = null, - }) { - return _then(_value.copyWith( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value.headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - concurrentRequests: null == concurrentRequests - ? _value.concurrentRequests - : concurrentRequests // ignore: cast_nullable_to_non_nullable - as int, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - useIsolates: null == useIsolates - ? _value.useIsolates - : useIsolates // ignore: cast_nullable_to_non_nullable - as bool, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$StressTestConfigImplCopyWith<$Res> - implements $StressTestConfigCopyWith<$Res> { - factory _$$StressTestConfigImplCopyWith(_$StressTestConfigImpl value, - $Res Function(_$StressTestConfigImpl) then) = - __$$StressTestConfigImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - int concurrentRequests, - Duration? timeout, - bool useIsolates}); -} - -/// @nodoc -class __$$StressTestConfigImplCopyWithImpl<$Res> - extends _$StressTestConfigCopyWithImpl<$Res, _$StressTestConfigImpl> - implements _$$StressTestConfigImplCopyWith<$Res> { - __$$StressTestConfigImplCopyWithImpl(_$StressTestConfigImpl _value, - $Res Function(_$StressTestConfigImpl) _then) - : super(_value, _then); - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? concurrentRequests = null, - Object? timeout = freezed, - Object? useIsolates = null, - }) { - return _then(_$StressTestConfigImpl( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value._headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - concurrentRequests: null == concurrentRequests - ? _value.concurrentRequests - : concurrentRequests // ignore: cast_nullable_to_non_nullable - as int, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - useIsolates: null == useIsolates - ? _value.useIsolates - : useIsolates // ignore: cast_nullable_to_non_nullable - as bool, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$StressTestConfigImpl implements _StressTestConfig { - const _$StressTestConfigImpl( - {required this.url, - required this.method, - final Map? headers, - this.body, - required this.concurrentRequests, - this.timeout, - this.useIsolates = false}) - : _headers = headers; - - factory _$StressTestConfigImpl.fromJson(Map json) => - _$$StressTestConfigImplFromJson(json); - - @override - final String url; - @override - final String method; - final Map? _headers; - @override - Map? get headers { - final value = _headers; - if (value == null) return null; - if (_headers is EqualUnmodifiableMapView) return _headers; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(value); - } - - @override - final dynamic body; - @override - final int concurrentRequests; - @override - final Duration? timeout; - @override - @JsonKey() - final bool useIsolates; - - @override - String toString() { - return 'StressTestConfig(url: $url, method: $method, headers: $headers, body: $body, concurrentRequests: $concurrentRequests, timeout: $timeout, useIsolates: $useIsolates)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$StressTestConfigImpl && - (identical(other.url, url) || other.url == url) && - (identical(other.method, method) || other.method == method) && - const DeepCollectionEquality().equals(other._headers, _headers) && - const DeepCollectionEquality().equals(other.body, body) && - (identical(other.concurrentRequests, concurrentRequests) || - other.concurrentRequests == concurrentRequests) && - (identical(other.timeout, timeout) || other.timeout == timeout) && - (identical(other.useIsolates, useIsolates) || - other.useIsolates == useIsolates)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - url, - method, - const DeepCollectionEquality().hash(_headers), - const DeepCollectionEquality().hash(body), - concurrentRequests, - timeout, - useIsolates); - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => - __$$StressTestConfigImplCopyWithImpl<_$StressTestConfigImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$StressTestConfigImplToJson( - this, - ); - } -} - -abstract class _StressTestConfig implements StressTestConfig { - const factory _StressTestConfig( - {required final String url, - required final String method, - final Map? headers, - final dynamic body, - required final int concurrentRequests, - final Duration? timeout, - final bool useIsolates}) = _$StressTestConfigImpl; - - factory _StressTestConfig.fromJson(Map json) = - _$StressTestConfigImpl.fromJson; - - @override - String get url; - @override - String get method; - @override - Map? get headers; - @override - dynamic get body; - @override - int get concurrentRequests; - @override - Duration? get timeout; - @override - bool get useIsolates; - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/stress_test_config.g.dart b/lib/models/stress_test/stress_test_config.g.dart deleted file mode 100644 index 6193507c3..000000000 --- a/lib/models/stress_test/stress_test_config.g.dart +++ /dev/null @@ -1,35 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'stress_test_config.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$StressTestConfigImpl _$$StressTestConfigImplFromJson( - Map json) => - _$StressTestConfigImpl( - url: json['url'] as String, - method: json['method'] as String, - headers: (json['headers'] as Map?)?.map( - (k, e) => MapEntry(k, e as String), - ), - body: json['body'], - concurrentRequests: (json['concurrentRequests'] as num).toInt(), - timeout: json['timeout'] == null - ? null - : Duration(microseconds: (json['timeout'] as num).toInt()), - useIsolates: json['useIsolates'] as bool? ?? false, - ); - -Map _$$StressTestConfigImplToJson( - _$StressTestConfigImpl instance) => - { - 'url': instance.url, - 'method': instance.method, - 'headers': instance.headers, - 'body': instance.body, - 'concurrentRequests': instance.concurrentRequests, - 'timeout': instance.timeout?.inMicroseconds, - 'useIsolates': instance.useIsolates, - }; diff --git a/lib/models/stress_test/stress_test_summary.freezed.dart b/lib/models/stress_test/stress_test_summary.freezed.dart deleted file mode 100644 index b5fd37df1..000000000 --- a/lib/models/stress_test/stress_test_summary.freezed.dart +++ /dev/null @@ -1,266 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'stress_test_summary.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -StressTestSummary _$StressTestSummaryFromJson(Map json) { - return _StressTestSummary.fromJson(json); -} - -/// @nodoc -mixin _$StressTestSummary { - List get results => throw _privateConstructorUsedError; - Duration get totalDuration => throw _privateConstructorUsedError; - double get avgResponseTime => throw _privateConstructorUsedError; - int get successCount => throw _privateConstructorUsedError; - int get failureCount => throw _privateConstructorUsedError; - - /// Serializes this StressTestSummary to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $StressTestSummaryCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $StressTestSummaryCopyWith<$Res> { - factory $StressTestSummaryCopyWith( - StressTestSummary value, $Res Function(StressTestSummary) then) = - _$StressTestSummaryCopyWithImpl<$Res, StressTestSummary>; - @useResult - $Res call( - {List results, - Duration totalDuration, - double avgResponseTime, - int successCount, - int failureCount}); -} - -/// @nodoc -class _$StressTestSummaryCopyWithImpl<$Res, $Val extends StressTestSummary> - implements $StressTestSummaryCopyWith<$Res> { - _$StressTestSummaryCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? results = null, - Object? totalDuration = null, - Object? avgResponseTime = null, - Object? successCount = null, - Object? failureCount = null, - }) { - return _then(_value.copyWith( - results: null == results - ? _value.results - : results // ignore: cast_nullable_to_non_nullable - as List, - totalDuration: null == totalDuration - ? _value.totalDuration - : totalDuration // ignore: cast_nullable_to_non_nullable - as Duration, - avgResponseTime: null == avgResponseTime - ? _value.avgResponseTime - : avgResponseTime // ignore: cast_nullable_to_non_nullable - as double, - successCount: null == successCount - ? _value.successCount - : successCount // ignore: cast_nullable_to_non_nullable - as int, - failureCount: null == failureCount - ? _value.failureCount - : failureCount // ignore: cast_nullable_to_non_nullable - as int, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$StressTestSummaryImplCopyWith<$Res> - implements $StressTestSummaryCopyWith<$Res> { - factory _$$StressTestSummaryImplCopyWith(_$StressTestSummaryImpl value, - $Res Function(_$StressTestSummaryImpl) then) = - __$$StressTestSummaryImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {List results, - Duration totalDuration, - double avgResponseTime, - int successCount, - int failureCount}); -} - -/// @nodoc -class __$$StressTestSummaryImplCopyWithImpl<$Res> - extends _$StressTestSummaryCopyWithImpl<$Res, _$StressTestSummaryImpl> - implements _$$StressTestSummaryImplCopyWith<$Res> { - __$$StressTestSummaryImplCopyWithImpl(_$StressTestSummaryImpl _value, - $Res Function(_$StressTestSummaryImpl) _then) - : super(_value, _then); - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? results = null, - Object? totalDuration = null, - Object? avgResponseTime = null, - Object? successCount = null, - Object? failureCount = null, - }) { - return _then(_$StressTestSummaryImpl( - results: null == results - ? _value._results - : results // ignore: cast_nullable_to_non_nullable - as List, - totalDuration: null == totalDuration - ? _value.totalDuration - : totalDuration // ignore: cast_nullable_to_non_nullable - as Duration, - avgResponseTime: null == avgResponseTime - ? _value.avgResponseTime - : avgResponseTime // ignore: cast_nullable_to_non_nullable - as double, - successCount: null == successCount - ? _value.successCount - : successCount // ignore: cast_nullable_to_non_nullable - as int, - failureCount: null == failureCount - ? _value.failureCount - : failureCount // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$StressTestSummaryImpl implements _StressTestSummary { - const _$StressTestSummaryImpl( - {required final List results, - required this.totalDuration, - required this.avgResponseTime, - required this.successCount, - required this.failureCount}) - : _results = results; - - factory _$StressTestSummaryImpl.fromJson(Map json) => - _$$StressTestSummaryImplFromJson(json); - - final List _results; - @override - List get results { - if (_results is EqualUnmodifiableListView) return _results; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_results); - } - - @override - final Duration totalDuration; - @override - final double avgResponseTime; - @override - final int successCount; - @override - final int failureCount; - - @override - String toString() { - return 'StressTestSummary(results: $results, totalDuration: $totalDuration, avgResponseTime: $avgResponseTime, successCount: $successCount, failureCount: $failureCount)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$StressTestSummaryImpl && - const DeepCollectionEquality().equals(other._results, _results) && - (identical(other.totalDuration, totalDuration) || - other.totalDuration == totalDuration) && - (identical(other.avgResponseTime, avgResponseTime) || - other.avgResponseTime == avgResponseTime) && - (identical(other.successCount, successCount) || - other.successCount == successCount) && - (identical(other.failureCount, failureCount) || - other.failureCount == failureCount)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_results), - totalDuration, - avgResponseTime, - successCount, - failureCount); - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => - __$$StressTestSummaryImplCopyWithImpl<_$StressTestSummaryImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$StressTestSummaryImplToJson( - this, - ); - } -} - -abstract class _StressTestSummary implements StressTestSummary { - const factory _StressTestSummary( - {required final List results, - required final Duration totalDuration, - required final double avgResponseTime, - required final int successCount, - required final int failureCount}) = _$StressTestSummaryImpl; - - factory _StressTestSummary.fromJson(Map json) = - _$StressTestSummaryImpl.fromJson; - - @override - List get results; - @override - Duration get totalDuration; - @override - double get avgResponseTime; - @override - int get successCount; - @override - int get failureCount; - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/stress_test_summary.g.dart b/lib/models/stress_test/stress_test_summary.g.dart deleted file mode 100644 index 5bf4d5b48..000000000 --- a/lib/models/stress_test/stress_test_summary.g.dart +++ /dev/null @@ -1,30 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'stress_test_summary.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$StressTestSummaryImpl _$$StressTestSummaryImplFromJson( - Map json) => - _$StressTestSummaryImpl( - results: (json['results'] as List) - .map((e) => ApiRequestResult.fromJson(e as Map)) - .toList(), - totalDuration: - Duration(microseconds: (json['totalDuration'] as num).toInt()), - avgResponseTime: (json['avgResponseTime'] as num).toDouble(), - successCount: (json['successCount'] as num).toInt(), - failureCount: (json['failureCount'] as num).toInt(), - ); - -Map _$$StressTestSummaryImplToJson( - _$StressTestSummaryImpl instance) => - { - 'results': instance.results, - 'totalDuration': instance.totalDuration.inMicroseconds, - 'avgResponseTime': instance.avgResponseTime, - 'successCount': instance.successCount, - 'failureCount': instance.failureCount, - }; From eba2b0a84ca14476e78d58ecc8ba0792cd5df88d Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:48:40 +0530 Subject: [PATCH 096/188] feat: add stress test config model --- .../stress_test_config.freezed.dart | 309 ++++++++++++++++++ .../stress_test/stress_test_config.g.dart | 35 ++ 2 files changed, 344 insertions(+) create mode 100644 lib/models/stress_test/stress_test_config.freezed.dart create mode 100644 lib/models/stress_test/stress_test_config.g.dart diff --git a/lib/models/stress_test/stress_test_config.freezed.dart b/lib/models/stress_test/stress_test_config.freezed.dart new file mode 100644 index 000000000..2671a5dfe --- /dev/null +++ b/lib/models/stress_test/stress_test_config.freezed.dart @@ -0,0 +1,309 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'stress_test_config.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +StressTestConfig _$StressTestConfigFromJson(Map json) { + return _StressTestConfig.fromJson(json); +} + +/// @nodoc +mixin _$StressTestConfig { + String get url => throw _privateConstructorUsedError; + String get method => throw _privateConstructorUsedError; + Map? get headers => throw _privateConstructorUsedError; + dynamic get body => throw _privateConstructorUsedError; + int get concurrentRequests => throw _privateConstructorUsedError; + Duration? get timeout => throw _privateConstructorUsedError; + bool get useIsolates => throw _privateConstructorUsedError; + + /// Serializes this StressTestConfig to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $StressTestConfigCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $StressTestConfigCopyWith<$Res> { + factory $StressTestConfigCopyWith( + StressTestConfig value, $Res Function(StressTestConfig) then) = + _$StressTestConfigCopyWithImpl<$Res, StressTestConfig>; + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + int concurrentRequests, + Duration? timeout, + bool useIsolates}); +} + +/// @nodoc +class _$StressTestConfigCopyWithImpl<$Res, $Val extends StressTestConfig> + implements $StressTestConfigCopyWith<$Res> { + _$StressTestConfigCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? concurrentRequests = null, + Object? timeout = freezed, + Object? useIsolates = null, + }) { + return _then(_value.copyWith( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value.headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + concurrentRequests: null == concurrentRequests + ? _value.concurrentRequests + : concurrentRequests // ignore: cast_nullable_to_non_nullable + as int, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + useIsolates: null == useIsolates + ? _value.useIsolates + : useIsolates // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$StressTestConfigImplCopyWith<$Res> + implements $StressTestConfigCopyWith<$Res> { + factory _$$StressTestConfigImplCopyWith(_$StressTestConfigImpl value, + $Res Function(_$StressTestConfigImpl) then) = + __$$StressTestConfigImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + int concurrentRequests, + Duration? timeout, + bool useIsolates}); +} + +/// @nodoc +class __$$StressTestConfigImplCopyWithImpl<$Res> + extends _$StressTestConfigCopyWithImpl<$Res, _$StressTestConfigImpl> + implements _$$StressTestConfigImplCopyWith<$Res> { + __$$StressTestConfigImplCopyWithImpl(_$StressTestConfigImpl _value, + $Res Function(_$StressTestConfigImpl) _then) + : super(_value, _then); + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? concurrentRequests = null, + Object? timeout = freezed, + Object? useIsolates = null, + }) { + return _then(_$StressTestConfigImpl( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value._headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + concurrentRequests: null == concurrentRequests + ? _value.concurrentRequests + : concurrentRequests // ignore: cast_nullable_to_non_nullable + as int, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + useIsolates: null == useIsolates + ? _value.useIsolates + : useIsolates // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$StressTestConfigImpl implements _StressTestConfig { + const _$StressTestConfigImpl( + {required this.url, + required this.method, + final Map? headers, + this.body, + required this.concurrentRequests, + this.timeout, + this.useIsolates = false}) + : _headers = headers; + + factory _$StressTestConfigImpl.fromJson(Map json) => + _$$StressTestConfigImplFromJson(json); + + @override + final String url; + @override + final String method; + final Map? _headers; + @override + Map? get headers { + final value = _headers; + if (value == null) return null; + if (_headers is EqualUnmodifiableMapView) return _headers; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); + } + + @override + final dynamic body; + @override + final int concurrentRequests; + @override + final Duration? timeout; + @override + @JsonKey() + final bool useIsolates; + + @override + String toString() { + return 'StressTestConfig(url: $url, method: $method, headers: $headers, body: $body, concurrentRequests: $concurrentRequests, timeout: $timeout, useIsolates: $useIsolates)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$StressTestConfigImpl && + (identical(other.url, url) || other.url == url) && + (identical(other.method, method) || other.method == method) && + const DeepCollectionEquality().equals(other._headers, _headers) && + const DeepCollectionEquality().equals(other.body, body) && + (identical(other.concurrentRequests, concurrentRequests) || + other.concurrentRequests == concurrentRequests) && + (identical(other.timeout, timeout) || other.timeout == timeout) && + (identical(other.useIsolates, useIsolates) || + other.useIsolates == useIsolates)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + url, + method, + const DeepCollectionEquality().hash(_headers), + const DeepCollectionEquality().hash(body), + concurrentRequests, + timeout, + useIsolates); + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => + __$$StressTestConfigImplCopyWithImpl<_$StressTestConfigImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$StressTestConfigImplToJson( + this, + ); + } +} + +abstract class _StressTestConfig implements StressTestConfig { + const factory _StressTestConfig( + {required final String url, + required final String method, + final Map? headers, + final dynamic body, + required final int concurrentRequests, + final Duration? timeout, + final bool useIsolates}) = _$StressTestConfigImpl; + + factory _StressTestConfig.fromJson(Map json) = + _$StressTestConfigImpl.fromJson; + + @override + String get url; + @override + String get method; + @override + Map? get headers; + @override + dynamic get body; + @override + int get concurrentRequests; + @override + Duration? get timeout; + @override + bool get useIsolates; + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/stress_test_config.g.dart b/lib/models/stress_test/stress_test_config.g.dart new file mode 100644 index 000000000..6193507c3 --- /dev/null +++ b/lib/models/stress_test/stress_test_config.g.dart @@ -0,0 +1,35 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stress_test_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$StressTestConfigImpl _$$StressTestConfigImplFromJson( + Map json) => + _$StressTestConfigImpl( + url: json['url'] as String, + method: json['method'] as String, + headers: (json['headers'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + body: json['body'], + concurrentRequests: (json['concurrentRequests'] as num).toInt(), + timeout: json['timeout'] == null + ? null + : Duration(microseconds: (json['timeout'] as num).toInt()), + useIsolates: json['useIsolates'] as bool? ?? false, + ); + +Map _$$StressTestConfigImplToJson( + _$StressTestConfigImpl instance) => + { + 'url': instance.url, + 'method': instance.method, + 'headers': instance.headers, + 'body': instance.body, + 'concurrentRequests': instance.concurrentRequests, + 'timeout': instance.timeout?.inMicroseconds, + 'useIsolates': instance.useIsolates, + }; From 103c73bfb293ff08159682b173dcf63cd5a7b163 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:49:38 +0530 Subject: [PATCH 097/188] feat: add api request result model --- .../api_request_result.freezed.dart | 224 ++++++++++++++++++ .../stress_test/api_request_result.g.dart | 25 ++ 2 files changed, 249 insertions(+) create mode 100644 lib/models/stress_test/api_request_result.freezed.dart create mode 100644 lib/models/stress_test/api_request_result.g.dart diff --git a/lib/models/stress_test/api_request_result.freezed.dart b/lib/models/stress_test/api_request_result.freezed.dart new file mode 100644 index 000000000..6bdee80a3 --- /dev/null +++ b/lib/models/stress_test/api_request_result.freezed.dart @@ -0,0 +1,224 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'api_request_result.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +ApiRequestResult _$ApiRequestResultFromJson(Map json) { + return _ApiRequestResult.fromJson(json); +} + +/// @nodoc +mixin _$ApiRequestResult { + int get statusCode => throw _privateConstructorUsedError; + String get body => throw _privateConstructorUsedError; + Duration get duration => throw _privateConstructorUsedError; + String? get error => throw _privateConstructorUsedError; + + /// Serializes this ApiRequestResult to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $ApiRequestResultCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ApiRequestResultCopyWith<$Res> { + factory $ApiRequestResultCopyWith( + ApiRequestResult value, $Res Function(ApiRequestResult) then) = + _$ApiRequestResultCopyWithImpl<$Res, ApiRequestResult>; + @useResult + $Res call({int statusCode, String body, Duration duration, String? error}); +} + +/// @nodoc +class _$ApiRequestResultCopyWithImpl<$Res, $Val extends ApiRequestResult> + implements $ApiRequestResultCopyWith<$Res> { + _$ApiRequestResultCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? statusCode = null, + Object? body = null, + Object? duration = null, + Object? error = freezed, + }) { + return _then(_value.copyWith( + statusCode: null == statusCode + ? _value.statusCode + : statusCode // ignore: cast_nullable_to_non_nullable + as int, + body: null == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as String, + duration: null == duration + ? _value.duration + : duration // ignore: cast_nullable_to_non_nullable + as Duration, + error: freezed == error + ? _value.error + : error // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$ApiRequestResultImplCopyWith<$Res> + implements $ApiRequestResultCopyWith<$Res> { + factory _$$ApiRequestResultImplCopyWith(_$ApiRequestResultImpl value, + $Res Function(_$ApiRequestResultImpl) then) = + __$$ApiRequestResultImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({int statusCode, String body, Duration duration, String? error}); +} + +/// @nodoc +class __$$ApiRequestResultImplCopyWithImpl<$Res> + extends _$ApiRequestResultCopyWithImpl<$Res, _$ApiRequestResultImpl> + implements _$$ApiRequestResultImplCopyWith<$Res> { + __$$ApiRequestResultImplCopyWithImpl(_$ApiRequestResultImpl _value, + $Res Function(_$ApiRequestResultImpl) _then) + : super(_value, _then); + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? statusCode = null, + Object? body = null, + Object? duration = null, + Object? error = freezed, + }) { + return _then(_$ApiRequestResultImpl( + statusCode: null == statusCode + ? _value.statusCode + : statusCode // ignore: cast_nullable_to_non_nullable + as int, + body: null == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as String, + duration: null == duration + ? _value.duration + : duration // ignore: cast_nullable_to_non_nullable + as Duration, + error: freezed == error + ? _value.error + : error // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ApiRequestResultImpl implements _ApiRequestResult { + const _$ApiRequestResultImpl( + {required this.statusCode, + required this.body, + required this.duration, + this.error}); + + factory _$ApiRequestResultImpl.fromJson(Map json) => + _$$ApiRequestResultImplFromJson(json); + + @override + final int statusCode; + @override + final String body; + @override + final Duration duration; + @override + final String? error; + + @override + String toString() { + return 'ApiRequestResult(statusCode: $statusCode, body: $body, duration: $duration, error: $error)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ApiRequestResultImpl && + (identical(other.statusCode, statusCode) || + other.statusCode == statusCode) && + (identical(other.body, body) || other.body == body) && + (identical(other.duration, duration) || + other.duration == duration) && + (identical(other.error, error) || other.error == error)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => + Object.hash(runtimeType, statusCode, body, duration, error); + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => + __$$ApiRequestResultImplCopyWithImpl<_$ApiRequestResultImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$ApiRequestResultImplToJson( + this, + ); + } +} + +abstract class _ApiRequestResult implements ApiRequestResult { + const factory _ApiRequestResult( + {required final int statusCode, + required final String body, + required final Duration duration, + final String? error}) = _$ApiRequestResultImpl; + + factory _ApiRequestResult.fromJson(Map json) = + _$ApiRequestResultImpl.fromJson; + + @override + int get statusCode; + @override + String get body; + @override + Duration get duration; + @override + String? get error; + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/api_request_result.g.dart b/lib/models/stress_test/api_request_result.g.dart new file mode 100644 index 000000000..4182a8e58 --- /dev/null +++ b/lib/models/stress_test/api_request_result.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'api_request_result.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$ApiRequestResultImpl _$$ApiRequestResultImplFromJson( + Map json) => + _$ApiRequestResultImpl( + statusCode: (json['statusCode'] as num).toInt(), + body: json['body'] as String, + duration: Duration(microseconds: (json['duration'] as num).toInt()), + error: json['error'] as String?, + ); + +Map _$$ApiRequestResultImplToJson( + _$ApiRequestResultImpl instance) => + { + 'statusCode': instance.statusCode, + 'body': instance.body, + 'duration': instance.duration.inMicroseconds, + 'error': instance.error, + }; From 18a94eaf174b8f1aad895f84b9f6b9a5f95ad4ff Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:50:18 +0530 Subject: [PATCH 098/188] feat: add isolate model for stress test --- .../stress_test/isolate_message.freezed.dart | 264 ++++++++++++++++++ lib/models/stress_test/isolate_message.g.dart | 30 ++ 2 files changed, 294 insertions(+) create mode 100644 lib/models/stress_test/isolate_message.freezed.dart create mode 100644 lib/models/stress_test/isolate_message.g.dart diff --git a/lib/models/stress_test/isolate_message.freezed.dart b/lib/models/stress_test/isolate_message.freezed.dart new file mode 100644 index 000000000..c3abc6857 --- /dev/null +++ b/lib/models/stress_test/isolate_message.freezed.dart @@ -0,0 +1,264 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'isolate_message.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +IsolateMessage _$IsolateMessageFromJson(Map json) { + return _IsolateMessage.fromJson(json); +} + +/// @nodoc +mixin _$IsolateMessage { + String get url => throw _privateConstructorUsedError; + String get method => throw _privateConstructorUsedError; + Map? get headers => throw _privateConstructorUsedError; + dynamic get body => throw _privateConstructorUsedError; + Duration? get timeout => throw _privateConstructorUsedError; + + /// Serializes this IsolateMessage to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $IsolateMessageCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $IsolateMessageCopyWith<$Res> { + factory $IsolateMessageCopyWith( + IsolateMessage value, $Res Function(IsolateMessage) then) = + _$IsolateMessageCopyWithImpl<$Res, IsolateMessage>; + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + Duration? timeout}); +} + +/// @nodoc +class _$IsolateMessageCopyWithImpl<$Res, $Val extends IsolateMessage> + implements $IsolateMessageCopyWith<$Res> { + _$IsolateMessageCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? timeout = freezed, + }) { + return _then(_value.copyWith( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value.headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$IsolateMessageImplCopyWith<$Res> + implements $IsolateMessageCopyWith<$Res> { + factory _$$IsolateMessageImplCopyWith(_$IsolateMessageImpl value, + $Res Function(_$IsolateMessageImpl) then) = + __$$IsolateMessageImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + Duration? timeout}); +} + +/// @nodoc +class __$$IsolateMessageImplCopyWithImpl<$Res> + extends _$IsolateMessageCopyWithImpl<$Res, _$IsolateMessageImpl> + implements _$$IsolateMessageImplCopyWith<$Res> { + __$$IsolateMessageImplCopyWithImpl( + _$IsolateMessageImpl _value, $Res Function(_$IsolateMessageImpl) _then) + : super(_value, _then); + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? timeout = freezed, + }) { + return _then(_$IsolateMessageImpl( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value._headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$IsolateMessageImpl implements _IsolateMessage { + const _$IsolateMessageImpl( + {required this.url, + required this.method, + final Map? headers, + this.body, + this.timeout}) + : _headers = headers; + + factory _$IsolateMessageImpl.fromJson(Map json) => + _$$IsolateMessageImplFromJson(json); + + @override + final String url; + @override + final String method; + final Map? _headers; + @override + Map? get headers { + final value = _headers; + if (value == null) return null; + if (_headers is EqualUnmodifiableMapView) return _headers; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); + } + + @override + final dynamic body; + @override + final Duration? timeout; + + @override + String toString() { + return 'IsolateMessage(url: $url, method: $method, headers: $headers, body: $body, timeout: $timeout)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$IsolateMessageImpl && + (identical(other.url, url) || other.url == url) && + (identical(other.method, method) || other.method == method) && + const DeepCollectionEquality().equals(other._headers, _headers) && + const DeepCollectionEquality().equals(other.body, body) && + (identical(other.timeout, timeout) || other.timeout == timeout)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + url, + method, + const DeepCollectionEquality().hash(_headers), + const DeepCollectionEquality().hash(body), + timeout); + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => + __$$IsolateMessageImplCopyWithImpl<_$IsolateMessageImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$IsolateMessageImplToJson( + this, + ); + } +} + +abstract class _IsolateMessage implements IsolateMessage { + const factory _IsolateMessage( + {required final String url, + required final String method, + final Map? headers, + final dynamic body, + final Duration? timeout}) = _$IsolateMessageImpl; + + factory _IsolateMessage.fromJson(Map json) = + _$IsolateMessageImpl.fromJson; + + @override + String get url; + @override + String get method; + @override + Map? get headers; + @override + dynamic get body; + @override + Duration? get timeout; + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/isolate_message.g.dart b/lib/models/stress_test/isolate_message.g.dart new file mode 100644 index 000000000..e5f403ea1 --- /dev/null +++ b/lib/models/stress_test/isolate_message.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'isolate_message.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$IsolateMessageImpl _$$IsolateMessageImplFromJson(Map json) => + _$IsolateMessageImpl( + url: json['url'] as String, + method: json['method'] as String, + headers: (json['headers'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + body: json['body'], + timeout: json['timeout'] == null + ? null + : Duration(microseconds: (json['timeout'] as num).toInt()), + ); + +Map _$$IsolateMessageImplToJson( + _$IsolateMessageImpl instance) => + { + 'url': instance.url, + 'method': instance.method, + 'headers': instance.headers, + 'body': instance.body, + 'timeout': instance.timeout?.inMicroseconds, + }; From cb2eb5643a029a5566861d013f14b540f4be7635 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:51:01 +0530 Subject: [PATCH 099/188] feat: add stress test summer model --- .../stress_test_summary.freezed.dart | 266 ++++++++++++++++++ .../stress_test/stress_test_summary.g.dart | 30 ++ 2 files changed, 296 insertions(+) create mode 100644 lib/models/stress_test/stress_test_summary.freezed.dart create mode 100644 lib/models/stress_test/stress_test_summary.g.dart diff --git a/lib/models/stress_test/stress_test_summary.freezed.dart b/lib/models/stress_test/stress_test_summary.freezed.dart new file mode 100644 index 000000000..b5fd37df1 --- /dev/null +++ b/lib/models/stress_test/stress_test_summary.freezed.dart @@ -0,0 +1,266 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'stress_test_summary.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +StressTestSummary _$StressTestSummaryFromJson(Map json) { + return _StressTestSummary.fromJson(json); +} + +/// @nodoc +mixin _$StressTestSummary { + List get results => throw _privateConstructorUsedError; + Duration get totalDuration => throw _privateConstructorUsedError; + double get avgResponseTime => throw _privateConstructorUsedError; + int get successCount => throw _privateConstructorUsedError; + int get failureCount => throw _privateConstructorUsedError; + + /// Serializes this StressTestSummary to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $StressTestSummaryCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $StressTestSummaryCopyWith<$Res> { + factory $StressTestSummaryCopyWith( + StressTestSummary value, $Res Function(StressTestSummary) then) = + _$StressTestSummaryCopyWithImpl<$Res, StressTestSummary>; + @useResult + $Res call( + {List results, + Duration totalDuration, + double avgResponseTime, + int successCount, + int failureCount}); +} + +/// @nodoc +class _$StressTestSummaryCopyWithImpl<$Res, $Val extends StressTestSummary> + implements $StressTestSummaryCopyWith<$Res> { + _$StressTestSummaryCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? results = null, + Object? totalDuration = null, + Object? avgResponseTime = null, + Object? successCount = null, + Object? failureCount = null, + }) { + return _then(_value.copyWith( + results: null == results + ? _value.results + : results // ignore: cast_nullable_to_non_nullable + as List, + totalDuration: null == totalDuration + ? _value.totalDuration + : totalDuration // ignore: cast_nullable_to_non_nullable + as Duration, + avgResponseTime: null == avgResponseTime + ? _value.avgResponseTime + : avgResponseTime // ignore: cast_nullable_to_non_nullable + as double, + successCount: null == successCount + ? _value.successCount + : successCount // ignore: cast_nullable_to_non_nullable + as int, + failureCount: null == failureCount + ? _value.failureCount + : failureCount // ignore: cast_nullable_to_non_nullable + as int, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$StressTestSummaryImplCopyWith<$Res> + implements $StressTestSummaryCopyWith<$Res> { + factory _$$StressTestSummaryImplCopyWith(_$StressTestSummaryImpl value, + $Res Function(_$StressTestSummaryImpl) then) = + __$$StressTestSummaryImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {List results, + Duration totalDuration, + double avgResponseTime, + int successCount, + int failureCount}); +} + +/// @nodoc +class __$$StressTestSummaryImplCopyWithImpl<$Res> + extends _$StressTestSummaryCopyWithImpl<$Res, _$StressTestSummaryImpl> + implements _$$StressTestSummaryImplCopyWith<$Res> { + __$$StressTestSummaryImplCopyWithImpl(_$StressTestSummaryImpl _value, + $Res Function(_$StressTestSummaryImpl) _then) + : super(_value, _then); + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? results = null, + Object? totalDuration = null, + Object? avgResponseTime = null, + Object? successCount = null, + Object? failureCount = null, + }) { + return _then(_$StressTestSummaryImpl( + results: null == results + ? _value._results + : results // ignore: cast_nullable_to_non_nullable + as List, + totalDuration: null == totalDuration + ? _value.totalDuration + : totalDuration // ignore: cast_nullable_to_non_nullable + as Duration, + avgResponseTime: null == avgResponseTime + ? _value.avgResponseTime + : avgResponseTime // ignore: cast_nullable_to_non_nullable + as double, + successCount: null == successCount + ? _value.successCount + : successCount // ignore: cast_nullable_to_non_nullable + as int, + failureCount: null == failureCount + ? _value.failureCount + : failureCount // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$StressTestSummaryImpl implements _StressTestSummary { + const _$StressTestSummaryImpl( + {required final List results, + required this.totalDuration, + required this.avgResponseTime, + required this.successCount, + required this.failureCount}) + : _results = results; + + factory _$StressTestSummaryImpl.fromJson(Map json) => + _$$StressTestSummaryImplFromJson(json); + + final List _results; + @override + List get results { + if (_results is EqualUnmodifiableListView) return _results; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_results); + } + + @override + final Duration totalDuration; + @override + final double avgResponseTime; + @override + final int successCount; + @override + final int failureCount; + + @override + String toString() { + return 'StressTestSummary(results: $results, totalDuration: $totalDuration, avgResponseTime: $avgResponseTime, successCount: $successCount, failureCount: $failureCount)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$StressTestSummaryImpl && + const DeepCollectionEquality().equals(other._results, _results) && + (identical(other.totalDuration, totalDuration) || + other.totalDuration == totalDuration) && + (identical(other.avgResponseTime, avgResponseTime) || + other.avgResponseTime == avgResponseTime) && + (identical(other.successCount, successCount) || + other.successCount == successCount) && + (identical(other.failureCount, failureCount) || + other.failureCount == failureCount)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(_results), + totalDuration, + avgResponseTime, + successCount, + failureCount); + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => + __$$StressTestSummaryImplCopyWithImpl<_$StressTestSummaryImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$StressTestSummaryImplToJson( + this, + ); + } +} + +abstract class _StressTestSummary implements StressTestSummary { + const factory _StressTestSummary( + {required final List results, + required final Duration totalDuration, + required final double avgResponseTime, + required final int successCount, + required final int failureCount}) = _$StressTestSummaryImpl; + + factory _StressTestSummary.fromJson(Map json) = + _$StressTestSummaryImpl.fromJson; + + @override + List get results; + @override + Duration get totalDuration; + @override + double get avgResponseTime; + @override + int get successCount; + @override + int get failureCount; + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/stress_test_summary.g.dart b/lib/models/stress_test/stress_test_summary.g.dart new file mode 100644 index 000000000..5bf4d5b48 --- /dev/null +++ b/lib/models/stress_test/stress_test_summary.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stress_test_summary.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$StressTestSummaryImpl _$$StressTestSummaryImplFromJson( + Map json) => + _$StressTestSummaryImpl( + results: (json['results'] as List) + .map((e) => ApiRequestResult.fromJson(e as Map)) + .toList(), + totalDuration: + Duration(microseconds: (json['totalDuration'] as num).toInt()), + avgResponseTime: (json['avgResponseTime'] as num).toDouble(), + successCount: (json['successCount'] as num).toInt(), + failureCount: (json['failureCount'] as num).toInt(), + ); + +Map _$$StressTestSummaryImplToJson( + _$StressTestSummaryImpl instance) => + { + 'results': instance.results, + 'totalDuration': instance.totalDuration.inMicroseconds, + 'avgResponseTime': instance.avgResponseTime, + 'successCount': instance.successCount, + 'failureCount': instance.failureCount, + }; From 0c2442d5215deb906a3f649e9965ffb9fe9906ee Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 13:53:49 +0530 Subject: [PATCH 100/188] chore: implement the stress test UI in request pane --- .../api_request_result.freezed.dart | 224 ------------- .../stress_test/api_request_result.g.dart | 25 -- .../stress_test/isolate_message.freezed.dart | 264 --------------- lib/models/stress_test/isolate_message.g.dart | 30 -- .../stress_test_config.freezed.dart | 309 ------------------ .../stress_test/stress_test_config.g.dart | 35 -- .../stress_test_summary.freezed.dart | 266 --------------- .../stress_test/stress_test_summary.g.dart | 30 -- 8 files changed, 1183 deletions(-) delete mode 100644 lib/models/stress_test/api_request_result.freezed.dart delete mode 100644 lib/models/stress_test/api_request_result.g.dart delete mode 100644 lib/models/stress_test/isolate_message.freezed.dart delete mode 100644 lib/models/stress_test/isolate_message.g.dart delete mode 100644 lib/models/stress_test/stress_test_config.freezed.dart delete mode 100644 lib/models/stress_test/stress_test_config.g.dart delete mode 100644 lib/models/stress_test/stress_test_summary.freezed.dart delete mode 100644 lib/models/stress_test/stress_test_summary.g.dart diff --git a/lib/models/stress_test/api_request_result.freezed.dart b/lib/models/stress_test/api_request_result.freezed.dart deleted file mode 100644 index 6bdee80a3..000000000 --- a/lib/models/stress_test/api_request_result.freezed.dart +++ /dev/null @@ -1,224 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'api_request_result.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -ApiRequestResult _$ApiRequestResultFromJson(Map json) { - return _ApiRequestResult.fromJson(json); -} - -/// @nodoc -mixin _$ApiRequestResult { - int get statusCode => throw _privateConstructorUsedError; - String get body => throw _privateConstructorUsedError; - Duration get duration => throw _privateConstructorUsedError; - String? get error => throw _privateConstructorUsedError; - - /// Serializes this ApiRequestResult to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ApiRequestResultCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ApiRequestResultCopyWith<$Res> { - factory $ApiRequestResultCopyWith( - ApiRequestResult value, $Res Function(ApiRequestResult) then) = - _$ApiRequestResultCopyWithImpl<$Res, ApiRequestResult>; - @useResult - $Res call({int statusCode, String body, Duration duration, String? error}); -} - -/// @nodoc -class _$ApiRequestResultCopyWithImpl<$Res, $Val extends ApiRequestResult> - implements $ApiRequestResultCopyWith<$Res> { - _$ApiRequestResultCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? statusCode = null, - Object? body = null, - Object? duration = null, - Object? error = freezed, - }) { - return _then(_value.copyWith( - statusCode: null == statusCode - ? _value.statusCode - : statusCode // ignore: cast_nullable_to_non_nullable - as int, - body: null == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as String, - duration: null == duration - ? _value.duration - : duration // ignore: cast_nullable_to_non_nullable - as Duration, - error: freezed == error - ? _value.error - : error // ignore: cast_nullable_to_non_nullable - as String?, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$ApiRequestResultImplCopyWith<$Res> - implements $ApiRequestResultCopyWith<$Res> { - factory _$$ApiRequestResultImplCopyWith(_$ApiRequestResultImpl value, - $Res Function(_$ApiRequestResultImpl) then) = - __$$ApiRequestResultImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({int statusCode, String body, Duration duration, String? error}); -} - -/// @nodoc -class __$$ApiRequestResultImplCopyWithImpl<$Res> - extends _$ApiRequestResultCopyWithImpl<$Res, _$ApiRequestResultImpl> - implements _$$ApiRequestResultImplCopyWith<$Res> { - __$$ApiRequestResultImplCopyWithImpl(_$ApiRequestResultImpl _value, - $Res Function(_$ApiRequestResultImpl) _then) - : super(_value, _then); - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? statusCode = null, - Object? body = null, - Object? duration = null, - Object? error = freezed, - }) { - return _then(_$ApiRequestResultImpl( - statusCode: null == statusCode - ? _value.statusCode - : statusCode // ignore: cast_nullable_to_non_nullable - as int, - body: null == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as String, - duration: null == duration - ? _value.duration - : duration // ignore: cast_nullable_to_non_nullable - as Duration, - error: freezed == error - ? _value.error - : error // ignore: cast_nullable_to_non_nullable - as String?, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$ApiRequestResultImpl implements _ApiRequestResult { - const _$ApiRequestResultImpl( - {required this.statusCode, - required this.body, - required this.duration, - this.error}); - - factory _$ApiRequestResultImpl.fromJson(Map json) => - _$$ApiRequestResultImplFromJson(json); - - @override - final int statusCode; - @override - final String body; - @override - final Duration duration; - @override - final String? error; - - @override - String toString() { - return 'ApiRequestResult(statusCode: $statusCode, body: $body, duration: $duration, error: $error)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ApiRequestResultImpl && - (identical(other.statusCode, statusCode) || - other.statusCode == statusCode) && - (identical(other.body, body) || other.body == body) && - (identical(other.duration, duration) || - other.duration == duration) && - (identical(other.error, error) || other.error == error)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => - Object.hash(runtimeType, statusCode, body, duration, error); - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => - __$$ApiRequestResultImplCopyWithImpl<_$ApiRequestResultImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$ApiRequestResultImplToJson( - this, - ); - } -} - -abstract class _ApiRequestResult implements ApiRequestResult { - const factory _ApiRequestResult( - {required final int statusCode, - required final String body, - required final Duration duration, - final String? error}) = _$ApiRequestResultImpl; - - factory _ApiRequestResult.fromJson(Map json) = - _$ApiRequestResultImpl.fromJson; - - @override - int get statusCode; - @override - String get body; - @override - Duration get duration; - @override - String? get error; - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/api_request_result.g.dart b/lib/models/stress_test/api_request_result.g.dart deleted file mode 100644 index 4182a8e58..000000000 --- a/lib/models/stress_test/api_request_result.g.dart +++ /dev/null @@ -1,25 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'api_request_result.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$ApiRequestResultImpl _$$ApiRequestResultImplFromJson( - Map json) => - _$ApiRequestResultImpl( - statusCode: (json['statusCode'] as num).toInt(), - body: json['body'] as String, - duration: Duration(microseconds: (json['duration'] as num).toInt()), - error: json['error'] as String?, - ); - -Map _$$ApiRequestResultImplToJson( - _$ApiRequestResultImpl instance) => - { - 'statusCode': instance.statusCode, - 'body': instance.body, - 'duration': instance.duration.inMicroseconds, - 'error': instance.error, - }; diff --git a/lib/models/stress_test/isolate_message.freezed.dart b/lib/models/stress_test/isolate_message.freezed.dart deleted file mode 100644 index c3abc6857..000000000 --- a/lib/models/stress_test/isolate_message.freezed.dart +++ /dev/null @@ -1,264 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'isolate_message.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -IsolateMessage _$IsolateMessageFromJson(Map json) { - return _IsolateMessage.fromJson(json); -} - -/// @nodoc -mixin _$IsolateMessage { - String get url => throw _privateConstructorUsedError; - String get method => throw _privateConstructorUsedError; - Map? get headers => throw _privateConstructorUsedError; - dynamic get body => throw _privateConstructorUsedError; - Duration? get timeout => throw _privateConstructorUsedError; - - /// Serializes this IsolateMessage to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $IsolateMessageCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $IsolateMessageCopyWith<$Res> { - factory $IsolateMessageCopyWith( - IsolateMessage value, $Res Function(IsolateMessage) then) = - _$IsolateMessageCopyWithImpl<$Res, IsolateMessage>; - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - Duration? timeout}); -} - -/// @nodoc -class _$IsolateMessageCopyWithImpl<$Res, $Val extends IsolateMessage> - implements $IsolateMessageCopyWith<$Res> { - _$IsolateMessageCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? timeout = freezed, - }) { - return _then(_value.copyWith( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value.headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$IsolateMessageImplCopyWith<$Res> - implements $IsolateMessageCopyWith<$Res> { - factory _$$IsolateMessageImplCopyWith(_$IsolateMessageImpl value, - $Res Function(_$IsolateMessageImpl) then) = - __$$IsolateMessageImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - Duration? timeout}); -} - -/// @nodoc -class __$$IsolateMessageImplCopyWithImpl<$Res> - extends _$IsolateMessageCopyWithImpl<$Res, _$IsolateMessageImpl> - implements _$$IsolateMessageImplCopyWith<$Res> { - __$$IsolateMessageImplCopyWithImpl( - _$IsolateMessageImpl _value, $Res Function(_$IsolateMessageImpl) _then) - : super(_value, _then); - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? timeout = freezed, - }) { - return _then(_$IsolateMessageImpl( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value._headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$IsolateMessageImpl implements _IsolateMessage { - const _$IsolateMessageImpl( - {required this.url, - required this.method, - final Map? headers, - this.body, - this.timeout}) - : _headers = headers; - - factory _$IsolateMessageImpl.fromJson(Map json) => - _$$IsolateMessageImplFromJson(json); - - @override - final String url; - @override - final String method; - final Map? _headers; - @override - Map? get headers { - final value = _headers; - if (value == null) return null; - if (_headers is EqualUnmodifiableMapView) return _headers; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(value); - } - - @override - final dynamic body; - @override - final Duration? timeout; - - @override - String toString() { - return 'IsolateMessage(url: $url, method: $method, headers: $headers, body: $body, timeout: $timeout)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$IsolateMessageImpl && - (identical(other.url, url) || other.url == url) && - (identical(other.method, method) || other.method == method) && - const DeepCollectionEquality().equals(other._headers, _headers) && - const DeepCollectionEquality().equals(other.body, body) && - (identical(other.timeout, timeout) || other.timeout == timeout)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - url, - method, - const DeepCollectionEquality().hash(_headers), - const DeepCollectionEquality().hash(body), - timeout); - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => - __$$IsolateMessageImplCopyWithImpl<_$IsolateMessageImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$IsolateMessageImplToJson( - this, - ); - } -} - -abstract class _IsolateMessage implements IsolateMessage { - const factory _IsolateMessage( - {required final String url, - required final String method, - final Map? headers, - final dynamic body, - final Duration? timeout}) = _$IsolateMessageImpl; - - factory _IsolateMessage.fromJson(Map json) = - _$IsolateMessageImpl.fromJson; - - @override - String get url; - @override - String get method; - @override - Map? get headers; - @override - dynamic get body; - @override - Duration? get timeout; - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/isolate_message.g.dart b/lib/models/stress_test/isolate_message.g.dart deleted file mode 100644 index e5f403ea1..000000000 --- a/lib/models/stress_test/isolate_message.g.dart +++ /dev/null @@ -1,30 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'isolate_message.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$IsolateMessageImpl _$$IsolateMessageImplFromJson(Map json) => - _$IsolateMessageImpl( - url: json['url'] as String, - method: json['method'] as String, - headers: (json['headers'] as Map?)?.map( - (k, e) => MapEntry(k, e as String), - ), - body: json['body'], - timeout: json['timeout'] == null - ? null - : Duration(microseconds: (json['timeout'] as num).toInt()), - ); - -Map _$$IsolateMessageImplToJson( - _$IsolateMessageImpl instance) => - { - 'url': instance.url, - 'method': instance.method, - 'headers': instance.headers, - 'body': instance.body, - 'timeout': instance.timeout?.inMicroseconds, - }; diff --git a/lib/models/stress_test/stress_test_config.freezed.dart b/lib/models/stress_test/stress_test_config.freezed.dart deleted file mode 100644 index 2671a5dfe..000000000 --- a/lib/models/stress_test/stress_test_config.freezed.dart +++ /dev/null @@ -1,309 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'stress_test_config.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -StressTestConfig _$StressTestConfigFromJson(Map json) { - return _StressTestConfig.fromJson(json); -} - -/// @nodoc -mixin _$StressTestConfig { - String get url => throw _privateConstructorUsedError; - String get method => throw _privateConstructorUsedError; - Map? get headers => throw _privateConstructorUsedError; - dynamic get body => throw _privateConstructorUsedError; - int get concurrentRequests => throw _privateConstructorUsedError; - Duration? get timeout => throw _privateConstructorUsedError; - bool get useIsolates => throw _privateConstructorUsedError; - - /// Serializes this StressTestConfig to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $StressTestConfigCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $StressTestConfigCopyWith<$Res> { - factory $StressTestConfigCopyWith( - StressTestConfig value, $Res Function(StressTestConfig) then) = - _$StressTestConfigCopyWithImpl<$Res, StressTestConfig>; - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - int concurrentRequests, - Duration? timeout, - bool useIsolates}); -} - -/// @nodoc -class _$StressTestConfigCopyWithImpl<$Res, $Val extends StressTestConfig> - implements $StressTestConfigCopyWith<$Res> { - _$StressTestConfigCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? concurrentRequests = null, - Object? timeout = freezed, - Object? useIsolates = null, - }) { - return _then(_value.copyWith( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value.headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - concurrentRequests: null == concurrentRequests - ? _value.concurrentRequests - : concurrentRequests // ignore: cast_nullable_to_non_nullable - as int, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - useIsolates: null == useIsolates - ? _value.useIsolates - : useIsolates // ignore: cast_nullable_to_non_nullable - as bool, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$StressTestConfigImplCopyWith<$Res> - implements $StressTestConfigCopyWith<$Res> { - factory _$$StressTestConfigImplCopyWith(_$StressTestConfigImpl value, - $Res Function(_$StressTestConfigImpl) then) = - __$$StressTestConfigImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - int concurrentRequests, - Duration? timeout, - bool useIsolates}); -} - -/// @nodoc -class __$$StressTestConfigImplCopyWithImpl<$Res> - extends _$StressTestConfigCopyWithImpl<$Res, _$StressTestConfigImpl> - implements _$$StressTestConfigImplCopyWith<$Res> { - __$$StressTestConfigImplCopyWithImpl(_$StressTestConfigImpl _value, - $Res Function(_$StressTestConfigImpl) _then) - : super(_value, _then); - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? concurrentRequests = null, - Object? timeout = freezed, - Object? useIsolates = null, - }) { - return _then(_$StressTestConfigImpl( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value._headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - concurrentRequests: null == concurrentRequests - ? _value.concurrentRequests - : concurrentRequests // ignore: cast_nullable_to_non_nullable - as int, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - useIsolates: null == useIsolates - ? _value.useIsolates - : useIsolates // ignore: cast_nullable_to_non_nullable - as bool, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$StressTestConfigImpl implements _StressTestConfig { - const _$StressTestConfigImpl( - {required this.url, - required this.method, - final Map? headers, - this.body, - required this.concurrentRequests, - this.timeout, - this.useIsolates = false}) - : _headers = headers; - - factory _$StressTestConfigImpl.fromJson(Map json) => - _$$StressTestConfigImplFromJson(json); - - @override - final String url; - @override - final String method; - final Map? _headers; - @override - Map? get headers { - final value = _headers; - if (value == null) return null; - if (_headers is EqualUnmodifiableMapView) return _headers; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(value); - } - - @override - final dynamic body; - @override - final int concurrentRequests; - @override - final Duration? timeout; - @override - @JsonKey() - final bool useIsolates; - - @override - String toString() { - return 'StressTestConfig(url: $url, method: $method, headers: $headers, body: $body, concurrentRequests: $concurrentRequests, timeout: $timeout, useIsolates: $useIsolates)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$StressTestConfigImpl && - (identical(other.url, url) || other.url == url) && - (identical(other.method, method) || other.method == method) && - const DeepCollectionEquality().equals(other._headers, _headers) && - const DeepCollectionEquality().equals(other.body, body) && - (identical(other.concurrentRequests, concurrentRequests) || - other.concurrentRequests == concurrentRequests) && - (identical(other.timeout, timeout) || other.timeout == timeout) && - (identical(other.useIsolates, useIsolates) || - other.useIsolates == useIsolates)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - url, - method, - const DeepCollectionEquality().hash(_headers), - const DeepCollectionEquality().hash(body), - concurrentRequests, - timeout, - useIsolates); - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => - __$$StressTestConfigImplCopyWithImpl<_$StressTestConfigImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$StressTestConfigImplToJson( - this, - ); - } -} - -abstract class _StressTestConfig implements StressTestConfig { - const factory _StressTestConfig( - {required final String url, - required final String method, - final Map? headers, - final dynamic body, - required final int concurrentRequests, - final Duration? timeout, - final bool useIsolates}) = _$StressTestConfigImpl; - - factory _StressTestConfig.fromJson(Map json) = - _$StressTestConfigImpl.fromJson; - - @override - String get url; - @override - String get method; - @override - Map? get headers; - @override - dynamic get body; - @override - int get concurrentRequests; - @override - Duration? get timeout; - @override - bool get useIsolates; - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/stress_test_config.g.dart b/lib/models/stress_test/stress_test_config.g.dart deleted file mode 100644 index 6193507c3..000000000 --- a/lib/models/stress_test/stress_test_config.g.dart +++ /dev/null @@ -1,35 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'stress_test_config.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$StressTestConfigImpl _$$StressTestConfigImplFromJson( - Map json) => - _$StressTestConfigImpl( - url: json['url'] as String, - method: json['method'] as String, - headers: (json['headers'] as Map?)?.map( - (k, e) => MapEntry(k, e as String), - ), - body: json['body'], - concurrentRequests: (json['concurrentRequests'] as num).toInt(), - timeout: json['timeout'] == null - ? null - : Duration(microseconds: (json['timeout'] as num).toInt()), - useIsolates: json['useIsolates'] as bool? ?? false, - ); - -Map _$$StressTestConfigImplToJson( - _$StressTestConfigImpl instance) => - { - 'url': instance.url, - 'method': instance.method, - 'headers': instance.headers, - 'body': instance.body, - 'concurrentRequests': instance.concurrentRequests, - 'timeout': instance.timeout?.inMicroseconds, - 'useIsolates': instance.useIsolates, - }; diff --git a/lib/models/stress_test/stress_test_summary.freezed.dart b/lib/models/stress_test/stress_test_summary.freezed.dart deleted file mode 100644 index b5fd37df1..000000000 --- a/lib/models/stress_test/stress_test_summary.freezed.dart +++ /dev/null @@ -1,266 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'stress_test_summary.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -StressTestSummary _$StressTestSummaryFromJson(Map json) { - return _StressTestSummary.fromJson(json); -} - -/// @nodoc -mixin _$StressTestSummary { - List get results => throw _privateConstructorUsedError; - Duration get totalDuration => throw _privateConstructorUsedError; - double get avgResponseTime => throw _privateConstructorUsedError; - int get successCount => throw _privateConstructorUsedError; - int get failureCount => throw _privateConstructorUsedError; - - /// Serializes this StressTestSummary to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $StressTestSummaryCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $StressTestSummaryCopyWith<$Res> { - factory $StressTestSummaryCopyWith( - StressTestSummary value, $Res Function(StressTestSummary) then) = - _$StressTestSummaryCopyWithImpl<$Res, StressTestSummary>; - @useResult - $Res call( - {List results, - Duration totalDuration, - double avgResponseTime, - int successCount, - int failureCount}); -} - -/// @nodoc -class _$StressTestSummaryCopyWithImpl<$Res, $Val extends StressTestSummary> - implements $StressTestSummaryCopyWith<$Res> { - _$StressTestSummaryCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? results = null, - Object? totalDuration = null, - Object? avgResponseTime = null, - Object? successCount = null, - Object? failureCount = null, - }) { - return _then(_value.copyWith( - results: null == results - ? _value.results - : results // ignore: cast_nullable_to_non_nullable - as List, - totalDuration: null == totalDuration - ? _value.totalDuration - : totalDuration // ignore: cast_nullable_to_non_nullable - as Duration, - avgResponseTime: null == avgResponseTime - ? _value.avgResponseTime - : avgResponseTime // ignore: cast_nullable_to_non_nullable - as double, - successCount: null == successCount - ? _value.successCount - : successCount // ignore: cast_nullable_to_non_nullable - as int, - failureCount: null == failureCount - ? _value.failureCount - : failureCount // ignore: cast_nullable_to_non_nullable - as int, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$StressTestSummaryImplCopyWith<$Res> - implements $StressTestSummaryCopyWith<$Res> { - factory _$$StressTestSummaryImplCopyWith(_$StressTestSummaryImpl value, - $Res Function(_$StressTestSummaryImpl) then) = - __$$StressTestSummaryImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {List results, - Duration totalDuration, - double avgResponseTime, - int successCount, - int failureCount}); -} - -/// @nodoc -class __$$StressTestSummaryImplCopyWithImpl<$Res> - extends _$StressTestSummaryCopyWithImpl<$Res, _$StressTestSummaryImpl> - implements _$$StressTestSummaryImplCopyWith<$Res> { - __$$StressTestSummaryImplCopyWithImpl(_$StressTestSummaryImpl _value, - $Res Function(_$StressTestSummaryImpl) _then) - : super(_value, _then); - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? results = null, - Object? totalDuration = null, - Object? avgResponseTime = null, - Object? successCount = null, - Object? failureCount = null, - }) { - return _then(_$StressTestSummaryImpl( - results: null == results - ? _value._results - : results // ignore: cast_nullable_to_non_nullable - as List, - totalDuration: null == totalDuration - ? _value.totalDuration - : totalDuration // ignore: cast_nullable_to_non_nullable - as Duration, - avgResponseTime: null == avgResponseTime - ? _value.avgResponseTime - : avgResponseTime // ignore: cast_nullable_to_non_nullable - as double, - successCount: null == successCount - ? _value.successCount - : successCount // ignore: cast_nullable_to_non_nullable - as int, - failureCount: null == failureCount - ? _value.failureCount - : failureCount // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$StressTestSummaryImpl implements _StressTestSummary { - const _$StressTestSummaryImpl( - {required final List results, - required this.totalDuration, - required this.avgResponseTime, - required this.successCount, - required this.failureCount}) - : _results = results; - - factory _$StressTestSummaryImpl.fromJson(Map json) => - _$$StressTestSummaryImplFromJson(json); - - final List _results; - @override - List get results { - if (_results is EqualUnmodifiableListView) return _results; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_results); - } - - @override - final Duration totalDuration; - @override - final double avgResponseTime; - @override - final int successCount; - @override - final int failureCount; - - @override - String toString() { - return 'StressTestSummary(results: $results, totalDuration: $totalDuration, avgResponseTime: $avgResponseTime, successCount: $successCount, failureCount: $failureCount)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$StressTestSummaryImpl && - const DeepCollectionEquality().equals(other._results, _results) && - (identical(other.totalDuration, totalDuration) || - other.totalDuration == totalDuration) && - (identical(other.avgResponseTime, avgResponseTime) || - other.avgResponseTime == avgResponseTime) && - (identical(other.successCount, successCount) || - other.successCount == successCount) && - (identical(other.failureCount, failureCount) || - other.failureCount == failureCount)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_results), - totalDuration, - avgResponseTime, - successCount, - failureCount); - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => - __$$StressTestSummaryImplCopyWithImpl<_$StressTestSummaryImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$StressTestSummaryImplToJson( - this, - ); - } -} - -abstract class _StressTestSummary implements StressTestSummary { - const factory _StressTestSummary( - {required final List results, - required final Duration totalDuration, - required final double avgResponseTime, - required final int successCount, - required final int failureCount}) = _$StressTestSummaryImpl; - - factory _StressTestSummary.fromJson(Map json) = - _$StressTestSummaryImpl.fromJson; - - @override - List get results; - @override - Duration get totalDuration; - @override - double get avgResponseTime; - @override - int get successCount; - @override - int get failureCount; - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/stress_test_summary.g.dart b/lib/models/stress_test/stress_test_summary.g.dart deleted file mode 100644 index 5bf4d5b48..000000000 --- a/lib/models/stress_test/stress_test_summary.g.dart +++ /dev/null @@ -1,30 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'stress_test_summary.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$StressTestSummaryImpl _$$StressTestSummaryImplFromJson( - Map json) => - _$StressTestSummaryImpl( - results: (json['results'] as List) - .map((e) => ApiRequestResult.fromJson(e as Map)) - .toList(), - totalDuration: - Duration(microseconds: (json['totalDuration'] as num).toInt()), - avgResponseTime: (json['avgResponseTime'] as num).toDouble(), - successCount: (json['successCount'] as num).toInt(), - failureCount: (json['failureCount'] as num).toInt(), - ); - -Map _$$StressTestSummaryImplToJson( - _$StressTestSummaryImpl instance) => - { - 'results': instance.results, - 'totalDuration': instance.totalDuration.inMicroseconds, - 'avgResponseTime': instance.avgResponseTime, - 'successCount': instance.successCount, - 'failureCount': instance.failureCount, - }; From 8e7c98a8a2c42f032f292ded612eeea86fd086ac Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:48:40 +0530 Subject: [PATCH 101/188] feat: add stress test config model --- .../stress_test_config.freezed.dart | 309 ++++++++++++++++++ .../stress_test/stress_test_config.g.dart | 35 ++ 2 files changed, 344 insertions(+) create mode 100644 lib/models/stress_test/stress_test_config.freezed.dart create mode 100644 lib/models/stress_test/stress_test_config.g.dart diff --git a/lib/models/stress_test/stress_test_config.freezed.dart b/lib/models/stress_test/stress_test_config.freezed.dart new file mode 100644 index 000000000..2671a5dfe --- /dev/null +++ b/lib/models/stress_test/stress_test_config.freezed.dart @@ -0,0 +1,309 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'stress_test_config.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +StressTestConfig _$StressTestConfigFromJson(Map json) { + return _StressTestConfig.fromJson(json); +} + +/// @nodoc +mixin _$StressTestConfig { + String get url => throw _privateConstructorUsedError; + String get method => throw _privateConstructorUsedError; + Map? get headers => throw _privateConstructorUsedError; + dynamic get body => throw _privateConstructorUsedError; + int get concurrentRequests => throw _privateConstructorUsedError; + Duration? get timeout => throw _privateConstructorUsedError; + bool get useIsolates => throw _privateConstructorUsedError; + + /// Serializes this StressTestConfig to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $StressTestConfigCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $StressTestConfigCopyWith<$Res> { + factory $StressTestConfigCopyWith( + StressTestConfig value, $Res Function(StressTestConfig) then) = + _$StressTestConfigCopyWithImpl<$Res, StressTestConfig>; + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + int concurrentRequests, + Duration? timeout, + bool useIsolates}); +} + +/// @nodoc +class _$StressTestConfigCopyWithImpl<$Res, $Val extends StressTestConfig> + implements $StressTestConfigCopyWith<$Res> { + _$StressTestConfigCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? concurrentRequests = null, + Object? timeout = freezed, + Object? useIsolates = null, + }) { + return _then(_value.copyWith( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value.headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + concurrentRequests: null == concurrentRequests + ? _value.concurrentRequests + : concurrentRequests // ignore: cast_nullable_to_non_nullable + as int, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + useIsolates: null == useIsolates + ? _value.useIsolates + : useIsolates // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$StressTestConfigImplCopyWith<$Res> + implements $StressTestConfigCopyWith<$Res> { + factory _$$StressTestConfigImplCopyWith(_$StressTestConfigImpl value, + $Res Function(_$StressTestConfigImpl) then) = + __$$StressTestConfigImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + int concurrentRequests, + Duration? timeout, + bool useIsolates}); +} + +/// @nodoc +class __$$StressTestConfigImplCopyWithImpl<$Res> + extends _$StressTestConfigCopyWithImpl<$Res, _$StressTestConfigImpl> + implements _$$StressTestConfigImplCopyWith<$Res> { + __$$StressTestConfigImplCopyWithImpl(_$StressTestConfigImpl _value, + $Res Function(_$StressTestConfigImpl) _then) + : super(_value, _then); + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? concurrentRequests = null, + Object? timeout = freezed, + Object? useIsolates = null, + }) { + return _then(_$StressTestConfigImpl( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value._headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + concurrentRequests: null == concurrentRequests + ? _value.concurrentRequests + : concurrentRequests // ignore: cast_nullable_to_non_nullable + as int, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + useIsolates: null == useIsolates + ? _value.useIsolates + : useIsolates // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$StressTestConfigImpl implements _StressTestConfig { + const _$StressTestConfigImpl( + {required this.url, + required this.method, + final Map? headers, + this.body, + required this.concurrentRequests, + this.timeout, + this.useIsolates = false}) + : _headers = headers; + + factory _$StressTestConfigImpl.fromJson(Map json) => + _$$StressTestConfigImplFromJson(json); + + @override + final String url; + @override + final String method; + final Map? _headers; + @override + Map? get headers { + final value = _headers; + if (value == null) return null; + if (_headers is EqualUnmodifiableMapView) return _headers; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); + } + + @override + final dynamic body; + @override + final int concurrentRequests; + @override + final Duration? timeout; + @override + @JsonKey() + final bool useIsolates; + + @override + String toString() { + return 'StressTestConfig(url: $url, method: $method, headers: $headers, body: $body, concurrentRequests: $concurrentRequests, timeout: $timeout, useIsolates: $useIsolates)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$StressTestConfigImpl && + (identical(other.url, url) || other.url == url) && + (identical(other.method, method) || other.method == method) && + const DeepCollectionEquality().equals(other._headers, _headers) && + const DeepCollectionEquality().equals(other.body, body) && + (identical(other.concurrentRequests, concurrentRequests) || + other.concurrentRequests == concurrentRequests) && + (identical(other.timeout, timeout) || other.timeout == timeout) && + (identical(other.useIsolates, useIsolates) || + other.useIsolates == useIsolates)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + url, + method, + const DeepCollectionEquality().hash(_headers), + const DeepCollectionEquality().hash(body), + concurrentRequests, + timeout, + useIsolates); + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => + __$$StressTestConfigImplCopyWithImpl<_$StressTestConfigImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$StressTestConfigImplToJson( + this, + ); + } +} + +abstract class _StressTestConfig implements StressTestConfig { + const factory _StressTestConfig( + {required final String url, + required final String method, + final Map? headers, + final dynamic body, + required final int concurrentRequests, + final Duration? timeout, + final bool useIsolates}) = _$StressTestConfigImpl; + + factory _StressTestConfig.fromJson(Map json) = + _$StressTestConfigImpl.fromJson; + + @override + String get url; + @override + String get method; + @override + Map? get headers; + @override + dynamic get body; + @override + int get concurrentRequests; + @override + Duration? get timeout; + @override + bool get useIsolates; + + /// Create a copy of StressTestConfig + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/stress_test_config.g.dart b/lib/models/stress_test/stress_test_config.g.dart new file mode 100644 index 000000000..6193507c3 --- /dev/null +++ b/lib/models/stress_test/stress_test_config.g.dart @@ -0,0 +1,35 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stress_test_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$StressTestConfigImpl _$$StressTestConfigImplFromJson( + Map json) => + _$StressTestConfigImpl( + url: json['url'] as String, + method: json['method'] as String, + headers: (json['headers'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + body: json['body'], + concurrentRequests: (json['concurrentRequests'] as num).toInt(), + timeout: json['timeout'] == null + ? null + : Duration(microseconds: (json['timeout'] as num).toInt()), + useIsolates: json['useIsolates'] as bool? ?? false, + ); + +Map _$$StressTestConfigImplToJson( + _$StressTestConfigImpl instance) => + { + 'url': instance.url, + 'method': instance.method, + 'headers': instance.headers, + 'body': instance.body, + 'concurrentRequests': instance.concurrentRequests, + 'timeout': instance.timeout?.inMicroseconds, + 'useIsolates': instance.useIsolates, + }; From 9767d883490c881c10cef8505ba29c9eecb2b516 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:49:38 +0530 Subject: [PATCH 102/188] feat: add api request result model --- .../api_request_result.freezed.dart | 224 ++++++++++++++++++ .../stress_test/api_request_result.g.dart | 25 ++ 2 files changed, 249 insertions(+) create mode 100644 lib/models/stress_test/api_request_result.freezed.dart create mode 100644 lib/models/stress_test/api_request_result.g.dart diff --git a/lib/models/stress_test/api_request_result.freezed.dart b/lib/models/stress_test/api_request_result.freezed.dart new file mode 100644 index 000000000..6bdee80a3 --- /dev/null +++ b/lib/models/stress_test/api_request_result.freezed.dart @@ -0,0 +1,224 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'api_request_result.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +ApiRequestResult _$ApiRequestResultFromJson(Map json) { + return _ApiRequestResult.fromJson(json); +} + +/// @nodoc +mixin _$ApiRequestResult { + int get statusCode => throw _privateConstructorUsedError; + String get body => throw _privateConstructorUsedError; + Duration get duration => throw _privateConstructorUsedError; + String? get error => throw _privateConstructorUsedError; + + /// Serializes this ApiRequestResult to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $ApiRequestResultCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ApiRequestResultCopyWith<$Res> { + factory $ApiRequestResultCopyWith( + ApiRequestResult value, $Res Function(ApiRequestResult) then) = + _$ApiRequestResultCopyWithImpl<$Res, ApiRequestResult>; + @useResult + $Res call({int statusCode, String body, Duration duration, String? error}); +} + +/// @nodoc +class _$ApiRequestResultCopyWithImpl<$Res, $Val extends ApiRequestResult> + implements $ApiRequestResultCopyWith<$Res> { + _$ApiRequestResultCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? statusCode = null, + Object? body = null, + Object? duration = null, + Object? error = freezed, + }) { + return _then(_value.copyWith( + statusCode: null == statusCode + ? _value.statusCode + : statusCode // ignore: cast_nullable_to_non_nullable + as int, + body: null == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as String, + duration: null == duration + ? _value.duration + : duration // ignore: cast_nullable_to_non_nullable + as Duration, + error: freezed == error + ? _value.error + : error // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$ApiRequestResultImplCopyWith<$Res> + implements $ApiRequestResultCopyWith<$Res> { + factory _$$ApiRequestResultImplCopyWith(_$ApiRequestResultImpl value, + $Res Function(_$ApiRequestResultImpl) then) = + __$$ApiRequestResultImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({int statusCode, String body, Duration duration, String? error}); +} + +/// @nodoc +class __$$ApiRequestResultImplCopyWithImpl<$Res> + extends _$ApiRequestResultCopyWithImpl<$Res, _$ApiRequestResultImpl> + implements _$$ApiRequestResultImplCopyWith<$Res> { + __$$ApiRequestResultImplCopyWithImpl(_$ApiRequestResultImpl _value, + $Res Function(_$ApiRequestResultImpl) _then) + : super(_value, _then); + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? statusCode = null, + Object? body = null, + Object? duration = null, + Object? error = freezed, + }) { + return _then(_$ApiRequestResultImpl( + statusCode: null == statusCode + ? _value.statusCode + : statusCode // ignore: cast_nullable_to_non_nullable + as int, + body: null == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as String, + duration: null == duration + ? _value.duration + : duration // ignore: cast_nullable_to_non_nullable + as Duration, + error: freezed == error + ? _value.error + : error // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ApiRequestResultImpl implements _ApiRequestResult { + const _$ApiRequestResultImpl( + {required this.statusCode, + required this.body, + required this.duration, + this.error}); + + factory _$ApiRequestResultImpl.fromJson(Map json) => + _$$ApiRequestResultImplFromJson(json); + + @override + final int statusCode; + @override + final String body; + @override + final Duration duration; + @override + final String? error; + + @override + String toString() { + return 'ApiRequestResult(statusCode: $statusCode, body: $body, duration: $duration, error: $error)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ApiRequestResultImpl && + (identical(other.statusCode, statusCode) || + other.statusCode == statusCode) && + (identical(other.body, body) || other.body == body) && + (identical(other.duration, duration) || + other.duration == duration) && + (identical(other.error, error) || other.error == error)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => + Object.hash(runtimeType, statusCode, body, duration, error); + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => + __$$ApiRequestResultImplCopyWithImpl<_$ApiRequestResultImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$ApiRequestResultImplToJson( + this, + ); + } +} + +abstract class _ApiRequestResult implements ApiRequestResult { + const factory _ApiRequestResult( + {required final int statusCode, + required final String body, + required final Duration duration, + final String? error}) = _$ApiRequestResultImpl; + + factory _ApiRequestResult.fromJson(Map json) = + _$ApiRequestResultImpl.fromJson; + + @override + int get statusCode; + @override + String get body; + @override + Duration get duration; + @override + String? get error; + + /// Create a copy of ApiRequestResult + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/api_request_result.g.dart b/lib/models/stress_test/api_request_result.g.dart new file mode 100644 index 000000000..4182a8e58 --- /dev/null +++ b/lib/models/stress_test/api_request_result.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'api_request_result.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$ApiRequestResultImpl _$$ApiRequestResultImplFromJson( + Map json) => + _$ApiRequestResultImpl( + statusCode: (json['statusCode'] as num).toInt(), + body: json['body'] as String, + duration: Duration(microseconds: (json['duration'] as num).toInt()), + error: json['error'] as String?, + ); + +Map _$$ApiRequestResultImplToJson( + _$ApiRequestResultImpl instance) => + { + 'statusCode': instance.statusCode, + 'body': instance.body, + 'duration': instance.duration.inMicroseconds, + 'error': instance.error, + }; From a8e4ef9c4569e8157ad4ada11ff85ce02ac1338e Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:50:18 +0530 Subject: [PATCH 103/188] feat: add isolate model for stress test --- .../stress_test/isolate_message.freezed.dart | 264 ++++++++++++++++++ lib/models/stress_test/isolate_message.g.dart | 30 ++ 2 files changed, 294 insertions(+) create mode 100644 lib/models/stress_test/isolate_message.freezed.dart create mode 100644 lib/models/stress_test/isolate_message.g.dart diff --git a/lib/models/stress_test/isolate_message.freezed.dart b/lib/models/stress_test/isolate_message.freezed.dart new file mode 100644 index 000000000..c3abc6857 --- /dev/null +++ b/lib/models/stress_test/isolate_message.freezed.dart @@ -0,0 +1,264 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'isolate_message.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +IsolateMessage _$IsolateMessageFromJson(Map json) { + return _IsolateMessage.fromJson(json); +} + +/// @nodoc +mixin _$IsolateMessage { + String get url => throw _privateConstructorUsedError; + String get method => throw _privateConstructorUsedError; + Map? get headers => throw _privateConstructorUsedError; + dynamic get body => throw _privateConstructorUsedError; + Duration? get timeout => throw _privateConstructorUsedError; + + /// Serializes this IsolateMessage to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $IsolateMessageCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $IsolateMessageCopyWith<$Res> { + factory $IsolateMessageCopyWith( + IsolateMessage value, $Res Function(IsolateMessage) then) = + _$IsolateMessageCopyWithImpl<$Res, IsolateMessage>; + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + Duration? timeout}); +} + +/// @nodoc +class _$IsolateMessageCopyWithImpl<$Res, $Val extends IsolateMessage> + implements $IsolateMessageCopyWith<$Res> { + _$IsolateMessageCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? timeout = freezed, + }) { + return _then(_value.copyWith( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value.headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$IsolateMessageImplCopyWith<$Res> + implements $IsolateMessageCopyWith<$Res> { + factory _$$IsolateMessageImplCopyWith(_$IsolateMessageImpl value, + $Res Function(_$IsolateMessageImpl) then) = + __$$IsolateMessageImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String url, + String method, + Map? headers, + dynamic body, + Duration? timeout}); +} + +/// @nodoc +class __$$IsolateMessageImplCopyWithImpl<$Res> + extends _$IsolateMessageCopyWithImpl<$Res, _$IsolateMessageImpl> + implements _$$IsolateMessageImplCopyWith<$Res> { + __$$IsolateMessageImplCopyWithImpl( + _$IsolateMessageImpl _value, $Res Function(_$IsolateMessageImpl) _then) + : super(_value, _then); + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? url = null, + Object? method = null, + Object? headers = freezed, + Object? body = freezed, + Object? timeout = freezed, + }) { + return _then(_$IsolateMessageImpl( + url: null == url + ? _value.url + : url // ignore: cast_nullable_to_non_nullable + as String, + method: null == method + ? _value.method + : method // ignore: cast_nullable_to_non_nullable + as String, + headers: freezed == headers + ? _value._headers + : headers // ignore: cast_nullable_to_non_nullable + as Map?, + body: freezed == body + ? _value.body + : body // ignore: cast_nullable_to_non_nullable + as dynamic, + timeout: freezed == timeout + ? _value.timeout + : timeout // ignore: cast_nullable_to_non_nullable + as Duration?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$IsolateMessageImpl implements _IsolateMessage { + const _$IsolateMessageImpl( + {required this.url, + required this.method, + final Map? headers, + this.body, + this.timeout}) + : _headers = headers; + + factory _$IsolateMessageImpl.fromJson(Map json) => + _$$IsolateMessageImplFromJson(json); + + @override + final String url; + @override + final String method; + final Map? _headers; + @override + Map? get headers { + final value = _headers; + if (value == null) return null; + if (_headers is EqualUnmodifiableMapView) return _headers; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); + } + + @override + final dynamic body; + @override + final Duration? timeout; + + @override + String toString() { + return 'IsolateMessage(url: $url, method: $method, headers: $headers, body: $body, timeout: $timeout)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$IsolateMessageImpl && + (identical(other.url, url) || other.url == url) && + (identical(other.method, method) || other.method == method) && + const DeepCollectionEquality().equals(other._headers, _headers) && + const DeepCollectionEquality().equals(other.body, body) && + (identical(other.timeout, timeout) || other.timeout == timeout)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + url, + method, + const DeepCollectionEquality().hash(_headers), + const DeepCollectionEquality().hash(body), + timeout); + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => + __$$IsolateMessageImplCopyWithImpl<_$IsolateMessageImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$IsolateMessageImplToJson( + this, + ); + } +} + +abstract class _IsolateMessage implements IsolateMessage { + const factory _IsolateMessage( + {required final String url, + required final String method, + final Map? headers, + final dynamic body, + final Duration? timeout}) = _$IsolateMessageImpl; + + factory _IsolateMessage.fromJson(Map json) = + _$IsolateMessageImpl.fromJson; + + @override + String get url; + @override + String get method; + @override + Map? get headers; + @override + dynamic get body; + @override + Duration? get timeout; + + /// Create a copy of IsolateMessage + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/isolate_message.g.dart b/lib/models/stress_test/isolate_message.g.dart new file mode 100644 index 000000000..e5f403ea1 --- /dev/null +++ b/lib/models/stress_test/isolate_message.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'isolate_message.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$IsolateMessageImpl _$$IsolateMessageImplFromJson(Map json) => + _$IsolateMessageImpl( + url: json['url'] as String, + method: json['method'] as String, + headers: (json['headers'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + body: json['body'], + timeout: json['timeout'] == null + ? null + : Duration(microseconds: (json['timeout'] as num).toInt()), + ); + +Map _$$IsolateMessageImplToJson( + _$IsolateMessageImpl instance) => + { + 'url': instance.url, + 'method': instance.method, + 'headers': instance.headers, + 'body': instance.body, + 'timeout': instance.timeout?.inMicroseconds, + }; From 6b8c7983ab3cfae3306827494b33770af8878f78 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 00:51:01 +0530 Subject: [PATCH 104/188] feat: add stress test summer model --- .../stress_test_summary.freezed.dart | 266 ++++++++++++++++++ .../stress_test/stress_test_summary.g.dart | 30 ++ 2 files changed, 296 insertions(+) create mode 100644 lib/models/stress_test/stress_test_summary.freezed.dart create mode 100644 lib/models/stress_test/stress_test_summary.g.dart diff --git a/lib/models/stress_test/stress_test_summary.freezed.dart b/lib/models/stress_test/stress_test_summary.freezed.dart new file mode 100644 index 000000000..b5fd37df1 --- /dev/null +++ b/lib/models/stress_test/stress_test_summary.freezed.dart @@ -0,0 +1,266 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'stress_test_summary.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +StressTestSummary _$StressTestSummaryFromJson(Map json) { + return _StressTestSummary.fromJson(json); +} + +/// @nodoc +mixin _$StressTestSummary { + List get results => throw _privateConstructorUsedError; + Duration get totalDuration => throw _privateConstructorUsedError; + double get avgResponseTime => throw _privateConstructorUsedError; + int get successCount => throw _privateConstructorUsedError; + int get failureCount => throw _privateConstructorUsedError; + + /// Serializes this StressTestSummary to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $StressTestSummaryCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $StressTestSummaryCopyWith<$Res> { + factory $StressTestSummaryCopyWith( + StressTestSummary value, $Res Function(StressTestSummary) then) = + _$StressTestSummaryCopyWithImpl<$Res, StressTestSummary>; + @useResult + $Res call( + {List results, + Duration totalDuration, + double avgResponseTime, + int successCount, + int failureCount}); +} + +/// @nodoc +class _$StressTestSummaryCopyWithImpl<$Res, $Val extends StressTestSummary> + implements $StressTestSummaryCopyWith<$Res> { + _$StressTestSummaryCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? results = null, + Object? totalDuration = null, + Object? avgResponseTime = null, + Object? successCount = null, + Object? failureCount = null, + }) { + return _then(_value.copyWith( + results: null == results + ? _value.results + : results // ignore: cast_nullable_to_non_nullable + as List, + totalDuration: null == totalDuration + ? _value.totalDuration + : totalDuration // ignore: cast_nullable_to_non_nullable + as Duration, + avgResponseTime: null == avgResponseTime + ? _value.avgResponseTime + : avgResponseTime // ignore: cast_nullable_to_non_nullable + as double, + successCount: null == successCount + ? _value.successCount + : successCount // ignore: cast_nullable_to_non_nullable + as int, + failureCount: null == failureCount + ? _value.failureCount + : failureCount // ignore: cast_nullable_to_non_nullable + as int, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$StressTestSummaryImplCopyWith<$Res> + implements $StressTestSummaryCopyWith<$Res> { + factory _$$StressTestSummaryImplCopyWith(_$StressTestSummaryImpl value, + $Res Function(_$StressTestSummaryImpl) then) = + __$$StressTestSummaryImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {List results, + Duration totalDuration, + double avgResponseTime, + int successCount, + int failureCount}); +} + +/// @nodoc +class __$$StressTestSummaryImplCopyWithImpl<$Res> + extends _$StressTestSummaryCopyWithImpl<$Res, _$StressTestSummaryImpl> + implements _$$StressTestSummaryImplCopyWith<$Res> { + __$$StressTestSummaryImplCopyWithImpl(_$StressTestSummaryImpl _value, + $Res Function(_$StressTestSummaryImpl) _then) + : super(_value, _then); + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? results = null, + Object? totalDuration = null, + Object? avgResponseTime = null, + Object? successCount = null, + Object? failureCount = null, + }) { + return _then(_$StressTestSummaryImpl( + results: null == results + ? _value._results + : results // ignore: cast_nullable_to_non_nullable + as List, + totalDuration: null == totalDuration + ? _value.totalDuration + : totalDuration // ignore: cast_nullable_to_non_nullable + as Duration, + avgResponseTime: null == avgResponseTime + ? _value.avgResponseTime + : avgResponseTime // ignore: cast_nullable_to_non_nullable + as double, + successCount: null == successCount + ? _value.successCount + : successCount // ignore: cast_nullable_to_non_nullable + as int, + failureCount: null == failureCount + ? _value.failureCount + : failureCount // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$StressTestSummaryImpl implements _StressTestSummary { + const _$StressTestSummaryImpl( + {required final List results, + required this.totalDuration, + required this.avgResponseTime, + required this.successCount, + required this.failureCount}) + : _results = results; + + factory _$StressTestSummaryImpl.fromJson(Map json) => + _$$StressTestSummaryImplFromJson(json); + + final List _results; + @override + List get results { + if (_results is EqualUnmodifiableListView) return _results; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_results); + } + + @override + final Duration totalDuration; + @override + final double avgResponseTime; + @override + final int successCount; + @override + final int failureCount; + + @override + String toString() { + return 'StressTestSummary(results: $results, totalDuration: $totalDuration, avgResponseTime: $avgResponseTime, successCount: $successCount, failureCount: $failureCount)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$StressTestSummaryImpl && + const DeepCollectionEquality().equals(other._results, _results) && + (identical(other.totalDuration, totalDuration) || + other.totalDuration == totalDuration) && + (identical(other.avgResponseTime, avgResponseTime) || + other.avgResponseTime == avgResponseTime) && + (identical(other.successCount, successCount) || + other.successCount == successCount) && + (identical(other.failureCount, failureCount) || + other.failureCount == failureCount)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(_results), + totalDuration, + avgResponseTime, + successCount, + failureCount); + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => + __$$StressTestSummaryImplCopyWithImpl<_$StressTestSummaryImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$StressTestSummaryImplToJson( + this, + ); + } +} + +abstract class _StressTestSummary implements StressTestSummary { + const factory _StressTestSummary( + {required final List results, + required final Duration totalDuration, + required final double avgResponseTime, + required final int successCount, + required final int failureCount}) = _$StressTestSummaryImpl; + + factory _StressTestSummary.fromJson(Map json) = + _$StressTestSummaryImpl.fromJson; + + @override + List get results; + @override + Duration get totalDuration; + @override + double get avgResponseTime; + @override + int get successCount; + @override + int get failureCount; + + /// Create a copy of StressTestSummary + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/stress_test/stress_test_summary.g.dart b/lib/models/stress_test/stress_test_summary.g.dart new file mode 100644 index 000000000..5bf4d5b48 --- /dev/null +++ b/lib/models/stress_test/stress_test_summary.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stress_test_summary.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$StressTestSummaryImpl _$$StressTestSummaryImplFromJson( + Map json) => + _$StressTestSummaryImpl( + results: (json['results'] as List) + .map((e) => ApiRequestResult.fromJson(e as Map)) + .toList(), + totalDuration: + Duration(microseconds: (json['totalDuration'] as num).toInt()), + avgResponseTime: (json['avgResponseTime'] as num).toDouble(), + successCount: (json['successCount'] as num).toInt(), + failureCount: (json['failureCount'] as num).toInt(), + ); + +Map _$$StressTestSummaryImplToJson( + _$StressTestSummaryImpl instance) => + { + 'results': instance.results, + 'totalDuration': instance.totalDuration.inMicroseconds, + 'avgResponseTime': instance.avgResponseTime, + 'successCount': instance.successCount, + 'failureCount': instance.failureCount, + }; From 1eea21a7ba8c97062117b79a0f44bdfb66ea8ee5 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 29 Mar 2025 13:53:49 +0530 Subject: [PATCH 105/188] chore: implement the stress test UI in request pane --- .../api_request_result.freezed.dart | 224 ------------- .../stress_test/api_request_result.g.dart | 25 -- .../stress_test/isolate_message.freezed.dart | 264 --------------- lib/models/stress_test/isolate_message.g.dart | 30 -- .../stress_test_config.freezed.dart | 309 ------------------ .../stress_test/stress_test_config.g.dart | 35 -- .../stress_test_summary.freezed.dart | 266 --------------- .../stress_test/stress_test_summary.g.dart | 30 -- 8 files changed, 1183 deletions(-) delete mode 100644 lib/models/stress_test/api_request_result.freezed.dart delete mode 100644 lib/models/stress_test/api_request_result.g.dart delete mode 100644 lib/models/stress_test/isolate_message.freezed.dart delete mode 100644 lib/models/stress_test/isolate_message.g.dart delete mode 100644 lib/models/stress_test/stress_test_config.freezed.dart delete mode 100644 lib/models/stress_test/stress_test_config.g.dart delete mode 100644 lib/models/stress_test/stress_test_summary.freezed.dart delete mode 100644 lib/models/stress_test/stress_test_summary.g.dart diff --git a/lib/models/stress_test/api_request_result.freezed.dart b/lib/models/stress_test/api_request_result.freezed.dart deleted file mode 100644 index 6bdee80a3..000000000 --- a/lib/models/stress_test/api_request_result.freezed.dart +++ /dev/null @@ -1,224 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'api_request_result.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -ApiRequestResult _$ApiRequestResultFromJson(Map json) { - return _ApiRequestResult.fromJson(json); -} - -/// @nodoc -mixin _$ApiRequestResult { - int get statusCode => throw _privateConstructorUsedError; - String get body => throw _privateConstructorUsedError; - Duration get duration => throw _privateConstructorUsedError; - String? get error => throw _privateConstructorUsedError; - - /// Serializes this ApiRequestResult to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ApiRequestResultCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ApiRequestResultCopyWith<$Res> { - factory $ApiRequestResultCopyWith( - ApiRequestResult value, $Res Function(ApiRequestResult) then) = - _$ApiRequestResultCopyWithImpl<$Res, ApiRequestResult>; - @useResult - $Res call({int statusCode, String body, Duration duration, String? error}); -} - -/// @nodoc -class _$ApiRequestResultCopyWithImpl<$Res, $Val extends ApiRequestResult> - implements $ApiRequestResultCopyWith<$Res> { - _$ApiRequestResultCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? statusCode = null, - Object? body = null, - Object? duration = null, - Object? error = freezed, - }) { - return _then(_value.copyWith( - statusCode: null == statusCode - ? _value.statusCode - : statusCode // ignore: cast_nullable_to_non_nullable - as int, - body: null == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as String, - duration: null == duration - ? _value.duration - : duration // ignore: cast_nullable_to_non_nullable - as Duration, - error: freezed == error - ? _value.error - : error // ignore: cast_nullable_to_non_nullable - as String?, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$ApiRequestResultImplCopyWith<$Res> - implements $ApiRequestResultCopyWith<$Res> { - factory _$$ApiRequestResultImplCopyWith(_$ApiRequestResultImpl value, - $Res Function(_$ApiRequestResultImpl) then) = - __$$ApiRequestResultImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({int statusCode, String body, Duration duration, String? error}); -} - -/// @nodoc -class __$$ApiRequestResultImplCopyWithImpl<$Res> - extends _$ApiRequestResultCopyWithImpl<$Res, _$ApiRequestResultImpl> - implements _$$ApiRequestResultImplCopyWith<$Res> { - __$$ApiRequestResultImplCopyWithImpl(_$ApiRequestResultImpl _value, - $Res Function(_$ApiRequestResultImpl) _then) - : super(_value, _then); - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? statusCode = null, - Object? body = null, - Object? duration = null, - Object? error = freezed, - }) { - return _then(_$ApiRequestResultImpl( - statusCode: null == statusCode - ? _value.statusCode - : statusCode // ignore: cast_nullable_to_non_nullable - as int, - body: null == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as String, - duration: null == duration - ? _value.duration - : duration // ignore: cast_nullable_to_non_nullable - as Duration, - error: freezed == error - ? _value.error - : error // ignore: cast_nullable_to_non_nullable - as String?, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$ApiRequestResultImpl implements _ApiRequestResult { - const _$ApiRequestResultImpl( - {required this.statusCode, - required this.body, - required this.duration, - this.error}); - - factory _$ApiRequestResultImpl.fromJson(Map json) => - _$$ApiRequestResultImplFromJson(json); - - @override - final int statusCode; - @override - final String body; - @override - final Duration duration; - @override - final String? error; - - @override - String toString() { - return 'ApiRequestResult(statusCode: $statusCode, body: $body, duration: $duration, error: $error)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ApiRequestResultImpl && - (identical(other.statusCode, statusCode) || - other.statusCode == statusCode) && - (identical(other.body, body) || other.body == body) && - (identical(other.duration, duration) || - other.duration == duration) && - (identical(other.error, error) || other.error == error)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => - Object.hash(runtimeType, statusCode, body, duration, error); - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => - __$$ApiRequestResultImplCopyWithImpl<_$ApiRequestResultImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$ApiRequestResultImplToJson( - this, - ); - } -} - -abstract class _ApiRequestResult implements ApiRequestResult { - const factory _ApiRequestResult( - {required final int statusCode, - required final String body, - required final Duration duration, - final String? error}) = _$ApiRequestResultImpl; - - factory _ApiRequestResult.fromJson(Map json) = - _$ApiRequestResultImpl.fromJson; - - @override - int get statusCode; - @override - String get body; - @override - Duration get duration; - @override - String? get error; - - /// Create a copy of ApiRequestResult - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ApiRequestResultImplCopyWith<_$ApiRequestResultImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/api_request_result.g.dart b/lib/models/stress_test/api_request_result.g.dart deleted file mode 100644 index 4182a8e58..000000000 --- a/lib/models/stress_test/api_request_result.g.dart +++ /dev/null @@ -1,25 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'api_request_result.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$ApiRequestResultImpl _$$ApiRequestResultImplFromJson( - Map json) => - _$ApiRequestResultImpl( - statusCode: (json['statusCode'] as num).toInt(), - body: json['body'] as String, - duration: Duration(microseconds: (json['duration'] as num).toInt()), - error: json['error'] as String?, - ); - -Map _$$ApiRequestResultImplToJson( - _$ApiRequestResultImpl instance) => - { - 'statusCode': instance.statusCode, - 'body': instance.body, - 'duration': instance.duration.inMicroseconds, - 'error': instance.error, - }; diff --git a/lib/models/stress_test/isolate_message.freezed.dart b/lib/models/stress_test/isolate_message.freezed.dart deleted file mode 100644 index c3abc6857..000000000 --- a/lib/models/stress_test/isolate_message.freezed.dart +++ /dev/null @@ -1,264 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'isolate_message.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -IsolateMessage _$IsolateMessageFromJson(Map json) { - return _IsolateMessage.fromJson(json); -} - -/// @nodoc -mixin _$IsolateMessage { - String get url => throw _privateConstructorUsedError; - String get method => throw _privateConstructorUsedError; - Map? get headers => throw _privateConstructorUsedError; - dynamic get body => throw _privateConstructorUsedError; - Duration? get timeout => throw _privateConstructorUsedError; - - /// Serializes this IsolateMessage to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $IsolateMessageCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $IsolateMessageCopyWith<$Res> { - factory $IsolateMessageCopyWith( - IsolateMessage value, $Res Function(IsolateMessage) then) = - _$IsolateMessageCopyWithImpl<$Res, IsolateMessage>; - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - Duration? timeout}); -} - -/// @nodoc -class _$IsolateMessageCopyWithImpl<$Res, $Val extends IsolateMessage> - implements $IsolateMessageCopyWith<$Res> { - _$IsolateMessageCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? timeout = freezed, - }) { - return _then(_value.copyWith( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value.headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$IsolateMessageImplCopyWith<$Res> - implements $IsolateMessageCopyWith<$Res> { - factory _$$IsolateMessageImplCopyWith(_$IsolateMessageImpl value, - $Res Function(_$IsolateMessageImpl) then) = - __$$IsolateMessageImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - Duration? timeout}); -} - -/// @nodoc -class __$$IsolateMessageImplCopyWithImpl<$Res> - extends _$IsolateMessageCopyWithImpl<$Res, _$IsolateMessageImpl> - implements _$$IsolateMessageImplCopyWith<$Res> { - __$$IsolateMessageImplCopyWithImpl( - _$IsolateMessageImpl _value, $Res Function(_$IsolateMessageImpl) _then) - : super(_value, _then); - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? timeout = freezed, - }) { - return _then(_$IsolateMessageImpl( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value._headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$IsolateMessageImpl implements _IsolateMessage { - const _$IsolateMessageImpl( - {required this.url, - required this.method, - final Map? headers, - this.body, - this.timeout}) - : _headers = headers; - - factory _$IsolateMessageImpl.fromJson(Map json) => - _$$IsolateMessageImplFromJson(json); - - @override - final String url; - @override - final String method; - final Map? _headers; - @override - Map? get headers { - final value = _headers; - if (value == null) return null; - if (_headers is EqualUnmodifiableMapView) return _headers; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(value); - } - - @override - final dynamic body; - @override - final Duration? timeout; - - @override - String toString() { - return 'IsolateMessage(url: $url, method: $method, headers: $headers, body: $body, timeout: $timeout)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$IsolateMessageImpl && - (identical(other.url, url) || other.url == url) && - (identical(other.method, method) || other.method == method) && - const DeepCollectionEquality().equals(other._headers, _headers) && - const DeepCollectionEquality().equals(other.body, body) && - (identical(other.timeout, timeout) || other.timeout == timeout)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - url, - method, - const DeepCollectionEquality().hash(_headers), - const DeepCollectionEquality().hash(body), - timeout); - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => - __$$IsolateMessageImplCopyWithImpl<_$IsolateMessageImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$IsolateMessageImplToJson( - this, - ); - } -} - -abstract class _IsolateMessage implements IsolateMessage { - const factory _IsolateMessage( - {required final String url, - required final String method, - final Map? headers, - final dynamic body, - final Duration? timeout}) = _$IsolateMessageImpl; - - factory _IsolateMessage.fromJson(Map json) = - _$IsolateMessageImpl.fromJson; - - @override - String get url; - @override - String get method; - @override - Map? get headers; - @override - dynamic get body; - @override - Duration? get timeout; - - /// Create a copy of IsolateMessage - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$IsolateMessageImplCopyWith<_$IsolateMessageImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/isolate_message.g.dart b/lib/models/stress_test/isolate_message.g.dart deleted file mode 100644 index e5f403ea1..000000000 --- a/lib/models/stress_test/isolate_message.g.dart +++ /dev/null @@ -1,30 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'isolate_message.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$IsolateMessageImpl _$$IsolateMessageImplFromJson(Map json) => - _$IsolateMessageImpl( - url: json['url'] as String, - method: json['method'] as String, - headers: (json['headers'] as Map?)?.map( - (k, e) => MapEntry(k, e as String), - ), - body: json['body'], - timeout: json['timeout'] == null - ? null - : Duration(microseconds: (json['timeout'] as num).toInt()), - ); - -Map _$$IsolateMessageImplToJson( - _$IsolateMessageImpl instance) => - { - 'url': instance.url, - 'method': instance.method, - 'headers': instance.headers, - 'body': instance.body, - 'timeout': instance.timeout?.inMicroseconds, - }; diff --git a/lib/models/stress_test/stress_test_config.freezed.dart b/lib/models/stress_test/stress_test_config.freezed.dart deleted file mode 100644 index 2671a5dfe..000000000 --- a/lib/models/stress_test/stress_test_config.freezed.dart +++ /dev/null @@ -1,309 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'stress_test_config.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -StressTestConfig _$StressTestConfigFromJson(Map json) { - return _StressTestConfig.fromJson(json); -} - -/// @nodoc -mixin _$StressTestConfig { - String get url => throw _privateConstructorUsedError; - String get method => throw _privateConstructorUsedError; - Map? get headers => throw _privateConstructorUsedError; - dynamic get body => throw _privateConstructorUsedError; - int get concurrentRequests => throw _privateConstructorUsedError; - Duration? get timeout => throw _privateConstructorUsedError; - bool get useIsolates => throw _privateConstructorUsedError; - - /// Serializes this StressTestConfig to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $StressTestConfigCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $StressTestConfigCopyWith<$Res> { - factory $StressTestConfigCopyWith( - StressTestConfig value, $Res Function(StressTestConfig) then) = - _$StressTestConfigCopyWithImpl<$Res, StressTestConfig>; - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - int concurrentRequests, - Duration? timeout, - bool useIsolates}); -} - -/// @nodoc -class _$StressTestConfigCopyWithImpl<$Res, $Val extends StressTestConfig> - implements $StressTestConfigCopyWith<$Res> { - _$StressTestConfigCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? concurrentRequests = null, - Object? timeout = freezed, - Object? useIsolates = null, - }) { - return _then(_value.copyWith( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value.headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - concurrentRequests: null == concurrentRequests - ? _value.concurrentRequests - : concurrentRequests // ignore: cast_nullable_to_non_nullable - as int, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - useIsolates: null == useIsolates - ? _value.useIsolates - : useIsolates // ignore: cast_nullable_to_non_nullable - as bool, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$StressTestConfigImplCopyWith<$Res> - implements $StressTestConfigCopyWith<$Res> { - factory _$$StressTestConfigImplCopyWith(_$StressTestConfigImpl value, - $Res Function(_$StressTestConfigImpl) then) = - __$$StressTestConfigImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String url, - String method, - Map? headers, - dynamic body, - int concurrentRequests, - Duration? timeout, - bool useIsolates}); -} - -/// @nodoc -class __$$StressTestConfigImplCopyWithImpl<$Res> - extends _$StressTestConfigCopyWithImpl<$Res, _$StressTestConfigImpl> - implements _$$StressTestConfigImplCopyWith<$Res> { - __$$StressTestConfigImplCopyWithImpl(_$StressTestConfigImpl _value, - $Res Function(_$StressTestConfigImpl) _then) - : super(_value, _then); - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? url = null, - Object? method = null, - Object? headers = freezed, - Object? body = freezed, - Object? concurrentRequests = null, - Object? timeout = freezed, - Object? useIsolates = null, - }) { - return _then(_$StressTestConfigImpl( - url: null == url - ? _value.url - : url // ignore: cast_nullable_to_non_nullable - as String, - method: null == method - ? _value.method - : method // ignore: cast_nullable_to_non_nullable - as String, - headers: freezed == headers - ? _value._headers - : headers // ignore: cast_nullable_to_non_nullable - as Map?, - body: freezed == body - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as dynamic, - concurrentRequests: null == concurrentRequests - ? _value.concurrentRequests - : concurrentRequests // ignore: cast_nullable_to_non_nullable - as int, - timeout: freezed == timeout - ? _value.timeout - : timeout // ignore: cast_nullable_to_non_nullable - as Duration?, - useIsolates: null == useIsolates - ? _value.useIsolates - : useIsolates // ignore: cast_nullable_to_non_nullable - as bool, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$StressTestConfigImpl implements _StressTestConfig { - const _$StressTestConfigImpl( - {required this.url, - required this.method, - final Map? headers, - this.body, - required this.concurrentRequests, - this.timeout, - this.useIsolates = false}) - : _headers = headers; - - factory _$StressTestConfigImpl.fromJson(Map json) => - _$$StressTestConfigImplFromJson(json); - - @override - final String url; - @override - final String method; - final Map? _headers; - @override - Map? get headers { - final value = _headers; - if (value == null) return null; - if (_headers is EqualUnmodifiableMapView) return _headers; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(value); - } - - @override - final dynamic body; - @override - final int concurrentRequests; - @override - final Duration? timeout; - @override - @JsonKey() - final bool useIsolates; - - @override - String toString() { - return 'StressTestConfig(url: $url, method: $method, headers: $headers, body: $body, concurrentRequests: $concurrentRequests, timeout: $timeout, useIsolates: $useIsolates)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$StressTestConfigImpl && - (identical(other.url, url) || other.url == url) && - (identical(other.method, method) || other.method == method) && - const DeepCollectionEquality().equals(other._headers, _headers) && - const DeepCollectionEquality().equals(other.body, body) && - (identical(other.concurrentRequests, concurrentRequests) || - other.concurrentRequests == concurrentRequests) && - (identical(other.timeout, timeout) || other.timeout == timeout) && - (identical(other.useIsolates, useIsolates) || - other.useIsolates == useIsolates)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - url, - method, - const DeepCollectionEquality().hash(_headers), - const DeepCollectionEquality().hash(body), - concurrentRequests, - timeout, - useIsolates); - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => - __$$StressTestConfigImplCopyWithImpl<_$StressTestConfigImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$StressTestConfigImplToJson( - this, - ); - } -} - -abstract class _StressTestConfig implements StressTestConfig { - const factory _StressTestConfig( - {required final String url, - required final String method, - final Map? headers, - final dynamic body, - required final int concurrentRequests, - final Duration? timeout, - final bool useIsolates}) = _$StressTestConfigImpl; - - factory _StressTestConfig.fromJson(Map json) = - _$StressTestConfigImpl.fromJson; - - @override - String get url; - @override - String get method; - @override - Map? get headers; - @override - dynamic get body; - @override - int get concurrentRequests; - @override - Duration? get timeout; - @override - bool get useIsolates; - - /// Create a copy of StressTestConfig - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$StressTestConfigImplCopyWith<_$StressTestConfigImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/stress_test_config.g.dart b/lib/models/stress_test/stress_test_config.g.dart deleted file mode 100644 index 6193507c3..000000000 --- a/lib/models/stress_test/stress_test_config.g.dart +++ /dev/null @@ -1,35 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'stress_test_config.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$StressTestConfigImpl _$$StressTestConfigImplFromJson( - Map json) => - _$StressTestConfigImpl( - url: json['url'] as String, - method: json['method'] as String, - headers: (json['headers'] as Map?)?.map( - (k, e) => MapEntry(k, e as String), - ), - body: json['body'], - concurrentRequests: (json['concurrentRequests'] as num).toInt(), - timeout: json['timeout'] == null - ? null - : Duration(microseconds: (json['timeout'] as num).toInt()), - useIsolates: json['useIsolates'] as bool? ?? false, - ); - -Map _$$StressTestConfigImplToJson( - _$StressTestConfigImpl instance) => - { - 'url': instance.url, - 'method': instance.method, - 'headers': instance.headers, - 'body': instance.body, - 'concurrentRequests': instance.concurrentRequests, - 'timeout': instance.timeout?.inMicroseconds, - 'useIsolates': instance.useIsolates, - }; diff --git a/lib/models/stress_test/stress_test_summary.freezed.dart b/lib/models/stress_test/stress_test_summary.freezed.dart deleted file mode 100644 index b5fd37df1..000000000 --- a/lib/models/stress_test/stress_test_summary.freezed.dart +++ /dev/null @@ -1,266 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'stress_test_summary.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -StressTestSummary _$StressTestSummaryFromJson(Map json) { - return _StressTestSummary.fromJson(json); -} - -/// @nodoc -mixin _$StressTestSummary { - List get results => throw _privateConstructorUsedError; - Duration get totalDuration => throw _privateConstructorUsedError; - double get avgResponseTime => throw _privateConstructorUsedError; - int get successCount => throw _privateConstructorUsedError; - int get failureCount => throw _privateConstructorUsedError; - - /// Serializes this StressTestSummary to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $StressTestSummaryCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $StressTestSummaryCopyWith<$Res> { - factory $StressTestSummaryCopyWith( - StressTestSummary value, $Res Function(StressTestSummary) then) = - _$StressTestSummaryCopyWithImpl<$Res, StressTestSummary>; - @useResult - $Res call( - {List results, - Duration totalDuration, - double avgResponseTime, - int successCount, - int failureCount}); -} - -/// @nodoc -class _$StressTestSummaryCopyWithImpl<$Res, $Val extends StressTestSummary> - implements $StressTestSummaryCopyWith<$Res> { - _$StressTestSummaryCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? results = null, - Object? totalDuration = null, - Object? avgResponseTime = null, - Object? successCount = null, - Object? failureCount = null, - }) { - return _then(_value.copyWith( - results: null == results - ? _value.results - : results // ignore: cast_nullable_to_non_nullable - as List, - totalDuration: null == totalDuration - ? _value.totalDuration - : totalDuration // ignore: cast_nullable_to_non_nullable - as Duration, - avgResponseTime: null == avgResponseTime - ? _value.avgResponseTime - : avgResponseTime // ignore: cast_nullable_to_non_nullable - as double, - successCount: null == successCount - ? _value.successCount - : successCount // ignore: cast_nullable_to_non_nullable - as int, - failureCount: null == failureCount - ? _value.failureCount - : failureCount // ignore: cast_nullable_to_non_nullable - as int, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$StressTestSummaryImplCopyWith<$Res> - implements $StressTestSummaryCopyWith<$Res> { - factory _$$StressTestSummaryImplCopyWith(_$StressTestSummaryImpl value, - $Res Function(_$StressTestSummaryImpl) then) = - __$$StressTestSummaryImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {List results, - Duration totalDuration, - double avgResponseTime, - int successCount, - int failureCount}); -} - -/// @nodoc -class __$$StressTestSummaryImplCopyWithImpl<$Res> - extends _$StressTestSummaryCopyWithImpl<$Res, _$StressTestSummaryImpl> - implements _$$StressTestSummaryImplCopyWith<$Res> { - __$$StressTestSummaryImplCopyWithImpl(_$StressTestSummaryImpl _value, - $Res Function(_$StressTestSummaryImpl) _then) - : super(_value, _then); - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? results = null, - Object? totalDuration = null, - Object? avgResponseTime = null, - Object? successCount = null, - Object? failureCount = null, - }) { - return _then(_$StressTestSummaryImpl( - results: null == results - ? _value._results - : results // ignore: cast_nullable_to_non_nullable - as List, - totalDuration: null == totalDuration - ? _value.totalDuration - : totalDuration // ignore: cast_nullable_to_non_nullable - as Duration, - avgResponseTime: null == avgResponseTime - ? _value.avgResponseTime - : avgResponseTime // ignore: cast_nullable_to_non_nullable - as double, - successCount: null == successCount - ? _value.successCount - : successCount // ignore: cast_nullable_to_non_nullable - as int, - failureCount: null == failureCount - ? _value.failureCount - : failureCount // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$StressTestSummaryImpl implements _StressTestSummary { - const _$StressTestSummaryImpl( - {required final List results, - required this.totalDuration, - required this.avgResponseTime, - required this.successCount, - required this.failureCount}) - : _results = results; - - factory _$StressTestSummaryImpl.fromJson(Map json) => - _$$StressTestSummaryImplFromJson(json); - - final List _results; - @override - List get results { - if (_results is EqualUnmodifiableListView) return _results; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_results); - } - - @override - final Duration totalDuration; - @override - final double avgResponseTime; - @override - final int successCount; - @override - final int failureCount; - - @override - String toString() { - return 'StressTestSummary(results: $results, totalDuration: $totalDuration, avgResponseTime: $avgResponseTime, successCount: $successCount, failureCount: $failureCount)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$StressTestSummaryImpl && - const DeepCollectionEquality().equals(other._results, _results) && - (identical(other.totalDuration, totalDuration) || - other.totalDuration == totalDuration) && - (identical(other.avgResponseTime, avgResponseTime) || - other.avgResponseTime == avgResponseTime) && - (identical(other.successCount, successCount) || - other.successCount == successCount) && - (identical(other.failureCount, failureCount) || - other.failureCount == failureCount)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_results), - totalDuration, - avgResponseTime, - successCount, - failureCount); - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => - __$$StressTestSummaryImplCopyWithImpl<_$StressTestSummaryImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$StressTestSummaryImplToJson( - this, - ); - } -} - -abstract class _StressTestSummary implements StressTestSummary { - const factory _StressTestSummary( - {required final List results, - required final Duration totalDuration, - required final double avgResponseTime, - required final int successCount, - required final int failureCount}) = _$StressTestSummaryImpl; - - factory _StressTestSummary.fromJson(Map json) = - _$StressTestSummaryImpl.fromJson; - - @override - List get results; - @override - Duration get totalDuration; - @override - double get avgResponseTime; - @override - int get successCount; - @override - int get failureCount; - - /// Create a copy of StressTestSummary - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$StressTestSummaryImplCopyWith<_$StressTestSummaryImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/models/stress_test/stress_test_summary.g.dart b/lib/models/stress_test/stress_test_summary.g.dart deleted file mode 100644 index 5bf4d5b48..000000000 --- a/lib/models/stress_test/stress_test_summary.g.dart +++ /dev/null @@ -1,30 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'stress_test_summary.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$StressTestSummaryImpl _$$StressTestSummaryImplFromJson( - Map json) => - _$StressTestSummaryImpl( - results: (json['results'] as List) - .map((e) => ApiRequestResult.fromJson(e as Map)) - .toList(), - totalDuration: - Duration(microseconds: (json['totalDuration'] as num).toInt()), - avgResponseTime: (json['avgResponseTime'] as num).toDouble(), - successCount: (json['successCount'] as num).toInt(), - failureCount: (json['failureCount'] as num).toInt(), - ); - -Map _$$StressTestSummaryImplToJson( - _$StressTestSummaryImpl instance) => - { - 'results': instance.results, - 'totalDuration': instance.totalDuration.inMicroseconds, - 'avgResponseTime': instance.avgResponseTime, - 'successCount': instance.successCount, - 'failureCount': instance.failureCount, - }; From 60551c272f3cafa181d8634e4dc3784da26a46ad Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Tue, 8 Apr 2025 22:39:43 +0530 Subject: [PATCH 106/188] chore: create and design models workflow model --- .../lib/src/models/core_models.dart | 16 ++++ .../lib/src/models/models.dart | 6 ++ .../lib/src/models/node_status.dart | 8 ++ .../lib/src/models/request_model.dart | 3 + .../src/models/workflow_connection_model.dart | 40 ++++++++ .../lib/src/models/workflow_model.dart | 31 +++++++ .../lib/src/models/workflow_node_model.dart | 93 +++++++++++++++++++ 7 files changed, 197 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/models/core_models.dart create mode 100644 packages/api_testing_suite/lib/src/models/models.dart create mode 100644 packages/api_testing_suite/lib/src/models/node_status.dart create mode 100644 packages/api_testing_suite/lib/src/models/request_model.dart create mode 100644 packages/api_testing_suite/lib/src/models/workflow_connection_model.dart create mode 100644 packages/api_testing_suite/lib/src/models/workflow_model.dart create mode 100644 packages/api_testing_suite/lib/src/models/workflow_node_model.dart diff --git a/packages/api_testing_suite/lib/src/models/core_models.dart b/packages/api_testing_suite/lib/src/models/core_models.dart new file mode 100644 index 000000000..e1e76db5a --- /dev/null +++ b/packages/api_testing_suite/lib/src/models/core_models.dart @@ -0,0 +1,16 @@ +/// Temporary placeholder for apidash_core models + +/// Basic representation of an API request model +class RequestModel { + final String id; + final String name; + final String method; + final String url; + + const RequestModel({ + required this.id, + this.name = '', + this.method = 'GET', + this.url = '', + }); +} diff --git a/packages/api_testing_suite/lib/src/models/models.dart b/packages/api_testing_suite/lib/src/models/models.dart new file mode 100644 index 000000000..7b334cfcb --- /dev/null +++ b/packages/api_testing_suite/lib/src/models/models.dart @@ -0,0 +1,6 @@ +// Export all models for easy importing +export 'node_status.dart'; +export 'request_model.dart'; +export 'workflow_connection_model.dart'; +export 'workflow_model.dart'; +export 'workflow_node_model.dart'; diff --git a/packages/api_testing_suite/lib/src/models/node_status.dart b/packages/api_testing_suite/lib/src/models/node_status.dart new file mode 100644 index 000000000..cd6f1e6bc --- /dev/null +++ b/packages/api_testing_suite/lib/src/models/node_status.dart @@ -0,0 +1,8 @@ +/// Enum representing the different states a workflow node can be in +enum NodeStatus { + pending, + running, + success, + failure, + inactive, +} diff --git a/packages/api_testing_suite/lib/src/models/request_model.dart b/packages/api_testing_suite/lib/src/models/request_model.dart new file mode 100644 index 000000000..03efd890c --- /dev/null +++ b/packages/api_testing_suite/lib/src/models/request_model.dart @@ -0,0 +1,3 @@ +/// Re-export of the RequestModel from core_models +/// This provides a placeholder until we integrate with the actual apidash_core +export 'core_models.dart' show RequestModel; diff --git a/packages/api_testing_suite/lib/src/models/workflow_connection_model.dart b/packages/api_testing_suite/lib/src/models/workflow_connection_model.dart new file mode 100644 index 000000000..4fbc0391e --- /dev/null +++ b/packages/api_testing_suite/lib/src/models/workflow_connection_model.dart @@ -0,0 +1,40 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:uuid/uuid.dart'; + +part 'workflow_connection_model.freezed.dart'; +part 'workflow_connection_model.g.dart'; + +/// Model representing a connection between nodes in a workflow +@freezed +class WorkflowConnectionModel with _$WorkflowConnectionModel { + const WorkflowConnectionModel._(); + + const factory WorkflowConnectionModel({ + required String id, + required String sourceId, + required String targetId, + @Default('') String label, + @Default(false) bool isConditional, + @Default('') String condition, + }) = _WorkflowConnectionModel; + + factory WorkflowConnectionModel.create({ + required String sourceId, + required String targetId, + String label = '', + bool isConditional = false, + String condition = '', + }) { + return WorkflowConnectionModel( + id: const Uuid().v4(), + sourceId: sourceId, + targetId: targetId, + label: label, + isConditional: isConditional, + condition: condition, + ); + } + + factory WorkflowConnectionModel.fromJson(Map json) => + _$WorkflowConnectionModelFromJson(json); +} diff --git a/packages/api_testing_suite/lib/src/models/workflow_model.dart b/packages/api_testing_suite/lib/src/models/workflow_model.dart new file mode 100644 index 000000000..0062c1ab9 --- /dev/null +++ b/packages/api_testing_suite/lib/src/models/workflow_model.dart @@ -0,0 +1,31 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:uuid/uuid.dart'; + +import 'workflow_node_model.dart'; +import 'workflow_connection_model.dart'; + +part 'workflow_model.freezed.dart'; +part 'workflow_model.g.dart'; + +/// Model representing an entire workflow +@freezed +class WorkflowModel with _$WorkflowModel { + const WorkflowModel._(); + + const factory WorkflowModel({ + required String id, + @Default('New Workflow') String name, + @Default('') String description, + @Default([]) List nodes, + @Default([]) List connections, + }) = _WorkflowModel; + + factory WorkflowModel.create() { + return WorkflowModel( + id: const Uuid().v4(), + ); + } + + factory WorkflowModel.fromJson(Map json) => + _$WorkflowModelFromJson(json); +} diff --git a/packages/api_testing_suite/lib/src/models/workflow_node_model.dart b/packages/api_testing_suite/lib/src/models/workflow_node_model.dart new file mode 100644 index 000000000..e54cd960f --- /dev/null +++ b/packages/api_testing_suite/lib/src/models/workflow_node_model.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:uuid/uuid.dart'; + +import 'core_models.dart'; +import 'node_status.dart'; + +part 'workflow_node_model.freezed.dart'; +part 'workflow_node_model.g.dart'; + +/// Custom JSON converter for Flutter's Offset class +class OffsetConverter implements JsonConverter> { + const OffsetConverter(); + + @override + Offset fromJson(Map json) { + return Offset( + (json['dx'] as num).toDouble(), + (json['dy'] as num).toDouble(), + ); + } + + @override + Map toJson(Offset offset) { + return { + 'dx': offset.dx, + 'dy': offset.dy, + }; + } +} + +/// Custom JSON converter for RequestModel +class RequestModelConverter implements JsonConverter?> { + const RequestModelConverter(); + + @override + RequestModel? fromJson(Map? json) { + if (json == null) return null; + return RequestModel( + id: json['id'] as String, + name: json['name'] as String? ?? '', + method: json['method'] as String? ?? 'GET', + url: json['url'] as String? ?? '', + ); + } + + @override + Map? toJson(RequestModel? requestModel) { + if (requestModel == null) return null; + return { + 'id': requestModel.id, + 'name': requestModel.name, + 'method': requestModel.method, + 'url': requestModel.url, + }; + } +} + +/// Model representing a node in a workflow +@freezed +class WorkflowNodeModel with _$WorkflowNodeModel { + const WorkflowNodeModel._(); + + const factory WorkflowNodeModel({ + required String id, + required String requestId, + @OffsetConverter() required Offset position, + @Default('') String label, + @Default(NodeStatus.inactive) NodeStatus status, + @Default([]) List connectedToIds, + @RequestModelConverter() RequestModel? requestModel, + @Default({}) Map simulatedResponse, + @Default(200) int simulatedStatusCode, + }) = _WorkflowNodeModel; + + factory WorkflowNodeModel.create({ + required String requestId, + @OffsetConverter() required Offset position, + required String label, + RequestModel? requestModel, + }) { + return WorkflowNodeModel( + id: const Uuid().v4(), + requestId: requestId, + position: position, + label: label, + requestModel: requestModel, + ); + } + + factory WorkflowNodeModel.fromJson(Map json) => + _$WorkflowNodeModelFromJson(json); +} From d985ad7ff993b086c6203d43be8244b2266144ff Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Tue, 8 Apr 2025 23:08:14 +0530 Subject: [PATCH 107/188] chore: implement notifier and provider for workflow builder --- .../lib/src/providers/workflow_providers.dart | 199 ++++++++++++++++++ .../lib/src/providers/workflows_notifier.dart | 174 +++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/providers/workflow_providers.dart create mode 100644 packages/api_testing_suite/lib/src/providers/workflows_notifier.dart diff --git a/packages/api_testing_suite/lib/src/providers/workflow_providers.dart b/packages/api_testing_suite/lib/src/providers/workflow_providers.dart new file mode 100644 index 000000000..80fff0b2c --- /dev/null +++ b/packages/api_testing_suite/lib/src/providers/workflow_providers.dart @@ -0,0 +1,199 @@ +import 'package:api_testing_suite/src/models/models.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../models/workflow_model.dart'; + +/// Provider for workflows list +import 'workflows_notifier.dart'; + +/// Provider for workflows list +final workflowsProvider = StateNotifierProvider>((ref) { + return WorkflowsNotifier(ref); +}); + +/// Provider for current workflow ID +final currentWorkflowProvider = StateProvider((ref) => null); + +/// Provider for active workflow +final activeWorkflowProvider = Provider((ref) { + final currentWorkflowId = ref.watch(currentWorkflowProvider); + final workflows = ref.watch(workflowsProvider); + + if (currentWorkflowId == null) return null; + + return workflows.firstWhere( + (workflow) => workflow.id == currentWorkflowId, + orElse: () => WorkflowModel.create(), + ); +}); + +/// Provider for workflow execution state +final workflowExecutionStateProvider = StateNotifierProvider((ref) { + return WorkflowExecutionNotifier(ref); +}); + +/// Workflow execution status enum +enum WorkflowExecutionStatus { + idle, + running, + paused, + completed, +} + +/// Workflow execution state class +class WorkflowExecutionState { + final WorkflowExecutionStatus status; + final String? currentNodeId; + final List executedNodeIds; + final Map executionContext; + + WorkflowExecutionState({ + this.status = WorkflowExecutionStatus.idle, + this.currentNodeId, + this.executedNodeIds = const [], + this.executionContext = const {}, + }); + + WorkflowExecutionState copyWith({ + WorkflowExecutionStatus? status, + String? currentNodeId, + List? executedNodeIds, + Map? executionContext, + }) { + return WorkflowExecutionState( + status: status ?? this.status, + currentNodeId: currentNodeId ?? this.currentNodeId, + executedNodeIds: executedNodeIds ?? this.executedNodeIds, + executionContext: executionContext ?? this.executionContext, + ); + } +} + +/// Workflow execution notifier class +class WorkflowExecutionNotifier extends StateNotifier { + final Ref _ref; + + WorkflowExecutionNotifier(this._ref) : super(WorkflowExecutionState()); + + Future start() async { + if (state.status == WorkflowExecutionStatus.running) return; + + final workflow = _ref.read(activeWorkflowProvider); + if (workflow == null || workflow.nodes.isEmpty) return; + + // Start with the first node + final firstNode = workflow.nodes.first; + + state = WorkflowExecutionState( + status: WorkflowExecutionStatus.running, + currentNodeId: firstNode.id, + executedNodeIds: [], + executionContext: {}, + ); + + // Execute the first node + await _executeNode(firstNode.id); + } + + Future pause() async { + if (state.status != WorkflowExecutionStatus.running) return; + + state = state.copyWith(status: WorkflowExecutionStatus.paused); + } + + Future resume() async { + if (state.status != WorkflowExecutionStatus.paused) return; + + state = state.copyWith(status: WorkflowExecutionStatus.running); + + if (state.currentNodeId != null) { + await _executeNode(state.currentNodeId!); + } + } + + Future stop() async { + state = WorkflowExecutionState(); + + // Reset all node statuses + final workflow = _ref.read(activeWorkflowProvider); + if (workflow != null) { + _ref.read(workflowsProvider.notifier).updateNodes( + workflow.id, + workflow.nodes.map((node) => node.copyWith(status: NodeStatus.inactive)).toList(), + ); + } + } + + Future _executeNode(String nodeId) async { + if (state.status != WorkflowExecutionStatus.running) return; + + final workflow = _ref.read(activeWorkflowProvider); + if (workflow == null) return; + + final nodeIndex = workflow.nodes.indexWhere((node) => node.id == nodeId); + if (nodeIndex == -1) return; + + final node = workflow.nodes[nodeIndex]; + + // Update node status to running + _ref.read(workflowsProvider.notifier).updateNode( + workflow.id, + node.copyWith(status: NodeStatus.running), + ); + + // Simulate API execution + await Future.delayed(const Duration(seconds: 1)); + + // Determine success or failure + final isSuccess = DateTime.now().millisecondsSinceEpoch % 5 != 0; // 80% success rate + + // Update node status based on result + _ref.read(workflowsProvider.notifier).updateNode( + workflow.id, + node.copyWith(status: isSuccess ? NodeStatus.success : NodeStatus.failure), + ); + + // Add this node to executed nodes + final updatedExecutedNodeIds = [...state.executedNodeIds, nodeId]; + state = state.copyWith( + executedNodeIds: updatedExecutedNodeIds, + ); + + // If failed and is conditional, stop execution + if (!isSuccess && node.connectedToIds.isNotEmpty) { + final connections = workflow.connections.where( + (conn) => conn.sourceId == nodeId && conn.isConditional + ).toList(); + + if (connections.isNotEmpty) { + state = state.copyWith(status: WorkflowExecutionStatus.completed); + return; + } + } + + // Get next nodes + final nextNodeIds = node.connectedToIds.isEmpty + ? [] + : workflow.connections + .where((conn) => conn.sourceId == nodeId) + .map((conn) => conn.targetId) + .toList(); + + // If no next nodes, workflow is completed + if (nextNodeIds.isEmpty) { + state = state.copyWith(status: WorkflowExecutionStatus.completed); + return; + } + + // Execute next nodes + for (final nextNodeId in nextNodeIds) { + state = state.copyWith(currentNodeId: nextNodeId); + await _executeNode(nextNodeId); + + // If execution was paused or stopped, break the loop + if (state.status != WorkflowExecutionStatus.running) { + break; + } + } + } +} diff --git a/packages/api_testing_suite/lib/src/providers/workflows_notifier.dart b/packages/api_testing_suite/lib/src/providers/workflows_notifier.dart new file mode 100644 index 000000000..c38efaf68 --- /dev/null +++ b/packages/api_testing_suite/lib/src/providers/workflows_notifier.dart @@ -0,0 +1,174 @@ +import 'package:api_testing_suite/src/models/models.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../models/request_model.dart'; + +import '../models/workflow_model.dart'; + +/// Notifier class for workflows state management +class WorkflowsNotifier extends StateNotifier> { + final Ref _ref; + + WorkflowsNotifier(this._ref) : super([]); + + /// Add a new workflow + void add() { + final newWorkflow = WorkflowModel.create(); + state = [...state, newWorkflow]; + // We need to update the state provider directly through the ref + _ref.read(StateProvider((ref) => null).notifier).state = newWorkflow.id; + } + + /// Update an existing workflow + void update(WorkflowModel workflow) { + state = [ + for (final existingWorkflow in state) + if (existingWorkflow.id == workflow.id) workflow else existingWorkflow, + ]; + } + + /// Add a node to a workflow + void addNode(String workflowId, WorkflowNodeModel node) { + state = [ + for (final workflow in state) + if (workflow.id == workflowId) + workflow.copyWith(nodes: [...workflow.nodes, node]) + else + workflow, + ]; + } + + /// Update a node in a workflow + void updateNode(String workflowId, WorkflowNodeModel updatedNode) { + state = [ + for (final workflow in state) + if (workflow.id == workflowId) + workflow.copyWith( + nodes: [ + for (final node in workflow.nodes) + if (node.id == updatedNode.id) updatedNode else node, + ], + ) + else + workflow, + ]; + } + + /// Update multiple nodes in a workflow + void updateNodes(String workflowId, List updatedNodes) { + state = [ + for (final workflow in state) + if (workflow.id == workflowId) + workflow.copyWith(nodes: updatedNodes) + else + workflow, + ]; + } + + /// Update a node's position in a workflow + void updateNodePosition(String workflowId, String nodeId, Offset position) { + state = [ + for (final workflow in state) + if (workflow.id == workflowId) + workflow.copyWith( + nodes: [ + for (final node in workflow.nodes) + if (node.id == nodeId) + node.copyWith(position: position) + else + node, + ], + ) + else + workflow, + ]; + } + + /// Remove a node from a workflow + void removeNode(String workflowId, String nodeId) { + state = [ + for (final workflow in state) + if (workflow.id == workflowId) + workflow.copyWith( + nodes: workflow.nodes.where((node) => node.id != nodeId).toList(), + connections: workflow.connections.where( + (conn) => conn.sourceId != nodeId && conn.targetId != nodeId + ).toList(), + ) + else + workflow, + ]; + } + + /// Add a connection between nodes in a workflow + void addConnection(String workflowId, WorkflowConnectionModel connection) { + state = [ + for (final workflow in state) + if (workflow.id == workflowId) + workflow.copyWith(connections: [...workflow.connections, connection]) + else + workflow, + ]; + } + + /// Update a connection in a workflow + void updateConnection(String workflowId, WorkflowConnectionModel updatedConnection) { + state = [ + for (final workflow in state) + if (workflow.id == workflowId) + workflow.copyWith( + connections: [ + for (final connection in workflow.connections) + if (connection.id == updatedConnection.id) updatedConnection else connection, + ], + ) + else + workflow, + ]; + } + + /// Remove a connection from a workflow + void removeConnection(String workflowId, String connectionId) { + state = [ + for (final workflow in state) + if (workflow.id == workflowId) + workflow.copyWith( + connections: workflow.connections.where((conn) => conn.id != connectionId).toList(), + ) + else + workflow, + ]; + } + + /// Delete a workflow + void delete(String workflowId) { + state = state.where((workflow) => workflow.id != workflowId).toList(); + if (_ref.read(StateProvider((ref) => null)) == workflowId) { + _ref.read(StateProvider((ref) => null).notifier).state = state.isNotEmpty ? state.first.id : null; + } + } + + /// Import a request as a workflow node + void importRequestAsNode(String workflowId, String requestId, Offset position, Map collection) { + try { + // Get the request from the collection + final request = collection[requestId]; + if (request == null) { + debugPrint('Request not found with ID: $requestId'); + return; + } + + final node = WorkflowNodeModel.create( + requestId: requestId, + position: position, + label: request.name.isNotEmpty ? request.name : 'Request ${requestId.substring(0, 4)}', + requestModel: request, + ); + + addNode(workflowId, node); + } catch (e) { + debugPrint('Error importing node: $e'); + } + } +} From 83599d25d43143b304bc1343e5b08722cf2fdefd Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Tue, 8 Apr 2025 23:09:18 +0530 Subject: [PATCH 108/188] chore: export providers --- packages/api_testing_suite/lib/src/providers/providers.dart | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/api_testing_suite/lib/src/providers/providers.dart diff --git a/packages/api_testing_suite/lib/src/providers/providers.dart b/packages/api_testing_suite/lib/src/providers/providers.dart new file mode 100644 index 000000000..9202c2f21 --- /dev/null +++ b/packages/api_testing_suite/lib/src/providers/providers.dart @@ -0,0 +1 @@ +export 'workflow_providers.dart'; From 01042242ee648719c254d34467880c325118300d Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Tue, 8 Apr 2025 23:13:44 +0530 Subject: [PATCH 109/188] chore: export api_testing_suite package implementation --- .../api_testing_suite/lib/api_testing_suite.dart | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 packages/api_testing_suite/lib/api_testing_suite.dart diff --git a/packages/api_testing_suite/lib/api_testing_suite.dart b/packages/api_testing_suite/lib/api_testing_suite.dart new file mode 100644 index 000000000..d366e2ba2 --- /dev/null +++ b/packages/api_testing_suite/lib/api_testing_suite.dart @@ -0,0 +1,13 @@ +library api_testing_suite; + +// Models +export 'src/models/workflow_model.dart'; + +// Providers +export 'src/providers/workflow_providers.dart'; + +// Widgets +export 'src/widgets/workflow_builder/workflow_canvas.dart'; +export 'src/widgets/workflow_builder/workflow_connection.dart'; +export 'src/widgets/workflow_builder/workflow_node.dart'; +export 'src/widgets/workflow_builder/workflow_screens.dart'; From e6d3a6369c5b5a8d9bad6e207167bc5aec3a4434 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Tue, 8 Apr 2025 23:20:27 +0530 Subject: [PATCH 110/188] chore: create widgets for workflow builder (canvas, node, connection, worklow pane) --- .../workflow_builder_page.dart | 151 +++++ .../workflow_builder/workflow_canvas.dart | 553 ++++++++++++++++++ .../workflow_builder/workflow_connection.dart | 165 ++++++ .../workflow_builder/workflow_node.dart | 275 +++++++++ 4 files changed, 1144 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_builder_page.dart create mode 100644 packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_canvas.dart create mode 100644 packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_connection.dart create mode 100644 packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_node.dart diff --git a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_builder_page.dart b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_builder_page.dart new file mode 100644 index 000000000..49550e1cf --- /dev/null +++ b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_builder_page.dart @@ -0,0 +1,151 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../providers/workflow_providers.dart'; +import 'workflow_canvas.dart'; + +/// The main page for building and editing workflows +class WorkflowBuilderPage extends ConsumerWidget { + const WorkflowBuilderPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final workflowId = ref.watch(currentWorkflowProvider); + final execState = ref.watch(workflowExecutionStateProvider); + + if (workflowId == null) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('No workflow selected'), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () { + ref.read(workflowsProvider.notifier).add(); + }, + child: const Text('Create New Workflow'), + ), + ], + ), + ); + } + + return Scaffold( + body: Column( + children: [ + // Toolbar + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], + ), + child: Row( + children: [ + Expanded( + child: Text( + 'Workflow Builder', + style: Theme.of(context).textTheme.titleLarge, + ), + ), + // Execution controls + if (execState.status == WorkflowExecutionStatus.idle) ...[ + IconButton( + icon: const Icon(Icons.play_arrow, color: Colors.green), + tooltip: 'Start Workflow', + onPressed: () { + ref.read(workflowExecutionStateProvider.notifier).start(); + }, + ), + ] else if (execState.status == WorkflowExecutionStatus.running) ...[ + IconButton( + icon: const Icon(Icons.pause, color: Colors.orange), + tooltip: 'Pause Workflow', + onPressed: () { + ref.read(workflowExecutionStateProvider.notifier).pause(); + }, + ), + IconButton( + icon: const Icon(Icons.stop, color: Colors.red), + tooltip: 'Stop Workflow', + onPressed: () { + ref.read(workflowExecutionStateProvider.notifier).stop(); + }, + ), + ] else if (execState.status == WorkflowExecutionStatus.paused) ...[ + IconButton( + icon: const Icon(Icons.play_arrow, color: Colors.green), + tooltip: 'Resume Workflow', + onPressed: () { + ref.read(workflowExecutionStateProvider.notifier).resume(); + }, + ), + IconButton( + icon: const Icon(Icons.stop, color: Colors.red), + tooltip: 'Stop Workflow', + onPressed: () { + ref.read(workflowExecutionStateProvider.notifier).stop(); + }, + ), + ], + + IconButton( + icon: const Icon(Icons.add), + tooltip: 'Add Node', + onPressed: () { + _showAddNodeDialog(context, ref, workflowId); + }, + ), + IconButton( + icon: const Icon(Icons.save), + tooltip: 'Save Workflow', + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Workflow saved')), + ); + }, + ), + ], + ), + ), + + // Main canvas area + Expanded( + child: WorkflowCanvas(workflowId: workflowId), + ), + ], + ), + ); + } + + void _showAddNodeDialog(BuildContext context, WidgetRef ref, String workflowId) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Add Node'), + content: const Text('Select an API request to add as a node'), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + // TODO: @abhinavs1920 Add a node based on a selected request + Navigator.of(context).pop(); + }, + child: const Text('Add'), + ), + ], + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_canvas.dart b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_canvas.dart new file mode 100644 index 000000000..bb7475a15 --- /dev/null +++ b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_canvas.dart @@ -0,0 +1,553 @@ +import 'package:api_testing_suite/src/models/models.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../models/workflow_model.dart'; +import '../../providers/workflow_providers.dart'; +import 'workflow_node.dart'; +import 'workflow_connection.dart'; + +/// A widget that displays the workflow canvas where nodes can be placed and connected +class WorkflowCanvas extends ConsumerStatefulWidget { + const WorkflowCanvas({ + Key? key, + required this.workflowId, + }) : super(key: key); + + final String workflowId; + + @override + ConsumerState createState() => _WorkflowCanvasState(); +} + +class _WorkflowCanvasState extends ConsumerState { + // For drag and drop functionality + Offset _panPosition = Offset.zero; + double _scale = 1.0; + Offset? _startConnectingPosition; + String? _startConnectingNodeId; + String? _highlightNodeId; // Target node being highlighted + String? _highlightSourceId; // Source node being highlighted + Offset? _currentConnectingPosition; + bool _isDraggingCanvas = false; + bool _isConnectionModeActive = false; // New variable to track if we're in connection mode + + @override + Widget build(BuildContext context) { + final workflow = ref.watch(activeWorkflowProvider); + if (workflow == null) { + return const Center(child: Text('No workflow selected')); + } + + if (_isConnectionModeActive) { + return RawKeyboardListener( + focusNode: FocusNode(), + autofocus: true, + onKey: (event) { + if (event.logicalKey == LogicalKeyboardKey.escape) { + _resetConnectionMode(); + } + }, + child: _buildMainCanvas(workflow), + ); + } + + return _buildMainCanvas(workflow); + } + + Widget _buildMainCanvas(WorkflowModel workflow) { + return GestureDetector( + onTap: () { + if (_isConnectionModeActive) { + _resetConnectionMode(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Connection cancelled'), + duration: Duration(seconds: 1), + backgroundColor: Colors.grey, + ), + ); + } + }, + onSecondaryTapDown: (_) { + if (_isConnectionModeActive) { + _resetConnectionMode(); + } else { + setState(() { + _isDraggingCanvas = true; + }); + } + }, + onSecondaryTapUp: (_) { + setState(() { + _isDraggingCanvas = false; + }); + }, + onScaleStart: (details) { + setState(() { + _isDraggingCanvas = true; + }); + }, + onScaleUpdate: (details) { + if (!_isDraggingCanvas && _startConnectingNodeId != null) { + return; + } + setState(() { + if (details.scale == 1.0) { + _panPosition += details.focalPointDelta; + } else { + _scale = (_scale * details.scale).clamp(0.5, 2.0); + } + }); + }, + onScaleEnd: (_) { + setState(() { + _isDraggingCanvas = false; + }); + }, + child: Container( + color: Theme.of(context).scaffoldBackgroundColor, + child: Stack( + children: [ + CustomPaint( + painter: GridPainter(offset: _panPosition, scale: _scale), + size: MediaQuery.of(context).size, + ), + + if (_startConnectingPosition != null && _currentConnectingPosition != null) + CustomPaint( + painter: ConnectionLinePainter( + start: _startConnectingPosition!, + end: _currentConnectingPosition!, + isPreview: true, + ), + size: MediaQuery.of(context).size, + ), + + ...workflow.connections.map((connection) { + final sourceNodeIndex = workflow.nodes.indexWhere((node) => node.id == connection.sourceId); + final targetNodeIndex = workflow.nodes.indexWhere((node) => node.id == connection.targetId); + + if (sourceNodeIndex == -1 || targetNodeIndex == -1) { + return const SizedBox.shrink(); // Connection refers to nodes that don't exist + } + + final sourceNode = workflow.nodes[sourceNodeIndex]; + final targetNode = workflow.nodes[targetNodeIndex]; + + final scaledSourcePosition = Offset( + sourceNode.position.dx * _scale + _panPosition.dx + 120 * _scale, // Right edge of source node + sourceNode.position.dy * _scale + _panPosition.dy + 40 * _scale, // Middle of source node + ); + + final scaledTargetPosition = Offset( + targetNode.position.dx * _scale + _panPosition.dx, // Left edge of target node + targetNode.position.dy * _scale + _panPosition.dy + 40 * _scale, // Middle of target node + ); + + return Positioned.fill( + child: WorkflowConnection( + connection: connection, + sourcePosition: scaledSourcePosition, + targetPosition: scaledTargetPosition, + isConditional: connection.isConditional, + scale: _scale, + onTap: () => _showConnectionDetails(connection), + onRemove: () => _removeConnection(connection.id), + ), + ); + }).toList(), + + // Workflow nodes + ...workflow.nodes.map((node) { + final isHighlighted = _highlightNodeId == node.id || _highlightSourceId == node.id; + final canBeTarget = _startConnectingNodeId != null && + _startConnectingNodeId != node.id && + !workflow.connections.any((conn) => + conn.sourceId == _startConnectingNodeId && conn.targetId == node.id); + + final position = Offset( + node.position.dx * _scale + _panPosition.dx, + node.position.dy * _scale + _panPosition.dy + ); + + return Positioned( + left: position.dx, + top: position.dy, + child: isHighlighted + ? Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.blue.withOpacity(0.6), + blurRadius: 10, + spreadRadius: 5, + ), + ], + ), + child: WorkflowNode( + node: node, + scale: _scale, + onDragUpdate: (delta) => _moveNode(node.id, delta), + onConnect: (position) => _startConnecting(node.id, position), + onConnectMove: _updateConnecting, + onConnectEnd: _finishConnecting, + onTap: () => _showNodeDetails(node), + onRemove: () => _removeNode(node.id), + isConnectionModeActive: _isConnectionModeActive && canBeTarget, + onConnectionTarget: canBeTarget ? () => _connectNodes(_startConnectingNodeId!, node.id) : null, + ) + ) : WorkflowNode( + node: node, + scale: _scale, + onDragUpdate: (delta) => _moveNode(node.id, delta), + onConnect: (position) => _startConnecting(node.id, position), + onConnectMove: _updateConnecting, + onConnectEnd: _finishConnecting, + onTap: () => _showNodeDetails(node), + onRemove: () => _removeNode(node.id), + isConnectionModeActive: _isConnectionModeActive && canBeTarget, + onConnectionTarget: canBeTarget ? () => _connectNodes(_startConnectingNodeId!, node.id) : null, + ), + ); + }).toList(), + ], + ), + ), + ); + } + + void _moveNode(String nodeId, Offset delta) { + if (_isConnectionModeActive) return; + + ref.read(workflowsProvider.notifier).updateNodePosition( + widget.workflowId, + nodeId, + Offset( + (ref.read(activeWorkflowProvider)!.nodes + .firstWhere((node) => node.id == nodeId).position.dx + delta.dx), + (ref.read(activeWorkflowProvider)!.nodes + .firstWhere((node) => node.id == nodeId).position.dy + delta.dy), + ), + ); + } + + void _startConnecting(String nodeId, Offset position) { + setState(() { + _startConnectingNodeId = nodeId; + _startConnectingPosition = position; + _currentConnectingPosition = position; + _isConnectionModeActive = true; + + final workflow = ref.read(activeWorkflowProvider); + if (workflow != null) { + final existingConnections = workflow.connections.where( + (conn) => conn.sourceId == nodeId + ).toList(); + + _highlightSourceId = nodeId; + + if (existingConnections.isNotEmpty) { + _highlightNodeId = existingConnections.first.targetId; + } + } + }); + } + + void _updateConnecting(Offset position) { + setState(() { + _currentConnectingPosition = position; + }); + } + + void _resetConnectionMode() { + setState(() { + _startConnectingNodeId = null; + _startConnectingPosition = null; + _currentConnectingPosition = null; + _highlightNodeId = null; + _highlightSourceId = null; + _isConnectionModeActive = false; + }); + } + + void _connectNodes(String sourceId, String targetId) { + final workflow = ref.read(activeWorkflowProvider); + if (workflow == null) return; + + final existingConnection = workflow.connections.any( + (conn) => conn.sourceId == sourceId && conn.targetId == targetId + ); + + if (existingConnection) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Connection already exists'), + duration: Duration(seconds: 1), + backgroundColor: Colors.orange, + ), + ); + _resetConnectionMode(); + return; + } + + final connection = WorkflowConnectionModel.create( + sourceId: sourceId, + targetId: targetId, + ); + + ref.read(workflowsProvider.notifier).addConnection( + widget.workflowId, + connection, + ); + + final sourceNode = workflow.nodes.firstWhere((node) => node.id == sourceId); + final updatedConnectedToIds = [...sourceNode.connectedToIds, targetId]; + + ref.read(workflowsProvider.notifier).updateNode( + widget.workflowId, + sourceNode.copyWith(connectedToIds: updatedConnectedToIds), + ); + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Nodes connected successfully'), + duration: Duration(seconds: 1), + backgroundColor: Colors.green, + ), + ); + + _resetConnectionMode(); + } + + void _finishConnecting(Offset position) { + // We'll TODO: @abhinavs1920 Implement hit detection here for connecting to another node + _resetConnectionMode(); + } + + void _showNodeDetails(WorkflowNodeModel node) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Node Details'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('ID: ${node.id}'), + SizedBox(height: 8), + Text('Label:'), + TextFormField( + initialValue: node.label, + onChanged: (value) { + final updatedNode = node.copyWith(label: value); + ref.read(workflowsProvider.notifier).updateNode( + widget.workflowId, + updatedNode, + ); + }, + decoration: InputDecoration( + hintText: 'Enter node label', + border: OutlineInputBorder(), + ), + ), + SizedBox(height: 16), + Text('Status: ${node.status.toString().split('.').last}'), + SizedBox(height: 16), + Text('Connected to: ${node.connectedToIds.join(', ')}'), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text('Close'), + ), + ], + ), + ); + } + + void _showConnectionDetails(WorkflowConnectionModel connection) { + bool isConditional = connection.isConditional; + String condition = connection.condition; + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Connection Details'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('From: ${connection.sourceId}'), + Text('To: ${connection.targetId}'), + SizedBox(height: 16), + CheckboxListTile( + title: Text('Conditional Connection'), + value: isConditional, + onChanged: (value) { + setState(() { + isConditional = value ?? false; + }); + }, + ), + if (isConditional) ...[ + SizedBox(height: 8), + Text('Condition:'), + TextFormField( + initialValue: condition, + onChanged: (value) { + condition = value; + }, + decoration: InputDecoration( + hintText: 'Enter condition expression', + border: OutlineInputBorder(), + helperText: 'Example: response.status == 200', + ), + ), + ], + ], + ), + actions: [ + TextButton.icon( + icon: Icon(Icons.delete, color: Colors.red), + label: Text('Remove', style: TextStyle(color: Colors.red)), + onPressed: () { + _removeConnection(connection.id); + Navigator.of(context).pop(); + }, + ), + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + final updatedConnection = connection.copyWith( + isConditional: isConditional, + condition: condition, + ); + ref.read(workflowsProvider.notifier).updateConnection( + widget.workflowId, + updatedConnection, + ); + Navigator.of(context).pop(); + }, + child: const Text('Save'), + ), + ], + ), + ); + } + + void _removeNode(String nodeId) { + final workflow = ref.read(activeWorkflowProvider); + if (workflow == null) return; + + ref.read(workflowsProvider.notifier).removeNode( + workflow.id, + nodeId, + ); + } + + void _removeConnection(String connectionId) { + final workflow = ref.read(activeWorkflowProvider); + if (workflow == null) return; + + ref.read(workflowsProvider.notifier).removeConnection( + workflow.id, + connectionId, + ); + } +} + +class GridPainter extends CustomPainter { + final Offset offset; + final double scale; + + GridPainter({ + required this.offset, + required this.scale, + }); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = Colors.grey.withOpacity(0.2) + ..strokeWidth = 1; + + const gridSize = 40.0; + + final scaledGridSize = gridSize * scale; + final startX = (offset.dx * scale) % scaledGridSize; + final startY = (offset.dy * scale) % scaledGridSize; + + for (double x = startX; x <= size.width; x += scaledGridSize) { + canvas.drawLine(Offset(x, 0), Offset(x, size.height), paint); + } + + for (double y = startY; y <= size.height; y += scaledGridSize) { + canvas.drawLine(Offset(0, y), Offset(size.width, y), paint); + } + } + + @override + bool shouldRepaint(covariant GridPainter oldDelegate) { + return oldDelegate.offset != offset || oldDelegate.scale != scale; + } +} + +class ConnectionLinePainter extends CustomPainter { + final Offset start; + final Offset end; + final bool isPreview; + + ConnectionLinePainter({ + required this.start, + required this.end, + this.isPreview = false, + }); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = isPreview ? Colors.blue.withOpacity(0.7) : Colors.blue + ..strokeWidth = isPreview ? 3 : 2 + ..style = PaintingStyle.stroke; + + final path = Path() + ..moveTo(start.dx, start.dy) + ..cubicTo( + start.dx + (end.dx - start.dx) * 0.5, + start.dy, + start.dx + (end.dx - start.dx) * 0.5, + end.dy, + end.dx, + end.dy, + ); + + canvas.drawPath(path, paint); + + // Draw arrow at the end + final arrowSize = 10.0; + final arrowPath = Path() + ..moveTo(end.dx, end.dy) + ..lineTo( + end.dx - arrowSize * 1.5 * (end.dx > start.dx ? 1 : -1), + end.dy - arrowSize, + ) + ..lineTo( + end.dx - arrowSize * 1.5 * (end.dx > start.dx ? 1 : -1), + end.dy + arrowSize, + ) + ..close(); + + canvas.drawPath(arrowPath, paint..style = PaintingStyle.fill); + } + + @override + bool shouldRepaint(covariant ConnectionLinePainter oldDelegate) { + return oldDelegate.start != start || oldDelegate.end != end; + } +} diff --git a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_connection.dart b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_connection.dart new file mode 100644 index 000000000..e0d466754 --- /dev/null +++ b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_connection.dart @@ -0,0 +1,165 @@ +import 'dart:math'; +import 'package:api_testing_suite/src/models/models.dart'; +import 'package:flutter/material.dart'; + +/// A widget that displays a connection between two workflow nodes +class WorkflowConnection extends StatefulWidget { + final WorkflowConnectionModel connection; + final Offset sourcePosition; + final Offset targetPosition; + final bool isConditional; + final double scale; + final VoidCallback onTap; + final VoidCallback onRemove; + + const WorkflowConnection({ + Key? key, + required this.connection, + required this.sourcePosition, + required this.targetPosition, + this.isConditional = false, + this.scale = 1.0, + required this.onTap, + required this.onRemove, + }) : super(key: key); + + @override + State createState() => _WorkflowConnectionState(); +} + +class _WorkflowConnectionState extends State { + bool _isHovering = false; + + @override + Widget build(BuildContext context) { + final dx = widget.targetPosition.dx - widget.sourcePosition.dx; + final dy = widget.targetPosition.dy - widget.sourcePosition.dy; + final distance = sqrt(dx * dx + dy * dy); + + final angle = atan2(dy, dx); + + final centerX = widget.sourcePosition.dx + dx / 2; + final centerY = widget.sourcePosition.dy + dy / 2; + + return Stack( + children: [ + Positioned( + left: widget.sourcePosition.dx, + top: widget.sourcePosition.dy, + child: Transform( + transform: Matrix4.identity() + ..translate(0.0, 0.0) + ..rotateZ(angle), + alignment: Alignment.centerLeft, + child: Container( + width: distance, + height: 2 * widget.scale, + decoration: BoxDecoration( + color: widget.isConditional ? Colors.orange : Colors.blue, + borderRadius: BorderRadius.circular(1 * widget.scale), + ), + ), + ), + ), + + Positioned( + left: widget.targetPosition.dx - 10 * widget.scale, + top: widget.targetPosition.dy - 5 * widget.scale, + child: Transform( + transform: Matrix4.identity()..rotateZ(angle), + alignment: Alignment.center, + child: Container( + width: 10 * widget.scale, + height: 10 * widget.scale, + decoration: BoxDecoration( + color: widget.isConditional ? Colors.orange : Colors.blue, + shape: BoxShape.circle, + ), + ), + ), + ), + + if (widget.isConditional) + Positioned( + left: centerX - 8 * widget.scale, + top: centerY - 8 * widget.scale, + child: Transform( + transform: Matrix4.identity()..rotateZ(pi/4), + alignment: Alignment.center, + child: Container( + width: 16 * widget.scale, + height: 16 * widget.scale, + decoration: BoxDecoration( + color: Colors.orange, + border: Border.all( + color: Colors.white, + width: 1 * widget.scale, + ), + ), + ), + ), + ), + + Positioned( + left: widget.sourcePosition.dx, + top: widget.sourcePosition.dy - 15 * widget.scale, + child: MouseRegion( + onEnter: (_) => setState(() => _isHovering = true), + onExit: (_) => setState(() => _isHovering = false), + child: GestureDetector( + onTap: widget.onTap, + child: Transform( + transform: Matrix4.identity()..rotateZ(angle), + alignment: Alignment.centerLeft, + child: Container( + width: distance, + height: 30 * widget.scale, + color: Colors.transparent, + ), + ), + ), + ), + ), + + if (_isHovering) + Positioned( + left: centerX - 32, + top: centerY - 20, + child: Container( + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 4, + spreadRadius: 1, + ), + ], + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.settings, size: 18), + tooltip: 'Edit connection', + onPressed: widget.onTap, + visualDensity: VisualDensity.compact, + constraints: const BoxConstraints.tightFor(width: 28, height: 28), + ), + IconButton( + icon: const Icon(Icons.delete, size: 18, color: Colors.red), + tooltip: 'Remove connection', + onPressed: widget.onRemove, + visualDensity: VisualDensity.compact, + constraints: const BoxConstraints.tightFor(width: 28, height: 28), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_node.dart b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_node.dart new file mode 100644 index 000000000..14f6d43d4 --- /dev/null +++ b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_node.dart @@ -0,0 +1,275 @@ +import 'package:api_testing_suite/src/models/models.dart'; +import 'package:flutter/material.dart'; + +/// A widget that displays a single node in a workflow +class WorkflowNode extends StatefulWidget { + const WorkflowNode({ + Key? key, + required this.node, + this.scale = 1.0, + required this.onDragUpdate, + required this.onConnect, + required this.onConnectMove, + required this.onConnectEnd, + required this.onTap, + required this.onRemove, + this.isConnectionModeActive = false, + this.onConnectionTarget, + }) : super(key: key); + + final WorkflowNodeModel node; + final double scale; + final Function(Offset) onDragUpdate; + final Function(Offset) onConnect; + final Function(Offset) onConnectMove; + final Function(Offset) onConnectEnd; + final VoidCallback onTap; + final VoidCallback onRemove; + final bool isConnectionModeActive; + final VoidCallback? onConnectionTarget; + @override + State createState() => _WorkflowNodeState(); +} + +class _WorkflowNodeState extends State { + bool _isDragging = false; + bool _isHovering = false; + bool _isOutputPortHovering = false; + bool _isInputPortHovering = false; + + @override + Widget build(BuildContext context) { + final nodeWidth = 120.0 * widget.scale; + final nodeHeight = 80.0 * widget.scale; + + Color nodeColor; + Color borderColor; + + switch (widget.node.status) { + case NodeStatus.running: + nodeColor = Colors.blue.withOpacity(0.7); + borderColor = Colors.blue; + break; + case NodeStatus.success: + nodeColor = Colors.green.withOpacity(0.7); + borderColor = Colors.green; + break; + case NodeStatus.failure: + nodeColor = Colors.red.withOpacity(0.7); + borderColor = Colors.red; + break; + case NodeStatus.pending: + nodeColor = Colors.orange.withOpacity(0.7); + borderColor = Colors.orange; + break; + default: + nodeColor = Theme.of(context).cardColor; + borderColor = Theme.of(context).dividerColor; + } + + return MouseRegion( + onEnter: (_) => setState(() => _isHovering = true), + onExit: (_) => setState(() => _isHovering = false), + child: GestureDetector( + onTap: () { + if (widget.isConnectionModeActive && widget.onConnectionTarget != null) { + widget.onConnectionTarget!(); + } else { + widget.onTap(); + } + }, + onPanStart: (details) { + setState(() => _isDragging = true); + }, + onPanUpdate: (details) { + widget.onDragUpdate(details.delta / widget.scale); + }, + onPanEnd: (details) { + setState(() => _isDragging = false); + }, + child: Stack( + children: [ + Positioned( + left: -8 * widget.scale, + top: nodeHeight / 2 - (8 * widget.scale), + child: MouseRegion( + onEnter: (_) => setState(() { _isInputPortHovering = true; }), + onExit: (_) => setState(() { _isInputPortHovering = false; }), + child: Stack( + clipBehavior: Clip.none, + children: [ + Container( + width: 16 * widget.scale, + height: 16 * widget.scale, + decoration: BoxDecoration( + color: _isInputPortHovering ? Colors.green.shade700 : Colors.green, + shape: BoxShape.circle, + border: Border.all( + color: Colors.white, + width: 2 * widget.scale, + ), + boxShadow: _isInputPortHovering || _isHovering ? [ + BoxShadow( + color: Colors.green.withOpacity(0.5), + blurRadius: 4, + spreadRadius: 2, + ) + ] : null, + ), + child: Icon( + Icons.arrow_back, + color: Colors.white, + size: 10 * widget.scale, + ), + ), + ], + ), + ), + ), + + Container( + width: nodeWidth, + height: nodeHeight, + decoration: BoxDecoration( + color: nodeColor, + borderRadius: BorderRadius.circular(8 * widget.scale), + border: Border.all( + color: borderColor, + width: 2 * widget.scale, + ), + boxShadow: _isDragging || _isHovering + ? [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 4 * widget.scale, + spreadRadius: 1 * widget.scale, + offset: Offset(0, 2 * widget.scale), + ), + ] + : null, + ), + child: Padding( + padding: EdgeInsets.all(8 * widget.scale), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + widget.node.label, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 12 * widget.scale, + color: Theme.of(context).textTheme.bodyLarge?.color, + ), + overflow: TextOverflow.ellipsis, + maxLines: 2, + textAlign: TextAlign.center, + ), + SizedBox(height: 4 * widget.scale), + Text( + 'ID: ${widget.node.id.substring(0, 4)}...', + style: TextStyle( + fontSize: 9 * widget.scale, + color: Theme.of(context).textTheme.bodySmall?.color, + ), + ), + ], + ), + ), + ), + + // Remove button + if (_isHovering) + Positioned( + right: 0, + top: 0, + child: GestureDetector( + onTap: widget.onRemove, + child: Container( + padding: EdgeInsets.all(4 * widget.scale), + decoration: BoxDecoration( + color: Colors.red, + shape: BoxShape.circle, + ), + child: Icon( + Icons.close, + size: 12 * widget.scale, + color: Colors.white, + ), + ), + ), + ), + + Positioned( + right: -8 * widget.scale, + top: nodeHeight / 2 - (8 * widget.scale), + child: MouseRegion( + onEnter: (_) => setState(() { _isOutputPortHovering = true; }), + onExit: (_) => setState(() { _isOutputPortHovering = false; }), + child: GestureDetector( + onTap: () { + final globalPosition = Offset( + widget.node.position.dx + nodeWidth / 2, + widget.node.position.dy + nodeHeight / 2, + ); + widget.onConnect(globalPosition); + }, + child: Stack( + clipBehavior: Clip.none, + children: [ + Container( + width: 16 * widget.scale, + height: 16 * widget.scale, + decoration: BoxDecoration( + color: _isOutputPortHovering ? Colors.blue.shade700 : Colors.blue, + shape: BoxShape.circle, + border: Border.all( + color: Colors.white, + width: 2 * widget.scale, + ), + boxShadow: _isOutputPortHovering || _isHovering ? [ + BoxShadow( + color: Colors.blue.withOpacity(0.5), + blurRadius: 4, + spreadRadius: 2, + ) + ] : null, + ), + child: Icon( + Icons.arrow_forward, + color: Colors.white, + size: 10 * widget.scale, + ), + ), + if (_isOutputPortHovering) + Positioned( + top: -20 * widget.scale, + right: 8 * widget.scale, + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 6 * widget.scale, + vertical: 3 * widget.scale, + ), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.7), + borderRadius: BorderRadius.circular(4 * widget.scale), + ), + child: Text( + 'Drag to connect', + style: TextStyle( + color: Colors.white, + fontSize: 10 * widget.scale, + ), + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ); + } +} From 820156128b6dc31f0c9ae6b08a9eef2ede0a2008 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Tue, 8 Apr 2025 23:20:52 +0530 Subject: [PATCH 111/188] chore: export workflow widgets --- .../lib/src/widgets/workflow_builder/workflow_screens.dart | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_screens.dart diff --git a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_screens.dart b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_screens.dart new file mode 100644 index 000000000..fdc69e8e5 --- /dev/null +++ b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_screens.dart @@ -0,0 +1,4 @@ +export 'workflow_builder_page.dart'; +export 'workflow_canvas.dart'; +export 'workflow_node.dart'; +export 'workflow_connection.dart'; From 3b8202cd7d1372db4f5f473647cf30dabf5747f5 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Wed, 9 Apr 2025 00:14:06 +0530 Subject: [PATCH 112/188] refactor: add package in pubspec --- pubspec.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 28ffae063..7a4fd7a0c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,8 @@ dependencies: path: packages/apidash_core apidash_design_system: path: packages/apidash_design_system + api_testing_suite: + path: packages/api_testing_suite code_builder: ^4.10.0 csv: ^6.0.0 data_table_2: 2.5.16 @@ -60,7 +62,7 @@ dependencies: share_plus: ^10.1.4 shared_preferences: ^2.5.2 url_launcher: ^6.2.5 - uuid: ^4.5.0 + uuid: ^3.0.7 vector_graphics_compiler: ^1.1.9+1 video_player: ^2.9.3 video_player_platform_interface: ^6.3.0 From e96600e19e68f40763e89986b0247ad46bc94040 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:21:40 +0530 Subject: [PATCH 113/188] chore: remove workflow builder components (page, canvas, node, connection) from apidash/src --- .../workflow_builder_page.dart | 151 ----- .../workflow_builder/workflow_canvas.dart | 553 ------------------ .../workflow_builder/workflow_connection.dart | 165 ------ .../workflow_builder/workflow_node.dart | 275 --------- .../workflow_builder/workflow_screens.dart | 4 - 5 files changed, 1148 deletions(-) delete mode 100644 packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_builder_page.dart delete mode 100644 packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_canvas.dart delete mode 100644 packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_connection.dart delete mode 100644 packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_node.dart delete mode 100644 packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_screens.dart diff --git a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_builder_page.dart b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_builder_page.dart deleted file mode 100644 index 49550e1cf..000000000 --- a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_builder_page.dart +++ /dev/null @@ -1,151 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../../providers/workflow_providers.dart'; -import 'workflow_canvas.dart'; - -/// The main page for building and editing workflows -class WorkflowBuilderPage extends ConsumerWidget { - const WorkflowBuilderPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final workflowId = ref.watch(currentWorkflowProvider); - final execState = ref.watch(workflowExecutionStateProvider); - - if (workflowId == null) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('No workflow selected'), - const SizedBox(height: 16), - ElevatedButton( - onPressed: () { - ref.read(workflowsProvider.notifier).add(); - }, - child: const Text('Create New Workflow'), - ), - ], - ), - ); - } - - return Scaffold( - body: Column( - children: [ - // Toolbar - Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.1), - blurRadius: 4, - offset: const Offset(0, 2), - ), - ], - ), - child: Row( - children: [ - Expanded( - child: Text( - 'Workflow Builder', - style: Theme.of(context).textTheme.titleLarge, - ), - ), - // Execution controls - if (execState.status == WorkflowExecutionStatus.idle) ...[ - IconButton( - icon: const Icon(Icons.play_arrow, color: Colors.green), - tooltip: 'Start Workflow', - onPressed: () { - ref.read(workflowExecutionStateProvider.notifier).start(); - }, - ), - ] else if (execState.status == WorkflowExecutionStatus.running) ...[ - IconButton( - icon: const Icon(Icons.pause, color: Colors.orange), - tooltip: 'Pause Workflow', - onPressed: () { - ref.read(workflowExecutionStateProvider.notifier).pause(); - }, - ), - IconButton( - icon: const Icon(Icons.stop, color: Colors.red), - tooltip: 'Stop Workflow', - onPressed: () { - ref.read(workflowExecutionStateProvider.notifier).stop(); - }, - ), - ] else if (execState.status == WorkflowExecutionStatus.paused) ...[ - IconButton( - icon: const Icon(Icons.play_arrow, color: Colors.green), - tooltip: 'Resume Workflow', - onPressed: () { - ref.read(workflowExecutionStateProvider.notifier).resume(); - }, - ), - IconButton( - icon: const Icon(Icons.stop, color: Colors.red), - tooltip: 'Stop Workflow', - onPressed: () { - ref.read(workflowExecutionStateProvider.notifier).stop(); - }, - ), - ], - - IconButton( - icon: const Icon(Icons.add), - tooltip: 'Add Node', - onPressed: () { - _showAddNodeDialog(context, ref, workflowId); - }, - ), - IconButton( - icon: const Icon(Icons.save), - tooltip: 'Save Workflow', - onPressed: () { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Workflow saved')), - ); - }, - ), - ], - ), - ), - - // Main canvas area - Expanded( - child: WorkflowCanvas(workflowId: workflowId), - ), - ], - ), - ); - } - - void _showAddNodeDialog(BuildContext context, WidgetRef ref, String workflowId) { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('Add Node'), - content: const Text('Select an API request to add as a node'), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - // TODO: @abhinavs1920 Add a node based on a selected request - Navigator.of(context).pop(); - }, - child: const Text('Add'), - ), - ], - ), - ); - } -} diff --git a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_canvas.dart b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_canvas.dart deleted file mode 100644 index bb7475a15..000000000 --- a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_canvas.dart +++ /dev/null @@ -1,553 +0,0 @@ -import 'package:api_testing_suite/src/models/models.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../../models/workflow_model.dart'; -import '../../providers/workflow_providers.dart'; -import 'workflow_node.dart'; -import 'workflow_connection.dart'; - -/// A widget that displays the workflow canvas where nodes can be placed and connected -class WorkflowCanvas extends ConsumerStatefulWidget { - const WorkflowCanvas({ - Key? key, - required this.workflowId, - }) : super(key: key); - - final String workflowId; - - @override - ConsumerState createState() => _WorkflowCanvasState(); -} - -class _WorkflowCanvasState extends ConsumerState { - // For drag and drop functionality - Offset _panPosition = Offset.zero; - double _scale = 1.0; - Offset? _startConnectingPosition; - String? _startConnectingNodeId; - String? _highlightNodeId; // Target node being highlighted - String? _highlightSourceId; // Source node being highlighted - Offset? _currentConnectingPosition; - bool _isDraggingCanvas = false; - bool _isConnectionModeActive = false; // New variable to track if we're in connection mode - - @override - Widget build(BuildContext context) { - final workflow = ref.watch(activeWorkflowProvider); - if (workflow == null) { - return const Center(child: Text('No workflow selected')); - } - - if (_isConnectionModeActive) { - return RawKeyboardListener( - focusNode: FocusNode(), - autofocus: true, - onKey: (event) { - if (event.logicalKey == LogicalKeyboardKey.escape) { - _resetConnectionMode(); - } - }, - child: _buildMainCanvas(workflow), - ); - } - - return _buildMainCanvas(workflow); - } - - Widget _buildMainCanvas(WorkflowModel workflow) { - return GestureDetector( - onTap: () { - if (_isConnectionModeActive) { - _resetConnectionMode(); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Connection cancelled'), - duration: Duration(seconds: 1), - backgroundColor: Colors.grey, - ), - ); - } - }, - onSecondaryTapDown: (_) { - if (_isConnectionModeActive) { - _resetConnectionMode(); - } else { - setState(() { - _isDraggingCanvas = true; - }); - } - }, - onSecondaryTapUp: (_) { - setState(() { - _isDraggingCanvas = false; - }); - }, - onScaleStart: (details) { - setState(() { - _isDraggingCanvas = true; - }); - }, - onScaleUpdate: (details) { - if (!_isDraggingCanvas && _startConnectingNodeId != null) { - return; - } - setState(() { - if (details.scale == 1.0) { - _panPosition += details.focalPointDelta; - } else { - _scale = (_scale * details.scale).clamp(0.5, 2.0); - } - }); - }, - onScaleEnd: (_) { - setState(() { - _isDraggingCanvas = false; - }); - }, - child: Container( - color: Theme.of(context).scaffoldBackgroundColor, - child: Stack( - children: [ - CustomPaint( - painter: GridPainter(offset: _panPosition, scale: _scale), - size: MediaQuery.of(context).size, - ), - - if (_startConnectingPosition != null && _currentConnectingPosition != null) - CustomPaint( - painter: ConnectionLinePainter( - start: _startConnectingPosition!, - end: _currentConnectingPosition!, - isPreview: true, - ), - size: MediaQuery.of(context).size, - ), - - ...workflow.connections.map((connection) { - final sourceNodeIndex = workflow.nodes.indexWhere((node) => node.id == connection.sourceId); - final targetNodeIndex = workflow.nodes.indexWhere((node) => node.id == connection.targetId); - - if (sourceNodeIndex == -1 || targetNodeIndex == -1) { - return const SizedBox.shrink(); // Connection refers to nodes that don't exist - } - - final sourceNode = workflow.nodes[sourceNodeIndex]; - final targetNode = workflow.nodes[targetNodeIndex]; - - final scaledSourcePosition = Offset( - sourceNode.position.dx * _scale + _panPosition.dx + 120 * _scale, // Right edge of source node - sourceNode.position.dy * _scale + _panPosition.dy + 40 * _scale, // Middle of source node - ); - - final scaledTargetPosition = Offset( - targetNode.position.dx * _scale + _panPosition.dx, // Left edge of target node - targetNode.position.dy * _scale + _panPosition.dy + 40 * _scale, // Middle of target node - ); - - return Positioned.fill( - child: WorkflowConnection( - connection: connection, - sourcePosition: scaledSourcePosition, - targetPosition: scaledTargetPosition, - isConditional: connection.isConditional, - scale: _scale, - onTap: () => _showConnectionDetails(connection), - onRemove: () => _removeConnection(connection.id), - ), - ); - }).toList(), - - // Workflow nodes - ...workflow.nodes.map((node) { - final isHighlighted = _highlightNodeId == node.id || _highlightSourceId == node.id; - final canBeTarget = _startConnectingNodeId != null && - _startConnectingNodeId != node.id && - !workflow.connections.any((conn) => - conn.sourceId == _startConnectingNodeId && conn.targetId == node.id); - - final position = Offset( - node.position.dx * _scale + _panPosition.dx, - node.position.dy * _scale + _panPosition.dy - ); - - return Positioned( - left: position.dx, - top: position.dy, - child: isHighlighted - ? Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - boxShadow: [ - BoxShadow( - color: Colors.blue.withOpacity(0.6), - blurRadius: 10, - spreadRadius: 5, - ), - ], - ), - child: WorkflowNode( - node: node, - scale: _scale, - onDragUpdate: (delta) => _moveNode(node.id, delta), - onConnect: (position) => _startConnecting(node.id, position), - onConnectMove: _updateConnecting, - onConnectEnd: _finishConnecting, - onTap: () => _showNodeDetails(node), - onRemove: () => _removeNode(node.id), - isConnectionModeActive: _isConnectionModeActive && canBeTarget, - onConnectionTarget: canBeTarget ? () => _connectNodes(_startConnectingNodeId!, node.id) : null, - ) - ) : WorkflowNode( - node: node, - scale: _scale, - onDragUpdate: (delta) => _moveNode(node.id, delta), - onConnect: (position) => _startConnecting(node.id, position), - onConnectMove: _updateConnecting, - onConnectEnd: _finishConnecting, - onTap: () => _showNodeDetails(node), - onRemove: () => _removeNode(node.id), - isConnectionModeActive: _isConnectionModeActive && canBeTarget, - onConnectionTarget: canBeTarget ? () => _connectNodes(_startConnectingNodeId!, node.id) : null, - ), - ); - }).toList(), - ], - ), - ), - ); - } - - void _moveNode(String nodeId, Offset delta) { - if (_isConnectionModeActive) return; - - ref.read(workflowsProvider.notifier).updateNodePosition( - widget.workflowId, - nodeId, - Offset( - (ref.read(activeWorkflowProvider)!.nodes - .firstWhere((node) => node.id == nodeId).position.dx + delta.dx), - (ref.read(activeWorkflowProvider)!.nodes - .firstWhere((node) => node.id == nodeId).position.dy + delta.dy), - ), - ); - } - - void _startConnecting(String nodeId, Offset position) { - setState(() { - _startConnectingNodeId = nodeId; - _startConnectingPosition = position; - _currentConnectingPosition = position; - _isConnectionModeActive = true; - - final workflow = ref.read(activeWorkflowProvider); - if (workflow != null) { - final existingConnections = workflow.connections.where( - (conn) => conn.sourceId == nodeId - ).toList(); - - _highlightSourceId = nodeId; - - if (existingConnections.isNotEmpty) { - _highlightNodeId = existingConnections.first.targetId; - } - } - }); - } - - void _updateConnecting(Offset position) { - setState(() { - _currentConnectingPosition = position; - }); - } - - void _resetConnectionMode() { - setState(() { - _startConnectingNodeId = null; - _startConnectingPosition = null; - _currentConnectingPosition = null; - _highlightNodeId = null; - _highlightSourceId = null; - _isConnectionModeActive = false; - }); - } - - void _connectNodes(String sourceId, String targetId) { - final workflow = ref.read(activeWorkflowProvider); - if (workflow == null) return; - - final existingConnection = workflow.connections.any( - (conn) => conn.sourceId == sourceId && conn.targetId == targetId - ); - - if (existingConnection) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Connection already exists'), - duration: Duration(seconds: 1), - backgroundColor: Colors.orange, - ), - ); - _resetConnectionMode(); - return; - } - - final connection = WorkflowConnectionModel.create( - sourceId: sourceId, - targetId: targetId, - ); - - ref.read(workflowsProvider.notifier).addConnection( - widget.workflowId, - connection, - ); - - final sourceNode = workflow.nodes.firstWhere((node) => node.id == sourceId); - final updatedConnectedToIds = [...sourceNode.connectedToIds, targetId]; - - ref.read(workflowsProvider.notifier).updateNode( - widget.workflowId, - sourceNode.copyWith(connectedToIds: updatedConnectedToIds), - ); - - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Nodes connected successfully'), - duration: Duration(seconds: 1), - backgroundColor: Colors.green, - ), - ); - - _resetConnectionMode(); - } - - void _finishConnecting(Offset position) { - // We'll TODO: @abhinavs1920 Implement hit detection here for connecting to another node - _resetConnectionMode(); - } - - void _showNodeDetails(WorkflowNodeModel node) { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Node Details'), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('ID: ${node.id}'), - SizedBox(height: 8), - Text('Label:'), - TextFormField( - initialValue: node.label, - onChanged: (value) { - final updatedNode = node.copyWith(label: value); - ref.read(workflowsProvider.notifier).updateNode( - widget.workflowId, - updatedNode, - ); - }, - decoration: InputDecoration( - hintText: 'Enter node label', - border: OutlineInputBorder(), - ), - ), - SizedBox(height: 16), - Text('Status: ${node.status.toString().split('.').last}'), - SizedBox(height: 16), - Text('Connected to: ${node.connectedToIds.join(', ')}'), - ], - ), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: Text('Close'), - ), - ], - ), - ); - } - - void _showConnectionDetails(WorkflowConnectionModel connection) { - bool isConditional = connection.isConditional; - String condition = connection.condition; - - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Connection Details'), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('From: ${connection.sourceId}'), - Text('To: ${connection.targetId}'), - SizedBox(height: 16), - CheckboxListTile( - title: Text('Conditional Connection'), - value: isConditional, - onChanged: (value) { - setState(() { - isConditional = value ?? false; - }); - }, - ), - if (isConditional) ...[ - SizedBox(height: 8), - Text('Condition:'), - TextFormField( - initialValue: condition, - onChanged: (value) { - condition = value; - }, - decoration: InputDecoration( - hintText: 'Enter condition expression', - border: OutlineInputBorder(), - helperText: 'Example: response.status == 200', - ), - ), - ], - ], - ), - actions: [ - TextButton.icon( - icon: Icon(Icons.delete, color: Colors.red), - label: Text('Remove', style: TextStyle(color: Colors.red)), - onPressed: () { - _removeConnection(connection.id); - Navigator.of(context).pop(); - }, - ), - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - final updatedConnection = connection.copyWith( - isConditional: isConditional, - condition: condition, - ); - ref.read(workflowsProvider.notifier).updateConnection( - widget.workflowId, - updatedConnection, - ); - Navigator.of(context).pop(); - }, - child: const Text('Save'), - ), - ], - ), - ); - } - - void _removeNode(String nodeId) { - final workflow = ref.read(activeWorkflowProvider); - if (workflow == null) return; - - ref.read(workflowsProvider.notifier).removeNode( - workflow.id, - nodeId, - ); - } - - void _removeConnection(String connectionId) { - final workflow = ref.read(activeWorkflowProvider); - if (workflow == null) return; - - ref.read(workflowsProvider.notifier).removeConnection( - workflow.id, - connectionId, - ); - } -} - -class GridPainter extends CustomPainter { - final Offset offset; - final double scale; - - GridPainter({ - required this.offset, - required this.scale, - }); - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = Colors.grey.withOpacity(0.2) - ..strokeWidth = 1; - - const gridSize = 40.0; - - final scaledGridSize = gridSize * scale; - final startX = (offset.dx * scale) % scaledGridSize; - final startY = (offset.dy * scale) % scaledGridSize; - - for (double x = startX; x <= size.width; x += scaledGridSize) { - canvas.drawLine(Offset(x, 0), Offset(x, size.height), paint); - } - - for (double y = startY; y <= size.height; y += scaledGridSize) { - canvas.drawLine(Offset(0, y), Offset(size.width, y), paint); - } - } - - @override - bool shouldRepaint(covariant GridPainter oldDelegate) { - return oldDelegate.offset != offset || oldDelegate.scale != scale; - } -} - -class ConnectionLinePainter extends CustomPainter { - final Offset start; - final Offset end; - final bool isPreview; - - ConnectionLinePainter({ - required this.start, - required this.end, - this.isPreview = false, - }); - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = isPreview ? Colors.blue.withOpacity(0.7) : Colors.blue - ..strokeWidth = isPreview ? 3 : 2 - ..style = PaintingStyle.stroke; - - final path = Path() - ..moveTo(start.dx, start.dy) - ..cubicTo( - start.dx + (end.dx - start.dx) * 0.5, - start.dy, - start.dx + (end.dx - start.dx) * 0.5, - end.dy, - end.dx, - end.dy, - ); - - canvas.drawPath(path, paint); - - // Draw arrow at the end - final arrowSize = 10.0; - final arrowPath = Path() - ..moveTo(end.dx, end.dy) - ..lineTo( - end.dx - arrowSize * 1.5 * (end.dx > start.dx ? 1 : -1), - end.dy - arrowSize, - ) - ..lineTo( - end.dx - arrowSize * 1.5 * (end.dx > start.dx ? 1 : -1), - end.dy + arrowSize, - ) - ..close(); - - canvas.drawPath(arrowPath, paint..style = PaintingStyle.fill); - } - - @override - bool shouldRepaint(covariant ConnectionLinePainter oldDelegate) { - return oldDelegate.start != start || oldDelegate.end != end; - } -} diff --git a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_connection.dart b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_connection.dart deleted file mode 100644 index e0d466754..000000000 --- a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_connection.dart +++ /dev/null @@ -1,165 +0,0 @@ -import 'dart:math'; -import 'package:api_testing_suite/src/models/models.dart'; -import 'package:flutter/material.dart'; - -/// A widget that displays a connection between two workflow nodes -class WorkflowConnection extends StatefulWidget { - final WorkflowConnectionModel connection; - final Offset sourcePosition; - final Offset targetPosition; - final bool isConditional; - final double scale; - final VoidCallback onTap; - final VoidCallback onRemove; - - const WorkflowConnection({ - Key? key, - required this.connection, - required this.sourcePosition, - required this.targetPosition, - this.isConditional = false, - this.scale = 1.0, - required this.onTap, - required this.onRemove, - }) : super(key: key); - - @override - State createState() => _WorkflowConnectionState(); -} - -class _WorkflowConnectionState extends State { - bool _isHovering = false; - - @override - Widget build(BuildContext context) { - final dx = widget.targetPosition.dx - widget.sourcePosition.dx; - final dy = widget.targetPosition.dy - widget.sourcePosition.dy; - final distance = sqrt(dx * dx + dy * dy); - - final angle = atan2(dy, dx); - - final centerX = widget.sourcePosition.dx + dx / 2; - final centerY = widget.sourcePosition.dy + dy / 2; - - return Stack( - children: [ - Positioned( - left: widget.sourcePosition.dx, - top: widget.sourcePosition.dy, - child: Transform( - transform: Matrix4.identity() - ..translate(0.0, 0.0) - ..rotateZ(angle), - alignment: Alignment.centerLeft, - child: Container( - width: distance, - height: 2 * widget.scale, - decoration: BoxDecoration( - color: widget.isConditional ? Colors.orange : Colors.blue, - borderRadius: BorderRadius.circular(1 * widget.scale), - ), - ), - ), - ), - - Positioned( - left: widget.targetPosition.dx - 10 * widget.scale, - top: widget.targetPosition.dy - 5 * widget.scale, - child: Transform( - transform: Matrix4.identity()..rotateZ(angle), - alignment: Alignment.center, - child: Container( - width: 10 * widget.scale, - height: 10 * widget.scale, - decoration: BoxDecoration( - color: widget.isConditional ? Colors.orange : Colors.blue, - shape: BoxShape.circle, - ), - ), - ), - ), - - if (widget.isConditional) - Positioned( - left: centerX - 8 * widget.scale, - top: centerY - 8 * widget.scale, - child: Transform( - transform: Matrix4.identity()..rotateZ(pi/4), - alignment: Alignment.center, - child: Container( - width: 16 * widget.scale, - height: 16 * widget.scale, - decoration: BoxDecoration( - color: Colors.orange, - border: Border.all( - color: Colors.white, - width: 1 * widget.scale, - ), - ), - ), - ), - ), - - Positioned( - left: widget.sourcePosition.dx, - top: widget.sourcePosition.dy - 15 * widget.scale, - child: MouseRegion( - onEnter: (_) => setState(() => _isHovering = true), - onExit: (_) => setState(() => _isHovering = false), - child: GestureDetector( - onTap: widget.onTap, - child: Transform( - transform: Matrix4.identity()..rotateZ(angle), - alignment: Alignment.centerLeft, - child: Container( - width: distance, - height: 30 * widget.scale, - color: Colors.transparent, - ), - ), - ), - ), - ), - - if (_isHovering) - Positioned( - left: centerX - 32, - top: centerY - 20, - child: Container( - padding: const EdgeInsets.all(4), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.2), - blurRadius: 4, - spreadRadius: 1, - ), - ], - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: const Icon(Icons.settings, size: 18), - tooltip: 'Edit connection', - onPressed: widget.onTap, - visualDensity: VisualDensity.compact, - constraints: const BoxConstraints.tightFor(width: 28, height: 28), - ), - IconButton( - icon: const Icon(Icons.delete, size: 18, color: Colors.red), - tooltip: 'Remove connection', - onPressed: widget.onRemove, - visualDensity: VisualDensity.compact, - constraints: const BoxConstraints.tightFor(width: 28, height: 28), - ), - ], - ), - ), - ), - ], - ); - } -} diff --git a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_node.dart b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_node.dart deleted file mode 100644 index 14f6d43d4..000000000 --- a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_node.dart +++ /dev/null @@ -1,275 +0,0 @@ -import 'package:api_testing_suite/src/models/models.dart'; -import 'package:flutter/material.dart'; - -/// A widget that displays a single node in a workflow -class WorkflowNode extends StatefulWidget { - const WorkflowNode({ - Key? key, - required this.node, - this.scale = 1.0, - required this.onDragUpdate, - required this.onConnect, - required this.onConnectMove, - required this.onConnectEnd, - required this.onTap, - required this.onRemove, - this.isConnectionModeActive = false, - this.onConnectionTarget, - }) : super(key: key); - - final WorkflowNodeModel node; - final double scale; - final Function(Offset) onDragUpdate; - final Function(Offset) onConnect; - final Function(Offset) onConnectMove; - final Function(Offset) onConnectEnd; - final VoidCallback onTap; - final VoidCallback onRemove; - final bool isConnectionModeActive; - final VoidCallback? onConnectionTarget; - @override - State createState() => _WorkflowNodeState(); -} - -class _WorkflowNodeState extends State { - bool _isDragging = false; - bool _isHovering = false; - bool _isOutputPortHovering = false; - bool _isInputPortHovering = false; - - @override - Widget build(BuildContext context) { - final nodeWidth = 120.0 * widget.scale; - final nodeHeight = 80.0 * widget.scale; - - Color nodeColor; - Color borderColor; - - switch (widget.node.status) { - case NodeStatus.running: - nodeColor = Colors.blue.withOpacity(0.7); - borderColor = Colors.blue; - break; - case NodeStatus.success: - nodeColor = Colors.green.withOpacity(0.7); - borderColor = Colors.green; - break; - case NodeStatus.failure: - nodeColor = Colors.red.withOpacity(0.7); - borderColor = Colors.red; - break; - case NodeStatus.pending: - nodeColor = Colors.orange.withOpacity(0.7); - borderColor = Colors.orange; - break; - default: - nodeColor = Theme.of(context).cardColor; - borderColor = Theme.of(context).dividerColor; - } - - return MouseRegion( - onEnter: (_) => setState(() => _isHovering = true), - onExit: (_) => setState(() => _isHovering = false), - child: GestureDetector( - onTap: () { - if (widget.isConnectionModeActive && widget.onConnectionTarget != null) { - widget.onConnectionTarget!(); - } else { - widget.onTap(); - } - }, - onPanStart: (details) { - setState(() => _isDragging = true); - }, - onPanUpdate: (details) { - widget.onDragUpdate(details.delta / widget.scale); - }, - onPanEnd: (details) { - setState(() => _isDragging = false); - }, - child: Stack( - children: [ - Positioned( - left: -8 * widget.scale, - top: nodeHeight / 2 - (8 * widget.scale), - child: MouseRegion( - onEnter: (_) => setState(() { _isInputPortHovering = true; }), - onExit: (_) => setState(() { _isInputPortHovering = false; }), - child: Stack( - clipBehavior: Clip.none, - children: [ - Container( - width: 16 * widget.scale, - height: 16 * widget.scale, - decoration: BoxDecoration( - color: _isInputPortHovering ? Colors.green.shade700 : Colors.green, - shape: BoxShape.circle, - border: Border.all( - color: Colors.white, - width: 2 * widget.scale, - ), - boxShadow: _isInputPortHovering || _isHovering ? [ - BoxShadow( - color: Colors.green.withOpacity(0.5), - blurRadius: 4, - spreadRadius: 2, - ) - ] : null, - ), - child: Icon( - Icons.arrow_back, - color: Colors.white, - size: 10 * widget.scale, - ), - ), - ], - ), - ), - ), - - Container( - width: nodeWidth, - height: nodeHeight, - decoration: BoxDecoration( - color: nodeColor, - borderRadius: BorderRadius.circular(8 * widget.scale), - border: Border.all( - color: borderColor, - width: 2 * widget.scale, - ), - boxShadow: _isDragging || _isHovering - ? [ - BoxShadow( - color: Colors.black.withOpacity(0.2), - blurRadius: 4 * widget.scale, - spreadRadius: 1 * widget.scale, - offset: Offset(0, 2 * widget.scale), - ), - ] - : null, - ), - child: Padding( - padding: EdgeInsets.all(8 * widget.scale), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - widget.node.label, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 12 * widget.scale, - color: Theme.of(context).textTheme.bodyLarge?.color, - ), - overflow: TextOverflow.ellipsis, - maxLines: 2, - textAlign: TextAlign.center, - ), - SizedBox(height: 4 * widget.scale), - Text( - 'ID: ${widget.node.id.substring(0, 4)}...', - style: TextStyle( - fontSize: 9 * widget.scale, - color: Theme.of(context).textTheme.bodySmall?.color, - ), - ), - ], - ), - ), - ), - - // Remove button - if (_isHovering) - Positioned( - right: 0, - top: 0, - child: GestureDetector( - onTap: widget.onRemove, - child: Container( - padding: EdgeInsets.all(4 * widget.scale), - decoration: BoxDecoration( - color: Colors.red, - shape: BoxShape.circle, - ), - child: Icon( - Icons.close, - size: 12 * widget.scale, - color: Colors.white, - ), - ), - ), - ), - - Positioned( - right: -8 * widget.scale, - top: nodeHeight / 2 - (8 * widget.scale), - child: MouseRegion( - onEnter: (_) => setState(() { _isOutputPortHovering = true; }), - onExit: (_) => setState(() { _isOutputPortHovering = false; }), - child: GestureDetector( - onTap: () { - final globalPosition = Offset( - widget.node.position.dx + nodeWidth / 2, - widget.node.position.dy + nodeHeight / 2, - ); - widget.onConnect(globalPosition); - }, - child: Stack( - clipBehavior: Clip.none, - children: [ - Container( - width: 16 * widget.scale, - height: 16 * widget.scale, - decoration: BoxDecoration( - color: _isOutputPortHovering ? Colors.blue.shade700 : Colors.blue, - shape: BoxShape.circle, - border: Border.all( - color: Colors.white, - width: 2 * widget.scale, - ), - boxShadow: _isOutputPortHovering || _isHovering ? [ - BoxShadow( - color: Colors.blue.withOpacity(0.5), - blurRadius: 4, - spreadRadius: 2, - ) - ] : null, - ), - child: Icon( - Icons.arrow_forward, - color: Colors.white, - size: 10 * widget.scale, - ), - ), - if (_isOutputPortHovering) - Positioned( - top: -20 * widget.scale, - right: 8 * widget.scale, - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 6 * widget.scale, - vertical: 3 * widget.scale, - ), - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.7), - borderRadius: BorderRadius.circular(4 * widget.scale), - ), - child: Text( - 'Drag to connect', - style: TextStyle( - color: Colors.white, - fontSize: 10 * widget.scale, - ), - ), - ), - ), - ], - ), - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_screens.dart b/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_screens.dart deleted file mode 100644 index fdc69e8e5..000000000 --- a/packages/api_testing_suite/lib/src/widgets/workflow_builder/workflow_screens.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'workflow_builder_page.dart'; -export 'workflow_canvas.dart'; -export 'workflow_node.dart'; -export 'workflow_connection.dart'; From 006b98b8eaed3b3484058b14556681f100455a86 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:22:27 +0530 Subject: [PATCH 114/188] refactor: update WorkflowConnectionModel to include workflowId and position fields --- .../src/models/workflow_connection_model.dart | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/packages/api_testing_suite/lib/src/models/workflow_connection_model.dart b/packages/api_testing_suite/lib/src/models/workflow_connection_model.dart index 4fbc0391e..0271f1b60 100644 --- a/packages/api_testing_suite/lib/src/models/workflow_connection_model.dart +++ b/packages/api_testing_suite/lib/src/models/workflow_connection_model.dart @@ -1,40 +1,52 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:uuid/uuid.dart'; part 'workflow_connection_model.freezed.dart'; -part 'workflow_connection_model.g.dart'; /// Model representing a connection between nodes in a workflow @freezed class WorkflowConnectionModel with _$WorkflowConnectionModel { - const WorkflowConnectionModel._(); - const factory WorkflowConnectionModel({ required String id, required String sourceId, required String targetId, - @Default('') String label, + required String workflowId, + @JsonKey(fromJson: _offsetFromJson, toJson: _offsetToJson) required Offset position, + String? label, @Default(false) bool isConditional, - @Default('') String condition, + String? condition, }) = _WorkflowConnectionModel; + factory WorkflowConnectionModel.fromJson(Map json) => + _$WorkflowConnectionModelFromJson(json); + factory WorkflowConnectionModel.create({ required String sourceId, required String targetId, - String label = '', + required String workflowId, + required Offset position, + String? label, bool isConditional = false, - String condition = '', - }) { - return WorkflowConnectionModel( - id: const Uuid().v4(), - sourceId: sourceId, - targetId: targetId, - label: label, - isConditional: isConditional, - condition: condition, - ); - } + String? condition, + }) => + WorkflowConnectionModel( + id: const Uuid().v4(), + sourceId: sourceId, + targetId: targetId, + workflowId: workflowId, + position: position, + label: label, + isConditional: isConditional, + condition: condition, + ); +} - factory WorkflowConnectionModel.fromJson(Map json) => - _$WorkflowConnectionModelFromJson(json); +Offset _offsetFromJson(Map json) { + return Offset(json['dx'] as double, json['dy'] as double); +} + +Map _offsetToJson(Offset offset) { + return {'dx': offset.dx, 'dy': offset.dy}; } From 17ab7eb899ef89e38dcad48a9b2776002cd1d304 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:25:11 +0530 Subject: [PATCH 115/188] feat: add WorkflowConnection model with connection types and factory methods --- .../lib/src/models/workflow_connection.dart | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/models/workflow_connection.dart diff --git a/packages/api_testing_suite/lib/src/models/workflow_connection.dart b/packages/api_testing_suite/lib/src/models/workflow_connection.dart new file mode 100644 index 000000000..4e3ad89e9 --- /dev/null +++ b/packages/api_testing_suite/lib/src/models/workflow_connection.dart @@ -0,0 +1,66 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:flutter/material.dart'; +import 'package:uuid/uuid.dart'; + +part 'workflow_connection.freezed.dart'; + +/// Model representing a connection between two nodes in a workflow +@freezed +class WorkflowConnection with _$WorkflowConnection { + const WorkflowConnection._(); + + const factory WorkflowConnection({ + required String id, + required String sourceId, + required String targetId, + required String workflowId, + required Offset position, + @Default(ConnectionType.standard) ConnectionType type, + @Default([]) List labels, + @Default({}) Map metadata, + }) = _WorkflowConnection; + + factory WorkflowConnection.fromJson(Map json) => + _$WorkflowConnectionFromJson(json); + + factory WorkflowConnection.create({ + required String sourceId, + required String targetId, + required String workflowId, + required Offset position, + ConnectionType type = ConnectionType.standard, + List labels = const [], + Map metadata = const {}, + }) { + return WorkflowConnection( + id: const Uuid().v4(), + sourceId: sourceId, + targetId: targetId, + workflowId: workflowId, + position: position, + type: type, + labels: labels, + metadata: metadata, + ); + } + +} + +enum ConnectionType { + standard, + conditional, + error, +} + +extension ConnectionTypeExtension on ConnectionType { + Color get color { + switch (this) { + case ConnectionType.standard: + return Colors.blue; + case ConnectionType.conditional: + return Colors.orange; + case ConnectionType.error: + return Colors.red; + } + } +} From bca30e7ad573900c8a24e52ea42ae4b17b2d0a22 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:26:40 +0530 Subject: [PATCH 116/188] refactor: update WorkflowModel to require name, nodes, and connections fields --- .../lib/src/models/workflow_model.dart | 59 ++++++++++++++++--- 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/packages/api_testing_suite/lib/src/models/workflow_model.dart b/packages/api_testing_suite/lib/src/models/workflow_model.dart index 0062c1ab9..2dfb57546 100644 --- a/packages/api_testing_suite/lib/src/models/workflow_model.dart +++ b/packages/api_testing_suite/lib/src/models/workflow_model.dart @@ -1,31 +1,74 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:uuid/uuid.dart'; - import 'workflow_node_model.dart'; import 'workflow_connection_model.dart'; part 'workflow_model.freezed.dart'; part 'workflow_model.g.dart'; -/// Model representing an entire workflow +/// Model representing a workflow @freezed class WorkflowModel with _$WorkflowModel { const WorkflowModel._(); const factory WorkflowModel({ required String id, - @Default('New Workflow') String name, + required String name, @Default('') String description, - @Default([]) List nodes, - @Default([]) List connections, + required List nodes, + required List connections, + @Default(false) bool isConnectionModeActive, + @Default(null) String? selectedNodeId, + @Default(null) String? startNodeId, + @Default([]) List activeNodeIds, + @Default([]) List completedNodeIds, + @Default({}) Map metadata, }) = _WorkflowModel; - factory WorkflowModel.create() { + factory WorkflowModel.fromJson(Map json) => + _$WorkflowModelFromJson(json); + + factory WorkflowModel.create({ + required String name, + String description = '', + List nodes = const [], + List connections = const [], + Map metadata = const {}, + }) { return WorkflowModel( id: const Uuid().v4(), + name: name, + description: description, + nodes: nodes, + connections: connections, + metadata: metadata, ); } - factory WorkflowModel.fromJson(Map json) => - _$WorkflowModelFromJson(json); + bool get hasStartNode => startNodeId != null; + + WorkflowNodeModel? getStartNode() { + if (startNodeId == null) return null; + return nodes.firstWhere((node) => node.id == startNodeId); + } + + List getActiveNodes() { + return nodes.where((node) => activeNodeIds.contains(node.id)).toList(); + } + + List getCompletedNodes() { + return nodes.where((node) => completedNodeIds.contains(node.id)).toList(); + } + + WorkflowNodeModel? getNodeById(String nodeId) { + return nodes.firstWhere((node) => node.id == nodeId); + } + + List getConnectionsFromNode(String nodeId) { + return connections.where((conn) => conn.sourceId == nodeId).toList(); + } + + List getConnectionsToNode(String nodeId) { + return connections.where((conn) => conn.targetId == nodeId).toList(); + } } From c16e05672a2efe30b8ff41dc9dc04ee9cf3adc06 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:27:51 +0530 Subject: [PATCH 117/188] feat: add WorkflowExecutionState model and WorkflowNodeModel enhancements with new fields and enum for node types --- .../src/models/workflow_execution_state.dart | 27 +++++++++++++++++++ .../lib/src/models/workflow_node_model.dart | 27 ++++++++++++++++--- 2 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 packages/api_testing_suite/lib/src/models/workflow_execution_state.dart diff --git a/packages/api_testing_suite/lib/src/models/workflow_execution_state.dart b/packages/api_testing_suite/lib/src/models/workflow_execution_state.dart new file mode 100644 index 000000000..e5aa3a2b0 --- /dev/null +++ b/packages/api_testing_suite/lib/src/models/workflow_execution_state.dart @@ -0,0 +1,27 @@ +enum WorkflowExecutionStatus { + idle, + running, + paused, + completed, + error +} + +class WorkflowExecutionState { + final WorkflowExecutionStatus status; + final String? errorMessage; + + const WorkflowExecutionState({ + this.status = WorkflowExecutionStatus.idle, + this.errorMessage, + }); + + WorkflowExecutionState copyWith({ + WorkflowExecutionStatus? status, + String? errorMessage, + }) { + return WorkflowExecutionState( + status: status ?? this.status, + errorMessage: errorMessage ?? this.errorMessage, + ); + } +} diff --git a/packages/api_testing_suite/lib/src/models/workflow_node_model.dart b/packages/api_testing_suite/lib/src/models/workflow_node_model.dart index e54cd960f..733be2dee 100644 --- a/packages/api_testing_suite/lib/src/models/workflow_node_model.dart +++ b/packages/api_testing_suite/lib/src/models/workflow_node_model.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:json_annotation/json_annotation.dart'; import 'package:uuid/uuid.dart'; import 'core_models.dart'; import 'node_status.dart'; +import 'workflow_connection_model.dart'; part 'workflow_node_model.freezed.dart'; part 'workflow_node_model.g.dart'; @@ -69,15 +71,20 @@ class WorkflowNodeModel with _$WorkflowNodeModel { @Default(NodeStatus.inactive) NodeStatus status, @Default([]) List connectedToIds, @RequestModelConverter() RequestModel? requestModel, + @Default({}) Map nodeData, + @Default(NodeType.request) NodeType nodeType, + @Default([]) List connections, @Default({}) Map simulatedResponse, @Default(200) int simulatedStatusCode, }) = _WorkflowNodeModel; factory WorkflowNodeModel.create({ required String requestId, - @OffsetConverter() required Offset position, - required String label, + required Offset position, + String label = '', RequestModel? requestModel, + NodeType nodeType = NodeType.request, + Map nodeData = const {}, }) { return WorkflowNodeModel( id: const Uuid().v4(), @@ -85,9 +92,21 @@ class WorkflowNodeModel with _$WorkflowNodeModel { position: position, label: label, requestModel: requestModel, + nodeType: nodeType, + nodeData: nodeData, ); } - factory WorkflowNodeModel.fromJson(Map json) => - _$WorkflowNodeModelFromJson(json); + double get x => position.dx; + double get y => position.dy; + String get workflowId => requestId; + + factory WorkflowNodeModel.fromJson(Map json) => _$WorkflowNodeModelFromJson(json); +} + +enum NodeType { + request, + response, + condition, + action, } From 65a82aaa78d683d2371395816cf00f91eb62bbc4 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:29:33 +0530 Subject: [PATCH 118/188] refactor: reorder NodeStatus enum values and update extension for color mapping --- .../lib/src/models/node_status.dart | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/api_testing_suite/lib/src/models/node_status.dart b/packages/api_testing_suite/lib/src/models/node_status.dart index cd6f1e6bc..01b5f3508 100644 --- a/packages/api_testing_suite/lib/src/models/node_status.dart +++ b/packages/api_testing_suite/lib/src/models/node_status.dart @@ -1,8 +1,27 @@ +import 'package:flutter/material.dart'; + /// Enum representing the different states a workflow node can be in enum NodeStatus { - pending, + inactive, running, success, failure, - inactive, + pending, +} + +extension NodeStatusExtension on NodeStatus { + Color get color { + switch (this) { + case NodeStatus.running: + return Colors.blue; + case NodeStatus.success: + return Colors.green; + case NodeStatus.failure: + return Colors.red; + case NodeStatus.pending: + return Colors.orange; + case NodeStatus.inactive: + return Colors.grey; + } + } } From 382de99536c4c1d8e2219b383fb5538dd3f4c974 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:32:07 +0530 Subject: [PATCH 119/188] fix: remove error giving fucntion (TODO: Resolving is required) --- .../lib/src/providers/workflow_providers.dart | 79 ++++++++----------- 1 file changed, 34 insertions(+), 45 deletions(-) diff --git a/packages/api_testing_suite/lib/src/providers/workflow_providers.dart b/packages/api_testing_suite/lib/src/providers/workflow_providers.dart index 80fff0b2c..e75f9b64a 100644 --- a/packages/api_testing_suite/lib/src/providers/workflow_providers.dart +++ b/packages/api_testing_suite/lib/src/providers/workflow_providers.dart @@ -23,7 +23,7 @@ final activeWorkflowProvider = Provider((ref) { return workflows.firstWhere( (workflow) => workflow.id == currentWorkflowId, - orElse: () => WorkflowModel.create(), + // orElse: () => WorkflowModel.create(), ); }); @@ -32,7 +32,6 @@ final workflowExecutionStateProvider = StateNotifierProvider { final Ref _ref; @@ -81,7 +78,6 @@ class WorkflowExecutionNotifier extends StateNotifier { final workflow = _ref.read(activeWorkflowProvider); if (workflow == null || workflow.nodes.isEmpty) return; - // Start with the first node final firstNode = workflow.nodes.first; state = WorkflowExecutionState( @@ -91,7 +87,6 @@ class WorkflowExecutionNotifier extends StateNotifier { executionContext: {}, ); - // Execute the first node await _executeNode(firstNode.id); } @@ -114,7 +109,6 @@ class WorkflowExecutionNotifier extends StateNotifier { Future stop() async { state = WorkflowExecutionState(); - // Reset all node statuses final workflow = _ref.read(activeWorkflowProvider); if (workflow != null) { _ref.read(workflowsProvider.notifier).updateNodes( @@ -135,65 +129,60 @@ class WorkflowExecutionNotifier extends StateNotifier { final node = workflow.nodes[nodeIndex]; - // Update node status to running _ref.read(workflowsProvider.notifier).updateNode( workflow.id, node.copyWith(status: NodeStatus.running), ); - // Simulate API execution await Future.delayed(const Duration(seconds: 1)); - // Determine success or failure final isSuccess = DateTime.now().millisecondsSinceEpoch % 5 != 0; // 80% success rate - // Update node status based on result _ref.read(workflowsProvider.notifier).updateNode( workflow.id, node.copyWith(status: isSuccess ? NodeStatus.success : NodeStatus.failure), ); - // Add this node to executed nodes final updatedExecutedNodeIds = [...state.executedNodeIds, nodeId]; state = state.copyWith( executedNodeIds: updatedExecutedNodeIds, ); // If failed and is conditional, stop execution - if (!isSuccess && node.connectedToIds.isNotEmpty) { - final connections = workflow.connections.where( - (conn) => conn.sourceId == nodeId && conn.isConditional - ).toList(); + // if (!isSuccess && node.connectedToIds.isNotEmpty) { + // final connections = workflow.connections.where( + // (conn) => conn.sourceId == nodeId && conn.isConditional + // ).toList(); - if (connections.isNotEmpty) { - state = state.copyWith(status: WorkflowExecutionStatus.completed); - return; - } - } - - // Get next nodes - final nextNodeIds = node.connectedToIds.isEmpty - ? [] - : workflow.connections - .where((conn) => conn.sourceId == nodeId) - .map((conn) => conn.targetId) - .toList(); - - // If no next nodes, workflow is completed - if (nextNodeIds.isEmpty) { - state = state.copyWith(status: WorkflowExecutionStatus.completed); - return; - } - - // Execute next nodes - for (final nextNodeId in nextNodeIds) { - state = state.copyWith(currentNodeId: nextNodeId); - await _executeNode(nextNodeId); + // if (connections.isNotEmpty) { + // state = state.copyWith(status: WorkflowExecutionStatus.completed); + // return; + // } + // } + + // // Get next nodes + // final nextNodeIds = node.connectedToIds.isEmpty + // ? [] + // : workflow.connections + // .where((conn) => conn.sourceId == nodeId) + // .map((conn) => conn.targetId) + // .toList(); + + // // If no next nodes, workflow is completed + // if (nextNodeIds.isEmpty) { + // state = state.copyWith(status: WorkflowExecutionStatus.completed); + // return; + // } + + // // Execute next nodes + // for (final nextNodeId in nextNodeIds) { + // state = state.copyWith(currentNodeId: nextNodeId); + // await _executeNode(nextNodeId); - // If execution was paused or stopped, break the loop - if (state.status != WorkflowExecutionStatus.running) { - break; - } - } + // // If execution was paused or stopped, break the loop + // if (state.status != WorkflowExecutionStatus.running) { + // break; + // } + // } } } From 5d61efd0ab794383e13fc51a0c3f4f8d3efe7028 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:33:04 +0530 Subject: [PATCH 120/188] refactor: enhance add method in WorkflowsNotifier and comment out removeNode and removeConnection methods (Fix in future) --- .../lib/src/providers/workflows_notifier.dart | 58 +++++++++---------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/packages/api_testing_suite/lib/src/providers/workflows_notifier.dart b/packages/api_testing_suite/lib/src/providers/workflows_notifier.dart index c38efaf68..8a7d86c62 100644 --- a/packages/api_testing_suite/lib/src/providers/workflows_notifier.dart +++ b/packages/api_testing_suite/lib/src/providers/workflows_notifier.dart @@ -12,11 +12,9 @@ class WorkflowsNotifier extends StateNotifier> { WorkflowsNotifier(this._ref) : super([]); - /// Add a new workflow void add() { - final newWorkflow = WorkflowModel.create(); + final newWorkflow = WorkflowModel.create(name: 'New Workflow'); state = [...state, newWorkflow]; - // We need to update the state provider directly through the ref _ref.read(StateProvider((ref) => null).notifier).state = newWorkflow.id; } @@ -85,23 +83,21 @@ class WorkflowsNotifier extends StateNotifier> { ]; } - /// Remove a node from a workflow - void removeNode(String workflowId, String nodeId) { - state = [ - for (final workflow in state) - if (workflow.id == workflowId) - workflow.copyWith( - nodes: workflow.nodes.where((node) => node.id != nodeId).toList(), - connections: workflow.connections.where( - (conn) => conn.sourceId != nodeId && conn.targetId != nodeId - ).toList(), - ) - else - workflow, - ]; - } + // void removeNode(String workflowId, String nodeId) { + // state = [ + // for (final workflow in state) + // if (workflow.id == workflowId) + // workflow.copyWith( + // nodes: workflow.nodes.where((node) => node.id != nodeId).toList(), + // connections: workflow.connections.where( + // (conn) => conn?.sourceId != nodeId && conn.targetId != nodeId + // ).toList(), + // ) + // else + // workflow, + // ]; + // } - /// Add a connection between nodes in a workflow void addConnection(String workflowId, WorkflowConnectionModel connection) { state = [ for (final workflow in state) @@ -128,20 +124,18 @@ class WorkflowsNotifier extends StateNotifier> { ]; } - /// Remove a connection from a workflow - void removeConnection(String workflowId, String connectionId) { - state = [ - for (final workflow in state) - if (workflow.id == workflowId) - workflow.copyWith( - connections: workflow.connections.where((conn) => conn.id != connectionId).toList(), - ) - else - workflow, - ]; - } + // void removeConnection(String workflowId, String connectionId) { + // state = [ + // for (final workflow in state) + // if (workflow.id == workflowId) + // workflow.copyWith( + // connections: workflow.connections.where((conn) => conn.id != connectionId).toList(), + // ) + // else + // workflow, + // ]; + // } - /// Delete a workflow void delete(String workflowId) { state = state.where((workflow) => workflow.id != workflowId).toList(); if (_ref.read(StateProvider((ref) => null)) == workflowId) { From a550dbb7645fea93fd201e8e0ce0ab9bade3764d Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:39:07 +0530 Subject: [PATCH 121/188] feat: add WorkflowBuilderPage, WorkflowCanvas, WorkflowNodeWidget, and WorkflowConnectionWidget for enhanced workflow management --- .../workflow_builder_page.dart | 85 ++++++++ .../src/workflow_builder/workflow_canvas.dart | 206 ++++++++++++++++++ .../workflow_builder/workflow_connection.dart | 106 +++++++++ .../src/workflow_builder/workflow_node.dart | 142 ++++++++++++ .../workflow_builder/workflow_screens.dart | 4 + 5 files changed, 543 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflow_builder_page.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflow_canvas.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflow_connection.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder_page.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder_page.dart new file mode 100644 index 000000000..c308209fe --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder_page.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'workflow_providers.dart'; +import 'workflow_canvas.dart'; +import '../models/workflow_execution_state.dart'; + +class WorkflowBuilderPage extends ConsumerWidget { + const WorkflowBuilderPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final workflowId = ref.watch(currentWorkflowProvider); + final workflowExecutionState = ref.watch(workflowExecutionStateProvider); + + return Scaffold( + appBar: AppBar( + title: const Text('Workflow Builder'), + actions: [ + IconButton( + icon: const Icon(Icons.play_arrow), + onPressed: () { + ref.read(workflowExecutionStateProvider.notifier).state = + workflowExecutionState.copyWith(status: WorkflowExecutionStatus.running); + }, + ), + IconButton( + icon: const Icon(Icons.pause), + onPressed: () { + ref.read(workflowExecutionStateProvider.notifier).state = + workflowExecutionState.copyWith(status: WorkflowExecutionStatus.paused); + }, + ), + IconButton( + icon: const Icon(Icons.stop), + onPressed: () { + ref.read(workflowExecutionStateProvider.notifier).state = + workflowExecutionState.copyWith(status: WorkflowExecutionStatus.idle); + }, + ), + ], + ), + body: Column( + children: [ + Expanded( + child: WorkflowCanvas( + workflowId: workflowId, + ), + ), + Container( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + ElevatedButton( + onPressed: () { + ref.read(workflowsNotifierProvider.notifier).addWorkflow('New Workflow'); + }, + child: const Text('New Workflow'), + ), + const SizedBox(width: 16), + ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Add Node'), + content: const Text('Select node type'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('Cancel'), + ), + ], + ), + ); + }, + child: const Text('Add Node'), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_canvas.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_canvas.dart new file mode 100644 index 000000000..3afe9fc42 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_canvas.dart @@ -0,0 +1,206 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:uuid/uuid.dart'; +import '../models/workflow_node_model.dart'; +import '../models/workflow_connection_model.dart'; +import '../models/workflow_connection.dart'; +import 'workflow_providers.dart'; + +class WorkflowCanvas extends ConsumerStatefulWidget { + final String? workflowId; + + const WorkflowCanvas({ + super.key, + required this.workflowId, + }); + + @override + ConsumerState createState() => _WorkflowCanvasState(); +} + +class _WorkflowCanvasState extends ConsumerState { + late Offset _startPosition; + bool _isDragging = false; + WorkflowConnectionModel? _tempConnection; + + @override + Widget build(BuildContext context) { + final workflows = ref.watch(workflowsNotifierProvider); + final currentWorkflow = workflows.firstWhere( + (workflow) => workflow.id == widget.workflowId, + orElse: () => throw Exception('Workflow not found'), + ); + + final nodePositions = Map.fromEntries( + currentWorkflow.nodes.map((node) => MapEntry(node.id, node.position)), + ); + + return GestureDetector( + onPanStart: (details) { + _startPosition = details.localPosition; + }, + onPanUpdate: (details) { + setState(() { + _isDragging = true; + _tempConnection = WorkflowConnectionModel( + id: const Uuid().v4(), + sourceId: '', + targetId: '', + workflowId: widget.workflowId!, + position: details.localPosition, + ); + }); + }, + onPanEnd: (details) { + setState(() { + _isDragging = false; + _tempConnection = null; + }); + }, + child: Stack( + children: [ + // Background grid + CustomPaint( + painter: _GridPainter(), + size: const Size(double.infinity, double.infinity), + ), + // Nodes + ...currentWorkflow.nodes.map((node) => Positioned( + left: node.position.dx, + top: node.position.dy, + child: WorkflowNodeWidget( + node: node, + onNodeSelected: (nodeId) { + ref.read(workflowsNotifierProvider.notifier).selectNode(nodeId); + }, + onNodeMoved: (nodeId, newPosition) { + // TODO: Implement node movement + }, + ), + )), + // Connections + CustomPaint( + painter: _ConnectionPainter( + connections: currentWorkflow.connections, + tempConnection: _tempConnection, + nodePositions: nodePositions, + ), + size: const Size(double.infinity, double.infinity), + ), + ], + ), + ); + } +} + +class _GridPainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = Colors.grey.withOpacity(0.1) + ..strokeWidth = 1; + + for (double i = 0; i < size.width; i += 20) { + canvas.drawLine(Offset(i, 0), Offset(i, size.height), paint); + } + + for (double i = 0; i < size.height; i += 20) { + canvas.drawLine(Offset(0, i), Offset(size.width, i), paint); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} + +class _ConnectionPainter extends CustomPainter { + final List connections; + final WorkflowConnectionModel? tempConnection; + final Map nodePositions; + final Color color; + final double width; + + _ConnectionPainter({ + required this.connections, + this.tempConnection, + required this.nodePositions, + this.color = Colors.blue, + this.width = 2.0, + }); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = color + ..strokeWidth = width + ..style = PaintingStyle.stroke; + + for (var connection in connections) { + if (nodePositions.containsKey(connection.sourceId) && + nodePositions.containsKey(connection.targetId)) { + final start = nodePositions[connection.sourceId]!; + final end = nodePositions[connection.targetId]!; + canvas.drawLine(start, end, paint); + } + } + + if (tempConnection != null) { + final start = tempConnection!.position; + final targetId = tempConnection!.targetId; + + if (targetId.isNotEmpty && nodePositions.containsKey(targetId)) { + final end = nodePositions[targetId]!; + canvas.drawLine(start, end, paint); + } + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => true; +} + +class WorkflowNodeWidget extends StatelessWidget { + final WorkflowNodeModel node; + final Function(String) onNodeSelected; + final Function(String, Offset) onNodeMoved; + + WorkflowNodeWidget({ + required this.node, + required this.onNodeSelected, + required this.onNodeMoved, + }); + + @override + Widget build(BuildContext context) { + return Draggable( + maxSimultaneousDrags: 1, + feedback: Container( + width: 100, + height: 50, + color: Colors.blue.withOpacity(0.5), + child: Center( + child: Text(node.id), + ), + ), + childWhenDragging: Container( + width: 100, + height: 50, + color: Colors.grey, + child: Center( + child: Text(node.id), + ), + ), + onDragEnd: (details) { + onNodeMoved(node.id, details.offset); + }, + child: Container( + width: 100, + height: 50, + color: Colors.blue, + child: Center( + child: Text(node.id), + ), + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_connection.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_connection.dart new file mode 100644 index 000000000..c726b7a57 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_connection.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import '../models/workflow_connection_model.dart'; + +class WorkflowConnectionWidget extends StatelessWidget { + final WorkflowConnectionModel connection; + final Offset sourcePosition; + final Offset targetPosition; + final VoidCallback onTap; + final VoidCallback onRemove; + + WorkflowConnectionWidget({ + required this.connection, + required this.sourcePosition, + required this.targetPosition, + required this.onTap, + required this.onRemove, + }); + + @override + Widget build(BuildContext context) { + return Positioned( + left: 0, + top: 0, + child: GestureDetector( + onTap: onTap, + child: CustomPaint( + painter: ConnectionPainter( + source: sourcePosition, + target: targetPosition, + isConditional: connection.isConditional, + ), + size: Size.infinite, + ), + ), + ); + } +} + +class ConnectionPainter extends CustomPainter { + final Offset source; + final Offset target; + final bool isConditional; + + ConnectionPainter({ + required this.source, + required this.target, + required this.isConditional, + }); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = Colors.blue + ..strokeWidth = 2 + ..style = PaintingStyle.stroke; + + canvas.drawLine(source, target, paint); + + final arrowSize = 10.0; + final arrowPath = Path() + ..moveTo(target.dx, target.dy) + ..lineTo( + target.dx - arrowSize * 1.5 * (target.dx > source.dx ? 1 : -1), + target.dy - arrowSize, + ) + ..lineTo( + target.dx - arrowSize * 1.5 * (target.dx > source.dx ? 1 : -1), + target.dy + arrowSize, + ) + ..close(); + + canvas.drawPath(arrowPath, paint..style = PaintingStyle.fill); + + if (isConditional) { + final conditionPath = Path() + ..moveTo( + (source.dx + target.dx) / 2, + (source.dy + target.dy) / 2 - 10, + ) + ..lineTo( + (source.dx + target.dx) / 2 + 10, + (source.dy + target.dy) / 2 + 10, + ) + ..lineTo( + (source.dx + target.dx) / 2 - 10, + (source.dy + target.dy) / 2 + 10, + ) + ..close(); + + canvas.drawPath( + conditionPath, + Paint() + ..color = Colors.yellow + ..style = PaintingStyle.fill, + ); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return oldDelegate is ConnectionPainter && + (oldDelegate.source != source || + oldDelegate.target != target || + oldDelegate.isConditional != isConditional); + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart new file mode 100644 index 000000000..7cf849b69 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart @@ -0,0 +1,142 @@ +// import 'package:apidash_core/apidash_core.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter_riverpod/flutter_riverpod.dart'; +// import '../models/node_status.dart'; +// import '../models/workflow_node_model.dart'; +// import '../models/workflow_connection_model.dart'; +// import 'workflow_providers.dart'; + +// /// Widget that displays a workflow node +// @immutable +// class WorkflowNodeWidget extends StatefulWidget { +// final WorkflowNodeModel node; +// final double scale; +// final Function(double, double) onDragUpdate; +// final Function(Offset) onConnect; + +// const WorkflowNodeWidget({ +// required this.node, +// required this.scale, +// required this.onDragUpdate, +// required this.onConnect, +// Key? key, +// }) : super(key: key); + +// @override +// State createState() => _WorkflowNodeWidgetState(); +// } + +// class _WorkflowNodeWidgetState extends State { +// Offset _dragOffset = Offset.zero; +// bool _isDragging = false; + +// @override +// Widget build(BuildContext context) { +// return Positioned( +// left: widget.node.position.dx * widget.scale + _dragOffset.dx, +// top: widget.node.position.dy * widget.scale + _dragOffset.dy, +// child: GestureDetector( +// onPanStart: (details) { +// setState(() { +// _isDragging = true; +// _dragOffset = details.localPosition; +// }); +// }, +// onPanUpdate: (details) { +// if (_isDragging) { +// setState(() { +// _dragOffset = details.localPosition; +// }); +// widget.onDragUpdate(_dragOffset.dx, _dragOffset.dy); +// } +// }, +// onPanEnd: (details) { +// setState(() { +// _isDragging = false; +// _dragOffset = Offset.zero; +// }); +// }, +// child: MouseRegion( +// onEnter: (_) { +// setState(() { +// _isDragging = false; +// _dragOffset = Offset.zero; +// }); +// }, +// child: Container( +// width: 200 * widget.scale, +// height: 100 * widget.scale, +// decoration: BoxDecoration( +// color: Colors.white, +// border: Border.all( +// color: widget.node.status.color, +// width: 2 * widget.scale, +// ), +// borderRadius: BorderRadius.circular(8 * widget.scale), +// boxShadow: [ +// BoxShadow( +// color: Colors.black.withOpacity(0.1), +// blurRadius: 4 * widget.scale, +// offset: Offset(0, 2 * widget.scale), +// ), +// ], +// ), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Padding( +// padding: EdgeInsets.all(8 * widget.scale), +// child: Row( +// children: [ +// Expanded( +// child: Text( +// widget.node.nodeType.toString(), +// style: TextStyle( +// fontSize: 16 * widget.scale, +// fontWeight: FontWeight.bold, +// ), +// ), +// ), +// IconButton( +// icon: Icon(Icons.delete, size: 20 * widget.scale), +// onPressed: () { +// context.read(workflowsNotifierProvider.notifier).removeNode(widget.node.id); +// }, +// ), +// ], +// ), +// ), +// Padding( +// padding: EdgeInsets.all(8 * widget.scale), +// child: Text( +// widget.node.nodeData.toString(), +// style: TextStyle(fontSize: 12 * widget.scale), +// ), +// ), +// Padding( +// padding: EdgeInsets.all(8 * widget.scale), +// child: Row( +// children: [ +// IconButton( +// icon: Icon(Icons.edit, size: 20 * widget.scale), +// onPressed: () { +// context.read(workflowsNotifierProvider.notifier).selectNode(widget.node.id); +// }, +// ), +// IconButton( +// icon: Icon(Icons.connect_without_contact, size: 20 * widget.scale), +// onPressed: () { +// widget.onConnect(widget.node.position); +// }, +// ), +// ], +// ), +// ), +// ], +// ), +// ), +// ), +// ), +// ); +// } +// } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart new file mode 100644 index 000000000..fdc69e8e5 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart @@ -0,0 +1,4 @@ +export 'workflow_builder_page.dart'; +export 'workflow_canvas.dart'; +export 'workflow_node.dart'; +export 'workflow_connection.dart'; From c95e061452f720330aaddcf76846a9d1fd050846 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:40:23 +0530 Subject: [PATCH 122/188] feat: implement WorkflowsNotifier with methods for managing workflows and nodes, comment methods giving error --- .../workflow_builder/workflow_providers.dart | 14 ++ .../workflow_builder/workflows_notifier.dart | 125 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflow_providers.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflows_notifier.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_providers.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_providers.dart new file mode 100644 index 000000000..256f829a8 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_providers.dart @@ -0,0 +1,14 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../models/workflow_model.dart'; +import '../models/workflow_execution_state.dart'; +import 'workflows_notifier.dart'; + +final workflowsNotifierProvider = StateNotifierProvider>((ref) { + return WorkflowsNotifier(); +}); + +final currentWorkflowProvider = StateProvider((ref) => null); + +final workflowExecutionStateProvider = StateProvider((ref) { + return const WorkflowExecutionState(); +}); diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflows_notifier.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflows_notifier.dart new file mode 100644 index 000000000..af96f2937 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflows_notifier.dart @@ -0,0 +1,125 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:uuid/uuid.dart'; + +import '../models/workflow_node_model.dart'; +import '../models/workflow_connection_model.dart'; +import '../models/workflow_model.dart'; +import '../models/node_status.dart'; + +class WorkflowsNotifier extends StateNotifier> { + WorkflowsNotifier() : super([]); + + void addWorkflow(String name) { + state = [ + ...state, + WorkflowModel( + id: const Uuid().v4(), + name: name, + nodes: [], + connections: [], + ), + ]; + } + + void removeWorkflow(String id) { + state = state.where((workflow) => workflow.id != id).toList(); + } + + void addNode(String workflowId, WorkflowNodeModel node) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: [...workflow.nodes, node], + ); + } + return workflow; + }).toList(); + } + + // void removeNode(String nodeId) { + // state = state.map((workflow) { + // // Find the node to remove + // final nodeToRemove = workflow.nodes.firstWhere( + // (node) => node.id == nodeId, + // orElse: () => throw Exception('Node not found'), + // ); + + // // Remove any connections involving this node + // final updatedConnections = workflow.connections.where( + // (conn) => conn.sourceId != nodeId && conn.targetId != nodeId, + // ).toList(); + + // return workflow.copyWith( + // nodes: workflow.nodes.where((node) => node.id != nodeId).toList(), + // connections: updatedConnections, + // ); + // }).toList(); + // } + + void updateNodePosition(String nodeId, Offset position) { + state = state.map((workflow) { + return workflow.copyWith( + nodes: workflow.nodes.map((node) { + if (node.id == nodeId) { + return node.copyWith(position: position); + } + return node; + }).toList(), + ); + }).toList(); + } + + void selectNode(String nodeId) { + // Implementation for selecting a node + // This could update a selected node state or trigger an action + } + + // void addConnection(String workflowId, String sourceId, String targetId) { + // if (sourceId == targetId) return; // Prevent self-connections + + // state = state.map((workflow) { + // if (workflow.id == workflowId) { + // // Check if connection already exists + // final connectionExists = workflow.connections.any( + // (conn) => conn.sourceId == sourceId && conn.targetId == targetId, + // ); + + // if (!connectionExists) { + // final newConnection = WorkflowConnectionModel.create( + // sourceId: sourceId, + // targetId: targetId, + // workflowId: workflowId, + // position: Offset.zero, // This will be updated based on node positions + // ); + + // return workflow.copyWith( + // connections: [...workflow.connections, newConnection], + // ); + // } + // } + // return workflow; + // }).toList(); + // } + + // void removeConnection(String connectionId) { + // state = state.map((workflow) { + // return workflow.copyWith( + // connections: workflow.connections.where((conn) => conn.id != connectionId).toList(), + // ); + // }).toList(); + // } + + void updateNodeStatus(String nodeId, NodeStatus status) { + state = state.map((workflow) { + return workflow.copyWith( + nodes: workflow.nodes.map((node) { + if (node.id == nodeId) { + return node.copyWith(status: status); + } + return node; + }).toList(), + ); + }).toList(); + } +} From acaf93ad4d71e8ef8b0b6da902429a23621a36a3 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:40:49 +0530 Subject: [PATCH 123/188] feat: add NodeStatus enum with label and color getters for workflow management --- .../workflow_builder/models/node_status.dart | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/models/node_status.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/node_status.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/node_status.dart new file mode 100644 index 000000000..8661470f5 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/node_status.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +enum NodeStatus { + pending, + running, + success, + error, + idle, + failure; + + String get label { + switch (this) { + case NodeStatus.pending: + return 'Pending'; + case NodeStatus.running: + return 'Running'; + case NodeStatus.success: + return 'Success'; + case NodeStatus.error: + return 'Error'; + case NodeStatus.idle: + return 'Idle'; + case NodeStatus.failure: + return 'Failure'; + } + } + + Color get color { + switch (this) { + case NodeStatus.pending: + return Colors.grey; + case NodeStatus.running: + return Colors.blue; + case NodeStatus.success: + return Colors.green; + case NodeStatus.error: + return Colors.red; + case NodeStatus.idle: + return Colors.grey; + case NodeStatus.failure: + return Colors.red; + } + } +} From 691374c86c404d3808f96df8950127abf287c058 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:41:09 +0530 Subject: [PATCH 124/188] feat: add initial tests for WorkflowCanvas and WorkflowNode rendering (template only) --- .../lib/src/tests/workflow_builder_test.dart | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/tests/workflow_builder_test.dart diff --git a/packages/api_testing_suite/lib/src/tests/workflow_builder_test.dart b/packages/api_testing_suite/lib/src/tests/workflow_builder_test.dart new file mode 100644 index 000000000..9606a0c03 --- /dev/null +++ b/packages/api_testing_suite/lib/src/tests/workflow_builder_test.dart @@ -0,0 +1,15 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:api_testing_suite/src/workflow_builder/workflow_canvas.dart'; +import 'package:api_testing_suite/src/workflow_builder/workflow_node.dart'; + +void main() { + group('Workflow Builder Tests', () { + testWidgets('WorkflowCanvas basic structure', (tester) async { + // TODO: Implement basic workflow canvas test + }); + + testWidgets('WorkflowNode rendering', (tester) async { + // TODO: Implement workflow node rendering test + }); + }); +} From 28cebf82025907daf3e32b0b525db09750f94e52 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:42:17 +0530 Subject: [PATCH 125/188] feat: add StressTestConfig and StressTestSummary models for stress testing functionality --- .../models/stress_test_config.dart | 20 +++++++++++++++++++ .../models/stress_test_summary.dart | 18 +++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/stress_test/models/stress_test_config.dart create mode 100644 packages/api_testing_suite/lib/src/stress_test/models/stress_test_summary.dart diff --git a/packages/api_testing_suite/lib/src/stress_test/models/stress_test_config.dart b/packages/api_testing_suite/lib/src/stress_test/models/stress_test_config.dart new file mode 100644 index 000000000..ff30a0132 --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/models/stress_test_config.dart @@ -0,0 +1,20 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:apidash_core/apidash_core.dart'; + +part 'stress_test_config.freezed.dart'; +part 'stress_test_config.g.dart'; + +@freezed +class StressTestConfig with _$StressTestConfig { + const factory StressTestConfig({ + required String url, + required String method, + Map? headers, + dynamic body, + required int concurrentRequests, + Duration? timeout, + @Default(false) bool useIsolates, + }) = _StressTestConfig; + + factory StressTestConfig.fromJson(Map json) => _$StressTestConfigFromJson(json); +} diff --git a/packages/api_testing_suite/lib/src/stress_test/models/stress_test_summary.dart b/packages/api_testing_suite/lib/src/stress_test/models/stress_test_summary.dart new file mode 100644 index 000000000..fcb4444d6 --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/models/stress_test_summary.dart @@ -0,0 +1,18 @@ +import 'package:api_testing_suite/src/stress_test/models/stress_test_models.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'stress_test_summary.freezed.dart'; +part 'stress_test_summary.g.dart'; + +@freezed +class StressTestSummary with _$StressTestSummary { + const factory StressTestSummary({ + required List results, + required Duration totalDuration, + required double avgResponseTime, + required int successCount, + required int failureCount, + }) = _StressTestSummary; + + factory StressTestSummary.fromJson(Map json) => _$StressTestSummaryFromJson(json); +} From a3930754d7810eefb0cfd493de0d30c7f29aeba3 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:42:57 +0530 Subject: [PATCH 126/188] feat: add ApiRequestResult and IsolateMessage models for stress testing functionality --- .../stress_test/models/api_request_result.dart | 16 ++++++++++++++++ .../src/stress_test/models/isolate_message.dart | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/stress_test/models/api_request_result.dart create mode 100644 packages/api_testing_suite/lib/src/stress_test/models/isolate_message.dart diff --git a/packages/api_testing_suite/lib/src/stress_test/models/api_request_result.dart b/packages/api_testing_suite/lib/src/stress_test/models/api_request_result.dart new file mode 100644 index 000000000..2cbf40251 --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/models/api_request_result.dart @@ -0,0 +1,16 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'api_request_result.freezed.dart'; +part 'api_request_result.g.dart'; + +@freezed +class ApiRequestResult with _$ApiRequestResult { + const factory ApiRequestResult({ + required int statusCode, + required String body, + required Duration duration, + String? error, + }) = _ApiRequestResult; + + factory ApiRequestResult.fromJson(Map json) => _$ApiRequestResultFromJson(json); +} diff --git a/packages/api_testing_suite/lib/src/stress_test/models/isolate_message.dart b/packages/api_testing_suite/lib/src/stress_test/models/isolate_message.dart new file mode 100644 index 000000000..61bc33121 --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/models/isolate_message.dart @@ -0,0 +1,17 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'isolate_message.freezed.dart'; +part 'isolate_message.g.dart'; + +@freezed +class IsolateMessage with _$IsolateMessage { + const factory IsolateMessage({ + required String url, + required String method, + Map? headers, + dynamic body, + Duration? timeout, + }) = _IsolateMessage; + + factory IsolateMessage.fromJson(Map json) => _$IsolateMessageFromJson(json); +} From 8cfce627a3c704c5ed986e110be48bb523586c2a Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:43:51 +0530 Subject: [PATCH 127/188] feat: enhance StressTestSummary with response time metrics and success rates --- .../stress_test/stress_test_summary.dart | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/models/stress_test/stress_test_summary.dart b/lib/models/stress_test/stress_test_summary.dart index 3beaa3a18..8c211d5ba 100644 --- a/lib/models/stress_test/stress_test_summary.dart +++ b/lib/models/stress_test/stress_test_summary.dart @@ -7,6 +7,8 @@ part 'stress_test_summary.g.dart'; @freezed class StressTestSummary with _$StressTestSummary { + const StressTestSummary._(); + const factory StressTestSummary({ required List results, required Duration totalDuration, @@ -16,4 +18,31 @@ class StressTestSummary with _$StressTestSummary { }) = _StressTestSummary; factory StressTestSummary.fromJson(Map json) => _$StressTestSummaryFromJson(json); + + double get successRate => results.isEmpty ? 0 : (successCount / results.length) * 100; + + double get failureRate => results.isEmpty ? 0 : (failureCount / results.length) * 100; + + Duration get minResponseTime { + if (results.isEmpty) return Duration.zero; + return results.map((r) => r.duration).reduce((a, b) => a < b ? a : b); + } + + Duration get maxResponseTime { + if (results.isEmpty) return Duration.zero; + return results.map((r) => r.duration).reduce((a, b) => a > b ? a : b); + } + + Duration get medianResponseTime { + if (results.isEmpty) return Duration.zero; + final sortedDurations = results.map((r) => r.duration).toList()..sort(); + final middle = sortedDurations.length ~/ 2; + if (sortedDurations.length.isOdd) { + return sortedDurations[middle]; + } + return Duration(microseconds: ( + sortedDurations[middle - 1].inMicroseconds + + sortedDurations[middle].inMicroseconds + ) ~/ 2); + } } From 8f549cb9cbf3a4325437abbb31271397cba1d30e Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:44:00 +0530 Subject: [PATCH 128/188] feat: add stress test models exports for configuration, request results, and summary --- .../lib/src/stress_test/models/stress_test_models.dart | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/stress_test/models/stress_test_models.dart diff --git a/packages/api_testing_suite/lib/src/stress_test/models/stress_test_models.dart b/packages/api_testing_suite/lib/src/stress_test/models/stress_test_models.dart new file mode 100644 index 000000000..10c1bcacd --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/models/stress_test_models.dart @@ -0,0 +1,3 @@ +export 'stress_test_config.dart'; +export 'api_request_result.dart'; +export 'stress_test_summary.dart'; From 43fd884f01fc41dc7c83f9c1960e93921322dcd8 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:44:31 +0530 Subject: [PATCH 129/188] feat: implement StressTestService for executing parallel API tests with isolates --- .../services/stress_test_service.dart | 283 ++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart diff --git a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart new file mode 100644 index 000000000..6bba4af78 --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart @@ -0,0 +1,283 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:isolate'; + +import 'package:http/http.dart' as http; + +import '../models/api_request_result.dart'; +import '../models/stress_test_config.dart'; +import '../models/stress_test_summary.dart'; +import '../models/isolate_message.dart'; + +// Stress Test Service Class +class StressTestService { + + static Future _executeRequest({ + required String url, + required String method, + Map? headers, + dynamic body, + Duration? timeout, + }) async { + final stopwatch = Stopwatch()..start(); + final client = http.Client(); + + try { + http.Response response; + final defaultTimeout = const Duration(seconds: 30); + + try { + switch (method.toUpperCase()) { + case 'GET': + response = await client.get( + Uri.parse(url), + headers: headers, + ).timeout(timeout ?? defaultTimeout); + break; + case 'POST': + response = await client.post( + Uri.parse(url), + headers: headers, + body: body is String ? body : jsonEncode(body), + ).timeout(timeout ?? defaultTimeout); + break; + case 'PUT': + response = await client.put( + Uri.parse(url), + headers: headers, + body: body is String ? body : jsonEncode(body), + ).timeout(timeout ?? defaultTimeout); + break; + case 'DELETE': + response = await client.delete( + Uri.parse(url), + headers: headers, + body: body is String ? body : jsonEncode(body), + ).timeout(timeout ?? defaultTimeout); + break; + case 'PATCH': + response = await client.patch( + Uri.parse(url), + headers: headers, + body: body is String ? body : jsonEncode(body), + ).timeout(timeout ?? defaultTimeout); + break; + default: + throw Exception('Unsupported HTTP method: $method'); + } + + stopwatch.stop(); + return ApiRequestResult( + statusCode: response.statusCode, + body: response.body, + duration: stopwatch.elapsed, + error: null, + ); + } on TimeoutException { + stopwatch.stop(); + return ApiRequestResult( + statusCode: -1, + body: '', + duration: stopwatch.elapsed, + error: 'Request timed out after ${(timeout ?? defaultTimeout).inSeconds} seconds', + ); + } on http.ClientException catch (e) { + stopwatch.stop(); + return ApiRequestResult( + statusCode: -1, + body: '', + duration: stopwatch.elapsed, + error: 'HTTP client error: ${e.message}', + ); + } on FormatException catch (e) { + stopwatch.stop(); + return ApiRequestResult( + statusCode: -1, + body: '', + duration: stopwatch.elapsed, + error: 'Format error: ${e.message}', + ); + } catch (e) { + stopwatch.stop(); + return ApiRequestResult( + statusCode: -1, + body: '', + duration: stopwatch.elapsed, + error: e.toString(), + ); + } + } finally { + client.close(); + } + } + + /// Isolate worker function + static Future _isolateWorker(SendPort sendPort) async { + final receivePort = ReceivePort(); + sendPort.send(receivePort.sendPort); + + await for (final message in receivePort) { + if (message is IsolateMessage) { + final result = await _executeRequest( + url: message.url, + method: message.method, + headers: message.headers, + body: message.body, + timeout: message.timeout, + ); + sendPort.send(result); + } else if (message == 'close') { + break; + } + } + + Isolate.exit(); + } + + /// Execute a parallel API test + static Future runTest(StressTestConfig config) async { + final totalStopwatch = Stopwatch()..start(); + final List results = []; + + if (config.useIsolates) { + final isolates = []; + final receivePorts = []; + final sendPorts = []; + final completers = >[]; + + try { + for (var i = 0; i < config.concurrentRequests; i++) { + final receivePort = ReceivePort(); + final completer = Completer(); + + final isolate = await Isolate.spawn( + _isolateWorker, + receivePort.sendPort, + errorsAreFatal: false, + onExit: receivePort.sendPort, + onError: receivePort.sendPort, + ); + + isolates.add(isolate); + receivePorts.add(receivePort); + completers.add(completer); + + receivePort.listen((message) { + if (message is SendPort) { + sendPorts.add(message); + if (sendPorts.length == config.concurrentRequests) { + for (var j = 0; j < config.concurrentRequests; j++) { + sendPorts[j].send(IsolateMessage( + url: config.url, + method: config.method, + headers: config.headers, + body: config.body, + timeout: config.timeout, + )); + } + } + } else if (message is ApiRequestResult) { + if (!completer.isCompleted) { + completer.complete(message); + } + } else if (message is List && message.length >= 2) { + if (!completer.isCompleted) { + completer.complete(ApiRequestResult( + statusCode: -1, + body: '', + duration: Duration.zero, + error: 'Isolate error: ${message[0]}', + )); + } + } + }); + } + + for (var completer in completers) { + try { + final result = await completer.future.timeout( + (config.timeout ?? const Duration(seconds: 30)) + const Duration(seconds: 10) + ); + results.add(result); + } on TimeoutException { + results.add(ApiRequestResult( + statusCode: -1, + body: '', + duration: Duration.zero, + error: 'Isolate communication timed out', + )); + } + } + } finally { + for (var i = 0; i < isolates.length; i++) { + try { + if (sendPorts.length > i) { + sendPorts[i].send('close'); + } + isolates[i].kill(priority: Isolate.immediate); + } catch (_) { /*We need to ignore once termination is done*/ } + } + + for (var port in receivePorts) { + port.close(); + } + } + } else { + final futures = >[]; + + for (int i = 0; i < config.concurrentRequests; i++) { + futures.add(_executeRequest( + url: config.url, + method: config.method, + headers: config.headers, + body: config.body, + timeout: config.timeout, + )); + } + + try { + final overallTimeout = (config.timeout ?? const Duration(seconds: 30)) + const Duration(seconds: 10); + final futureResults = await Future.wait(futures).timeout(overallTimeout); + results.addAll(futureResults); + } on TimeoutException { + final completedResults = results.length; + final remainingCount = config.concurrentRequests - completedResults; + + for (int i = 0; i < remainingCount; i++) { + results.add(ApiRequestResult( + statusCode: -1, + body: '', + duration: Duration.zero, + error: 'Operation timed out', + )); + } + } + } + + totalStopwatch.stop(); + + final successCount = results.where((r) => + r.statusCode >= 200 && r.statusCode < 300 && r.error == null + ).length; + + final failureCount = results.length - successCount; + + final validResults = results.where((r) => r.error == null); + final totalResponseTime = validResults.fold( + 0, + (prev, result) => prev + result.duration.inMicroseconds + ); + + final avgResponseTime = validResults.isEmpty + ? 0.0 + : totalResponseTime / validResults.length / 1000; + + return StressTestSummary( + results: results, + totalDuration: totalStopwatch.elapsed, + avgResponseTime: avgResponseTime, + successCount: successCount, + failureCount: failureCount, + ); + } +} From 2123922c7ae91c58e350d1ff14becd068b7f0873 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:45:49 +0530 Subject: [PATCH 130/188] feat: add StressTestResultCard widget for displaying stress test results summary --- .../widgets/stress_test_result_card.dart | 291 ++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/stress_test/widgets/stress_test_result_card.dart diff --git a/packages/api_testing_suite/lib/src/stress_test/widgets/stress_test_result_card.dart b/packages/api_testing_suite/lib/src/stress_test/widgets/stress_test_result_card.dart new file mode 100644 index 000000000..8713b6b79 --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/widgets/stress_test_result_card.dart @@ -0,0 +1,291 @@ +import 'package:api_testing_suite/src/stress_test/models/stress_test_models.dart'; +import 'package:flutter/material.dart'; + +class StressTestResultCard extends StatelessWidget { + final StressTestSummary summary; + + const StressTestResultCard({Key? key, required this.summary}) : super(key: key); + + @override + Widget build(BuildContext context) { + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + final successColor = isDarkMode ? Colors.green[400] : Colors.green[700]; + final errorColor = isDarkMode ? Colors.red[400] : Colors.red[700]; + final cardColor = isDarkMode ? Colors.grey[850] : Colors.grey[200]; + final textColor = isDarkMode ? Colors.grey[400] : Colors.grey[800]; + + return Card( + color: cardColor, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: _buildSummaryItem( + context, + 'Total Requests', + '${summary.results.length}', + Icons.http, + textColor, + ), + ), + Expanded( + child: _buildSummaryItem( + context, + 'Total Duration', + '${(summary.totalDuration.inMilliseconds / 1000).toStringAsFixed(2)}s', + Icons.timer, + textColor, + ), + ), + Expanded( + child: _buildSummaryItem( + context, + 'Avg Response Time', + '${summary.avgResponseTime.toStringAsFixed(2)}ms', + Icons.speed, + textColor, + ), + ), + ], + ), + const SizedBox(height: 16), + Row( + children: [ + Expanded( + child: _buildStatusItem( + context, + 'Success', + summary.successCount, + summary.results.length, + successColor!, + ), + ), + Expanded( + child: _buildStatusItem( + context, + 'Failed', + summary.failureCount, + summary.results.length, + errorColor!, + ), + ), + ], + ), + const SizedBox(height: 24), + Text( + 'Response Time Distribution', + style: Theme.of(context).textTheme.titleSmall, + ), + const SizedBox(height: 8), + _buildResponseTimeDistribution(context, summary.results), + const SizedBox(height: 16), + Text( + 'Status Code Distribution', + style: Theme.of(context).textTheme.titleSmall, + ), + const SizedBox(height: 8), + _buildStatusCodeDistribution(context, summary.results), + ], + ), + ), + ); + } + + Widget _buildSummaryItem( + BuildContext context, + String label, + String value, + IconData icon, + Color? textColor, + ) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon(icon, size: 14, color: textColor), + const SizedBox(width: 4), + Text( + label, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: textColor, + ), + ), + ], + ), + const SizedBox(height: 4), + Text( + value, + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ); + } + + Widget _buildStatusItem( + BuildContext context, + String label, + int count, + int total, + Color color, + ) { + final percentage = total > 0 ? (count / total * 100).toStringAsFixed(1) : '0.0'; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 4), + Row( + children: [ + Text( + '$count', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: color, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 4), + Text( + '($percentage%)', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: color, + ), + ), + ], + ), + const SizedBox(height: 4), + ClipRRect( + borderRadius: BorderRadius.circular(2), + child: LinearProgressIndicator( + value: total > 0 ? count / total : 0, + backgroundColor: Colors.grey[400], + valueColor: AlwaysStoppedAnimation(color), + minHeight: 4, + ), + ), + ], + ); + } + + Widget _buildResponseTimeDistribution(BuildContext context, List results) { + final successfulResults = results.where((r) => r.error == null).toList(); + if (successfulResults.isEmpty) { + return const Text('No successful responses to analyze'); + } + + successfulResults.sort((a, b) => a.duration.compareTo(b.duration)); + + final p50Index = (successfulResults.length * 0.5).floor(); + final p75Index = (successfulResults.length * 0.75).floor(); + final p90Index = (successfulResults.length * 0.9).floor(); + final p95Index = (successfulResults.length * 0.95).floor(); + final p99Index = (successfulResults.length * 0.99).floor(); + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Column( + children: [ + _buildPercentileRow(context, 'Min', + '${successfulResults.first.duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P50', + '${successfulResults[p50Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P75', + '${successfulResults[p75Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P90', + '${successfulResults[p90Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P95', + '${successfulResults[p95Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P99', + '${successfulResults[p99Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'Max', + '${successfulResults.last.duration.inMilliseconds} ms'), + ], + ), + ); + } + + Widget _buildPercentileRow(BuildContext context, String percentile, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2.0), + child: Row( + children: [ + SizedBox( + width: 40, + child: Text( + percentile, + style: Theme.of(context).textTheme.bodySmall, + ), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + value, + style: Theme.of(context).textTheme.bodyMedium, + ), + ), + ], + ), + ); + } + + Widget _buildStatusCodeDistribution(BuildContext context, List results) { + final statusCodeCounts = {}; + for (final result in results) { + if (result.statusCode != -1) { // Skip errors + statusCodeCounts[result.statusCode] = + (statusCodeCounts[result.statusCode] ?? 0) + 1; + } + } + + if (statusCodeCounts.isEmpty) { + return const Text('No status codes to analyze'); + } + + final sortedEntries = statusCodeCounts.entries.toList() + ..sort((a, b) => a.key.compareTo(b.key)); + + return Wrap( + spacing: 8, + runSpacing: 8, + children: sortedEntries.map((entry) { + final statusCode = entry.key; + final count = entry.value; + final isSuccess = statusCode >= 200 && statusCode < 300; + final isRedirect = statusCode >= 300 && statusCode < 400; + final isClientError = statusCode >= 400 && statusCode < 500; + final isServerError = statusCode >= 500; + + Color chipColor; + if (isSuccess) { + chipColor = Colors.green; + } else if (isRedirect) { + chipColor = Colors.blue; + } else if (isClientError) { + chipColor = Colors.orange; + } else if (isServerError) { + chipColor = Colors.red; + } else { + chipColor = Colors.grey; + } + + return Chip( + label: Text('$statusCode ($count)'), + backgroundColor: chipColor.withOpacity(0.2), + labelStyle: TextStyle(color: chipColor), + ); + }).toList(), + ); + } +} From 547e287f3f84fdeed070a66ecfb6869ff184ca4d Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:46:45 +0530 Subject: [PATCH 131/188] feat: add FakeDataConfig model for managing fake data configuration --- .../models/fake_data_config.dart | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/fake_data_provider/models/fake_data_config.dart diff --git a/packages/api_testing_suite/lib/src/fake_data_provider/models/fake_data_config.dart b/packages/api_testing_suite/lib/src/fake_data_provider/models/fake_data_config.dart new file mode 100644 index 000000000..a4f9498dc --- /dev/null +++ b/packages/api_testing_suite/lib/src/fake_data_provider/models/fake_data_config.dart @@ -0,0 +1,20 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'fake_data_config.g.dart'; + +@JsonSerializable() +class FakeDataConfig { + final List tags; + final int iterations; + final bool includeTimestamp; + + FakeDataConfig({ + this.tags = const [], + this.iterations = 10, + this.includeTimestamp = false, + }); + + factory FakeDataConfig.fromJson(Map json) => _$FakeDataConfigFromJson(json); + + Map toJson() => _$FakeDataConfigToJson(this); +} From e0ded6e4608f424694b985081553a1870411feed Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:48:37 +0530 Subject: [PATCH 132/188] feat: implement FakeDataProvider for generating various fake data types for API testing --- .../lib/src/fake_data_provider/services}/fake_data_provider.dart | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {lib/utils => packages/api_testing_suite/lib/src/fake_data_provider/services}/fake_data_provider.dart (100%) diff --git a/lib/utils/fake_data_provider.dart b/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart similarity index 100% rename from lib/utils/fake_data_provider.dart rename to packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart From 0809b612046ac4426bd90fb56eb4e421c37a8539 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 14:49:30 +0530 Subject: [PATCH 133/188] feat: add FakeDataProvidersPane widget for displaying and managing fake data placeholders --- .../fake_data_provider/widgets}/fake_data_pane.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) rename {lib/screens/envvar/editor_pane => packages/api_testing_suite/lib/src/fake_data_provider/widgets}/fake_data_pane.dart (95%) diff --git a/lib/screens/envvar/editor_pane/fake_data_pane.dart b/packages/api_testing_suite/lib/src/fake_data_provider/widgets/fake_data_pane.dart similarity index 95% rename from lib/screens/envvar/editor_pane/fake_data_pane.dart rename to packages/api_testing_suite/lib/src/fake_data_provider/widgets/fake_data_pane.dart index bd1685eb2..ede942a69 100644 --- a/lib/screens/envvar/editor_pane/fake_data_pane.dart +++ b/packages/api_testing_suite/lib/src/fake_data_provider/widgets/fake_data_pane.dart @@ -1,8 +1,9 @@ -import 'package:apidash_design_system/apidash_design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:apidash/utils/fake_data_provider.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:api_testing_suite/src/fake_data_provider/models/fake_data_config.dart'; + +import '../services/fake_data_provider.dart'; class FakeDataProvidersPane extends ConsumerWidget { const FakeDataProvidersPane({super.key}); @@ -12,9 +13,9 @@ class FakeDataProvidersPane extends ConsumerWidget { return Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, - borderRadius: kBorderRadius12, + borderRadius: BorderRadius.circular(12), ), - margin: kP10, + margin: const EdgeInsets.all(10), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ From c70a016efceb02f9174139dd4040a5e831b2ea13 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 17:09:43 +0530 Subject: [PATCH 134/188] feat: add WorkflowBuilderPage to dashboard and update navigation index --- lib/screens/dashboard.dart | 17 ++++++++++++++++- lib/utils/envvar_utils.dart | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index 428ffaebc..f8657247f 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -10,6 +10,7 @@ import 'envvar/environment_page.dart'; import 'home_page/home_page.dart'; import 'history/history_page.dart'; import 'settings_page.dart'; +import 'package:apidash/api_testing/api_testing_integration.dart'; class Dashboard extends ConsumerWidget { const Dashboard({super.key}); @@ -68,6 +69,19 @@ class Dashboard extends ConsumerWidget { 'History', style: Theme.of(context).textTheme.labelSmall, ), + kVSpacer10, + IconButton( + isSelected: railIdx == 3, + onPressed: () { + ref.read(navRailIndexStateProvider.notifier).state = 3; + }, + icon: const Icon(Icons.account_tree_outlined), + selectedIcon: const Icon(Icons.account_tree), + ), + Text( + 'Workflows', + style: Theme.of(context).textTheme.labelSmall, + ), ], ), Expanded( @@ -92,7 +106,7 @@ class Dashboard extends ConsumerWidget { padding: const EdgeInsets.only(bottom: 16.0), child: NavbarButton( railIdx: railIdx, - buttonIdx: 3, + buttonIdx: 4, selectedIcon: Icons.settings, icon: Icons.settings_outlined, label: 'Settings', @@ -118,6 +132,7 @@ class Dashboard extends ConsumerWidget { HomePage(), EnvironmentPage(), HistoryPage(), + WorkflowBuilderPage(), SettingsPage(), ], ), diff --git a/lib/utils/envvar_utils.dart b/lib/utils/envvar_utils.dart index 448690811..db625ba1b 100644 --- a/lib/utils/envvar_utils.dart +++ b/lib/utils/envvar_utils.dart @@ -1,6 +1,6 @@ import 'package:apidash_core/apidash_core.dart'; import 'package:apidash/consts.dart'; -import 'fake_data_provider.dart'; +import 'package:api_testing_suite/src/fake_data_provider/services/fake_data_provider.dart'; String getEnvironmentTitle(String? name) { if (name == null || name.trim() == "") { From 881a5719c468384b5ec7811502b06ea68f997fef Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Thu, 10 Apr 2025 17:15:49 +0530 Subject: [PATCH 135/188] fix: update export paths for workflow builder widgets and reorganize model exports --- .../api_testing_suite/lib/api_testing_suite.dart | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/api_testing_suite/lib/api_testing_suite.dart b/packages/api_testing_suite/lib/api_testing_suite.dart index d366e2ba2..8fd223f9d 100644 --- a/packages/api_testing_suite/lib/api_testing_suite.dart +++ b/packages/api_testing_suite/lib/api_testing_suite.dart @@ -7,7 +7,12 @@ export 'src/models/workflow_model.dart'; export 'src/providers/workflow_providers.dart'; // Widgets -export 'src/widgets/workflow_builder/workflow_canvas.dart'; -export 'src/widgets/workflow_builder/workflow_connection.dart'; -export 'src/widgets/workflow_builder/workflow_node.dart'; -export 'src/widgets/workflow_builder/workflow_screens.dart'; +export 'src/workflow_builder/workflow_canvas.dart'; +export 'src/workflow_builder/workflow_connection.dart'; +export 'src/workflow_builder/workflow_node.dart'; +export 'src/workflow_builder/workflow_screens.dart'; + +// Models +export 'src/stress_test/models/stress_test_config.dart'; +export 'src/stress_test/models/stress_test_summary.dart'; +export 'src/fake_data_provider/models/fake_data_config.dart'; From b339f6e0f14873fd8d898cadf8307cb742105303 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 12 Apr 2025 02:56:47 +0530 Subject: [PATCH 136/188] refactor: add all the models inside workflow_builder which were in models --- .../lib/src/models/node_status.dart | 27 -------- .../lib/src/models/workflow_connection.dart | 66 ------------------- .../models/core_models.dart | 0 .../{ => workflow_builder}/models/models.dart | 0 .../workflow_builder/models/node_status.dart | 37 +++-------- .../models/request_model.dart | 0 .../models/workflow_connection_model.dart | 1 + .../models/workflow_execution_state.dart | 0 .../models/workflow_model.dart | 0 .../models/workflow_node_model.dart | 0 10 files changed, 11 insertions(+), 120 deletions(-) delete mode 100644 packages/api_testing_suite/lib/src/models/node_status.dart delete mode 100644 packages/api_testing_suite/lib/src/models/workflow_connection.dart rename packages/api_testing_suite/lib/src/{ => workflow_builder}/models/core_models.dart (100%) rename packages/api_testing_suite/lib/src/{ => workflow_builder}/models/models.dart (100%) rename packages/api_testing_suite/lib/src/{ => workflow_builder}/models/request_model.dart (100%) rename packages/api_testing_suite/lib/src/{ => workflow_builder}/models/workflow_connection_model.dart (97%) rename packages/api_testing_suite/lib/src/{ => workflow_builder}/models/workflow_execution_state.dart (100%) rename packages/api_testing_suite/lib/src/{ => workflow_builder}/models/workflow_model.dart (100%) rename packages/api_testing_suite/lib/src/{ => workflow_builder}/models/workflow_node_model.dart (100%) diff --git a/packages/api_testing_suite/lib/src/models/node_status.dart b/packages/api_testing_suite/lib/src/models/node_status.dart deleted file mode 100644 index 01b5f3508..000000000 --- a/packages/api_testing_suite/lib/src/models/node_status.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; - -/// Enum representing the different states a workflow node can be in -enum NodeStatus { - inactive, - running, - success, - failure, - pending, -} - -extension NodeStatusExtension on NodeStatus { - Color get color { - switch (this) { - case NodeStatus.running: - return Colors.blue; - case NodeStatus.success: - return Colors.green; - case NodeStatus.failure: - return Colors.red; - case NodeStatus.pending: - return Colors.orange; - case NodeStatus.inactive: - return Colors.grey; - } - } -} diff --git a/packages/api_testing_suite/lib/src/models/workflow_connection.dart b/packages/api_testing_suite/lib/src/models/workflow_connection.dart deleted file mode 100644 index 4e3ad89e9..000000000 --- a/packages/api_testing_suite/lib/src/models/workflow_connection.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:flutter/material.dart'; -import 'package:uuid/uuid.dart'; - -part 'workflow_connection.freezed.dart'; - -/// Model representing a connection between two nodes in a workflow -@freezed -class WorkflowConnection with _$WorkflowConnection { - const WorkflowConnection._(); - - const factory WorkflowConnection({ - required String id, - required String sourceId, - required String targetId, - required String workflowId, - required Offset position, - @Default(ConnectionType.standard) ConnectionType type, - @Default([]) List labels, - @Default({}) Map metadata, - }) = _WorkflowConnection; - - factory WorkflowConnection.fromJson(Map json) => - _$WorkflowConnectionFromJson(json); - - factory WorkflowConnection.create({ - required String sourceId, - required String targetId, - required String workflowId, - required Offset position, - ConnectionType type = ConnectionType.standard, - List labels = const [], - Map metadata = const {}, - }) { - return WorkflowConnection( - id: const Uuid().v4(), - sourceId: sourceId, - targetId: targetId, - workflowId: workflowId, - position: position, - type: type, - labels: labels, - metadata: metadata, - ); - } - -} - -enum ConnectionType { - standard, - conditional, - error, -} - -extension ConnectionTypeExtension on ConnectionType { - Color get color { - switch (this) { - case ConnectionType.standard: - return Colors.blue; - case ConnectionType.conditional: - return Colors.orange; - case ConnectionType.error: - return Colors.red; - } - } -} diff --git a/packages/api_testing_suite/lib/src/models/core_models.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/core_models.dart similarity index 100% rename from packages/api_testing_suite/lib/src/models/core_models.dart rename to packages/api_testing_suite/lib/src/workflow_builder/models/core_models.dart diff --git a/packages/api_testing_suite/lib/src/models/models.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/models.dart similarity index 100% rename from packages/api_testing_suite/lib/src/models/models.dart rename to packages/api_testing_suite/lib/src/workflow_builder/models/models.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/node_status.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/node_status.dart index 8661470f5..01b5f3508 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/models/node_status.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/node_status.dart @@ -1,44 +1,27 @@ import 'package:flutter/material.dart'; +/// Enum representing the different states a workflow node can be in enum NodeStatus { - pending, + inactive, running, success, - error, - idle, - failure; - - String get label { - switch (this) { - case NodeStatus.pending: - return 'Pending'; - case NodeStatus.running: - return 'Running'; - case NodeStatus.success: - return 'Success'; - case NodeStatus.error: - return 'Error'; - case NodeStatus.idle: - return 'Idle'; - case NodeStatus.failure: - return 'Failure'; - } - } + failure, + pending, +} +extension NodeStatusExtension on NodeStatus { Color get color { switch (this) { - case NodeStatus.pending: - return Colors.grey; case NodeStatus.running: return Colors.blue; case NodeStatus.success: return Colors.green; - case NodeStatus.error: - return Colors.red; - case NodeStatus.idle: - return Colors.grey; case NodeStatus.failure: return Colors.red; + case NodeStatus.pending: + return Colors.orange; + case NodeStatus.inactive: + return Colors.grey; } } } diff --git a/packages/api_testing_suite/lib/src/models/request_model.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/request_model.dart similarity index 100% rename from packages/api_testing_suite/lib/src/models/request_model.dart rename to packages/api_testing_suite/lib/src/workflow_builder/models/request_model.dart diff --git a/packages/api_testing_suite/lib/src/models/workflow_connection_model.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_connection_model.dart similarity index 97% rename from packages/api_testing_suite/lib/src/models/workflow_connection_model.dart rename to packages/api_testing_suite/lib/src/workflow_builder/models/workflow_connection_model.dart index 0271f1b60..5a95d4fa8 100644 --- a/packages/api_testing_suite/lib/src/models/workflow_connection_model.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_connection_model.dart @@ -4,6 +4,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:uuid/uuid.dart'; part 'workflow_connection_model.freezed.dart'; +part 'workflow_connection_model.g.dart'; /// Model representing a connection between nodes in a workflow @freezed diff --git a/packages/api_testing_suite/lib/src/models/workflow_execution_state.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_execution_state.dart similarity index 100% rename from packages/api_testing_suite/lib/src/models/workflow_execution_state.dart rename to packages/api_testing_suite/lib/src/workflow_builder/models/workflow_execution_state.dart diff --git a/packages/api_testing_suite/lib/src/models/workflow_model.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart similarity index 100% rename from packages/api_testing_suite/lib/src/models/workflow_model.dart rename to packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart diff --git a/packages/api_testing_suite/lib/src/models/workflow_node_model.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_node_model.dart similarity index 100% rename from packages/api_testing_suite/lib/src/models/workflow_node_model.dart rename to packages/api_testing_suite/lib/src/workflow_builder/models/workflow_node_model.dart From 922a001c9b81ea7c6d7b8a3a493c59c397a51df9 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 14 Apr 2025 11:33:20 +0530 Subject: [PATCH 137/188] refactor: change WorkflowBuilder to dashboard in place of WorkflowBuilderPage, wrapper for integrating the suite --- lib/api_testing/api_testing_integration.dart | 20 ++++++++++++++++++++ lib/screens/dashboard.dart | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 lib/api_testing/api_testing_integration.dart diff --git a/lib/api_testing/api_testing_integration.dart b/lib/api_testing/api_testing_integration.dart new file mode 100644 index 000000000..e25c2aae7 --- /dev/null +++ b/lib/api_testing/api_testing_integration.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:api_testing_suite/api_testing_suite.dart'; +export 'package:api_testing_suite/api_testing_suite.dart'; + +final workflowCollectionProvider = Provider>((ref) { + //It is usable when collections are implemented in the future. + //For now, we return an empty map to avoid errors. + return {}; +}); + +/// WorkflowBuilderPage that uses the implementation from api_testing_suite +class WorkflowBuilder extends StatelessWidget { + const WorkflowBuilder({super.key}); + + @override + Widget build(BuildContext context) { + return const WorkflowBuilderPage(); + } +} diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index f8657247f..00d4f3e59 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -132,7 +132,7 @@ class Dashboard extends ConsumerWidget { HomePage(), EnvironmentPage(), HistoryPage(), - WorkflowBuilderPage(), + WorkflowBuilder(), SettingsPage(), ], ), From da20e02bf58b3fda555970e86f7fb9b5a960792f Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 14 Apr 2025 11:34:00 +0530 Subject: [PATCH 138/188] refactor: remove unused import for fake_data_pane in environment_editor.dart and api_suite_integration.dart --- lib/screens/envvar/environment_editor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/screens/envvar/environment_editor.dart b/lib/screens/envvar/environment_editor.dart index 8ea924120..ea3fb8a80 100644 --- a/lib/screens/envvar/environment_editor.dart +++ b/lib/screens/envvar/environment_editor.dart @@ -1,3 +1,4 @@ +import 'package:apidash/api_testing/api_testing_integration.dart'; import 'package:apidash_design_system/apidash_design_system.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -6,7 +7,6 @@ import 'package:apidash/widgets/widgets.dart'; import 'package:apidash/consts.dart'; import '../common_widgets/common_widgets.dart'; import './editor_pane/variables_pane.dart'; -import './editor_pane/fake_data_pane.dart'; final environmentEditorTabProvider = StateProvider((ref) => 0); From f90549430af404abf08ef5ea85067076b46bd0c4 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 14 Apr 2025 11:34:55 +0530 Subject: [PATCH 139/188] feat: add depen's in pubspec.yaml for API Testing Suite --- packages/api_testing_suite/pubspec.yaml | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 packages/api_testing_suite/pubspec.yaml diff --git a/packages/api_testing_suite/pubspec.yaml b/packages/api_testing_suite/pubspec.yaml new file mode 100644 index 000000000..c14bb99e8 --- /dev/null +++ b/packages/api_testing_suite/pubspec.yaml @@ -0,0 +1,36 @@ +name: api_testing_suite +description: API Testing Suite for APIDash +version: 0.1.0 +publish_to: none + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.7.0" + +dependencies: + flutter: + sdk: flutter + flutter_riverpod: ^2.4.1 + uuid: ^3.0.7 + freezed_annotation: ^2.4.1 + json_annotation: ^4.9.0 + apidash_core: + path: ../apidash_core + faker: ^2.0.0 + http: ^1.1.0 + dio: ^5.3.0 + logger: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + build_runner: ^2.4.15 + flutter_launcher_icons: ^0.14.3 + flutter_lints: ^5.0.0 + flutter_native_splash: ^2.4.5 + freezed: ^2.5.7 + json_serializable: ^6.9.4 + test: ^1.24.0 + +flutter: + uses-material-design: true From 1810d3b7c16eec642b56e557b483757d806a18a8 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 14 Apr 2025 11:37:09 +0530 Subject: [PATCH 140/188] refactor: remove unused import and clean up whitespace in fake_data_pane.dart --- .../lib/src/fake_data_provider/widgets/fake_data_pane.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/api_testing_suite/lib/src/fake_data_provider/widgets/fake_data_pane.dart b/packages/api_testing_suite/lib/src/fake_data_provider/widgets/fake_data_pane.dart index ede942a69..5d4194019 100644 --- a/packages/api_testing_suite/lib/src/fake_data_provider/widgets/fake_data_pane.dart +++ b/packages/api_testing_suite/lib/src/fake_data_provider/widgets/fake_data_pane.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:api_testing_suite/src/fake_data_provider/models/fake_data_config.dart'; import '../services/fake_data_provider.dart'; @@ -144,7 +143,6 @@ class FakeDataProvidersPane extends ConsumerWidget { width: 1, ), children: [ - TableRow( decoration: BoxDecoration( color: Theme.of(context).colorScheme.surfaceContainerHigh, @@ -156,7 +154,6 @@ class FakeDataProvidersPane extends ConsumerWidget { _buildTableCell(context, 'Copy', isHeader: true), ], ), - for (var item in fakeDataTags) TableRow( children: [ @@ -170,7 +167,8 @@ class FakeDataProvidersPane extends ConsumerWidget { ); } - Widget _buildTableCell(BuildContext context, String text, {bool isHeader = false}) { + Widget _buildTableCell(BuildContext context, String text, + {bool isHeader = false}) { return Padding( padding: const EdgeInsets.all(8), child: Text( From 88ddf99ec5e5db2bc0420b1344c86a0b09ff4f5a Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 14 Apr 2025 11:42:22 +0530 Subject: [PATCH 141/188] refactor: improve code formatting and readability in stress_test_result_card.dart --- .../widgets/stress_test_result_card.dart | 69 ++++++++++--------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/packages/api_testing_suite/lib/src/stress_test/widgets/stress_test_result_card.dart b/packages/api_testing_suite/lib/src/stress_test/widgets/stress_test_result_card.dart index 8713b6b79..a72fdecf3 100644 --- a/packages/api_testing_suite/lib/src/stress_test/widgets/stress_test_result_card.dart +++ b/packages/api_testing_suite/lib/src/stress_test/widgets/stress_test_result_card.dart @@ -4,7 +4,8 @@ import 'package:flutter/material.dart'; class StressTestResultCard extends StatelessWidget { final StressTestSummary summary; - const StressTestResultCard({Key? key, required this.summary}) : super(key: key); + const StressTestResultCard({Key? key, required this.summary}) + : super(key: key); @override Widget build(BuildContext context) { @@ -13,7 +14,7 @@ class StressTestResultCard extends StatelessWidget { final errorColor = isDarkMode ? Colors.red[400] : Colors.red[700]; final cardColor = isDarkMode ? Colors.grey[850] : Colors.grey[200]; final textColor = isDarkMode ? Colors.grey[400] : Colors.grey[800]; - + return Card( color: cardColor, elevation: 0, @@ -116,8 +117,8 @@ class StressTestResultCard extends StatelessWidget { Text( label, style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: textColor, - ), + color: textColor, + ), ), ], ), @@ -137,8 +138,9 @@ class StressTestResultCard extends StatelessWidget { int total, Color color, ) { - final percentage = total > 0 ? (count / total * 100).toStringAsFixed(1) : '0.0'; - + final percentage = + total > 0 ? (count / total * 100).toStringAsFixed(1) : '0.0'; + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -152,16 +154,16 @@ class StressTestResultCard extends StatelessWidget { Text( '$count', style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: color, - fontWeight: FontWeight.bold, - ), + color: color, + fontWeight: FontWeight.bold, + ), ), const SizedBox(width: 4), Text( '($percentage%)', style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: color, - ), + color: color, + ), ), ], ), @@ -179,44 +181,46 @@ class StressTestResultCard extends StatelessWidget { ); } - Widget _buildResponseTimeDistribution(BuildContext context, List results) { + Widget _buildResponseTimeDistribution( + BuildContext context, List results) { final successfulResults = results.where((r) => r.error == null).toList(); if (successfulResults.isEmpty) { return const Text('No successful responses to analyze'); } - + successfulResults.sort((a, b) => a.duration.compareTo(b.duration)); - + final p50Index = (successfulResults.length * 0.5).floor(); final p75Index = (successfulResults.length * 0.75).floor(); final p90Index = (successfulResults.length * 0.9).floor(); final p95Index = (successfulResults.length * 0.95).floor(); final p99Index = (successfulResults.length * 0.99).floor(); - + return Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Column( children: [ - _buildPercentileRow(context, 'Min', + _buildPercentileRow(context, 'Min', '${successfulResults.first.duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P50', + _buildPercentileRow(context, 'P50', '${successfulResults[p50Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P75', + _buildPercentileRow(context, 'P75', '${successfulResults[p75Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P90', + _buildPercentileRow(context, 'P90', '${successfulResults[p90Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P95', + _buildPercentileRow(context, 'P95', '${successfulResults[p95Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P99', + _buildPercentileRow(context, 'P99', '${successfulResults[p99Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'Max', + _buildPercentileRow(context, 'Max', '${successfulResults.last.duration.inMilliseconds} ms'), ], ), ); } - Widget _buildPercentileRow(BuildContext context, String percentile, String value) { + Widget _buildPercentileRow( + BuildContext context, String percentile, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 2.0), child: Row( @@ -240,22 +244,23 @@ class StressTestResultCard extends StatelessWidget { ); } - Widget _buildStatusCodeDistribution(BuildContext context, List results) { + Widget _buildStatusCodeDistribution( + BuildContext context, List results) { final statusCodeCounts = {}; for (final result in results) { - if (result.statusCode != -1) { // Skip errors - statusCodeCounts[result.statusCode] = + if (result.statusCode != -1) { + statusCodeCounts[result.statusCode] = (statusCodeCounts[result.statusCode] ?? 0) + 1; } } - + if (statusCodeCounts.isEmpty) { return const Text('No status codes to analyze'); } - + final sortedEntries = statusCodeCounts.entries.toList() ..sort((a, b) => a.key.compareTo(b.key)); - + return Wrap( spacing: 8, runSpacing: 8, @@ -266,7 +271,7 @@ class StressTestResultCard extends StatelessWidget { final isRedirect = statusCode >= 300 && statusCode < 400; final isClientError = statusCode >= 400 && statusCode < 500; final isServerError = statusCode >= 500; - + Color chipColor; if (isSuccess) { chipColor = Colors.green; @@ -279,10 +284,10 @@ class StressTestResultCard extends StatelessWidget { } else { chipColor = Colors.grey; } - + return Chip( label: Text('$statusCode ($count)'), - backgroundColor: chipColor.withOpacity(0.2), + backgroundColor: chipColor.withAlpha(51), labelStyle: TextStyle(color: chipColor), ); }).toList(), From e453d362a5582f0d897954e78e1083c01a3a660f Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 14 Apr 2025 11:48:58 +0530 Subject: [PATCH 142/188] feat: add ConnectionPainter and DagExecutionEngine for workflow visualization and execution management --- .../workflow_builder/connection_painter.dart | 224 ++++++++++++++ .../dag_execution_engine.dart | 290 ++++++++++++++++++ .../src/workflow_builder/grid_painter.dart | 41 +++ 3 files changed, 555 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/connection_painter.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/dag_execution_engine.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/connection_painter.dart b/packages/api_testing_suite/lib/src/workflow_builder/connection_painter.dart new file mode 100644 index 000000000..6d2f8cfbb --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/connection_painter.dart @@ -0,0 +1,224 @@ +import 'dart:ui' as ui; +import 'package:flutter/material.dart'; +import 'models/workflow_connection_model.dart'; +import 'models/workflow_node_model.dart'; + +/// Painter for workflow connections +class ConnectionPainter extends CustomPainter { + final List connections; + final List nodes; + final Map nodePositions; + final Offset? tempConnectionStart; + final Offset? tempConnectionEnd; + final List runningNodeIds; + final List completedNodeIds; + final Size nodeSize; + + ConnectionPainter({ + required this.connections, + required this.nodes, + required this.nodePositions, + this.tempConnectionStart, + this.tempConnectionEnd, + this.runningNodeIds = const [], + this.completedNodeIds = const [], + this.nodeSize = const Size(160, 80), + }); + + @override + void paint(Canvas canvas, Size size) { + for (final connection in connections) { + try { + final sourceId = connection.sourceId; + final targetId = connection.targetId; + + if (nodePositions.containsKey(sourceId) && + nodePositions.containsKey(targetId)) { + final isActive = runningNodeIds.contains(sourceId) || + runningNodeIds.contains(targetId); + final isCompleted = completedNodeIds.contains(sourceId) && + completedNodeIds.contains(targetId); + + _drawConnection( + canvas, + sourceId, + targetId, + isActive: isActive, + isCompleted: isCompleted, + ); + } + } catch (e) { + debugPrint('Error drawing connection: $e'); + } + } + + if (tempConnectionStart != null && tempConnectionEnd != null) { + _drawTempConnection(canvas, tempConnectionStart!, tempConnectionEnd!); + } + } + + void _drawConnection( + Canvas canvas, + String sourceId, + String targetId, { + bool isActive = false, + bool isCompleted = false, + }) { + try { + final start = nodePositions[sourceId]!; + final end = nodePositions[targetId]!; + + final Color color = isActive + ? Colors.blue + : isCompleted + ? Colors.green + : Colors.grey; + + final double width = isActive || isCompleted ? 2.0 : 1.0; + + _drawBezierLine(canvas, start, end, color, width); + } catch (e) { + debugPrint('Error drawing connection: $e'); + } + } + + void _drawTempConnection(Canvas canvas, Offset start, Offset end) { + _drawBezierLine(canvas, start, end, Colors.blue.withOpacity(0.7), 2.0, + isDashed: true); + } + + void _drawBezierLine( + Canvas canvas, + Offset start, + Offset end, + Color color, + double width, { + bool isDashed = false, + }) { + try { + final sourceCenter = + Offset(start.dx + nodeSize.width / 2, start.dy + nodeSize.height / 2); + + final targetCenter = + Offset(end.dx + nodeSize.width / 2, end.dy + nodeSize.height / 2); + + final controlPoint1 = Offset( + sourceCenter.dx + (targetCenter.dx - sourceCenter.dx) * 0.5, + sourceCenter.dy, + ); + + final controlPoint2 = Offset( + sourceCenter.dx + (targetCenter.dx - sourceCenter.dx) * 0.5, + targetCenter.dy, + ); + + final path = Path() + ..moveTo(sourceCenter.dx, sourceCenter.dy) + ..cubicTo( + controlPoint1.dx, + controlPoint1.dy, + controlPoint2.dx, + controlPoint2.dy, + targetCenter.dx, + targetCenter.dy, + ); + + final shadowPaint = Paint() + ..color = Colors.black.withOpacity(0.3) + ..style = PaintingStyle.stroke + ..strokeWidth = width + 2.0; + + final paint = Paint() + ..color = color + ..style = PaintingStyle.stroke + ..strokeWidth = width; + + if (isDashed) { + final dashPath = _dashPath( + path, + dashArray: CircularIntervalList([5, 5]), + ); + canvas.drawPath(dashPath, paint); + } else { + canvas.drawPath(path, shadowPaint); + canvas.drawPath(path, paint); + + _drawArrow(canvas, targetCenter.dx, targetCenter.dy, color, width); + } + } catch (e) { + debugPrint('Error drawing bezier line: $e'); + } + } + + void _drawArrow( + Canvas canvas, double x, double y, Color color, double width) { + final paint = Paint() + ..color = color + ..style = PaintingStyle.fill; + + final path = Path(); + + path.moveTo(x, y); + path.lineTo(x - 6, y - 3); + path.lineTo(x - 6, y + 3); + path.close(); + + canvas.drawPath(path, paint); + } + + Path _dashPath( + Path source, { + required CircularIntervalList dashArray, + }) { + final Path dest = Path(); + final List metrics = source.computeMetrics().toList(); + + for (final ui.PathMetric pathMetric in metrics) { + double distance = 0; + bool draw = true; + while (distance < pathMetric.length) { + final double len = dashArray.next; + if (draw) { + try { + dest.addPath( + pathMetric.extractPath(distance, distance + len), + Offset.zero, + ); + } catch (e) { + debugPrint('Error adding path segment: $e'); + } + } + distance += len; + draw = !draw; + } + } + + return dest; + } + + @override + bool shouldRepaint(covariant ConnectionPainter oldDelegate) { + return oldDelegate.connections != connections || + oldDelegate.nodePositions != nodePositions || + oldDelegate.tempConnectionStart != tempConnectionStart || + oldDelegate.tempConnectionEnd != tempConnectionEnd || + oldDelegate.runningNodeIds != runningNodeIds || + oldDelegate.completedNodeIds != completedNodeIds; + } +} + +class CircularIntervalList { + final List _items; + int _index = 0; + + CircularIntervalList(this._items); + + T get next { + if (_items.isEmpty) { + throw Exception('CircularIntervalList is empty'); + } + final item = _items[_index]; + _index = (_index + 1) % _items.length; + return item; + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/dag_execution_engine.dart b/packages/api_testing_suite/lib/src/workflow_builder/dag_execution_engine.dart new file mode 100644 index 000000000..3c798aa24 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/dag_execution_engine.dart @@ -0,0 +1,290 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'models/workflow_model.dart'; +import 'models/workflow_node_model.dart'; +import 'models/node_status.dart'; +import 'models/workflow_execution_state.dart'; + +/// A class that handles the execution of a workflow DAG +class DagExecutionEngine { + + final WorkflowModel workflow; + final Function(String, NodeStatus) onNodeStatusChanged; + final Function(WorkflowExecutionState) onExecutionStateChanged; + WorkflowExecutionState _executionState; + late Map> _nodeDependencyMap; + late Map> _nodeOutgoingMap; + final _executionController = StreamController.broadcast(); + StreamSubscription? _executionSubscription; + Timer? _executionTimer; + + DagExecutionEngine({ + required this.workflow, + required this.onNodeStatusChanged, + required this.onExecutionStateChanged, + }) : _executionState = const WorkflowExecutionState() { + _initializeDependencyMaps(); + } + + void _initializeDependencyMaps() { + _nodeDependencyMap = {}; + _nodeOutgoingMap = {}; + + for (final node in workflow.nodes) { + _nodeDependencyMap[node.id] = []; + _nodeOutgoingMap[node.id] = []; + } + + for (final connection in workflow.connections) { + final sourceId = connection.sourceId; + final targetId = connection.targetId; + + if (_nodeDependencyMap.containsKey(targetId)) { + _nodeDependencyMap[targetId]!.add(sourceId); + } + + if (_nodeOutgoingMap.containsKey(sourceId)) { + _nodeOutgoingMap[sourceId]!.add(targetId); + } + } + } + + void start() { + if (_executionState.isRunning) { + return; + } + + if (_executionState.isCompleted || _executionState.hasError) { + _resetExecutionState(); + } + + final initialNodes = _findInitialNodes(); + if (initialNodes.isEmpty) { + _updateExecutionState( + _executionState.copyWith( + status: WorkflowExecutionStatus.error, + errorMessage: 'No starting nodes found in the workflow', + ), + ); + return; + } + + _updateExecutionState( + _executionState.copyWith( + status: WorkflowExecutionStatus.running, + startTime: DateTime.now(), + pendingNodeIds: initialNodes.map((node) => node.id).toList(), + ), + ); + + _setupExecutionListener(); + _executeNextNodes(); + } + + void pause() { + if (!_executionState.isRunning) { + return; + } + + _updateExecutionState( + _executionState.copyWith( + status: WorkflowExecutionStatus.paused, + ), + ); + + _cancelExecutionTimer(); + } + + void resume() { + if (!_executionState.isPaused) { + return; + } + + _updateExecutionState( + _executionState.copyWith( + status: WorkflowExecutionStatus.running, + ), + ); + + _executeNextNodes(); + } + + void stop() { + _cancelExecutionSubscription(); + _cancelExecutionTimer(); + + _updateExecutionState( + _executionState.copyWith( + status: WorkflowExecutionStatus.idle, + currentNodeId: null, + ), + ); + } + + void _resetExecutionState() { + _updateExecutionState( + const WorkflowExecutionState(), + ); + } + + List _findInitialNodes() { + return workflow.nodes.where((node) { + return _nodeDependencyMap[node.id]!.isEmpty; + }).toList(); + } + + void _setupExecutionListener() { + _cancelExecutionSubscription(); + + _executionSubscription = _executionController.stream.listen((command) { + switch (command) { + case ExecutionCommand.start: + start(); + break; + case ExecutionCommand.pause: + pause(); + break; + case ExecutionCommand.resume: + resume(); + break; + case ExecutionCommand.stop: + stop(); + break; + case ExecutionCommand.reset: + _resetExecutionState(); + break; + } + }); + } + + void _executeNextNodes() { + if (!_executionState.isRunning) { + return; + } + + final pendingNodeIds = List.from(_executionState.pendingNodeIds); + if (pendingNodeIds.isEmpty) { + _completeExecution(); + return; + } + + final nodeId = pendingNodeIds.first; + pendingNodeIds.removeAt(0); + + final node = workflow.nodes.firstWhere( + (n) => n.id == nodeId, + orElse: () => throw Exception('Node not found: $nodeId'), + ); + + final dependencies = _nodeDependencyMap[nodeId] ?? []; + final allDependenciesMet = dependencies.every( + (depId) => _executionState.executedNodeIds.contains(depId), + ); + + if (!allDependenciesMet) { + pendingNodeIds.add(nodeId); + + _updateExecutionState( + _executionState.copyWith( + pendingNodeIds: pendingNodeIds, + ), + ); + + _scheduleNextExecution(); + return; + } + + _updateExecutionState( + _executionState.copyWith( + currentNodeId: nodeId, + pendingNodeIds: pendingNodeIds, + ), + ); + + onNodeStatusChanged(nodeId, NodeStatus.running); + + _executionTimer = Timer(const Duration(milliseconds: 500), () { + + final success = true; + final newStatus = success ? NodeStatus.success : NodeStatus.failure; + + onNodeStatusChanged(nodeId, newStatus); + + final updatedResults = + Map.from(_executionState.executionResults); + updatedResults[nodeId] = { + 'status': success ? 'success' : 'failure', + 'timestamp': DateTime.now().toIso8601String(), + 'data': {'message': 'Simulated ${success ? 'success' : 'failure'}'}, + }; + + final nextNodeIds = _nodeOutgoingMap[nodeId] ?? []; + final updatedPendingIds = List.from(pendingNodeIds); + + for (final nextNodeId in nextNodeIds) { + if (!updatedPendingIds.contains(nextNodeId) && + !_executionState.executedNodeIds.contains(nextNodeId)) { + updatedPendingIds.add(nextNodeId); + } + } + + _updateExecutionState( + _executionState.copyWith( + currentNodeId: null, + executedNodeIds: [..._executionState.executedNodeIds, nodeId], + pendingNodeIds: updatedPendingIds, + executionResults: updatedResults, + ), + ); + + if (updatedPendingIds.isNotEmpty) { + _scheduleNextExecution(); + } else { + _completeExecution(); + } + }); + } + void _scheduleNextExecution() { + _executionTimer = + Timer(const Duration(milliseconds: 100), _executeNextNodes); + } + + void _completeExecution() { + _updateExecutionState( + _executionState.copyWith( + status: WorkflowExecutionStatus.completed, + endTime: DateTime.now(), + ), + ); + } + + void _updateExecutionState(WorkflowExecutionState newState) { + _executionState = newState; + onExecutionStateChanged(newState); + } + + void _cancelExecutionTimer() { + _executionTimer?.cancel(); + _executionTimer = null; + } + + void _cancelExecutionSubscription() { + _executionSubscription?.cancel(); + _executionSubscription = null; + } + + void dispose() { + _cancelExecutionSubscription(); + _cancelExecutionTimer(); + _executionController.close(); + } +} + +enum ExecutionCommand { + start, + pause, + resume, + stop, + reset, +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart b/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart new file mode 100644 index 000000000..d98fb6bb9 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; + +/// Painter for the background grid +class GridPainter extends CustomPainter { + final Color gridColor; + final double gridWidth; + final double gridSpacing; + + GridPainter({ + required this.gridColor, + required this.gridWidth, + required this.gridSpacing, + }); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = gridColor + ..strokeWidth = gridWidth; + + final horizontalLines = (size.height / gridSpacing).ceil(); + final verticalLines = (size.width / gridSpacing).ceil(); + + for (var i = 0; i <= horizontalLines; i++) { + final y = i * gridSpacing; + canvas.drawLine(Offset(0, y), Offset(size.width, y), paint); + } + + for (var i = 0; i <= verticalLines; i++) { + final x = i * gridSpacing; + canvas.drawLine(Offset(x, 0), Offset(x, size.height), paint); + } + } + + @override + bool shouldRepaint(covariant GridPainter oldDelegate) { + return oldDelegate.gridColor != gridColor || + oldDelegate.gridWidth != gridWidth || + oldDelegate.gridSpacing != gridSpacing; + } +} From 163ac1ad8dab673704d592d3e54f6c5acb8df8ba Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 14 Apr 2025 11:51:38 +0530 Subject: [PATCH 143/188] refactor: add workflow providers and notifier for workflow management change folder structure --- .../providers/providers.dart | 0 .../providers/workflow_providers.dart | 77 ++++++++----------- .../providers/workflows_notifier.dart | 64 +++++++-------- 3 files changed, 59 insertions(+), 82 deletions(-) rename packages/api_testing_suite/lib/src/{ => workflow_builder}/providers/providers.dart (100%) rename packages/api_testing_suite/lib/src/{ => workflow_builder}/providers/workflow_providers.dart (72%) rename packages/api_testing_suite/lib/src/{ => workflow_builder}/providers/workflows_notifier.dart (73%) diff --git a/packages/api_testing_suite/lib/src/providers/providers.dart b/packages/api_testing_suite/lib/src/workflow_builder/providers/providers.dart similarity index 100% rename from packages/api_testing_suite/lib/src/providers/providers.dart rename to packages/api_testing_suite/lib/src/workflow_builder/providers/providers.dart diff --git a/packages/api_testing_suite/lib/src/providers/workflow_providers.dart b/packages/api_testing_suite/lib/src/workflow_builder/providers/workflow_providers.dart similarity index 72% rename from packages/api_testing_suite/lib/src/providers/workflow_providers.dart rename to packages/api_testing_suite/lib/src/workflow_builder/providers/workflow_providers.dart index e75f9b64a..86b7d5d32 100644 --- a/packages/api_testing_suite/lib/src/providers/workflow_providers.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/providers/workflow_providers.dart @@ -1,20 +1,13 @@ -import 'package:api_testing_suite/src/models/models.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../models/workflow_model.dart'; - -/// Provider for workflows list +import '../models/models.dart'; import 'workflows_notifier.dart'; -/// Provider for workflows list final workflowsProvider = StateNotifierProvider>((ref) { return WorkflowsNotifier(ref); }); -/// Provider for current workflow ID final currentWorkflowProvider = StateProvider((ref) => null); -/// Provider for active workflow final activeWorkflowProvider = Provider((ref) { final currentWorkflowId = ref.watch(currentWorkflowProvider); final workflows = ref.watch(workflowsProvider); @@ -23,11 +16,10 @@ final activeWorkflowProvider = Provider((ref) { return workflows.firstWhere( (workflow) => workflow.id == currentWorkflowId, - // orElse: () => WorkflowModel.create(), + orElse: () => WorkflowModel.create(name: 'Workflow $currentWorkflowId'), ); }); -/// Provider for workflow execution state final workflowExecutionStateProvider = StateNotifierProvider((ref) { return WorkflowExecutionNotifier(ref); }); @@ -136,7 +128,7 @@ class WorkflowExecutionNotifier extends StateNotifier { await Future.delayed(const Duration(seconds: 1)); - final isSuccess = DateTime.now().millisecondsSinceEpoch % 5 != 0; // 80% success rate + final isSuccess = DateTime.now().millisecondsSinceEpoch % 5 != 0; _ref.read(workflowsProvider.notifier).updateNode( workflow.id, @@ -148,41 +140,36 @@ class WorkflowExecutionNotifier extends StateNotifier { executedNodeIds: updatedExecutedNodeIds, ); - // If failed and is conditional, stop execution - // if (!isSuccess && node.connectedToIds.isNotEmpty) { - // final connections = workflow.connections.where( - // (conn) => conn.sourceId == nodeId && conn.isConditional - // ).toList(); + if (!isSuccess && node.connectedToIds.isNotEmpty) { + final connections = workflow.connections.where( + (conn) => conn.sourceId == nodeId && conn.isConditional + ).toList(); - // if (connections.isNotEmpty) { - // state = state.copyWith(status: WorkflowExecutionStatus.completed); - // return; - // } - // } - - // // Get next nodes - // final nextNodeIds = node.connectedToIds.isEmpty - // ? [] - // : workflow.connections - // .where((conn) => conn.sourceId == nodeId) - // .map((conn) => conn.targetId) - // .toList(); - - // // If no next nodes, workflow is completed - // if (nextNodeIds.isEmpty) { - // state = state.copyWith(status: WorkflowExecutionStatus.completed); - // return; - // } - - // // Execute next nodes - // for (final nextNodeId in nextNodeIds) { - // state = state.copyWith(currentNodeId: nextNodeId); - // await _executeNode(nextNodeId); + if (connections.isNotEmpty) { + state = state.copyWith(status: WorkflowExecutionStatus.completed); + return; + } + } + + final nextNodeIds = node.connectedToIds.isEmpty + ? [] + : workflow.connections + .where((conn) => conn.sourceId == nodeId) + .map((conn) => conn.targetId) + .toList(); + + if (nextNodeIds.isEmpty) { + state = state.copyWith(status: WorkflowExecutionStatus.completed); + return; + } + + for (final nextNodeId in nextNodeIds) { + state = state.copyWith(currentNodeId: nextNodeId); + await _executeNode(nextNodeId); - // // If execution was paused or stopped, break the loop - // if (state.status != WorkflowExecutionStatus.running) { - // break; - // } - // } + if (state.status != WorkflowExecutionStatus.running) { + break; + } + } } } diff --git a/packages/api_testing_suite/lib/src/providers/workflows_notifier.dart b/packages/api_testing_suite/lib/src/workflow_builder/providers/workflows_notifier.dart similarity index 73% rename from packages/api_testing_suite/lib/src/providers/workflows_notifier.dart rename to packages/api_testing_suite/lib/src/workflow_builder/providers/workflows_notifier.dart index 8a7d86c62..4327ca2dc 100644 --- a/packages/api_testing_suite/lib/src/providers/workflows_notifier.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/providers/workflows_notifier.dart @@ -1,10 +1,8 @@ -import 'package:api_testing_suite/src/models/models.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../models/request_model.dart'; +import '../models/models.dart'; -import '../models/workflow_model.dart'; /// Notifier class for workflows state management class WorkflowsNotifier extends StateNotifier> { @@ -18,7 +16,6 @@ class WorkflowsNotifier extends StateNotifier> { _ref.read(StateProvider((ref) => null).notifier).state = newWorkflow.id; } - /// Update an existing workflow void update(WorkflowModel workflow) { state = [ for (final existingWorkflow in state) @@ -26,7 +23,6 @@ class WorkflowsNotifier extends StateNotifier> { ]; } - /// Add a node to a workflow void addNode(String workflowId, WorkflowNodeModel node) { state = [ for (final workflow in state) @@ -37,7 +33,6 @@ class WorkflowsNotifier extends StateNotifier> { ]; } - /// Update a node in a workflow void updateNode(String workflowId, WorkflowNodeModel updatedNode) { state = [ for (final workflow in state) @@ -53,7 +48,6 @@ class WorkflowsNotifier extends StateNotifier> { ]; } - /// Update multiple nodes in a workflow void updateNodes(String workflowId, List updatedNodes) { state = [ for (final workflow in state) @@ -64,7 +58,6 @@ class WorkflowsNotifier extends StateNotifier> { ]; } - /// Update a node's position in a workflow void updateNodePosition(String workflowId, String nodeId, Offset position) { state = [ for (final workflow in state) @@ -83,20 +76,20 @@ class WorkflowsNotifier extends StateNotifier> { ]; } - // void removeNode(String workflowId, String nodeId) { - // state = [ - // for (final workflow in state) - // if (workflow.id == workflowId) - // workflow.copyWith( - // nodes: workflow.nodes.where((node) => node.id != nodeId).toList(), - // connections: workflow.connections.where( - // (conn) => conn?.sourceId != nodeId && conn.targetId != nodeId - // ).toList(), - // ) - // else - // workflow, - // ]; - // } + void removeNode(String workflowId, String nodeId) { + state = [ + for (final workflow in state) + if (workflow.id == workflowId) + workflow.copyWith( + nodes: workflow.nodes.where((node) => node.id != nodeId).toList(), + connections: workflow.connections.where( + (conn) => conn.sourceId != nodeId && conn.targetId != nodeId + ).toList(), + ) + else + workflow, + ]; + } void addConnection(String workflowId, WorkflowConnectionModel connection) { state = [ @@ -108,7 +101,6 @@ class WorkflowsNotifier extends StateNotifier> { ]; } - /// Update a connection in a workflow void updateConnection(String workflowId, WorkflowConnectionModel updatedConnection) { state = [ for (final workflow in state) @@ -124,17 +116,17 @@ class WorkflowsNotifier extends StateNotifier> { ]; } - // void removeConnection(String workflowId, String connectionId) { - // state = [ - // for (final workflow in state) - // if (workflow.id == workflowId) - // workflow.copyWith( - // connections: workflow.connections.where((conn) => conn.id != connectionId).toList(), - // ) - // else - // workflow, - // ]; - // } + void removeConnection(String workflowId, String connectionId) { + state = [ + for (final workflow in state) + if (workflow.id == workflowId) + workflow.copyWith( + connections: workflow.connections.where((conn) => conn.id != connectionId).toList(), + ) + else + workflow, + ]; + } void delete(String workflowId) { state = state.where((workflow) => workflow.id != workflowId).toList(); @@ -143,11 +135,9 @@ class WorkflowsNotifier extends StateNotifier> { } } - /// Import a request as a workflow node void importRequestAsNode(String workflowId, String requestId, Offset position, Map collection) { try { - // Get the request from the collection - final request = collection[requestId]; + final RequestModel? request = collection[requestId]; if (request == null) { debugPrint('Request not found with ID: $requestId'); return; From f85e17d4f00414807789d634962db8744ac05ecd Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 14 Apr 2025 12:55:28 +0530 Subject: [PATCH 144/188] Refactor WorkflowCanvas and related components for improved node management and connection handling - Updated WorkflowCanvas to enhance node addition, dragging, and connection features. - Introduced transformation controller for better zoom and pan functionality. - Refactored WorkflowNodeWidget to WorkflowNode with improved styling and state management. - Enhanced connection drawing logic with ConnectionPainter for better visual representation. - Added canvas controls for resetting and zooming the view. - Improved error handling and node interaction feedback. --- .../src/workflow_builder/grid_painter.dart | 10 +- .../workflow_builder_page.dart | 893 +++++++++++++++++- .../src/workflow_builder/workflow_canvas.dart | 597 +++++++++--- .../workflow_builder/workflow_connection.dart | 7 +- .../src/workflow_builder/workflow_node.dart | 463 ++++++--- 5 files changed, 1619 insertions(+), 351 deletions(-) diff --git a/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart b/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart index d98fb6bb9..1450ce609 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -/// Painter for the background grid class GridPainter extends CustomPainter { final Color gridColor; final double gridWidth; @@ -33,9 +32,10 @@ class GridPainter extends CustomPainter { } @override - bool shouldRepaint(covariant GridPainter oldDelegate) { - return oldDelegate.gridColor != gridColor || - oldDelegate.gridWidth != gridWidth || - oldDelegate.gridSpacing != gridSpacing; + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return oldDelegate is GridPainter && + (oldDelegate.gridColor != gridColor || + oldDelegate.gridWidth != gridWidth || + oldDelegate.gridSpacing != gridSpacing); } } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder_page.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder_page.dart index c308209fe..3be47f5b7 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder_page.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder_page.dart @@ -1,85 +1,880 @@ +import 'package:apidash_core/consts.dart'; +import 'package:apidash_core/models/http_request_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'models/models.dart'; import 'workflow_providers.dart'; import 'workflow_canvas.dart'; -import '../models/workflow_execution_state.dart'; +import 'models/workflow_execution_state.dart'; +import 'widgets/widgets.dart'; + +enum LeftPanelTab { + apiComponents, + templates, +} + +final leftPanelTabProvider = StateProvider((ref) { + return LeftPanelTab.apiComponents; +}); class WorkflowBuilderPage extends ConsumerWidget { - const WorkflowBuilderPage({super.key}); + const WorkflowBuilderPage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { final workflowId = ref.watch(currentWorkflowProvider); final workflowExecutionState = ref.watch(workflowExecutionStateProvider); + final connectionMode = ref.watch(connectionModeProvider); + final executionControl = ref.watch(workflowExecutionControlProvider); + final selectedNodeId = ref.watch(selectedNodeIdProvider); + + if (workflowId == null) { + return _buildNoWorkflowScreen(context, ref); + } + + WorkflowNodeModel? selectedNode; + if (selectedNodeId != null) { + final workflows = ref.watch(workflowsNotifierProvider); + final workflow = workflows.firstWhere( + (w) => w.id == workflowId, + orElse: () => throw Exception('Workflow not found'), + ); + + selectedNode = workflow.nodes.firstWhere( + (n) => n.id == selectedNodeId, + orElse: () => throw Exception('Node not found'), + ); + } return Scaffold( - appBar: AppBar( - title: const Text('Workflow Builder'), - actions: [ - IconButton( - icon: const Icon(Icons.play_arrow), - onPressed: () { - ref.read(workflowExecutionStateProvider.notifier).state = - workflowExecutionState.copyWith(status: WorkflowExecutionStatus.running); - }, + backgroundColor: const Color(0xFF1A1A1A), + appBar: _buildAppBar(ref, workflowExecutionState), + body: Column( + children: [ + _buildToolbar( + ref, connectionMode, workflowExecutionState, executionControl), + Expanded( + child: Row( + children: [ + _buildLeftPanel(ref), + Expanded( + child: _buildCanvasWithLogs(workflowId), + ), + if (selectedNode != null) + NodeDetailsPanel( + node: selectedNode, + workflowId: workflowId, + ), + ], + ), ), - IconButton( - icon: const Icon(Icons.pause), + ], + ), + floatingActionButton: FloatingActionButton( + backgroundColor: Colors.blue, + onPressed: () { + final notifier = ref.read(workflowsNotifierProvider.notifier); + notifier.addNode( + workflowId, + WorkflowNodeModel.create( + requestId: workflowId, + position: const Offset(100, 100), + label: 'New API', + nodeType: NodeType.request, + ), + ); + }, + child: const Icon(Icons.add), + ), + ); + } + + Widget _buildToolbar( + WidgetRef ref, + bool connectionMode, + WorkflowExecutionState executionState, + WorkflowExecutionControl executionControl, + ) { + return Container( + height: 56, + color: const Color(0xFF212121), + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + children: [ + ToggleButton( + icon: Icons.cable, + label: 'Connection Mode', + isActive: connectionMode, onPressed: () { - ref.read(workflowExecutionStateProvider.notifier).state = - workflowExecutionState.copyWith(status: WorkflowExecutionStatus.paused); + ref.read(connectionModeProvider.notifier).state = !connectionMode; }, ), - IconButton( - icon: const Icon(Icons.stop), - onPressed: () { - ref.read(workflowExecutionStateProvider.notifier).state = - workflowExecutionState.copyWith(status: WorkflowExecutionStatus.idle); - }, + + const SizedBox(width: 16), + + Container(width: 1, height: 24, color: Colors.grey.shade700), + + const SizedBox(width: 16), + + _buildExecutionControls(executionState, executionControl), + + const Spacer(), + + if (executionState.isRunning || + executionState.isPaused || + executionState.isCompleted) + Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), + decoration: BoxDecoration( + color: _getStatusColor(executionState), + borderRadius: BorderRadius.circular(16), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + _getStatusIcon(executionState), + color: Colors.white, + size: 16, + ), + const SizedBox(width: 4), + Text( + _getStatusText(executionState), + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildExecutionControls( + WorkflowExecutionState executionState, + WorkflowExecutionControl executionControl, + ) { + return Row( + children: [ + IconButton( + icon: const Icon(Icons.play_arrow), + color: executionState.isRunning ? Colors.grey : Colors.green, + onPressed: executionState.isRunning + ? null + : executionControl.startExecution, + tooltip: 'Start Execution', + ), + + IconButton( + icon: Icon(executionState.isPaused ? Icons.play_circle : Icons.pause), + color: !executionState.isRunning && !executionState.isPaused + ? Colors.grey + : executionState.isPaused + ? Colors.amber + : Colors.blue, + onPressed: !executionState.isRunning && !executionState.isPaused + ? null + : executionState.isPaused + ? executionControl.resumeExecution + : executionControl.pauseExecution, + tooltip: executionState.isPaused ? 'Resume' : 'Pause', + ), + + IconButton( + icon: const Icon(Icons.stop), + color: !executionState.isRunning && !executionState.isPaused + ? Colors.grey + : Colors.red, + onPressed: !executionState.isRunning && !executionState.isPaused + ? null + : executionControl.stopExecution, + tooltip: 'Stop Execution', + ), + ], + ); + } + + AppBar _buildAppBar( + WidgetRef ref, + WorkflowExecutionState workflowExecutionState, + ) { + final workflowId = ref.watch(currentWorkflowProvider); + final workflows = ref.watch(workflowsNotifierProvider); + final workflow = workflowId != null + ? workflows.firstWhere( + (w) => w.id == workflowId, + orElse: () => throw Exception('Workflow not found'), + ) + : null; + + return AppBar( + backgroundColor: const Color(0xFF212121), + title: Row( + children: [ + const Icon(Icons.account_tree, color: Colors.blue), + const SizedBox(width: 8), + Text( + workflow?.name ?? 'Workflow Builder', + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), ), ], ), - body: Column( + actions: [ + if (workflowExecutionState.isRunning || + workflowExecutionState.isPaused || + workflowExecutionState.isCompleted || + workflowExecutionState.hasError) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Chip( + backgroundColor: _getStatusColor(workflowExecutionState), + label: Text( + _getStatusText(workflowExecutionState), + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + avatar: Icon( + _getStatusIcon(workflowExecutionState), + color: Colors.white, + size: 16, + ), + ), + ), + ], + ); + } + + Widget _buildLeftPanel(WidgetRef ref) { + final selectedTab = ref.watch(leftPanelTabProvider); + + return Container( + width: 250, + color: const Color(0xFF252525), + child: Column( children: [ + Container( + color: const Color(0xFF212121), + child: Row( + children: [ + _buildSidebarItem( + Icons.api, + 'API Components', + selectedTab == LeftPanelTab.apiComponents, + onTap: () => ref.read(leftPanelTabProvider.notifier).state = + LeftPanelTab.apiComponents, + ), + _buildSidebarItem( + Icons.description_outlined, + 'Templates', + selectedTab == LeftPanelTab.templates, + onTap: () => ref.read(leftPanelTabProvider.notifier).state = + LeftPanelTab.templates, + ), + ], + ), + ), + Expanded( - child: WorkflowCanvas( - workflowId: workflowId, + child: selectedTab == LeftPanelTab.apiComponents + ? const APIComponentList() + : const TemplatesList(), + ), + ], + ), + ); + } + + Widget _buildSidebarItem( + IconData icon, + String title, + bool isSelected, + {VoidCallback? onTap} + ) { + return Expanded( + child: InkWell( + onTap: onTap, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 12), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: isSelected ? Colors.blue : Colors.transparent, + width: 2, + ), ), + color: isSelected + ? const Color(0xFF2A2A2A) + : const Color(0xFF212121), ), - Container( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + icon, + color: isSelected ? Colors.blue : Colors.grey, + size: 20, + ), + const SizedBox(height: 4), + Text( + title, + style: TextStyle( + color: isSelected ? Colors.white : Colors.grey, + fontSize: 12, + ), + ), + ], + ), + ), + ), + ); + } + + Widget _buildCanvasWithLogs(String workflowId) { + return Column( + children: [ + Expanded( + flex: 7, + child: _buildRightPanel(workflowId), + ), + + Expanded( + flex: 3, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: LogsViewer(workflowId: workflowId), + ), + ), + ], + ); + } + + Widget _buildRightPanel(String? workflowId) { + return Container( + color: const Color(0xFF1A1A1A), + child: workflowId != null + ? WorkflowCanvas(workflowId: workflowId) + : const Center(child: Text('No workflow selected')), + ); + } + + Widget _buildNoWorkflowScreen(BuildContext context, WidgetRef ref) { + return Scaffold( + backgroundColor: const Color(0xFF1A1A1A), + appBar: AppBar( + backgroundColor: const Color(0xFF212121), + title: const Row( + children: [ + Icon(Icons.account_tree, color: Colors.blue), + SizedBox(width: 8), + Text( + 'Workflow Builder', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.account_tree_outlined, + size: 100, + color: Colors.blue.withOpacity(0.7), + ), + const SizedBox(height: 32), + const Text( + 'Welcome to Workflow Builder', + style: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 16), + const Text( + 'Create and manage API workflows visually', + style: TextStyle( + color: Colors.grey, + fontSize: 16, + ), + ), + const SizedBox(height: 32), + ElevatedButton.icon( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + padding: + const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + icon: const Icon(Icons.add), + label: const Text('Create New Workflow'), + onPressed: () { + final notifier = ref.read(workflowsNotifierProvider.notifier); + final newWorkflowId = notifier.createWorkflow( + name: 'New Workflow', + description: 'Created on ${DateTime.now().toLocal()}', + ); + + ref.read(currentWorkflowProvider.notifier).state = newWorkflowId; + }, + ), + ], + ), + ), + ); + } + + Color _getStatusColor(WorkflowExecutionState state) { + if (state.hasError) return Colors.red; + if (state.isCompleted) return Colors.green; + if (state.isPaused) return Colors.amber; + if (state.isRunning) return Colors.blue; + return Colors.grey; + } + + IconData _getStatusIcon(WorkflowExecutionState state) { + if (state.hasError) return Icons.error_outline; + if (state.isCompleted) return Icons.check_circle_outline; + if (state.isPaused) return Icons.pause_circle_outline; + if (state.isRunning) return Icons.sync; + return Icons.hourglass_empty; + } + + String _getStatusText(WorkflowExecutionState state) { + if (state.hasError) return 'Failed'; + if (state.isCompleted) return 'Completed'; + if (state.isPaused) return 'Paused'; + if (state.isRunning) return 'Running'; + return 'Ready'; + } +} + +class ToggleButton extends StatelessWidget { + final IconData icon; + final String label; + final bool isActive; + final VoidCallback onPressed; + + const ToggleButton({ + Key? key, + required this.icon, + required this.label, + required this.isActive, + required this.onPressed, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onPressed, + borderRadius: BorderRadius.circular(4), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: isActive ? Colors.blue.withOpacity(0.2) : Colors.transparent, + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: isActive ? Colors.blue : Colors.transparent, + width: 1, + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + icon, + color: isActive ? Colors.blue : Colors.grey, + size: 18, + ), + const SizedBox(width: 8), + Text( + label, + style: TextStyle( + color: isActive ? Colors.blue : Colors.grey, + fontWeight: isActive ? FontWeight.bold : FontWeight.normal, + ), + ), + ], + ), + ), + ); + } +} + +class APIComponentList extends ConsumerWidget { + const APIComponentList({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return ListView( + padding: const EdgeInsets.all(12), + children: [ + const Text( + 'API Request Components', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 16), + _APIComponentCard( + icon: Icons.call_made, + title: 'GET Request', + description: 'Fetch data from an API', + requestModel: RequestModel( + id: 'api1', + name: 'GET Request', + httpRequestModel: HttpRequestModel( + method: HTTPVerb.get, + url: 'https://api.example.com/data', + ), + ), + ), + _APIComponentCard( + icon: Icons.send, + title: 'POST Request', + description: 'Send data to an API', + requestModel: RequestModel( + id: 'api2', + name: 'POST Request', + httpRequestModel: HttpRequestModel( + method: HTTPVerb.post, + url: 'https://api.example.com/create', + ), + ), + ), + const SizedBox(height: 24), + const Text( + 'Flow Control Components', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 16), + _FlowControlCard( + icon: Icons.fork_right, + title: 'Condition', + description: 'Branch based on a condition', + nodeType: NodeType.condition, + ), + _FlowControlCard( + icon: Icons.settings, + title: 'Action', + description: 'Perform a custom action', + nodeType: NodeType.action, + ), + _FlowControlCard( + icon: Icons.call_received, + title: 'Response', + description: 'Simulate an API response', + nodeType: NodeType.response, + ), + ], + ); + } +} + +class TemplatesList extends StatelessWidget { + const TemplatesList({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ListView( + padding: const EdgeInsets.all(12), + children: [ + const Text( + 'Workflow Templates', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 16), + _TemplateCard( + title: 'Authentication Flow', + description: 'Login, get token, and fetch user data', + icon: Icons.security, + ), + _TemplateCard( + title: 'CRUD Operations', + description: 'Create, read, update, and delete resources', + icon: Icons.storage, + ), + _TemplateCard( + title: 'Data Processing', + description: 'Fetch, transform, and store data', + icon: Icons.transform, + ), + ], + ); + } +} + +class _APIComponentCard extends StatelessWidget { + final IconData icon; + final String title; + final String description; + final RequestModel? requestModel; + + const _APIComponentCard({ + required this.icon, + required this.title, + required this.description, + required this.requestModel, + }); + + @override + Widget build(BuildContext context) { + return Card( + margin: const EdgeInsets.only(bottom: 8), + child: Draggable( + data: WorkflowNodeModel.create( + requestId: 'temp_id', + position: const Offset(0, 0), + label: title, + requestModel: requestModel, + ), + feedback: Material( + color: Colors.transparent, + elevation: 4, + borderRadius: BorderRadius.circular(8), + child: Container( + width: 200, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.blue, width: 2), + ), padding: const EdgeInsets.all(16), - child: Row( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - ElevatedButton( - onPressed: () { - ref.read(workflowsNotifierProvider.notifier).addWorkflow('New Workflow'); - }, - child: const Text('New Workflow'), + Row( + children: [ + Icon(icon, color: Colors.blue, size: 20), + const SizedBox(width: 8), + Text( + title, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ], ), - const SizedBox(width: 16), - ElevatedButton( - onPressed: () { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('Add Node'), - content: const Text('Select node type'), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text('Cancel'), - ), - ], + const SizedBox(height: 8), + Text( + description, + style: TextStyle(color: Colors.grey[600], fontSize: 12), + ), + ], + ), + ), + ), + childWhenDragging: Opacity( + opacity: 0.5, + child: ListTile( + leading: Icon(icon, color: Colors.grey), + title: Text(title), + subtitle: Text(description), + ), + ), + child: ListTile( + leading: Icon(icon, color: Colors.blue), + title: Text(title), + subtitle: Text(description), + contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), + ), + ), + ); + } +} + +class _FlowControlCard extends StatelessWidget { + final IconData icon; + final String title; + final String description; + final NodeType nodeType; + + const _FlowControlCard({ + required this.icon, + required this.title, + required this.description, + required this.nodeType, + }); + + @override + Widget build(BuildContext context) { + return Card( + margin: const EdgeInsets.only(bottom: 8), + child: Draggable( + data: WorkflowNodeModel.create( + requestId: 'temp_id', + position: const Offset(0, 0), + label: title, + nodeType: nodeType, + nodeData: nodeType == NodeType.condition + ? {'condition': 'statusCode == 200'} + : nodeType == NodeType.action + ? {'actionType': 'transform', 'actionConfig': ''} + : {}, + ), + feedback: Material( + color: Colors.transparent, + elevation: 4, + borderRadius: BorderRadius.circular(8), + child: Container( + width: 200, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: nodeType == NodeType.condition + ? Colors.amber + : nodeType == NodeType.action + ? Colors.green + : Colors.purple, + width: 2, + ), + ), + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Icon( + icon, + color: nodeType == NodeType.condition + ? Colors.amber + : nodeType == NodeType.action + ? Colors.green + : Colors.purple, + size: 20, + ), + const SizedBox(width: 8), + Text( + title, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, ), - ); - }, - child: const Text('Add Node'), + ), + ], + ), + const SizedBox(height: 8), + Text( + description, + style: TextStyle(color: Colors.grey[600], fontSize: 12), ), ], ), ), - ], + ), + childWhenDragging: Opacity( + opacity: 0.5, + child: ListTile( + leading: Icon(icon, color: Colors.grey), + title: Text(title), + subtitle: Text(description), + ), + ), + child: ListTile( + leading: Icon( + icon, + color: nodeType == NodeType.condition + ? Colors.amber + : nodeType == NodeType.action + ? Colors.green + : Colors.purple, + ), + title: Text(title), + subtitle: Text(description), + contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), + ), + ), + ); + } +} + +class _TemplateCard extends StatelessWidget { + final String title; + final String description; + final IconData icon; + + const _TemplateCard({ + required this.title, + required this.description, + required this.icon, + }); + + @override + Widget build(BuildContext context) { + return Card( + margin: const EdgeInsets.only(bottom: 8), + child: ListTile( + leading: Icon(icon, color: Colors.teal), + title: Text(title), + subtitle: Text(description), + contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), + trailing: const Icon(Icons.add_circle_outline, color: Colors.teal), + onTap: () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Template "$title" will be implemented soon'), + ), + ); + }, ), ); } } + +class GridBackgroundPainter extends CustomPainter { + final Color gridColor; + final double step; + + GridBackgroundPainter({ + required this.gridColor, + required this.step, + }); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = gridColor + ..strokeWidth = 1.0; + + for (double x = 0; x < size.width; x += step) { + canvas.drawLine(Offset(x, 0), Offset(x, size.height), paint); + } + for (double y = 0; y < size.height; y += step) { + canvas.drawLine(Offset(0, y), Offset(size.width, y), paint); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_canvas.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_canvas.dart index 3afe9fc42..b9f81cc44 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_canvas.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_canvas.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:uuid/uuid.dart'; -import '../models/workflow_node_model.dart'; -import '../models/workflow_connection_model.dart'; -import '../models/workflow_connection.dart'; +import 'models/workflow_node_model.dart'; +import 'workflow_node.dart'; import 'workflow_providers.dart'; +import 'models/workflow_model.dart'; +import 'grid_painter.dart'; +import 'connection_painter.dart'; class WorkflowCanvas extends ConsumerStatefulWidget { final String? workflowId; @@ -19,188 +21,473 @@ class WorkflowCanvas extends ConsumerStatefulWidget { } class _WorkflowCanvasState extends ConsumerState { - late Offset _startPosition; - bool _isDragging = false; - WorkflowConnectionModel? _tempConnection; + final TransformationController _transformationController = + TransformationController(); + double _scale = 1.0; + + final GlobalKey _canvasKey = GlobalKey(); + + String? _sourceNodeId; + Offset? _connectionStart; + Offset? _connectionEnd; + + String? _draggedNodeId; + Offset? _lastDragPosition; @override - Widget build(BuildContext context) { - final workflows = ref.watch(workflowsNotifierProvider); + void initState() { + super.initState(); + _transformationController.value = Matrix4.identity()..translate(50.0, 50.0); + } + + void _handleApiNodeAdded(WorkflowNodeModel newNode, Offset position) { + try { + if (widget.workflowId == null) return; + + final RenderBox? renderBox = + _canvasKey.currentContext?.findRenderObject() as RenderBox?; + if (renderBox == null) return; + + final viewportCenter = _transformationController.toScene( + Offset( + renderBox.size.width / 2, + renderBox.size.height / 2, + ), + ); + + final finalPosition = position.dx == 0 && position.dy == 0 + ? Offset(viewportCenter.dx - 80, + viewportCenter.dy - 40) // Center with offset + : _transformationController.toScene(position); + + final node = newNode.copyWith( + id: const Uuid().v4(), + requestId: widget.workflowId!, + position: finalPosition, + ); + + ref.read(workflowsNotifierProvider.notifier).addNode( + widget.workflowId!, + node, + ); + } catch (e) { + debugPrint('Error adding node: $e'); + } + } + + void _startConnection(String nodeId, Offset position) { + setState(() { + _sourceNodeId = nodeId; + _connectionStart = position; + _connectionEnd = position; + }); + } + + void _updateConnection(Offset position) { + if (_sourceNodeId == null) return; + + setState(() { + _connectionEnd = position; + }); + } + + void _finishConnection(String? targetNodeId) { + if (_sourceNodeId != null && + targetNodeId != null && + _sourceNodeId != targetNodeId) { + ref.read(workflowsNotifierProvider.notifier).createConnection( + widget.workflowId!, + _sourceNodeId!, + targetNodeId, + ); + } + + setState(() { + _sourceNodeId = null; + _connectionStart = null; + _connectionEnd = null; + }); + } + + Offset? _dragOffsetFromNodeTopLeft; + + void _startNodeDrag(String nodeId, Offset globalPosition) { + final scenePosition = _transformationController.toScene(globalPosition); + + final workflows = ref.read(workflowsNotifierProvider); final currentWorkflow = workflows.firstWhere( (workflow) => workflow.id == widget.workflowId, orElse: () => throw Exception('Workflow not found'), ); - final nodePositions = Map.fromEntries( - currentWorkflow.nodes.map((node) => MapEntry(node.id, node.position)), + final node = currentWorkflow.nodes.firstWhere( + (node) => node.id == nodeId, + orElse: () => throw Exception('Node not found'), ); - return GestureDetector( - onPanStart: (details) { - _startPosition = details.localPosition; - }, - onPanUpdate: (details) { - setState(() { - _isDragging = true; - _tempConnection = WorkflowConnectionModel( - id: const Uuid().v4(), - sourceId: '', - targetId: '', - workflowId: widget.workflowId!, - position: details.localPosition, - ); - }); - }, - onPanEnd: (details) { - setState(() { - _isDragging = false; - _tempConnection = null; - }); - }, - child: Stack( - children: [ - // Background grid - CustomPaint( - painter: _GridPainter(), - size: const Size(double.infinity, double.infinity), - ), - // Nodes - ...currentWorkflow.nodes.map((node) => Positioned( - left: node.position.dx, - top: node.position.dy, - child: WorkflowNodeWidget( - node: node, - onNodeSelected: (nodeId) { - ref.read(workflowsNotifierProvider.notifier).selectNode(nodeId); - }, - onNodeMoved: (nodeId, newPosition) { - // TODO: Implement node movement - }, - ), - )), - // Connections - CustomPaint( - painter: _ConnectionPainter( - connections: currentWorkflow.connections, - tempConnection: _tempConnection, - nodePositions: nodePositions, - ), - size: const Size(double.infinity, double.infinity), - ), - ], - ), - ); + final offsetFromTopLeft = scenePosition - node.position; + + setState(() { + _draggedNodeId = nodeId; + _lastDragPosition = scenePosition; + _dragOffsetFromNodeTopLeft = offsetFromTopLeft; + }); } -} -class _GridPainter extends CustomPainter { - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = Colors.grey.withOpacity(0.1) - ..strokeWidth = 1; + void _updateNodeDrag(Offset globalPosition) { + if (_draggedNodeId == null || _dragOffsetFromNodeTopLeft == null) return; - for (double i = 0; i < size.width; i += 20) { - canvas.drawLine(Offset(i, 0), Offset(i, size.height), paint); - } + final scenePosition = _transformationController.toScene(globalPosition); - for (double i = 0; i < size.height; i += 20) { - canvas.drawLine(Offset(0, i), Offset(size.width, i), paint); - } + final newTopLeft = scenePosition - _dragOffsetFromNodeTopLeft!; + + ref.read(workflowsNotifierProvider.notifier).updateNodePosition( + widget.workflowId!, + _draggedNodeId!, + newTopLeft, + ); + + setState(() { + _lastDragPosition = scenePosition; + }); + } + + void _finishNodeDrag() { + setState(() { + _draggedNodeId = null; + _lastDragPosition = null; + }); } @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => false; -} + Widget build(BuildContext context) { + final workflowId = widget.workflowId; + if (workflowId == null) { + return const Center(child: Text('No workflow selected')); + } -class _ConnectionPainter extends CustomPainter { - final List connections; - final WorkflowConnectionModel? tempConnection; - final Map nodePositions; - final Color color; - final double width; - - _ConnectionPainter({ - required this.connections, - this.tempConnection, - required this.nodePositions, - this.color = Colors.blue, - this.width = 2.0, - }); + final currentWorkflow = ref.watch( + activeWorkflowProvider, + ); - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = color - ..strokeWidth = width - ..style = PaintingStyle.stroke; - - for (var connection in connections) { - if (nodePositions.containsKey(connection.sourceId) && - nodePositions.containsKey(connection.targetId)) { - final start = nodePositions[connection.sourceId]!; - final end = nodePositions[connection.targetId]!; - canvas.drawLine(start, end, paint); - } + if (currentWorkflow == null) { + return const Center(child: Text('Workflow not found')); } - if (tempConnection != null) { - final start = tempConnection!.position; - final targetId = tempConnection!.targetId; - - if (targetId.isNotEmpty && nodePositions.containsKey(targetId)) { - final end = nodePositions[targetId]!; - canvas.drawLine(start, end, paint); - } + final isConnectionMode = ref.watch(connectionModeProvider); + + final currentConnections = currentWorkflow.connections; + + final nodePositions = {}; + for (final node in currentWorkflow.nodes) { + nodePositions[node.id] = node.position; } - } - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => true; -} + final workflowExecutionState = ref.watch(workflowExecutionStateProvider); + final runningNodeIds = workflowExecutionState.currentNodeId != null + ? [workflowExecutionState.currentNodeId!] + : []; + final completedNodeIds = workflowExecutionState.executedNodeIds.toList(); + + return Stack( + key: _canvasKey, + children: [ + Container( + color: const Color(0xFF121212), + width: double.infinity, + height: double.infinity, + child: CustomPaint( + painter: GridPainter( + gridColor: Colors.grey.shade800.withOpacity(0.3), + gridWidth: 1.0, + gridSpacing: 40 * _scale, + ), + ), + ), -class WorkflowNodeWidget extends StatelessWidget { - final WorkflowNodeModel node; - final Function(String) onNodeSelected; - final Function(String, Offset) onNodeMoved; + InteractiveViewer( + transformationController: _transformationController, + minScale: 0.3, + maxScale: 2.0, + constrained: false, + onInteractionUpdate: (details) { + setState(() { + _scale = _transformationController.value.getMaxScaleOnAxis(); + }); + }, + child: SizedBox( + width: 3000, + height: 2000, + child: Stack( + clipBehavior: Clip.none, + children: [ + Positioned.fill( + child: GestureDetector( + onTapDown: (details) { + if (isConnectionMode && _sourceNodeId != null) { + final scenePosition = _transformationController + .toScene(details.localPosition); + + final hitNodeId = _hitTestNodes( + scenePosition, currentWorkflow.nodes); + + if (hitNodeId == null) { + setState(() { + _sourceNodeId = null; + _connectionStart = null; + _connectionEnd = null; + }); + } + } + + if (!isConnectionMode) { + ref.read(selectedNodeIdProvider.notifier).state = null; + } + }, + onPanUpdate: (details) { + if (isConnectionMode && _sourceNodeId != null) { + _updateConnection(_transformationController + .toScene(details.localPosition)); + } + }, + child: Container( + color: Colors.transparent, + ), + ), + ), - WorkflowNodeWidget({ - required this.node, - required this.onNodeSelected, - required this.onNodeMoved, - }); + CustomPaint( + painter: ConnectionPainter( + connections: currentConnections, + nodes: currentWorkflow.nodes, + nodePositions: nodePositions, + tempConnectionStart: _connectionStart, + tempConnectionEnd: _connectionEnd, + runningNodeIds: runningNodeIds, + completedNodeIds: completedNodeIds, + ), + size: const Size(3000, 2000), + ), - @override - Widget build(BuildContext context) { - return Draggable( - maxSimultaneousDrags: 1, - feedback: Container( - width: 100, - height: 50, - color: Colors.blue.withOpacity(0.5), - child: Center( - child: Text(node.id), + ...currentWorkflow.nodes.map( + (node) { + return Positioned( + left: node.position.dx, + top: node.position.dy, + child: MouseRegion( + cursor: SystemMouseCursors.grab, + child: GestureDetector( + onTap: () { + ref.read(selectedNodeIdProvider.notifier).state = + node.id; + + if (isConnectionMode) { + if (_sourceNodeId == null) { + final nodeCenter = Offset( + node.position.dx + 80, // Half of node width + node.position.dy + 40, // Half of node height + ); + _startConnection(node.id, nodeCenter); + } else if (_sourceNodeId != node.id) { + _finishConnection(node.id); + } else { + setState(() { + _sourceNodeId = null; + _connectionStart = null; + _connectionEnd = null; + }); + } + } + }, + onPanStart: (details) { + if (!isConnectionMode) { + _startNodeDrag(node.id, details.globalPosition); + } + }, + onPanUpdate: (details) { + if (!isConnectionMode) { + _updateNodeDrag(details.globalPosition); + } else if (_sourceNodeId == node.id) { + final localPosition = _transformationController + .toScene(details.localPosition); + _updateConnection(localPosition); + } + }, + onPanEnd: (details) { + if (!isConnectionMode) { + _finishNodeDrag(); + } else if (_sourceNodeId == node.id) { + setState(() { + _sourceNodeId = null; + _connectionStart = null; + _connectionEnd = null; + }); + } + }, + child: WorkflowNode( + node: node, + isSelected: node.id == ref.watch(selectedNodeIdProvider), + isRunning: + workflowExecutionState.currentNodeId == node.id, + isCompleted: + workflowExecutionState.executedNodeIds.contains(node.id), + hasError: workflowExecutionState.executionResults[node.id] + ?['status'] == + 'error', + ), + ), + ), + ); + }, + ).toList(), + + Positioned.fill( + child: DragTarget( + builder: (context, candidateData, rejectedData) { + return const SizedBox.expand(); + }, + onWillAccept: (data) => true, + onAccept: (data) { + final RenderBox renderBox = + context.findRenderObject() as RenderBox; + final localPosition = renderBox.globalToLocal(Offset.zero); + final position = _transformationController.toScene(localPosition); + + _handleApiNodeAdded(data, position); + }, + ), + ), + ], + ), + ), ), - ), - childWhenDragging: Container( - width: 100, - height: 50, - color: Colors.grey, - child: Center( - child: Text(node.id), + + Positioned( + left: 16, + bottom: 16, + child: _buildCanvasControls(), + ), + + Positioned( + top: 16, + right: 16, + child: _buildCanvasInfo(currentWorkflow), + ), + ], + ); + } + + Widget _buildCanvasControls() { + return Card( + color: Colors.black.withOpacity(0.7), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.home, color: Colors.white), + onPressed: () { + _transformationController.value = Matrix4.identity() + ..translate(50.0, 50.0); + setState(() { + _scale = 1.0; + }); + }, + tooltip: 'Reset View', + ), + IconButton( + icon: const Icon(Icons.remove, color: Colors.white), + onPressed: () { + final scale = + _transformationController.value.getMaxScaleOnAxis(); + if (scale > 0.5) { + _transformationController.value.scale(0.8, 0.8, 0.8); + setState(() { + _scale = + _transformationController.value.getMaxScaleOnAxis(); + }); + } + }, + tooltip: 'Zoom Out', + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: Colors.blue.withOpacity(0.2), + borderRadius: BorderRadius.circular(4), + ), + child: Text( + '${(_scale * 100).toInt()}%', + style: const TextStyle(color: Colors.white), + ), + ), + IconButton( + icon: const Icon(Icons.add, color: Colors.white), + onPressed: () { + final scale = + _transformationController.value.getMaxScaleOnAxis(); + if (scale < 2.0) { + _transformationController.value.scale(1.25, 1.25, 1.25); + setState(() { + _scale = + _transformationController.value.getMaxScaleOnAxis(); + }); + } + }, + tooltip: 'Zoom In', + ), + ], ), ), - onDragEnd: (details) { - onNodeMoved(node.id, details.offset); - }, - child: Container( - width: 100, - height: 50, - color: Colors.blue, - child: Center( - child: Text(node.id), + ); + } + + Widget _buildCanvasInfo(WorkflowModel workflow) { + return Card( + color: Colors.black.withOpacity(0.7), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + workflow.name, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 4), + Text( + '${workflow.nodes.length} nodes · ${workflow.connections.length} connections', + style: TextStyle( + color: Colors.grey[400], + fontSize: 12, + ), + ), + ], ), ), ); } + + String? _hitTestNodes(Offset position, List nodes) { + for (final node in nodes) { + final nodeRect = Rect.fromLTWH( + node.position.dx, + node.position.dy, + 160, + 80, + ); + + if (nodeRect.contains(position)) { + return node.id; + } + } + + return null; + } } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_connection.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_connection.dart index c726b7a57..64f1ce63c 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_connection.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_connection.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import '../models/workflow_connection_model.dart'; +import 'models/workflow_connection_model.dart'; class WorkflowConnectionWidget extends StatelessWidget { final WorkflowConnectionModel connection; @@ -8,7 +8,8 @@ class WorkflowConnectionWidget extends StatelessWidget { final VoidCallback onTap; final VoidCallback onRemove; - WorkflowConnectionWidget({ + const WorkflowConnectionWidget({ + super.key, required this.connection, required this.sourcePosition, required this.targetPosition, @@ -99,7 +100,7 @@ class ConnectionPainter extends CustomPainter { @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return oldDelegate is ConnectionPainter && - (oldDelegate.source != source || + (oldDelegate.source != source || oldDelegate.target != target || oldDelegate.isConditional != isConditional); } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart index 7cf849b69..ae82a1bcb 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart @@ -1,142 +1,327 @@ -// import 'package:apidash_core/apidash_core.dart'; -// import 'package:flutter/material.dart'; -// import 'package:flutter_riverpod/flutter_riverpod.dart'; -// import '../models/node_status.dart'; -// import '../models/workflow_node_model.dart'; -// import '../models/workflow_connection_model.dart'; -// import 'workflow_providers.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; -// /// Widget that displays a workflow node -// @immutable -// class WorkflowNodeWidget extends StatefulWidget { -// final WorkflowNodeModel node; -// final double scale; -// final Function(double, double) onDragUpdate; -// final Function(Offset) onConnect; +import 'models/models.dart'; +class WorkflowNode extends ConsumerWidget { + final WorkflowNodeModel node; + final bool isSelected; + final bool isRunning; + final bool isCompleted; + final bool hasError; -// const WorkflowNodeWidget({ -// required this.node, -// required this.scale, -// required this.onDragUpdate, -// required this.onConnect, -// Key? key, -// }) : super(key: key); + const WorkflowNode({ + Key? key, + required this.node, + this.isSelected = false, + this.isRunning = false, + this.isCompleted = false, + this.hasError = false, + }) : super(key: key); -// @override -// State createState() => _WorkflowNodeWidgetState(); -// } - -// class _WorkflowNodeWidgetState extends State { -// Offset _dragOffset = Offset.zero; -// bool _isDragging = false; - -// @override -// Widget build(BuildContext context) { -// return Positioned( -// left: widget.node.position.dx * widget.scale + _dragOffset.dx, -// top: widget.node.position.dy * widget.scale + _dragOffset.dy, -// child: GestureDetector( -// onPanStart: (details) { -// setState(() { -// _isDragging = true; -// _dragOffset = details.localPosition; -// }); -// }, -// onPanUpdate: (details) { -// if (_isDragging) { -// setState(() { -// _dragOffset = details.localPosition; -// }); -// widget.onDragUpdate(_dragOffset.dx, _dragOffset.dy); -// } -// }, -// onPanEnd: (details) { -// setState(() { -// _isDragging = false; -// _dragOffset = Offset.zero; -// }); -// }, -// child: MouseRegion( -// onEnter: (_) { -// setState(() { -// _isDragging = false; -// _dragOffset = Offset.zero; -// }); -// }, -// child: Container( -// width: 200 * widget.scale, -// height: 100 * widget.scale, -// decoration: BoxDecoration( -// color: Colors.white, -// border: Border.all( -// color: widget.node.status.color, -// width: 2 * widget.scale, -// ), -// borderRadius: BorderRadius.circular(8 * widget.scale), -// boxShadow: [ -// BoxShadow( -// color: Colors.black.withOpacity(0.1), -// blurRadius: 4 * widget.scale, -// offset: Offset(0, 2 * widget.scale), -// ), -// ], -// ), -// child: Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Padding( -// padding: EdgeInsets.all(8 * widget.scale), -// child: Row( -// children: [ -// Expanded( -// child: Text( -// widget.node.nodeType.toString(), -// style: TextStyle( -// fontSize: 16 * widget.scale, -// fontWeight: FontWeight.bold, -// ), -// ), -// ), -// IconButton( -// icon: Icon(Icons.delete, size: 20 * widget.scale), -// onPressed: () { -// context.read(workflowsNotifierProvider.notifier).removeNode(widget.node.id); -// }, -// ), -// ], -// ), -// ), -// Padding( -// padding: EdgeInsets.all(8 * widget.scale), -// child: Text( -// widget.node.nodeData.toString(), -// style: TextStyle(fontSize: 12 * widget.scale), -// ), -// ), -// Padding( -// padding: EdgeInsets.all(8 * widget.scale), -// child: Row( -// children: [ -// IconButton( -// icon: Icon(Icons.edit, size: 20 * widget.scale), -// onPressed: () { -// context.read(workflowsNotifierProvider.notifier).selectNode(widget.node.id); -// }, -// ), -// IconButton( -// icon: Icon(Icons.connect_without_contact, size: 20 * widget.scale), -// onPressed: () { -// widget.onConnect(widget.node.position); -// }, -// ), -// ], -// ), -// ), -// ], -// ), -// ), -// ), -// ), -// ); -// } -// } + @override + Widget build(BuildContext context, WidgetRef ref) { + final nodeSize = _getNodeSize(); + final nodeColor = _getNodeColor(); + final borderColor = isSelected ? Colors.blue : Colors.transparent; + final iconData = _getNodeIcon(); + final elevation = isSelected ? 8.0 : 4.0; + + return Material( + elevation: elevation, + color: Colors.transparent, + borderRadius: BorderRadius.circular(8), + shadowColor: isRunning + ? Colors.blue.withOpacity(0.6) + : isCompleted + ? Colors.green.withOpacity(0.6) + : hasError + ? Colors.red.withOpacity(0.6) + : Colors.black.withOpacity(0.3), + child: Container( + width: nodeSize.width, + height: nodeSize.height, + decoration: BoxDecoration( + color: nodeColor, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: borderColor, + width: 2, + ), + boxShadow: [ + BoxShadow( + color: isRunning + ? Colors.blue.withOpacity(0.3) + : isCompleted + ? Colors.green.withOpacity(0.3) + : hasError + ? Colors.red.withOpacity(0.3) + : Colors.black.withOpacity(0.2), + blurRadius: 5, + spreadRadius: 1, + ), + ], + ), + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + iconData, + color: Colors.white, + size: 18, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + node.label.isEmpty + ? 'Node ${node.id.substring(0, 4)}' + : node.label, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 14, + ), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + const SizedBox(height: 8), + if (node.nodeType == NodeType.request && node.requestModel != null) + _buildRequestDetails(), + if (node.nodeType == NodeType.condition) + _buildConditionDetails(), + if (node.nodeType == NodeType.action) + _buildActionDetails(), + if (node.nodeType == NodeType.response) + _buildResponseDetails(), + ], + ), + ), + + Positioned( + top: 4, + right: 4, + child: Container( + width: 12, + height: 12, + decoration: BoxDecoration( + color: isRunning + ? Colors.blue + : isCompleted + ? Colors.green + : hasError + ? Colors.red + : Colors.grey.withOpacity(0.5), + shape: BoxShape.circle, + ), + child: isRunning + ? const Center( + child: SizedBox( + width: 6, + height: 6, + child: CircularProgressIndicator( + strokeWidth: 1, + valueColor: + AlwaysStoppedAnimation(Colors.white), + ), + ), + ) + : null, + ), + ), + ], + ), + ), + ); + } + + Widget _buildRequestDetails() { + final method = node.requestModel?.method ?? 'GET'; + final url = node.requestModel?.url ?? ''; + final shortUrl = url.length > 25 ? '${url.substring(0, 22)}...' : url; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), + decoration: BoxDecoration( + color: _getMethodColor(method), + borderRadius: BorderRadius.circular(4), + ), + child: Text( + method, + style: const TextStyle( + color: Colors.white, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + ), + ), + const SizedBox(height: 2), + Text( + shortUrl, + style: TextStyle( + color: Colors.white.withOpacity(0.8), + fontSize: 10, + ), + overflow: TextOverflow.ellipsis, + ), + ], + ); + } + + Widget _buildConditionDetails() { + final condition = node.nodeData['condition'] as String? ?? ''; + final shortCondition = condition.length > 30 ? '${condition.substring(0, 27)}...' : condition; + + return Container( + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.2), + borderRadius: BorderRadius.circular(4), + ), + child: Text( + shortCondition.isEmpty ? 'No condition set' : shortCondition, + style: TextStyle( + color: Colors.white.withOpacity(0.8), + fontSize: 10, + fontStyle: shortCondition.isEmpty ? FontStyle.italic : FontStyle.normal, + ), + ), + ); + } + + Widget _buildActionDetails() { + final actionType = node.nodeData['actionType'] as String? ?? 'transform'; + + return Row( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.2), + borderRadius: BorderRadius.circular(4), + ), + child: Text( + actionType.toUpperCase(), + style: const TextStyle( + color: Colors.white, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ); + } + + Widget _buildResponseDetails() { + final statusCode = node.simulatedStatusCode; + + return Row( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), + decoration: BoxDecoration( + color: _getStatusCodeColor(statusCode), + borderRadius: BorderRadius.circular(4), + ), + child: Text( + 'Status: $statusCode', + style: const TextStyle( + color: Colors.white, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ); + } + + Size _getNodeSize() { + switch (node.nodeType) { + case NodeType.request: + return const Size(160, 80); + case NodeType.response: + return const Size(160, 60); + case NodeType.condition: + return const Size(160, 60); + case NodeType.action: + return const Size(160, 60); + } + } + + Color _getNodeColor() { + final baseColors = { + NodeType.request: const Color(0xFF2d3748), + NodeType.response: const Color(0xFF553C9A), + NodeType.condition: const Color(0xFFb91c1c), + NodeType.action: const Color(0xFF047857), + }; + + Color baseColor = baseColors[node.nodeType] ?? const Color(0xFF2d3748); + + if (isRunning) { + return Color.lerp(baseColor, Colors.blue, 0.15) ?? baseColor; + } else if (isCompleted) { + return Color.lerp(baseColor, Colors.green, 0.15) ?? baseColor; + } else if (hasError) { + return Color.lerp(baseColor, Colors.red, 0.15) ?? baseColor; + } else { + return baseColor; + } + } + + IconData _getNodeIcon() { + switch (node.nodeType) { + case NodeType.request: + return Icons.send; + case NodeType.response: + return Icons.call_received; + case NodeType.condition: + return Icons.fork_right; + case NodeType.action: + return Icons.settings; + } + } + + Color _getMethodColor(String method) { + switch (method.toUpperCase()) { + case 'GET': + return const Color(0xFF3182CE); + case 'POST': + return const Color(0xFF38A169); + case 'PUT': + return const Color(0xFFDD6B20); + case 'DELETE': + return const Color(0xFFE53E3E); + case 'PATCH': + return const Color(0xFF805AD5); + default: + return const Color(0xFF718096); + } + } + + Color _getStatusCodeColor(int statusCode) { + if (statusCode >= 200 && statusCode < 300) { + return Colors.green; + } else if (statusCode >= 300 && statusCode < 400) { + return Colors.blue; + } else if (statusCode >= 400 && statusCode < 500) { + return Colors.orange; + } else if (statusCode >= 500) { + return Colors.red; + } else { + return Colors.grey; + } + } +} From 88b8ca497a145f8642682e9173fb0416c8340af1 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 14 Apr 2025 12:57:17 +0530 Subject: [PATCH 145/188] feat: add LogsViewer and NodeDetailsPanel widgets for enhanced workflow visualization --- .../workflow_builder/widgets/logs_viewer.dart | 299 ++++++++ .../widgets/node_details_panel.dart | 640 ++++++++++++++++++ .../src/workflow_builder/widgets/widgets.dart | 3 + 3 files changed, 942 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/widgets/node_details_panel.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/widgets/widgets.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart b/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart new file mode 100644 index 000000000..6062b9019 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart @@ -0,0 +1,299 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../models/workflow_execution_state.dart'; +import '../workflow_providers.dart'; + +class LogsViewer extends ConsumerWidget { + final String workflowId; + + const LogsViewer({ + Key? key, + required this.workflowId, + }) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final executionState = ref.watch(workflowExecutionStateProvider); + final logs = ref.watch(workflowLogsProvider(workflowId)); + + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: const Color(0xFF1E1E1E), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: Colors.grey[800]!, + width: 1, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _buildHeader(executionState), + const SizedBox(height: 8), + Expanded( + child: logs.isEmpty + ? _buildEmptyState() + : _buildLogsList(logs), + ), + ], + ), + ); + } + + Widget _buildHeader(WorkflowExecutionState executionState) { + return Row( + children: [ + const Icon( + Icons.history, + color: Colors.blue, + size: 20, + ), + const SizedBox(width: 8), + const Text( + 'Execution Logs', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const Spacer(), + if (executionState.isRunning) + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: Colors.blue.withOpacity(0.2), + borderRadius: BorderRadius.circular(4), + border: Border.all(color: Colors.blue, width: 1), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 8, + height: 8, + decoration: const BoxDecoration( + color: Colors.blue, + shape: BoxShape.circle, + ), + ), + const SizedBox(width: 4), + const Text( + 'LIVE', + style: TextStyle( + color: Colors.blue, + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), + ], + ), + ), + if (executionState.isCompleted) + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: Colors.green.withOpacity(0.2), + borderRadius: BorderRadius.circular(4), + border: Border.all(color: Colors.green, width: 1), + ), + child: const Text( + 'COMPLETED', + style: TextStyle( + color: Colors.green, + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), + ), + ], + ); + } + + Widget _buildEmptyState() { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.info_outline, + size: 40, + color: Colors.grey[600], + ), + const SizedBox(height: 16), + Text( + 'No execution logs yet', + style: TextStyle( + color: Colors.grey[400], + fontSize: 16, + ), + ), + const SizedBox(height: 8), + Text( + 'Start the workflow execution to see logs', + style: TextStyle( + color: Colors.grey[600], + fontSize: 14, + ), + ), + ], + ), + ); + } + + Widget _buildLogsList(List logs) { + return ListView.builder( + itemCount: logs.length, + reverse: true, + itemBuilder: (context, index) { + final log = logs[logs.length - 1 - index]; + return _buildLogItem(log); + }, + ); + } + + Widget _buildLogItem(WorkflowLogEntry log) { + Color getLogColor() { + switch (log.level) { + case LogLevel.info: + return Colors.blue; + case LogLevel.success: + return Colors.green; + case LogLevel.warning: + return Colors.orange; + case LogLevel.error: + return Colors.red; + case LogLevel.debug: + return Colors.grey; + } + } + + IconData getLogIcon() { + switch (log.level) { + case LogLevel.info: + return Icons.info_outline; + case LogLevel.success: + return Icons.check_circle_outline; + case LogLevel.warning: + return Icons.warning_amber_outlined; + case LogLevel.error: + return Icons.error_outline; + case LogLevel.debug: + return Icons.code; + } + } + + final color = getLogColor(); + + return Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(top: 2), + child: Icon( + getLogIcon(), + size: 16, + color: color, + ), + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + log.nodeId != null + ? 'Node ${log.nodeId!.substring(0, 4)}' + : 'Workflow', + style: TextStyle( + color: color, + fontWeight: FontWeight.bold, + fontSize: 13, + ), + ), + const Spacer(), + Text( + _formatTimestamp(log.timestamp), + style: TextStyle( + color: Colors.grey[600], + fontSize: 11, + ), + ), + ], + ), + const SizedBox(height: 2), + Text( + log.message, + style: const TextStyle( + color: Colors.white, + fontSize: 13, + ), + ), + if (log.details != null) ...[ + const SizedBox(height: 4), + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.3), + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: Colors.grey[850]!, + width: 1, + ), + ), + child: Text( + log.details!, + style: TextStyle( + color: Colors.grey[400], + fontSize: 12, + fontFamily: 'monospace', + ), + ), + ), + ], + ], + ), + ), + ], + ), + ); + } + + String _formatTimestamp(DateTime timestamp) { + final now = DateTime.now(); + if (now.difference(timestamp).inDays == 0) { + return '${timestamp.hour.toString().padLeft(2, '0')}:${timestamp.minute.toString().padLeft(2, '0')}:${timestamp.second.toString().padLeft(2, '0')}'; + } else { + return '${timestamp.month.toString().padLeft(2, '0')}/${timestamp.day.toString().padLeft(2, '0')} ${timestamp.hour.toString().padLeft(2, '0')}:${timestamp.minute.toString().padLeft(2, '0')}'; + } + } +} + +class WorkflowLogEntry { + final String message; + final LogLevel level; + final DateTime timestamp; + final String? nodeId; + final String? details; + + WorkflowLogEntry({ + required this.message, + this.level = LogLevel.info, + DateTime? timestamp, + this.nodeId, + this.details, + }) : timestamp = timestamp ?? DateTime.now(); +} + +enum LogLevel { + debug, + info, + success, + warning, + error, +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/widgets/node_details_panel.dart b/packages/api_testing_suite/lib/src/workflow_builder/widgets/node_details_panel.dart new file mode 100644 index 000000000..a0581be13 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/widgets/node_details_panel.dart @@ -0,0 +1,640 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../models/models.dart'; +import '../workflow_providers.dart'; + +class NodeDetailsPanel extends ConsumerWidget { + final WorkflowNodeModel node; + final String workflowId; + + const NodeDetailsPanel({ + Key? key, + required this.node, + required this.workflowId, + }) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Container( + width: 320, + color: const Color(0xFF212121), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildHeader(context, ref), + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildNodeTypeSection(ref), + const SizedBox(height: 16), + _buildNodeDetailsSection(ref), + const SizedBox(height: 16), + if (node.nodeType == NodeType.request) + _buildRequestDetailsSection(ref), + if (node.nodeType == NodeType.condition) + _buildConditionDetailsSection(ref), + if (node.nodeType == NodeType.action) + _buildActionDetailsSection(ref), + if (node.nodeType == NodeType.response) + _buildResponseDetailsSection(ref), + const SizedBox(height: 24), + _buildDependenciesSection(ref), + ], + ), + ), + ), + ], + ), + ); + } + + Widget _buildHeader(BuildContext context, WidgetRef ref) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: node.status.color.withOpacity(0.2), + border: Border( + bottom: BorderSide( + color: node.status.color.withOpacity(0.5), + width: 1, + ), + ), + ), + child: Row( + children: [ + Icon( + _getNodeTypeIcon(), + color: Colors.white, + size: 24, + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + node.label.isEmpty ? 'Node ${node.id.substring(0, 4)}' : node.label, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + const SizedBox(height: 4), + Text( + _getNodeTypeText(), + style: TextStyle( + color: Colors.white.withOpacity(0.7), + fontSize: 14, + ), + ), + ], + ), + ), + IconButton( + icon: const Icon(Icons.close, color: Colors.white), + onPressed: () { + ref.read(selectedNodeIdProvider.notifier).state = null; + }, + ), + ], + ), + ); + } + + Widget _buildNodeTypeSection(WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Node Type', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 8), + DropdownButtonFormField( + value: node.nodeType, + decoration: InputDecoration( + filled: true, + fillColor: Colors.grey[800], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + ), + style: const TextStyle(color: Colors.white), + dropdownColor: Colors.grey[800], + items: NodeType.values.map((type) { + return DropdownMenuItem( + value: type, + child: Text(_getNodeTypeText(type)), + ); + }).toList(), + onChanged: (NodeType? newValue) { + if (newValue != null) { + ref.read(workflowsNotifierProvider.notifier).updateNode( + workflowId, + node.copyWith(nodeType: newValue), + ); + } + }, + ), + ], + ); + } + + Widget _buildNodeDetailsSection(WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Node Details', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 8), + TextFormField( + initialValue: node.label, + decoration: InputDecoration( + labelText: 'Label', + filled: true, + fillColor: Colors.grey[800], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + labelStyle: TextStyle(color: Colors.grey[400]), + ), + style: const TextStyle(color: Colors.white), + onChanged: (value) { + ref.read(workflowsNotifierProvider.notifier).updateNode( + workflowId, + node.copyWith(label: value), + ); + }, + ), + const SizedBox(height: 16), + Row( + children: [ + Text( + 'Status: ${_getStatusText(node.status)}', + style: TextStyle( + color: node.status.color, + fontWeight: FontWeight.bold, + ), + ), + const Spacer(), + IconButton( + icon: const Icon(Icons.refresh, color: Colors.white), + onPressed: () { + final nextStatus = _getNextStatus(node.status); + ref.read(workflowsNotifierProvider.notifier).updateNodeStatus( + workflowId, + node.id, + nextStatus, + ); + }, + tooltip: 'Cycle status (testing only)', + ), + ], + ), + ], + ); + } + + Widget _buildRequestDetailsSection(WidgetRef ref) { + final requestModel = node.requestModel; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + const Text( + 'API Request Details', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 8), + DropdownButtonFormField( + value: requestModel?.method ?? 'GET', + decoration: InputDecoration( + labelText: 'Method', + filled: true, + fillColor: Colors.grey[800], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + labelStyle: TextStyle(color: Colors.grey[400]), + ), + style: const TextStyle(color: Colors.white), + dropdownColor: Colors.grey[800], + items: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].map((method) { + return DropdownMenuItem( + value: method, + child: Text(method), + ); + }).toList(), + onChanged: (String? newValue) { + if (newValue != null && requestModel != null) { + final updatedRequest = requestModel.copyWith(method: newValue); + ref.read(workflowsNotifierProvider.notifier).updateNode( + workflowId, + node.copyWith(requestModel: updatedRequest), + ); + } + }, + ), + const SizedBox(height: 16), + TextFormField( + initialValue: requestModel?.url ?? '', + decoration: InputDecoration( + labelText: 'URL', + filled: true, + fillColor: Colors.grey[800], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + labelStyle: TextStyle(color: Colors.grey[400]), + ), + style: const TextStyle(color: Colors.white), + onChanged: (value) { + if (requestModel != null) { + final updatedRequest = requestModel.copyWith(url: value); + ref.read(workflowsNotifierProvider.notifier).updateNode( + workflowId, + node.copyWith(requestModel: updatedRequest), + ); + } + }, + ), + const SizedBox(height: 16), + ElevatedButton.icon( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + icon: const Icon(Icons.edit, size: 16), + label: const Text('Edit Full Request'), + onPressed: () { + ScaffoldMessenger.of(ref.context).showSnackBar( + const SnackBar( + content: Text('Full request editor to be implemented'), + ), + ); + }, + ), + ], + ); + } + + Widget _buildConditionDetailsSection(WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + const Text( + 'Condition Details', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 8), + TextFormField( + initialValue: node.nodeData['condition'] as String? ?? '', + decoration: InputDecoration( + labelText: 'Condition Expression', + filled: true, + fillColor: Colors.grey[800], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + labelStyle: TextStyle(color: Colors.grey[400]), + ), + style: const TextStyle(color: Colors.white), + onChanged: (value) { + final updatedData = Map.from(node.nodeData); + updatedData['condition'] = value; + ref.read(workflowsNotifierProvider.notifier).updateNode( + workflowId, + node.copyWith(nodeData: updatedData), + ); + }, + ), + const SizedBox(height: 8), + const Text( + 'Examples: statusCode == 200 | response.body.contains("success")', + style: TextStyle( + color: Colors.grey, + fontSize: 12, + ), + ), + ], + ); + } + + Widget _buildActionDetailsSection(WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + const Text( + 'Action Details', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 8), + DropdownButtonFormField( + value: node.nodeData['actionType'] as String? ?? 'transform', + decoration: InputDecoration( + labelText: 'Action Type', + filled: true, + fillColor: Colors.grey[800], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + labelStyle: TextStyle(color: Colors.grey[400]), + ), + style: const TextStyle(color: Colors.white), + dropdownColor: Colors.grey[800], + items: ['transform', 'delay', 'notify', 'log'].map((actionType) { + return DropdownMenuItem( + value: actionType, + child: Text(actionType.toUpperCase()), + ); + }).toList(), + onChanged: (String? newValue) { + if (newValue != null) { + final updatedData = Map.from(node.nodeData); + updatedData['actionType'] = newValue; + ref.read(workflowsNotifierProvider.notifier).updateNode( + workflowId, + node.copyWith(nodeData: updatedData), + ); + } + }, + ), + const SizedBox(height: 16), + TextFormField( + initialValue: node.nodeData['actionConfig'] as String? ?? '', + decoration: InputDecoration( + labelText: 'Action Configuration', + filled: true, + fillColor: Colors.grey[800], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + labelStyle: TextStyle(color: Colors.grey[400]), + ), + style: const TextStyle(color: Colors.white), + maxLines: 3, + onChanged: (value) { + final updatedData = Map.from(node.nodeData); + updatedData['actionConfig'] = value; + ref.read(workflowsNotifierProvider.notifier).updateNode( + workflowId, + node.copyWith(nodeData: updatedData), + ); + }, + ), + ], + ); + } + + Widget _buildResponseDetailsSection(WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + const Text( + 'Response Simulation', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 8), + TextFormField( + initialValue: node.simulatedStatusCode.toString(), + decoration: InputDecoration( + labelText: 'Simulated Status Code', + filled: true, + fillColor: Colors.grey[800], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + labelStyle: TextStyle(color: Colors.grey[400]), + ), + style: const TextStyle(color: Colors.white), + keyboardType: TextInputType.number, + onChanged: (value) { + final statusCode = int.tryParse(value) ?? 200; + ref.read(workflowsNotifierProvider.notifier).updateNode( + workflowId, + node.copyWith(simulatedStatusCode: statusCode), + ); + }, + ), + const SizedBox(height: 16), + TextFormField( + initialValue: node.simulatedResponse['body'] as String? ?? '', + decoration: InputDecoration( + labelText: 'Simulated Response Body', + filled: true, + fillColor: Colors.grey[800], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, + ), + labelStyle: TextStyle(color: Colors.grey[400]), + ), + style: const TextStyle(color: Colors.white), + maxLines: 5, + onChanged: (value) { + final updatedResponse = Map.from(node.simulatedResponse); + updatedResponse['body'] = value; + ref.read(workflowsNotifierProvider.notifier).updateNode( + workflowId, + node.copyWith(simulatedResponse: updatedResponse), + ); + }, + ), + ], + ); + } + + Widget _buildDependenciesSection(WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Dependencies', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 8), + node.connections.isEmpty + ? const Text( + 'No connections', + style: TextStyle(color: Colors.grey), + ) + : Column( + children: node.connections.map((connection) { + return _buildConnectionItem(ref, connection); + }).toList(), + ), + ], + ); + } + + Widget _buildConnectionItem(WidgetRef ref, WorkflowConnectionModel connection) { + final workflows = ref.watch(workflowsNotifierProvider); + final workflowModel = workflows.firstWhere( + (w) => w.id == workflowId, + orElse: () => throw Exception('Workflow not found'), + ); + + String getNodeLabel(String nodeId) { + try { + final connectedNode = workflowModel.nodes.firstWhere( + (n) => n.id == nodeId, + orElse: () => throw Exception('Node not found'), + ); + return connectedNode.label.isEmpty + ? 'Node ${connectedNode.id.substring(0, 4)}' + : connectedNode.label; + } catch (e) { + return 'Unknown Node'; + } + } + + final sourceLabel = getNodeLabel(connection.sourceId); + final targetLabel = getNodeLabel(connection.targetId); + + return Card( + margin: const EdgeInsets.only(bottom: 8), + color: Colors.grey[850], + child: ListTile( + title: RichText( + text: TextSpan( + children: [ + TextSpan( + text: sourceLabel, + style: const TextStyle( + color: Colors.blue, + fontWeight: FontWeight.bold, + ), + ), + const TextSpan( + text: ' ➝ ', + style: TextStyle(color: Colors.grey), + ), + TextSpan( + text: targetLabel, + style: const TextStyle( + color: Colors.green, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + trailing: IconButton( + icon: const Icon(Icons.delete, color: Colors.red), + onPressed: () { + ref.read(workflowsNotifierProvider.notifier).removeConnection( + workflowId, + connection.id, + ); + }, + ), + ), + ); + } + + IconData _getNodeTypeIcon() { + switch (node.nodeType) { + case NodeType.request: + return Icons.send; + case NodeType.response: + return Icons.call_received; + case NodeType.condition: + return Icons.fork_right; + case NodeType.action: + return Icons.settings; + } + } + + String _getNodeTypeText([NodeType? type]) { + final nodeType = type ?? node.nodeType; + switch (nodeType) { + case NodeType.request: + return 'API Request'; + case NodeType.response: + return 'Response Simulation'; + case NodeType.condition: + return 'Conditional Logic'; + case NodeType.action: + return 'Action Node'; + } + } + + String _getStatusText(NodeStatus status) { + switch (status) { + case NodeStatus.inactive: + return 'Inactive'; + case NodeStatus.running: + return 'Running'; + case NodeStatus.success: + return 'Success'; + case NodeStatus.failure: + return 'Failed'; + case NodeStatus.pending: + return 'Pending'; + } + } + + NodeStatus _getNextStatus(NodeStatus current) { + switch (current) { + case NodeStatus.inactive: + return NodeStatus.pending; + case NodeStatus.pending: + return NodeStatus.running; + case NodeStatus.running: + return NodeStatus.success; + case NodeStatus.success: + return NodeStatus.failure; + case NodeStatus.failure: + return NodeStatus.inactive; + } + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/widgets/widgets.dart b/packages/api_testing_suite/lib/src/workflow_builder/widgets/widgets.dart new file mode 100644 index 000000000..6e64d61f2 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/widgets/widgets.dart @@ -0,0 +1,3 @@ +/// Export all widget +export 'logs_viewer.dart'; +export 'node_details_panel.dart'; From 67ecba073da3267904e443364f8072155e2e1d00 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 14 Apr 2025 13:06:06 +0530 Subject: [PATCH 146/188] feat: enhance workflow execution state management and logging capabilities --- .../models/workflow_execution_state.dart | 42 ++- .../workflow_builder/workflow_providers.dart | 168 ++++++++- .../workflow_builder/workflow_screens.dart | 16 +- .../workflow_builder/workflows_notifier.dart | 356 +++++++++++++----- 4 files changed, 480 insertions(+), 102 deletions(-) diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_execution_state.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_execution_state.dart index e5aa3a2b0..5cfcc4d4b 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_execution_state.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_execution_state.dart @@ -1,27 +1,55 @@ -enum WorkflowExecutionStatus { - idle, - running, - paused, - completed, - error -} +enum WorkflowExecutionStatus { idle, running, paused, completed, error } class WorkflowExecutionState { final WorkflowExecutionStatus status; final String? errorMessage; + final String? currentNodeId; + final List executedNodeIds; + final List pendingNodeIds; + final Map executionResults; + final Map executionContext; + final DateTime? startTime; + final DateTime? endTime; const WorkflowExecutionState({ this.status = WorkflowExecutionStatus.idle, this.errorMessage, + this.currentNodeId, + this.executedNodeIds = const [], + this.pendingNodeIds = const [], + this.executionResults = const {}, + this.executionContext = const {}, + this.startTime, + this.endTime, }); WorkflowExecutionState copyWith({ WorkflowExecutionStatus? status, String? errorMessage, + String? currentNodeId, + List? executedNodeIds, + List? pendingNodeIds, + Map? executionResults, + Map? executionContext, + DateTime? startTime, + DateTime? endTime, }) { return WorkflowExecutionState( status: status ?? this.status, errorMessage: errorMessage ?? this.errorMessage, + currentNodeId: currentNodeId ?? this.currentNodeId, + executedNodeIds: executedNodeIds ?? this.executedNodeIds, + pendingNodeIds: pendingNodeIds ?? this.pendingNodeIds, + executionResults: executionResults ?? this.executionResults, + executionContext: executionContext ?? this.executionContext, + startTime: startTime ?? this.startTime, + endTime: endTime ?? this.endTime, ); } + + bool get isRunning => status == WorkflowExecutionStatus.running; + bool get isPaused => status == WorkflowExecutionStatus.paused; + bool get isCompleted => status == WorkflowExecutionStatus.completed; + bool get hasError => status == WorkflowExecutionStatus.error; + bool get isIdle => status == WorkflowExecutionStatus.idle; } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_providers.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_providers.dart index 256f829a8..85eb0226a 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_providers.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_providers.dart @@ -1,14 +1,174 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../models/workflow_model.dart'; -import '../models/workflow_execution_state.dart'; +import 'models/models.dart'; +import 'widgets/logs_viewer.dart'; +import 'models/workflow_model.dart'; +import 'models/workflow_execution_state.dart'; import 'workflows_notifier.dart'; -final workflowsNotifierProvider = StateNotifierProvider>((ref) { +final workflowsNotifierProvider = + StateNotifierProvider>((ref) { return WorkflowsNotifier(); }); final currentWorkflowProvider = StateProvider((ref) => null); -final workflowExecutionStateProvider = StateProvider((ref) { +final selectedNodeIdProvider = StateProvider((ref) => null); + +final activeWorkflowProvider = Provider((ref) { + final currentWorkflowId = ref.watch(currentWorkflowProvider); + final workflows = ref.watch(workflowsNotifierProvider); + + if (currentWorkflowId == null) return null; + + return workflows.firstWhere( + (workflow) => workflow.id == currentWorkflowId, + orElse: () => throw Exception('Workflow not found'), + ); +}); + +final workflowExecutionStateProvider = + StateProvider((ref) { return const WorkflowExecutionState(); }); + +final connectionModeProvider = StateProvider((ref) => false); + +final workflowLogsProvider = Provider.family, String>((ref, workflowId) { + final workflows = ref.watch(workflowsNotifierProvider); + final workflowModel = workflows.firstWhere( + (workflow) => workflow.id == workflowId, + orElse: () => throw Exception('Workflow not found'), + ); + + final executionState = ref.watch(workflowExecutionStateProvider); + + final logs = []; + + logs.add( + WorkflowLogEntry( + message: 'Workflow initialized', + level: LogLevel.info, + timestamp: workflowModel.createdAt, + ), + ); + + if (executionState.startTime != null) { + logs.add( + WorkflowLogEntry( + message: 'Execution started', + level: LogLevel.info, + timestamp: executionState.startTime, + ), + ); + + for (final nodeId in executionState.executedNodeIds) { + final node = workflowModel.nodes.firstWhere( + (n) => n.id == nodeId, + orElse: () => throw Exception('Node not found'), + ); + + final result = executionState.executionResults[nodeId]; + final success = result != null && result['status'] == 'success'; + + logs.add( + WorkflowLogEntry( + message: success + ? 'Node execution completed successfully' + : 'Node execution failed', + level: success ? LogLevel.success : LogLevel.error, + nodeId: nodeId, + details: node.nodeType == NodeType.request + ? 'URL: ${node.requestModel?.url ?? "No URL"}\nMethod: ${node.requestModel?.method ?? "GET"}' + : null, + ), + ); + } + + if (executionState.currentNodeId != null) { + logs.add( + WorkflowLogEntry( + message: 'Node execution in progress', + level: LogLevel.info, + nodeId: executionState.currentNodeId, + ), + ); + } + + if (executionState.isCompleted && executionState.endTime != null) { + logs.add( + WorkflowLogEntry( + message: 'Workflow execution completed', + level: LogLevel.success, + timestamp: executionState.endTime, + ), + ); + } + + if (executionState.isPaused) { + logs.add( + WorkflowLogEntry( + message: 'Workflow execution paused', + level: LogLevel.warning, + ), + ); + } + + if (executionState.hasError) { + logs.add( + WorkflowLogEntry( + message: 'Workflow execution failed: ${executionState.errorMessage}', + level: LogLevel.error, + ), + ); + } + } + + return logs; +}); + +final workflowExecutionControlProvider = + Provider((ref) { + final notifier = ref.watch(workflowsNotifierProvider.notifier); + final currentId = ref.watch(currentWorkflowProvider); + + return WorkflowExecutionControl( + startExecution: () { + if (currentId != null) { + notifier.startExecution( + currentId, + (state) => ref.read(workflowExecutionStateProvider.notifier).state = + state); + } + }, + pauseExecution: () { + if (currentId != null) { + notifier.pauseExecution(currentId); + } + }, + resumeExecution: () { + if (currentId != null) { + notifier.resumeExecution(currentId); + } + }, + stopExecution: () { + if (currentId != null) { + notifier.stopExecution(currentId); + } + }, + ); +}); + +class WorkflowExecutionControl { + final VoidCallback startExecution; + final VoidCallback pauseExecution; + final VoidCallback resumeExecution; + final VoidCallback stopExecution; + + const WorkflowExecutionControl({ + required this.startExecution, + required this.pauseExecution, + required this.resumeExecution, + required this.stopExecution, + }); +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart index fdc69e8e5..6b6102a0c 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart @@ -1,4 +1,18 @@ + +// Main components +export 'workflow_builder.dart'; export 'workflow_builder_page.dart'; export 'workflow_canvas.dart'; export 'workflow_node.dart'; -export 'workflow_connection.dart'; +export 'workflow_connection.dart' hide ConnectionPainter; +export 'dag_execution_engine.dart'; + +// Providers +export 'workflow_providers.dart'; +export 'workflows_notifier.dart'; + +// Models +export 'models/models.dart'; + +// Widgets +export 'widgets/widgets.dart'; diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflows_notifier.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflows_notifier.dart index af96f2937..5ca5515a9 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflows_notifier.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflows_notifier.dart @@ -1,29 +1,43 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:uuid/uuid.dart'; -import '../models/workflow_node_model.dart'; -import '../models/workflow_connection_model.dart'; -import '../models/workflow_model.dart'; -import '../models/node_status.dart'; +import 'models/workflow_node_model.dart'; +import 'models/workflow_connection_model.dart'; +import 'models/workflow_model.dart'; +import 'models/node_status.dart'; +import 'models/workflow_execution_state.dart'; +import 'dag_execution_engine.dart'; class WorkflowsNotifier extends StateNotifier> { + final Map _executionEngines = {}; + Timer? _debounceTimer; + WorkflowsNotifier() : super([]); - void addWorkflow(String name) { + String createWorkflow({String? name, String? description}) { + final newWorkflow = WorkflowModel.create( + name: name ?? 'New Workflow', + description: description ?? '', + ); + + state = [...state, newWorkflow]; + return newWorkflow.id; + } + + void updateWorkflow(WorkflowModel updatedWorkflow) { state = [ - ...state, - WorkflowModel( - id: const Uuid().v4(), - name: name, - nodes: [], - connections: [], - ), + for (final workflow in state) + if (workflow.id == updatedWorkflow.id) updatedWorkflow else workflow, ]; } - void removeWorkflow(String id) { - state = state.where((workflow) => workflow.id != id).toList(); + void deleteWorkflow(String workflowId) { + _executionEngines[workflowId]?.dispose(); + _executionEngines.remove(workflowId); + + state = state.where((workflow) => workflow.id != workflowId).toList(); } void addNode(String workflowId, WorkflowNodeModel node) { @@ -37,89 +51,251 @@ class WorkflowsNotifier extends StateNotifier> { }).toList(); } - // void removeNode(String nodeId) { - // state = state.map((workflow) { - // // Find the node to remove - // final nodeToRemove = workflow.nodes.firstWhere( - // (node) => node.id == nodeId, - // orElse: () => throw Exception('Node not found'), - // ); + void updateNode(String workflowId, WorkflowNodeModel updatedNode) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: workflow.nodes.map((node) { + return node.id == updatedNode.id ? updatedNode : node; + }).toList(), + ); + } + return workflow; + }).toList(); + } + + void updateNodeStatus(String workflowId, String nodeId, NodeStatus status) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: workflow.nodes.map((node) { + return node.id == nodeId ? node.copyWith(status: status) : node; + }).toList(), + ); + } + return workflow; + }).toList(); + } + + void updateNodePosition(String workflowId, String nodeId, Offset position) { + _debounceTimer?.cancel(); + _debounceTimer = Timer(const Duration(milliseconds: 50), () { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: workflow.nodes.map((node) { + return node.id == nodeId + ? node.copyWith(position: position) + : node; + }).toList(), + ); + } + return workflow; + }).toList(); + }); + } + + void removeNode(String workflowId, String nodeId) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: workflow.nodes.where((node) => node.id != nodeId).toList(), + connections: workflow.connections + .where( + (conn) => conn.sourceId != nodeId && conn.targetId != nodeId) + .toList(), + ); + } + return workflow; + }).toList(); + } - // // Remove any connections involving this node - // final updatedConnections = workflow.connections.where( - // (conn) => conn.sourceId != nodeId && conn.targetId != nodeId, - // ).toList(); + void selectNode(String workflowId, String? nodeId) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith(selectedNodeId: nodeId); + } + return workflow; + }).toList(); + } - // return workflow.copyWith( - // nodes: workflow.nodes.where((node) => node.id != nodeId).toList(), - // connections: updatedConnections, - // ); - // }).toList(); - // } + void createConnection(String workflowId, String sourceId, String targetId) { + if (sourceId == targetId) return; - void updateNodePosition(String nodeId, Offset position) { state = state.map((workflow) { - return workflow.copyWith( - nodes: workflow.nodes.map((node) { - if (node.id == nodeId) { - return node.copyWith(position: position); - } - return node; - }).toList(), - ); + if (workflow.id == workflowId) { + final connectionExists = workflow.connections.any( + (conn) => conn.sourceId == sourceId && conn.targetId == targetId, + ); + + if (!connectionExists) { + final newConnection = WorkflowConnectionModel.create( + sourceId: sourceId, + targetId: targetId, + workflowId: workflowId, + position: Offset.zero, + ); + + return workflow.copyWith( + connections: [...workflow.connections, newConnection], + ); + } + } + return workflow; }).toList(); + + _resetExecutionEngine(workflowId); } - void selectNode(String nodeId) { - // Implementation for selecting a node - // This could update a selected node state or trigger an action - } - - // void addConnection(String workflowId, String sourceId, String targetId) { - // if (sourceId == targetId) return; // Prevent self-connections - - // state = state.map((workflow) { - // if (workflow.id == workflowId) { - // // Check if connection already exists - // final connectionExists = workflow.connections.any( - // (conn) => conn.sourceId == sourceId && conn.targetId == targetId, - // ); - - // if (!connectionExists) { - // final newConnection = WorkflowConnectionModel.create( - // sourceId: sourceId, - // targetId: targetId, - // workflowId: workflowId, - // position: Offset.zero, // This will be updated based on node positions - // ); - - // return workflow.copyWith( - // connections: [...workflow.connections, newConnection], - // ); - // } - // } - // return workflow; - // }).toList(); - // } - - // void removeConnection(String connectionId) { - // state = state.map((workflow) { - // return workflow.copyWith( - // connections: workflow.connections.where((conn) => conn.id != connectionId).toList(), - // ); - // }).toList(); - // } - - void updateNodeStatus(String nodeId, NodeStatus status) { + void removeConnection(String workflowId, String connectionId) { state = state.map((workflow) { - return workflow.copyWith( - nodes: workflow.nodes.map((node) { - if (node.id == nodeId) { - return node.copyWith(status: status); - } - return node; - }).toList(), - ); + if (workflow.id == workflowId) { + return workflow.copyWith( + connections: workflow.connections + .where((conn) => conn.id != connectionId) + .toList(), + ); + } + return workflow; }).toList(); + + _resetExecutionEngine(workflowId); + } + + void setActiveNodes(String workflowId, List nodeIds) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith(activeNodeIds: nodeIds); + } + return workflow; + }).toList(); + } + + void setCompletedNodes(String workflowId, List nodeIds) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith(completedNodeIds: nodeIds); + } + return workflow; + }).toList(); + } + + void setStartNode(String workflowId, String? nodeId) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith(startNodeId: nodeId); + } + return workflow; + }).toList(); + } + + WorkflowModel? getWorkflow(String workflowId) { + return state.firstWhere( + (workflow) => workflow.id == workflowId, + orElse: () => throw Exception('Workflow not found: $workflowId'), + ); + } + + void startExecution( + String workflowId, Function(WorkflowExecutionState) onStateChanged) { + final engine = _getOrCreateExecutionEngine(workflowId); + if (engine != null) { + engine.start(); + } + } + + void pauseExecution(String workflowId) { + final engine = _executionEngines[workflowId]; + engine?.pause(); + } + + void resumeExecution(String workflowId) { + final engine = _executionEngines[workflowId]; + engine?.resume(); + } + + void stopExecution(String workflowId) { + final engine = _executionEngines[workflowId]; + engine?.stop(); + } + + DagExecutionEngine? _getOrCreateExecutionEngine(String workflowId) { + if (_executionEngines.containsKey(workflowId)) { + return _executionEngines[workflowId]; + } + + final workflow = getWorkflow(workflowId); + if (workflow == null) return null; + + final engine = DagExecutionEngine( + workflow: workflow, + onNodeStatusChanged: (nodeId, status) => + updateNodeStatus(workflowId, nodeId, status), + onExecutionStateChanged: (state) { + setActiveNodes(workflowId, state.pendingNodeIds); + setCompletedNodes(workflowId, state.executedNodeIds); + }, + ); + + _executionEngines[workflowId] = engine; + return engine; + } + + void _resetExecutionEngine(String workflowId) { + _executionEngines[workflowId]?.dispose(); + _executionEngines.remove(workflowId); + } + + void startNodeDrag(String workflowId, String nodeId, Offset position) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: workflow.nodes.map((node) { + return node.id == nodeId + ? node.copyWith( + position: position, + isDragging: true, + ) + : node.copyWith( + isDragging: false, + ); + }).toList(), + ); + } + return workflow; + }).toList(); + } + + void updateNodeDrag(String workflowId, String nodeId, Offset position) { + updateNodePosition(workflowId, nodeId, position); + } + + void finishNodeDrag(String workflowId, String nodeId) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: workflow.nodes.map((node) { + return node.id == nodeId + ? node.copyWith( + isDragging: false, + ) + : node; + }).toList(), + ); + } + return workflow; + }).toList(); + } + + @override + void dispose() { + _debounceTimer?.cancel(); + + for (final engine in _executionEngines.values) { + engine.dispose(); + } + _executionEngines.clear(); + + super.dispose(); } } From 7d93e363466810d5293dbd9ffc2538b5c7761f86 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 14 Apr 2025 13:09:54 +0530 Subject: [PATCH 147/188] feat: reorganize exports and enhance RequestModel with copyWith method --- .../lib/api_testing_suite.dart | 18 +++++-------- .../workflow_builder/models/core_models.dart | 19 ++++++++++++- .../models/request_model.dart | 2 -- .../models/workflow_model.dart | 2 ++ .../models/workflow_node_model.dart | 4 ++- .../workflow_builder/workflow_builder.dart | 27 +++++++++++++++++++ 6 files changed, 57 insertions(+), 15 deletions(-) create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart diff --git a/packages/api_testing_suite/lib/api_testing_suite.dart b/packages/api_testing_suite/lib/api_testing_suite.dart index 8fd223f9d..89119820b 100644 --- a/packages/api_testing_suite/lib/api_testing_suite.dart +++ b/packages/api_testing_suite/lib/api_testing_suite.dart @@ -1,18 +1,14 @@ library api_testing_suite; -// Models -export 'src/models/workflow_model.dart'; - -// Providers -export 'src/providers/workflow_providers.dart'; - -// Widgets -export 'src/workflow_builder/workflow_canvas.dart'; -export 'src/workflow_builder/workflow_connection.dart'; -export 'src/workflow_builder/workflow_node.dart'; +// Workflow Builder Feature +export 'src/workflow_builder/workflow_builder.dart'; export 'src/workflow_builder/workflow_screens.dart'; -// Models +// Stress Test Components +export 'src/stress_test/widgets/stress_test_result_card.dart'; export 'src/stress_test/models/stress_test_config.dart'; export 'src/stress_test/models/stress_test_summary.dart'; + +// Fake Data Provider Components +export 'src/fake_data_provider/widgets/fake_data_pane.dart'; export 'src/fake_data_provider/models/fake_data_config.dart'; diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/core_models.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/core_models.dart index e1e76db5a..c22f716ab 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/models/core_models.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/core_models.dart @@ -1,4 +1,4 @@ -/// Temporary placeholder for apidash_core models +import 'package:apidash_core/models/http_request_model.dart'; /// Basic representation of an API request model class RequestModel { @@ -6,11 +6,28 @@ class RequestModel { final String name; final String method; final String url; + final HttpRequestModel httpRequestModel; const RequestModel({ required this.id, + required this.httpRequestModel, this.name = '', this.method = 'GET', this.url = '', }); + + RequestModel copyWith({ + String? method, + String? url, + String? name, + HttpRequestModel? httpRequestModel, + }) { + return RequestModel( + id: id, + method: method ?? this.method, + url: url ?? this.url, + name: name ?? this.name, + httpRequestModel: httpRequestModel ?? this.httpRequestModel, + ); + } } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/request_model.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/request_model.dart index 03efd890c..ff28be679 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/models/request_model.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/request_model.dart @@ -1,3 +1 @@ -/// Re-export of the RequestModel from core_models -/// This provides a placeholder until we integrate with the actual apidash_core export 'core_models.dart' show RequestModel; diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart index 2dfb57546..3049f4d4b 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart @@ -47,6 +47,8 @@ class WorkflowModel with _$WorkflowModel { bool get hasStartNode => startNodeId != null; + get createdAt => null; + WorkflowNodeModel? getStartNode() { if (startNodeId == null) return null; return nodes.firstWhere((node) => node.id == startNodeId); diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_node_model.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_node_model.dart index 733be2dee..07a03d2ab 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_node_model.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_node_model.dart @@ -1,3 +1,4 @@ +import 'package:apidash_core/models/http_request_model.dart'; import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -42,7 +43,7 @@ class RequestModelConverter implements JsonConverter connectedToIds, @RequestModelConverter() RequestModel? requestModel, @Default({}) Map nodeData, diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart new file mode 100644 index 000000000..f45a20636 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart @@ -0,0 +1,27 @@ +// Export core components +export 'workflow_builder_page.dart'; +export 'workflow_canvas.dart'; +export 'workflow_node.dart'; +export 'dag_execution_engine.dart'; +export 'workflows_notifier.dart'; +export 'workflow_providers.dart'; + +// Export models +export 'models/models.dart'; + +// Export widgets +export 'widgets/widgets.dart'; + +// /// The main workflow builder widget that can be used directly in the application +// import 'package:flutter/material.dart'; +// import 'package:flutter_riverpod/flutter_riverpod.dart'; +// import 'workflow_builder_page.dart'; + +// class WorkflowBuilder extends ConsumerWidget { +// const WorkflowBuilder({Key? key}) : super(key: key); + +// @override +// Widget build(BuildContext context, WidgetRef ref) { +// return const WorkflowBuilderPage(); +// } +// } From f57299ac68d84aac7e3b930a30ba8047ff426a85 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Fri, 18 Apr 2025 12:04:17 +0530 Subject: [PATCH 148/188] fix: remove duplicate import and correct WorkflowBuilder return widget --- lib/api_testing/api_testing_integration.dart | 11 +++++++---- lib/screens/envvar/environment_editor.dart | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/api_testing/api_testing_integration.dart b/lib/api_testing/api_testing_integration.dart index e25c2aae7..9d6376644 100644 --- a/lib/api_testing/api_testing_integration.dart +++ b/lib/api_testing/api_testing_integration.dart @@ -1,7 +1,6 @@ +import 'package:api_testing_suite/api_testing_suite.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:api_testing_suite/api_testing_suite.dart'; -export 'package:api_testing_suite/api_testing_suite.dart'; final workflowCollectionProvider = Provider>((ref) { //It is usable when collections are implemented in the future. @@ -9,12 +8,16 @@ final workflowCollectionProvider = Provider>((ref) { return {}; }); -/// WorkflowBuilderPage that uses the implementation from api_testing_suite +/// WorkflowBuilderPagePage that uses the implementation from api_testing_suite class WorkflowBuilder extends StatelessWidget { const WorkflowBuilder({super.key}); @override Widget build(BuildContext context) { - return const WorkflowBuilderPage(); + return const Scaffold( + body: Center( + child: WorkflowBuilderScreen(), + ), + ); } } diff --git a/lib/screens/envvar/environment_editor.dart b/lib/screens/envvar/environment_editor.dart index ea3fb8a80..8dbb1cebb 100644 --- a/lib/screens/envvar/environment_editor.dart +++ b/lib/screens/envvar/environment_editor.dart @@ -1,4 +1,4 @@ -import 'package:apidash/api_testing/api_testing_integration.dart'; +import 'package:api_testing_suite/api_testing_suite.dart'; import 'package:apidash_design_system/apidash_design_system.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; From f76ebff8e903576707187dfc014c358478990f43 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Fri, 18 Apr 2025 12:05:59 +0530 Subject: [PATCH 149/188] feat: add reusable widgets for improved UI components including DraggableComponent, PanelContainer, StatusIndicator, StyledCard, and ToggleActionButton --- .../common/widgets/draggable_component.dart | 65 +++++++++ .../src/common/widgets/panel_container.dart | 126 ++++++++++++++++++ .../src/common/widgets/status_indicator.dart | 48 +++++++ .../lib/src/common/widgets/styled_card.dart | 50 +++++++ .../common/widgets/toggle_action_button.dart | 75 +++++++++++ .../lib/src/common/widgets/widgets.dart | 5 + 6 files changed, 369 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/common/widgets/draggable_component.dart create mode 100644 packages/api_testing_suite/lib/src/common/widgets/panel_container.dart create mode 100644 packages/api_testing_suite/lib/src/common/widgets/status_indicator.dart create mode 100644 packages/api_testing_suite/lib/src/common/widgets/styled_card.dart create mode 100644 packages/api_testing_suite/lib/src/common/widgets/toggle_action_button.dart create mode 100644 packages/api_testing_suite/lib/src/common/widgets/widgets.dart diff --git a/packages/api_testing_suite/lib/src/common/widgets/draggable_component.dart b/packages/api_testing_suite/lib/src/common/widgets/draggable_component.dart new file mode 100644 index 000000000..6be8709eb --- /dev/null +++ b/packages/api_testing_suite/lib/src/common/widgets/draggable_component.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; + +/// A reusable draggable component that can be used for drag and drop operations +class DraggableComponent extends StatelessWidget { + final Widget child; + final Widget? feedback; + final Widget? childWhenDragging; + final Object data; + final DragAnchorStrategy? dragAnchorStrategy; + final Axis? axis; + final bool useDefaultFeedback; + final VoidCallback? onDragStarted; + final DragEndCallback? onDragEnd; + final VoidCallback? onDraggableCanceled; + + const DraggableComponent({ + super.key, + required this.child, + required this.data, + this.feedback, + this.childWhenDragging, + this.dragAnchorStrategy, + this.axis, + this.useDefaultFeedback = false, + this.onDragStarted, + this.onDragEnd, + this.onDraggableCanceled, + }); + + @override + Widget build(BuildContext context) { + return Draggable( + data: data, + dragAnchorStrategy: dragAnchorStrategy ?? childDragAnchorStrategy, + axis: axis, + onDragStarted: onDragStarted, + onDragEnd: onDragEnd, + onDraggableCanceled: (_, __) => onDraggableCanceled?.call(), + feedback: feedback ?? _buildDefaultFeedback(context), + childWhenDragging: childWhenDragging ?? + Opacity(opacity: 0.5, child: child), + child: child, + ); + } + + Widget _buildDefaultFeedback(BuildContext context) { + if (!useDefaultFeedback && feedback != null) return feedback!; + + return Material( + color: Colors.transparent, + elevation: 4, + borderRadius: BorderRadius.circular(8), + child: Container( + width: 200, + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.blue, width: 2), + ), + child: child, + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/common/widgets/panel_container.dart b/packages/api_testing_suite/lib/src/common/widgets/panel_container.dart new file mode 100644 index 000000000..18a6451ce --- /dev/null +++ b/packages/api_testing_suite/lib/src/common/widgets/panel_container.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; + +/// A reusable panel container with a title and consistent styling +class PanelContainer extends StatelessWidget { + final String title; + final Widget child; + final double? width; + final Color? backgroundColor; + final Color? borderColor; + final List? actions; + final bool collapsible; + final bool initiallyExpanded; + final EdgeInsetsGeometry? padding; + final EdgeInsetsGeometry? margin; + + const PanelContainer({ + super.key, + required this.title, + required this.child, + this.width, + this.backgroundColor, + this.borderColor, + this.actions, + this.collapsible = false, + this.initiallyExpanded = true, + this.padding, + this.margin, + }); + + @override + Widget build(BuildContext context) { + if (collapsible) { + return _buildCollapsiblePanel(context); + } + return _buildFixedPanel(context); + } + + Widget _buildFixedPanel(BuildContext context) { + return Container( + width: width, + margin: margin ?? EdgeInsets.zero, + decoration: BoxDecoration( + color: backgroundColor ?? const Color(0xFF212121), + border: borderColor != null + ? Border.all(color: borderColor!) + : const Border( + left: BorderSide(color: Color(0xFF333333)), + right: BorderSide(color: Color(0xFF333333)), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildHeader(), + Expanded( + child: Padding( + padding: padding ?? const EdgeInsets.all(12.0), + child: child, + ), + ), + ], + ), + ); + } + + Widget _buildCollapsiblePanel(BuildContext context) { + return Container( + width: width, + margin: margin ?? EdgeInsets.zero, + decoration: BoxDecoration( + color: backgroundColor ?? const Color(0xFF212121), + border: borderColor != null + ? Border.all(color: borderColor!) + : const Border( + left: BorderSide(color: Color(0xFF333333)), + right: BorderSide(color: Color(0xFF333333)), + ), + ), + child: ExpansionTile( + title: Text( + title, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + initiallyExpanded: initiallyExpanded, + collapsedBackgroundColor: const Color(0xFF272727), + backgroundColor: const Color(0xFF272727), + childrenPadding: padding ?? const EdgeInsets.all(12.0), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (actions != null) ...actions!, + const Icon(Icons.expand_more, color: Colors.white54), + ], + ), + children: [child], + ), + ); + } + + Widget _buildHeader() { + return Container( + height: 48, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: const BoxDecoration( + color: Color(0xFF272727), + border: Border(bottom: BorderSide(color: Color(0xFF333333))), + ), + child: Row( + children: [ + Text( + title, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + const Spacer(), + if (actions != null) ...actions!, + ], + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/common/widgets/status_indicator.dart b/packages/api_testing_suite/lib/src/common/widgets/status_indicator.dart new file mode 100644 index 000000000..97ace4a88 --- /dev/null +++ b/packages/api_testing_suite/lib/src/common/widgets/status_indicator.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; + +/// A reusable status indicator widget that displays status with icon, color and text +class StatusIndicator extends StatelessWidget { + final IconData icon; + final Color color; + final String text; + final double? size; + final bool showText; + + const StatusIndicator({ + super.key, + required this.icon, + required this.color, + required this.text, + this.size, + this.showText = true, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), + decoration: BoxDecoration( + color: color.withOpacity(0.1), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: color.withOpacity(0.3)), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, color: color, size: size ?? 16), + if (showText) ...[ + const SizedBox(width: 6), + Text( + text, + style: TextStyle( + color: color, + fontWeight: FontWeight.bold, + fontSize: size != null ? size! * 0.75 : 12, + ), + ), + ], + ], + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/common/widgets/styled_card.dart b/packages/api_testing_suite/lib/src/common/widgets/styled_card.dart new file mode 100644 index 000000000..17fad2ed8 --- /dev/null +++ b/packages/api_testing_suite/lib/src/common/widgets/styled_card.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; + +/// A reusable styled card widget for consistent UI appearance +class StyledCard extends StatelessWidget { + final Widget child; + final Color? color; + final EdgeInsets? padding; + final EdgeInsets? margin; + final double? elevation; + final BorderRadius? borderRadius; + final Color? borderColor; + final double? borderWidth; + final VoidCallback? onTap; + + const StyledCard({ + super.key, + required this.child, + this.color, + this.padding, + this.margin, + this.elevation, + this.borderRadius, + this.borderColor, + this.borderWidth, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Card( + elevation: elevation ?? 1.0, + margin: margin ?? const EdgeInsets.only(bottom: 8), + shape: RoundedRectangleBorder( + borderRadius: borderRadius ?? BorderRadius.circular(8), + side: borderColor != null + ? BorderSide(color: borderColor!, width: borderWidth ?? 1.0) + : BorderSide.none, + ), + color: color, + child: InkWell( + onTap: onTap, + borderRadius: borderRadius ?? BorderRadius.circular(8), + child: Padding( + padding: padding ?? const EdgeInsets.all(12), + child: child, + ), + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/common/widgets/toggle_action_button.dart b/packages/api_testing_suite/lib/src/common/widgets/toggle_action_button.dart new file mode 100644 index 000000000..a4e385700 --- /dev/null +++ b/packages/api_testing_suite/lib/src/common/widgets/toggle_action_button.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; + +/// A reusable toggle action button that can be used across the application +class ToggleActionButton extends StatelessWidget { + final IconData icon; + final String label; + final bool isActive; + final VoidCallback onPressed; + final Color? activeColor; + final Color? inactiveColor; + final Color? textColor; + final double? iconSize; + final double? fontSize; + + const ToggleActionButton({ + super.key, + required this.icon, + required this.label, + required this.isActive, + required this.onPressed, + this.activeColor, + this.inactiveColor, + this.textColor, + this.iconSize, + this.fontSize, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final effectiveActiveColor = activeColor ?? theme.colorScheme.primary; + final effectiveInactiveColor = inactiveColor ?? Colors.grey.shade600; + final effectiveTextColor = textColor ?? Colors.white; + + return Material( + color: Colors.transparent, + child: InkWell( + onTap: onPressed, + borderRadius: BorderRadius.circular(4), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: isActive + ? effectiveActiveColor.withOpacity(0.1) + : Colors.transparent, + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: isActive ? effectiveActiveColor : effectiveInactiveColor, + width: 1, + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + icon, + size: iconSize ?? 16, + color: isActive ? effectiveActiveColor : effectiveInactiveColor, + ), + const SizedBox(width: 8), + Text( + label, + style: TextStyle( + color: isActive ? effectiveActiveColor : effectiveInactiveColor, + fontWeight: isActive ? FontWeight.bold : FontWeight.normal, + fontSize: fontSize ?? 14, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/common/widgets/widgets.dart b/packages/api_testing_suite/lib/src/common/widgets/widgets.dart new file mode 100644 index 000000000..23c2c21c4 --- /dev/null +++ b/packages/api_testing_suite/lib/src/common/widgets/widgets.dart @@ -0,0 +1,5 @@ +export 'draggable_component.dart'; +export 'panel_container.dart'; +export 'status_indicator.dart'; +export 'styled_card.dart'; +export 'toggle_action_button.dart'; From ef27eb41519f576e9833e3901cda4c519b0e5029 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Fri, 18 Apr 2025 15:17:01 +0530 Subject: [PATCH 150/188] feat: refactor ConnectionPainter and GridBackgroundPainter for improved rendering and add drag-and-drop functionality to WorkflowNode --- .../workflow_builder/connection_painter.dart | 336 +++++++++--------- .../src/workflow_builder/grid_painter.dart | 34 +- .../workflow_builder/workflow_builder.dart | 23 +- .../src/workflow_builder/workflow_node.dart | 317 ++++++++++------- .../workflow_builder/workflow_screens.dart | 24 +- 5 files changed, 383 insertions(+), 351 deletions(-) diff --git a/packages/api_testing_suite/lib/src/workflow_builder/connection_painter.dart b/packages/api_testing_suite/lib/src/workflow_builder/connection_painter.dart index 6d2f8cfbb..2cfa314fe 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/connection_painter.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/connection_painter.dart @@ -1,224 +1,214 @@ -import 'dart:ui' as ui; +import 'package:api_testing_suite/api_testing_suite.dart'; import 'package:flutter/material.dart'; -import 'models/workflow_connection_model.dart'; -import 'models/workflow_node_model.dart'; /// Painter for workflow connections class ConnectionPainter extends CustomPainter { final List connections; final List nodes; final Map nodePositions; - final Offset? tempConnectionStart; - final Offset? tempConnectionEnd; final List runningNodeIds; final List completedNodeIds; - final Size nodeSize; + final Offset? tempConnectionStart; + final Offset? tempConnectionEnd; + final double strokeWidth; + + final Offset? start; + final Offset? end; + final Color? color; + final bool animated; + final String? label; ConnectionPainter({ - required this.connections, - required this.nodes, - required this.nodePositions, - this.tempConnectionStart, - this.tempConnectionEnd, + this.connections = const [], + this.nodes = const [], + this.nodePositions = const {}, this.runningNodeIds = const [], this.completedNodeIds = const [], - this.nodeSize = const Size(160, 80), + this.tempConnectionStart, + this.tempConnectionEnd, + this.strokeWidth = 2.0, + this.start, + this.end, + this.color, + this.animated = false, + this.label, }); @override void paint(Canvas canvas, Size size) { + if (start != null && end != null) { + _paintSingleConnection(canvas, start!, end!, color ?? Colors.blue, animated, label); + return; + } + for (final connection in connections) { - try { - final sourceId = connection.sourceId; - final targetId = connection.targetId; - - if (nodePositions.containsKey(sourceId) && - nodePositions.containsKey(targetId)) { - final isActive = runningNodeIds.contains(sourceId) || - runningNodeIds.contains(targetId); - final isCompleted = completedNodeIds.contains(sourceId) && - completedNodeIds.contains(targetId); - - _drawConnection( - canvas, - sourceId, - targetId, - isActive: isActive, - isCompleted: isCompleted, - ); + final sourcePos = _getNodeCenterPosition(connection.sourceId); + final targetPos = _getNodeCenterPosition(connection.targetId); + + if (sourcePos != null && targetPos != null) { + final isActive = _isConnectionActive(connection.sourceId, connection.targetId); + final isCompleted = _isConnectionCompleted(connection.sourceId, connection.targetId); + + Color connectionColor; + if (isActive) { + connectionColor = Colors.blue.shade500; + } else if (isCompleted) { + connectionColor = Colors.green.shade500; + } else if (connection.condition != null) { + connectionColor = Colors.amber.shade500; + } else { + connectionColor = Colors.blue.shade300; } - } catch (e) { - debugPrint('Error drawing connection: $e'); + + _paintSingleConnection( + canvas, + sourcePos, + targetPos, + connectionColor, + isActive, + connection.condition + ); } } - + if (tempConnectionStart != null && tempConnectionEnd != null) { - _drawTempConnection(canvas, tempConnectionStart!, tempConnectionEnd!); + _paintSingleConnection( + canvas, + tempConnectionStart!, + tempConnectionEnd!, + Colors.blue.withOpacity(0.7), + true, + null + ); } } - - void _drawConnection( - Canvas canvas, - String sourceId, - String targetId, { - bool isActive = false, - bool isCompleted = false, - }) { - try { - final start = nodePositions[sourceId]!; - final end = nodePositions[targetId]!; - - final Color color = isActive - ? Colors.blue - : isCompleted - ? Colors.green - : Colors.grey; - - final double width = isActive || isCompleted ? 2.0 : 1.0; - - _drawBezierLine(canvas, start, end, color, width); - } catch (e) { - debugPrint('Error drawing connection: $e'); + + Offset? _getNodeCenterPosition(String nodeId) { + final position = nodePositions[nodeId]; + if (position != null) { + return position + const Offset(80, 40); } + return null; } - - void _drawTempConnection(Canvas canvas, Offset start, Offset end) { - _drawBezierLine(canvas, start, end, Colors.blue.withOpacity(0.7), 2.0, - isDashed: true); + + bool _isConnectionActive(String sourceId, String targetId) { + return runningNodeIds.contains(sourceId) || runningNodeIds.contains(targetId); + } + + bool _isConnectionCompleted(String sourceId, String targetId) { + return completedNodeIds.contains(sourceId) && completedNodeIds.contains(targetId); } - void _drawBezierLine( + void _paintSingleConnection( Canvas canvas, Offset start, Offset end, Color color, - double width, { - bool isDashed = false, - }) { - try { - final sourceCenter = - Offset(start.dx + nodeSize.width / 2, start.dy + nodeSize.height / 2); - - final targetCenter = - Offset(end.dx + nodeSize.width / 2, end.dy + nodeSize.height / 2); - - final controlPoint1 = Offset( - sourceCenter.dx + (targetCenter.dx - sourceCenter.dx) * 0.5, - sourceCenter.dy, - ); - - final controlPoint2 = Offset( - sourceCenter.dx + (targetCenter.dx - sourceCenter.dx) * 0.5, - targetCenter.dy, - ); - - final path = Path() - ..moveTo(sourceCenter.dx, sourceCenter.dy) - ..cubicTo( - controlPoint1.dx, - controlPoint1.dy, - controlPoint2.dx, - controlPoint2.dy, - targetCenter.dx, - targetCenter.dy, - ); - - final shadowPaint = Paint() - ..color = Colors.black.withOpacity(0.3) - ..style = PaintingStyle.stroke - ..strokeWidth = width + 2.0; - - final paint = Paint() - ..color = color - ..style = PaintingStyle.stroke - ..strokeWidth = width; - - if (isDashed) { - final dashPath = _dashPath( - path, - dashArray: CircularIntervalList([5, 5]), - ); - canvas.drawPath(dashPath, paint); - } else { - canvas.drawPath(path, shadowPaint); - canvas.drawPath(path, paint); - - _drawArrow(canvas, targetCenter.dx, targetCenter.dy, color, width); - } - } catch (e) { - debugPrint('Error drawing bezier line: $e'); - } - } - - void _drawArrow( - Canvas canvas, double x, double y, Color color, double width) { + bool animated, + String? label, + ) { final paint = Paint() ..color = color - ..style = PaintingStyle.fill; - - final path = Path(); + ..strokeWidth = strokeWidth + ..style = PaintingStyle.stroke; + + if (animated) { + paint.shader = LinearGradient( + colors: [ + color.withOpacity(0.3), + color, + color.withOpacity(0.3), + ], + stops: const [0.0, 0.5, 1.0], + ).createShader(Rect.fromPoints(start, end)); + } - path.moveTo(x, y); - path.lineTo(x - 6, y - 3); - path.lineTo(x - 6, y + 3); - path.close(); + final path = Path() + ..moveTo(start.dx, start.dy) + ..lineTo(end.dx, end.dy); canvas.drawPath(path, paint); - } - Path _dashPath( - Path source, { - required CircularIntervalList dashArray, - }) { - final Path dest = Path(); - final List metrics = source.computeMetrics().toList(); - - for (final ui.PathMetric pathMetric in metrics) { - double distance = 0; - bool draw = true; - while (distance < pathMetric.length) { - final double len = dashArray.next; - if (draw) { - try { - dest.addPath( - pathMetric.extractPath(distance, distance + len), - Offset.zero, - ); - } catch (e) { - debugPrint('Error adding path segment: $e'); - } - } - distance += len; - draw = !draw; - } + if (label != null && label.isNotEmpty) { + final midpoint = Offset( + (start.dx + end.dx) / 2, + (start.dy + end.dy) / 2, + ); + _drawLabel(canvas, midpoint, label); } + } - return dest; + // Offset _calculateTangent(Offset controlPoint, Offset endPoint) { + // return (endPoint - controlPoint).normalize(); + // } + + // void _drawArrow(Canvas canvas, Offset position, Offset direction, Paint paint) { + // final arrowSize = 8.0; + // final angle = atan2(direction.dy, direction.dx); + + // final path = Path() + // ..moveTo(position.dx, position.dy) + // ..lineTo( + // position.dx - arrowSize * cos(angle - pi / 6), + // position.dy - arrowSize * sin(angle - pi / 6), + // ) + // ..lineTo( + // position.dx - arrowSize * cos(angle + pi / 6), + // position.dy - arrowSize * sin(angle + pi / 6), + // ) + // ..close(); + + // canvas.drawPath(path, paint..style = PaintingStyle.fill); + // } + + void _drawLabel(Canvas canvas, Offset position, String label) { + final textPainter = TextPainter( + text: TextSpan( + text: label, + style: const TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + textDirection: TextDirection.ltr, + ); + textPainter.layout(); + + canvas.drawRect( + Rect.fromCenter( + center: position, + width: textPainter.width + 16, + height: textPainter.height + 8, + ), + Paint()..color = Colors.black.withOpacity(0.7), + ); + + textPainter.paint( + canvas, + Offset( + position.dx - textPainter.width / 2, + position.dy - textPainter.height / 2, + ), + ); } @override - bool shouldRepaint(covariant ConnectionPainter oldDelegate) { + bool shouldRepaint(ConnectionPainter oldDelegate) { return oldDelegate.connections != connections || - oldDelegate.nodePositions != nodePositions || + oldDelegate.runningNodeIds != runningNodeIds || + oldDelegate.completedNodeIds != completedNodeIds || oldDelegate.tempConnectionStart != tempConnectionStart || oldDelegate.tempConnectionEnd != tempConnectionEnd || - oldDelegate.runningNodeIds != runningNodeIds || - oldDelegate.completedNodeIds != completedNodeIds; + oldDelegate.start != start || + oldDelegate.end != end; } } -class CircularIntervalList { - final List _items; - int _index = 0; - - CircularIntervalList(this._items); - - T get next { - if (_items.isEmpty) { - throw Exception('CircularIntervalList is empty'); - } - final item = _items[_index]; - _index = (_index + 1) % _items.length; - return item; +extension OffsetExtension on Offset { + Offset normalize() { + final magnitude = distance; + if (magnitude == 0) return Offset.zero; + return Offset(dx / magnitude, dy / magnitude); } } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart b/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart index 1450ce609..522888969 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart @@ -1,41 +1,29 @@ import 'package:flutter/material.dart'; -class GridPainter extends CustomPainter { +/// A custom painter for drawing a grid background +class GridBackgroundPainter extends CustomPainter { final Color gridColor; - final double gridWidth; - final double gridSpacing; + final double step; - GridPainter({ + GridBackgroundPainter({ required this.gridColor, - required this.gridWidth, - required this.gridSpacing, + required this.step, }); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = gridColor - ..strokeWidth = gridWidth; + ..strokeWidth = 1.0; - final horizontalLines = (size.height / gridSpacing).ceil(); - final verticalLines = (size.width / gridSpacing).ceil(); - - for (var i = 0; i <= horizontalLines; i++) { - final y = i * gridSpacing; - canvas.drawLine(Offset(0, y), Offset(size.width, y), paint); - } - - for (var i = 0; i <= verticalLines; i++) { - final x = i * gridSpacing; + for (double x = 0; x < size.width; x += step) { canvas.drawLine(Offset(x, 0), Offset(x, size.height), paint); } + for (double y = 0; y < size.height; y += step) { + canvas.drawLine(Offset(0, y), Offset(size.width, y), paint); + } } @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return oldDelegate is GridPainter && - (oldDelegate.gridColor != gridColor || - oldDelegate.gridWidth != gridWidth || - oldDelegate.gridSpacing != gridSpacing); - } + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart index f45a20636..fa61a427a 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart @@ -1,27 +1,18 @@ // Export core components -export 'workflow_builder_page.dart'; -export 'workflow_canvas.dart'; +export 'components/workflow_builder_screen.dart'; export 'workflow_node.dart'; -export 'dag_execution_engine.dart'; export 'workflows_notifier.dart'; export 'workflow_providers.dart'; +// Export optimized components +export 'components/components.dart'; +export 'execution/workflow_executor.dart'; + // Export models export 'models/models.dart'; // Export widgets export 'widgets/widgets.dart'; -// /// The main workflow builder widget that can be used directly in the application -// import 'package:flutter/material.dart'; -// import 'package:flutter_riverpod/flutter_riverpod.dart'; -// import 'workflow_builder_page.dart'; - -// class WorkflowBuilder extends ConsumerWidget { -// const WorkflowBuilder({Key? key}) : super(key: key); - -// @override -// Widget build(BuildContext context, WidgetRef ref) { -// return const WorkflowBuilderPage(); -// } -// } +// Export common widgets with hide +export '../common/widgets/widgets.dart'; diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart index ae82a1bcb..e13313b4b 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart @@ -1,22 +1,33 @@ +import 'package:api_testing_suite/api_testing_suite.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'models/models.dart'; + class WorkflowNode extends ConsumerWidget { final WorkflowNodeModel node; final bool isSelected; final bool isRunning; final bool isCompleted; final bool hasError; + final Function(String)? onTap; + final Function(String, Offset)? onStartConnection; + final Function(String, Offset)? onDragStart; + final Function(Offset)? onDragUpdate; + final Function()? onDragEnd; const WorkflowNode({ - Key? key, + super.key, required this.node, this.isSelected = false, this.isRunning = false, this.isCompleted = false, this.hasError = false, - }) : super(key: key); + this.onTap, + this.onStartConnection, + this.onDragStart, + this.onDragUpdate, + this.onDragEnd, + }); @override Widget build(BuildContext context, WidgetRef ref) { @@ -26,153 +37,189 @@ class WorkflowNode extends ConsumerWidget { final iconData = _getNodeIcon(); final elevation = isSelected ? 8.0 : 4.0; - return Material( - elevation: elevation, - color: Colors.transparent, - borderRadius: BorderRadius.circular(8), - shadowColor: isRunning - ? Colors.blue.withOpacity(0.6) - : isCompleted - ? Colors.green.withOpacity(0.6) - : hasError - ? Colors.red.withOpacity(0.6) - : Colors.black.withOpacity(0.3), - child: Container( - width: nodeSize.width, - height: nodeSize.height, - decoration: BoxDecoration( - color: nodeColor, - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: borderColor, - width: 2, - ), - boxShadow: [ - BoxShadow( - color: isRunning - ? Colors.blue.withOpacity(0.3) - : isCompleted - ? Colors.green.withOpacity(0.3) - : hasError - ? Colors.red.withOpacity(0.3) - : Colors.black.withOpacity(0.2), - blurRadius: 5, - spreadRadius: 1, + return GestureDetector( + onTap: onTap != null ? () => onTap!(node.id) : null, + onPanStart: onDragStart != null ? (details) => onDragStart!(node.id, details.globalPosition) : null, + onPanUpdate: onDragUpdate != null ? (details) => onDragUpdate!(details.globalPosition) : null, + onPanEnd: onDragEnd != null ? (_) => onDragEnd!() : null, + child: Material( + elevation: elevation, + color: Colors.transparent, + borderRadius: BorderRadius.circular(8), + shadowColor: isRunning + ? Colors.blue.withOpacity(0.6) + : isCompleted + ? Colors.green.withOpacity(0.6) + : hasError + ? Colors.red.withOpacity(0.6) + : Colors.black.withOpacity(0.3), + child: Container( + width: nodeSize.width, + height: nodeSize.height, + decoration: BoxDecoration( + color: nodeColor, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: borderColor, + width: 2, ), - ], - ), - child: Stack( - children: [ - Padding( - padding: const EdgeInsets.all(10.0), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - iconData, - color: Colors.white, - size: 18, - ), - const SizedBox(width: 8), - Expanded( - child: Text( - node.label.isEmpty - ? 'Node ${node.id.substring(0, 4)}' - : node.label, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 14, + boxShadow: [ + BoxShadow( + color: isRunning + ? Colors.blue.withOpacity(0.3) + : isCompleted + ? Colors.green.withOpacity(0.3) + : hasError + ? Colors.red.withOpacity(0.3) + : Colors.black.withOpacity(0.2), + blurRadius: 5, + spreadRadius: 1, + ), + ], + ), + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + iconData, + color: Colors.white, + size: 18, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + node.label.isEmpty + ? 'Node ${node.id.substring(0, 4)}' + : node.label, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 14, + ), + overflow: TextOverflow.ellipsis, ), - overflow: TextOverflow.ellipsis, ), - ), - ], - ), - const SizedBox(height: 8), - if (node.nodeType == NodeType.request && node.requestModel != null) - _buildRequestDetails(), - if (node.nodeType == NodeType.condition) - _buildConditionDetails(), - if (node.nodeType == NodeType.action) - _buildActionDetails(), - if (node.nodeType == NodeType.response) - _buildResponseDetails(), - ], + ], + ), + const SizedBox(height: 8), + if (node.nodeType == NodeType.request) + _buildRequestDetails(), + if (node.nodeType == NodeType.condition) + _buildConditionDetails(), + if (node.nodeType == NodeType.action) + _buildActionDetails(), + if (node.nodeType == NodeType.response) + _buildResponseDetails(), + ], + ), ), - ), - - Positioned( - top: 4, - right: 4, - child: Container( - width: 12, - height: 12, - decoration: BoxDecoration( - color: isRunning - ? Colors.blue - : isCompleted - ? Colors.green - : hasError - ? Colors.red - : Colors.grey.withOpacity(0.5), - shape: BoxShape.circle, + + if (isRunning || isCompleted || hasError) + Positioned( + right: 5, + top: 5, + child: Container( + width: 16, + height: 16, + decoration: BoxDecoration( + color: isRunning + ? Colors.blue + : isCompleted + ? Colors.green + : Colors.red, + shape: BoxShape.circle, + border: Border.all(color: Colors.white, width: 2), + ), + child: Icon( + isRunning + ? Icons.update + : isCompleted + ? Icons.check + : Icons.error, + color: Colors.white, + size: 10, + ), + ), ), - child: isRunning - ? const Center( - child: SizedBox( - width: 6, - height: 6, - child: CircularProgressIndicator( - strokeWidth: 1, - valueColor: - AlwaysStoppedAnimation(Colors.white), - ), + + if (onStartConnection != null) + Positioned( + right: 0, + top: nodeSize.height / 2 - 6, + child: GestureDetector( + onPanStart: (details) { + onStartConnection!( + node.id, + details.globalPosition, + ); + }, + child: Container( + width: 12, + height: 12, + decoration: BoxDecoration( + color: Colors.blue, + shape: BoxShape.circle, + border: Border.all( + color: Colors.white, + width: 2, ), - ) - : null, - ), - ), - ], + ), + ), + ), + ), + ], + ), ), ), ); } - + Widget _buildRequestDetails() { - final method = node.requestModel?.method ?? 'GET'; - final url = node.requestModel?.url ?? ''; - final shortUrl = url.length > 25 ? '${url.substring(0, 22)}...' : url; + final method = node.nodeData['method'] as String? ?? 'GET'; + final url = node.nodeData['url'] as String? ?? ''; + final methodColor = _getMethodColor(method); + final shortUrl = url.length > 30 ? '${url.substring(0, 27)}...' : url; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), - decoration: BoxDecoration( - color: _getMethodColor(method), - borderRadius: BorderRadius.circular(4), - ), - child: Text( - method, - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), + decoration: BoxDecoration( + color: methodColor, + borderRadius: BorderRadius.circular(4), + ), + child: Text( + method, + style: const TextStyle( + color: Colors.white, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + ), ), - ), - ), - const SizedBox(height: 2), - Text( - shortUrl, - style: TextStyle( - color: Colors.white.withOpacity(0.8), - fontSize: 10, - ), - overflow: TextOverflow.ellipsis, + const SizedBox(width: 4), + Expanded( + child: Text( + shortUrl.isEmpty ? 'No URL set' : shortUrl, + style: TextStyle( + color: Colors.white.withOpacity(0.8), + fontSize: 10, + fontStyle: shortUrl.isEmpty ? FontStyle.italic : FontStyle.normal, + ), + overflow: TextOverflow.ellipsis, + ), + ), + ], ), ], ); @@ -224,7 +271,7 @@ class WorkflowNode extends ConsumerWidget { } Widget _buildResponseDetails() { - final statusCode = node.simulatedStatusCode; + final statusCode = node.nodeData['statusCode'] as int? ?? 200; return Row( children: [ diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart index 6b6102a0c..66404fe0e 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart @@ -1,11 +1,9 @@ - // Main components export 'workflow_builder.dart'; -export 'workflow_builder_page.dart'; -export 'workflow_canvas.dart'; +export 'components/workflow_builder_screen.dart'; export 'workflow_node.dart'; export 'workflow_connection.dart' hide ConnectionPainter; -export 'dag_execution_engine.dart'; +export 'execution/workflow_executor.dart'; // Providers export 'workflow_providers.dart'; @@ -16,3 +14,21 @@ export 'models/models.dart'; // Widgets export 'widgets/widgets.dart'; + +// Common widgets with hide +export '../common/widgets/widgets.dart'; + +// Screens entry points +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'components/workflow_builder_screen.dart'; + +/// A wrapper widget that provides an entry point to the workflow builder +class WorkflowBuilderEntry extends ConsumerWidget { + const WorkflowBuilderEntry({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return const WorkflowBuilderScreen(); + } +} From 1ef2cf3b7a01c42259abcb2bc76a5cb7b5518d7c Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Fri, 18 Apr 2025 15:21:23 +0530 Subject: [PATCH 151/188] Remove WorkflowBuilderPage and WorkflowCanvas implementations to streamline the API testing suite. This includes the deletion of associated models, providers, and UI components, enhancing maintainability and reducing complexity. --- .../dag_execution_engine.dart | 290 ------ .../widgets/node_details_panel.dart | 640 ------------- .../workflow_builder_page.dart | 880 ------------------ .../src/workflow_builder/workflow_canvas.dart | 493 ---------- 4 files changed, 2303 deletions(-) delete mode 100644 packages/api_testing_suite/lib/src/workflow_builder/dag_execution_engine.dart delete mode 100644 packages/api_testing_suite/lib/src/workflow_builder/widgets/node_details_panel.dart delete mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflow_builder_page.dart delete mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflow_canvas.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/dag_execution_engine.dart b/packages/api_testing_suite/lib/src/workflow_builder/dag_execution_engine.dart deleted file mode 100644 index 3c798aa24..000000000 --- a/packages/api_testing_suite/lib/src/workflow_builder/dag_execution_engine.dart +++ /dev/null @@ -1,290 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/foundation.dart'; -import 'models/workflow_model.dart'; -import 'models/workflow_node_model.dart'; -import 'models/node_status.dart'; -import 'models/workflow_execution_state.dart'; - -/// A class that handles the execution of a workflow DAG -class DagExecutionEngine { - - final WorkflowModel workflow; - final Function(String, NodeStatus) onNodeStatusChanged; - final Function(WorkflowExecutionState) onExecutionStateChanged; - WorkflowExecutionState _executionState; - late Map> _nodeDependencyMap; - late Map> _nodeOutgoingMap; - final _executionController = StreamController.broadcast(); - StreamSubscription? _executionSubscription; - Timer? _executionTimer; - - DagExecutionEngine({ - required this.workflow, - required this.onNodeStatusChanged, - required this.onExecutionStateChanged, - }) : _executionState = const WorkflowExecutionState() { - _initializeDependencyMaps(); - } - - void _initializeDependencyMaps() { - _nodeDependencyMap = {}; - _nodeOutgoingMap = {}; - - for (final node in workflow.nodes) { - _nodeDependencyMap[node.id] = []; - _nodeOutgoingMap[node.id] = []; - } - - for (final connection in workflow.connections) { - final sourceId = connection.sourceId; - final targetId = connection.targetId; - - if (_nodeDependencyMap.containsKey(targetId)) { - _nodeDependencyMap[targetId]!.add(sourceId); - } - - if (_nodeOutgoingMap.containsKey(sourceId)) { - _nodeOutgoingMap[sourceId]!.add(targetId); - } - } - } - - void start() { - if (_executionState.isRunning) { - return; - } - - if (_executionState.isCompleted || _executionState.hasError) { - _resetExecutionState(); - } - - final initialNodes = _findInitialNodes(); - if (initialNodes.isEmpty) { - _updateExecutionState( - _executionState.copyWith( - status: WorkflowExecutionStatus.error, - errorMessage: 'No starting nodes found in the workflow', - ), - ); - return; - } - - _updateExecutionState( - _executionState.copyWith( - status: WorkflowExecutionStatus.running, - startTime: DateTime.now(), - pendingNodeIds: initialNodes.map((node) => node.id).toList(), - ), - ); - - _setupExecutionListener(); - _executeNextNodes(); - } - - void pause() { - if (!_executionState.isRunning) { - return; - } - - _updateExecutionState( - _executionState.copyWith( - status: WorkflowExecutionStatus.paused, - ), - ); - - _cancelExecutionTimer(); - } - - void resume() { - if (!_executionState.isPaused) { - return; - } - - _updateExecutionState( - _executionState.copyWith( - status: WorkflowExecutionStatus.running, - ), - ); - - _executeNextNodes(); - } - - void stop() { - _cancelExecutionSubscription(); - _cancelExecutionTimer(); - - _updateExecutionState( - _executionState.copyWith( - status: WorkflowExecutionStatus.idle, - currentNodeId: null, - ), - ); - } - - void _resetExecutionState() { - _updateExecutionState( - const WorkflowExecutionState(), - ); - } - - List _findInitialNodes() { - return workflow.nodes.where((node) { - return _nodeDependencyMap[node.id]!.isEmpty; - }).toList(); - } - - void _setupExecutionListener() { - _cancelExecutionSubscription(); - - _executionSubscription = _executionController.stream.listen((command) { - switch (command) { - case ExecutionCommand.start: - start(); - break; - case ExecutionCommand.pause: - pause(); - break; - case ExecutionCommand.resume: - resume(); - break; - case ExecutionCommand.stop: - stop(); - break; - case ExecutionCommand.reset: - _resetExecutionState(); - break; - } - }); - } - - void _executeNextNodes() { - if (!_executionState.isRunning) { - return; - } - - final pendingNodeIds = List.from(_executionState.pendingNodeIds); - if (pendingNodeIds.isEmpty) { - _completeExecution(); - return; - } - - final nodeId = pendingNodeIds.first; - pendingNodeIds.removeAt(0); - - final node = workflow.nodes.firstWhere( - (n) => n.id == nodeId, - orElse: () => throw Exception('Node not found: $nodeId'), - ); - - final dependencies = _nodeDependencyMap[nodeId] ?? []; - final allDependenciesMet = dependencies.every( - (depId) => _executionState.executedNodeIds.contains(depId), - ); - - if (!allDependenciesMet) { - pendingNodeIds.add(nodeId); - - _updateExecutionState( - _executionState.copyWith( - pendingNodeIds: pendingNodeIds, - ), - ); - - _scheduleNextExecution(); - return; - } - - _updateExecutionState( - _executionState.copyWith( - currentNodeId: nodeId, - pendingNodeIds: pendingNodeIds, - ), - ); - - onNodeStatusChanged(nodeId, NodeStatus.running); - - _executionTimer = Timer(const Duration(milliseconds: 500), () { - - final success = true; - final newStatus = success ? NodeStatus.success : NodeStatus.failure; - - onNodeStatusChanged(nodeId, newStatus); - - final updatedResults = - Map.from(_executionState.executionResults); - updatedResults[nodeId] = { - 'status': success ? 'success' : 'failure', - 'timestamp': DateTime.now().toIso8601String(), - 'data': {'message': 'Simulated ${success ? 'success' : 'failure'}'}, - }; - - final nextNodeIds = _nodeOutgoingMap[nodeId] ?? []; - final updatedPendingIds = List.from(pendingNodeIds); - - for (final nextNodeId in nextNodeIds) { - if (!updatedPendingIds.contains(nextNodeId) && - !_executionState.executedNodeIds.contains(nextNodeId)) { - updatedPendingIds.add(nextNodeId); - } - } - - _updateExecutionState( - _executionState.copyWith( - currentNodeId: null, - executedNodeIds: [..._executionState.executedNodeIds, nodeId], - pendingNodeIds: updatedPendingIds, - executionResults: updatedResults, - ), - ); - - if (updatedPendingIds.isNotEmpty) { - _scheduleNextExecution(); - } else { - _completeExecution(); - } - }); - } - void _scheduleNextExecution() { - _executionTimer = - Timer(const Duration(milliseconds: 100), _executeNextNodes); - } - - void _completeExecution() { - _updateExecutionState( - _executionState.copyWith( - status: WorkflowExecutionStatus.completed, - endTime: DateTime.now(), - ), - ); - } - - void _updateExecutionState(WorkflowExecutionState newState) { - _executionState = newState; - onExecutionStateChanged(newState); - } - - void _cancelExecutionTimer() { - _executionTimer?.cancel(); - _executionTimer = null; - } - - void _cancelExecutionSubscription() { - _executionSubscription?.cancel(); - _executionSubscription = null; - } - - void dispose() { - _cancelExecutionSubscription(); - _cancelExecutionTimer(); - _executionController.close(); - } -} - -enum ExecutionCommand { - start, - pause, - resume, - stop, - reset, -} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/widgets/node_details_panel.dart b/packages/api_testing_suite/lib/src/workflow_builder/widgets/node_details_panel.dart deleted file mode 100644 index a0581be13..000000000 --- a/packages/api_testing_suite/lib/src/workflow_builder/widgets/node_details_panel.dart +++ /dev/null @@ -1,640 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../models/models.dart'; -import '../workflow_providers.dart'; - -class NodeDetailsPanel extends ConsumerWidget { - final WorkflowNodeModel node; - final String workflowId; - - const NodeDetailsPanel({ - Key? key, - required this.node, - required this.workflowId, - }) : super(key: key); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return Container( - width: 320, - color: const Color(0xFF212121), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildHeader(context, ref), - Expanded( - child: SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildNodeTypeSection(ref), - const SizedBox(height: 16), - _buildNodeDetailsSection(ref), - const SizedBox(height: 16), - if (node.nodeType == NodeType.request) - _buildRequestDetailsSection(ref), - if (node.nodeType == NodeType.condition) - _buildConditionDetailsSection(ref), - if (node.nodeType == NodeType.action) - _buildActionDetailsSection(ref), - if (node.nodeType == NodeType.response) - _buildResponseDetailsSection(ref), - const SizedBox(height: 24), - _buildDependenciesSection(ref), - ], - ), - ), - ), - ], - ), - ); - } - - Widget _buildHeader(BuildContext context, WidgetRef ref) { - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: node.status.color.withOpacity(0.2), - border: Border( - bottom: BorderSide( - color: node.status.color.withOpacity(0.5), - width: 1, - ), - ), - ), - child: Row( - children: [ - Icon( - _getNodeTypeIcon(), - color: Colors.white, - size: 24, - ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - node.label.isEmpty ? 'Node ${node.id.substring(0, 4)}' : node.label, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - const SizedBox(height: 4), - Text( - _getNodeTypeText(), - style: TextStyle( - color: Colors.white.withOpacity(0.7), - fontSize: 14, - ), - ), - ], - ), - ), - IconButton( - icon: const Icon(Icons.close, color: Colors.white), - onPressed: () { - ref.read(selectedNodeIdProvider.notifier).state = null; - }, - ), - ], - ), - ); - } - - Widget _buildNodeTypeSection(WidgetRef ref) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Node Type', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - const SizedBox(height: 8), - DropdownButtonFormField( - value: node.nodeType, - decoration: InputDecoration( - filled: true, - fillColor: Colors.grey[800], - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - ), - style: const TextStyle(color: Colors.white), - dropdownColor: Colors.grey[800], - items: NodeType.values.map((type) { - return DropdownMenuItem( - value: type, - child: Text(_getNodeTypeText(type)), - ); - }).toList(), - onChanged: (NodeType? newValue) { - if (newValue != null) { - ref.read(workflowsNotifierProvider.notifier).updateNode( - workflowId, - node.copyWith(nodeType: newValue), - ); - } - }, - ), - ], - ); - } - - Widget _buildNodeDetailsSection(WidgetRef ref) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Node Details', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - const SizedBox(height: 8), - TextFormField( - initialValue: node.label, - decoration: InputDecoration( - labelText: 'Label', - filled: true, - fillColor: Colors.grey[800], - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - labelStyle: TextStyle(color: Colors.grey[400]), - ), - style: const TextStyle(color: Colors.white), - onChanged: (value) { - ref.read(workflowsNotifierProvider.notifier).updateNode( - workflowId, - node.copyWith(label: value), - ); - }, - ), - const SizedBox(height: 16), - Row( - children: [ - Text( - 'Status: ${_getStatusText(node.status)}', - style: TextStyle( - color: node.status.color, - fontWeight: FontWeight.bold, - ), - ), - const Spacer(), - IconButton( - icon: const Icon(Icons.refresh, color: Colors.white), - onPressed: () { - final nextStatus = _getNextStatus(node.status); - ref.read(workflowsNotifierProvider.notifier).updateNodeStatus( - workflowId, - node.id, - nextStatus, - ); - }, - tooltip: 'Cycle status (testing only)', - ), - ], - ), - ], - ); - } - - Widget _buildRequestDetailsSection(WidgetRef ref) { - final requestModel = node.requestModel; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16), - const Text( - 'API Request Details', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - const SizedBox(height: 8), - DropdownButtonFormField( - value: requestModel?.method ?? 'GET', - decoration: InputDecoration( - labelText: 'Method', - filled: true, - fillColor: Colors.grey[800], - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - labelStyle: TextStyle(color: Colors.grey[400]), - ), - style: const TextStyle(color: Colors.white), - dropdownColor: Colors.grey[800], - items: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].map((method) { - return DropdownMenuItem( - value: method, - child: Text(method), - ); - }).toList(), - onChanged: (String? newValue) { - if (newValue != null && requestModel != null) { - final updatedRequest = requestModel.copyWith(method: newValue); - ref.read(workflowsNotifierProvider.notifier).updateNode( - workflowId, - node.copyWith(requestModel: updatedRequest), - ); - } - }, - ), - const SizedBox(height: 16), - TextFormField( - initialValue: requestModel?.url ?? '', - decoration: InputDecoration( - labelText: 'URL', - filled: true, - fillColor: Colors.grey[800], - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - labelStyle: TextStyle(color: Colors.grey[400]), - ), - style: const TextStyle(color: Colors.white), - onChanged: (value) { - if (requestModel != null) { - final updatedRequest = requestModel.copyWith(url: value); - ref.read(workflowsNotifierProvider.notifier).updateNode( - workflowId, - node.copyWith(requestModel: updatedRequest), - ); - } - }, - ), - const SizedBox(height: 16), - ElevatedButton.icon( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - icon: const Icon(Icons.edit, size: 16), - label: const Text('Edit Full Request'), - onPressed: () { - ScaffoldMessenger.of(ref.context).showSnackBar( - const SnackBar( - content: Text('Full request editor to be implemented'), - ), - ); - }, - ), - ], - ); - } - - Widget _buildConditionDetailsSection(WidgetRef ref) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16), - const Text( - 'Condition Details', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - const SizedBox(height: 8), - TextFormField( - initialValue: node.nodeData['condition'] as String? ?? '', - decoration: InputDecoration( - labelText: 'Condition Expression', - filled: true, - fillColor: Colors.grey[800], - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - labelStyle: TextStyle(color: Colors.grey[400]), - ), - style: const TextStyle(color: Colors.white), - onChanged: (value) { - final updatedData = Map.from(node.nodeData); - updatedData['condition'] = value; - ref.read(workflowsNotifierProvider.notifier).updateNode( - workflowId, - node.copyWith(nodeData: updatedData), - ); - }, - ), - const SizedBox(height: 8), - const Text( - 'Examples: statusCode == 200 | response.body.contains("success")', - style: TextStyle( - color: Colors.grey, - fontSize: 12, - ), - ), - ], - ); - } - - Widget _buildActionDetailsSection(WidgetRef ref) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16), - const Text( - 'Action Details', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - const SizedBox(height: 8), - DropdownButtonFormField( - value: node.nodeData['actionType'] as String? ?? 'transform', - decoration: InputDecoration( - labelText: 'Action Type', - filled: true, - fillColor: Colors.grey[800], - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - labelStyle: TextStyle(color: Colors.grey[400]), - ), - style: const TextStyle(color: Colors.white), - dropdownColor: Colors.grey[800], - items: ['transform', 'delay', 'notify', 'log'].map((actionType) { - return DropdownMenuItem( - value: actionType, - child: Text(actionType.toUpperCase()), - ); - }).toList(), - onChanged: (String? newValue) { - if (newValue != null) { - final updatedData = Map.from(node.nodeData); - updatedData['actionType'] = newValue; - ref.read(workflowsNotifierProvider.notifier).updateNode( - workflowId, - node.copyWith(nodeData: updatedData), - ); - } - }, - ), - const SizedBox(height: 16), - TextFormField( - initialValue: node.nodeData['actionConfig'] as String? ?? '', - decoration: InputDecoration( - labelText: 'Action Configuration', - filled: true, - fillColor: Colors.grey[800], - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - labelStyle: TextStyle(color: Colors.grey[400]), - ), - style: const TextStyle(color: Colors.white), - maxLines: 3, - onChanged: (value) { - final updatedData = Map.from(node.nodeData); - updatedData['actionConfig'] = value; - ref.read(workflowsNotifierProvider.notifier).updateNode( - workflowId, - node.copyWith(nodeData: updatedData), - ); - }, - ), - ], - ); - } - - Widget _buildResponseDetailsSection(WidgetRef ref) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16), - const Text( - 'Response Simulation', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - const SizedBox(height: 8), - TextFormField( - initialValue: node.simulatedStatusCode.toString(), - decoration: InputDecoration( - labelText: 'Simulated Status Code', - filled: true, - fillColor: Colors.grey[800], - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - labelStyle: TextStyle(color: Colors.grey[400]), - ), - style: const TextStyle(color: Colors.white), - keyboardType: TextInputType.number, - onChanged: (value) { - final statusCode = int.tryParse(value) ?? 200; - ref.read(workflowsNotifierProvider.notifier).updateNode( - workflowId, - node.copyWith(simulatedStatusCode: statusCode), - ); - }, - ), - const SizedBox(height: 16), - TextFormField( - initialValue: node.simulatedResponse['body'] as String? ?? '', - decoration: InputDecoration( - labelText: 'Simulated Response Body', - filled: true, - fillColor: Colors.grey[800], - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - labelStyle: TextStyle(color: Colors.grey[400]), - ), - style: const TextStyle(color: Colors.white), - maxLines: 5, - onChanged: (value) { - final updatedResponse = Map.from(node.simulatedResponse); - updatedResponse['body'] = value; - ref.read(workflowsNotifierProvider.notifier).updateNode( - workflowId, - node.copyWith(simulatedResponse: updatedResponse), - ); - }, - ), - ], - ); - } - - Widget _buildDependenciesSection(WidgetRef ref) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Dependencies', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - const SizedBox(height: 8), - node.connections.isEmpty - ? const Text( - 'No connections', - style: TextStyle(color: Colors.grey), - ) - : Column( - children: node.connections.map((connection) { - return _buildConnectionItem(ref, connection); - }).toList(), - ), - ], - ); - } - - Widget _buildConnectionItem(WidgetRef ref, WorkflowConnectionModel connection) { - final workflows = ref.watch(workflowsNotifierProvider); - final workflowModel = workflows.firstWhere( - (w) => w.id == workflowId, - orElse: () => throw Exception('Workflow not found'), - ); - - String getNodeLabel(String nodeId) { - try { - final connectedNode = workflowModel.nodes.firstWhere( - (n) => n.id == nodeId, - orElse: () => throw Exception('Node not found'), - ); - return connectedNode.label.isEmpty - ? 'Node ${connectedNode.id.substring(0, 4)}' - : connectedNode.label; - } catch (e) { - return 'Unknown Node'; - } - } - - final sourceLabel = getNodeLabel(connection.sourceId); - final targetLabel = getNodeLabel(connection.targetId); - - return Card( - margin: const EdgeInsets.only(bottom: 8), - color: Colors.grey[850], - child: ListTile( - title: RichText( - text: TextSpan( - children: [ - TextSpan( - text: sourceLabel, - style: const TextStyle( - color: Colors.blue, - fontWeight: FontWeight.bold, - ), - ), - const TextSpan( - text: ' ➝ ', - style: TextStyle(color: Colors.grey), - ), - TextSpan( - text: targetLabel, - style: const TextStyle( - color: Colors.green, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - trailing: IconButton( - icon: const Icon(Icons.delete, color: Colors.red), - onPressed: () { - ref.read(workflowsNotifierProvider.notifier).removeConnection( - workflowId, - connection.id, - ); - }, - ), - ), - ); - } - - IconData _getNodeTypeIcon() { - switch (node.nodeType) { - case NodeType.request: - return Icons.send; - case NodeType.response: - return Icons.call_received; - case NodeType.condition: - return Icons.fork_right; - case NodeType.action: - return Icons.settings; - } - } - - String _getNodeTypeText([NodeType? type]) { - final nodeType = type ?? node.nodeType; - switch (nodeType) { - case NodeType.request: - return 'API Request'; - case NodeType.response: - return 'Response Simulation'; - case NodeType.condition: - return 'Conditional Logic'; - case NodeType.action: - return 'Action Node'; - } - } - - String _getStatusText(NodeStatus status) { - switch (status) { - case NodeStatus.inactive: - return 'Inactive'; - case NodeStatus.running: - return 'Running'; - case NodeStatus.success: - return 'Success'; - case NodeStatus.failure: - return 'Failed'; - case NodeStatus.pending: - return 'Pending'; - } - } - - NodeStatus _getNextStatus(NodeStatus current) { - switch (current) { - case NodeStatus.inactive: - return NodeStatus.pending; - case NodeStatus.pending: - return NodeStatus.running; - case NodeStatus.running: - return NodeStatus.success; - case NodeStatus.success: - return NodeStatus.failure; - case NodeStatus.failure: - return NodeStatus.inactive; - } - } -} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder_page.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder_page.dart deleted file mode 100644 index 3be47f5b7..000000000 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder_page.dart +++ /dev/null @@ -1,880 +0,0 @@ -import 'package:apidash_core/consts.dart'; -import 'package:apidash_core/models/http_request_model.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'models/models.dart'; -import 'workflow_providers.dart'; -import 'workflow_canvas.dart'; -import 'models/workflow_execution_state.dart'; -import 'widgets/widgets.dart'; - -enum LeftPanelTab { - apiComponents, - templates, -} - -final leftPanelTabProvider = StateProvider((ref) { - return LeftPanelTab.apiComponents; -}); - -class WorkflowBuilderPage extends ConsumerWidget { - const WorkflowBuilderPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final workflowId = ref.watch(currentWorkflowProvider); - final workflowExecutionState = ref.watch(workflowExecutionStateProvider); - final connectionMode = ref.watch(connectionModeProvider); - final executionControl = ref.watch(workflowExecutionControlProvider); - final selectedNodeId = ref.watch(selectedNodeIdProvider); - - if (workflowId == null) { - return _buildNoWorkflowScreen(context, ref); - } - - WorkflowNodeModel? selectedNode; - if (selectedNodeId != null) { - final workflows = ref.watch(workflowsNotifierProvider); - final workflow = workflows.firstWhere( - (w) => w.id == workflowId, - orElse: () => throw Exception('Workflow not found'), - ); - - selectedNode = workflow.nodes.firstWhere( - (n) => n.id == selectedNodeId, - orElse: () => throw Exception('Node not found'), - ); - } - - return Scaffold( - backgroundColor: const Color(0xFF1A1A1A), - appBar: _buildAppBar(ref, workflowExecutionState), - body: Column( - children: [ - _buildToolbar( - ref, connectionMode, workflowExecutionState, executionControl), - Expanded( - child: Row( - children: [ - _buildLeftPanel(ref), - Expanded( - child: _buildCanvasWithLogs(workflowId), - ), - if (selectedNode != null) - NodeDetailsPanel( - node: selectedNode, - workflowId: workflowId, - ), - ], - ), - ), - ], - ), - floatingActionButton: FloatingActionButton( - backgroundColor: Colors.blue, - onPressed: () { - final notifier = ref.read(workflowsNotifierProvider.notifier); - notifier.addNode( - workflowId, - WorkflowNodeModel.create( - requestId: workflowId, - position: const Offset(100, 100), - label: 'New API', - nodeType: NodeType.request, - ), - ); - }, - child: const Icon(Icons.add), - ), - ); - } - - Widget _buildToolbar( - WidgetRef ref, - bool connectionMode, - WorkflowExecutionState executionState, - WorkflowExecutionControl executionControl, - ) { - return Container( - height: 56, - color: const Color(0xFF212121), - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Row( - children: [ - ToggleButton( - icon: Icons.cable, - label: 'Connection Mode', - isActive: connectionMode, - onPressed: () { - ref.read(connectionModeProvider.notifier).state = !connectionMode; - }, - ), - - const SizedBox(width: 16), - - Container(width: 1, height: 24, color: Colors.grey.shade700), - - const SizedBox(width: 16), - - _buildExecutionControls(executionState, executionControl), - - const Spacer(), - - if (executionState.isRunning || - executionState.isPaused || - executionState.isCompleted) - Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), - decoration: BoxDecoration( - color: _getStatusColor(executionState), - borderRadius: BorderRadius.circular(16), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - _getStatusIcon(executionState), - color: Colors.white, - size: 16, - ), - const SizedBox(width: 4), - Text( - _getStatusText(executionState), - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 12, - ), - ), - ], - ), - ), - ], - ), - ); - } - - Widget _buildExecutionControls( - WorkflowExecutionState executionState, - WorkflowExecutionControl executionControl, - ) { - return Row( - children: [ - IconButton( - icon: const Icon(Icons.play_arrow), - color: executionState.isRunning ? Colors.grey : Colors.green, - onPressed: executionState.isRunning - ? null - : executionControl.startExecution, - tooltip: 'Start Execution', - ), - - IconButton( - icon: Icon(executionState.isPaused ? Icons.play_circle : Icons.pause), - color: !executionState.isRunning && !executionState.isPaused - ? Colors.grey - : executionState.isPaused - ? Colors.amber - : Colors.blue, - onPressed: !executionState.isRunning && !executionState.isPaused - ? null - : executionState.isPaused - ? executionControl.resumeExecution - : executionControl.pauseExecution, - tooltip: executionState.isPaused ? 'Resume' : 'Pause', - ), - - IconButton( - icon: const Icon(Icons.stop), - color: !executionState.isRunning && !executionState.isPaused - ? Colors.grey - : Colors.red, - onPressed: !executionState.isRunning && !executionState.isPaused - ? null - : executionControl.stopExecution, - tooltip: 'Stop Execution', - ), - ], - ); - } - - AppBar _buildAppBar( - WidgetRef ref, - WorkflowExecutionState workflowExecutionState, - ) { - final workflowId = ref.watch(currentWorkflowProvider); - final workflows = ref.watch(workflowsNotifierProvider); - final workflow = workflowId != null - ? workflows.firstWhere( - (w) => w.id == workflowId, - orElse: () => throw Exception('Workflow not found'), - ) - : null; - - return AppBar( - backgroundColor: const Color(0xFF212121), - title: Row( - children: [ - const Icon(Icons.account_tree, color: Colors.blue), - const SizedBox(width: 8), - Text( - workflow?.name ?? 'Workflow Builder', - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - actions: [ - if (workflowExecutionState.isRunning || - workflowExecutionState.isPaused || - workflowExecutionState.isCompleted || - workflowExecutionState.hasError) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: Chip( - backgroundColor: _getStatusColor(workflowExecutionState), - label: Text( - _getStatusText(workflowExecutionState), - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ), - avatar: Icon( - _getStatusIcon(workflowExecutionState), - color: Colors.white, - size: 16, - ), - ), - ), - ], - ); - } - - Widget _buildLeftPanel(WidgetRef ref) { - final selectedTab = ref.watch(leftPanelTabProvider); - - return Container( - width: 250, - color: const Color(0xFF252525), - child: Column( - children: [ - Container( - color: const Color(0xFF212121), - child: Row( - children: [ - _buildSidebarItem( - Icons.api, - 'API Components', - selectedTab == LeftPanelTab.apiComponents, - onTap: () => ref.read(leftPanelTabProvider.notifier).state = - LeftPanelTab.apiComponents, - ), - _buildSidebarItem( - Icons.description_outlined, - 'Templates', - selectedTab == LeftPanelTab.templates, - onTap: () => ref.read(leftPanelTabProvider.notifier).state = - LeftPanelTab.templates, - ), - ], - ), - ), - - Expanded( - child: selectedTab == LeftPanelTab.apiComponents - ? const APIComponentList() - : const TemplatesList(), - ), - ], - ), - ); - } - - Widget _buildSidebarItem( - IconData icon, - String title, - bool isSelected, - {VoidCallback? onTap} - ) { - return Expanded( - child: InkWell( - onTap: onTap, - child: Container( - padding: const EdgeInsets.symmetric(vertical: 12), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: isSelected ? Colors.blue : Colors.transparent, - width: 2, - ), - ), - color: isSelected - ? const Color(0xFF2A2A2A) - : const Color(0xFF212121), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - icon, - color: isSelected ? Colors.blue : Colors.grey, - size: 20, - ), - const SizedBox(height: 4), - Text( - title, - style: TextStyle( - color: isSelected ? Colors.white : Colors.grey, - fontSize: 12, - ), - ), - ], - ), - ), - ), - ); - } - - Widget _buildCanvasWithLogs(String workflowId) { - return Column( - children: [ - Expanded( - flex: 7, - child: _buildRightPanel(workflowId), - ), - - Expanded( - flex: 3, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: LogsViewer(workflowId: workflowId), - ), - ), - ], - ); - } - - Widget _buildRightPanel(String? workflowId) { - return Container( - color: const Color(0xFF1A1A1A), - child: workflowId != null - ? WorkflowCanvas(workflowId: workflowId) - : const Center(child: Text('No workflow selected')), - ); - } - - Widget _buildNoWorkflowScreen(BuildContext context, WidgetRef ref) { - return Scaffold( - backgroundColor: const Color(0xFF1A1A1A), - appBar: AppBar( - backgroundColor: const Color(0xFF212121), - title: const Row( - children: [ - Icon(Icons.account_tree, color: Colors.blue), - SizedBox(width: 8), - Text( - 'Workflow Builder', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.account_tree_outlined, - size: 100, - color: Colors.blue.withOpacity(0.7), - ), - const SizedBox(height: 32), - const Text( - 'Welcome to Workflow Builder', - style: TextStyle( - color: Colors.white, - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 16), - const Text( - 'Create and manage API workflows visually', - style: TextStyle( - color: Colors.grey, - fontSize: 16, - ), - ), - const SizedBox(height: 32), - ElevatedButton.icon( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - padding: - const EdgeInsets.symmetric(horizontal: 32, vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - icon: const Icon(Icons.add), - label: const Text('Create New Workflow'), - onPressed: () { - final notifier = ref.read(workflowsNotifierProvider.notifier); - final newWorkflowId = notifier.createWorkflow( - name: 'New Workflow', - description: 'Created on ${DateTime.now().toLocal()}', - ); - - ref.read(currentWorkflowProvider.notifier).state = newWorkflowId; - }, - ), - ], - ), - ), - ); - } - - Color _getStatusColor(WorkflowExecutionState state) { - if (state.hasError) return Colors.red; - if (state.isCompleted) return Colors.green; - if (state.isPaused) return Colors.amber; - if (state.isRunning) return Colors.blue; - return Colors.grey; - } - - IconData _getStatusIcon(WorkflowExecutionState state) { - if (state.hasError) return Icons.error_outline; - if (state.isCompleted) return Icons.check_circle_outline; - if (state.isPaused) return Icons.pause_circle_outline; - if (state.isRunning) return Icons.sync; - return Icons.hourglass_empty; - } - - String _getStatusText(WorkflowExecutionState state) { - if (state.hasError) return 'Failed'; - if (state.isCompleted) return 'Completed'; - if (state.isPaused) return 'Paused'; - if (state.isRunning) return 'Running'; - return 'Ready'; - } -} - -class ToggleButton extends StatelessWidget { - final IconData icon; - final String label; - final bool isActive; - final VoidCallback onPressed; - - const ToggleButton({ - Key? key, - required this.icon, - required this.label, - required this.isActive, - required this.onPressed, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: onPressed, - borderRadius: BorderRadius.circular(4), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), - decoration: BoxDecoration( - color: isActive ? Colors.blue.withOpacity(0.2) : Colors.transparent, - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: isActive ? Colors.blue : Colors.transparent, - width: 1, - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - icon, - color: isActive ? Colors.blue : Colors.grey, - size: 18, - ), - const SizedBox(width: 8), - Text( - label, - style: TextStyle( - color: isActive ? Colors.blue : Colors.grey, - fontWeight: isActive ? FontWeight.bold : FontWeight.normal, - ), - ), - ], - ), - ), - ); - } -} - -class APIComponentList extends ConsumerWidget { - const APIComponentList({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return ListView( - padding: const EdgeInsets.all(12), - children: [ - const Text( - 'API Request Components', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - const SizedBox(height: 16), - _APIComponentCard( - icon: Icons.call_made, - title: 'GET Request', - description: 'Fetch data from an API', - requestModel: RequestModel( - id: 'api1', - name: 'GET Request', - httpRequestModel: HttpRequestModel( - method: HTTPVerb.get, - url: 'https://api.example.com/data', - ), - ), - ), - _APIComponentCard( - icon: Icons.send, - title: 'POST Request', - description: 'Send data to an API', - requestModel: RequestModel( - id: 'api2', - name: 'POST Request', - httpRequestModel: HttpRequestModel( - method: HTTPVerb.post, - url: 'https://api.example.com/create', - ), - ), - ), - const SizedBox(height: 24), - const Text( - 'Flow Control Components', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - const SizedBox(height: 16), - _FlowControlCard( - icon: Icons.fork_right, - title: 'Condition', - description: 'Branch based on a condition', - nodeType: NodeType.condition, - ), - _FlowControlCard( - icon: Icons.settings, - title: 'Action', - description: 'Perform a custom action', - nodeType: NodeType.action, - ), - _FlowControlCard( - icon: Icons.call_received, - title: 'Response', - description: 'Simulate an API response', - nodeType: NodeType.response, - ), - ], - ); - } -} - -class TemplatesList extends StatelessWidget { - const TemplatesList({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ListView( - padding: const EdgeInsets.all(12), - children: [ - const Text( - 'Workflow Templates', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - const SizedBox(height: 16), - _TemplateCard( - title: 'Authentication Flow', - description: 'Login, get token, and fetch user data', - icon: Icons.security, - ), - _TemplateCard( - title: 'CRUD Operations', - description: 'Create, read, update, and delete resources', - icon: Icons.storage, - ), - _TemplateCard( - title: 'Data Processing', - description: 'Fetch, transform, and store data', - icon: Icons.transform, - ), - ], - ); - } -} - -class _APIComponentCard extends StatelessWidget { - final IconData icon; - final String title; - final String description; - final RequestModel? requestModel; - - const _APIComponentCard({ - required this.icon, - required this.title, - required this.description, - required this.requestModel, - }); - - @override - Widget build(BuildContext context) { - return Card( - margin: const EdgeInsets.only(bottom: 8), - child: Draggable( - data: WorkflowNodeModel.create( - requestId: 'temp_id', - position: const Offset(0, 0), - label: title, - requestModel: requestModel, - ), - feedback: Material( - color: Colors.transparent, - elevation: 4, - borderRadius: BorderRadius.circular(8), - child: Container( - width: 200, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), - border: Border.all(color: Colors.blue, width: 2), - ), - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Icon(icon, color: Colors.blue, size: 20), - const SizedBox(width: 8), - Text( - title, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - ], - ), - const SizedBox(height: 8), - Text( - description, - style: TextStyle(color: Colors.grey[600], fontSize: 12), - ), - ], - ), - ), - ), - childWhenDragging: Opacity( - opacity: 0.5, - child: ListTile( - leading: Icon(icon, color: Colors.grey), - title: Text(title), - subtitle: Text(description), - ), - ), - child: ListTile( - leading: Icon(icon, color: Colors.blue), - title: Text(title), - subtitle: Text(description), - contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), - ), - ), - ); - } -} - -class _FlowControlCard extends StatelessWidget { - final IconData icon; - final String title; - final String description; - final NodeType nodeType; - - const _FlowControlCard({ - required this.icon, - required this.title, - required this.description, - required this.nodeType, - }); - - @override - Widget build(BuildContext context) { - return Card( - margin: const EdgeInsets.only(bottom: 8), - child: Draggable( - data: WorkflowNodeModel.create( - requestId: 'temp_id', - position: const Offset(0, 0), - label: title, - nodeType: nodeType, - nodeData: nodeType == NodeType.condition - ? {'condition': 'statusCode == 200'} - : nodeType == NodeType.action - ? {'actionType': 'transform', 'actionConfig': ''} - : {}, - ), - feedback: Material( - color: Colors.transparent, - elevation: 4, - borderRadius: BorderRadius.circular(8), - child: Container( - width: 200, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: nodeType == NodeType.condition - ? Colors.amber - : nodeType == NodeType.action - ? Colors.green - : Colors.purple, - width: 2, - ), - ), - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Icon( - icon, - color: nodeType == NodeType.condition - ? Colors.amber - : nodeType == NodeType.action - ? Colors.green - : Colors.purple, - size: 20, - ), - const SizedBox(width: 8), - Text( - title, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - ], - ), - const SizedBox(height: 8), - Text( - description, - style: TextStyle(color: Colors.grey[600], fontSize: 12), - ), - ], - ), - ), - ), - childWhenDragging: Opacity( - opacity: 0.5, - child: ListTile( - leading: Icon(icon, color: Colors.grey), - title: Text(title), - subtitle: Text(description), - ), - ), - child: ListTile( - leading: Icon( - icon, - color: nodeType == NodeType.condition - ? Colors.amber - : nodeType == NodeType.action - ? Colors.green - : Colors.purple, - ), - title: Text(title), - subtitle: Text(description), - contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), - ), - ), - ); - } -} - -class _TemplateCard extends StatelessWidget { - final String title; - final String description; - final IconData icon; - - const _TemplateCard({ - required this.title, - required this.description, - required this.icon, - }); - - @override - Widget build(BuildContext context) { - return Card( - margin: const EdgeInsets.only(bottom: 8), - child: ListTile( - leading: Icon(icon, color: Colors.teal), - title: Text(title), - subtitle: Text(description), - contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), - trailing: const Icon(Icons.add_circle_outline, color: Colors.teal), - onTap: () { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Template "$title" will be implemented soon'), - ), - ); - }, - ), - ); - } -} - -class GridBackgroundPainter extends CustomPainter { - final Color gridColor; - final double step; - - GridBackgroundPainter({ - required this.gridColor, - required this.step, - }); - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = gridColor - ..strokeWidth = 1.0; - - for (double x = 0; x < size.width; x += step) { - canvas.drawLine(Offset(x, 0), Offset(x, size.height), paint); - } - for (double y = 0; y < size.height; y += step) { - canvas.drawLine(Offset(0, y), Offset(size.width, y), paint); - } - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => false; -} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_canvas.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_canvas.dart deleted file mode 100644 index b9f81cc44..000000000 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_canvas.dart +++ /dev/null @@ -1,493 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:uuid/uuid.dart'; -import 'models/workflow_node_model.dart'; -import 'workflow_node.dart'; -import 'workflow_providers.dart'; -import 'models/workflow_model.dart'; -import 'grid_painter.dart'; -import 'connection_painter.dart'; - -class WorkflowCanvas extends ConsumerStatefulWidget { - final String? workflowId; - - const WorkflowCanvas({ - super.key, - required this.workflowId, - }); - - @override - ConsumerState createState() => _WorkflowCanvasState(); -} - -class _WorkflowCanvasState extends ConsumerState { - final TransformationController _transformationController = - TransformationController(); - double _scale = 1.0; - - final GlobalKey _canvasKey = GlobalKey(); - - String? _sourceNodeId; - Offset? _connectionStart; - Offset? _connectionEnd; - - String? _draggedNodeId; - Offset? _lastDragPosition; - - @override - void initState() { - super.initState(); - _transformationController.value = Matrix4.identity()..translate(50.0, 50.0); - } - - void _handleApiNodeAdded(WorkflowNodeModel newNode, Offset position) { - try { - if (widget.workflowId == null) return; - - final RenderBox? renderBox = - _canvasKey.currentContext?.findRenderObject() as RenderBox?; - if (renderBox == null) return; - - final viewportCenter = _transformationController.toScene( - Offset( - renderBox.size.width / 2, - renderBox.size.height / 2, - ), - ); - - final finalPosition = position.dx == 0 && position.dy == 0 - ? Offset(viewportCenter.dx - 80, - viewportCenter.dy - 40) // Center with offset - : _transformationController.toScene(position); - - final node = newNode.copyWith( - id: const Uuid().v4(), - requestId: widget.workflowId!, - position: finalPosition, - ); - - ref.read(workflowsNotifierProvider.notifier).addNode( - widget.workflowId!, - node, - ); - } catch (e) { - debugPrint('Error adding node: $e'); - } - } - - void _startConnection(String nodeId, Offset position) { - setState(() { - _sourceNodeId = nodeId; - _connectionStart = position; - _connectionEnd = position; - }); - } - - void _updateConnection(Offset position) { - if (_sourceNodeId == null) return; - - setState(() { - _connectionEnd = position; - }); - } - - void _finishConnection(String? targetNodeId) { - if (_sourceNodeId != null && - targetNodeId != null && - _sourceNodeId != targetNodeId) { - ref.read(workflowsNotifierProvider.notifier).createConnection( - widget.workflowId!, - _sourceNodeId!, - targetNodeId, - ); - } - - setState(() { - _sourceNodeId = null; - _connectionStart = null; - _connectionEnd = null; - }); - } - - Offset? _dragOffsetFromNodeTopLeft; - - void _startNodeDrag(String nodeId, Offset globalPosition) { - final scenePosition = _transformationController.toScene(globalPosition); - - final workflows = ref.read(workflowsNotifierProvider); - final currentWorkflow = workflows.firstWhere( - (workflow) => workflow.id == widget.workflowId, - orElse: () => throw Exception('Workflow not found'), - ); - - final node = currentWorkflow.nodes.firstWhere( - (node) => node.id == nodeId, - orElse: () => throw Exception('Node not found'), - ); - - final offsetFromTopLeft = scenePosition - node.position; - - setState(() { - _draggedNodeId = nodeId; - _lastDragPosition = scenePosition; - _dragOffsetFromNodeTopLeft = offsetFromTopLeft; - }); - } - - void _updateNodeDrag(Offset globalPosition) { - if (_draggedNodeId == null || _dragOffsetFromNodeTopLeft == null) return; - - final scenePosition = _transformationController.toScene(globalPosition); - - final newTopLeft = scenePosition - _dragOffsetFromNodeTopLeft!; - - ref.read(workflowsNotifierProvider.notifier).updateNodePosition( - widget.workflowId!, - _draggedNodeId!, - newTopLeft, - ); - - setState(() { - _lastDragPosition = scenePosition; - }); - } - - void _finishNodeDrag() { - setState(() { - _draggedNodeId = null; - _lastDragPosition = null; - }); - } - - @override - Widget build(BuildContext context) { - final workflowId = widget.workflowId; - if (workflowId == null) { - return const Center(child: Text('No workflow selected')); - } - - final currentWorkflow = ref.watch( - activeWorkflowProvider, - ); - - if (currentWorkflow == null) { - return const Center(child: Text('Workflow not found')); - } - - final isConnectionMode = ref.watch(connectionModeProvider); - - final currentConnections = currentWorkflow.connections; - - final nodePositions = {}; - for (final node in currentWorkflow.nodes) { - nodePositions[node.id] = node.position; - } - - final workflowExecutionState = ref.watch(workflowExecutionStateProvider); - final runningNodeIds = workflowExecutionState.currentNodeId != null - ? [workflowExecutionState.currentNodeId!] - : []; - final completedNodeIds = workflowExecutionState.executedNodeIds.toList(); - - return Stack( - key: _canvasKey, - children: [ - Container( - color: const Color(0xFF121212), - width: double.infinity, - height: double.infinity, - child: CustomPaint( - painter: GridPainter( - gridColor: Colors.grey.shade800.withOpacity(0.3), - gridWidth: 1.0, - gridSpacing: 40 * _scale, - ), - ), - ), - - InteractiveViewer( - transformationController: _transformationController, - minScale: 0.3, - maxScale: 2.0, - constrained: false, - onInteractionUpdate: (details) { - setState(() { - _scale = _transformationController.value.getMaxScaleOnAxis(); - }); - }, - child: SizedBox( - width: 3000, - height: 2000, - child: Stack( - clipBehavior: Clip.none, - children: [ - Positioned.fill( - child: GestureDetector( - onTapDown: (details) { - if (isConnectionMode && _sourceNodeId != null) { - final scenePosition = _transformationController - .toScene(details.localPosition); - - final hitNodeId = _hitTestNodes( - scenePosition, currentWorkflow.nodes); - - if (hitNodeId == null) { - setState(() { - _sourceNodeId = null; - _connectionStart = null; - _connectionEnd = null; - }); - } - } - - if (!isConnectionMode) { - ref.read(selectedNodeIdProvider.notifier).state = null; - } - }, - onPanUpdate: (details) { - if (isConnectionMode && _sourceNodeId != null) { - _updateConnection(_transformationController - .toScene(details.localPosition)); - } - }, - child: Container( - color: Colors.transparent, - ), - ), - ), - - CustomPaint( - painter: ConnectionPainter( - connections: currentConnections, - nodes: currentWorkflow.nodes, - nodePositions: nodePositions, - tempConnectionStart: _connectionStart, - tempConnectionEnd: _connectionEnd, - runningNodeIds: runningNodeIds, - completedNodeIds: completedNodeIds, - ), - size: const Size(3000, 2000), - ), - - ...currentWorkflow.nodes.map( - (node) { - return Positioned( - left: node.position.dx, - top: node.position.dy, - child: MouseRegion( - cursor: SystemMouseCursors.grab, - child: GestureDetector( - onTap: () { - ref.read(selectedNodeIdProvider.notifier).state = - node.id; - - if (isConnectionMode) { - if (_sourceNodeId == null) { - final nodeCenter = Offset( - node.position.dx + 80, // Half of node width - node.position.dy + 40, // Half of node height - ); - _startConnection(node.id, nodeCenter); - } else if (_sourceNodeId != node.id) { - _finishConnection(node.id); - } else { - setState(() { - _sourceNodeId = null; - _connectionStart = null; - _connectionEnd = null; - }); - } - } - }, - onPanStart: (details) { - if (!isConnectionMode) { - _startNodeDrag(node.id, details.globalPosition); - } - }, - onPanUpdate: (details) { - if (!isConnectionMode) { - _updateNodeDrag(details.globalPosition); - } else if (_sourceNodeId == node.id) { - final localPosition = _transformationController - .toScene(details.localPosition); - _updateConnection(localPosition); - } - }, - onPanEnd: (details) { - if (!isConnectionMode) { - _finishNodeDrag(); - } else if (_sourceNodeId == node.id) { - setState(() { - _sourceNodeId = null; - _connectionStart = null; - _connectionEnd = null; - }); - } - }, - child: WorkflowNode( - node: node, - isSelected: node.id == ref.watch(selectedNodeIdProvider), - isRunning: - workflowExecutionState.currentNodeId == node.id, - isCompleted: - workflowExecutionState.executedNodeIds.contains(node.id), - hasError: workflowExecutionState.executionResults[node.id] - ?['status'] == - 'error', - ), - ), - ), - ); - }, - ).toList(), - - Positioned.fill( - child: DragTarget( - builder: (context, candidateData, rejectedData) { - return const SizedBox.expand(); - }, - onWillAccept: (data) => true, - onAccept: (data) { - final RenderBox renderBox = - context.findRenderObject() as RenderBox; - final localPosition = renderBox.globalToLocal(Offset.zero); - final position = _transformationController.toScene(localPosition); - - _handleApiNodeAdded(data, position); - }, - ), - ), - ], - ), - ), - ), - - Positioned( - left: 16, - bottom: 16, - child: _buildCanvasControls(), - ), - - Positioned( - top: 16, - right: 16, - child: _buildCanvasInfo(currentWorkflow), - ), - ], - ); - } - - Widget _buildCanvasControls() { - return Card( - color: Colors.black.withOpacity(0.7), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: const Icon(Icons.home, color: Colors.white), - onPressed: () { - _transformationController.value = Matrix4.identity() - ..translate(50.0, 50.0); - setState(() { - _scale = 1.0; - }); - }, - tooltip: 'Reset View', - ), - IconButton( - icon: const Icon(Icons.remove, color: Colors.white), - onPressed: () { - final scale = - _transformationController.value.getMaxScaleOnAxis(); - if (scale > 0.5) { - _transformationController.value.scale(0.8, 0.8, 0.8); - setState(() { - _scale = - _transformationController.value.getMaxScaleOnAxis(); - }); - } - }, - tooltip: 'Zoom Out', - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - decoration: BoxDecoration( - color: Colors.blue.withOpacity(0.2), - borderRadius: BorderRadius.circular(4), - ), - child: Text( - '${(_scale * 100).toInt()}%', - style: const TextStyle(color: Colors.white), - ), - ), - IconButton( - icon: const Icon(Icons.add, color: Colors.white), - onPressed: () { - final scale = - _transformationController.value.getMaxScaleOnAxis(); - if (scale < 2.0) { - _transformationController.value.scale(1.25, 1.25, 1.25); - setState(() { - _scale = - _transformationController.value.getMaxScaleOnAxis(); - }); - } - }, - tooltip: 'Zoom In', - ), - ], - ), - ), - ); - } - - Widget _buildCanvasInfo(WorkflowModel workflow) { - return Card( - color: Colors.black.withOpacity(0.7), - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - workflow.name, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - const SizedBox(height: 4), - Text( - '${workflow.nodes.length} nodes · ${workflow.connections.length} connections', - style: TextStyle( - color: Colors.grey[400], - fontSize: 12, - ), - ), - ], - ), - ), - ); - } - - String? _hitTestNodes(Offset position, List nodes) { - for (final node in nodes) { - final nodeRect = Rect.fromLTWH( - node.position.dx, - node.position.dy, - 160, - 80, - ); - - if (nodeRect.contains(position)) { - return node.id; - } - } - - return null; - } -} From a48aa53ed529334d3925a0f452bb3f359ce41942 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Fri, 18 Apr 2025 15:22:09 +0530 Subject: [PATCH 152/188] feat: add LogEntry and WorkflowLogEntry models with logging functionality; update LogsViewer to utilize new models --- .../workflow_builder/models/log_entry.dart | 57 +++++++++++++++++++ .../src/workflow_builder/models/models.dart | 4 ++ .../workflow_builder/models/node_type.dart | 7 +++ .../models/workflow_log_entry.dart | 22 +++++++ .../workflow_builder/widgets/logs_viewer.dart | 39 +++---------- 5 files changed, 98 insertions(+), 31 deletions(-) create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/models/log_entry.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/models/node_type.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/models/workflow_log_entry.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/log_entry.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/log_entry.dart new file mode 100644 index 000000000..7fe46c07c --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/log_entry.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +/// Log level enum for categorizing log entries +enum LogLevel { + debug, + info, + success, + warning, + error, +} + +/// Model for workflow log entries +class LogEntry { + final String message; + final LogLevel level; + final DateTime timestamp; + final String? nodeId; + final String? details; + + LogEntry({ + required this.message, + this.level = LogLevel.info, + DateTime? timestamp, + this.nodeId, + this.details, + }) : timestamp = timestamp ?? DateTime.now(); + + Color getColor() { + switch (level) { + case LogLevel.info: + return Colors.blue; + case LogLevel.success: + return Colors.green; + case LogLevel.warning: + return Colors.orange; + case LogLevel.error: + return Colors.red; + case LogLevel.debug: + return Colors.grey; + } + } + + IconData getIcon() { + switch (level) { + case LogLevel.info: + return Icons.info_outline; + case LogLevel.success: + return Icons.check_circle_outline; + case LogLevel.warning: + return Icons.warning_amber_outlined; + case LogLevel.error: + return Icons.error_outline; + case LogLevel.debug: + return Icons.code; + } + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/models.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/models.dart index 7b334cfcb..e824a64ff 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/models/models.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/models.dart @@ -4,3 +4,7 @@ export 'request_model.dart'; export 'workflow_connection_model.dart'; export 'workflow_model.dart'; export 'workflow_node_model.dart'; +export 'node_type.dart'; +export 'log_entry.dart'; +export 'execution_command.dart'; +export 'workflow_execution_state.dart'; diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/node_type.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/node_type.dart new file mode 100644 index 000000000..7f1597da0 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/node_type.dart @@ -0,0 +1,7 @@ +/// Enum representing the different types of nodes in a workflow +enum NodeType { + request, + condition, + action, + response, +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_log_entry.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_log_entry.dart new file mode 100644 index 000000000..88e6c7266 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_log_entry.dart @@ -0,0 +1,22 @@ +import 'log_entry.dart'; + +class WorkflowLogEntry extends LogEntry { + WorkflowLogEntry({ + required String message, + required LogLevel level, + DateTime? timestamp, + String? nodeId, + String? details, + }) : super( + message: message, + level: level, + timestamp: timestamp, + nodeId: nodeId, + details: details, + ); + + @override + String toString() { + return '[$level] ${timestamp.toIso8601String()}: $message'; + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart b/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart index 6062b9019..b4abc6acf 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../models/workflow_execution_state.dart'; import '../workflow_providers.dart'; +import '../models/log_entry.dart'; class LogsViewer extends ConsumerWidget { final String workflowId; @@ -34,7 +35,7 @@ class LogsViewer extends ConsumerWidget { Expanded( child: logs.isEmpty ? _buildEmptyState() - : _buildLogsList(logs), + : _buildLogsList(logs.cast()), ), ], ), @@ -142,7 +143,7 @@ class LogsViewer extends ConsumerWidget { ); } - Widget _buildLogsList(List logs) { + Widget _buildLogsList(List logs) { return ListView.builder( itemCount: logs.length, reverse: true, @@ -153,9 +154,11 @@ class LogsViewer extends ConsumerWidget { ); } - Widget _buildLogItem(WorkflowLogEntry log) { + Widget _buildLogItem(LogEntry log) { Color getLogColor() { switch (log.level) { + case LogLevel.debug: + return Colors.grey; case LogLevel.info: return Colors.blue; case LogLevel.success: @@ -164,13 +167,13 @@ class LogsViewer extends ConsumerWidget { return Colors.orange; case LogLevel.error: return Colors.red; - case LogLevel.debug: - return Colors.grey; } } IconData getLogIcon() { switch (log.level) { + case LogLevel.debug: + return Icons.code; case LogLevel.info: return Icons.info_outline; case LogLevel.success: @@ -179,8 +182,6 @@ class LogsViewer extends ConsumerWidget { return Icons.warning_amber_outlined; case LogLevel.error: return Icons.error_outline; - case LogLevel.debug: - return Icons.code; } } @@ -273,27 +274,3 @@ class LogsViewer extends ConsumerWidget { } } } - -class WorkflowLogEntry { - final String message; - final LogLevel level; - final DateTime timestamp; - final String? nodeId; - final String? details; - - WorkflowLogEntry({ - required this.message, - this.level = LogLevel.info, - DateTime? timestamp, - this.nodeId, - this.details, - }) : timestamp = timestamp ?? DateTime.now(); -} - -enum LogLevel { - debug, - info, - success, - warning, - error, -} From b7f0899d5ad7bb6f13694d9a6fef14bbe4048751 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Fri, 18 Apr 2025 15:23:29 +0530 Subject: [PATCH 153/188] feat: implement canvas components including connection layer, controls, drop target, event handlers, info card, node layer, and view in separate files and directory --- .../canvas/canvas_connection_layer.dart | 179 ++++++++++++ .../components/canvas/canvas_controls.dart | 59 ++++ .../components/canvas/canvas_drop_target.dart | 30 ++ .../canvas/canvas_event_handlers.dart | 181 ++++++++++++ .../components/canvas/canvas_info_card.dart | 39 +++ .../components/canvas/canvas_node_layer.dart | 257 ++++++++++++++++++ .../components/canvas/canvas_view.dart | 183 +++++++++++++ 7 files changed, 928 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_connection_layer.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_controls.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_drop_target.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_info_card.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_view.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_connection_layer.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_connection_layer.dart new file mode 100644 index 000000000..fc025338e --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_connection_layer.dart @@ -0,0 +1,179 @@ +import 'package:flutter/material.dart'; +import '../../models/workflow_model.dart'; + +class ConnectionPainter extends CustomPainter { + final WorkflowModel workflow; + final Map nodePositions; + final List runningNodeIds; + final List completedNodeIds; + + ConnectionPainter({ + required this.workflow, + required this.nodePositions, + required this.runningNodeIds, + required this.completedNodeIds, + }); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = Colors.grey + ..strokeWidth = 2.0 + ..style = PaintingStyle.stroke; + + for (final connection in workflow.connections) { + final sourcePosition = nodePositions[connection.sourceId]; + final targetPosition = nodePositions[connection.targetId]; + + if (sourcePosition != null && targetPosition != null) { + // Adjust start and end points to be at the edges of nodes (assuming nodes are 150x60) + final start = Offset(sourcePosition.dx + 75, sourcePosition.dy + 30); + final end = Offset(targetPosition.dx + 75, targetPosition.dy + 30); + + // Draw arrow line + final path = Path(); + path.moveTo(start.dx, start.dy); + + // Create curved path + path.cubicTo( + start.dx + (end.dx - start.dx) * 0.5, start.dy, + start.dx + (end.dx - start.dx) * 0.5, end.dy, + end.dx, end.dy + ); + + // Draw connection + canvas.drawPath(path, paint); + + // Draw arrow tip + _drawArrowTip(canvas, end, start, paint); + } + } + } + + void _drawArrowTip(Canvas canvas, Offset end, Offset start, Paint paint) { + // Calculate direction vector + final direction = (end - start).normalized(); + // Calculate perpendicular vector + final perpendicular = Offset(-direction.dy, direction.dx); + + // Define arrow head + final arrowSize = 10.0; + final arrowTip = end; + final arrowBase1 = end - direction * arrowSize + perpendicular * arrowSize * 0.5; + final arrowBase2 = end - direction * arrowSize - perpendicular * arrowSize * 0.5; + + // Draw arrow head + final path = Path() + ..moveTo(arrowTip.dx, arrowTip.dy) + ..lineTo(arrowBase1.dx, arrowBase1.dy) + ..lineTo(arrowBase2.dx, arrowBase2.dy) + ..close(); + + canvas.drawPath(path, paint..style = PaintingStyle.fill); + } + + @override + bool shouldRepaint(ConnectionPainter oldDelegate) { + return oldDelegate.workflow != workflow || + oldDelegate.nodePositions != nodePositions || + oldDelegate.runningNodeIds != runningNodeIds || + oldDelegate.completedNodeIds != completedNodeIds; + } +} + +class TemporaryConnectionPainter extends CustomPainter { + final Offset start; + final Offset end; + + TemporaryConnectionPainter({ + required this.start, + required this.end, + }); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = Colors.blue.withOpacity(0.6) + ..strokeWidth = 2.0 + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + + // Draw dashed line + final path = Path(); + path.moveTo(start.dx, start.dy); + + // Create curved path + path.cubicTo( + start.dx + (end.dx - start.dx) * 0.5, start.dy, + start.dx + (end.dx - start.dx) * 0.5, end.dy, + end.dx, end.dy + ); + + // Draw connection + canvas.drawPath(path, paint); + } + + @override + bool shouldRepaint(TemporaryConnectionPainter oldDelegate) { + return oldDelegate.start != start || oldDelegate.end != end; + } +} + +// Extension to add normalized method to Offset +extension OffsetExt on Offset { + Offset normalized() { + final magnitude = distance; + if (magnitude == 0) return Offset.zero; + return Offset(dx / magnitude, dy / magnitude); + } +} + +class CanvasConnectionLayer extends StatelessWidget { + final WorkflowModel workflow; + final Map nodePositions; + final String? sourceNodeId; + final Offset? connectionStart; + final Offset? connectionEnd; + final List runningNodeIds; + final List completedNodeIds; + + const CanvasConnectionLayer({ + super.key, + required this.workflow, + required this.nodePositions, + this.sourceNodeId, + this.connectionStart, + this.connectionEnd, + required this.runningNodeIds, + required this.completedNodeIds, + }); + + @override + Widget build(BuildContext context) { + return SizedBox.expand( + child: Stack( + children: [ + // Permanent connections between nodes + CustomPaint( + size: Size.infinite, + painter: ConnectionPainter( + workflow: workflow, + nodePositions: nodePositions, + runningNodeIds: runningNodeIds, + completedNodeIds: completedNodeIds, + ), + ), + // Temporary connection being drawn + if (sourceNodeId != null && connectionStart != null && connectionEnd != null) + CustomPaint( + size: Size.infinite, + painter: TemporaryConnectionPainter( + start: connectionStart!, + end: connectionEnd!, + ), + ), + ], + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_controls.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_controls.dart new file mode 100644 index 000000000..9d023bedd --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_controls.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; + +class CanvasControls extends StatelessWidget { + final TransformationController transformationController; + final double scale; + final VoidCallback onReset; + final VoidCallback onZoomIn; + final VoidCallback onZoomOut; + + const CanvasControls({ + super.key, + required this.transformationController, + required this.scale, + required this.onReset, + required this.onZoomIn, + required this.onZoomOut, + }); + + @override + Widget build(BuildContext context) { + return Card( + color: Colors.black.withOpacity(0.7), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.home, color: Colors.white), + onPressed: onReset, + tooltip: 'Reset View', + ), + IconButton( + icon: const Icon(Icons.remove, color: Colors.white), + onPressed: onZoomOut, + tooltip: 'Zoom Out', + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: Colors.blue.withOpacity(0.2), + borderRadius: BorderRadius.circular(4), + ), + child: Text( + '${(scale * 100).toInt()}%', + style: const TextStyle(color: Colors.white), + ), + ), + IconButton( + icon: const Icon(Icons.add, color: Colors.white), + onPressed: onZoomIn, + tooltip: 'Zoom In', + ), + ], + ), + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_drop_target.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_drop_target.dart new file mode 100644 index 000000000..13ceece96 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_drop_target.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +class CanvasDropTarget extends StatelessWidget { + final Widget child; + final Function(dynamic, Offset) onAccept; + final TransformationController transformationController; + + const CanvasDropTarget({ + super.key, + required this.child, + required this.onAccept, + required this.transformationController, + }); + + @override + Widget build(BuildContext context) { + return DragTarget( + builder: (context, candidateData, rejectedData) { + return child; + }, + onWillAccept: (_) => true, + onAcceptWithDetails: (details) { + final RenderBox box = context.findRenderObject() as RenderBox; + final localPosition = box.globalToLocal(details.offset); + final position = transformationController.toScene(localPosition); + onAccept(details.data, position); + }, + ); + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart new file mode 100644 index 000000000..796831afd --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart @@ -0,0 +1,181 @@ +import 'package:api_testing_suite/src/workflow_builder/workflow_providers.dart'; +import '../../models/workflow_connection_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../models/workflow_node_model.dart'; + + +mixin CanvasEventHandlers on ConsumerState { + TransformationController get transformationController; + + // Declare mutable properties with setters and getters + String? _draggedNodeId; + Offset? _dragOffsetFromNodeTopLeft; + String? _sourceNodeId; + Offset? _connectionStart; + Offset? _connectionEnd; + + String? get draggedNodeId => _draggedNodeId; + set draggedNodeId(String? value) => setState(() => _draggedNodeId = value); + + Offset? get dragOffsetFromNodeTopLeft => _dragOffsetFromNodeTopLeft; + set dragOffsetFromNodeTopLeft(Offset? value) => setState(() => _dragOffsetFromNodeTopLeft = value); + + String? get sourceNodeId => _sourceNodeId; + set sourceNodeId(String? value) => setState(() => _sourceNodeId = value); + + Offset? get connectionStart => _connectionStart; + set connectionStart(Offset? value) => setState(() => _connectionStart = value); + + Offset? get connectionEnd => _connectionEnd; + set connectionEnd(Offset? value) => setState(() => _connectionEnd = value); + + void handleApiNodeAdded(WidgetRef ref, String workflowId, String requestId, Offset position) { + debugPrint('[CanvasEventHandlers] handleApiNodeAdded called with workflowId: $workflowId, requestId: $requestId, position: $position'); + try { + final renderBox = context.findRenderObject() as RenderBox; + final viewportCenter = transformationController.toScene( + Offset( + renderBox.size.width / 2, + renderBox.size.height / 2, + ), + ); + + final finalPosition = position.dx == 0 && position.dy == 0 + ? Offset(viewportCenter.dx - 100, viewportCenter.dy - 40) + : position; + + final nodeModel = WorkflowNodeModel.create( + requestId: requestId, + position: finalPosition, + label: 'API Request ${DateTime.now().millisecondsSinceEpoch % 1000}', + ); + + // Add the node to workflow state + ref.read(workflowsNotifierProvider.notifier).addNode(workflowId, nodeModel); + + // Debug print to confirm node was added + debugPrint('Added node with ID: ${nodeModel.id} at position: ${nodeModel.position}'); + + // Check if node was actually added to the workflow + final workflows = ref.read(workflowsNotifierProvider); + final workflow = workflows.firstWhere((w) => w.id == workflowId); + debugPrint('Current nodes in workflow: ${workflow.nodes.length}'); + for (final node in workflow.nodes) { + debugPrint('Node ID: ${node.id}, Position: ${node.position}, Label: ${node.label}'); + } + } catch (e) { + debugPrint('Error adding API node: $e'); + } + } + + void handleNodeDrag(WidgetRef ref, String workflowId, String nodeId, Offset position) { + // This is critical for smooth dragging - update state directly without debouncing + // Update the UI immediately + ref.read(workflowsNotifierProvider.notifier).updateNodePosition(workflowId, nodeId, position); + } + + void handleNodeDragStart(WidgetRef ref, String workflowId, String nodeId, Offset globalPosition) { + final RenderBox box = context.findRenderObject() as RenderBox; + + // Get current node position from the workflow + final workflows = ref.read(workflowsNotifierProvider); + final workflow = workflows.firstWhere((w) => w.id == workflowId); + final node = workflow.nodes.firstWhere((n) => n.id == nodeId); + + // Calculate node top-left in global coordinates + final nodeTopLeft = box.localToGlobal(node.position); + + // Set drag info + draggedNodeId = nodeId; + dragOffsetFromNodeTopLeft = globalPosition - nodeTopLeft; + + // Let the notifier know we're starting to drag + ref.read(workflowsNotifierProvider.notifier).startNodeDrag(workflowId, nodeId, node.position); + } + + void handleNodeDragUpdate(WidgetRef ref, String workflowId, Offset globalPosition) { + if (draggedNodeId != null && dragOffsetFromNodeTopLeft != null) { + final RenderBox box = context.findRenderObject() as RenderBox; + + // Calculate the new local position, adjusting by the drag offset + final localPosition = box.globalToLocal(globalPosition - dragOffsetFromNodeTopLeft!); + + // Update the node position using the real-time position + handleNodeDrag(ref, workflowId, draggedNodeId!, localPosition); + } + } + + void handleNodeDragEnd(WidgetRef ref, String workflowId) { + if (draggedNodeId != null) { + // Notify that dragging is finished + ref.read(workflowsNotifierProvider.notifier).finishNodeDrag(workflowId, draggedNodeId!); + + // Clear drag state + draggedNodeId = null; + dragOffsetFromNodeTopLeft = null; + } + } + + void handleNodeTap(WidgetRef ref, String workflowId, String nodeId, Offset position) { + // Check if connection mode is active + final isConnectionModeActive = ref.read(connectionModeProvider); + + if (isConnectionModeActive) { + // If we don't have a source node yet, set this as the source + if (sourceNodeId == null) { + debugPrint('Setting source node: $nodeId'); + sourceNodeId = nodeId; + connectionStart = position; + + // Force a rebuild to show the connection state + setState(() {}); + } + // If we already have a source and it's not the same node, create connection + else if (sourceNodeId != nodeId) { + debugPrint('Creating connection from ${sourceNodeId!} to $nodeId'); + handleConnectionCreated(ref, workflowId, sourceNodeId!, nodeId); + + // Reset connection state + sourceNodeId = null; + connectionStart = null; + connectionEnd = null; + } + } else { + // Not in connection mode - just select the node + ref.read(selectedNodeIdProvider.notifier).state = nodeId; + } + } + + void handleConnectionCreated(WidgetRef ref, String workflowId, String sourceNodeId, String targetNodeId) { + try { + // Find source node position for connection visual + final workflows = ref.read(workflowsNotifierProvider); + final workflow = workflows.firstWhere((w) => w.id == workflowId); + final sourceNode = workflow.nodes.firstWhere( + (n) => n.id == sourceNodeId, + orElse: () => throw Exception('Source node not found') + ); + + final connection = WorkflowConnectionModel.create( + sourceId: sourceNodeId, + targetId: targetNodeId, + workflowId: workflowId, + position: sourceNode.position, + ); + + ref.read(workflowsNotifierProvider.notifier).createConnection(workflowId, sourceNodeId, targetNodeId); + debugPrint('Added connection from $sourceNodeId to $targetNodeId'); + + // Notify the user visually that the connection was created + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Connected nodes'), + duration: const Duration(seconds: 2), + ), + ); + } catch (e) { + debugPrint('Error creating connection: $e'); + } + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_info_card.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_info_card.dart new file mode 100644 index 000000000..d2418da44 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_info_card.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import '../../models/workflow_model.dart'; + +class CanvasInfoCard extends StatelessWidget { + final WorkflowModel workflow; + const CanvasInfoCard({super.key, required this.workflow}); + + @override + Widget build(BuildContext context) { + return Card( + color: Colors.black.withOpacity(0.7), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + workflow.name, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 4), + Text( + '${workflow.nodes.length} nodes · ${workflow.connections.length} connections', + style: TextStyle( + color: Colors.grey[400], + fontSize: 12, + ), + ), + ], + ), + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart new file mode 100644 index 000000000..a0f8e726a --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart @@ -0,0 +1,257 @@ +import 'package:api_testing_suite/api_testing_suite.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class CanvasNodeLayer extends ConsumerWidget { + final String workflowId; + final Function(String, Offset) onNodeDragStart; + final Function(Offset) onNodeDragUpdate; + final VoidCallback onNodeDragEnd; + final Function(String, Offset) onStartConnection; + final Function(String, Offset) onNodeSelected; + final String? draggedNodeId; + final String? sourceNodeId; + + const CanvasNodeLayer({ + super.key, + required this.workflowId, + required this.onNodeDragStart, + required this.onNodeDragUpdate, + required this.onNodeDragEnd, + required this.onStartConnection, + required this.onNodeSelected, + this.draggedNodeId, + this.sourceNodeId, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + // Get workflow directly from the notifier provider + final workflows = ref.watch(workflowsNotifierProvider); + final workflow = workflows.firstWhere( + (w) => w.id == workflowId, + orElse: () => throw Exception('Workflow not found: $workflowId'), + ); + + // Check if connection mode is active for visual cues + final isConnectionMode = ref.watch(connectionModeProvider); + + debugPrint('[CanvasNodeLayer] Rendering nodes. Count: ${workflow.nodes.length}'); + for (final node in workflow.nodes) { + debugPrint('[CanvasNodeLayer] Node ID: ${node.id}, Position: ${node.position}, Label: ${node.label}'); + } + + return Stack( + children: workflow.nodes.map((node) { + // Calculate the center position of the node for better connection point + final nodeCenter = Offset( + node.position.dx + 100, // Half of width (200) + node.position.dy + 40, // Approximate half of height + ); + + return Positioned( + left: node.position.dx, + top: node.position.dy, + child: GestureDetector( + onTap: () { + // Pass both node ID and position to make connections easier + onNodeSelected(node.id, nodeCenter); + }, + onPanStart: (details) { + // Only start drag if we're not in connection mode + if (!isConnectionMode) { + onNodeDragStart(node.id, details.globalPosition); + } + }, + onPanUpdate: (details) { + // Only update drag if we're not in connection mode + if (!isConnectionMode) { + onNodeDragUpdate(details.globalPosition); + } + }, + onPanEnd: (_) { + // Only end drag if we're not in connection mode + if (!isConnectionMode) { + onNodeDragEnd(); + } + }, + onLongPress: () { + // In connection mode, long press also works to select a node + if (isConnectionMode) { + onStartConnection(node.id, nodeCenter); + } + }, + child: Opacity( + opacity: draggedNodeId == node.id ? 0.7 : 1.0, + child: Material( + elevation: draggedNodeId == node.id ? 8 : 3, + borderRadius: BorderRadius.circular(8), + color: Colors.transparent, + child: Container( + width: 200, + constraints: const BoxConstraints(minHeight: 80), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: sourceNodeId == node.id + ? [Colors.blue[300]!, Colors.blue[100]!] + : (isConnectionMode + ? [Colors.purple[100]!, Colors.purple[50]!] + : [Colors.white, const Color(0xFFF5F5F5)]), + ), + border: Border.all( + color: _getBorderColor(node.id, sourceNodeId, isConnectionMode), + width: isConnectionMode ? 2.5 : 2, + ), + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + spreadRadius: 1, + blurRadius: 3, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: node.nodeType == NodeType.request + ? Colors.blue.shade700 + : Colors.green.shade700, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(6), + topRight: Radius.circular(6), + ), + ), + child: Row( + children: [ + Icon( + node.nodeType == NodeType.request + ? Icons.http + : Icons.settings, + color: Colors.white, + size: 16, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + node.label, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + overflow: TextOverflow.ellipsis, + ), + ), + if (isConnectionMode) + const Icon( + Icons.cable_outlined, + color: Colors.white, + size: 16, + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (node.nodeType == NodeType.request && node.requestModel != null) + Row( + children: [ + _buildMethodBadge(node.requestModel!.method), + const SizedBox(width: 8), + Expanded( + child: Text( + node.requestModel!.url.isEmpty + ? 'No URL specified' + : node.requestModel!.url, + style: TextStyle( + fontSize: 12, + color: Colors.grey[700], + ), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ) + else + Text( + 'Node ID: ${node.id.substring(0, 6)}', + style: TextStyle( + fontSize: 12, + color: Colors.grey[700], + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ); + }).toList(), + ); + } + + Color _getBorderColor(String nodeId, String? sourceNodeId, bool isConnectionMode) { + if (sourceNodeId == nodeId) { + return Colors.blue; + } + + if (isConnectionMode) { + return Colors.purple; + } + + return Colors.grey.shade300; + } + + Widget _buildMethodBadge(String method) { + Color color; + switch (method.toUpperCase()) { + case 'GET': + color = Colors.blue; + break; + case 'POST': + color = Colors.green; + break; + case 'PUT': + color = Colors.orange; + break; + case 'DELETE': + color = Colors.red; + break; + case 'PATCH': + color = Colors.purple; + break; + default: + color = Colors.grey; + } + + return Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(4), + ), + child: Text( + method.toUpperCase(), + style: const TextStyle( + color: Colors.white, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_view.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_view.dart new file mode 100644 index 000000000..e7f01c1ad --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_view.dart @@ -0,0 +1,183 @@ +import 'package:api_testing_suite/api_testing_suite.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class CanvasView extends ConsumerStatefulWidget { + final String workflowId; + const CanvasView({super.key, required this.workflowId}); + + @override + ConsumerState createState() => _CanvasViewState(); +} + +class _CanvasViewState extends ConsumerState + with CanvasEventHandlers { + void handleNodeDrop(WidgetRef ref, String workflowId, Object data, Offset position) { + // Handle the drop from the left panel + if (data is String) { + final requestId = data; + handleApiNodeAdded(ref, workflowId, requestId, position); + } + } + + final TransformationController _transformationController = TransformationController(); + double _scale = 1.0; + + @override + TransformationController get transformationController => _transformationController; + + Offset? mouseMovePosition; + + @override + Widget build(BuildContext context) { + // Get workflow directly from the notifier provider + final workflows = ref.watch(workflowsNotifierProvider); + final workflow = workflows.firstWhere( + (w) => w.id == widget.workflowId, + orElse: () => throw Exception('Workflow not found: ${widget.workflowId}'), + ); + + // Get connection mode state + final isConnectionMode = ref.watch(connectionModeProvider); + + debugPrint('[CanvasView] Workflow nodes count: ${workflow.nodes.length}'); + for (final node in workflow.nodes) { + debugPrint('[CanvasView] Node ID: ${node.id}, Position: ${node.position}, Label: ${node.label}'); + } + + // Get running/completed nodes + final runningNodeIds = workflow.nodes + .where((node) => node.status == NodeStatus.running) + .map((node) => node.id) + .toList(); + + final completedNodeIds = workflow.nodes + .where((node) => node.status == NodeStatus.success) + .map((node) => node.id) + .toList(); + + final nodePositions = Map.fromEntries( + workflow.nodes.map((node) => MapEntry(node.id, node.position)), + ); + + return Stack( + children: [ + // Connection mode indicator + if (isConnectionMode) + Positioned( + top: 16, + left: 16, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + decoration: BoxDecoration( + color: Colors.purple.withOpacity(0.8), + borderRadius: BorderRadius.circular(20), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.cable, color: Colors.white, size: 16), + const SizedBox(width: 8), + Text( + sourceNodeId != null + ? 'Now click another node to connect' + : 'Click on a node to start connecting', + style: const TextStyle(color: Colors.white), + ), + ], + ), + ), + ), + MouseRegion( + onHover: (event) { + if (sourceNodeId != null && connectionStart != null) { + // Convert to local coordinates in the canvas + final RenderBox box = context.findRenderObject() as RenderBox; + final localPosition = box.globalToLocal(event.position); + + setState(() { + connectionEnd = localPosition; + }); + } + }, + child: InteractiveViewer( + transformationController: _transformationController, + minScale: 0.2, + maxScale: 2.0, + onInteractionUpdate: (details) { + setState(() { + _scale = _transformationController.value.getMaxScaleOnAxis(); + }); + }, + child: CanvasDropTarget( + transformationController: _transformationController, + onAccept: (data, position) { + handleNodeDrop(ref, widget.workflowId, data, position); + }, + child: Stack( + children: [ + CanvasConnectionLayer( + workflow: workflow, + nodePositions: nodePositions, + sourceNodeId: sourceNodeId, + connectionStart: connectionStart, + connectionEnd: connectionEnd, + runningNodeIds: runningNodeIds, + completedNodeIds: completedNodeIds, + ), + CanvasNodeLayer( + workflowId: widget.workflowId, + onNodeDragStart: (nodeId, globalPosition) { + handleNodeDragStart(ref, widget.workflowId, nodeId, globalPosition); + }, + onNodeDragUpdate: (globalPosition) { + handleNodeDragUpdate(ref, widget.workflowId, globalPosition); + }, + onNodeDragEnd: () { + handleNodeDragEnd(ref, widget.workflowId); + }, + onStartConnection: (nodeId, position) { + // Simplified - just use the node tap handler which now handles everything + handleNodeTap(ref, widget.workflowId, nodeId, position); + }, + onNodeSelected: (nodeId, position) { + // Use our simplified node tap handler with position + handleNodeTap(ref, widget.workflowId, nodeId, position); + }, + draggedNodeId: draggedNodeId, + sourceNodeId: sourceNodeId, + ), + ], + ), + ), + ), + ), + Positioned( + right: 16, + top: 16, + child: CanvasControls( + transformationController: _transformationController, + scale: _scale, + onReset: () { + _transformationController.value = Matrix4.identity(); + setState(() => _scale = 1.0); + }, + onZoomIn: () { + _transformationController.value.scale(1.2); + setState(() => _scale = _transformationController.value.getMaxScaleOnAxis()); + }, + onZoomOut: () { + _transformationController.value.scale(0.8); + setState(() => _scale = _transformationController.value.getMaxScaleOnAxis()); + }, + ), + ), + Positioned( + left: 16, + top: isConnectionMode ? 70 : 16, // Position below connection mode indicator + child: CanvasInfoCard(workflow: workflow), + ), + ], + ); + } +} From 654997ce4a841228bf70d9e3c0d5db42021bdf08 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Fri, 18 Apr 2025 15:26:22 +0530 Subject: [PATCH 154/188] refactor: edit components for the new canvas files --- .../src/workflow_builder/components/components.dart | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/components.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/components.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/components.dart new file mode 100644 index 000000000..ae14d4a01 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/components.dart @@ -0,0 +1,12 @@ + +export 'canvas/canvas_view.dart'; +export 'canvas/canvas_node_layer.dart'; +export 'canvas/canvas_connection_layer.dart'; +export 'canvas/canvas_controls.dart'; +export 'canvas/canvas_drop_target.dart'; +export 'canvas/canvas_event_handlers.dart'; +export 'canvas/canvas_info_card.dart'; +export 'node_details_panel.dart'; +export 'sidebar_panel.dart'; +export 'toolbar_panel.dart'; +export 'workflow_builder_screen.dart'; From 8944c0d430974fdeb83f8212ce4f44530c159c10 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Fri, 18 Apr 2025 16:03:35 +0530 Subject: [PATCH 155/188] feat: implement WorkflowExecutor with execution commands --- .../execution/workflow_executor.dart | 292 ++++++++++++++++++ .../models/execution_command.dart | 8 + .../src/workflow_builder/widgets/widgets.dart | 2 +- 3 files changed, 301 insertions(+), 1 deletion(-) create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_executor.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/models/execution_command.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_executor.dart b/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_executor.dart new file mode 100644 index 000000000..76fee23d4 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_executor.dart @@ -0,0 +1,292 @@ +import 'dart:async'; + +import '../models/models.dart'; + +/// A more optimized execution engine that follows single responsibility principle +class WorkflowExecutor { + final WorkflowModel workflow; + final Function(String, NodeStatus) onNodeStatusChanged; + final Function(WorkflowExecutionState) onExecutionStateChanged; + + WorkflowExecutionState _executionState; + + // Dependency and outgoing node maps for efficient traversal + final Map> _nodeDependencyMap = {}; + final Map> _nodeOutgoingMap = {}; + + // Stream control for execution commands + final _executionController = StreamController.broadcast(); + StreamSubscription? _executionSubscription; + Timer? _executionTimer; + + WorkflowExecutor({ + required this.workflow, + required this.onNodeStatusChanged, + required this.onExecutionStateChanged, + }) : _executionState = const WorkflowExecutionState() { + _initializeDependencyMaps(); + _setupExecutionListener(); + } + + void _initializeDependencyMaps() { + // Clear existing maps + _nodeDependencyMap.clear(); + _nodeOutgoingMap.clear(); + + // Initialize empty lists for each node + for (final node in workflow.nodes) { + _nodeDependencyMap[node.id] = []; + _nodeOutgoingMap[node.id] = []; + } + + // Populate the dependency maps based on connections + for (final connection in workflow.connections) { + final sourceId = connection.sourceId; + final targetId = connection.targetId; + + // Add the target as dependent on source + if (_nodeDependencyMap.containsKey(targetId)) { + _nodeDependencyMap[targetId]!.add(sourceId); + } + + // Add target as outgoing from source + if (_nodeOutgoingMap.containsKey(sourceId)) { + _nodeOutgoingMap[sourceId]!.add(targetId); + } + } + } + + /// Start the workflow execution + void start() { + if (_executionState.isRunning) return; + + if (_executionState.isCompleted || _executionState.hasError) { + _resetExecutionState(); + } + + final initialNodes = _findInitialNodes(); + if (initialNodes.isEmpty) { + _updateExecutionState( + _executionState.copyWith( + status: WorkflowExecutionStatus.error, + errorMessage: 'No starting nodes found in the workflow', + ), + ); + return; + } + + _updateExecutionState( + _executionState.copyWith( + status: WorkflowExecutionStatus.running, + startTime: DateTime.now(), + pendingNodeIds: initialNodes.map((node) => node.id).toList(), + ), + ); + + _executeNextNodes(); + } + + void pause() { + if (!_executionState.isRunning) return; + + _updateExecutionState( + _executionState.copyWith( + status: WorkflowExecutionStatus.paused, + ), + ); + + _cancelExecutionTimer(); + } + + void resume() { + if (!_executionState.isPaused) return; + + _updateExecutionState( + _executionState.copyWith( + status: WorkflowExecutionStatus.running, + ), + ); + + _executeNextNodes(); + } + + void stop() { + _cancelExecutionTimer(); + + _updateExecutionState( + _executionState.copyWith( + status: WorkflowExecutionStatus.idle, + currentNodeId: null, + ), + ); + } + + void reset() { + _resetExecutionState(); + } + + void _resetExecutionState() { + _updateExecutionState(const WorkflowExecutionState()); + } + + List _findInitialNodes() { + return workflow.nodes.where((node) { + return _nodeDependencyMap[node.id]?.isEmpty ?? true; + }).toList(); + } + + void _setupExecutionListener() { + _cancelExecutionSubscription(); + + _executionSubscription = _executionController.stream.listen((command) { + switch (command) { + case ExecutionCommand.start: + start(); + break; + case ExecutionCommand.pause: + pause(); + break; + case ExecutionCommand.resume: + resume(); + break; + case ExecutionCommand.stop: + stop(); + break; + case ExecutionCommand.reset: + reset(); + break; + } + }); + } + + void _executeNextNodes() { + if (!_executionState.isRunning) return; + + final pendingNodeIds = List.from(_executionState.pendingNodeIds); + if (pendingNodeIds.isEmpty) { + _completeExecution(); + return; + } + + final nodeId = pendingNodeIds.first; + pendingNodeIds.removeAt(0); + + final node = workflow.nodes.firstWhere( + (n) => n.id == nodeId, + orElse: () => throw Exception('Node not found: $nodeId'), + ); + + final dependencies = _nodeDependencyMap[nodeId] ?? []; + final allDependenciesMet = dependencies.every( + (depId) => _executionState.executedNodeIds.contains(depId), + ); + + if (!allDependenciesMet) { + pendingNodeIds.add(nodeId); + + _updateExecutionState( + _executionState.copyWith( + pendingNodeIds: pendingNodeIds, + ), + ); + + _scheduleNextExecution(); + return; + } + + _updateExecutionState( + _executionState.copyWith( + currentNodeId: nodeId, + pendingNodeIds: pendingNodeIds, + ), + ); + + onNodeStatusChanged(nodeId, NodeStatus.running); + + _executionTimer = Timer(const Duration(milliseconds: 500), () { + final success = _executeNode(node); + final newStatus = success ? NodeStatus.success : NodeStatus.failure; + + onNodeStatusChanged(nodeId, newStatus); + + final updatedResults = Map.from(_executionState.executionResults); + updatedResults[nodeId] = { + 'status': success ? 'success' : 'failure', + 'timestamp': DateTime.now().toIso8601String(), + 'data': {'message': 'Executed ${node.label}'}, + }; + + final nextNodeIds = _nodeOutgoingMap[nodeId] ?? []; + final updatedPendingIds = List.from(pendingNodeIds); + + for (final nextNodeId in nextNodeIds) { + if (!updatedPendingIds.contains(nextNodeId) && + !_executionState.executedNodeIds.contains(nextNodeId)) { + updatedPendingIds.add(nextNodeId); + } + } + + _updateExecutionState( + _executionState.copyWith( + currentNodeId: null, + executedNodeIds: [..._executionState.executedNodeIds, nodeId], + pendingNodeIds: updatedPendingIds, + executionResults: updatedResults, + ), + ); + + if (updatedPendingIds.isNotEmpty) { + _scheduleNextExecution(); + } else { + _completeExecution(); + } + }); + } + + bool _executeNode(WorkflowNodeModel node) { + switch (node.nodeType) { + case NodeType.request: + return true; + case NodeType.condition: + return true; + case NodeType.action: + return true; + default: + return true; + } + } + + void _scheduleNextExecution() { + _executionTimer = Timer(const Duration(milliseconds: 100), _executeNextNodes); + } + + void _completeExecution() { + _updateExecutionState( + _executionState.copyWith( + status: WorkflowExecutionStatus.completed, + endTime: DateTime.now(), + ), + ); + } + + void _updateExecutionState(WorkflowExecutionState newState) { + _executionState = newState; + onExecutionStateChanged(newState); + } + + void _cancelExecutionTimer() { + _executionTimer?.cancel(); + _executionTimer = null; + } + + void _cancelExecutionSubscription() { + _executionSubscription?.cancel(); + _executionSubscription = null; + } + + void dispose() { + _cancelExecutionSubscription(); + _cancelExecutionTimer(); + _executionController.close(); + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/execution_command.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/execution_command.dart new file mode 100644 index 000000000..8a6a6480d --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/execution_command.dart @@ -0,0 +1,8 @@ +/// Commands for controlling workflow execution +enum ExecutionCommand { + start, + pause, + resume, + stop, + reset, +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/widgets/widgets.dart b/packages/api_testing_suite/lib/src/workflow_builder/widgets/widgets.dart index 6e64d61f2..acc58494c 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/widgets/widgets.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/widgets/widgets.dart @@ -1,3 +1,3 @@ /// Export all widget export 'logs_viewer.dart'; -export 'node_details_panel.dart'; +// export 'node_details_panel.dart'; From 6ba3d17d3549396117cd02e6aa4e070df66f44c4 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Fri, 18 Apr 2025 23:56:09 +0530 Subject: [PATCH 156/188] refactor: move workflow providers to workflow_builder\providers --- .../providers/workflow_providers.dart | 297 +++++++++--------- .../workflow_builder/workflow_providers.dart | 174 ---------- 2 files changed, 155 insertions(+), 316 deletions(-) delete mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflow_providers.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/providers/workflow_providers.dart b/packages/api_testing_suite/lib/src/workflow_builder/providers/workflow_providers.dart index 86b7d5d32..537a4e7ea 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/providers/workflow_providers.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/providers/workflow_providers.dart @@ -1,175 +1,188 @@ +import 'package:api_testing_suite/api_testing_suite.dart'; +import 'package:api_testing_suite/src/workflow_builder/models/workflow_log_entry.dart' as workflow_log; +import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../models/models.dart'; import 'workflows_notifier.dart'; -final workflowsProvider = StateNotifierProvider>((ref) { - return WorkflowsNotifier(ref); +final workflowsNotifierProvider = + StateNotifierProvider>((ref) { + return WorkflowsNotifier(); }); final currentWorkflowProvider = StateProvider((ref) => null); +final selectedNodeIdProvider = StateProvider((ref) => null); + final activeWorkflowProvider = Provider((ref) { final currentWorkflowId = ref.watch(currentWorkflowProvider); - final workflows = ref.watch(workflowsProvider); - + final workflows = ref.watch(workflowsNotifierProvider); + if (currentWorkflowId == null) return null; - + return workflows.firstWhere( (workflow) => workflow.id == currentWorkflowId, - orElse: () => WorkflowModel.create(name: 'Workflow $currentWorkflowId'), + orElse: () => throw Exception('Workflow not found'), ); }); -final workflowExecutionStateProvider = StateNotifierProvider((ref) { - return WorkflowExecutionNotifier(ref); +final workflowExecutionStateProvider = + StateProvider((ref) { + return const WorkflowExecutionState(); }); -enum WorkflowExecutionStatus { - idle, - running, - paused, - completed, -} - -class WorkflowExecutionState { - final WorkflowExecutionStatus status; - final String? currentNodeId; - final List executedNodeIds; - final Map executionContext; - - WorkflowExecutionState({ - this.status = WorkflowExecutionStatus.idle, - this.currentNodeId, - this.executedNodeIds = const [], - this.executionContext = const {}, - }); +final connectionModeProvider = StateProvider((ref) => false); - WorkflowExecutionState copyWith({ - WorkflowExecutionStatus? status, - String? currentNodeId, - List? executedNodeIds, - Map? executionContext, - }) { - return WorkflowExecutionState( - status: status ?? this.status, - currentNodeId: currentNodeId ?? this.currentNodeId, - executedNodeIds: executedNodeIds ?? this.executedNodeIds, - executionContext: executionContext ?? this.executionContext, - ); - } -} - -class WorkflowExecutionNotifier extends StateNotifier { - final Ref _ref; +final workflowLogsProvider = Provider.family, String>((ref, workflowId) { + final workflows = ref.watch(workflowsNotifierProvider); + final workflowModel = workflows.firstWhere( + (workflow) => workflow.id == workflowId, + orElse: () => throw Exception('Workflow not found'), + ); - WorkflowExecutionNotifier(this._ref) : super(WorkflowExecutionState()); - - Future start() async { - if (state.status == WorkflowExecutionStatus.running) return; - - final workflow = _ref.read(activeWorkflowProvider); - if (workflow == null || workflow.nodes.isEmpty) return; - - final firstNode = workflow.nodes.first; - - state = WorkflowExecutionState( - status: WorkflowExecutionStatus.running, - currentNodeId: firstNode.id, - executedNodeIds: [], - executionContext: {}, + final executionState = ref.watch(workflowExecutionStateProvider); + + final logs = []; + + logs.add( + workflow_log.WorkflowLogEntry( + message: 'Workflow initialized', + level: LogLevel.info, + timestamp: workflowModel.createdAt, + ), + ); + + if (executionState.startTime != null) { + logs.add( + workflow_log.WorkflowLogEntry( + message: 'Execution started', + level: LogLevel.info, + timestamp: executionState.startTime, + ), ); - - await _executeNode(firstNode.id); - } - - Future pause() async { - if (state.status != WorkflowExecutionStatus.running) return; - - state = state.copyWith(status: WorkflowExecutionStatus.paused); - } - - Future resume() async { - if (state.status != WorkflowExecutionStatus.paused) return; - - state = state.copyWith(status: WorkflowExecutionStatus.running); - if (state.currentNodeId != null) { - await _executeNode(state.currentNodeId!); + for (final nodeId in executionState.executedNodeIds) { + final node = workflowModel.nodes.firstWhere( + (n) => n.id == nodeId, + orElse: () => throw Exception('Node not found'), + ); + + final result = executionState.executionResults[nodeId]; + final success = result != null && result['status'] == 'success'; + + logs.add( + workflow_log.WorkflowLogEntry( + message: success + ? 'Node execution completed successfully' + : 'Node execution failed', + level: success ? LogLevel.success : LogLevel.error, + nodeId: nodeId, + details: node.nodeType == NodeType.request + ? 'URL: ${node.requestModel?.url ?? "No URL"}\nMethod: ${node.requestModel?.method ?? "GET"}' + : null, + ), + ); } - } - - Future stop() async { - state = WorkflowExecutionState(); - final workflow = _ref.read(activeWorkflowProvider); - if (workflow != null) { - _ref.read(workflowsProvider.notifier).updateNodes( - workflow.id, - workflow.nodes.map((node) => node.copyWith(status: NodeStatus.inactive)).toList(), + if (executionState.currentNodeId != null) { + logs.add( + workflow_log.WorkflowLogEntry( + message: 'Node execution in progress', + level: LogLevel.info, + nodeId: executionState.currentNodeId, + ), ); } - } - - Future _executeNode(String nodeId) async { - if (state.status != WorkflowExecutionStatus.running) return; - - final workflow = _ref.read(activeWorkflowProvider); - if (workflow == null) return; - final nodeIndex = workflow.nodes.indexWhere((node) => node.id == nodeId); - if (nodeIndex == -1) return; - - final node = workflow.nodes[nodeIndex]; - - _ref.read(workflowsProvider.notifier).updateNode( - workflow.id, - node.copyWith(status: NodeStatus.running), - ); - - await Future.delayed(const Duration(seconds: 1)); - - final isSuccess = DateTime.now().millisecondsSinceEpoch % 5 != 0; - - _ref.read(workflowsProvider.notifier).updateNode( - workflow.id, - node.copyWith(status: isSuccess ? NodeStatus.success : NodeStatus.failure), - ); - - final updatedExecutedNodeIds = [...state.executedNodeIds, nodeId]; - state = state.copyWith( - executedNodeIds: updatedExecutedNodeIds, - ); - - if (!isSuccess && node.connectedToIds.isNotEmpty) { - final connections = workflow.connections.where( - (conn) => conn.sourceId == nodeId && conn.isConditional - ).toList(); - - if (connections.isNotEmpty) { - state = state.copyWith(status: WorkflowExecutionStatus.completed); - return; - } + if (executionState.isCompleted && executionState.endTime != null) { + logs.add( + workflow_log.WorkflowLogEntry( + message: 'Workflow execution completed', + level: LogLevel.success, + timestamp: executionState.endTime, + ), + ); } - final nextNodeIds = node.connectedToIds.isEmpty - ? [] - : workflow.connections - .where((conn) => conn.sourceId == nodeId) - .map((conn) => conn.targetId) - .toList(); - - if (nextNodeIds.isEmpty) { - state = state.copyWith(status: WorkflowExecutionStatus.completed); - return; + if (executionState.isPaused) { + logs.add( + workflow_log.WorkflowLogEntry( + message: 'Workflow execution paused', + level: LogLevel.warning, + ), + ); } - for (final nextNodeId in nextNodeIds) { - state = state.copyWith(currentNodeId: nextNodeId); - await _executeNode(nextNodeId); - - if (state.status != WorkflowExecutionStatus.running) { - break; - } + if (executionState.hasError) { + logs.add( + workflow_log.WorkflowLogEntry( + message: 'Workflow execution failed: ${executionState.errorMessage}', + level: LogLevel.error, + ), + ); } } + + return logs; +}); + +final workflowExecutionControlProvider = + Provider((ref) { + final notifier = ref.watch(workflowsNotifierProvider.notifier); + final currentId = ref.watch(currentWorkflowProvider); + + return WorkflowExecutionControl( + startExecution: () { + if (currentId != null) { + notifier.startExecution( + currentId, + (state) => ref.read(workflowExecutionStateProvider.notifier).state = + state); + } + }, + pauseExecution: () { + if (currentId != null) { + notifier.pauseExecution(currentId); + } + }, + resumeExecution: () { + if (currentId != null) { + notifier.resumeExecution(currentId); + } + }, + stopExecution: () { + if (currentId != null) { + notifier.stopExecution(currentId); + } + }, + ); +}); + +final workflowExecutionLogsProvider = StateNotifierProvider>((ref) { + return WorkflowExecutionLogsNotifier(); +}); + +class WorkflowExecutionLogsNotifier extends StateNotifier> { + WorkflowExecutionLogsNotifier() : super([]); + + void clearLogs() { + state = []; + } + + void addLog(LogEntry entry) { + state = [...state, entry]; + } +} + +class WorkflowExecutionControl { + final VoidCallback startExecution; + final VoidCallback pauseExecution; + final VoidCallback resumeExecution; + final VoidCallback stopExecution; + + const WorkflowExecutionControl({ + required this.startExecution, + required this.pauseExecution, + required this.resumeExecution, + required this.stopExecution, + }); } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_providers.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_providers.dart deleted file mode 100644 index 85eb0226a..000000000 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_providers.dart +++ /dev/null @@ -1,174 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'models/models.dart'; -import 'widgets/logs_viewer.dart'; -import 'models/workflow_model.dart'; -import 'models/workflow_execution_state.dart'; -import 'workflows_notifier.dart'; - -final workflowsNotifierProvider = - StateNotifierProvider>((ref) { - return WorkflowsNotifier(); -}); - -final currentWorkflowProvider = StateProvider((ref) => null); - -final selectedNodeIdProvider = StateProvider((ref) => null); - -final activeWorkflowProvider = Provider((ref) { - final currentWorkflowId = ref.watch(currentWorkflowProvider); - final workflows = ref.watch(workflowsNotifierProvider); - - if (currentWorkflowId == null) return null; - - return workflows.firstWhere( - (workflow) => workflow.id == currentWorkflowId, - orElse: () => throw Exception('Workflow not found'), - ); -}); - -final workflowExecutionStateProvider = - StateProvider((ref) { - return const WorkflowExecutionState(); -}); - -final connectionModeProvider = StateProvider((ref) => false); - -final workflowLogsProvider = Provider.family, String>((ref, workflowId) { - final workflows = ref.watch(workflowsNotifierProvider); - final workflowModel = workflows.firstWhere( - (workflow) => workflow.id == workflowId, - orElse: () => throw Exception('Workflow not found'), - ); - - final executionState = ref.watch(workflowExecutionStateProvider); - - final logs = []; - - logs.add( - WorkflowLogEntry( - message: 'Workflow initialized', - level: LogLevel.info, - timestamp: workflowModel.createdAt, - ), - ); - - if (executionState.startTime != null) { - logs.add( - WorkflowLogEntry( - message: 'Execution started', - level: LogLevel.info, - timestamp: executionState.startTime, - ), - ); - - for (final nodeId in executionState.executedNodeIds) { - final node = workflowModel.nodes.firstWhere( - (n) => n.id == nodeId, - orElse: () => throw Exception('Node not found'), - ); - - final result = executionState.executionResults[nodeId]; - final success = result != null && result['status'] == 'success'; - - logs.add( - WorkflowLogEntry( - message: success - ? 'Node execution completed successfully' - : 'Node execution failed', - level: success ? LogLevel.success : LogLevel.error, - nodeId: nodeId, - details: node.nodeType == NodeType.request - ? 'URL: ${node.requestModel?.url ?? "No URL"}\nMethod: ${node.requestModel?.method ?? "GET"}' - : null, - ), - ); - } - - if (executionState.currentNodeId != null) { - logs.add( - WorkflowLogEntry( - message: 'Node execution in progress', - level: LogLevel.info, - nodeId: executionState.currentNodeId, - ), - ); - } - - if (executionState.isCompleted && executionState.endTime != null) { - logs.add( - WorkflowLogEntry( - message: 'Workflow execution completed', - level: LogLevel.success, - timestamp: executionState.endTime, - ), - ); - } - - if (executionState.isPaused) { - logs.add( - WorkflowLogEntry( - message: 'Workflow execution paused', - level: LogLevel.warning, - ), - ); - } - - if (executionState.hasError) { - logs.add( - WorkflowLogEntry( - message: 'Workflow execution failed: ${executionState.errorMessage}', - level: LogLevel.error, - ), - ); - } - } - - return logs; -}); - -final workflowExecutionControlProvider = - Provider((ref) { - final notifier = ref.watch(workflowsNotifierProvider.notifier); - final currentId = ref.watch(currentWorkflowProvider); - - return WorkflowExecutionControl( - startExecution: () { - if (currentId != null) { - notifier.startExecution( - currentId, - (state) => ref.read(workflowExecutionStateProvider.notifier).state = - state); - } - }, - pauseExecution: () { - if (currentId != null) { - notifier.pauseExecution(currentId); - } - }, - resumeExecution: () { - if (currentId != null) { - notifier.resumeExecution(currentId); - } - }, - stopExecution: () { - if (currentId != null) { - notifier.stopExecution(currentId); - } - }, - ); -}); - -class WorkflowExecutionControl { - final VoidCallback startExecution; - final VoidCallback pauseExecution; - final VoidCallback resumeExecution; - final VoidCallback stopExecution; - - const WorkflowExecutionControl({ - required this.startExecution, - required this.pauseExecution, - required this.resumeExecution, - required this.stopExecution, - }); -} From ba2147ac0f2eba0be084d951b0f67dd42a8e41bd Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 19 Apr 2025 00:23:19 +0530 Subject: [PATCH 157/188] refactor: workflow builder components --- .../canvas/canvas_event_handlers.dart | 25 +- .../components/canvas/canvas_node_layer.dart | 15 +- .../components/canvas/canvas_view.dart | 11 +- .../components/node_details_panel.dart | 455 ++++++++++++++++++ .../components/sidebar_panel.dart | 217 +++++++++ .../components/toolbar_panel.dart | 138 ++++++ .../components/workflow_builder_screen.dart | 303 ++++++++++++ 7 files changed, 1121 insertions(+), 43 deletions(-) create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/node_details_panel.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/sidebar_panel.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/toolbar_panel.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/workflow_builder_screen.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart index 796831afd..6cf487863 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart @@ -1,4 +1,4 @@ -import 'package:api_testing_suite/src/workflow_builder/workflow_providers.dart'; +import 'package:api_testing_suite/src/workflow_builder/providers/providers.dart'; import '../../models/workflow_connection_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -8,7 +8,6 @@ import '../../models/workflow_node_model.dart'; mixin CanvasEventHandlers on ConsumerState { TransformationController get transformationController; - // Declare mutable properties with setters and getters String? _draggedNodeId; Offset? _dragOffsetFromNodeTopLeft; String? _sourceNodeId; @@ -51,13 +50,10 @@ mixin CanvasEventHandlers on ConsumerState label: 'API Request ${DateTime.now().millisecondsSinceEpoch % 1000}', ); - // Add the node to workflow state ref.read(workflowsNotifierProvider.notifier).addNode(workflowId, nodeModel); - // Debug print to confirm node was added debugPrint('Added node with ID: ${nodeModel.id} at position: ${nodeModel.position}'); - // Check if node was actually added to the workflow final workflows = ref.read(workflowsNotifierProvider); final workflow = workflows.firstWhere((w) => w.id == workflowId); debugPrint('Current nodes in workflow: ${workflow.nodes.length}'); @@ -70,27 +66,22 @@ mixin CanvasEventHandlers on ConsumerState } void handleNodeDrag(WidgetRef ref, String workflowId, String nodeId, Offset position) { - // This is critical for smooth dragging - update state directly without debouncing - // Update the UI immediately + ref.read(workflowsNotifierProvider.notifier).updateNodePosition(workflowId, nodeId, position); } void handleNodeDragStart(WidgetRef ref, String workflowId, String nodeId, Offset globalPosition) { final RenderBox box = context.findRenderObject() as RenderBox; - // Get current node position from the workflow final workflows = ref.read(workflowsNotifierProvider); final workflow = workflows.firstWhere((w) => w.id == workflowId); final node = workflow.nodes.firstWhere((n) => n.id == nodeId); - // Calculate node top-left in global coordinates final nodeTopLeft = box.localToGlobal(node.position); - // Set drag info draggedNodeId = nodeId; dragOffsetFromNodeTopLeft = globalPosition - nodeTopLeft; - // Let the notifier know we're starting to drag ref.read(workflowsNotifierProvider.notifier).startNodeDrag(workflowId, nodeId, node.position); } @@ -98,58 +89,47 @@ mixin CanvasEventHandlers on ConsumerState if (draggedNodeId != null && dragOffsetFromNodeTopLeft != null) { final RenderBox box = context.findRenderObject() as RenderBox; - // Calculate the new local position, adjusting by the drag offset final localPosition = box.globalToLocal(globalPosition - dragOffsetFromNodeTopLeft!); - // Update the node position using the real-time position handleNodeDrag(ref, workflowId, draggedNodeId!, localPosition); } } void handleNodeDragEnd(WidgetRef ref, String workflowId) { if (draggedNodeId != null) { - // Notify that dragging is finished ref.read(workflowsNotifierProvider.notifier).finishNodeDrag(workflowId, draggedNodeId!); - // Clear drag state draggedNodeId = null; dragOffsetFromNodeTopLeft = null; } } void handleNodeTap(WidgetRef ref, String workflowId, String nodeId, Offset position) { - // Check if connection mode is active final isConnectionModeActive = ref.read(connectionModeProvider); if (isConnectionModeActive) { - // If we don't have a source node yet, set this as the source if (sourceNodeId == null) { debugPrint('Setting source node: $nodeId'); sourceNodeId = nodeId; connectionStart = position; - // Force a rebuild to show the connection state setState(() {}); } - // If we already have a source and it's not the same node, create connection else if (sourceNodeId != nodeId) { debugPrint('Creating connection from ${sourceNodeId!} to $nodeId'); handleConnectionCreated(ref, workflowId, sourceNodeId!, nodeId); - // Reset connection state sourceNodeId = null; connectionStart = null; connectionEnd = null; } } else { - // Not in connection mode - just select the node ref.read(selectedNodeIdProvider.notifier).state = nodeId; } } void handleConnectionCreated(WidgetRef ref, String workflowId, String sourceNodeId, String targetNodeId) { try { - // Find source node position for connection visual final workflows = ref.read(workflowsNotifierProvider); final workflow = workflows.firstWhere((w) => w.id == workflowId); final sourceNode = workflow.nodes.firstWhere( @@ -167,7 +147,6 @@ mixin CanvasEventHandlers on ConsumerState ref.read(workflowsNotifierProvider.notifier).createConnection(workflowId, sourceNodeId, targetNodeId); debugPrint('Added connection from $sourceNodeId to $targetNodeId'); - // Notify the user visually that the connection was created ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Connected nodes'), diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart index a0f8e726a..703400707 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart @@ -1,4 +1,5 @@ import 'package:api_testing_suite/api_testing_suite.dart'; +import 'package:api_testing_suite/src/workflow_builder/providers/providers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -26,14 +27,12 @@ class CanvasNodeLayer extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - // Get workflow directly from the notifier provider final workflows = ref.watch(workflowsNotifierProvider); final workflow = workflows.firstWhere( (w) => w.id == workflowId, orElse: () => throw Exception('Workflow not found: $workflowId'), ); - // Check if connection mode is active for visual cues final isConnectionMode = ref.watch(connectionModeProvider); debugPrint('[CanvasNodeLayer] Rendering nodes. Count: ${workflow.nodes.length}'); @@ -43,10 +42,9 @@ class CanvasNodeLayer extends ConsumerWidget { return Stack( children: workflow.nodes.map((node) { - // Calculate the center position of the node for better connection point final nodeCenter = Offset( - node.position.dx + 100, // Half of width (200) - node.position.dy + 40, // Approximate half of height + node.position.dx + 100, + node.position.dy + 40, ); return Positioned( @@ -54,29 +52,24 @@ class CanvasNodeLayer extends ConsumerWidget { top: node.position.dy, child: GestureDetector( onTap: () { - // Pass both node ID and position to make connections easier onNodeSelected(node.id, nodeCenter); }, onPanStart: (details) { - // Only start drag if we're not in connection mode if (!isConnectionMode) { onNodeDragStart(node.id, details.globalPosition); } }, onPanUpdate: (details) { - // Only update drag if we're not in connection mode if (!isConnectionMode) { onNodeDragUpdate(details.globalPosition); } }, onPanEnd: (_) { - // Only end drag if we're not in connection mode if (!isConnectionMode) { onNodeDragEnd(); } }, onLongPress: () { - // In connection mode, long press also works to select a node if (isConnectionMode) { onStartConnection(node.id, nodeCenter); } @@ -149,7 +142,7 @@ class CanvasNodeLayer extends ConsumerWidget { overflow: TextOverflow.ellipsis, ), ), - if (isConnectionMode) + if (isConnectionMode) const Icon( Icons.cable_outlined, color: Colors.white, diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_view.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_view.dart index e7f01c1ad..6eb7d01be 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_view.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_view.dart @@ -1,4 +1,5 @@ import 'package:api_testing_suite/api_testing_suite.dart'; +import 'package:api_testing_suite/src/workflow_builder/providers/providers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -13,7 +14,6 @@ class CanvasView extends ConsumerStatefulWidget { class _CanvasViewState extends ConsumerState with CanvasEventHandlers { void handleNodeDrop(WidgetRef ref, String workflowId, Object data, Offset position) { - // Handle the drop from the left panel if (data is String) { final requestId = data; handleApiNodeAdded(ref, workflowId, requestId, position); @@ -30,14 +30,12 @@ class _CanvasViewState extends ConsumerState @override Widget build(BuildContext context) { - // Get workflow directly from the notifier provider final workflows = ref.watch(workflowsNotifierProvider); final workflow = workflows.firstWhere( (w) => w.id == widget.workflowId, orElse: () => throw Exception('Workflow not found: ${widget.workflowId}'), ); - // Get connection mode state final isConnectionMode = ref.watch(connectionModeProvider); debugPrint('[CanvasView] Workflow nodes count: ${workflow.nodes.length}'); @@ -45,7 +43,6 @@ class _CanvasViewState extends ConsumerState debugPrint('[CanvasView] Node ID: ${node.id}, Position: ${node.position}, Label: ${node.label}'); } - // Get running/completed nodes final runningNodeIds = workflow.nodes .where((node) => node.status == NodeStatus.running) .map((node) => node.id) @@ -62,7 +59,6 @@ class _CanvasViewState extends ConsumerState return Stack( children: [ - // Connection mode indicator if (isConnectionMode) Positioned( top: 16, @@ -91,7 +87,6 @@ class _CanvasViewState extends ConsumerState MouseRegion( onHover: (event) { if (sourceNodeId != null && connectionStart != null) { - // Convert to local coordinates in the canvas final RenderBox box = context.findRenderObject() as RenderBox; final localPosition = box.globalToLocal(event.position); @@ -137,11 +132,9 @@ class _CanvasViewState extends ConsumerState handleNodeDragEnd(ref, widget.workflowId); }, onStartConnection: (nodeId, position) { - // Simplified - just use the node tap handler which now handles everything handleNodeTap(ref, widget.workflowId, nodeId, position); }, onNodeSelected: (nodeId, position) { - // Use our simplified node tap handler with position handleNodeTap(ref, widget.workflowId, nodeId, position); }, draggedNodeId: draggedNodeId, @@ -174,7 +167,7 @@ class _CanvasViewState extends ConsumerState ), Positioned( left: 16, - top: isConnectionMode ? 70 : 16, // Position below connection mode indicator + top: isConnectionMode ? 70 : 16, child: CanvasInfoCard(workflow: workflow), ), ], diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/node_details_panel.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/node_details_panel.dart new file mode 100644 index 000000000..fa8e418bd --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/node_details_panel.dart @@ -0,0 +1,455 @@ +import 'package:api_testing_suite/src/workflow_builder/providers/providers.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../models/workflow_node_model.dart'; +import '../models/node_type.dart'; + +/// Node execution settings +class NodeExecutionSettings { + final bool parallel; + final int retries; + final Duration timeout; + + const NodeExecutionSettings({ + this.parallel = false, + this.retries = 0, + this.timeout = const Duration(seconds: 30), + }); +} + +class NodeDetailsPanel extends ConsumerWidget { + final WorkflowNodeModel node; + final String workflowId; + + const NodeDetailsPanel({ + super.key, + required this.node, + required this.workflowId, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Container( + width: 300, + decoration: const BoxDecoration( + color: Color(0xFF212121), + border: Border( + left: BorderSide(color: Color(0xFF333333)), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildHeader(ref), + Expanded( + child: _buildNodeEditor(context, ref), + ), + ], + ), + ); + } + + Widget _buildHeader(WidgetRef ref) { + return Container( + height: 48, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: const BoxDecoration( + color: Color(0xFF272727), + border: Border(bottom: BorderSide(color: Color(0xFF333333))), + ), + child: Row( + children: [ + const Text( + 'Node Properties', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + const Spacer(), + IconButton( + icon: const Icon(Icons.close, color: Colors.white54, size: 20), + onPressed: () { + ref.read(selectedNodeIdProvider.notifier).state = null; + }, + tooltip: 'Close panel', + ), + ], + ), + ); + } + + Widget _buildNodeEditor(BuildContext context, WidgetRef ref) { + final notifier = ref.read(workflowsNotifierProvider.notifier); + + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildSectionTitle('Basic Information'), + _buildTextField( + label: 'Label', + initialValue: node.label, + onChanged: (value) { + notifier.updateNode( + workflowId, + node.copyWith(label: value), + ); + }, + ), + const SizedBox(height: 16), + if (node.nodeType == NodeType.request) ...[ + _buildSectionTitle('Request Details'), + _buildRequestEditor(context, ref), + ] else if (node.nodeType == NodeType.condition) ...[ + _buildSectionTitle('Condition Settings'), + _buildConditionEditor(context, ref), + ] else if (node.nodeType == NodeType.action) ...[ + _buildSectionTitle('Action Settings'), + _buildActionEditor(context, ref), + ], + const SizedBox(height: 24), + _buildSectionTitle('Execution Settings'), + _buildExecutionSettings(context, ref), + const SizedBox(height: 24), + _buildDependenciesSection(ref), + const SizedBox(height: 24), + _buildResponseDetailsSection(ref), + const SizedBox(height: 24), + _buildDeleteButton(context, ref), + ], + ), + ), + ); + } + + Widget _buildSectionTitle(String title) { + return Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + title, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 14, + ), + ), + ); + } + + Widget _buildTextField({ + required String label, + required String initialValue, + required Function(String) onChanged, + bool isMultiline = false, + }) { + return Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: TextStyle( + color: Colors.grey[400], + fontSize: 12, + ), + ), + const SizedBox(height: 4), + TextFormField( + initialValue: initialValue, + onChanged: onChanged, + maxLines: isMultiline ? 5 : 1, + style: const TextStyle(color: Colors.white), + decoration: InputDecoration( + filled: true, + fillColor: const Color(0xFF333333), + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 10, + ), + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(4), + borderSide: BorderSide.none, + ), + hintStyle: TextStyle(color: Colors.grey[600]), + ), + ), + ], + ), + ); + } + + Widget _buildRequestEditor(BuildContext context, WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildTextField( + label: 'URL', + initialValue: node.nodeData['url'] ?? '', + onChanged: (value) { + final updatedNodeData = Map.from(node.nodeData); + updatedNodeData['url'] = value; + final notifier = ref.read(workflowsNotifierProvider.notifier); + notifier.updateNode( + workflowId, + node.copyWith(nodeData: updatedNodeData), + ); + }, + ), + _buildDropdown( + label: 'Method', + value: node.nodeData['method'] ?? 'GET', + items: const ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'], + onChanged: (value) { + final notifier = ref.read(workflowsNotifierProvider.notifier); + final updatedNodeData = Map.from(node.nodeData); + updatedNodeData['method'] = value; + notifier.updateNode( + workflowId, + node.copyWith(nodeData: updatedNodeData), + ); + }, + ), + _buildTextField( + label: 'Request Body', + initialValue: node.nodeData['body'] ?? '', + onChanged: (value) { + final notifier = ref.read(workflowsNotifierProvider.notifier); + final updatedNodeData = Map.from(node.nodeData); + updatedNodeData['body'] = value; + notifier.updateNode( + workflowId, + node.copyWith(nodeData: updatedNodeData), + ); + }, + isMultiline: true, + ), + ], + ); + } + + Widget _buildConditionEditor(BuildContext context, WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildTextField( + label: 'Condition Expression', + initialValue: node.nodeData['condition'] ?? '', + onChanged: (value) { + final notifier = ref.read(workflowsNotifierProvider.notifier); + final updatedNodeData = Map.from(node.nodeData); + updatedNodeData['condition'] = value; + notifier.updateNode( + workflowId, + node.copyWith(nodeData: updatedNodeData), + ); + }, + isMultiline: true, + ), + ], + ); + } + + Widget _buildActionEditor(BuildContext context, WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildDropdown( + label: 'Action Type', + value: node.nodeData['actionType'] ?? 'transform', + items: const ['transform', 'dataStore', 'logger'], + onChanged: (value) { + final notifier = ref.read(workflowsNotifierProvider.notifier); + final updatedNodeData = Map.from(node.nodeData); + updatedNodeData['actionType'] = value; + notifier.updateNode( + workflowId, + node.copyWith(nodeData: updatedNodeData), + ); + }, + ), + _buildTextField( + label: 'Action Configuration', + initialValue: node.nodeData['actionConfig'] ?? '', + onChanged: (value) { + final notifier = ref.read(workflowsNotifierProvider.notifier); + final updatedNodeData = Map.from(node.nodeData); + updatedNodeData['actionConfig'] = value; + notifier.updateNode( + workflowId, + node.copyWith(nodeData: updatedNodeData), + ); + }, + isMultiline: true, + ), + ], + ); + } + + Widget _buildDropdown({ + required String label, + required String value, + required List items, + required Function(String?) onChanged, + }) { + return Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: TextStyle( + color: Colors.grey[400], + fontSize: 12, + ), + ), + const SizedBox(height: 4), + Container( + padding: const EdgeInsets.symmetric(horizontal: 12), + decoration: BoxDecoration( + color: const Color(0xFF333333), + borderRadius: BorderRadius.circular(4), + ), + child: DropdownButtonHideUnderline( + child: DropdownButton( + value: value, + onChanged: onChanged, + isExpanded: true, + dropdownColor: const Color(0xFF333333), + style: const TextStyle(color: Colors.white), + items: items.map((String item) { + return DropdownMenuItem( + value: item, + child: Text(item), + ); + }).toList(), + ), + ), + ), + ], + ), + ); + } + + Widget _buildExecutionSettings(BuildContext context, WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildDropdown( + label: 'Retry Strategy', + value: node.nodeData['retryStrategy'] ?? 'none', + items: const ['none', 'fixed', 'exponential'], + onChanged: (value) { + final notifier = ref.read(workflowsNotifierProvider.notifier); + final updatedNodeData = Map.from(node.nodeData); + updatedNodeData['retryStrategy'] = value; + notifier.updateNode( + workflowId, + node.copyWith(nodeData: updatedNodeData), + ); + }, + ), + if (node.nodeData['retryStrategy'] != 'none') + _buildTextField( + label: 'Max Retries', + initialValue: node.nodeData['maxRetries']?.toString() ?? '3', + onChanged: (value) { + final notifier = ref.read(workflowsNotifierProvider.notifier); + final updatedNodeData = Map.from(node.nodeData); + updatedNodeData['maxRetries'] = int.tryParse(value) ?? 3; + notifier.updateNode( + workflowId, + node.copyWith(nodeData: updatedNodeData), + ); + }, + ), + ], + ); + } + + Widget _buildDependenciesSection(WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Dependencies', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 8), + _buildTextField( + label: 'Dependent Nodes', + initialValue: (node.nodeData['dependencies'] as List?)?.join(', ') ?? '', + onChanged: (value) { + final notifier = ref.read(workflowsNotifierProvider.notifier); + final updatedData = Map.from(node.nodeData); + updatedData['dependencies'] = value.split(',').map((e) => e.trim()).toList(); + notifier.updateNode( + workflowId, + node.copyWith(nodeData: updatedData), + ); + }, + isMultiline: true, + ), + ], + ); + } + + Widget _buildResponseDetailsSection(WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Response Details', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(height: 8), + _buildTextField( + label: 'Response Handler', + initialValue: node.nodeData['handler'] as String? ?? '', + onChanged: (value) { + final notifier = ref.read(workflowsNotifierProvider.notifier); + final updatedData = Map.from(node.nodeData); + updatedData['handler'] = value; + notifier.updateNode( + workflowId, + node.copyWith(nodeData: updatedData), + ); + }, + ), + ], + ); + } + + Widget _buildDeleteButton(BuildContext context, WidgetRef ref) { + return Center( + child: TextButton.icon( + icon: const Icon(Icons.delete, color: Colors.red), + label: const Text('Delete Node', style: TextStyle(color: Colors.red)), + onPressed: () { + ref.read(workflowsNotifierProvider.notifier).removeNode( + workflowId, + node.id, + ); + ref.read(selectedNodeIdProvider.notifier).state = null; + }, + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + ), + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/sidebar_panel.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/sidebar_panel.dart new file mode 100644 index 000000000..11d88a5fc --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/sidebar_panel.dart @@ -0,0 +1,217 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'workflow_builder_screen.dart'; + +class SidebarPanel extends ConsumerWidget { + final LeftPanelTab selectedTab; + final ValueChanged onTabChanged; + + const SidebarPanel({ + super.key, + required this.selectedTab, + required this.onTabChanged, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Container( + width: 280, + color: const Color(0xFF212121), + child: Column( + children: [ + _buildTabBar(context), + Expanded( + child: selectedTab == LeftPanelTab.apiComponents + ? APIComponentList() + : TemplatesList(), + ), + ], + ), + ); + } + + Widget _buildTabBar(BuildContext context) { + return Container( + height: 48, + color: const Color(0xFF272727), + child: Row( + children: [ + Expanded( + child: _buildSidebarItem( + Icons.api, + 'Components', + selectedTab == LeftPanelTab.apiComponents, + onTap: () => onTabChanged(LeftPanelTab.apiComponents), + ), + ), + Expanded( + child: _buildSidebarItem( + Icons.bookmark, + 'Templates', + selectedTab == LeftPanelTab.templates, + onTap: () => onTabChanged(LeftPanelTab.templates), + ), + ), + ], + ), + ); + } + + Widget _buildSidebarItem(IconData icon, String title, bool isSelected, + {VoidCallback? onTap}) { + return InkWell( + onTap: onTap, + child: Container( + height: 48, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: isSelected ? Colors.blue : Colors.transparent, + width: 2, + ), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + icon, + size: 16, + color: isSelected ? Colors.blue : Colors.grey, + ), + const SizedBox(width: 8), + Text( + title, + style: TextStyle( + color: isSelected ? Colors.blue : Colors.grey, + fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, + ), + ), + ], + ), + ), + ); + } +} + +class APIComponentList extends StatelessWidget { + const APIComponentList({super.key}); + + @override + Widget build(BuildContext context) { + return ListView( + padding: const EdgeInsets.all(16), + children: [ + CardItem( + icon: Icons.api, + title: 'HTTP Request', + description: 'Make HTTP requests to APIs', + onTap: () => _onComponentSelected(context, 'http_request'), + ), + CardItem( + icon: Icons.storage, + title: 'Data Store', + description: 'Store and retrieve data', + onTap: () => _onComponentSelected(context, 'data_store'), + ), + CardItem( + icon: Icons.transform, + title: 'Data Transformer', + description: 'Transform and process data', + onTap: () => _onComponentSelected(context, 'data_transformer'), + ), + ], + ); + } + + void _onComponentSelected(BuildContext context, String componentType) { + // TODO: @abhinavs1920 Implement component selection logic + } +} + +class TemplatesList extends StatelessWidget { + const TemplatesList({super.key}); + + @override + Widget build(BuildContext context) { + return ListView( + padding: const EdgeInsets.all(16), + children: [ + CardItem( + icon: Icons.api, + title: 'API Integration', + description: 'Template for API integration workflows', + onTap: () => _onTemplateSelected(context, 'api_integration'), + ), + CardItem( + icon: Icons.storage, + title: 'Data Processing', + description: 'Template for data processing workflows', + onTap: () => _onTemplateSelected(context, 'data_processing'), + ), + ], + ); + } + + void _onTemplateSelected(BuildContext context, String templateId) { + // TODO: @abhinavs1920 Implement template selection logic + } +} + +class CardItem extends StatelessWidget { + final IconData icon; + final String title; + final String description; + final VoidCallback onTap; + + const CardItem({ + super.key, + required this.icon, + required this.title, + required this.description, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Container( + padding: const EdgeInsets.all(16), + margin: const EdgeInsets.only(bottom: 16), + decoration: BoxDecoration( + color: const Color(0xFF272727), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Icon(icon, color: Colors.white, size: 24), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 4), + Text( + description, + style: const TextStyle( + color: Colors.grey, + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/toolbar_panel.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/toolbar_panel.dart new file mode 100644 index 000000000..4ca40684f --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/toolbar_panel.dart @@ -0,0 +1,138 @@ +import 'package:api_testing_suite/src/workflow_builder/providers/providers.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../common/widgets/widgets.dart'; +import '../models/workflow_execution_state.dart'; +class ToolbarPanel extends ConsumerWidget { + final bool connectionMode; + final WorkflowExecutionState executionState; + final WorkflowExecutionControl executionControl; + final VoidCallback onConnectionModeToggle; + + const ToolbarPanel({ + super.key, + required this.connectionMode, + required this.executionState, + required this.executionControl, + required this.onConnectionModeToggle, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Container( + height: 56, + color: const Color(0xFF212121), + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + children: [ + ToggleActionButton( + icon: Icons.cable, + label: 'Connection Mode', + isActive: connectionMode, + onPressed: onConnectionModeToggle, + ), + const SizedBox(width: 16), + Container(width: 1, height: 24, color: Colors.grey.shade700), + const SizedBox(width: 16), + ExecutionControls( + executionState: executionState, + executionControl: executionControl, + ), + const Spacer(), + if (executionState.isRunning || + executionState.isPaused || + executionState.isCompleted) + StatusIndicator( + icon: StatusIndicatorUtils.getStatusIcon(executionState), + color: StatusIndicatorUtils.getStatusColor(executionState), + text: StatusIndicatorUtils.getStatusText(executionState), + ), + ], + ), + ); + } +} + +class ExecutionControls extends StatelessWidget { + final WorkflowExecutionState executionState; + final WorkflowExecutionControl executionControl; + + + const ExecutionControls({ + super.key, + required this.executionState, + required this.executionControl, + }); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + if (!executionState.isRunning && !executionState.isPaused) + IconButton( + icon: const Icon(Icons.play_arrow), + color: Colors.green, + onPressed: () { + executionControl.startExecution(); + }, + tooltip: 'Run Workflow', + ) + else if (executionState.isRunning) + IconButton( + icon: const Icon(Icons.pause), + color: Colors.amber, + onPressed: executionControl.pauseExecution, + tooltip: 'Pause Execution', + ) + else if (executionState.isPaused) + IconButton( + icon: const Icon(Icons.play_arrow), + color: Colors.green, + onPressed: executionControl.resumeExecution, + tooltip: 'Resume Execution', + ), + if (executionState.isRunning || executionState.isPaused) + IconButton( + icon: const Icon(Icons.stop), + color: Colors.red, + onPressed: executionControl.stopExecution, + tooltip: 'Stop Execution', + ), + // if (executionState.isCompleted || executionState.hasError) + // IconButton( + // icon: const Icon(Icons.refresh), + // color: Colors.blue, + // onPressed: executionControl.rese, + // tooltip: 'Reset Workflow', + // ), + ], + ); + } +} + +class StatusIndicatorUtils { + static Color getStatusColor(WorkflowExecutionState state) { + if (state.isRunning) return Colors.blue; + if (state.isPaused) return Colors.amber; + if (state.isCompleted) return Colors.green; + if (state.hasError) return Colors.red; + return Colors.grey; + } + + static IconData getStatusIcon(WorkflowExecutionState state) { + if (state.isRunning) return Icons.play_arrow; + if (state.isPaused) return Icons.pause; + if (state.isCompleted) return Icons.check_circle; + if (state.hasError) return Icons.error; + return Icons.circle_outlined; + } + + static String getStatusText(WorkflowExecutionState state) { + if (state.isRunning) return 'Running'; + if (state.isPaused) return 'Paused'; + if (state.isCompleted) return 'Completed'; + if (state.hasError) return 'Error'; + return 'Idle'; + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/workflow_builder_screen.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/workflow_builder_screen.dart new file mode 100644 index 000000000..083716da4 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/workflow_builder_screen.dart @@ -0,0 +1,303 @@ +import 'package:api_testing_suite/src/workflow_builder/providers/providers.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../models/models.dart'; +import 'package:apidash_core/models/http_request_model.dart'; +import 'package:uuid/uuid.dart'; +import '../widgets/logs_viewer.dart'; +import 'canvas/canvas_view.dart'; +import 'node_details_panel.dart' as node_details; +import 'sidebar_panel.dart'; +import 'toolbar_panel.dart'; + + +enum LeftPanelTab { + apiComponents, + templates, +} + +final leftPanelTabProvider = StateProvider((ref) { + return LeftPanelTab.apiComponents; +}); + +/// An optimized version of the WorkflowBuilderPage that uses components +class WorkflowBuilderScreen extends ConsumerWidget { + const WorkflowBuilderScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final workflowId = ref.watch(currentWorkflowProvider); + final workflowExecutionState = ref.watch(workflowExecutionStateProvider); + final connectionMode = ref.watch(connectionModeProvider); + final executionControl = ref.watch(workflowExecutionControlProvider); + final selectedNodeId = ref.watch(selectedNodeIdProvider); + final leftPanelTab = ref.watch(leftPanelTabProvider); + + if (workflowId == null) { + return _buildNoWorkflowScreen(context, ref); + } + + WorkflowNodeModel? selectedNode; + if (selectedNodeId != null) { + final workflows = ref.watch(workflowsNotifierProvider); + final workflow = workflows.firstWhere( + (w) => w.id == workflowId, + orElse: () => throw Exception('Workflow not found'), + ); + + selectedNode = workflow.nodes.firstWhere( + (n) => n.id == selectedNodeId, + orElse: () => throw Exception('Node not found'), + ); + } + + return Scaffold( + backgroundColor: const Color(0xFF1A1A1A), + appBar: _buildAppBar(ref, workflowExecutionState), + body: Column( + children: [ + ToolbarPanel( + connectionMode: connectionMode, + executionState: workflowExecutionState, + executionControl: executionControl, + onConnectionModeToggle: () { + ref.read(connectionModeProvider.notifier).state = !connectionMode; + }, + ), + Expanded( + child: Row( + children: [ + SidebarPanel( + selectedTab: leftPanelTab, + onTabChanged: (tab) { + ref.read(leftPanelTabProvider.notifier).state = tab; + }, + ), + Expanded( + child: Column( + children: [ + Expanded( + child: CanvasView( + key: ValueKey('${workflowId}_${selectedNode != null ? selectedNode.id : ''}_${DateTime.now().millisecondsSinceEpoch}'), + workflowId: workflowId, + ), + ), + Expanded( + child: LogsViewer(workflowId: workflowId), + ), + ], + ), + ), + if (selectedNode != null) + node_details.NodeDetailsPanel( + node: selectedNode, + workflowId: workflowId, + ), + ], + ), + ), + ], + ), + floatingActionButton: FloatingActionButton( + backgroundColor: Colors.blue, + onPressed: () { + final workflowId = ref.read(currentWorkflowProvider); + if (workflowId == null) return; + final position = const Offset(100, 100); + final node = WorkflowNodeModel.create( + requestId: workflowId, + position: position, + label: 'New API', + nodeType: NodeType.request, + requestModel: RequestModel( + id: const Uuid().v4(), + httpRequestModel: HttpRequestModel(), + ), + ); + ref.read(workflowsNotifierProvider.notifier).addNode(workflowId, node); + debugPrint('[FAB] Added node with ID: ${node.id} at position: $position'); + }, + child: const Icon(Icons.add), + ), + ); + } + + AppBar _buildAppBar( + WidgetRef ref, WorkflowExecutionState workflowExecutionState) { + return AppBar( + backgroundColor: const Color(0xFF171717), + title: const Text('Workflow Builder'), + actions: [ + IconButton( + icon: const Icon(Icons.save), + onPressed: () { + //TODO: Implement save workflow + // ref.read(workflowsNotifierProvider.notifier).saveWorkflows(); + ScaffoldMessenger.of(ref.context).showSnackBar( + const SnackBar(content: Text('Workflow saved')), + ); + }, + tooltip: 'Save Workflow', + ), + IconButton( + icon: const Icon(Icons.settings), + onPressed: () { + ScaffoldMessenger.of(ref.context).showSnackBar( + const SnackBar( + content: Text('Settings will be implemented soon')), + ); + }, + tooltip: 'Workflow Settings', + ), + const SizedBox(width: 8), + ], + ); + } + + Widget _buildNoWorkflowScreen(BuildContext context, WidgetRef ref) { + return Scaffold( + backgroundColor: const Color(0xFF1A1A1A), + appBar: AppBar( + backgroundColor: const Color(0xFF171717), + title: const Text('Workflow Builder'), + ), + body: Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 600), + child: Card( + color: const Color(0xFF212121), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.account_tree, + size: 80, + color: Colors.blue, + ), + const SizedBox(height: 24), + const Text( + 'Create a New Workflow', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(height: 16), + const Text( + 'Get started by creating a new API workflow', + style: TextStyle( + color: Colors.grey, + fontSize: 16, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 32), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () => _showCreateWorkflowDialog(context, ref), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + 'Create Workflow', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + const SizedBox(height: 16), + TextButton.icon( + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Importing workflows will be implemented soon'), + ), + ); + }, + icon: const Icon(Icons.upload_file, color: Colors.blue), + label: const Text( + 'Import Workflow', + style: TextStyle(color: Colors.blue), + ), + ), + ], + ), + ), + ), + ), + ), + ); + } + + void _showCreateWorkflowDialog(BuildContext context, WidgetRef ref) { + final TextEditingController nameController = TextEditingController(); + final TextEditingController descriptionController = TextEditingController(); + + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Create New Workflow'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: nameController, + decoration: const InputDecoration( + labelText: 'Workflow Name', + hintText: 'Enter a name for your workflow', + ), + autofocus: true, + ), + const SizedBox(height: 16), + TextField( + controller: descriptionController, + decoration: const InputDecoration( + labelText: 'Description (Optional)', + hintText: 'Enter a description for your workflow', + ), + maxLines: 2, + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Cancel'), + ), + ElevatedButton( + onPressed: () { + if (nameController.text.trim().isNotEmpty) { + final notifier = ref.read(workflowsNotifierProvider.notifier); + final workflowId = notifier.createWorkflow( + name: nameController.text.trim(), + description: descriptionController.text.trim(), + ); + ref.read(currentWorkflowProvider.notifier).state = workflowId; + Navigator.of(context).pop(); + } + }, + child: const Text('Create'), + ), + ], + ); + }, + ); + } +} From 54edf2b99cd00a9fda23e9dd70edb712a5b3a1cd Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 19 Apr 2025 00:43:13 +0530 Subject: [PATCH 158/188] refactor: update workflows notifier and file structure --- .../models/workflow_node_model.dart | 11 +- .../providers/workflows_notifier.dart | 404 +++++++++++++----- .../workflow_builder/widgets/logs_viewer.dart | 2 +- .../workflow_builder/workflows_notifier.dart | 301 ------------- 4 files changed, 290 insertions(+), 428 deletions(-) delete mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflows_notifier.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_node_model.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_node_model.dart index 07a03d2ab..a0080306b 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_node_model.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_node_model.dart @@ -7,6 +7,7 @@ import 'package:uuid/uuid.dart'; import 'core_models.dart'; import 'node_status.dart'; import 'workflow_connection_model.dart'; +import 'node_type.dart'; part 'workflow_node_model.freezed.dart'; part 'workflow_node_model.g.dart'; @@ -43,7 +44,8 @@ class RequestModelConverter implements JsonConverter json) => _$WorkflowNodeModelFromJson(json); } - -enum NodeType { - request, - response, - condition, - action, -} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/providers/workflows_notifier.dart b/packages/api_testing_suite/lib/src/workflow_builder/providers/workflows_notifier.dart index 4327ca2dc..3e5af4d50 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/providers/workflows_notifier.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/providers/workflows_notifier.dart @@ -1,158 +1,326 @@ +import 'dart:async'; + +import 'package:api_testing_suite/api_testing_suite.dart'; +import 'package:apidash_core/models/http_request_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../models/models.dart'; - -/// Notifier class for workflows state management class WorkflowsNotifier extends StateNotifier> { - final Ref _ref; - - WorkflowsNotifier(this._ref) : super([]); + final Map _executionEngines = {}; + Timer? _debounceTimer; + + WorkflowsNotifier() : super([]); + + String createWorkflow({String? name, String? description}) { + final newWorkflow = WorkflowModel.create( + name: name ?? 'New Workflow', + description: description ?? '', + ); - void add() { - final newWorkflow = WorkflowModel.create(name: 'New Workflow'); state = [...state, newWorkflow]; - _ref.read(StateProvider((ref) => null).notifier).state = newWorkflow.id; + return newWorkflow.id; } - void update(WorkflowModel workflow) { + void updateWorkflow(WorkflowModel updatedWorkflow) { state = [ - for (final existingWorkflow in state) - if (existingWorkflow.id == workflow.id) workflow else existingWorkflow, + for (final workflow in state) + if (workflow.id == updatedWorkflow.id) updatedWorkflow else workflow, ]; } + void deleteWorkflow(String workflowId) { + _executionEngines[workflowId]?.dispose(); + _executionEngines.remove(workflowId); + + state = state.where((workflow) => workflow.id != workflowId).toList(); + } + void addNode(String workflowId, WorkflowNodeModel node) { - state = [ - for (final workflow in state) - if (workflow.id == workflowId) - workflow.copyWith(nodes: [...workflow.nodes, node]) - else - workflow, - ]; + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: [...workflow.nodes, node], + ); + } + return workflow; + }).toList(); } void updateNode(String workflowId, WorkflowNodeModel updatedNode) { - state = [ - for (final workflow in state) - if (workflow.id == workflowId) - workflow.copyWith( - nodes: [ - for (final node in workflow.nodes) - if (node.id == updatedNode.id) updatedNode else node, - ], - ) - else - workflow, - ]; + WorkflowNodeModel nodeToSave = updatedNode; + if (updatedNode.nodeType == NodeType.request) { + final nodeData = updatedNode.nodeData; + final url = nodeData['url'] ?? ''; + final method = nodeData['method'] ?? 'GET'; + if (updatedNode.requestModel != null) { + nodeToSave = updatedNode.copyWith( + requestModel: updatedNode.requestModel!.copyWith( + url: url, + method: method, + ), + ); + } else { + nodeToSave = updatedNode.copyWith( + requestModel: RequestModel( + id: updatedNode.id, + name: updatedNode.label, + method: method, + url: url, + httpRequestModel: HttpRequestModel(), + ), + ); + } + } + + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: workflow.nodes.map((node) { + return node.id == updatedNode.id ? nodeToSave : node; + }).toList(), + ); + } + return workflow; + }).toList(); + print('Updated node: ${nodeToSave.id} | nodeData: ${nodeToSave.nodeData} | requestModel: ${nodeToSave.requestModel}'); } - - void updateNodes(String workflowId, List updatedNodes) { - state = [ - for (final workflow in state) - if (workflow.id == workflowId) - workflow.copyWith(nodes: updatedNodes) - else - workflow, - ]; + + void updateNodeStatus(String workflowId, String nodeId, NodeStatus status) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: workflow.nodes.map((node) { + return node.id == nodeId ? node.copyWith(status: status) : node; + }).toList(), + ); + } + return workflow; + }).toList(); } void updateNodePosition(String workflowId, String nodeId, Offset position) { - state = [ - for (final workflow in state) - if (workflow.id == workflowId) - workflow.copyWith( - nodes: [ - for (final node in workflow.nodes) - if (node.id == nodeId) - node.copyWith(position: position) - else - node, - ], - ) - else - workflow, - ]; + _debounceTimer?.cancel(); + _debounceTimer = Timer(const Duration(milliseconds: 50), () { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: workflow.nodes.map((node) { + return node.id == nodeId + ? node.copyWith(position: position) + : node; + }).toList(), + ); + } + return workflow; + }).toList(); + }); } void removeNode(String workflowId, String nodeId) { - state = [ - for (final workflow in state) - if (workflow.id == workflowId) - workflow.copyWith( - nodes: workflow.nodes.where((node) => node.id != nodeId).toList(), - connections: workflow.connections.where( - (conn) => conn.sourceId != nodeId && conn.targetId != nodeId - ).toList(), - ) - else - workflow, - ]; + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: workflow.nodes.where((node) => node.id != nodeId).toList(), + connections: workflow.connections + .where( + (conn) => conn.sourceId != nodeId && conn.targetId != nodeId) + .toList(), + ); + } + return workflow; + }).toList(); } - void addConnection(String workflowId, WorkflowConnectionModel connection) { - state = [ - for (final workflow in state) - if (workflow.id == workflowId) - workflow.copyWith(connections: [...workflow.connections, connection]) - else - workflow, - ]; + void selectNode(String workflowId, String? nodeId) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith(selectedNodeId: nodeId); + } + return workflow; + }).toList(); } - void updateConnection(String workflowId, WorkflowConnectionModel updatedConnection) { - state = [ - for (final workflow in state) - if (workflow.id == workflowId) - workflow.copyWith( - connections: [ - for (final connection in workflow.connections) - if (connection.id == updatedConnection.id) updatedConnection else connection, - ], - ) - else - workflow, - ]; + void createConnection(String workflowId, String sourceId, String targetId) { + if (sourceId == targetId) return; + + state = state.map((workflow) { + if (workflow.id == workflowId) { + final connectionExists = workflow.connections.any( + (conn) => conn.sourceId == sourceId && conn.targetId == targetId, + ); + + if (!connectionExists) { + final newConnection = WorkflowConnectionModel.create( + sourceId: sourceId, + targetId: targetId, + workflowId: workflowId, + position: Offset.zero, + ); + + return workflow.copyWith( + connections: [...workflow.connections, newConnection], + ); + } + } + return workflow; + }).toList(); + + _resetExecutionEngine(workflowId); } void removeConnection(String workflowId, String connectionId) { - state = [ - for (final workflow in state) - if (workflow.id == workflowId) - workflow.copyWith( - connections: workflow.connections.where((conn) => conn.id != connectionId).toList(), - ) - else - workflow, - ]; + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + connections: workflow.connections + .where((conn) => conn.id != connectionId) + .toList(), + ); + } + return workflow; + }).toList(); + + _resetExecutionEngine(workflowId); } - void delete(String workflowId) { - state = state.where((workflow) => workflow.id != workflowId).toList(); - if (_ref.read(StateProvider((ref) => null)) == workflowId) { - _ref.read(StateProvider((ref) => null).notifier).state = state.isNotEmpty ? state.first.id : null; + void setActiveNodes(String workflowId, List nodeIds) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith(activeNodeIds: nodeIds); + } + return workflow; + }).toList(); + } + + void setCompletedNodes(String workflowId, List nodeIds) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith(completedNodeIds: nodeIds); + } + return workflow; + }).toList(); + } + + void setStartNode(String workflowId, String? nodeId) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith(startNodeId: nodeId); + } + return workflow; + }).toList(); + } + + WorkflowModel? getWorkflow(String workflowId) { + return state.firstWhere( + (workflow) => workflow.id == workflowId, + orElse: () => throw Exception('Workflow not found: $workflowId'), + ); + } + + void startExecution( + String workflowId, Function(WorkflowExecutionState) onStateChanged) { + final engine = _getOrCreateExecutionEngine(workflowId, onStateChanged: onStateChanged); + if (engine != null) { + engine.start(); + } + } + + void pauseExecution(String workflowId) { + final engine = _executionEngines[workflowId]; + engine?.pause(); + } + + void resumeExecution(String workflowId) { + final engine = _executionEngines[workflowId]; + engine?.resume(); + } + + void stopExecution(String workflowId) { + final engine = _executionEngines[workflowId]; + engine?.stop(); + } + + WorkflowExecutor? _getOrCreateExecutionEngine(String workflowId, {Function(WorkflowExecutionState)? onStateChanged}) { + if (_executionEngines.containsKey(workflowId)) { + return _executionEngines[workflowId]; } + + final workflow = getWorkflow(workflowId); + if (workflow == null) return null; + + final engine = WorkflowExecutor( + workflow: workflow, + onNodeStatusChanged: (nodeId, status) => + updateNodeStatus(workflowId, nodeId, status), + onExecutionStateChanged: (state) { + setActiveNodes(workflowId, state.pendingNodeIds); + setCompletedNodes(workflowId, state.executedNodeIds); + if (onStateChanged != null) { + onStateChanged(state); + } + }, + ); + + _executionEngines[workflowId] = engine; + return engine; } - void importRequestAsNode(String workflowId, String requestId, Offset position, Map collection) { - try { - final RequestModel? request = collection[requestId]; - if (request == null) { - debugPrint('Request not found with ID: $requestId'); - return; + void _resetExecutionEngine(String workflowId) { + _executionEngines[workflowId]?.dispose(); + _executionEngines.remove(workflowId); + } + + void startNodeDrag(String workflowId, String nodeId, Offset position) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: workflow.nodes.map((node) { + return node.id == nodeId + ? node.copyWith( + position: position, + isDragging: true, + ) + : node.copyWith( + isDragging: false, + ); + }).toList(), + ); + } + return workflow; + }).toList(); + } + + void updateNodeDrag(String workflowId, String nodeId, Offset position) { + updateNodePosition(workflowId, nodeId, position); + } + + void finishNodeDrag(String workflowId, String nodeId) { + state = state.map((workflow) { + if (workflow.id == workflowId) { + return workflow.copyWith( + nodes: workflow.nodes.map((node) { + return node.id == nodeId + ? node.copyWith( + isDragging: false, + ) + : node; + }).toList(), + ); } - - final node = WorkflowNodeModel.create( - requestId: requestId, - position: position, - label: request.name.isNotEmpty ? request.name : 'Request ${requestId.substring(0, 4)}', - requestModel: request, - ); - - addNode(workflowId, node); - } catch (e) { - debugPrint('Error importing node: $e'); + return workflow; + }).toList(); + } + + @override + void dispose() { + _debounceTimer?.cancel(); + + for (final engine in _executionEngines.values) { + engine.dispose(); } + _executionEngines.clear(); + + super.dispose(); } } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart b/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart index b4abc6acf..1a125b84a 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart @@ -1,7 +1,7 @@ +import 'package:api_testing_suite/src/workflow_builder/providers/providers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../models/workflow_execution_state.dart'; -import '../workflow_providers.dart'; import '../models/log_entry.dart'; class LogsViewer extends ConsumerWidget { diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflows_notifier.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflows_notifier.dart deleted file mode 100644 index 5ca5515a9..000000000 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflows_notifier.dart +++ /dev/null @@ -1,301 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'models/workflow_node_model.dart'; -import 'models/workflow_connection_model.dart'; -import 'models/workflow_model.dart'; -import 'models/node_status.dart'; -import 'models/workflow_execution_state.dart'; -import 'dag_execution_engine.dart'; - -class WorkflowsNotifier extends StateNotifier> { - final Map _executionEngines = {}; - Timer? _debounceTimer; - - WorkflowsNotifier() : super([]); - - String createWorkflow({String? name, String? description}) { - final newWorkflow = WorkflowModel.create( - name: name ?? 'New Workflow', - description: description ?? '', - ); - - state = [...state, newWorkflow]; - return newWorkflow.id; - } - - void updateWorkflow(WorkflowModel updatedWorkflow) { - state = [ - for (final workflow in state) - if (workflow.id == updatedWorkflow.id) updatedWorkflow else workflow, - ]; - } - - void deleteWorkflow(String workflowId) { - _executionEngines[workflowId]?.dispose(); - _executionEngines.remove(workflowId); - - state = state.where((workflow) => workflow.id != workflowId).toList(); - } - - void addNode(String workflowId, WorkflowNodeModel node) { - state = state.map((workflow) { - if (workflow.id == workflowId) { - return workflow.copyWith( - nodes: [...workflow.nodes, node], - ); - } - return workflow; - }).toList(); - } - - void updateNode(String workflowId, WorkflowNodeModel updatedNode) { - state = state.map((workflow) { - if (workflow.id == workflowId) { - return workflow.copyWith( - nodes: workflow.nodes.map((node) { - return node.id == updatedNode.id ? updatedNode : node; - }).toList(), - ); - } - return workflow; - }).toList(); - } - - void updateNodeStatus(String workflowId, String nodeId, NodeStatus status) { - state = state.map((workflow) { - if (workflow.id == workflowId) { - return workflow.copyWith( - nodes: workflow.nodes.map((node) { - return node.id == nodeId ? node.copyWith(status: status) : node; - }).toList(), - ); - } - return workflow; - }).toList(); - } - - void updateNodePosition(String workflowId, String nodeId, Offset position) { - _debounceTimer?.cancel(); - _debounceTimer = Timer(const Duration(milliseconds: 50), () { - state = state.map((workflow) { - if (workflow.id == workflowId) { - return workflow.copyWith( - nodes: workflow.nodes.map((node) { - return node.id == nodeId - ? node.copyWith(position: position) - : node; - }).toList(), - ); - } - return workflow; - }).toList(); - }); - } - - void removeNode(String workflowId, String nodeId) { - state = state.map((workflow) { - if (workflow.id == workflowId) { - return workflow.copyWith( - nodes: workflow.nodes.where((node) => node.id != nodeId).toList(), - connections: workflow.connections - .where( - (conn) => conn.sourceId != nodeId && conn.targetId != nodeId) - .toList(), - ); - } - return workflow; - }).toList(); - } - - void selectNode(String workflowId, String? nodeId) { - state = state.map((workflow) { - if (workflow.id == workflowId) { - return workflow.copyWith(selectedNodeId: nodeId); - } - return workflow; - }).toList(); - } - - void createConnection(String workflowId, String sourceId, String targetId) { - if (sourceId == targetId) return; - - state = state.map((workflow) { - if (workflow.id == workflowId) { - final connectionExists = workflow.connections.any( - (conn) => conn.sourceId == sourceId && conn.targetId == targetId, - ); - - if (!connectionExists) { - final newConnection = WorkflowConnectionModel.create( - sourceId: sourceId, - targetId: targetId, - workflowId: workflowId, - position: Offset.zero, - ); - - return workflow.copyWith( - connections: [...workflow.connections, newConnection], - ); - } - } - return workflow; - }).toList(); - - _resetExecutionEngine(workflowId); - } - - void removeConnection(String workflowId, String connectionId) { - state = state.map((workflow) { - if (workflow.id == workflowId) { - return workflow.copyWith( - connections: workflow.connections - .where((conn) => conn.id != connectionId) - .toList(), - ); - } - return workflow; - }).toList(); - - _resetExecutionEngine(workflowId); - } - - void setActiveNodes(String workflowId, List nodeIds) { - state = state.map((workflow) { - if (workflow.id == workflowId) { - return workflow.copyWith(activeNodeIds: nodeIds); - } - return workflow; - }).toList(); - } - - void setCompletedNodes(String workflowId, List nodeIds) { - state = state.map((workflow) { - if (workflow.id == workflowId) { - return workflow.copyWith(completedNodeIds: nodeIds); - } - return workflow; - }).toList(); - } - - void setStartNode(String workflowId, String? nodeId) { - state = state.map((workflow) { - if (workflow.id == workflowId) { - return workflow.copyWith(startNodeId: nodeId); - } - return workflow; - }).toList(); - } - - WorkflowModel? getWorkflow(String workflowId) { - return state.firstWhere( - (workflow) => workflow.id == workflowId, - orElse: () => throw Exception('Workflow not found: $workflowId'), - ); - } - - void startExecution( - String workflowId, Function(WorkflowExecutionState) onStateChanged) { - final engine = _getOrCreateExecutionEngine(workflowId); - if (engine != null) { - engine.start(); - } - } - - void pauseExecution(String workflowId) { - final engine = _executionEngines[workflowId]; - engine?.pause(); - } - - void resumeExecution(String workflowId) { - final engine = _executionEngines[workflowId]; - engine?.resume(); - } - - void stopExecution(String workflowId) { - final engine = _executionEngines[workflowId]; - engine?.stop(); - } - - DagExecutionEngine? _getOrCreateExecutionEngine(String workflowId) { - if (_executionEngines.containsKey(workflowId)) { - return _executionEngines[workflowId]; - } - - final workflow = getWorkflow(workflowId); - if (workflow == null) return null; - - final engine = DagExecutionEngine( - workflow: workflow, - onNodeStatusChanged: (nodeId, status) => - updateNodeStatus(workflowId, nodeId, status), - onExecutionStateChanged: (state) { - setActiveNodes(workflowId, state.pendingNodeIds); - setCompletedNodes(workflowId, state.executedNodeIds); - }, - ); - - _executionEngines[workflowId] = engine; - return engine; - } - - void _resetExecutionEngine(String workflowId) { - _executionEngines[workflowId]?.dispose(); - _executionEngines.remove(workflowId); - } - - void startNodeDrag(String workflowId, String nodeId, Offset position) { - state = state.map((workflow) { - if (workflow.id == workflowId) { - return workflow.copyWith( - nodes: workflow.nodes.map((node) { - return node.id == nodeId - ? node.copyWith( - position: position, - isDragging: true, - ) - : node.copyWith( - isDragging: false, - ); - }).toList(), - ); - } - return workflow; - }).toList(); - } - - void updateNodeDrag(String workflowId, String nodeId, Offset position) { - updateNodePosition(workflowId, nodeId, position); - } - - void finishNodeDrag(String workflowId, String nodeId) { - state = state.map((workflow) { - if (workflow.id == workflowId) { - return workflow.copyWith( - nodes: workflow.nodes.map((node) { - return node.id == nodeId - ? node.copyWith( - isDragging: false, - ) - : node; - }).toList(), - ); - } - return workflow; - }).toList(); - } - - @override - void dispose() { - _debounceTimer?.cancel(); - - for (final engine in _executionEngines.values) { - engine.dispose(); - } - _executionEngines.clear(); - - super.dispose(); - } -} From e37d9dbb8d6532f2707aa5fb7056da2865668e03 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 19 Apr 2025 00:44:07 +0530 Subject: [PATCH 159/188] refactor: remove direct exports of workflows_notifier and workflow_providers, use providers instead --- .../lib/src/workflow_builder/workflow_builder.dart | 2 -- .../lib/src/workflow_builder/workflow_screens.dart | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart index fa61a427a..e39d3eddf 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart @@ -1,8 +1,6 @@ // Export core components export 'components/workflow_builder_screen.dart'; export 'workflow_node.dart'; -export 'workflows_notifier.dart'; -export 'workflow_providers.dart'; // Export optimized components export 'components/components.dart'; diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart index 66404fe0e..03b7c8498 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart @@ -6,8 +6,7 @@ export 'workflow_connection.dart' hide ConnectionPainter; export 'execution/workflow_executor.dart'; // Providers -export 'workflow_providers.dart'; -export 'workflows_notifier.dart'; +export 'providers/providers.dart'; // Models export 'models/models.dart'; From 54ca85e8847159f956f2ae4544c39e11966e8b8c Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 19 Apr 2025 00:52:43 +0530 Subject: [PATCH 160/188] refactor: remove unused workflow components and clean up exports --- .../lib/src/tests/workflow_builder_test.dart | 1 - .../workflow_builder/connection_painter.dart | 214 ---------- .../src/workflow_builder/grid_painter.dart | 29 -- .../workflow_builder/workflow_builder.dart | 1 - .../workflow_builder/workflow_connection.dart | 107 ----- .../src/workflow_builder/workflow_node.dart | 374 ------------------ .../workflow_builder/workflow_screens.dart | 2 - 7 files changed, 728 deletions(-) delete mode 100644 packages/api_testing_suite/lib/src/workflow_builder/connection_painter.dart delete mode 100644 packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart delete mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflow_connection.dart delete mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart diff --git a/packages/api_testing_suite/lib/src/tests/workflow_builder_test.dart b/packages/api_testing_suite/lib/src/tests/workflow_builder_test.dart index 9606a0c03..0347ae30a 100644 --- a/packages/api_testing_suite/lib/src/tests/workflow_builder_test.dart +++ b/packages/api_testing_suite/lib/src/tests/workflow_builder_test.dart @@ -1,5 +1,4 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:api_testing_suite/src/workflow_builder/workflow_canvas.dart'; import 'package:api_testing_suite/src/workflow_builder/workflow_node.dart'; void main() { diff --git a/packages/api_testing_suite/lib/src/workflow_builder/connection_painter.dart b/packages/api_testing_suite/lib/src/workflow_builder/connection_painter.dart deleted file mode 100644 index 2cfa314fe..000000000 --- a/packages/api_testing_suite/lib/src/workflow_builder/connection_painter.dart +++ /dev/null @@ -1,214 +0,0 @@ -import 'package:api_testing_suite/api_testing_suite.dart'; -import 'package:flutter/material.dart'; - -/// Painter for workflow connections -class ConnectionPainter extends CustomPainter { - final List connections; - final List nodes; - final Map nodePositions; - final List runningNodeIds; - final List completedNodeIds; - final Offset? tempConnectionStart; - final Offset? tempConnectionEnd; - final double strokeWidth; - - final Offset? start; - final Offset? end; - final Color? color; - final bool animated; - final String? label; - - ConnectionPainter({ - this.connections = const [], - this.nodes = const [], - this.nodePositions = const {}, - this.runningNodeIds = const [], - this.completedNodeIds = const [], - this.tempConnectionStart, - this.tempConnectionEnd, - this.strokeWidth = 2.0, - this.start, - this.end, - this.color, - this.animated = false, - this.label, - }); - - @override - void paint(Canvas canvas, Size size) { - if (start != null && end != null) { - _paintSingleConnection(canvas, start!, end!, color ?? Colors.blue, animated, label); - return; - } - - for (final connection in connections) { - final sourcePos = _getNodeCenterPosition(connection.sourceId); - final targetPos = _getNodeCenterPosition(connection.targetId); - - if (sourcePos != null && targetPos != null) { - final isActive = _isConnectionActive(connection.sourceId, connection.targetId); - final isCompleted = _isConnectionCompleted(connection.sourceId, connection.targetId); - - Color connectionColor; - if (isActive) { - connectionColor = Colors.blue.shade500; - } else if (isCompleted) { - connectionColor = Colors.green.shade500; - } else if (connection.condition != null) { - connectionColor = Colors.amber.shade500; - } else { - connectionColor = Colors.blue.shade300; - } - - _paintSingleConnection( - canvas, - sourcePos, - targetPos, - connectionColor, - isActive, - connection.condition - ); - } - } - - if (tempConnectionStart != null && tempConnectionEnd != null) { - _paintSingleConnection( - canvas, - tempConnectionStart!, - tempConnectionEnd!, - Colors.blue.withOpacity(0.7), - true, - null - ); - } - } - - Offset? _getNodeCenterPosition(String nodeId) { - final position = nodePositions[nodeId]; - if (position != null) { - return position + const Offset(80, 40); - } - return null; - } - - bool _isConnectionActive(String sourceId, String targetId) { - return runningNodeIds.contains(sourceId) || runningNodeIds.contains(targetId); - } - - bool _isConnectionCompleted(String sourceId, String targetId) { - return completedNodeIds.contains(sourceId) && completedNodeIds.contains(targetId); - } - - void _paintSingleConnection( - Canvas canvas, - Offset start, - Offset end, - Color color, - bool animated, - String? label, - ) { - final paint = Paint() - ..color = color - ..strokeWidth = strokeWidth - ..style = PaintingStyle.stroke; - - if (animated) { - paint.shader = LinearGradient( - colors: [ - color.withOpacity(0.3), - color, - color.withOpacity(0.3), - ], - stops: const [0.0, 0.5, 1.0], - ).createShader(Rect.fromPoints(start, end)); - } - - final path = Path() - ..moveTo(start.dx, start.dy) - ..lineTo(end.dx, end.dy); - - canvas.drawPath(path, paint); - - if (label != null && label.isNotEmpty) { - final midpoint = Offset( - (start.dx + end.dx) / 2, - (start.dy + end.dy) / 2, - ); - _drawLabel(canvas, midpoint, label); - } - } - - // Offset _calculateTangent(Offset controlPoint, Offset endPoint) { - // return (endPoint - controlPoint).normalize(); - // } - - // void _drawArrow(Canvas canvas, Offset position, Offset direction, Paint paint) { - // final arrowSize = 8.0; - // final angle = atan2(direction.dy, direction.dx); - - // final path = Path() - // ..moveTo(position.dx, position.dy) - // ..lineTo( - // position.dx - arrowSize * cos(angle - pi / 6), - // position.dy - arrowSize * sin(angle - pi / 6), - // ) - // ..lineTo( - // position.dx - arrowSize * cos(angle + pi / 6), - // position.dy - arrowSize * sin(angle + pi / 6), - // ) - // ..close(); - - // canvas.drawPath(path, paint..style = PaintingStyle.fill); - // } - - void _drawLabel(Canvas canvas, Offset position, String label) { - final textPainter = TextPainter( - text: TextSpan( - text: label, - style: const TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), - textDirection: TextDirection.ltr, - ); - textPainter.layout(); - - canvas.drawRect( - Rect.fromCenter( - center: position, - width: textPainter.width + 16, - height: textPainter.height + 8, - ), - Paint()..color = Colors.black.withOpacity(0.7), - ); - - textPainter.paint( - canvas, - Offset( - position.dx - textPainter.width / 2, - position.dy - textPainter.height / 2, - ), - ); - } - - @override - bool shouldRepaint(ConnectionPainter oldDelegate) { - return oldDelegate.connections != connections || - oldDelegate.runningNodeIds != runningNodeIds || - oldDelegate.completedNodeIds != completedNodeIds || - oldDelegate.tempConnectionStart != tempConnectionStart || - oldDelegate.tempConnectionEnd != tempConnectionEnd || - oldDelegate.start != start || - oldDelegate.end != end; - } -} - -extension OffsetExtension on Offset { - Offset normalize() { - final magnitude = distance; - if (magnitude == 0) return Offset.zero; - return Offset(dx / magnitude, dy / magnitude); - } -} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart b/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart deleted file mode 100644 index 522888969..000000000 --- a/packages/api_testing_suite/lib/src/workflow_builder/grid_painter.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; - -/// A custom painter for drawing a grid background -class GridBackgroundPainter extends CustomPainter { - final Color gridColor; - final double step; - - GridBackgroundPainter({ - required this.gridColor, - required this.step, - }); - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = gridColor - ..strokeWidth = 1.0; - - for (double x = 0; x < size.width; x += step) { - canvas.drawLine(Offset(x, 0), Offset(x, size.height), paint); - } - for (double y = 0; y < size.height; y += step) { - canvas.drawLine(Offset(0, y), Offset(size.width, y), paint); - } - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => false; -} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart index e39d3eddf..0bc7aba20 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart @@ -1,6 +1,5 @@ // Export core components export 'components/workflow_builder_screen.dart'; -export 'workflow_node.dart'; // Export optimized components export 'components/components.dart'; diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_connection.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_connection.dart deleted file mode 100644 index 64f1ce63c..000000000 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_connection.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'package:flutter/material.dart'; -import 'models/workflow_connection_model.dart'; - -class WorkflowConnectionWidget extends StatelessWidget { - final WorkflowConnectionModel connection; - final Offset sourcePosition; - final Offset targetPosition; - final VoidCallback onTap; - final VoidCallback onRemove; - - const WorkflowConnectionWidget({ - super.key, - required this.connection, - required this.sourcePosition, - required this.targetPosition, - required this.onTap, - required this.onRemove, - }); - - @override - Widget build(BuildContext context) { - return Positioned( - left: 0, - top: 0, - child: GestureDetector( - onTap: onTap, - child: CustomPaint( - painter: ConnectionPainter( - source: sourcePosition, - target: targetPosition, - isConditional: connection.isConditional, - ), - size: Size.infinite, - ), - ), - ); - } -} - -class ConnectionPainter extends CustomPainter { - final Offset source; - final Offset target; - final bool isConditional; - - ConnectionPainter({ - required this.source, - required this.target, - required this.isConditional, - }); - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = Colors.blue - ..strokeWidth = 2 - ..style = PaintingStyle.stroke; - - canvas.drawLine(source, target, paint); - - final arrowSize = 10.0; - final arrowPath = Path() - ..moveTo(target.dx, target.dy) - ..lineTo( - target.dx - arrowSize * 1.5 * (target.dx > source.dx ? 1 : -1), - target.dy - arrowSize, - ) - ..lineTo( - target.dx - arrowSize * 1.5 * (target.dx > source.dx ? 1 : -1), - target.dy + arrowSize, - ) - ..close(); - - canvas.drawPath(arrowPath, paint..style = PaintingStyle.fill); - - if (isConditional) { - final conditionPath = Path() - ..moveTo( - (source.dx + target.dx) / 2, - (source.dy + target.dy) / 2 - 10, - ) - ..lineTo( - (source.dx + target.dx) / 2 + 10, - (source.dy + target.dy) / 2 + 10, - ) - ..lineTo( - (source.dx + target.dx) / 2 - 10, - (source.dy + target.dy) / 2 + 10, - ) - ..close(); - - canvas.drawPath( - conditionPath, - Paint() - ..color = Colors.yellow - ..style = PaintingStyle.fill, - ); - } - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return oldDelegate is ConnectionPainter && - (oldDelegate.source != source || - oldDelegate.target != target || - oldDelegate.isConditional != isConditional); - } -} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart deleted file mode 100644 index e13313b4b..000000000 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_node.dart +++ /dev/null @@ -1,374 +0,0 @@ -import 'package:api_testing_suite/api_testing_suite.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - - -class WorkflowNode extends ConsumerWidget { - final WorkflowNodeModel node; - final bool isSelected; - final bool isRunning; - final bool isCompleted; - final bool hasError; - final Function(String)? onTap; - final Function(String, Offset)? onStartConnection; - final Function(String, Offset)? onDragStart; - final Function(Offset)? onDragUpdate; - final Function()? onDragEnd; - - const WorkflowNode({ - super.key, - required this.node, - this.isSelected = false, - this.isRunning = false, - this.isCompleted = false, - this.hasError = false, - this.onTap, - this.onStartConnection, - this.onDragStart, - this.onDragUpdate, - this.onDragEnd, - }); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final nodeSize = _getNodeSize(); - final nodeColor = _getNodeColor(); - final borderColor = isSelected ? Colors.blue : Colors.transparent; - final iconData = _getNodeIcon(); - final elevation = isSelected ? 8.0 : 4.0; - - return GestureDetector( - onTap: onTap != null ? () => onTap!(node.id) : null, - onPanStart: onDragStart != null ? (details) => onDragStart!(node.id, details.globalPosition) : null, - onPanUpdate: onDragUpdate != null ? (details) => onDragUpdate!(details.globalPosition) : null, - onPanEnd: onDragEnd != null ? (_) => onDragEnd!() : null, - child: Material( - elevation: elevation, - color: Colors.transparent, - borderRadius: BorderRadius.circular(8), - shadowColor: isRunning - ? Colors.blue.withOpacity(0.6) - : isCompleted - ? Colors.green.withOpacity(0.6) - : hasError - ? Colors.red.withOpacity(0.6) - : Colors.black.withOpacity(0.3), - child: Container( - width: nodeSize.width, - height: nodeSize.height, - decoration: BoxDecoration( - color: nodeColor, - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: borderColor, - width: 2, - ), - boxShadow: [ - BoxShadow( - color: isRunning - ? Colors.blue.withOpacity(0.3) - : isCompleted - ? Colors.green.withOpacity(0.3) - : hasError - ? Colors.red.withOpacity(0.3) - : Colors.black.withOpacity(0.2), - blurRadius: 5, - spreadRadius: 1, - ), - ], - ), - child: Stack( - children: [ - Padding( - padding: const EdgeInsets.all(10.0), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - iconData, - color: Colors.white, - size: 18, - ), - const SizedBox(width: 8), - Expanded( - child: Text( - node.label.isEmpty - ? 'Node ${node.id.substring(0, 4)}' - : node.label, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 14, - ), - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - const SizedBox(height: 8), - if (node.nodeType == NodeType.request) - _buildRequestDetails(), - if (node.nodeType == NodeType.condition) - _buildConditionDetails(), - if (node.nodeType == NodeType.action) - _buildActionDetails(), - if (node.nodeType == NodeType.response) - _buildResponseDetails(), - ], - ), - ), - - if (isRunning || isCompleted || hasError) - Positioned( - right: 5, - top: 5, - child: Container( - width: 16, - height: 16, - decoration: BoxDecoration( - color: isRunning - ? Colors.blue - : isCompleted - ? Colors.green - : Colors.red, - shape: BoxShape.circle, - border: Border.all(color: Colors.white, width: 2), - ), - child: Icon( - isRunning - ? Icons.update - : isCompleted - ? Icons.check - : Icons.error, - color: Colors.white, - size: 10, - ), - ), - ), - - if (onStartConnection != null) - Positioned( - right: 0, - top: nodeSize.height / 2 - 6, - child: GestureDetector( - onPanStart: (details) { - onStartConnection!( - node.id, - details.globalPosition, - ); - }, - child: Container( - width: 12, - height: 12, - decoration: BoxDecoration( - color: Colors.blue, - shape: BoxShape.circle, - border: Border.all( - color: Colors.white, - width: 2, - ), - ), - ), - ), - ), - ], - ), - ), - ), - ); - } - - Widget _buildRequestDetails() { - final method = node.nodeData['method'] as String? ?? 'GET'; - final url = node.nodeData['url'] as String? ?? ''; - final methodColor = _getMethodColor(method); - final shortUrl = url.length > 30 ? '${url.substring(0, 27)}...' : url; - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Container( - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), - decoration: BoxDecoration( - color: methodColor, - borderRadius: BorderRadius.circular(4), - ), - child: Text( - method, - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), - ), - ), - const SizedBox(width: 4), - Expanded( - child: Text( - shortUrl.isEmpty ? 'No URL set' : shortUrl, - style: TextStyle( - color: Colors.white.withOpacity(0.8), - fontSize: 10, - fontStyle: shortUrl.isEmpty ? FontStyle.italic : FontStyle.normal, - ), - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ], - ); - } - - Widget _buildConditionDetails() { - final condition = node.nodeData['condition'] as String? ?? ''; - final shortCondition = condition.length > 30 ? '${condition.substring(0, 27)}...' : condition; - - return Container( - padding: const EdgeInsets.all(4), - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.2), - borderRadius: BorderRadius.circular(4), - ), - child: Text( - shortCondition.isEmpty ? 'No condition set' : shortCondition, - style: TextStyle( - color: Colors.white.withOpacity(0.8), - fontSize: 10, - fontStyle: shortCondition.isEmpty ? FontStyle.italic : FontStyle.normal, - ), - ), - ); - } - - Widget _buildActionDetails() { - final actionType = node.nodeData['actionType'] as String? ?? 'transform'; - - return Row( - children: [ - Container( - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.2), - borderRadius: BorderRadius.circular(4), - ), - child: Text( - actionType.toUpperCase(), - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), - ), - ), - ], - ); - } - - Widget _buildResponseDetails() { - final statusCode = node.nodeData['statusCode'] as int? ?? 200; - - return Row( - children: [ - Container( - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), - decoration: BoxDecoration( - color: _getStatusCodeColor(statusCode), - borderRadius: BorderRadius.circular(4), - ), - child: Text( - 'Status: $statusCode', - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), - ), - ), - ], - ); - } - - Size _getNodeSize() { - switch (node.nodeType) { - case NodeType.request: - return const Size(160, 80); - case NodeType.response: - return const Size(160, 60); - case NodeType.condition: - return const Size(160, 60); - case NodeType.action: - return const Size(160, 60); - } - } - - Color _getNodeColor() { - final baseColors = { - NodeType.request: const Color(0xFF2d3748), - NodeType.response: const Color(0xFF553C9A), - NodeType.condition: const Color(0xFFb91c1c), - NodeType.action: const Color(0xFF047857), - }; - - Color baseColor = baseColors[node.nodeType] ?? const Color(0xFF2d3748); - - if (isRunning) { - return Color.lerp(baseColor, Colors.blue, 0.15) ?? baseColor; - } else if (isCompleted) { - return Color.lerp(baseColor, Colors.green, 0.15) ?? baseColor; - } else if (hasError) { - return Color.lerp(baseColor, Colors.red, 0.15) ?? baseColor; - } else { - return baseColor; - } - } - - IconData _getNodeIcon() { - switch (node.nodeType) { - case NodeType.request: - return Icons.send; - case NodeType.response: - return Icons.call_received; - case NodeType.condition: - return Icons.fork_right; - case NodeType.action: - return Icons.settings; - } - } - - Color _getMethodColor(String method) { - switch (method.toUpperCase()) { - case 'GET': - return const Color(0xFF3182CE); - case 'POST': - return const Color(0xFF38A169); - case 'PUT': - return const Color(0xFFDD6B20); - case 'DELETE': - return const Color(0xFFE53E3E); - case 'PATCH': - return const Color(0xFF805AD5); - default: - return const Color(0xFF718096); - } - } - - Color _getStatusCodeColor(int statusCode) { - if (statusCode >= 200 && statusCode < 300) { - return Colors.green; - } else if (statusCode >= 300 && statusCode < 400) { - return Colors.blue; - } else if (statusCode >= 400 && statusCode < 500) { - return Colors.orange; - } else if (statusCode >= 500) { - return Colors.red; - } else { - return Colors.grey; - } - } -} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart index 03b7c8498..f9c36c50b 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart @@ -1,8 +1,6 @@ // Main components export 'workflow_builder.dart'; export 'components/workflow_builder_screen.dart'; -export 'workflow_node.dart'; -export 'workflow_connection.dart' hide ConnectionPainter; export 'execution/workflow_executor.dart'; // Providers From 3f3be60c251af881b8f70c446329433d42f5e788 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 19 Apr 2025 18:02:12 +0530 Subject: [PATCH 161/188] refactor: remove unused stress test models and related widgets and connect testing_suite pkg --- .../stress_test/api_request_result.dart | 16 - lib/models/stress_test/isolate_message.dart | 17 - .../stress_test/stress_test_config.dart | 20 - .../stress_test/stress_test_models.dart | 3 - .../stress_test/stress_test_summary.dart | 48 -- .../request_pane/request_stress_test.dart | 5 +- .../stress_test/stress_test_service.dart | 502 +++++++++--------- .../stress_test/number_input_field.dart | 59 -- .../stress_test/stress_test_result_card.dart | 291 ---------- 9 files changed, 252 insertions(+), 709 deletions(-) delete mode 100644 lib/models/stress_test/api_request_result.dart delete mode 100644 lib/models/stress_test/isolate_message.dart delete mode 100644 lib/models/stress_test/stress_test_config.dart delete mode 100644 lib/models/stress_test/stress_test_models.dart delete mode 100644 lib/models/stress_test/stress_test_summary.dart delete mode 100644 lib/widgets/stress_test/number_input_field.dart delete mode 100644 lib/widgets/stress_test/stress_test_result_card.dart diff --git a/lib/models/stress_test/api_request_result.dart b/lib/models/stress_test/api_request_result.dart deleted file mode 100644 index 2cbf40251..000000000 --- a/lib/models/stress_test/api_request_result.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'api_request_result.freezed.dart'; -part 'api_request_result.g.dart'; - -@freezed -class ApiRequestResult with _$ApiRequestResult { - const factory ApiRequestResult({ - required int statusCode, - required String body, - required Duration duration, - String? error, - }) = _ApiRequestResult; - - factory ApiRequestResult.fromJson(Map json) => _$ApiRequestResultFromJson(json); -} diff --git a/lib/models/stress_test/isolate_message.dart b/lib/models/stress_test/isolate_message.dart deleted file mode 100644 index 61bc33121..000000000 --- a/lib/models/stress_test/isolate_message.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'isolate_message.freezed.dart'; -part 'isolate_message.g.dart'; - -@freezed -class IsolateMessage with _$IsolateMessage { - const factory IsolateMessage({ - required String url, - required String method, - Map? headers, - dynamic body, - Duration? timeout, - }) = _IsolateMessage; - - factory IsolateMessage.fromJson(Map json) => _$IsolateMessageFromJson(json); -} diff --git a/lib/models/stress_test/stress_test_config.dart b/lib/models/stress_test/stress_test_config.dart deleted file mode 100644 index ff30a0132..000000000 --- a/lib/models/stress_test/stress_test_config.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:apidash_core/apidash_core.dart'; - -part 'stress_test_config.freezed.dart'; -part 'stress_test_config.g.dart'; - -@freezed -class StressTestConfig with _$StressTestConfig { - const factory StressTestConfig({ - required String url, - required String method, - Map? headers, - dynamic body, - required int concurrentRequests, - Duration? timeout, - @Default(false) bool useIsolates, - }) = _StressTestConfig; - - factory StressTestConfig.fromJson(Map json) => _$StressTestConfigFromJson(json); -} diff --git a/lib/models/stress_test/stress_test_models.dart b/lib/models/stress_test/stress_test_models.dart deleted file mode 100644 index 10c1bcacd..000000000 --- a/lib/models/stress_test/stress_test_models.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'stress_test_config.dart'; -export 'api_request_result.dart'; -export 'stress_test_summary.dart'; diff --git a/lib/models/stress_test/stress_test_summary.dart b/lib/models/stress_test/stress_test_summary.dart deleted file mode 100644 index 8c211d5ba..000000000 --- a/lib/models/stress_test/stress_test_summary.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -import 'package:apidash/models/stress_test/api_request_result.dart'; - -part 'stress_test_summary.freezed.dart'; -part 'stress_test_summary.g.dart'; - -@freezed -class StressTestSummary with _$StressTestSummary { - const StressTestSummary._(); - - const factory StressTestSummary({ - required List results, - required Duration totalDuration, - required double avgResponseTime, - required int successCount, - required int failureCount, - }) = _StressTestSummary; - - factory StressTestSummary.fromJson(Map json) => _$StressTestSummaryFromJson(json); - - double get successRate => results.isEmpty ? 0 : (successCount / results.length) * 100; - - double get failureRate => results.isEmpty ? 0 : (failureCount / results.length) * 100; - - Duration get minResponseTime { - if (results.isEmpty) return Duration.zero; - return results.map((r) => r.duration).reduce((a, b) => a < b ? a : b); - } - - Duration get maxResponseTime { - if (results.isEmpty) return Duration.zero; - return results.map((r) => r.duration).reduce((a, b) => a > b ? a : b); - } - - Duration get medianResponseTime { - if (results.isEmpty) return Duration.zero; - final sortedDurations = results.map((r) => r.duration).toList()..sort(); - final middle = sortedDurations.length ~/ 2; - if (sortedDurations.length.isOdd) { - return sortedDurations[middle]; - } - return Duration(microseconds: ( - sortedDurations[middle - 1].inMicroseconds + - sortedDurations[middle].inMicroseconds - ) ~/ 2); - } -} diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_stress_test.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_stress_test.dart index d7773d4f8..83bf70003 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_stress_test.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_stress_test.dart @@ -1,8 +1,5 @@ +import 'package:api_testing_suite/api_testing_suite.dart'; import 'package:apidash/providers/providers.dart'; -import 'package:apidash/services/stress_test/stress_test_service.dart'; -import 'package:apidash/models/stress_test/stress_test_config.dart'; -import 'package:apidash/models/stress_test/stress_test_summary.dart'; -import 'package:apidash/models/stress_test/api_request_result.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; diff --git a/lib/services/stress_test/stress_test_service.dart b/lib/services/stress_test/stress_test_service.dart index 1186e23f0..7038bed7e 100644 --- a/lib/services/stress_test/stress_test_service.dart +++ b/lib/services/stress_test/stress_test_service.dart @@ -1,278 +1,278 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:isolate'; +// import 'dart:async'; +// import 'dart:convert'; +// import 'dart:isolate'; -import 'package:http/http.dart' as http; +// import 'package:http/http.dart' as http; -import 'package:apidash/models/stress_test/api_request_result.dart'; -import 'package:apidash/models/stress_test/stress_test_config.dart'; -import 'package:apidash/models/stress_test/stress_test_summary.dart'; -import 'package:apidash/models/stress_test/isolate_message.dart'; +// import 'package:apidash/models/stress_test/api_request_result.dart'; +// import 'package:apidash/models/stress_test/stress_test_config.dart'; +// import 'package:apidash/models/stress_test/stress_test_summary.dart'; +// import 'package:apidash/models/stress_test/isolate_message.dart'; -// Stress Test Service Class -class StressTestService { +// // Stress Test Service Class +// class StressTestService { - static Future _executeRequest({ - required String url, - required String method, - Map? headers, - dynamic body, - Duration? timeout, - }) async { - final stopwatch = Stopwatch()..start(); - final client = http.Client(); +// static Future _executeRequest({ +// required String url, +// required String method, +// Map? headers, +// dynamic body, +// Duration? timeout, +// }) async { +// final stopwatch = Stopwatch()..start(); +// final client = http.Client(); - try { - http.Response response; - final defaultTimeout = const Duration(seconds: 30); +// try { +// http.Response response; +// final defaultTimeout = const Duration(seconds: 30); - try { - switch (method.toUpperCase()) { - case 'GET': - response = await client.get( - Uri.parse(url), - headers: headers, - ).timeout(timeout ?? defaultTimeout); - case 'POST': - response = await client.post( - Uri.parse(url), - headers: headers, - body: body is String ? body : jsonEncode(body), - ).timeout(timeout ?? defaultTimeout); - case 'PUT': - response = await client.put( - Uri.parse(url), - headers: headers, - body: body is String ? body : jsonEncode(body), - ).timeout(timeout ?? defaultTimeout); - case 'DELETE': - response = await client.delete( - Uri.parse(url), - headers: headers, - body: body is String ? body : jsonEncode(body), - ).timeout(timeout ?? defaultTimeout); - case 'PATCH': - response = await client.patch( - Uri.parse(url), - headers: headers, - body: body is String ? body : jsonEncode(body), - ).timeout(timeout ?? defaultTimeout); - default: - throw Exception('Unsupported HTTP method: $method'); - } +// try { +// switch (method.toUpperCase()) { +// case 'GET': +// response = await client.get( +// Uri.parse(url), +// headers: headers, +// ).timeout(timeout ?? defaultTimeout); +// case 'POST': +// response = await client.post( +// Uri.parse(url), +// headers: headers, +// body: body is String ? body : jsonEncode(body), +// ).timeout(timeout ?? defaultTimeout); +// case 'PUT': +// response = await client.put( +// Uri.parse(url), +// headers: headers, +// body: body is String ? body : jsonEncode(body), +// ).timeout(timeout ?? defaultTimeout); +// case 'DELETE': +// response = await client.delete( +// Uri.parse(url), +// headers: headers, +// body: body is String ? body : jsonEncode(body), +// ).timeout(timeout ?? defaultTimeout); +// case 'PATCH': +// response = await client.patch( +// Uri.parse(url), +// headers: headers, +// body: body is String ? body : jsonEncode(body), +// ).timeout(timeout ?? defaultTimeout); +// default: +// throw Exception('Unsupported HTTP method: $method'); +// } - stopwatch.stop(); - return ApiRequestResult( - statusCode: response.statusCode, - body: response.body, - duration: stopwatch.elapsed, - error: null, - ); - } on TimeoutException { - stopwatch.stop(); - return ApiRequestResult( - statusCode: -1, - body: '', - duration: stopwatch.elapsed, - error: 'Request timed out after ${(timeout ?? defaultTimeout).inSeconds} seconds', - ); - } on http.ClientException catch (e) { - stopwatch.stop(); - return ApiRequestResult( - statusCode: -1, - body: '', - duration: stopwatch.elapsed, - error: 'HTTP client error: ${e.message}', - ); - } on FormatException catch (e) { - stopwatch.stop(); - return ApiRequestResult( - statusCode: -1, - body: '', - duration: stopwatch.elapsed, - error: 'Format error: ${e.message}', - ); - } catch (e) { - stopwatch.stop(); - return ApiRequestResult( - statusCode: -1, - body: '', - duration: stopwatch.elapsed, - error: e.toString(), - ); - } - } finally { - client.close(); - } - } +// stopwatch.stop(); +// return ApiRequestResult( +// statusCode: response.statusCode, +// body: response.body, +// duration: stopwatch.elapsed, +// error: null, +// ); +// } on TimeoutException { +// stopwatch.stop(); +// return ApiRequestResult( +// statusCode: -1, +// body: '', +// duration: stopwatch.elapsed, +// error: 'Request timed out after ${(timeout ?? defaultTimeout).inSeconds} seconds', +// ); +// } on http.ClientException catch (e) { +// stopwatch.stop(); +// return ApiRequestResult( +// statusCode: -1, +// body: '', +// duration: stopwatch.elapsed, +// error: 'HTTP client error: ${e.message}', +// ); +// } on FormatException catch (e) { +// stopwatch.stop(); +// return ApiRequestResult( +// statusCode: -1, +// body: '', +// duration: stopwatch.elapsed, +// error: 'Format error: ${e.message}', +// ); +// } catch (e) { +// stopwatch.stop(); +// return ApiRequestResult( +// statusCode: -1, +// body: '', +// duration: stopwatch.elapsed, +// error: e.toString(), +// ); +// } +// } finally { +// client.close(); +// } +// } - /// Isolate worker function - static Future _isolateWorker(SendPort sendPort) async { - final receivePort = ReceivePort(); - sendPort.send(receivePort.sendPort); +// /// Isolate worker function +// static Future _isolateWorker(SendPort sendPort) async { +// final receivePort = ReceivePort(); +// sendPort.send(receivePort.sendPort); - await for (final message in receivePort) { - if (message is IsolateMessage) { - final result = await _executeRequest( - url: message.url, - method: message.method, - headers: message.headers, - body: message.body, - timeout: message.timeout, - ); - sendPort.send(result); - } else if (message == 'close') { - break; - } - } +// await for (final message in receivePort) { +// if (message is IsolateMessage) { +// final result = await _executeRequest( +// url: message.url, +// method: message.method, +// headers: message.headers, +// body: message.body, +// timeout: message.timeout, +// ); +// sendPort.send(result); +// } else if (message == 'close') { +// break; +// } +// } - Isolate.exit(); - } +// Isolate.exit(); +// } - /// Execute a parallel API test - static Future runTest(StressTestConfig config) async { - final totalStopwatch = Stopwatch()..start(); - final List results = []; +// /// Execute a parallel API test +// static Future runTest(StressTestConfig config) async { +// final totalStopwatch = Stopwatch()..start(); +// final List results = []; - if (config.useIsolates) { - final isolates = []; - final receivePorts = []; - final sendPorts = []; - final completers = >[]; +// if (config.useIsolates) { +// final isolates = []; +// final receivePorts = []; +// final sendPorts = []; +// final completers = >[]; - try { - for (var i = 0; i < config.concurrentRequests; i++) { - final receivePort = ReceivePort(); - final completer = Completer(); +// try { +// for (var i = 0; i < config.concurrentRequests; i++) { +// final receivePort = ReceivePort(); +// final completer = Completer(); - final isolate = await Isolate.spawn( - _isolateWorker, - receivePort.sendPort, - errorsAreFatal: false, - onExit: receivePort.sendPort, - onError: receivePort.sendPort, - ); +// final isolate = await Isolate.spawn( +// _isolateWorker, +// receivePort.sendPort, +// errorsAreFatal: false, +// onExit: receivePort.sendPort, +// onError: receivePort.sendPort, +// ); - isolates.add(isolate); - receivePorts.add(receivePort); - completers.add(completer); +// isolates.add(isolate); +// receivePorts.add(receivePort); +// completers.add(completer); - receivePort.listen((message) { - if (message is SendPort) { - sendPorts.add(message); - if (sendPorts.length == config.concurrentRequests) { - for (var j = 0; j < config.concurrentRequests; j++) { - sendPorts[j].send(IsolateMessage( - url: config.url, - method: config.method, - headers: config.headers, - body: config.body, - timeout: config.timeout, - )); - } - } - } else if (message is ApiRequestResult) { - if (!completer.isCompleted) { - completer.complete(message); - } - } else if (message is List && message.length >= 2) { - if (!completer.isCompleted) { - completer.complete(ApiRequestResult( - statusCode: -1, - body: '', - duration: Duration.zero, - error: 'Isolate error: ${message[0]}', - )); - } - } - }); - } +// receivePort.listen((message) { +// if (message is SendPort) { +// sendPorts.add(message); +// if (sendPorts.length == config.concurrentRequests) { +// for (var j = 0; j < config.concurrentRequests; j++) { +// sendPorts[j].send(IsolateMessage( +// url: config.url, +// method: config.method, +// headers: config.headers, +// body: config.body, +// timeout: config.timeout, +// )); +// } +// } +// } else if (message is ApiRequestResult) { +// if (!completer.isCompleted) { +// completer.complete(message); +// } +// } else if (message is List && message.length >= 2) { +// if (!completer.isCompleted) { +// completer.complete(ApiRequestResult( +// statusCode: -1, +// body: '', +// duration: Duration.zero, +// error: 'Isolate error: ${message[0]}', +// )); +// } +// } +// }); +// } - for (var completer in completers) { - try { - final result = await completer.future.timeout( - (config.timeout ?? const Duration(seconds: 30)) + const Duration(seconds: 10) - ); - results.add(result); - } on TimeoutException { - results.add(ApiRequestResult( - statusCode: -1, - body: '', - duration: Duration.zero, - error: 'Isolate communication timed out', - )); - } - } - } finally { - for (var i = 0; i < isolates.length; i++) { - try { - if (sendPorts.length > i) { - sendPorts[i].send('close'); - } - isolates[i].kill(priority: Isolate.immediate); - } catch (_) { /*We need to ignore once termination is done*/ } - } +// for (var completer in completers) { +// try { +// final result = await completer.future.timeout( +// (config.timeout ?? const Duration(seconds: 30)) + const Duration(seconds: 10) +// ); +// results.add(result); +// } on TimeoutException { +// results.add(ApiRequestResult( +// statusCode: -1, +// body: '', +// duration: Duration.zero, +// error: 'Isolate communication timed out', +// )); +// } +// } +// } finally { +// for (var i = 0; i < isolates.length; i++) { +// try { +// if (sendPorts.length > i) { +// sendPorts[i].send('close'); +// } +// isolates[i].kill(priority: Isolate.immediate); +// } catch (_) { /*We need to ignore once termination is done*/ } +// } - for (var port in receivePorts) { - port.close(); - } - } - } else { - final futures = >[]; +// for (var port in receivePorts) { +// port.close(); +// } +// } +// } else { +// final futures = >[]; - for (int i = 0; i < config.concurrentRequests; i++) { - futures.add(_executeRequest( - url: config.url, - method: config.method, - headers: config.headers, - body: config.body, - timeout: config.timeout, - )); - } +// for (int i = 0; i < config.concurrentRequests; i++) { +// futures.add(_executeRequest( +// url: config.url, +// method: config.method, +// headers: config.headers, +// body: config.body, +// timeout: config.timeout, +// )); +// } - try { - final overallTimeout = (config.timeout ?? const Duration(seconds: 30)) + const Duration(seconds: 10); - final futureResults = await Future.wait(futures).timeout(overallTimeout); - results.addAll(futureResults); - } on TimeoutException { - final completedResults = results.length; - final remainingCount = config.concurrentRequests - completedResults; +// try { +// final overallTimeout = (config.timeout ?? const Duration(seconds: 30)) + const Duration(seconds: 10); +// final futureResults = await Future.wait(futures).timeout(overallTimeout); +// results.addAll(futureResults); +// } on TimeoutException { +// final completedResults = results.length; +// final remainingCount = config.concurrentRequests - completedResults; - for (int i = 0; i < remainingCount; i++) { - results.add(ApiRequestResult( - statusCode: -1, - body: '', - duration: Duration.zero, - error: 'Operation timed out', - )); - } - } - } +// for (int i = 0; i < remainingCount; i++) { +// results.add(ApiRequestResult( +// statusCode: -1, +// body: '', +// duration: Duration.zero, +// error: 'Operation timed out', +// )); +// } +// } +// } - totalStopwatch.stop(); +// totalStopwatch.stop(); - final successCount = results.where((r) => - r.statusCode >= 200 && r.statusCode < 300 && r.error == null - ).length; +// final successCount = results.where((r) => +// r.statusCode >= 200 && r.statusCode < 300 && r.error == null +// ).length; - final failureCount = results.length - successCount; +// final failureCount = results.length - successCount; - final validResults = results.where((r) => r.error == null); - final totalResponseTime = validResults.fold( - 0, - (prev, result) => prev + result.duration.inMicroseconds - ); +// final validResults = results.where((r) => r.error == null); +// final totalResponseTime = validResults.fold( +// 0, +// (prev, result) => prev + result.duration.inMicroseconds +// ); - final avgResponseTime = validResults.isEmpty - ? 0.0 - : totalResponseTime / validResults.length / 1000; +// final avgResponseTime = validResults.isEmpty +// ? 0.0 +// : totalResponseTime / validResults.length / 1000; - return StressTestSummary( - results: results, - totalDuration: totalStopwatch.elapsed, - avgResponseTime: avgResponseTime, - successCount: successCount, - failureCount: failureCount, - ); - } -} +// return StressTestSummary( +// results: results, +// totalDuration: totalStopwatch.elapsed, +// avgResponseTime: avgResponseTime, +// successCount: successCount, +// failureCount: failureCount, +// ); +// } +// } diff --git a/lib/widgets/stress_test/number_input_field.dart b/lib/widgets/stress_test/number_input_field.dart deleted file mode 100644 index 2505ae696..000000000 --- a/lib/widgets/stress_test/number_input_field.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -class NumberInputField extends StatelessWidget { - final TextEditingController controller; - final String labelText; - final String? helperText; - final int min; - final int max; - final bool enabled; - - const NumberInputField({ - Key? key, - required this.controller, - required this.labelText, - this.helperText, - required this.min, - required this.max, - this.enabled = true, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - final isDarkMode = Theme.of(context).brightness == Brightness.dark; - - return TextField( - controller: controller, - decoration: InputDecoration( - labelText: labelText, - helperText: helperText, - border: const OutlineInputBorder(), - contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), - helperStyle: TextStyle( - color: isDarkMode ? Colors.grey[400] : Colors.grey[700], - ), - ), - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - enabled: enabled, - onChanged: (value) { - final numValue = int.tryParse(value) ?? min; - - if (numValue < min) { - controller.text = min.toString(); - controller.selection = TextSelection.fromPosition( - TextPosition(offset: controller.text.length), - ); - } else if (numValue > max) { - controller.text = max.toString(); - controller.selection = TextSelection.fromPosition( - TextPosition(offset: controller.text.length), - ); - } - }, - ); - } -} diff --git a/lib/widgets/stress_test/stress_test_result_card.dart b/lib/widgets/stress_test/stress_test_result_card.dart deleted file mode 100644 index beb1ecf47..000000000 --- a/lib/widgets/stress_test/stress_test_result_card.dart +++ /dev/null @@ -1,291 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:apidash/models/stress_test/stress_test_models.dart'; - -class StressTestResultCard extends StatelessWidget { - final StressTestSummary summary; - - const StressTestResultCard({Key? key, required this.summary}) : super(key: key); - - @override - Widget build(BuildContext context) { - final isDarkMode = Theme.of(context).brightness == Brightness.dark; - final successColor = isDarkMode ? Colors.green[400] : Colors.green[700]; - final errorColor = isDarkMode ? Colors.red[400] : Colors.red[700]; - final cardColor = isDarkMode ? Colors.grey[850] : Colors.grey[200]; - final textColor = isDarkMode ? Colors.grey[400] : Colors.grey[800]; - - return Card( - color: cardColor, - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - child: _buildSummaryItem( - context, - 'Total Requests', - '${summary.results.length}', - Icons.http, - textColor, - ), - ), - Expanded( - child: _buildSummaryItem( - context, - 'Total Duration', - '${(summary.totalDuration.inMilliseconds / 1000).toStringAsFixed(2)}s', - Icons.timer, - textColor, - ), - ), - Expanded( - child: _buildSummaryItem( - context, - 'Avg Response Time', - '${summary.avgResponseTime.toStringAsFixed(2)}ms', - Icons.speed, - textColor, - ), - ), - ], - ), - const SizedBox(height: 16), - Row( - children: [ - Expanded( - child: _buildStatusItem( - context, - 'Success', - summary.successCount, - summary.results.length, - successColor!, - ), - ), - Expanded( - child: _buildStatusItem( - context, - 'Failed', - summary.failureCount, - summary.results.length, - errorColor!, - ), - ), - ], - ), - const SizedBox(height: 24), - Text( - 'Response Time Distribution', - style: Theme.of(context).textTheme.titleSmall, - ), - const SizedBox(height: 8), - _buildResponseTimeDistribution(context, summary.results), - const SizedBox(height: 16), - Text( - 'Status Code Distribution', - style: Theme.of(context).textTheme.titleSmall, - ), - const SizedBox(height: 8), - _buildStatusCodeDistribution(context, summary.results), - ], - ), - ), - ); - } - - Widget _buildSummaryItem( - BuildContext context, - String label, - String value, - IconData icon, - Color? textColor, - ) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon(icon, size: 14, color: textColor), - const SizedBox(width: 4), - Text( - label, - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: textColor, - ), - ), - ], - ), - const SizedBox(height: 4), - Text( - value, - style: Theme.of(context).textTheme.titleMedium, - ), - ], - ); - } - - Widget _buildStatusItem( - BuildContext context, - String label, - int count, - int total, - Color color, - ) { - final percentage = total > 0 ? (count / total * 100).toStringAsFixed(1) : '0.0'; - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - label, - style: Theme.of(context).textTheme.bodySmall, - ), - const SizedBox(height: 4), - Row( - children: [ - Text( - '$count', - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: color, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(width: 4), - Text( - '($percentage%)', - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: color, - ), - ), - ], - ), - const SizedBox(height: 4), - ClipRRect( - borderRadius: BorderRadius.circular(2), - child: LinearProgressIndicator( - value: total > 0 ? count / total : 0, - backgroundColor: Colors.grey[400], - valueColor: AlwaysStoppedAnimation(color), - minHeight: 4, - ), - ), - ], - ); - } - - Widget _buildResponseTimeDistribution(BuildContext context, List results) { - final successfulResults = results.where((r) => r.error == null).toList(); - if (successfulResults.isEmpty) { - return const Text('No successful responses to analyze'); - } - - successfulResults.sort((a, b) => a.duration.compareTo(b.duration)); - - final p50Index = (successfulResults.length * 0.5).floor(); - final p75Index = (successfulResults.length * 0.75).floor(); - final p90Index = (successfulResults.length * 0.9).floor(); - final p95Index = (successfulResults.length * 0.95).floor(); - final p99Index = (successfulResults.length * 0.99).floor(); - - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Column( - children: [ - _buildPercentileRow(context, 'Min', - '${successfulResults.first.duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P50', - '${successfulResults[p50Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P75', - '${successfulResults[p75Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P90', - '${successfulResults[p90Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P95', - '${successfulResults[p95Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P99', - '${successfulResults[p99Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'Max', - '${successfulResults.last.duration.inMilliseconds} ms'), - ], - ), - ); - } - - Widget _buildPercentileRow(BuildContext context, String percentile, String value) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Row( - children: [ - SizedBox( - width: 40, - child: Text( - percentile, - style: Theme.of(context).textTheme.bodySmall, - ), - ), - const SizedBox(width: 8), - Expanded( - child: Text( - value, - style: Theme.of(context).textTheme.bodyMedium, - ), - ), - ], - ), - ); - } - - Widget _buildStatusCodeDistribution(BuildContext context, List results) { - final statusCodeCounts = {}; - for (final result in results) { - if (result.statusCode != -1) { // Skip errors - statusCodeCounts[result.statusCode] = - (statusCodeCounts[result.statusCode] ?? 0) + 1; - } - } - - if (statusCodeCounts.isEmpty) { - return const Text('No status codes to analyze'); - } - - final sortedEntries = statusCodeCounts.entries.toList() - ..sort((a, b) => a.key.compareTo(b.key)); - - return Wrap( - spacing: 8, - runSpacing: 8, - children: sortedEntries.map((entry) { - final statusCode = entry.key; - final count = entry.value; - final isSuccess = statusCode >= 200 && statusCode < 300; - final isRedirect = statusCode >= 300 && statusCode < 400; - final isClientError = statusCode >= 400 && statusCode < 500; - final isServerError = statusCode >= 500; - - Color chipColor; - if (isSuccess) { - chipColor = Colors.green; - } else if (isRedirect) { - chipColor = Colors.blue; - } else if (isClientError) { - chipColor = Colors.orange; - } else if (isServerError) { - chipColor = Colors.red; - } else { - chipColor = Colors.grey; - } - - return Chip( - label: Text('$statusCode ($count)'), - backgroundColor: chipColor.withOpacity(0.2), - labelStyle: TextStyle(color: chipColor), - ); - }).toList(), - ); - } -} From f8ac1ca557b049b274d1cf48802aa6976c8615b9 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 19 Apr 2025 18:03:07 +0530 Subject: [PATCH 162/188] refactor: clean up workflow builder exports and remove unused workflow_screens file --- .../lib/src/tests/workflow_builder_test.dart | 1 - .../workflow_builder/workflow_builder.dart | 28 +++++++++++++---- .../workflow_builder/workflow_screens.dart | 31 ------------------- 3 files changed, 22 insertions(+), 38 deletions(-) delete mode 100644 packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart diff --git a/packages/api_testing_suite/lib/src/tests/workflow_builder_test.dart b/packages/api_testing_suite/lib/src/tests/workflow_builder_test.dart index 0347ae30a..bf26fcd1e 100644 --- a/packages/api_testing_suite/lib/src/tests/workflow_builder_test.dart +++ b/packages/api_testing_suite/lib/src/tests/workflow_builder_test.dart @@ -1,5 +1,4 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:api_testing_suite/src/workflow_builder/workflow_node.dart'; void main() { group('Workflow Builder Tests', () { diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart index 0bc7aba20..3e6f61c20 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/workflow_builder.dart @@ -1,15 +1,31 @@ -// Export core components +// Main components export 'components/workflow_builder_screen.dart'; - -// Export optimized components export 'components/components.dart'; export 'execution/workflow_executor.dart'; -// Export models +// Providers +export 'providers/providers.dart'; + +// Models export 'models/models.dart'; -// Export widgets +// Widgets export 'widgets/widgets.dart'; -// Export common widgets with hide +// Common widgets with hide export '../common/widgets/widgets.dart'; + +// Screens entry points +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'components/workflow_builder_screen.dart'; + +/// A wrapper widget that provides an entry point to the workflow builder +class WorkflowBuilderEntry extends ConsumerWidget { + const WorkflowBuilderEntry({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return const WorkflowBuilderScreen(); + } +} diff --git a/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart b/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart deleted file mode 100644 index f9c36c50b..000000000 --- a/packages/api_testing_suite/lib/src/workflow_builder/workflow_screens.dart +++ /dev/null @@ -1,31 +0,0 @@ -// Main components -export 'workflow_builder.dart'; -export 'components/workflow_builder_screen.dart'; -export 'execution/workflow_executor.dart'; - -// Providers -export 'providers/providers.dart'; - -// Models -export 'models/models.dart'; - -// Widgets -export 'widgets/widgets.dart'; - -// Common widgets with hide -export '../common/widgets/widgets.dart'; - -// Screens entry points -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'components/workflow_builder_screen.dart'; - -/// A wrapper widget that provides an entry point to the workflow builder -class WorkflowBuilderEntry extends ConsumerWidget { - const WorkflowBuilderEntry({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return const WorkflowBuilderScreen(); - } -} From f2fdd1aa63e1adc7abd84e2f2acc035989e74162 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 19 Apr 2025 18:06:59 +0530 Subject: [PATCH 163/188] refactor: add fake data provider and stress test files with exports --- .../lib/src/fake_data_provider/fake_data_provider.dart | 7 +++++++ .../lib/src/stress_test/stress_test.dart | 8 ++++++++ 2 files changed, 15 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/fake_data_provider/fake_data_provider.dart create mode 100644 packages/api_testing_suite/lib/src/stress_test/stress_test.dart diff --git a/packages/api_testing_suite/lib/src/fake_data_provider/fake_data_provider.dart b/packages/api_testing_suite/lib/src/fake_data_provider/fake_data_provider.dart new file mode 100644 index 000000000..e19837e3f --- /dev/null +++ b/packages/api_testing_suite/lib/src/fake_data_provider/fake_data_provider.dart @@ -0,0 +1,7 @@ + +// models +export 'models/fake_data_config.dart'; +// services +export 'services/fake_data_provider.dart'; +// widgets +export 'widgets/fake_data_pane.dart'; diff --git a/packages/api_testing_suite/lib/src/stress_test/stress_test.dart b/packages/api_testing_suite/lib/src/stress_test/stress_test.dart new file mode 100644 index 000000000..98d8df916 --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/stress_test.dart @@ -0,0 +1,8 @@ +// Models +export 'models/stress_test_models.dart'; + +// Services +export 'services/stress_test_service.dart'; + +// Widgets +export 'widgets/stress_test_result_card.dart'; \ No newline at end of file From b4f6269584f9de36a737dc9618f456ab2d685e57 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 19 Apr 2025 18:09:49 +0530 Subject: [PATCH 164/188] refactor: update api_testing_suite.dart --- .../api_testing_suite/lib/api_testing_suite.dart | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/api_testing_suite/lib/api_testing_suite.dart b/packages/api_testing_suite/lib/api_testing_suite.dart index 89119820b..e527ff2d6 100644 --- a/packages/api_testing_suite/lib/api_testing_suite.dart +++ b/packages/api_testing_suite/lib/api_testing_suite.dart @@ -2,13 +2,9 @@ library api_testing_suite; // Workflow Builder Feature export 'src/workflow_builder/workflow_builder.dart'; -export 'src/workflow_builder/workflow_screens.dart'; -// Stress Test Components -export 'src/stress_test/widgets/stress_test_result_card.dart'; -export 'src/stress_test/models/stress_test_config.dart'; -export 'src/stress_test/models/stress_test_summary.dart'; +// Stress Test Feature +export 'src/stress_test/stress_test.dart'; -// Fake Data Provider Components -export 'src/fake_data_provider/widgets/fake_data_pane.dart'; -export 'src/fake_data_provider/models/fake_data_config.dart'; +// Fake Data Provider Feature +export 'src/fake_data_provider/fake_data_provider.dart'; From 5c9c35d1edf6650b6f10e6b080120856314d8855 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 19 Apr 2025 19:56:23 +0530 Subject: [PATCH 165/188] refactor: remove unused stress test service file --- .../stress_test/stress_test_service.dart | 278 ------------------ 1 file changed, 278 deletions(-) delete mode 100644 lib/services/stress_test/stress_test_service.dart diff --git a/lib/services/stress_test/stress_test_service.dart b/lib/services/stress_test/stress_test_service.dart deleted file mode 100644 index 7038bed7e..000000000 --- a/lib/services/stress_test/stress_test_service.dart +++ /dev/null @@ -1,278 +0,0 @@ -// import 'dart:async'; -// import 'dart:convert'; -// import 'dart:isolate'; - -// import 'package:http/http.dart' as http; - -// import 'package:apidash/models/stress_test/api_request_result.dart'; -// import 'package:apidash/models/stress_test/stress_test_config.dart'; -// import 'package:apidash/models/stress_test/stress_test_summary.dart'; -// import 'package:apidash/models/stress_test/isolate_message.dart'; - -// // Stress Test Service Class -// class StressTestService { - -// static Future _executeRequest({ -// required String url, -// required String method, -// Map? headers, -// dynamic body, -// Duration? timeout, -// }) async { -// final stopwatch = Stopwatch()..start(); -// final client = http.Client(); - -// try { -// http.Response response; -// final defaultTimeout = const Duration(seconds: 30); - -// try { -// switch (method.toUpperCase()) { -// case 'GET': -// response = await client.get( -// Uri.parse(url), -// headers: headers, -// ).timeout(timeout ?? defaultTimeout); -// case 'POST': -// response = await client.post( -// Uri.parse(url), -// headers: headers, -// body: body is String ? body : jsonEncode(body), -// ).timeout(timeout ?? defaultTimeout); -// case 'PUT': -// response = await client.put( -// Uri.parse(url), -// headers: headers, -// body: body is String ? body : jsonEncode(body), -// ).timeout(timeout ?? defaultTimeout); -// case 'DELETE': -// response = await client.delete( -// Uri.parse(url), -// headers: headers, -// body: body is String ? body : jsonEncode(body), -// ).timeout(timeout ?? defaultTimeout); -// case 'PATCH': -// response = await client.patch( -// Uri.parse(url), -// headers: headers, -// body: body is String ? body : jsonEncode(body), -// ).timeout(timeout ?? defaultTimeout); -// default: -// throw Exception('Unsupported HTTP method: $method'); -// } - -// stopwatch.stop(); -// return ApiRequestResult( -// statusCode: response.statusCode, -// body: response.body, -// duration: stopwatch.elapsed, -// error: null, -// ); -// } on TimeoutException { -// stopwatch.stop(); -// return ApiRequestResult( -// statusCode: -1, -// body: '', -// duration: stopwatch.elapsed, -// error: 'Request timed out after ${(timeout ?? defaultTimeout).inSeconds} seconds', -// ); -// } on http.ClientException catch (e) { -// stopwatch.stop(); -// return ApiRequestResult( -// statusCode: -1, -// body: '', -// duration: stopwatch.elapsed, -// error: 'HTTP client error: ${e.message}', -// ); -// } on FormatException catch (e) { -// stopwatch.stop(); -// return ApiRequestResult( -// statusCode: -1, -// body: '', -// duration: stopwatch.elapsed, -// error: 'Format error: ${e.message}', -// ); -// } catch (e) { -// stopwatch.stop(); -// return ApiRequestResult( -// statusCode: -1, -// body: '', -// duration: stopwatch.elapsed, -// error: e.toString(), -// ); -// } -// } finally { -// client.close(); -// } -// } - -// /// Isolate worker function -// static Future _isolateWorker(SendPort sendPort) async { -// final receivePort = ReceivePort(); -// sendPort.send(receivePort.sendPort); - -// await for (final message in receivePort) { -// if (message is IsolateMessage) { -// final result = await _executeRequest( -// url: message.url, -// method: message.method, -// headers: message.headers, -// body: message.body, -// timeout: message.timeout, -// ); -// sendPort.send(result); -// } else if (message == 'close') { -// break; -// } -// } - -// Isolate.exit(); -// } - -// /// Execute a parallel API test -// static Future runTest(StressTestConfig config) async { -// final totalStopwatch = Stopwatch()..start(); -// final List results = []; - -// if (config.useIsolates) { -// final isolates = []; -// final receivePorts = []; -// final sendPorts = []; -// final completers = >[]; - -// try { -// for (var i = 0; i < config.concurrentRequests; i++) { -// final receivePort = ReceivePort(); -// final completer = Completer(); - -// final isolate = await Isolate.spawn( -// _isolateWorker, -// receivePort.sendPort, -// errorsAreFatal: false, -// onExit: receivePort.sendPort, -// onError: receivePort.sendPort, -// ); - -// isolates.add(isolate); -// receivePorts.add(receivePort); -// completers.add(completer); - -// receivePort.listen((message) { -// if (message is SendPort) { -// sendPorts.add(message); -// if (sendPorts.length == config.concurrentRequests) { -// for (var j = 0; j < config.concurrentRequests; j++) { -// sendPorts[j].send(IsolateMessage( -// url: config.url, -// method: config.method, -// headers: config.headers, -// body: config.body, -// timeout: config.timeout, -// )); -// } -// } -// } else if (message is ApiRequestResult) { -// if (!completer.isCompleted) { -// completer.complete(message); -// } -// } else if (message is List && message.length >= 2) { -// if (!completer.isCompleted) { -// completer.complete(ApiRequestResult( -// statusCode: -1, -// body: '', -// duration: Duration.zero, -// error: 'Isolate error: ${message[0]}', -// )); -// } -// } -// }); -// } - -// for (var completer in completers) { -// try { -// final result = await completer.future.timeout( -// (config.timeout ?? const Duration(seconds: 30)) + const Duration(seconds: 10) -// ); -// results.add(result); -// } on TimeoutException { -// results.add(ApiRequestResult( -// statusCode: -1, -// body: '', -// duration: Duration.zero, -// error: 'Isolate communication timed out', -// )); -// } -// } -// } finally { -// for (var i = 0; i < isolates.length; i++) { -// try { -// if (sendPorts.length > i) { -// sendPorts[i].send('close'); -// } -// isolates[i].kill(priority: Isolate.immediate); -// } catch (_) { /*We need to ignore once termination is done*/ } -// } - -// for (var port in receivePorts) { -// port.close(); -// } -// } -// } else { -// final futures = >[]; - -// for (int i = 0; i < config.concurrentRequests; i++) { -// futures.add(_executeRequest( -// url: config.url, -// method: config.method, -// headers: config.headers, -// body: config.body, -// timeout: config.timeout, -// )); -// } - -// try { -// final overallTimeout = (config.timeout ?? const Duration(seconds: 30)) + const Duration(seconds: 10); -// final futureResults = await Future.wait(futures).timeout(overallTimeout); -// results.addAll(futureResults); -// } on TimeoutException { -// final completedResults = results.length; -// final remainingCount = config.concurrentRequests - completedResults; - -// for (int i = 0; i < remainingCount; i++) { -// results.add(ApiRequestResult( -// statusCode: -1, -// body: '', -// duration: Duration.zero, -// error: 'Operation timed out', -// )); -// } -// } -// } - -// totalStopwatch.stop(); - -// final successCount = results.where((r) => -// r.statusCode >= 200 && r.statusCode < 300 && r.error == null -// ).length; - -// final failureCount = results.length - successCount; - -// final validResults = results.where((r) => r.error == null); -// final totalResponseTime = validResults.fold( -// 0, -// (prev, result) => prev + result.duration.inMicroseconds -// ); - -// final avgResponseTime = validResults.isEmpty -// ? 0.0 -// : totalResponseTime / validResults.length / 1000; - -// return StressTestSummary( -// results: results, -// totalDuration: totalStopwatch.elapsed, -// avgResponseTime: avgResponseTime, -// successCount: successCount, -// failureCount: failureCount, -// ); -// } -// } From d42ebad57ff24c349da8ce999e53cdca00b11e93 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 19 Apr 2025 20:32:21 +0530 Subject: [PATCH 166/188] docs: add initial README.md for API Testing Suite documentation --- .../api_testing_suite/lib/src/docs/README.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/docs/README.md diff --git a/packages/api_testing_suite/lib/src/docs/README.md b/packages/api_testing_suite/lib/src/docs/README.md new file mode 100644 index 000000000..5a7aad647 --- /dev/null +++ b/packages/api_testing_suite/lib/src/docs/README.md @@ -0,0 +1,50 @@ +# API Testing Suite Documentation + +## Project Structure + +``` +lib/ +├── src/ +│ ├── workflow_builder/ # Workflow builder UI and logic +│ ├── stress_test/ # Stress testing functionality +│ ├── fake_data_provider/ # Fake data generation utilities +│ ├── lib/ # Core source code +│ ├── tests/ # Unit and integration tests +│ └── docs/ # Documentation +└── api_testing_suite.dart # Package entry point +``` + +## Workflow Builder +Contains the UI components and logic for building API workflows: +- `workflow_canvas.dart`: Main canvas widget +- `workflow_node.dart`: Individual workflow nodes +- `workflow_connection.dart`: Connection logic between nodes +- `workflow_screens.dart`: Screen management + +## Stress Test +(To be implemented) +Will contain: +- Stress test execution engine +- Performance metrics collection +- Test result logging +- Configuration management + +## Fake Data Provider +(To be implemented) +Will contain: +- Data generation utilities +- Service classes for different data types +- Configuration options +- Validation utilities + +## Testing +All modules should have corresponding test files in the `tests/` directory. Follow the pattern: +- `workflow_builder_test.dart` +- `stress_test_test.dart` +- `fake_data_provider_test.dart` + +## Contributing +1. Follow the existing code style and patterns +2. Add tests for new features +3. Update documentation when making changes +4. Maintain separation of concerns between modules From dccc4a935eb64df5e5ee4153563c53d37be87b81 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 19 Apr 2025 21:55:18 +0530 Subject: [PATCH 167/188] refactor: streamline processFakeDataTag method using a registry for tag mapping --- .../services/fake_data_provider.dart | 51 +++++++++---------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart b/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart index 6c5b84e7b..5fa17eff7 100644 --- a/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart +++ b/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart @@ -111,34 +111,29 @@ class FakeDataProvider { return '{"id": ${randomId()}, "name": "${randomName()}", "email": "${randomEmail()}", "active": ${randomBoolean()}}'; } + /// Registry for mapping tags to fake data generator functions. + static final Map _tagRegistry = { + 'randomusername': randomUsername, + 'randomemail': randomEmail, + 'randomid': randomId, + 'randomuuid': randomUuid, + 'randomname': randomName, + 'randomphone': randomPhone, + 'randomaddress': randomAddress, + 'randomdate': randomDate, + 'randomdatetime': randomDateTime, + 'randomboolean': randomBoolean, + 'randomnumber': randomNumber, + 'randomjson': randomJson, + }; + + /// Processes a fake data tag and returns generated data. + /// + /// Example: processFakeDataTag('randomEmail') -> 'jane123@example.com' + /// Returns the tag itself in {{tag}} format if not found. static String processFakeDataTag(String tag) { - switch (tag.toLowerCase()) { - case 'randomusername': - return randomUsername(); - case 'randomemail': - return randomEmail(); - case 'randomid': - return randomId(); - case 'randomuuid': - return randomUuid(); - case 'randomname': - return randomName(); - case 'randomphone': - return randomPhone(); - case 'randomaddress': - return randomAddress(); - case 'randomdate': - return randomDate(); - case 'randomdatetime': - return randomDateTime(); - case 'randomboolean': - return randomBoolean(); - case 'randomnumber': - return randomNumber(); - case 'randomjson': - return randomJson(); - default: - return '{{$tag}}'; - } + final key = tag.toLowerCase(); + final generator = _tagRegistry[key]; + return generator != null ? generator() : '{{$tag}}'; } } From a1f5ab5b37ccb50587c441086f4e8297e5baa20d Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sat, 19 Apr 2025 21:59:06 +0530 Subject: [PATCH 168/188] fix: remove opacity(deprecated) --- .../lib/src/common/widgets/draggable_component.dart | 6 +++--- .../lib/src/common/widgets/status_indicator.dart | 4 ++-- .../lib/src/common/widgets/toggle_action_button.dart | 2 +- .../components/canvas/canvas_connection_layer.dart | 2 +- .../workflow_builder/components/canvas/canvas_controls.dart | 4 ++-- .../components/canvas/canvas_info_card.dart | 2 +- .../components/canvas/canvas_node_layer.dart | 2 +- .../src/workflow_builder/components/canvas/canvas_view.dart | 2 +- .../lib/src/workflow_builder/widgets/logs_viewer.dart | 6 +++--- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/api_testing_suite/lib/src/common/widgets/draggable_component.dart b/packages/api_testing_suite/lib/src/common/widgets/draggable_component.dart index 6be8709eb..cda09accd 100644 --- a/packages/api_testing_suite/lib/src/common/widgets/draggable_component.dart +++ b/packages/api_testing_suite/lib/src/common/widgets/draggable_component.dart @@ -37,15 +37,15 @@ class DraggableComponent extends StatelessWidget { onDragEnd: onDragEnd, onDraggableCanceled: (_, __) => onDraggableCanceled?.call(), feedback: feedback ?? _buildDefaultFeedback(context), - childWhenDragging: childWhenDragging ?? - Opacity(opacity: 0.5, child: child), + childWhenDragging: + childWhenDragging ?? Opacity(opacity: 0.5, child: child), child: child, ); } Widget _buildDefaultFeedback(BuildContext context) { if (!useDefaultFeedback && feedback != null) return feedback!; - + return Material( color: Colors.transparent, elevation: 4, diff --git a/packages/api_testing_suite/lib/src/common/widgets/status_indicator.dart b/packages/api_testing_suite/lib/src/common/widgets/status_indicator.dart index 97ace4a88..0320e659e 100644 --- a/packages/api_testing_suite/lib/src/common/widgets/status_indicator.dart +++ b/packages/api_testing_suite/lib/src/common/widgets/status_indicator.dart @@ -22,9 +22,9 @@ class StatusIndicator extends StatelessWidget { return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), decoration: BoxDecoration( - color: color.withOpacity(0.1), + color: Color(0xff171433).withValues(alpha: 0.1), borderRadius: BorderRadius.circular(16), - border: Border.all(color: color.withOpacity(0.3)), + border: Border.all(color: Color(0xff171433).withValues(alpha: 0.1)), ), child: Row( mainAxisSize: MainAxisSize.min, diff --git a/packages/api_testing_suite/lib/src/common/widgets/toggle_action_button.dart b/packages/api_testing_suite/lib/src/common/widgets/toggle_action_button.dart index a4e385700..5be22a83b 100644 --- a/packages/api_testing_suite/lib/src/common/widgets/toggle_action_button.dart +++ b/packages/api_testing_suite/lib/src/common/widgets/toggle_action_button.dart @@ -41,7 +41,7 @@ class ToggleActionButton extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: isActive - ? effectiveActiveColor.withOpacity(0.1) + ? Color(0xff171433).withValues(alpha: 0.1) : Colors.transparent, borderRadius: BorderRadius.circular(4), border: Border.all( diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_connection_layer.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_connection_layer.dart index fc025338e..ae6e7b371 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_connection_layer.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_connection_layer.dart @@ -93,7 +93,7 @@ class TemporaryConnectionPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() - ..color = Colors.blue.withOpacity(0.6) + ..color = Color(0xff171433).withValues(alpha: 0.1) ..strokeWidth = 2.0 ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round; diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_controls.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_controls.dart index 9d023bedd..97eef3031 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_controls.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_controls.dart @@ -19,7 +19,7 @@ class CanvasControls extends StatelessWidget { @override Widget build(BuildContext context) { return Card( - color: Colors.black.withOpacity(0.7), + color: Color(0xff171433).withValues(alpha: 0.1), child: Padding( padding: const EdgeInsets.all(8.0), child: Row( @@ -38,7 +38,7 @@ class CanvasControls extends StatelessWidget { Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( - color: Colors.blue.withOpacity(0.2), + color: Color(0xff171433).withValues(alpha: 0.1), borderRadius: BorderRadius.circular(4), ), child: Text( diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_info_card.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_info_card.dart index d2418da44..71a0fe31b 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_info_card.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_info_card.dart @@ -8,7 +8,7 @@ class CanvasInfoCard extends StatelessWidget { @override Widget build(BuildContext context) { return Card( - color: Colors.black.withOpacity(0.7), + color: Color(0xff171433).withValues(alpha: 0.1), child: Padding( padding: const EdgeInsets.all(12.0), child: Column( diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart index 703400707..d4dc310c6 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart @@ -100,7 +100,7 @@ class CanvasNodeLayer extends ConsumerWidget { borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.1), + color: Color(0xff171433).withValues(alpha: 0.1), spreadRadius: 1, blurRadius: 3, offset: const Offset(0, 2), diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_view.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_view.dart index 6eb7d01be..6a438b068 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_view.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_view.dart @@ -66,7 +66,7 @@ class _CanvasViewState extends ConsumerState child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( - color: Colors.purple.withOpacity(0.8), + color: Color(0xff171433).withValues(alpha: 0.1), borderRadius: BorderRadius.circular(20), ), child: Row( diff --git a/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart b/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart index 1a125b84a..9c0f49aeb 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/widgets/logs_viewer.dart @@ -64,7 +64,7 @@ class LogsViewer extends ConsumerWidget { Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( - color: Colors.blue.withOpacity(0.2), + color: Color(0xff171433).withValues(alpha: 0.1), borderRadius: BorderRadius.circular(4), border: Border.all(color: Colors.blue, width: 1), ), @@ -95,7 +95,7 @@ class LogsViewer extends ConsumerWidget { Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( - color: Colors.green.withOpacity(0.2), + color: Color(0xff171433).withValues(alpha: 0.1), borderRadius: BorderRadius.circular(4), border: Border.all(color: Colors.green, width: 1), ), @@ -240,7 +240,7 @@ class LogsViewer extends ConsumerWidget { Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: Colors.black.withOpacity(0.3), + color: Color(0xff171433).withValues(alpha: 0.1), borderRadius: BorderRadius.circular(4), border: Border.all( color: Colors.grey[850]!, From 4aacea746274417a0962a7a6d55f779190ffa27a Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sun, 20 Apr 2025 16:20:55 +0530 Subject: [PATCH 169/188] refactor: implement IsolateWorker and RequestExecutor for handling API requests in StressTestService --- .../stress_test/services/isolate_worker.dart | 26 ++++ .../services/request_executor.dart | 102 +++++++++++++ .../services/stress_test_service.dart | 134 +----------------- 3 files changed, 132 insertions(+), 130 deletions(-) create mode 100644 packages/api_testing_suite/lib/src/stress_test/services/isolate_worker.dart create mode 100644 packages/api_testing_suite/lib/src/stress_test/services/request_executor.dart diff --git a/packages/api_testing_suite/lib/src/stress_test/services/isolate_worker.dart b/packages/api_testing_suite/lib/src/stress_test/services/isolate_worker.dart new file mode 100644 index 000000000..bf90bd89b --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/services/isolate_worker.dart @@ -0,0 +1,26 @@ +import 'dart:isolate'; +import '../models/isolate_message.dart'; + +import 'request_executor.dart'; + +class IsolateWorker { + static Future worker(SendPort sendPort) async { + final receivePort = ReceivePort(); + sendPort.send(receivePort.sendPort); + await for (final message in receivePort) { + if (message is IsolateMessage) { + final result = await RequestExecutor.execute( + url: message.url, + method: message.method, + headers: message.headers, + body: message.body, + timeout: message.timeout, + ); + sendPort.send(result); + } else if (message == 'close') { + break; + } + } + Isolate.exit(); + } +} diff --git a/packages/api_testing_suite/lib/src/stress_test/services/request_executor.dart b/packages/api_testing_suite/lib/src/stress_test/services/request_executor.dart new file mode 100644 index 000000000..6fc060d22 --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/services/request_executor.dart @@ -0,0 +1,102 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:http/http.dart' as http; +import '../models/api_request_result.dart'; + +class RequestExecutor { + static Future execute({ + required String url, + required String method, + Map? headers, + dynamic body, + Duration? timeout, + }) async { + final stopwatch = Stopwatch()..start(); + final client = http.Client(); + try { + http.Response response; + final defaultTimeout = const Duration(seconds: 30); + try { + switch (method.toUpperCase()) { + case 'GET': + response = await client.get( + Uri.parse(url), + headers: headers, + ).timeout(timeout ?? defaultTimeout); + break; + case 'POST': + response = await client.post( + Uri.parse(url), + headers: headers, + body: body is String ? body : jsonEncode(body), + ).timeout(timeout ?? defaultTimeout); + break; + case 'PUT': + response = await client.put( + Uri.parse(url), + headers: headers, + body: body is String ? body : jsonEncode(body), + ).timeout(timeout ?? defaultTimeout); + break; + case 'DELETE': + response = await client.delete( + Uri.parse(url), + headers: headers, + body: body is String ? body : jsonEncode(body), + ).timeout(timeout ?? defaultTimeout); + break; + case 'PATCH': + response = await client.patch( + Uri.parse(url), + headers: headers, + body: body is String ? body : jsonEncode(body), + ).timeout(timeout ?? defaultTimeout); + break; + default: + throw Exception('Unsupported HTTP method: $method'); + } + stopwatch.stop(); + return ApiRequestResult( + statusCode: response.statusCode, + body: response.body, + duration: stopwatch.elapsed, + error: null, + ); + } on TimeoutException { + stopwatch.stop(); + return ApiRequestResult( + statusCode: -1, + body: '', + duration: stopwatch.elapsed, + error: 'Request timed out after [${(timeout ?? defaultTimeout).inSeconds}[ seconds', + ); + } on http.ClientException catch (e) { + stopwatch.stop(); + return ApiRequestResult( + statusCode: -1, + body: '', + duration: stopwatch.elapsed, + error: 'HTTP client error: ${e.message}', + ); + } on FormatException catch (e) { + stopwatch.stop(); + return ApiRequestResult( + statusCode: -1, + body: '', + duration: stopwatch.elapsed, + error: 'Format error: ${e.message}', + ); + } catch (e) { + stopwatch.stop(); + return ApiRequestResult( + statusCode: -1, + body: '', + duration: stopwatch.elapsed, + error: e.toString(), + ); + } + } finally { + client.close(); + } + } +} diff --git a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart index 6bba4af78..b1833b78c 100644 --- a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart +++ b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart @@ -1,139 +1,13 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:isolate'; - -import 'package:http/http.dart' as http; - import '../models/api_request_result.dart'; import '../models/stress_test_config.dart'; import '../models/stress_test_summary.dart'; import '../models/isolate_message.dart'; +import 'request_executor.dart'; +import 'isolate_worker.dart'; -// Stress Test Service Class class StressTestService { - - static Future _executeRequest({ - required String url, - required String method, - Map? headers, - dynamic body, - Duration? timeout, - }) async { - final stopwatch = Stopwatch()..start(); - final client = http.Client(); - - try { - http.Response response; - final defaultTimeout = const Duration(seconds: 30); - - try { - switch (method.toUpperCase()) { - case 'GET': - response = await client.get( - Uri.parse(url), - headers: headers, - ).timeout(timeout ?? defaultTimeout); - break; - case 'POST': - response = await client.post( - Uri.parse(url), - headers: headers, - body: body is String ? body : jsonEncode(body), - ).timeout(timeout ?? defaultTimeout); - break; - case 'PUT': - response = await client.put( - Uri.parse(url), - headers: headers, - body: body is String ? body : jsonEncode(body), - ).timeout(timeout ?? defaultTimeout); - break; - case 'DELETE': - response = await client.delete( - Uri.parse(url), - headers: headers, - body: body is String ? body : jsonEncode(body), - ).timeout(timeout ?? defaultTimeout); - break; - case 'PATCH': - response = await client.patch( - Uri.parse(url), - headers: headers, - body: body is String ? body : jsonEncode(body), - ).timeout(timeout ?? defaultTimeout); - break; - default: - throw Exception('Unsupported HTTP method: $method'); - } - - stopwatch.stop(); - return ApiRequestResult( - statusCode: response.statusCode, - body: response.body, - duration: stopwatch.elapsed, - error: null, - ); - } on TimeoutException { - stopwatch.stop(); - return ApiRequestResult( - statusCode: -1, - body: '', - duration: stopwatch.elapsed, - error: 'Request timed out after ${(timeout ?? defaultTimeout).inSeconds} seconds', - ); - } on http.ClientException catch (e) { - stopwatch.stop(); - return ApiRequestResult( - statusCode: -1, - body: '', - duration: stopwatch.elapsed, - error: 'HTTP client error: ${e.message}', - ); - } on FormatException catch (e) { - stopwatch.stop(); - return ApiRequestResult( - statusCode: -1, - body: '', - duration: stopwatch.elapsed, - error: 'Format error: ${e.message}', - ); - } catch (e) { - stopwatch.stop(); - return ApiRequestResult( - statusCode: -1, - body: '', - duration: stopwatch.elapsed, - error: e.toString(), - ); - } - } finally { - client.close(); - } - } - - /// Isolate worker function - static Future _isolateWorker(SendPort sendPort) async { - final receivePort = ReceivePort(); - sendPort.send(receivePort.sendPort); - - await for (final message in receivePort) { - if (message is IsolateMessage) { - final result = await _executeRequest( - url: message.url, - method: message.method, - headers: message.headers, - body: message.body, - timeout: message.timeout, - ); - sendPort.send(result); - } else if (message == 'close') { - break; - } - } - - Isolate.exit(); - } - /// Execute a parallel API test static Future runTest(StressTestConfig config) async { final totalStopwatch = Stopwatch()..start(); @@ -151,7 +25,7 @@ class StressTestService { final completer = Completer(); final isolate = await Isolate.spawn( - _isolateWorker, + IsolateWorker.worker, receivePort.sendPort, errorsAreFatal: false, onExit: receivePort.sendPort, @@ -226,7 +100,7 @@ class StressTestService { final futures = >[]; for (int i = 0; i < config.concurrentRequests; i++) { - futures.add(_executeRequest( + futures.add(RequestExecutor.execute( url: config.url, method: config.method, headers: config.headers, From 237c199a6091df539508961784125180e407a79d Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sun, 20 Apr 2025 16:22:13 +0530 Subject: [PATCH 170/188] refactor: update FakeDataProvidersPane to dynamically fetch fake data tags from registry --- .../services/fake_data_provider.dart | 137 +++++++++++++----- .../widgets/fake_data_pane.dart | 105 ++++++-------- 2 files changed, 146 insertions(+), 96 deletions(-) diff --git a/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart b/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart index 5fa17eff7..66d483ce2 100644 --- a/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart +++ b/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart @@ -3,83 +3,149 @@ import 'dart:math'; /// A utility class that provides functions to generate fake data for API testing class FakeDataProvider { static final Random _random = Random(); - + static String randomUsername() { - final prefixes = ['user', 'test', 'dev', 'qa', 'admin', 'guest', 'john', 'jane']; + final prefixes = [ + 'user', + 'test', + 'dev', + 'qa', + 'admin', + 'guest', + 'john', + 'jane' + ]; final suffixes = ['123', '2023', '_test', '_dev', '_admin', '_guest']; - + final prefix = prefixes[_random.nextInt(prefixes.length)]; - final suffix = _random.nextInt(10) > 5 ? suffixes[_random.nextInt(suffixes.length)] : ''; - + final suffix = _random.nextInt(10) > 5 + ? suffixes[_random.nextInt(suffixes.length)] + : ''; + return '$prefix$suffix'; } - + static String randomEmail() { - final usernames = ['john', 'jane', 'user', 'test', 'dev', 'admin', 'info', 'support']; - final domains = ['example.com', 'test.com', 'acme.org', 'email.net', 'company.io', 'service.dev']; - + final usernames = [ + 'john', + 'jane', + 'user', + 'test', + 'dev', + 'admin', + 'info', + 'support' + ]; + final domains = [ + 'example.com', + 'test.com', + 'acme.org', + 'email.net', + 'company.io', + 'service.dev' + ]; + final username = usernames[_random.nextInt(usernames.length)]; final randomNum = _random.nextInt(1000); final domain = domains[_random.nextInt(domains.length)]; - + return '$username$randomNum@$domain'; } - + static String randomId() { return _random.nextInt(10000).toString().padLeft(4, '0'); } - + static String randomUuid() { const chars = 'abcdef0123456789'; - final segments = [8, 4, 4, 4, 12]; //standard length of different segments of uuid - + final segments = [ + 8, + 4, + 4, + 4, + 12 + ]; //standard length of different segments of uuid + final uuid = segments.map((segment) { - return List.generate(segment, (_) => chars[_random.nextInt(chars.length)]).join(); + return List.generate(segment, (_) => chars[_random.nextInt(chars.length)]) + .join(); }).join('-'); - + return uuid; } - + static String randomName() { - final firstNames = ['John', 'Jane', 'Michael', 'Emily', 'David', 'Sarah', 'Robert', 'Lisa']; - final lastNames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller', 'Davis', 'Wilson']; - + final firstNames = [ + 'John', + 'Jane', + 'Michael', + 'Emily', + 'David', + 'Sarah', + 'Robert', + 'Lisa' + ]; + final lastNames = [ + 'Smith', + 'Johnson', + 'Williams', + 'Brown', + 'Jones', + 'Miller', + 'Davis', + 'Wilson' + ]; + final firstName = firstNames[_random.nextInt(firstNames.length)]; final lastName = lastNames[_random.nextInt(lastNames.length)]; - + return '$firstName $lastName'; } - + static String randomPhone() { final areaCode = (100 + _random.nextInt(900)).toString(); final firstPart = (100 + _random.nextInt(900)).toString(); final secondPart = (1000 + _random.nextInt(9000)).toString(); - + return '+1-$areaCode-$firstPart-$secondPart'; } - + static String randomAddress() { final streetNumbers = List.generate(100, (i) => (i + 1) * 10); - final streetNames = ['Main St', 'Oak Ave', 'Park Rd', 'Maple Dr', 'Pine Ln', 'Cedar Blvd']; - final cities = ['Springfield', 'Rivertown', 'Lakeside', 'Mountainview', 'Brookfield']; + final streetNames = [ + 'Main St', + 'Oak Ave', + 'Park Rd', + 'Maple Dr', + 'Pine Ln', + 'Cedar Blvd' + ]; + final cities = [ + 'Springfield', + 'Rivertown', + 'Lakeside', + 'Mountainview', + 'Brookfield' + ]; final states = ['CA', 'NY', 'TX', 'FL', 'IL', 'WA']; - final zipCodes = List.generate(90, (i) => 10000 + (i * 1000) + _random.nextInt(999)); - + final zipCodes = + List.generate(90, (i) => 10000 + (i * 1000) + _random.nextInt(999)); + final streetNumber = streetNumbers[_random.nextInt(streetNumbers.length)]; final streetName = streetNames[_random.nextInt(streetNames.length)]; final city = cities[_random.nextInt(cities.length)]; final state = states[_random.nextInt(states.length)]; final zipCode = zipCodes[_random.nextInt(zipCodes.length)]; - + return '$streetNumber $streetName, $city, $state $zipCode'; } static String randomDate() { final now = DateTime.now(); - final randomDays = _random.nextInt(1000) - 500; + final randomDays = _random.nextInt(1000) - 500; final date = now.add(Duration(days: randomDays)); - - return date.toIso8601String().split('T')[0]; + + return date.toIso8601String().split('T')[0]; } static String randomDateTime() { @@ -88,14 +154,14 @@ class FakeDataProvider { final randomHours = _random.nextInt(24); final randomMinutes = _random.nextInt(60); final randomSeconds = _random.nextInt(60); - + final dateTime = now.add(Duration( days: randomDays, hours: randomHours, minutes: randomMinutes, seconds: randomSeconds, )); - + return dateTime.toIso8601String(); } @@ -131,6 +197,9 @@ class FakeDataProvider { /// /// Example: processFakeDataTag('randomEmail') -> 'jane123@example.com' /// Returns the tag itself in {{tag}} format if not found. + /// Public getter for the tag registry. + static Map get tagRegistry => _tagRegistry; + static String processFakeDataTag(String tag) { final key = tag.toLowerCase(); final generator = _tagRegistry[key]; diff --git a/packages/api_testing_suite/lib/src/fake_data_provider/widgets/fake_data_pane.dart b/packages/api_testing_suite/lib/src/fake_data_provider/widgets/fake_data_pane.dart index 5d4194019..f788abaf7 100644 --- a/packages/api_testing_suite/lib/src/fake_data_provider/widgets/fake_data_pane.dart +++ b/packages/api_testing_suite/lib/src/fake_data_provider/widgets/fake_data_pane.dart @@ -67,68 +67,18 @@ class FakeDataProvidersPane extends ConsumerWidget { } Widget _buildFakeDataTable(BuildContext context) { - final fakeDataTags = [ - _FakeDataItem( - name: 'randomUsername', - description: 'Random username (e.g., user123, test_dev)', - example: FakeDataProvider.randomUsername(), - ), - _FakeDataItem( - name: 'randomEmail', - description: 'Random email address', - example: FakeDataProvider.randomEmail(), - ), - _FakeDataItem( - name: 'randomId', - description: 'Random numeric ID', - example: FakeDataProvider.randomId(), - ), - _FakeDataItem( - name: 'randomUuid', - description: 'Random UUID', - example: FakeDataProvider.randomUuid(), - ), - _FakeDataItem( - name: 'randomName', - description: 'Random full name', - example: FakeDataProvider.randomName(), - ), - _FakeDataItem( - name: 'randomPhone', - description: 'Random phone number', - example: FakeDataProvider.randomPhone(), - ), - _FakeDataItem( - name: 'randomAddress', - description: 'Random address', - example: FakeDataProvider.randomAddress(), - ), - _FakeDataItem( - name: 'randomDate', - description: 'Random date (YYYY-MM-DD)', - example: FakeDataProvider.randomDate(), - ), - _FakeDataItem( - name: 'randomDateTime', - description: 'Random date and time (ISO format)', - example: FakeDataProvider.randomDateTime(), - ), - _FakeDataItem( - name: 'randomBoolean', - description: 'Random boolean value (true/false)', - example: FakeDataProvider.randomBoolean(), - ), - _FakeDataItem( - name: 'randomNumber', - description: 'Random number between 0-1000', - example: FakeDataProvider.randomNumber(), - ), - _FakeDataItem( - name: 'randomJson', - description: 'Random JSON object with basic fields', - example: FakeDataProvider.randomJson(), - ), - ]; + // Dynamically fetch tag list from FakeDataProvider's registry + final fakeDataTags = FakeDataProvider.tagRegistry.entries.map((entry) { + final tag = entry.key; + final example = entry.value(); + // For localization, use a lookup or resource map for descriptions + final description = _localizedDescription(tag, context); + return _FakeDataItem( + name: tag, + description: description, + example: example, + ); + }).toList(); return Table( columnWidths: const { @@ -198,6 +148,37 @@ class FakeDataProvidersPane extends ConsumerWidget { ), ); } + + String _localizedDescription(String tag, BuildContext context) { + switch (tag) { + case 'randomusername': + return 'Random username (e.g., user123, test_dev)'; + case 'randomemail': + return 'Random email address'; + case 'randomid': + return 'Random numeric ID'; + case 'randomuuid': + return 'Random UUID'; + case 'randomname': + return 'Random full name'; + case 'randomphone': + return 'Random US phone number'; + case 'randomaddress': + return 'Random US address'; + case 'randomdate': + return 'Random date (ISO 8601)'; + case 'randomdatetime': + return 'Random date & time (ISO 8601)'; + case 'randomboolean': + return 'Random boolean (true/false)'; + case 'randomnumber': + return 'Random number (0-1000)'; + case 'randomjson': + return 'Random JSON object'; + default: + return tag; + } + } } class _FakeDataItem { From 5a436f2d515c2453bf45136ec212c0da33fe37ec Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sun, 20 Apr 2025 16:43:28 +0530 Subject: [PATCH 171/188] feat: add result card components for displaying stress test metrics and status codes --- .../widgets/result_card_percentiles.dart | 55 ++++ .../widgets/result_card_status.dart | 58 ++++ .../widgets/result_card_status_codes.dart | 64 +++++ .../widgets/result_card_summary.dart | 81 ++++++ .../widgets/stress_test_result_card.dart | 262 ++---------------- 5 files changed, 280 insertions(+), 240 deletions(-) create mode 100644 packages/api_testing_suite/lib/src/stress_test/widgets/result_card_percentiles.dart create mode 100644 packages/api_testing_suite/lib/src/stress_test/widgets/result_card_status.dart create mode 100644 packages/api_testing_suite/lib/src/stress_test/widgets/result_card_status_codes.dart create mode 100644 packages/api_testing_suite/lib/src/stress_test/widgets/result_card_summary.dart diff --git a/packages/api_testing_suite/lib/src/stress_test/widgets/result_card_percentiles.dart b/packages/api_testing_suite/lib/src/stress_test/widgets/result_card_percentiles.dart new file mode 100644 index 000000000..9fb302d05 --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/widgets/result_card_percentiles.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:api_testing_suite/src/stress_test/models/api_request_result.dart'; + +class ResultCardPercentiles extends StatelessWidget { + final List results; + const ResultCardPercentiles({super.key, required this.results}); + + @override + Widget build(BuildContext context) { + final successfulResults = results.where((r) => r.error == null).toList(); + if (successfulResults.isEmpty) { + return const Text('No successful responses to analyze'); + } + successfulResults.sort((a, b) => a.duration.compareTo(b.duration)); + final p50Index = (successfulResults.length * 0.5).floor(); + final p75Index = (successfulResults.length * 0.75).floor(); + final p90Index = (successfulResults.length * 0.9).floor(); + final p95Index = (successfulResults.length * 0.95).floor(); + final p99Index = (successfulResults.length * 0.99).floor(); + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Column( + children: [ + _buildPercentileRow(context, 'Min', + '${successfulResults.first.duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P50', + '${successfulResults[p50Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P75', + '${successfulResults[p75Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P90', + '${successfulResults[p90Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P95', + '${successfulResults[p95Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'P99', + '${successfulResults[p99Index].duration.inMilliseconds} ms'), + _buildPercentileRow(context, 'Max', + '${successfulResults.last.duration.inMilliseconds} ms'), + ], + ), + ); + } + + Widget _buildPercentileRow(BuildContext context, String percentile, String value) { + return Tooltip( + message: '$percentile percentile response time', + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(percentile, style: Theme.of(context).textTheme.bodyMedium), + Text(value, style: Theme.of(context).textTheme.bodyMedium), + ], + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/stress_test/widgets/result_card_status.dart b/packages/api_testing_suite/lib/src/stress_test/widgets/result_card_status.dart new file mode 100644 index 000000000..c70dc99a4 --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/widgets/result_card_status.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; + +class ResultCardStatus extends StatelessWidget { + final String label; + final int count; + final int total; + final Color color; + const ResultCardStatus({ + super.key, + required this.label, + required this.count, + required this.total, + required this.color, + }); + + @override + Widget build(BuildContext context) { + final percentage = total > 0 ? (count / total * 100).toStringAsFixed(1) : '0.0'; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 4), + Row( + children: [ + Text( + '$count', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: color, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 4), + Text( + '($percentage%)', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: color, + ), + ), + ], + ), + const SizedBox(height: 4), + ClipRRect( + borderRadius: BorderRadius.circular(2), + child: LinearProgressIndicator( + value: total > 0 ? count / total : 0, + backgroundColor: Colors.grey[400], + valueColor: AlwaysStoppedAnimation(color), + minHeight: 4, + ), + ), + ], + ); + } +} diff --git a/packages/api_testing_suite/lib/src/stress_test/widgets/result_card_status_codes.dart b/packages/api_testing_suite/lib/src/stress_test/widgets/result_card_status_codes.dart new file mode 100644 index 000000000..5aeb05362 --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/widgets/result_card_status_codes.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:api_testing_suite/src/stress_test/models/api_request_result.dart'; + +class ResultCardStatusCodes extends StatelessWidget { + final List results; + const ResultCardStatusCodes({super.key, required this.results}); + + @override + Widget build(BuildContext context) { + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + final statusCodeCounts = {}; + for (final result in results) { + if (result.statusCode != -1) { + statusCodeCounts[result.statusCode] = + (statusCodeCounts[result.statusCode] ?? 0) + 1; + } + } + if (statusCodeCounts.isEmpty) { + return const Text('No status codes to analyze'); + } + final sortedEntries = statusCodeCounts.entries.toList() + ..sort((a, b) => a.key.compareTo(b.key)); + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: sortedEntries.map((entry) { + final statusCode = entry.key; + final count = entry.value; + final isSuccess = statusCode >= 200 && statusCode < 300; + final isRedirect = statusCode >= 300 && statusCode < 400; + final isClientError = statusCode >= 400 && statusCode < 500; + final isServerError = statusCode >= 500; + Color chipColor; + if (isSuccess) { + chipColor = Colors.green; + } else if (isRedirect) { + chipColor = Colors.blue; + } else if (isClientError) { + chipColor = Colors.orange; + } else if (isServerError) { + chipColor = Colors.red; + } else { + chipColor = Colors.grey; + } + return Tooltip( + message: 'Status $statusCode: $count responses', + child: Semantics( + label: 'HTTP status $statusCode, $count responses', + child: Chip( + label: Text('$statusCode ($count)'), + backgroundColor: chipColor.withAlpha(isDarkMode ? 80 : 51), + labelStyle: TextStyle( + color: chipColor.computeLuminance() < 0.5 + ? Colors.white + : chipColor, + ), + ), + ), + ); + }).toList(), + ), + ); + } +} diff --git a/packages/api_testing_suite/lib/src/stress_test/widgets/result_card_summary.dart b/packages/api_testing_suite/lib/src/stress_test/widgets/result_card_summary.dart new file mode 100644 index 000000000..185071fbb --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/widgets/result_card_summary.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; + +class ResultCardSummary extends StatelessWidget { + final int totalRequests; + final double totalDuration; + final double avgResponseTime; + final Color textColor; + const ResultCardSummary({ + super.key, + required this.totalRequests, + required this.totalDuration, + required this.avgResponseTime, + required this.textColor, + }); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: _buildSummaryItem( + context, + 'Total Requests', + '$totalRequests', + Icons.http, + textColor, + ), + ), + Expanded( + child: _buildSummaryItem( + context, + 'Total Duration', + '${totalDuration.toStringAsFixed(2)}s', + Icons.timer, + textColor, + ), + ), + Expanded( + child: _buildSummaryItem( + context, + 'Avg Response Time', + '${avgResponseTime.toStringAsFixed(2)}ms', + Icons.speed, + textColor, + ), + ), + ], + ); + } + + Widget _buildSummaryItem( + BuildContext context, + String label, + String value, + IconData icon, + Color? textColor, + ) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon(icon, size: 14, color: textColor), + const SizedBox(width: 4), + Text( + label, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: textColor, + ), + ), + ], + ), + const SizedBox(height: 4), + Text( + value, + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ); + } +} diff --git a/packages/api_testing_suite/lib/src/stress_test/widgets/stress_test_result_card.dart b/packages/api_testing_suite/lib/src/stress_test/widgets/stress_test_result_card.dart index a72fdecf3..b5dbbb9c0 100644 --- a/packages/api_testing_suite/lib/src/stress_test/widgets/stress_test_result_card.dart +++ b/packages/api_testing_suite/lib/src/stress_test/widgets/stress_test_result_card.dart @@ -1,11 +1,14 @@ import 'package:api_testing_suite/src/stress_test/models/stress_test_models.dart'; import 'package:flutter/material.dart'; +import 'package:api_testing_suite/src/stress_test/widgets/result_card_summary.dart'; +import 'package:api_testing_suite/src/stress_test/widgets/result_card_status.dart'; +import 'package:api_testing_suite/src/stress_test/widgets/result_card_percentiles.dart'; +import 'package:api_testing_suite/src/stress_test/widgets/result_card_status_codes.dart'; class StressTestResultCard extends StatelessWidget { final StressTestSummary summary; - const StressTestResultCard({Key? key, required this.summary}) - : super(key: key); + const StressTestResultCard({super.key, required this.summary}); @override Widget build(BuildContext context) { @@ -26,56 +29,29 @@ class StressTestResultCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Expanded( - child: _buildSummaryItem( - context, - 'Total Requests', - '${summary.results.length}', - Icons.http, - textColor, - ), - ), - Expanded( - child: _buildSummaryItem( - context, - 'Total Duration', - '${(summary.totalDuration.inMilliseconds / 1000).toStringAsFixed(2)}s', - Icons.timer, - textColor, - ), - ), - Expanded( - child: _buildSummaryItem( - context, - 'Avg Response Time', - '${summary.avgResponseTime.toStringAsFixed(2)}ms', - Icons.speed, - textColor, - ), - ), - ], + ResultCardSummary( + totalRequests: summary.results.length, + totalDuration: summary.totalDuration.inMilliseconds / 1000, + avgResponseTime: summary.avgResponseTime, + textColor: textColor!, ), const SizedBox(height: 16), Row( children: [ Expanded( - child: _buildStatusItem( - context, - 'Success', - summary.successCount, - summary.results.length, - successColor!, + child: ResultCardStatus( + label: 'Success', + count: summary.successCount, + total: summary.results.length, + color: successColor!, ), ), Expanded( - child: _buildStatusItem( - context, - 'Failed', - summary.failureCount, - summary.results.length, - errorColor!, + child: ResultCardStatus( + label: 'Failed', + count: summary.failureCount, + total: summary.results.length, + color: errorColor!, ), ), ], @@ -86,211 +62,17 @@ class StressTestResultCard extends StatelessWidget { style: Theme.of(context).textTheme.titleSmall, ), const SizedBox(height: 8), - _buildResponseTimeDistribution(context, summary.results), + ResultCardPercentiles(results: summary.results), const SizedBox(height: 16), Text( 'Status Code Distribution', style: Theme.of(context).textTheme.titleSmall, ), const SizedBox(height: 8), - _buildStatusCodeDistribution(context, summary.results), - ], - ), - ), - ); - } - - Widget _buildSummaryItem( - BuildContext context, - String label, - String value, - IconData icon, - Color? textColor, - ) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon(icon, size: 14, color: textColor), - const SizedBox(width: 4), - Text( - label, - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: textColor, - ), - ), - ], - ), - const SizedBox(height: 4), - Text( - value, - style: Theme.of(context).textTheme.titleMedium, - ), - ], - ); - } - - Widget _buildStatusItem( - BuildContext context, - String label, - int count, - int total, - Color color, - ) { - final percentage = - total > 0 ? (count / total * 100).toStringAsFixed(1) : '0.0'; - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - label, - style: Theme.of(context).textTheme.bodySmall, - ), - const SizedBox(height: 4), - Row( - children: [ - Text( - '$count', - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: color, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(width: 4), - Text( - '($percentage%)', - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: color, - ), - ), + ResultCardStatusCodes(results: summary.results), ], ), - const SizedBox(height: 4), - ClipRRect( - borderRadius: BorderRadius.circular(2), - child: LinearProgressIndicator( - value: total > 0 ? count / total : 0, - backgroundColor: Colors.grey[400], - valueColor: AlwaysStoppedAnimation(color), - minHeight: 4, - ), - ), - ], - ); - } - - Widget _buildResponseTimeDistribution( - BuildContext context, List results) { - final successfulResults = results.where((r) => r.error == null).toList(); - if (successfulResults.isEmpty) { - return const Text('No successful responses to analyze'); - } - - successfulResults.sort((a, b) => a.duration.compareTo(b.duration)); - - final p50Index = (successfulResults.length * 0.5).floor(); - final p75Index = (successfulResults.length * 0.75).floor(); - final p90Index = (successfulResults.length * 0.9).floor(); - final p95Index = (successfulResults.length * 0.95).floor(); - final p99Index = (successfulResults.length * 0.99).floor(); - - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Column( - children: [ - _buildPercentileRow(context, 'Min', - '${successfulResults.first.duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P50', - '${successfulResults[p50Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P75', - '${successfulResults[p75Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P90', - '${successfulResults[p90Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P95', - '${successfulResults[p95Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'P99', - '${successfulResults[p99Index].duration.inMilliseconds} ms'), - _buildPercentileRow(context, 'Max', - '${successfulResults.last.duration.inMilliseconds} ms'), - ], ), ); } - - Widget _buildPercentileRow( - BuildContext context, String percentile, String value) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Row( - children: [ - SizedBox( - width: 40, - child: Text( - percentile, - style: Theme.of(context).textTheme.bodySmall, - ), - ), - const SizedBox(width: 8), - Expanded( - child: Text( - value, - style: Theme.of(context).textTheme.bodyMedium, - ), - ), - ], - ), - ); - } - - Widget _buildStatusCodeDistribution( - BuildContext context, List results) { - final statusCodeCounts = {}; - for (final result in results) { - if (result.statusCode != -1) { - statusCodeCounts[result.statusCode] = - (statusCodeCounts[result.statusCode] ?? 0) + 1; - } - } - - if (statusCodeCounts.isEmpty) { - return const Text('No status codes to analyze'); - } - - final sortedEntries = statusCodeCounts.entries.toList() - ..sort((a, b) => a.key.compareTo(b.key)); - - return Wrap( - spacing: 8, - runSpacing: 8, - children: sortedEntries.map((entry) { - final statusCode = entry.key; - final count = entry.value; - final isSuccess = statusCode >= 200 && statusCode < 300; - final isRedirect = statusCode >= 300 && statusCode < 400; - final isClientError = statusCode >= 400 && statusCode < 500; - final isServerError = statusCode >= 500; - - Color chipColor; - if (isSuccess) { - chipColor = Colors.green; - } else if (isRedirect) { - chipColor = Colors.blue; - } else if (isClientError) { - chipColor = Colors.orange; - } else if (isServerError) { - chipColor = Colors.red; - } else { - chipColor = Colors.grey; - } - - return Chip( - label: Text('$statusCode ($count)'), - backgroundColor: chipColor.withAlpha(51), - labelStyle: TextStyle(color: chipColor), - ); - }).toList(), - ); - } } From 5cc3af53e4c224d9893eba815d8b5dd447201a45 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sun, 20 Apr 2025 22:54:27 +0530 Subject: [PATCH 172/188] refactor: clean debug prints --- .../models/stress_test_summary.g.dart | 30 +++++++++++++++++++ .../canvas/canvas_event_handlers.dart | 11 +++---- .../components/canvas/canvas_node_layer.dart | 7 +---- 3 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 packages/api_testing_suite/lib/src/stress_test/models/stress_test_summary.g.dart diff --git a/packages/api_testing_suite/lib/src/stress_test/models/stress_test_summary.g.dart b/packages/api_testing_suite/lib/src/stress_test/models/stress_test_summary.g.dart new file mode 100644 index 000000000..5bf4d5b48 --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/models/stress_test_summary.g.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stress_test_summary.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$StressTestSummaryImpl _$$StressTestSummaryImplFromJson( + Map json) => + _$StressTestSummaryImpl( + results: (json['results'] as List) + .map((e) => ApiRequestResult.fromJson(e as Map)) + .toList(), + totalDuration: + Duration(microseconds: (json['totalDuration'] as num).toInt()), + avgResponseTime: (json['avgResponseTime'] as num).toDouble(), + successCount: (json['successCount'] as num).toInt(), + failureCount: (json['failureCount'] as num).toInt(), + ); + +Map _$$StressTestSummaryImplToJson( + _$StressTestSummaryImpl instance) => + { + 'results': instance.results, + 'totalDuration': instance.totalDuration.inMicroseconds, + 'avgResponseTime': instance.avgResponseTime, + 'successCount': instance.successCount, + 'failureCount': instance.failureCount, + }; diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart index 6cf487863..7168ce9b5 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart @@ -30,7 +30,7 @@ mixin CanvasEventHandlers on ConsumerState set connectionEnd(Offset? value) => setState(() => _connectionEnd = value); void handleApiNodeAdded(WidgetRef ref, String workflowId, String requestId, Offset position) { - debugPrint('[CanvasEventHandlers] handleApiNodeAdded called with workflowId: $workflowId, requestId: $requestId, position: $position'); + try { final renderBox = context.findRenderObject() as RenderBox; final viewportCenter = transformationController.toScene( @@ -52,16 +52,13 @@ mixin CanvasEventHandlers on ConsumerState ref.read(workflowsNotifierProvider.notifier).addNode(workflowId, nodeModel); - debugPrint('Added node with ID: ${nodeModel.id} at position: ${nodeModel.position}'); + final workflows = ref.read(workflowsNotifierProvider); final workflow = workflows.firstWhere((w) => w.id == workflowId); - debugPrint('Current nodes in workflow: ${workflow.nodes.length}'); - for (final node in workflow.nodes) { - debugPrint('Node ID: ${node.id}, Position: ${node.position}, Label: ${node.label}'); - } + } catch (e) { - debugPrint('Error adding API node: $e'); + } } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart index d4dc310c6..fd2fe7057 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart @@ -34,12 +34,7 @@ class CanvasNodeLayer extends ConsumerWidget { ); final isConnectionMode = ref.watch(connectionModeProvider); - - debugPrint('[CanvasNodeLayer] Rendering nodes. Count: ${workflow.nodes.length}'); - for (final node in workflow.nodes) { - debugPrint('[CanvasNodeLayer] Node ID: ${node.id}, Position: ${node.position}, Label: ${node.label}'); - } - + return Stack( children: workflow.nodes.map((node) { final nodeCenter = Offset( From 7aef727c0d92d3dd1ae793b0ae2a5fed8afca41d Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sun, 20 Apr 2025 22:57:59 +0530 Subject: [PATCH 173/188] refactor: extract method badge styles into CanvasStyles class --- .../components/canvas/canvas_node_layer.dart | 35 +++---------------- .../components/canvas/canvas_styles.dart | 28 +++++++++++++++ 2 files changed, 33 insertions(+), 30 deletions(-) create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_styles.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart index fd2fe7057..7ae27e0eb 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart @@ -1,7 +1,7 @@ import 'package:api_testing_suite/api_testing_suite.dart'; -import 'package:api_testing_suite/src/workflow_builder/providers/providers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'canvas_styles.dart'; class CanvasNodeLayer extends ConsumerWidget { final String workflowId; @@ -205,40 +205,15 @@ class CanvasNodeLayer extends ConsumerWidget { } Widget _buildMethodBadge(String method) { - Color color; - switch (method.toUpperCase()) { - case 'GET': - color = Colors.blue; - break; - case 'POST': - color = Colors.green; - break; - case 'PUT': - color = Colors.orange; - break; - case 'DELETE': - color = Colors.red; - break; - case 'PATCH': - color = Colors.purple; - break; - default: - color = Colors.grey; - } - return Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + padding: CanvasStyles.methodBadgePadding, decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.circular(4), + color: CanvasStyles.methodBadgeColor(method), + borderRadius: BorderRadius.circular(CanvasStyles.methodBadgeBorderRadius), ), child: Text( method.toUpperCase(), - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), + style: CanvasStyles.methodBadgeTextStyle, ), ); } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_styles.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_styles.dart new file mode 100644 index 000000000..192e5e6fb --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_styles.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +class CanvasStyles { + static const EdgeInsets methodBadgePadding = EdgeInsets.symmetric(horizontal: 6, vertical: 2); + static const double methodBadgeBorderRadius = 4; + static const TextStyle methodBadgeTextStyle = TextStyle( + color: Colors.white, + fontSize: 10, + fontWeight: FontWeight.bold, + ); + + static Color methodBadgeColor(String method) { + switch (method.toUpperCase()) { + case 'GET': + return Colors.blue; + case 'POST': + return Colors.green; + case 'PUT': + return Colors.orange; + case 'DELETE': + return Colors.red; + case 'PATCH': + return Colors.purple; + default: + return Colors.grey; + } + } +} From 223fd1033aa0230ad3a2a2584e862718c035fec9 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Sun, 20 Apr 2025 23:02:50 +0530 Subject: [PATCH 174/188] refactor: streamline error handling in StressTestService by using _handleError method --- .../services/stress_test_service.dart | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart index b1833b78c..bad3d6c3c 100644 --- a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart +++ b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart @@ -56,12 +56,7 @@ class StressTestService { } } else if (message is List && message.length >= 2) { if (!completer.isCompleted) { - completer.complete(ApiRequestResult( - statusCode: -1, - body: '', - duration: Duration.zero, - error: 'Isolate error: ${message[0]}', - )); + completer.complete(_handleError('Isolate error: ${message[0]}')); } } }); @@ -74,12 +69,7 @@ class StressTestService { ); results.add(result); } on TimeoutException { - results.add(ApiRequestResult( - statusCode: -1, - body: '', - duration: Duration.zero, - error: 'Isolate communication timed out', - )); + results.add(_handleError('Isolate communication timed out')); } } } finally { @@ -118,12 +108,7 @@ class StressTestService { final remainingCount = config.concurrentRequests - completedResults; for (int i = 0; i < remainingCount; i++) { - results.add(ApiRequestResult( - statusCode: -1, - body: '', - duration: Duration.zero, - error: 'Operation timed out', - )); + results.add(_handleError('Operation timed out')); } } } @@ -154,4 +139,13 @@ class StressTestService { failureCount: failureCount, ); } + + static ApiRequestResult _handleError(String error) { + return ApiRequestResult( + statusCode: -1, + body: '', + duration: Duration.zero, + error: error, + ); + } } From 0d505046a7d356a8bc236c6cb39178f77281fa45 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 21 Apr 2025 01:20:57 +0530 Subject: [PATCH 175/188] refactor: update import statements and use firstWhereOrNull for safer node retrieval --- .../lib/src/workflow_builder/models/workflow_model.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart index 3049f4d4b..283133fda 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart @@ -1,7 +1,7 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:api_testing_suite/api_testing_suite.dart'; +import 'package:apidash_core/apidash_core.dart'; + import 'package:uuid/uuid.dart'; -import 'workflow_node_model.dart'; -import 'workflow_connection_model.dart'; part 'workflow_model.freezed.dart'; part 'workflow_model.g.dart'; @@ -63,7 +63,7 @@ class WorkflowModel with _$WorkflowModel { } WorkflowNodeModel? getNodeById(String nodeId) { - return nodes.firstWhere((node) => node.id == nodeId); + return nodes.firstWhereOrNull((node) => node.id == nodeId); } List getConnectionsFromNode(String nodeId) { From 7a6de0b99b2b9901f2a0e880e17a386993a040cf Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 21 Apr 2025 01:29:08 +0530 Subject: [PATCH 176/188] refactor: enhance createdAt getter to handle various date formats in metadata --- .../workflow_builder/models/workflow_model.dart | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart index 283133fda..7550c2da0 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart @@ -47,7 +47,20 @@ class WorkflowModel with _$WorkflowModel { bool get hasStartNode => startNodeId != null; - get createdAt => null; + DateTime? get createdAt { + if (metadata.containsKey('createdAt')) { + final value = metadata['createdAt']; + if (value is DateTime) return value; + if (value is String) { + try { + return DateTime.parse(value); + } catch (_) { + return null; + } + } + } + return null; + } WorkflowNodeModel? getStartNode() { if (startNodeId == null) return null; From a8e3415d135f504bd6f944c536a753aa950e063a Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 21 Apr 2025 01:29:49 +0530 Subject: [PATCH 177/188] refactor: add StressTestConstants for timeout values and add CanvasUIConstants for node dimensions --- .../lib/src/stress_test/services/stress_test_constants.dart | 5 +++++ .../lib/src/stress_test/services/stress_test_service.dart | 5 +++-- .../components/canvas/canvas_node_layer.dart | 5 +++-- .../components/canvas/canvas_ui_constants.dart | 5 +++++ 4 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 packages/api_testing_suite/lib/src/stress_test/services/stress_test_constants.dart create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_ui_constants.dart diff --git a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_constants.dart b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_constants.dart new file mode 100644 index 000000000..8b3205161 --- /dev/null +++ b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_constants.dart @@ -0,0 +1,5 @@ +/// Timeout and other stress test related constants +class StressTestConstants { + static const Duration defaultTimeout = Duration(seconds: 30); + static const Duration isolateCommunicationTimeout = Duration(seconds: 10); +} diff --git a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart index bad3d6c3c..27fe19ccf 100644 --- a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart +++ b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart @@ -3,6 +3,7 @@ import 'dart:isolate'; import '../models/api_request_result.dart'; import '../models/stress_test_config.dart'; import '../models/stress_test_summary.dart'; +import 'stress_test_constants.dart'; import '../models/isolate_message.dart'; import 'request_executor.dart'; import 'isolate_worker.dart'; @@ -65,7 +66,7 @@ class StressTestService { for (var completer in completers) { try { final result = await completer.future.timeout( - (config.timeout ?? const Duration(seconds: 30)) + const Duration(seconds: 10) + (config.timeout ?? StressTestConstants.defaultTimeout) + StressTestConstants.isolateCommunicationTimeout ); results.add(result); } on TimeoutException { @@ -100,7 +101,7 @@ class StressTestService { } try { - final overallTimeout = (config.timeout ?? const Duration(seconds: 30)) + const Duration(seconds: 10); + final overallTimeout = (config.timeout ?? StressTestConstants.defaultTimeout) + StressTestConstants.isolateCommunicationTimeout; final futureResults = await Future.wait(futures).timeout(overallTimeout); results.addAll(futureResults); } on TimeoutException { diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart index 7ae27e0eb..0bae3468c 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart @@ -2,6 +2,7 @@ import 'package:api_testing_suite/api_testing_suite.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'canvas_styles.dart'; +import 'canvas_ui_constants.dart'; class CanvasNodeLayer extends ConsumerWidget { final String workflowId; @@ -38,8 +39,8 @@ class CanvasNodeLayer extends ConsumerWidget { return Stack( children: workflow.nodes.map((node) { final nodeCenter = Offset( - node.position.dx + 100, - node.position.dy + 40, + node.position.dx + CanvasUIConstants.nodeWidth, + node.position.dy + CanvasUIConstants.nodeHeight, ); return Positioned( diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_ui_constants.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_ui_constants.dart new file mode 100644 index 000000000..99b24baff --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_ui_constants.dart @@ -0,0 +1,5 @@ +/// UI constants for Canvas Node Layer and related components +class CanvasUIConstants { + static const double nodeWidth = 100; + static const double nodeHeight = 40; +} From b8a5d7327952c7962b301a8c8c9338d508a8cc7b Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 21 Apr 2025 01:42:09 +0530 Subject: [PATCH 178/188] refactor: improve error handling by using StateError for not found workflows and nodes --- .../src/stress_test/services/stress_test_service.dart | 11 +++++------ .../components/canvas/canvas_node_layer.dart | 2 +- .../workflow_builder/execution/workflow_executor.dart | 3 +-- .../providers/workflows_notifier.dart | 11 +++++++---- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart index 27fe19ccf..7ff7e0ff2 100644 --- a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart +++ b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart @@ -9,7 +9,6 @@ import 'request_executor.dart'; import 'isolate_worker.dart'; class StressTestService { - /// Execute a parallel API test static Future runTest(StressTestConfig config) async { final totalStopwatch = Stopwatch()..start(); final List results = []; @@ -62,7 +61,7 @@ class StressTestService { } }); } - + for (var completer in completers) { try { final result = await completer.future.timeout( @@ -70,7 +69,7 @@ class StressTestService { ); results.add(result); } on TimeoutException { - results.add(_handleError('Isolate communication timed out')); + results.add(_handleError('Isolate communication timed out', context: 'Completer index: {completers.indexOf(completer)}')); } } } finally { @@ -109,7 +108,7 @@ class StressTestService { final remainingCount = config.concurrentRequests - completedResults; for (int i = 0; i < remainingCount; i++) { - results.add(_handleError('Operation timed out')); + results.add(_handleError('Operation timed out', context: 'Timeout batch fill')); } } } @@ -141,12 +140,12 @@ class StressTestService { ); } - static ApiRequestResult _handleError(String error) { + static ApiRequestResult _handleError(String error, {String? context}) { return ApiRequestResult( statusCode: -1, body: '', duration: Duration.zero, - error: error, + error: context != null ? '[Context: $context] $error' : error, ); } } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart index 0bae3468c..73f2502f4 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart @@ -31,7 +31,7 @@ class CanvasNodeLayer extends ConsumerWidget { final workflows = ref.watch(workflowsNotifierProvider); final workflow = workflows.firstWhere( (w) => w.id == workflowId, - orElse: () => throw Exception('Workflow not found: $workflowId'), + orElse: () => throw StateError('Workflow not found: $workflowId in CanvasNodeLayer'), ); final isConnectionMode = ref.watch(connectionModeProvider); diff --git a/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_executor.dart b/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_executor.dart index 76fee23d4..d1e566a53 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_executor.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_executor.dart @@ -10,7 +10,6 @@ class WorkflowExecutor { WorkflowExecutionState _executionState; - // Dependency and outgoing node maps for efficient traversal final Map> _nodeDependencyMap = {}; final Map> _nodeOutgoingMap = {}; @@ -173,7 +172,7 @@ class WorkflowExecutor { final node = workflow.nodes.firstWhere( (n) => n.id == nodeId, - orElse: () => throw Exception('Node not found: $nodeId'), + orElse: () => throw StateError('Node not found: $nodeId in WorkflowExecutor._executeNextNodes'), ); final dependencies = _nodeDependencyMap[nodeId] ?? []; diff --git a/packages/api_testing_suite/lib/src/workflow_builder/providers/workflows_notifier.dart b/packages/api_testing_suite/lib/src/workflow_builder/providers/workflows_notifier.dart index 3e5af4d50..205fb584e 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/providers/workflows_notifier.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/providers/workflows_notifier.dart @@ -212,10 +212,13 @@ class WorkflowsNotifier extends StateNotifier> { } WorkflowModel? getWorkflow(String workflowId) { - return state.firstWhere( - (workflow) => workflow.id == workflowId, - orElse: () => throw Exception('Workflow not found: $workflowId'), - ); + try { + return state.firstWhere( + (workflow) => workflow.id == workflowId, + ); + } on StateError { + throw StateError('Workflow not found: $workflowId in getWorkflow'); + } } void startExecution( From 96e955a72264338fddd26acc9d8d98a568c9677d Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 21 Apr 2025 01:46:30 +0530 Subject: [PATCH 179/188] refactor: add isolate communication by consolidating receive ports and improving message handling --- .../services/stress_test_service.dart | 79 +++++++++---------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart index 7ff7e0ff2..acdf049be 100644 --- a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart +++ b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart @@ -15,53 +15,52 @@ class StressTestService { if (config.useIsolates) { final isolates = []; - final receivePorts = []; final sendPorts = []; final completers = >[]; - + final mainReceivePort = ReceivePort(); + int sendPortCount = 0; + int resultCount = 0; + try { for (var i = 0; i < config.concurrentRequests; i++) { - final receivePort = ReceivePort(); final completer = Completer(); - + completers.add(completer); final isolate = await Isolate.spawn( - IsolateWorker.worker, - receivePort.sendPort, + IsolateWorker.worker, + mainReceivePort.sendPort, errorsAreFatal: false, - onExit: receivePort.sendPort, - onError: receivePort.sendPort, ); - isolates.add(isolate); - receivePorts.add(receivePort); - completers.add(completer); - - receivePort.listen((message) { - if (message is SendPort) { - sendPorts.add(message); - if (sendPorts.length == config.concurrentRequests) { - for (var j = 0; j < config.concurrentRequests; j++) { - sendPorts[j].send(IsolateMessage( - url: config.url, - method: config.method, - headers: config.headers, - body: config.body, - timeout: config.timeout, - )); - } - } - } else if (message is ApiRequestResult) { - if (!completer.isCompleted) { - completer.complete(message); - } - } else if (message is List && message.length >= 2) { - if (!completer.isCompleted) { - completer.complete(_handleError('Isolate error: ${message[0]}')); + } + + mainReceivePort.listen((message) { + if (message is SendPort) { + sendPorts.add(message); + sendPortCount++; + if (sendPortCount == config.concurrentRequests) { + for (var j = 0; j < config.concurrentRequests; j++) { + sendPorts[j].send(IsolateMessage( + url: config.url, + method: config.method, + headers: config.headers, + body: config.body, + timeout: config.timeout, + )); } } - }); - } - + } else if (message is ApiRequestResult) { + if (resultCount < completers.length && !completers[resultCount].isCompleted) { + completers[resultCount].complete(message); + resultCount++; + } + } else if (message is List && message.length >= 2) { + if (resultCount < completers.length && !completers[resultCount].isCompleted) { + completers[resultCount].complete(_handleError('Isolate error: ${message[0]}')); + resultCount++; + } + } + }); + for (var completer in completers) { try { final result = await completer.future.timeout( @@ -69,7 +68,7 @@ class StressTestService { ); results.add(result); } on TimeoutException { - results.add(_handleError('Isolate communication timed out', context: 'Completer index: {completers.indexOf(completer)}')); + results.add(_handleError('Isolate communication timed out', context: 'Completer index: {completers.indexOf(completer)}')); } } } finally { @@ -79,12 +78,10 @@ class StressTestService { sendPorts[i].send('close'); } isolates[i].kill(priority: Isolate.immediate); - } catch (_) { /*We need to ignore once termination is done*/ } + } catch (_) {} } - for (var port in receivePorts) { - port.close(); - } + } } else { final futures = >[]; From c56f59042b531c6079bcdb71a7aa2bf1b76e564f Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 21 Apr 2025 01:59:44 +0530 Subject: [PATCH 180/188] refactor: make StressTestConfig to include request-specific fields and update StressTestService --- .../models/stress_test_config.dart | 32 ++++++++++++++++--- .../services/stress_test_service.dart | 16 +++++----- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/packages/api_testing_suite/lib/src/stress_test/models/stress_test_config.dart b/packages/api_testing_suite/lib/src/stress_test/models/stress_test_config.dart index ff30a0132..b3b11ad61 100644 --- a/packages/api_testing_suite/lib/src/stress_test/models/stress_test_config.dart +++ b/packages/api_testing_suite/lib/src/stress_test/models/stress_test_config.dart @@ -6,15 +6,39 @@ part 'stress_test_config.g.dart'; @freezed class StressTestConfig with _$StressTestConfig { + const factory StressTestConfig({ - required String url, - required String method, - Map? headers, - dynamic body, + @JsonKey(name: 'url') String? url, + @JsonKey(name: 'method') String? method, + @JsonKey(name: 'headers') Map? headers, + @JsonKey(name: 'body') dynamic body, + @JsonKey(name: 'requestUrl') String? requestUrl, + @JsonKey(name: 'requestMethod') String? requestMethod, + @JsonKey(name: 'requestHeaders') Map? requestHeaders, + @JsonKey(name: 'requestBody') dynamic requestBody, required int concurrentRequests, Duration? timeout, @Default(false) bool useIsolates, }) = _StressTestConfig; + + + + + factory StressTestConfig.fromJson(Map json) => _$StressTestConfigFromJson(json); } + +extension StressTestConfigCompat on StressTestConfig { + String? get requestUrl => (this as dynamic).requestUrl ?? (this as dynamic).url; + String? get requestMethod => (this as dynamic).requestMethod ?? (this as dynamic).method; + Map? get requestHeaders => (this as dynamic).requestHeaders ?? (this as dynamic).headers; + dynamic get requestBody => (this as dynamic).requestBody ?? (this as dynamic).body; + + String get resolvedUrl => requestUrl ?? ''; + String get resolvedMethod => requestMethod ?? ''; + Map? get resolvedHeaders => requestHeaders; + dynamic get resolvedBody => requestBody; +} + + diff --git a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart index acdf049be..73aa9251f 100644 --- a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart +++ b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart @@ -40,10 +40,10 @@ class StressTestService { if (sendPortCount == config.concurrentRequests) { for (var j = 0; j < config.concurrentRequests; j++) { sendPorts[j].send(IsolateMessage( - url: config.url, - method: config.method, - headers: config.headers, - body: config.body, + url: config.resolvedUrl, + method: config.resolvedMethod, + headers: config.resolvedHeaders, + body: config.resolvedBody, timeout: config.timeout, )); } @@ -88,10 +88,10 @@ class StressTestService { for (int i = 0; i < config.concurrentRequests; i++) { futures.add(RequestExecutor.execute( - url: config.url, - method: config.method, - headers: config.headers, - body: config.body, + url: config.resolvedUrl, + method: config.resolvedMethod, + headers: config.resolvedHeaders, + body: config.resolvedBody, timeout: config.timeout, )); } From 6b463d3c51c997dec57c5a588b3c4923a24ece96 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 21 Apr 2025 14:36:01 +0530 Subject: [PATCH 181/188] refactor: update WorkflowModel to initialize createdAt and improve error handling in node retrieval --- .../models/workflow_model.dart | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart index 7550c2da0..f82f8c751 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/models/workflow_model.dart @@ -1,7 +1,7 @@ -import 'package:api_testing_suite/api_testing_suite.dart'; -import 'package:apidash_core/apidash_core.dart'; - +import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:uuid/uuid.dart'; +import 'workflow_node_model.dart'; +import 'workflow_connection_model.dart'; part 'workflow_model.freezed.dart'; part 'workflow_model.g.dart'; @@ -23,11 +23,13 @@ class WorkflowModel with _$WorkflowModel { @Default([]) List activeNodeIds, @Default([]) List completedNodeIds, @Default({}) Map metadata, + DateTime? createdAt, }) = _WorkflowModel; factory WorkflowModel.fromJson(Map json) => _$WorkflowModelFromJson(json); + /// Create a new workflow with a unique ID factory WorkflowModel.create({ required String name, String description = '', @@ -42,29 +44,20 @@ class WorkflowModel with _$WorkflowModel { nodes: nodes, connections: connections, metadata: metadata, + createdAt: DateTime.now(), ); } + /// Returns whether a start node has been set for this workflow bool get hasStartNode => startNodeId != null; - DateTime? get createdAt { - if (metadata.containsKey('createdAt')) { - final value = metadata['createdAt']; - if (value is DateTime) return value; - if (value is String) { - try { - return DateTime.parse(value); - } catch (_) { - return null; - } - } - } - return null; - } - WorkflowNodeModel? getStartNode() { if (startNodeId == null) return null; - return nodes.firstWhere((node) => node.id == startNodeId); + try { + return nodes.firstWhere((node) => node.id == startNodeId); + } catch (e) { + return null; + } } List getActiveNodes() { @@ -75,8 +68,15 @@ class WorkflowModel with _$WorkflowModel { return nodes.where((node) => completedNodeIds.contains(node.id)).toList(); } + /// Find a node by its ID + /// + /// Returns null if the node is not found WorkflowNodeModel? getNodeById(String nodeId) { - return nodes.firstWhereOrNull((node) => node.id == nodeId); + try { + return nodes.firstWhere((node) => node.id == nodeId); + } catch (e) { + return null; + } } List getConnectionsFromNode(String nodeId) { From ff5205b4512010456b6bef040ebc3811eb37b545 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 21 Apr 2025 15:05:39 +0530 Subject: [PATCH 182/188] feat: add ApiTestLogger for centralized logging in the API testing suite --- .../lib/src/common/utils/logger.dart | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 packages/api_testing_suite/lib/src/common/utils/logger.dart diff --git a/packages/api_testing_suite/lib/src/common/utils/logger.dart b/packages/api_testing_suite/lib/src/common/utils/logger.dart new file mode 100644 index 000000000..1bc7964f4 --- /dev/null +++ b/packages/api_testing_suite/lib/src/common/utils/logger.dart @@ -0,0 +1,38 @@ +import 'package:logger/logger.dart'; + +/// A centralized logger for the API testing suite. +/// +/// This provides consistent logging throughout the application with +/// appropriate log levels and formatting. +class ApiTestLogger { + static final Logger _logger = Logger( + printer: PrettyPrinter( + methodCount: 0, + errorMethodCount: 5, + lineLength: 80, + colors: true, + printEmojis: true, + ), + level: Level.debug, + ); + + static void debug(String message, [Object? error, StackTrace? stackTrace]) { + _logger.d(message, error: error, stackTrace: stackTrace); + } + + static void info(String message, [Object? error, StackTrace? stackTrace]) { + _logger.i(message, error: error, stackTrace: stackTrace); + } + + static void warning(String message, [Object? error, StackTrace? stackTrace]) { + _logger.w(message, error: error, stackTrace: stackTrace); + } + + static void error(String message, [Object? error, StackTrace? stackTrace]) { + _logger.e(message, error: error, stackTrace: stackTrace); + } + + static void critical(String message, [Object? error, StackTrace? stackTrace]) { + _logger.f(message, error: error, stackTrace: stackTrace); + } +} From 82a67b341f64dd3c9af9ef0a8b938061ee959235 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 21 Apr 2025 15:07:38 +0530 Subject: [PATCH 183/188] refactor: update CanvasStyles and add WorkflowExecutionConstants --- .../components/canvas/canvas_styles.dart | 79 ++++++++++++++++++- .../canvas/canvas_ui_constants.dart | 31 +++++++- .../workflow_execution_constants.dart | 19 +++++ 3 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_execution_constants.dart diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_styles.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_styles.dart index 192e5e6fb..97ea3ea38 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_styles.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_styles.dart @@ -1,14 +1,63 @@ import 'package:flutter/material.dart'; +import 'canvas_ui_constants.dart'; class CanvasStyles { - static const EdgeInsets methodBadgePadding = EdgeInsets.symmetric(horizontal: 6, vertical: 2); - static const double methodBadgeBorderRadius = 4; + // Method badge styles + static EdgeInsets methodBadgePadding = EdgeInsets.symmetric( + horizontal: CanvasUIConstants.methodBadgePaddingHorizontal, + vertical: CanvasUIConstants.methodBadgePaddingVertical + ); + + static BorderRadius methodBadgeBorderRadius = BorderRadius.circular( + CanvasUIConstants.methodBadgeBorderRadius + ); + static const TextStyle methodBadgeTextStyle = TextStyle( color: Colors.white, - fontSize: 10, + fontSize: CanvasUIConstants.methodBadgeFontSize, + fontWeight: FontWeight.bold, + ); + + // Node styles + static BoxShadow nodeShadow = BoxShadow( + color: const Color(0xff171433).withOpacity(0.1), + spreadRadius: 1, + blurRadius: 3, + offset: const Offset(0, 2), + ); + + static BorderRadius nodeBorderRadius = BorderRadius.circular( + CanvasUIConstants.nodeBorderRadius + ); + + static const TextStyle nodeHeaderTextStyle = TextStyle( + color: Colors.white, fontWeight: FontWeight.bold, ); + + static const TextStyle nodeContentTextStyle = TextStyle( + fontSize: CanvasUIConstants.nodeContentFontSize, + color: Color(0xFF757575), // Colors.grey[700] + ); + + // Header container decorations + static BoxDecoration requestHeaderDecoration = BoxDecoration( + color: Colors.blue.shade700, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(6), + topRight: Radius.circular(6), + ), + ); + + static BoxDecoration processingHeaderDecoration = BoxDecoration( + color: Colors.green.shade700, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(6), + topRight: Radius.circular(6), + ), + ); + // Method-specific colors static Color methodBadgeColor(String method) { switch (method.toUpperCase()) { case 'GET': @@ -25,4 +74,28 @@ class CanvasStyles { return Colors.grey; } } + + // Node border colors + static Color getBorderColor(String nodeId, String? sourceNodeId, bool isConnectionMode) { + if (sourceNodeId == nodeId) { + return Colors.blue; + } + + if (isConnectionMode) { + return Colors.purple; + } + + return Colors.grey.shade300; + } + + // Node background gradients + static List getNodeGradient(String nodeId, String? sourceNodeId, bool isConnectionMode) { + if (sourceNodeId == nodeId) { + return [Colors.blue[300]!, Colors.blue[100]!]; + } else if (isConnectionMode) { + return [Colors.purple[100]!, Colors.purple[50]!]; + } else { + return [Colors.white, const Color(0xFFF5F5F5)]; + } + } } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_ui_constants.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_ui_constants.dart index 99b24baff..23a21b655 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_ui_constants.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_ui_constants.dart @@ -1,5 +1,34 @@ -/// UI constants for Canvas Node Layer and related components class CanvasUIConstants { + // Node dimensions static const double nodeWidth = 100; static const double nodeHeight = 40; + + // Node appearance + static const double nodeElevationDefault = 3; + static const double nodeElevationDragged = 8; + static const double nodeOpacityDefault = 1.0; + static const double nodeOpacityDragged = 0.7; + static const double nodeBorderRadius = 8.0; + static const double nodeBorderWidthDefault = 2.0; + static const double nodeBorderWidthActive = 2.5; + + // Node header + static const double nodeHeaderPaddingHorizontal = 12.0; + static const double nodeHeaderPaddingVertical = 8.0; + static const double nodeHeaderIconSize = 16.0; + static const double nodeHeaderSpacing = 8.0; + + // Node content + static const double nodeContentPadding = 12.0; + static const double methodBadgePaddingHorizontal = 6.0; + static const double methodBadgePaddingVertical = 2.0; + static const double methodBadgeBorderRadius = 4.0; + static const double methodBadgeFontSize = 10.0; + static const double nodeContentFontSize = 12.0; + static const double nodeContentSpacing = 8.0; + + // Connection constants + static const double connectionLineWidth = 2.0; + static const double connectionLineActiveWidth = 3.0; + static const double connectionArrowSize = 8.0; } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_execution_constants.dart b/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_execution_constants.dart new file mode 100644 index 000000000..be75122b9 --- /dev/null +++ b/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_execution_constants.dart @@ -0,0 +1,19 @@ +class WorkflowExecutionConstants { + // Execution timing + static const Duration nodeExecutionDelay = Duration(milliseconds: 500); + static const Duration nextNodeScheduleDelay = Duration(milliseconds: 100); + + // Node execution result keys + static const String resultStatusKey = 'status'; + static const String resultTimestampKey = 'timestamp'; + static const String resultDataKey = 'data'; + static const String resultMessageKey = 'message'; + + // Status values + static const String statusSuccess = 'success'; + static const String statusFailure = 'failure'; + + // Error messages + static const String errorNoStartingNodes = 'No starting nodes found in the workflow'; + static const String errorNodeNotFound = 'Node not found in the workflow'; +} From 4553cc6c15e89307d42c6bf738b883f0362b86e6 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 21 Apr 2025 15:09:34 +0530 Subject: [PATCH 184/188] feat: add centralized ErrorHandler for consistent error handling in API testing suite --- .../lib/src/common/utils/error_handler.dart | 51 +++++++++++++++++++ .../services/stress_test_constants.dart | 25 ++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 packages/api_testing_suite/lib/src/common/utils/error_handler.dart diff --git a/packages/api_testing_suite/lib/src/common/utils/error_handler.dart b/packages/api_testing_suite/lib/src/common/utils/error_handler.dart new file mode 100644 index 000000000..bf33e6799 --- /dev/null +++ b/packages/api_testing_suite/lib/src/common/utils/error_handler.dart @@ -0,0 +1,51 @@ +import 'logger.dart'; + +/// A centralized error handling utility for the API testing suite. +/// +/// This provides consistent error handling patterns with appropriate logging +/// and user feedback mechanisms. +class ErrorHandler { + /// Executes a function safely and handles any exceptions that occur. + /// + /// [operation] - A description of the operation being performed (for logging). + /// [action] - The function to execute. + /// [onError] - Optional callback to handle errors in a custom way. + /// Returns the result of [action] or null if an exception occurred. + static T? execute( + String operation, + T Function() action, { + Function(Exception)? onError, + }) { + try { + return action(); + } on Exception catch (e, stackTrace) { + ApiTestLogger.error('Error during $operation', e, stackTrace); + if (onError != null) { + onError(e); + } + return null; + } + } + + /// Executes an async function safely and handles any exceptions that occur. + /// + /// [operation] - A description of the operation being performed (for logging). + /// [action] - The async function to execute. + /// [onError] - Optional callback to handle errors in a custom way. + /// Returns a Future with the result of [action] or null if an exception occurred. + static Future executeAsync( + String operation, + Future Function() action, { + Function(Exception)? onError, + }) async { + try { + return await action(); + } on Exception catch (e, stackTrace) { + ApiTestLogger.error('Error during $operation', e, stackTrace); + if (onError != null) { + onError(e); + } + return null; + } + } +} diff --git a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_constants.dart b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_constants.dart index 8b3205161..3f02aa3d5 100644 --- a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_constants.dart +++ b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_constants.dart @@ -1,5 +1,28 @@ -/// Timeout and other stress test related constants +/// Constants related to stress testing API endpoints class StressTestConstants { + // Timeout constants static const Duration defaultTimeout = Duration(seconds: 30); static const Duration isolateCommunicationTimeout = Duration(seconds: 10); + static const Duration gracePeriodTimeout = Duration(seconds: 5); + + // Error codes + static const int errorStatusCode = -1; + + // Status codes + static const int successMinStatusCode = 200; + static const int successMaxStatusCode = 299; + + // Default values + static const String defaultErrorMessage = 'Unknown error'; + static const String timeoutErrorMessage = 'Operation timed out'; + static const String isolateTimeoutErrorMessage = 'Isolate communication timed out'; + static const String isolateErrorMessage = 'Isolate error'; + + // Performance metrics + static const double microsToMillisConversion = 1000.0; + + // UI Configuration + static const Duration resultCardAnimationDuration = Duration(milliseconds: 300); + static const double resultCardElevation = 2.0; + static const double resultCardBorderRadius = 8.0; } From 65ba660d6b92450f47b3bbd66aaf591941241e96 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 21 Apr 2025 18:56:55 +0530 Subject: [PATCH 185/188] refactor: update StressTestService for improved isolate handling and logging --- .../services/stress_test_service.dart | 264 +++++++++++------- 1 file changed, 159 insertions(+), 105 deletions(-) diff --git a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart index 73aa9251f..006ea92a6 100644 --- a/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart +++ b/packages/api_testing_suite/lib/src/stress_test/services/stress_test_service.dart @@ -1,148 +1,202 @@ import 'dart:async'; import 'dart:isolate'; +import 'package:api_testing_suite/src/common/utils/logger.dart'; import '../models/api_request_result.dart'; import '../models/stress_test_config.dart'; import '../models/stress_test_summary.dart'; -import 'stress_test_constants.dart'; import '../models/isolate_message.dart'; +import 'stress_test_constants.dart'; import 'request_executor.dart'; import 'isolate_worker.dart'; +/// Service responsible for executing stress tests on API endpoints class StressTestService { + /// Run a stress test according to the provided configuration + /// + /// This method will execute concurrent API requests according to the configuration + /// and return a summary of the results. static Future runTest(StressTestConfig config) async { final totalStopwatch = Stopwatch()..start(); final List results = []; + ApiTestLogger.info('Starting stress test with ${config.concurrentRequests} concurrent requests'); + ApiTestLogger.debug('Using isolates: ${config.useIsolates}'); + if (config.useIsolates) { - final isolates = []; - final sendPorts = []; - final completers = >[]; - final mainReceivePort = ReceivePort(); - int sendPortCount = 0; - int resultCount = 0; - - try { - for (var i = 0; i < config.concurrentRequests; i++) { - final completer = Completer(); - completers.add(completer); - final isolate = await Isolate.spawn( - IsolateWorker.worker, - mainReceivePort.sendPort, - errorsAreFatal: false, - ); - isolates.add(isolate); - } + await _runIsolateTest(config, results); + } else { + await _runDirectTest(config, results); + } - mainReceivePort.listen((message) { - if (message is SendPort) { - sendPorts.add(message); - sendPortCount++; - if (sendPortCount == config.concurrentRequests) { - for (var j = 0; j < config.concurrentRequests; j++) { - sendPorts[j].send(IsolateMessage( - url: config.resolvedUrl, - method: config.resolvedMethod, - headers: config.resolvedHeaders, - body: config.resolvedBody, - timeout: config.timeout, - )); - } - } - } else if (message is ApiRequestResult) { - if (resultCount < completers.length && !completers[resultCount].isCompleted) { - completers[resultCount].complete(message); - resultCount++; - } - } else if (message is List && message.length >= 2) { - if (resultCount < completers.length && !completers[resultCount].isCompleted) { - completers[resultCount].complete(_handleError('Isolate error: ${message[0]}')); - resultCount++; - } - } - }); + totalStopwatch.stop(); + + final successCount = _calculateSuccessCount(results); + final failureCount = results.length - successCount; + final avgResponseTime = _calculateAverageResponseTime(results); + + ApiTestLogger.info('Stress test completed in ${totalStopwatch.elapsed.inMilliseconds}ms'); + ApiTestLogger.info('Success: $successCount, Failures: $failureCount'); + + return StressTestSummary( + results: results, + totalDuration: totalStopwatch.elapsed, + avgResponseTime: avgResponseTime, + successCount: successCount, + failureCount: failureCount, + ); + } - for (var completer in completers) { - try { - final result = await completer.future.timeout( - (config.timeout ?? StressTestConstants.defaultTimeout) + StressTestConstants.isolateCommunicationTimeout - ); - results.add(result); - } on TimeoutException { - results.add(_handleError('Isolate communication timed out', context: 'Completer index: {completers.indexOf(completer)}')); + static Future _runIsolateTest(StressTestConfig config, List results) async { + final isolates = []; + final sendPorts = []; + final completers = >[]; + final mainReceivePort = ReceivePort(); + int sendPortCount = 0; + int resultCount = 0; + + try { + for (var i = 0; i < config.concurrentRequests; i++) { + final completer = Completer(); + completers.add(completer); + + final isolate = await Isolate.spawn( + IsolateWorker.worker, + mainReceivePort.sendPort, + errorsAreFatal: false, + ); + + isolates.add(isolate); + } + + mainReceivePort.listen((message) { + if (message is SendPort) { + sendPorts.add(message); + sendPortCount++; + + if (sendPortCount == config.concurrentRequests) { + _dispatchIsolateRequests(config, sendPorts); + } + } else if (message is ApiRequestResult) { + if (resultCount < completers.length && !completers[resultCount].isCompleted) { + completers[resultCount].complete(message); + resultCount++; + } + } else if (message is List && message.length >= 2) { + if (resultCount < completers.length && !completers[resultCount].isCompleted) { + completers[resultCount].complete(_createErrorResult( + '${StressTestConstants.isolateErrorMessage}: ${message[0]}' + )); + resultCount++; } } - } finally { - for (var i = 0; i < isolates.length; i++) { - try { - if (sendPorts.length > i) { - sendPorts[i].send('close'); - } - isolates[i].kill(priority: Isolate.immediate); - } catch (_) {} - } - + }); + for (var completer in completers) { + try { + final timeout = (config.timeout ?? StressTestConstants.defaultTimeout) + + StressTestConstants.isolateCommunicationTimeout; + + final result = await completer.future.timeout(timeout); + results.add(result); + } on TimeoutException { + results.add(_createErrorResult(StressTestConstants.isolateTimeoutErrorMessage)); + } } - } else { - final futures = >[]; - - for (int i = 0; i < config.concurrentRequests; i++) { - futures.add(RequestExecutor.execute( - url: config.resolvedUrl, - method: config.resolvedMethod, - headers: config.resolvedHeaders, - body: config.resolvedBody, - timeout: config.timeout, - )); - } - + } finally { + _cleanupIsolates(isolates, sendPorts); + mainReceivePort.close(); + } + } + + static void _dispatchIsolateRequests( + StressTestConfig config, + List sendPorts + ) { + for (var j = 0; j < config.concurrentRequests; j++) { + sendPorts[j].send(IsolateMessage( + url: config.resolvedUrl, + method: config.resolvedMethod, + headers: config.resolvedHeaders, + body: config.resolvedBody, + timeout: config.timeout, + )); + } + } + + static void _cleanupIsolates(List isolates, List sendPorts) { + for (var i = 0; i < isolates.length; i++) { try { - final overallTimeout = (config.timeout ?? StressTestConstants.defaultTimeout) + StressTestConstants.isolateCommunicationTimeout; - final futureResults = await Future.wait(futures).timeout(overallTimeout); - results.addAll(futureResults); - } on TimeoutException { - final completedResults = results.length; - final remainingCount = config.concurrentRequests - completedResults; - - for (int i = 0; i < remainingCount; i++) { - results.add(_handleError('Operation timed out', context: 'Timeout batch fill')); + if (sendPorts.length > i) { + sendPorts[i].send('close'); } + isolates[i].kill(priority: Isolate.immediate); + } catch (e) { + ApiTestLogger.debug('Error during isolate cleanup: $e'); } } + } - totalStopwatch.stop(); + static Future _runDirectTest(StressTestConfig config, List results) async { + final futures = >[]; + + for (int i = 0; i < config.concurrentRequests; i++) { + futures.add(RequestExecutor.execute( + url: config.resolvedUrl, + method: config.resolvedMethod, + headers: config.resolvedHeaders, + body: config.resolvedBody, + timeout: config.timeout, + )); + } + + try { + final overallTimeout = (config.timeout ?? StressTestConstants.defaultTimeout) + + StressTestConstants.gracePeriodTimeout; + + final futureResults = await Future.wait(futures).timeout(overallTimeout); + results.addAll(futureResults); + } on TimeoutException { + final completedResults = results.length; + final remainingCount = config.concurrentRequests - completedResults; + + ApiTestLogger.warning('Timeout occurred while waiting for results. ' + + 'Completed: $completedResults, Remaining: $remainingCount'); + + for (int i = 0; i < remainingCount; i++) { + results.add(_createErrorResult(StressTestConstants.timeoutErrorMessage)); + } + } + } - final successCount = results.where((r) => - r.statusCode >= 200 && r.statusCode < 300 && r.error == null + static int _calculateSuccessCount(List results) { + return results.where((r) => + r.statusCode >= StressTestConstants.successMinStatusCode && + r.statusCode <= StressTestConstants.successMaxStatusCode && + r.error == null ).length; + } + + static double _calculateAverageResponseTime(List results) { + final validResults = results.where((r) => r.error == null); - final failureCount = results.length - successCount; + if (validResults.isEmpty) { + return 0.0; + } - final validResults = results.where((r) => r.error == null); final totalResponseTime = validResults.fold( 0, (prev, result) => prev + result.duration.inMicroseconds ); - final avgResponseTime = validResults.isEmpty - ? 0.0 - : totalResponseTime / validResults.length / 1000; - - return StressTestSummary( - results: results, - totalDuration: totalStopwatch.elapsed, - avgResponseTime: avgResponseTime, - successCount: successCount, - failureCount: failureCount, - ); + return totalResponseTime / validResults.length / StressTestConstants.microsToMillisConversion; } - static ApiRequestResult _handleError(String error, {String? context}) { + static ApiRequestResult _createErrorResult(String error) { return ApiRequestResult( - statusCode: -1, + statusCode: StressTestConstants.errorStatusCode, body: '', duration: Duration.zero, - error: context != null ? '[Context: $context] $error' : error, + error: error, ); } } From 6b60744f3ba13f96e945fa8c72f31e41a41b7082 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 21 Apr 2025 19:00:11 +0530 Subject: [PATCH 186/188] refactor: update error handling and logging in canvas event handlers and workflow executor --- .../canvas/canvas_event_handlers.dart | 70 +++++----- .../components/canvas/canvas_node_layer.dart | 91 +++++------- .../execution/workflow_executor.dart | 130 +++++++++++++----- 3 files changed, 165 insertions(+), 126 deletions(-) diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart index 7168ce9b5..9f4c6f8b0 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_event_handlers.dart @@ -1,10 +1,12 @@ +import 'package:api_testing_suite/src/common/utils/error_handler.dart'; +import 'package:api_testing_suite/src/common/utils/logger.dart'; import 'package:api_testing_suite/src/workflow_builder/providers/providers.dart'; import '../../models/workflow_connection_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../models/workflow_node_model.dart'; - +/// A mixin that provides canvas event handling capabilities to a ConsumerStatefulWidget mixin CanvasEventHandlers on ConsumerState { TransformationController get transformationController; @@ -30,8 +32,7 @@ mixin CanvasEventHandlers on ConsumerState set connectionEnd(Offset? value) => setState(() => _connectionEnd = value); void handleApiNodeAdded(WidgetRef ref, String workflowId, String requestId, Offset position) { - - try { + ErrorHandler.execute('adding API node', () { final renderBox = context.findRenderObject() as RenderBox; final viewportCenter = transformationController.toScene( Offset( @@ -52,34 +53,29 @@ mixin CanvasEventHandlers on ConsumerState ref.read(workflowsNotifierProvider.notifier).addNode(workflowId, nodeModel); - - - final workflows = ref.read(workflowsNotifierProvider); - final workflow = workflows.firstWhere((w) => w.id == workflowId); - - } catch (e) { - - } + ApiTestLogger.info('Added node: ${nodeModel.id} at position: ${nodeModel.position}'); + }); } void handleNodeDrag(WidgetRef ref, String workflowId, String nodeId, Offset position) { - ref.read(workflowsNotifierProvider.notifier).updateNodePosition(workflowId, nodeId, position); } void handleNodeDragStart(WidgetRef ref, String workflowId, String nodeId, Offset globalPosition) { - final RenderBox box = context.findRenderObject() as RenderBox; - - final workflows = ref.read(workflowsNotifierProvider); - final workflow = workflows.firstWhere((w) => w.id == workflowId); - final node = workflow.nodes.firstWhere((n) => n.id == nodeId); - - final nodeTopLeft = box.localToGlobal(node.position); - - draggedNodeId = nodeId; - dragOffsetFromNodeTopLeft = globalPosition - nodeTopLeft; - - ref.read(workflowsNotifierProvider.notifier).startNodeDrag(workflowId, nodeId, node.position); + ErrorHandler.execute('starting node drag', () { + final RenderBox box = context.findRenderObject() as RenderBox; + + final workflows = ref.read(workflowsNotifierProvider); + final workflow = workflows.firstWhere((w) => w.id == workflowId); + final node = workflow.nodes.firstWhere((n) => n.id == nodeId); + + final nodeTopLeft = box.localToGlobal(node.position); + + draggedNodeId = nodeId; + dragOffsetFromNodeTopLeft = globalPosition - nodeTopLeft; + + ref.read(workflowsNotifierProvider.notifier).startNodeDrag(workflowId, nodeId, node.position); + }); } void handleNodeDragUpdate(WidgetRef ref, String workflowId, Offset globalPosition) { @@ -106,14 +102,14 @@ mixin CanvasEventHandlers on ConsumerState if (isConnectionModeActive) { if (sourceNodeId == null) { - debugPrint('Setting source node: $nodeId'); + ApiTestLogger.debug('Setting source node: $nodeId'); sourceNodeId = nodeId; connectionStart = position; setState(() {}); } else if (sourceNodeId != nodeId) { - debugPrint('Creating connection from ${sourceNodeId!} to $nodeId'); + ApiTestLogger.debug('Creating connection from ${sourceNodeId!} to $nodeId'); handleConnectionCreated(ref, workflowId, sourceNodeId!, nodeId); sourceNodeId = null; @@ -126,12 +122,12 @@ mixin CanvasEventHandlers on ConsumerState } void handleConnectionCreated(WidgetRef ref, String workflowId, String sourceNodeId, String targetNodeId) { - try { + ErrorHandler.execute('creating connection', () { final workflows = ref.read(workflowsNotifierProvider); final workflow = workflows.firstWhere((w) => w.id == workflowId); final sourceNode = workflow.nodes.firstWhere( (n) => n.id == sourceNodeId, - orElse: () => throw Exception('Source node not found') + orElse: () => throw StateError('Source node not found') ); final connection = WorkflowConnectionModel.create( @@ -142,16 +138,22 @@ mixin CanvasEventHandlers on ConsumerState ); ref.read(workflowsNotifierProvider.notifier).createConnection(workflowId, sourceNodeId, targetNodeId); - debugPrint('Added connection from $sourceNodeId to $targetNodeId'); + ApiTestLogger.info('Added connection from $sourceNodeId to $targetNodeId'); ScaffoldMessenger.of(context).showSnackBar( - SnackBar( + const SnackBar( content: Text('Connected nodes'), - duration: const Duration(seconds: 2), + duration: Duration(seconds: 2), ), ); - } catch (e) { - debugPrint('Error creating connection: $e'); - } + }, onError: (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Failed to connect nodes: ${e.toString()}'), + backgroundColor: Colors.red, + duration: const Duration(seconds: 3), + ), + ); + }); } } diff --git a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart index 73f2502f4..8719f6eea 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/components/canvas/canvas_node_layer.dart @@ -71,53 +71,45 @@ class CanvasNodeLayer extends ConsumerWidget { } }, child: Opacity( - opacity: draggedNodeId == node.id ? 0.7 : 1.0, + opacity: draggedNodeId == node.id + ? CanvasUIConstants.nodeOpacityDragged + : CanvasUIConstants.nodeOpacityDefault, child: Material( - elevation: draggedNodeId == node.id ? 8 : 3, - borderRadius: BorderRadius.circular(8), + elevation: draggedNodeId == node.id + ? CanvasUIConstants.nodeElevationDragged + : CanvasUIConstants.nodeElevationDefault, + borderRadius: CanvasStyles.nodeBorderRadius, color: Colors.transparent, child: Container( - width: 200, - constraints: const BoxConstraints(minHeight: 80), + width: CanvasUIConstants.nodeWidth * 2, + constraints: const BoxConstraints(minHeight: CanvasUIConstants.nodeHeight * 2), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, - colors: sourceNodeId == node.id - ? [Colors.blue[300]!, Colors.blue[100]!] - : (isConnectionMode - ? [Colors.purple[100]!, Colors.purple[50]!] - : [Colors.white, const Color(0xFFF5F5F5)]), + colors: CanvasStyles.getNodeGradient(node.id, sourceNodeId, isConnectionMode), ), border: Border.all( - color: _getBorderColor(node.id, sourceNodeId, isConnectionMode), - width: isConnectionMode ? 2.5 : 2, + color: CanvasStyles.getBorderColor(node.id, sourceNodeId, isConnectionMode), + width: isConnectionMode + ? CanvasUIConstants.nodeBorderWidthActive + : CanvasUIConstants.nodeBorderWidthDefault, ), - borderRadius: BorderRadius.circular(8), - boxShadow: [ - BoxShadow( - color: Color(0xff171433).withValues(alpha: 0.1), - spreadRadius: 1, - blurRadius: 3, - offset: const Offset(0, 2), - ), - ], + borderRadius: CanvasStyles.nodeBorderRadius, + boxShadow: [CanvasStyles.nodeShadow], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - decoration: BoxDecoration( - color: node.nodeType == NodeType.request - ? Colors.blue.shade700 - : Colors.green.shade700, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(6), - topRight: Radius.circular(6), - ), + padding: EdgeInsets.symmetric( + horizontal: CanvasUIConstants.nodeHeaderPaddingHorizontal, + vertical: CanvasUIConstants.nodeHeaderPaddingVertical ), + decoration: node.nodeType == NodeType.request + ? CanvasStyles.requestHeaderDecoration + : CanvasStyles.processingHeaderDecoration, child: Row( children: [ Icon( @@ -125,16 +117,13 @@ class CanvasNodeLayer extends ConsumerWidget { ? Icons.http : Icons.settings, color: Colors.white, - size: 16, + size: CanvasUIConstants.nodeHeaderIconSize, ), - const SizedBox(width: 8), + SizedBox(width: CanvasUIConstants.nodeHeaderSpacing), Expanded( child: Text( node.label, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), + style: CanvasStyles.nodeHeaderTextStyle, overflow: TextOverflow.ellipsis, ), ), @@ -142,13 +131,13 @@ class CanvasNodeLayer extends ConsumerWidget { const Icon( Icons.cable_outlined, color: Colors.white, - size: 16, + size: CanvasUIConstants.nodeHeaderIconSize, ), ], ), ), Padding( - padding: const EdgeInsets.all(12), + padding: EdgeInsets.all(CanvasUIConstants.nodeContentPadding), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -156,16 +145,13 @@ class CanvasNodeLayer extends ConsumerWidget { Row( children: [ _buildMethodBadge(node.requestModel!.method), - const SizedBox(width: 8), + SizedBox(width: CanvasUIConstants.nodeContentSpacing), Expanded( child: Text( node.requestModel!.url.isEmpty ? 'No URL specified' : node.requestModel!.url, - style: TextStyle( - fontSize: 12, - color: Colors.grey[700], - ), + style: CanvasStyles.nodeContentTextStyle, overflow: TextOverflow.ellipsis, ), ), @@ -174,10 +160,7 @@ class CanvasNodeLayer extends ConsumerWidget { else Text( 'Node ID: ${node.id.substring(0, 6)}', - style: TextStyle( - fontSize: 12, - color: Colors.grey[700], - ), + style: CanvasStyles.nodeContentTextStyle, ), ], ), @@ -193,24 +176,12 @@ class CanvasNodeLayer extends ConsumerWidget { ); } - Color _getBorderColor(String nodeId, String? sourceNodeId, bool isConnectionMode) { - if (sourceNodeId == nodeId) { - return Colors.blue; - } - - if (isConnectionMode) { - return Colors.purple; - } - - return Colors.grey.shade300; - } - Widget _buildMethodBadge(String method) { return Container( padding: CanvasStyles.methodBadgePadding, decoration: BoxDecoration( color: CanvasStyles.methodBadgeColor(method), - borderRadius: BorderRadius.circular(CanvasStyles.methodBadgeBorderRadius), + borderRadius: CanvasStyles.methodBadgeBorderRadius, ), child: Text( method.toUpperCase(), diff --git a/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_executor.dart b/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_executor.dart index d1e566a53..67a0fa350 100644 --- a/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_executor.dart +++ b/packages/api_testing_suite/lib/src/workflow_builder/execution/workflow_executor.dart @@ -1,19 +1,24 @@ import 'dart:async'; +import 'package:api_testing_suite/src/common/utils/error_handler.dart'; +import 'package:api_testing_suite/src/common/utils/logger.dart'; +import 'workflow_execution_constants.dart'; import '../models/models.dart'; -/// A more optimized execution engine that follows single responsibility principle +/// Executes workflows by processing nodes in dependency order +/// +/// The WorkflowExecutor is responsible for managing the execution state +/// of a workflow, tracking dependencies between nodes, and executing +/// nodes in the correct order. class WorkflowExecutor { final WorkflowModel workflow; + final Function(String, NodeStatus) onNodeStatusChanged; final Function(WorkflowExecutionState) onExecutionStateChanged; - WorkflowExecutionState _executionState; - final Map> _nodeDependencyMap = {}; final Map> _nodeOutgoingMap = {}; - // Stream control for execution commands final _executionController = StreamController.broadcast(); StreamSubscription? _executionSubscription; Timer? _executionTimer; @@ -28,17 +33,14 @@ class WorkflowExecutor { } void _initializeDependencyMaps() { - // Clear existing maps _nodeDependencyMap.clear(); _nodeOutgoingMap.clear(); - // Initialize empty lists for each node for (final node in workflow.nodes) { _nodeDependencyMap[node.id] = []; _nodeOutgoingMap[node.id] = []; } - // Populate the dependency maps based on connections for (final connection in workflow.connections) { final sourceId = connection.sourceId; final targetId = connection.targetId; @@ -53,11 +55,19 @@ class WorkflowExecutor { _nodeOutgoingMap[sourceId]!.add(targetId); } } + + ApiTestLogger.debug('Initialized dependency maps for workflow ${workflow.id}'); } - /// Start the workflow execution + /// Starts the workflow execution + /// + /// If the workflow is already running, this method does nothing. + /// If the workflow has completed or encountered an error, it is reset first. void start() { - if (_executionState.isRunning) return; + if (_executionState.isRunning) { + ApiTestLogger.debug('Workflow already running, ignoring start command'); + return; + } if (_executionState.isCompleted || _executionState.hasError) { _resetExecutionState(); @@ -68,9 +78,10 @@ class WorkflowExecutor { _updateExecutionState( _executionState.copyWith( status: WorkflowExecutionStatus.error, - errorMessage: 'No starting nodes found in the workflow', + errorMessage: WorkflowExecutionConstants.errorNoStartingNodes, ), ); + ApiTestLogger.error('Cannot start workflow: no starting nodes found'); return; } @@ -81,12 +92,16 @@ class WorkflowExecutor { pendingNodeIds: initialNodes.map((node) => node.id).toList(), ), ); - + + ApiTestLogger.info('Started workflow execution with ${initialNodes.length} initial nodes'); _executeNextNodes(); } - void pause() { - if (!_executionState.isRunning) return; + void pause() { + if (!_executionState.isRunning) { + ApiTestLogger.debug('Workflow not running, ignoring pause command'); + return; + } _updateExecutionState( _executionState.copyWith( @@ -95,10 +110,17 @@ class WorkflowExecutor { ); _cancelExecutionTimer(); + ApiTestLogger.info('Paused workflow execution'); } + /// Resumes a paused workflow execution + /// + /// If the workflow is not paused, this method does nothing. void resume() { - if (!_executionState.isPaused) return; + if (!_executionState.isPaused) { + ApiTestLogger.debug('Workflow not paused, ignoring resume command'); + return; + } _updateExecutionState( _executionState.copyWith( @@ -106,9 +128,13 @@ class WorkflowExecutor { ), ); + ApiTestLogger.info('Resumed workflow execution'); _executeNextNodes(); } + /// Stops the workflow execution + /// + /// This cancels any pending executions and resets the current node. void stop() { _cancelExecutionTimer(); @@ -118,16 +144,22 @@ class WorkflowExecutor { currentNodeId: null, ), ); + + ApiTestLogger.info('Stopped workflow execution'); } void reset() { _resetExecutionState(); + ApiTestLogger.info('Reset workflow execution state'); } void _resetExecutionState() { _updateExecutionState(const WorkflowExecutionState()); } + /// Finds nodes that have no dependencies + /// + /// These nodes can be used as starting points for execution. List _findInitialNodes() { return workflow.nodes.where((node) { return _nodeDependencyMap[node.id]?.isEmpty ?? true; @@ -138,6 +170,7 @@ class WorkflowExecutor { _cancelExecutionSubscription(); _executionSubscription = _executionController.stream.listen((command) { + ApiTestLogger.debug('Received execution command: $command'); switch (command) { case ExecutionCommand.start: start(); @@ -170,10 +203,23 @@ class WorkflowExecutor { final nodeId = pendingNodeIds.first; pendingNodeIds.removeAt(0); - final node = workflow.nodes.firstWhere( - (n) => n.id == nodeId, - orElse: () => throw StateError('Node not found: $nodeId in WorkflowExecutor._executeNextNodes'), - ); + final node = ErrorHandler.execute('finding node for execution', () { + return workflow.nodes.firstWhere( + (n) => n.id == nodeId, + orElse: () => throw StateError('${WorkflowExecutionConstants.errorNodeNotFound}: $nodeId'), + ); + }); + + if (node == null) { + ApiTestLogger.error('Failed to execute node: $nodeId - node not found'); + _updateExecutionState( + _executionState.copyWith( + status: WorkflowExecutionStatus.error, + errorMessage: '${WorkflowExecutionConstants.errorNodeNotFound}: $nodeId', + ), + ); + return; + } final dependencies = _nodeDependencyMap[nodeId] ?? []; final allDependenciesMet = dependencies.every( @@ -201,18 +247,23 @@ class WorkflowExecutor { ); onNodeStatusChanged(nodeId, NodeStatus.running); + ApiTestLogger.debug('Executing node: ${node.label} (ID: $nodeId)'); - _executionTimer = Timer(const Duration(milliseconds: 500), () { + _executionTimer = Timer(WorkflowExecutionConstants.nodeExecutionDelay, () { final success = _executeNode(node); final newStatus = success ? NodeStatus.success : NodeStatus.failure; onNodeStatusChanged(nodeId, newStatus); + ApiTestLogger.debug('Node execution ${success ? "succeeded" : "failed"}: ${node.label}'); final updatedResults = Map.from(_executionState.executionResults); updatedResults[nodeId] = { - 'status': success ? 'success' : 'failure', - 'timestamp': DateTime.now().toIso8601String(), - 'data': {'message': 'Executed ${node.label}'}, + WorkflowExecutionConstants.resultStatusKey: + success ? WorkflowExecutionConstants.statusSuccess : WorkflowExecutionConstants.statusFailure, + WorkflowExecutionConstants.resultTimestampKey: DateTime.now().toIso8601String(), + WorkflowExecutionConstants.resultDataKey: { + WorkflowExecutionConstants.resultMessageKey: 'Executed ${node.label}' + }, }; final nextNodeIds = _nodeOutgoingMap[nodeId] ?? []; @@ -242,21 +293,34 @@ class WorkflowExecutor { }); } + /// Executes a single node and returns whether execution was successful + /// + /// The implementation of this method will vary based on the node type. bool _executeNode(WorkflowNodeModel node) { - switch (node.nodeType) { - case NodeType.request: - return true; - case NodeType.condition: - return true; - case NodeType.action: - return true; - default: - return true; + try { + ApiTestLogger.debug('Processing node of type: ${node.nodeType}'); + switch (node.nodeType) { + case NodeType.request: + return true; + case NodeType.condition: + return true; + case NodeType.action: + return true;// TODO: implement action execution + default: + ApiTestLogger.warning('Unknown node type: ${node.nodeType}'); + return true;// TODO: handle unknown node types + } + } catch (e) { + ApiTestLogger.error('Error executing node ${node.id}', e); + return false; } } void _scheduleNextExecution() { - _executionTimer = Timer(const Duration(milliseconds: 100), _executeNextNodes); + _executionTimer = Timer( + WorkflowExecutionConstants.nextNodeScheduleDelay, + _executeNextNodes + ); } void _completeExecution() { @@ -266,6 +330,7 @@ class WorkflowExecutor { endTime: DateTime.now(), ), ); + ApiTestLogger.info('Workflow execution completed successfully'); } void _updateExecutionState(WorkflowExecutionState newState) { @@ -287,5 +352,6 @@ class WorkflowExecutor { _cancelExecutionSubscription(); _cancelExecutionTimer(); _executionController.close(); + ApiTestLogger.debug('Disposed workflow executor'); } } From 0c5d0a3c97b806e0c1fbfda679f2b2b7da90a8b4 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 21 Apr 2025 19:01:45 +0530 Subject: [PATCH 187/188] feat: add FakeDataConstants for centralized fake data generation constants and update FakeDataProvider to utilize them --- .../services/fake_data_constants.dart | 35 ++++++ .../services/fake_data_provider.dart | 119 ++++++++++++------ 2 files changed, 117 insertions(+), 37 deletions(-) create mode 100644 packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_constants.dart diff --git a/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_constants.dart b/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_constants.dart new file mode 100644 index 000000000..25c0b6dba --- /dev/null +++ b/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_constants.dart @@ -0,0 +1,35 @@ +/// Constants for the fake data generation service +class FakeDataConstants { + // Random data generation limits + static const int defaultMaxRandomNumber = 1000; + static const int defaultMinRandomNumber = 0; + + // Random ID generation + static const int randomIdDigits = 4; + static const int randomIdMaxValue = 10000; + + // UUID format segments + static const List uuidSegmentLengths = [8, 4, 4, 4, 12]; + static const String uuidCharacterSet = 'abcdef0123456789'; + + // Phone number format + static const int phoneAreaCodeMin = 100; + static const int phoneAreaCodeMax = 999; + static const int phoneFirstPartMin = 100; + static const int phoneFirstPartMax = 999; + static const int phoneSecondPartMin = 1000; + static const int phoneSecondPartMax = 9999; + + // Date and datetime generation + static const int randomDateRangeDays = 1000; + static const int randomDateTimeRangeDays = 100; + + // Address generation + static const int streetNumberMin = 10; + static const int streetNumberMax = 1000; + static const int zipCodeMin = 10000; + static const int zipCodeMax = 99999; + + // Random array probability thresholds + static const int usernameSuffixProbabilityThreshold = 5; // out of 10 +} diff --git a/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart b/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart index 66d483ce2..d385fcb59 100644 --- a/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart +++ b/packages/api_testing_suite/lib/src/fake_data_provider/services/fake_data_provider.dart @@ -1,4 +1,6 @@ import 'dart:math'; +import 'package:api_testing_suite/src/common/utils/logger.dart'; +import 'fake_data_constants.dart'; /// A utility class that provides functions to generate fake data for API testing class FakeDataProvider { @@ -18,9 +20,8 @@ class FakeDataProvider { final suffixes = ['123', '2023', '_test', '_dev', '_admin', '_guest']; final prefix = prefixes[_random.nextInt(prefixes.length)]; - final suffix = _random.nextInt(10) > 5 - ? suffixes[_random.nextInt(suffixes.length)] - : ''; + final includeSuffix = _random.nextInt(10) > FakeDataConstants.usernameSuffixProbabilityThreshold; + final suffix = includeSuffix ? suffixes[_random.nextInt(suffixes.length)] : ''; return '$prefix$suffix'; } @@ -46,29 +47,26 @@ class FakeDataProvider { ]; final username = usernames[_random.nextInt(usernames.length)]; - final randomNum = _random.nextInt(1000); + final randomNum = _random.nextInt(FakeDataConstants.defaultMaxRandomNumber); final domain = domains[_random.nextInt(domains.length)]; return '$username$randomNum@$domain'; } static String randomId() { - return _random.nextInt(10000).toString().padLeft(4, '0'); + return _random.nextInt(FakeDataConstants.randomIdMaxValue) + .toString() + .padLeft(FakeDataConstants.randomIdDigits, '0'); } static String randomUuid() { - const chars = 'abcdef0123456789'; - final segments = [ - 8, - 4, - 4, - 4, - 12 - ]; //standard length of different segments of uuid - - final uuid = segments.map((segment) { - return List.generate(segment, (_) => chars[_random.nextInt(chars.length)]) - .join(); + final uuid = FakeDataConstants.uuidSegmentLengths.map((segmentLength) { + return List.generate( + segmentLength, + (_) => FakeDataConstants.uuidCharacterSet[ + _random.nextInt(FakeDataConstants.uuidCharacterSet.length) + ] + ).join(); }).join('-'); return uuid; @@ -103,15 +101,27 @@ class FakeDataProvider { } static String randomPhone() { - final areaCode = (100 + _random.nextInt(900)).toString(); - final firstPart = (100 + _random.nextInt(900)).toString(); - final secondPart = (1000 + _random.nextInt(9000)).toString(); + final areaCode = (FakeDataConstants.phoneAreaCodeMin + + _random.nextInt(FakeDataConstants.phoneAreaCodeMax - + FakeDataConstants.phoneAreaCodeMin)).toString(); + + final firstPart = (FakeDataConstants.phoneFirstPartMin + + _random.nextInt(FakeDataConstants.phoneFirstPartMax - + FakeDataConstants.phoneFirstPartMin)).toString(); + + final secondPart = (FakeDataConstants.phoneSecondPartMin + + _random.nextInt(FakeDataConstants.phoneSecondPartMax - + FakeDataConstants.phoneSecondPartMin)).toString(); return '+1-$areaCode-$firstPart-$secondPart'; } static String randomAddress() { - final streetNumbers = List.generate(100, (i) => (i + 1) * 10); + final streetNumbers = List.generate( + (FakeDataConstants.streetNumberMax - FakeDataConstants.streetNumberMin) ~/ 10, + (i) => (i + 1) * 10 + ); + final streetNames = [ 'Main St', 'Oak Ave', @@ -120,6 +130,7 @@ class FakeDataProvider { 'Pine Ln', 'Cedar Blvd' ]; + final cities = [ 'Springfield', 'Rivertown', @@ -127,9 +138,15 @@ class FakeDataProvider { 'Mountainview', 'Brookfield' ]; + final states = ['CA', 'NY', 'TX', 'FL', 'IL', 'WA']; - final zipCodes = - List.generate(90, (i) => 10000 + (i * 1000) + _random.nextInt(999)); + + final zipCodes = List.generate( + 90, + (i) => FakeDataConstants.zipCodeMin + + (i * 1000) + + _random.nextInt(999) + ); final streetNumber = streetNumbers[_random.nextInt(streetNumbers.length)]; final streetName = streetNames[_random.nextInt(streetNames.length)]; @@ -142,15 +159,18 @@ class FakeDataProvider { static String randomDate() { final now = DateTime.now(); - final randomDays = _random.nextInt(1000) - 500; + final randomDays = _random.nextInt(FakeDataConstants.randomDateRangeDays) - + (FakeDataConstants.randomDateRangeDays ~/ 2); final date = now.add(Duration(days: randomDays)); return date.toIso8601String().split('T')[0]; } + /// ISO 8601 format static String randomDateTime() { final now = DateTime.now(); - final randomDays = _random.nextInt(100) - 50; + final randomDays = _random.nextInt(FakeDataConstants.randomDateTimeRangeDays) - + (FakeDataConstants.randomDateTimeRangeDays ~/ 2); final randomHours = _random.nextInt(24); final randomMinutes = _random.nextInt(60); final randomSeconds = _random.nextInt(60); @@ -169,12 +189,22 @@ class FakeDataProvider { return _random.nextBool().toString(); } - static String randomNumber({int min = 0, int max = 1000}) { + static String randomNumber({ + int min = FakeDataConstants.defaultMinRandomNumber, + int max = FakeDataConstants.defaultMaxRandomNumber + }) { return (_random.nextInt(max - min) + min).toString(); } static String randomJson() { - return '{"id": ${randomId()}, "name": "${randomName()}", "email": "${randomEmail()}", "active": ${randomBoolean()}}'; + return ''' +{ + "id": ${randomId()}, + "name": "${randomName()}", + "email": "${randomEmail()}", + "active": ${randomBoolean()}, + "created_at": "${randomDateTime()}" +}'''; } /// Registry for mapping tags to fake data generator functions. @@ -193,16 +223,31 @@ class FakeDataProvider { 'randomjson': randomJson, }; - /// Processes a fake data tag and returns generated data. - /// - /// Example: processFakeDataTag('randomEmail') -> 'jane123@example.com' - /// Returns the tag itself in {{tag}} format if not found. - /// Public getter for the tag registry. static Map get tagRegistry => _tagRegistry; - - static String processFakeDataTag(String tag) { - final key = tag.toLowerCase(); - final generator = _tagRegistry[key]; - return generator != null ? generator() : '{{$tag}}'; + + /// Processes a string containing fake data tags and replaces them with generated data + /// + /// Example: "Hello {{randomName}}, your email is {{randomEmail}}" + /// Returns the input string with all tags replaced with generated values + static String processFakeDataTags(String input) { + ApiTestLogger.debug('Processing fake data tags in: $input'); + + final regex = RegExp(r'\{\{(\w+)(?:\((.*?)\))?\}\}'); + final result = input.replaceAllMapped(regex, (match) { + final tag = match.group(1)?.toLowerCase(); + + if (tag != null && _tagRegistry.containsKey(tag)) { + try { + return _tagRegistry[tag]!(); + } catch (e) { + ApiTestLogger.error('Error generating fake data for tag: $tag', e); + return match.group(0) ?? ''; + } + } + + return match.group(0) ?? ''; + }); + + return result; } } From e46f5a48b3e05d5d838f7b806d8d9d03b0623cfe Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Tue, 22 Apr 2025 16:52:15 +0530 Subject: [PATCH 188/188] refactor: update envvar_utils --- lib/utils/envvar_utils.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/envvar_utils.dart b/lib/utils/envvar_utils.dart index db625ba1b..3346fc345 100644 --- a/lib/utils/envvar_utils.dart +++ b/lib/utils/envvar_utils.dart @@ -156,7 +156,7 @@ EnvironmentVariableSuggestion getVariableStatus( // If not found in environments check if it's a random data generator if (key.startsWith('\$')) { final generatorType = key.substring(1); - final generator = FakeDataProvider.processFakeDataTag(generatorType); + final generator = FakeDataProvider.processFakeDataTags(generatorType); if (generator != '{{generatorType}}') { return EnvironmentVariableSuggestion( environmentId: "Random",

1A^yGEew9P@NVm%+-S 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 I@AUA00c_KDnE(I) From 4a392c2150f0a5c5846b84981331bea8c9b5adef Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:22:14 +0530 Subject: [PATCH 024/188] Update Application_K_Govind_API_Support.md --- doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md index e718af458..b88a29a30 100644 --- a/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md +++ b/doc/proposals/2025/gsoc/Application_K_Govind_API_Support.md @@ -191,7 +191,8 @@ Architecture is as shown below: ``` POC Link: Images of POC: -![SSE](/images/SSE(1).png) +![SSE](./images/SSE(1).png) +![SSE](./images/SSE(1).png) From 4043a79697ce27c9358fed97de1febad2539d36d Mon Sep 17 00:00:00 2001 From: K GOVIND <117105258+Clasherzz@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:24:17 +0530 Subject: [PATCH 025/188] Add files via upload --- .../images/Screenshot 2025-04-01 095628.png | Bin 0 -> 115056 bytes .../images/Screenshot 2025-04-01 095640.png | Bin 0 -> 102620 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 095628.png create mode 100644 doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 095640.png diff --git a/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 095628.png b/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 095628.png new file mode 100644 index 0000000000000000000000000000000000000000..94a371933c43e46644c2c551138c4c691bf4a887 GIT binary patch literal 115056 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 E0+!jy@Bjb+ literal 0 HcmV?d00001 diff --git a/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 095640.png b/doc/proposals/2025/gsoc/images/Screenshot 2025-04-01 095640.png new file mode 100644 index 0000000000000000000000000000000000000000..0c0f2c8886d29238c9cae3ec5071ace537c3e791 GIT binary patch literal 102620 zcmaI8by$>L_XetTcXvrj3DO}YD2)L~H%NDbbeBj;BMM4`bmyqV01`vZ&_fU1%$&#X zdwsv(`Qsd}3%G!Jp4rddYp;E;`@R=(I$ElPcyxI8?%gANrlzcS?;Zxny?bbHaj<~j zoKU0*0^ja?>ZvN;s~%(627bV>Q_xhncdsr5|JDK%_!-wt&Cv7SJ)-_UpZ7to<<|G^ zp*}xTR(R=Wx|fe>K5erwRC9(!Upuh`d_3SGeEto>Z&?&x63b@EVvgO!!kf+V=P&g1 zR`dwZC24HznqTGjJ??JDnmv(6w}nz2@UDWJF6P48!;vy~rl7kZSwwa40H`u zo7Rc&SQ14X)=1z>noLk|a2mhkYYZ%`fMkyMZc_IHFAkKvyuQPKY;Y+>pVG)|@hpbS zb{4QdndZ&V{o=**-SIb`xbIGMziP7Im@#coP|)}{8&5C945oX9NUd0G?ZS zzdm)y(~_u}dbg|U@b~>#tA%iJaUrda)MBo49y5-+GtRMbty1`~BX{>~5nm=P(;^|a zVx2^Xi4LA$8T=6;zKc=l*hIys{W}s0iuKmjV>X3fe3dcl;ve%hQeL zl;DndDxO587-Fj*;}-hHBo2$U_utM|hON{j53Ka79&XIS%u&cYn!_z66kOOtQs+nX z-+{Rkl8}(RyS+Lt(b|7=cy?B2g-yiNcaa_BNJWO>Z*)BQkoOW!EUV-{%1=6Gq3JRJ+*N?J@+mIbW>wfQdEtN7b&UPV+#ul zaY>&Qs_2FblGDC7j9JGc!<+ki4Qn+iH_pI7o@feAmsY-12Ad^D@umAg!{wE6+NuZZ z|DKzKM=KtOECh+nycdxpd4=bO*v4Ch!$UV|%sQg0S^k}$OIOc9Pc;nen4;&?J3lh` z#3Qu#XiT(1?yk5fc-}DhpKLT+MY^yw*lB`hs*FhB)vYuil>WWCC4fUbW`L5KjkE=J4p1p89{51o*PrJvL3wp@m*Iaz;WP@nzISG1rGNja^mXW@J6{ zfA>LnOk>1BAnATLqiI{gj&`T})6=HG0X_<-g#b$gRJNev@91!j%+|l*k|FQk-0-s>;sGi&tE1@uPs93k^L&Mj!Wo z1NLm72zvsq>ozA- z@6zJRn`tWi`N*4h(VAJ1EiaIt^xkUz@A30jHq#u6q|St^nMV3-_zW@ z$X~1Uc|Kj4R({{2WJ9gr1X^IIFa|beZjpk8(tkGsFlzlUYi0S%$S6BGgiTjS)~1@m zL@x}+ELX5jQl?!fHt{Z$_E*}6t!x?& zhJuts@BQ!twac5Ue)%5eE&{=$D|O|#goK75YJGG(sZ-7s7&(`gLH^$rigm=rC8Oq1 z6DsA9kf52an0>M|?@gK}*C&Q986rcr;N#REi3_ustu~=%)brpO|7{IP=qJhKB)o?C zV_RCTWB!fA)p;PeRB*^nWpMEE`z!iYi!^c`+4-PYD0uylkRvOa!%M4dY2RwbZXJVY zV4vUJN$W=7%HQ4H?X|)=FG_s}A`zTyY%*vUvTlO5;DRcQqQ6g9&BLK)9LbW#`-)fQ zZi+?JX`IAyrgF&0wL2=O!Sw{YwJ>YH9oud&J@kuKhxCC-(Cv}x<#le2tSY$$Q}P$hk8Gl%6lNi3pQBR#U6fg}pOhTc z%%52?afpdkH=bu`j{9xS*^R}LTKD#0Uak%c3hixVkx)|FfCRAOaa9tiPJ*sO6_sAF zo6LI^Xxk~>UyWf7?SY#G2?+mvjsfo^8IJd<(!MT1w<}y!dJ`?r*cnn}4kS+y2IZ>u zG{+lYS5vAHNm5$MxW|q&1|~S2^z}BQ(cfNGtoC9v^-X+I72cZb_AxeT^TPG828SVi zPgNV=PHTE)KMu^yPmps*<9l5#gFy%$uYoRC2?_IsKFtp#`b}LZDkRXyugz`! zM_Ru!9M` z5m~Dehd#YN-6kz0A!bIx*CLc+h*pYYmuVMk77~{o$ofiM zO&1amkw}}gH`lN>y;8%b6rJcJeV*VVmBwNBlwxvy=-Se&I`z}utHu>ES#}yWR zQJs)Fs~C|@ks`mkRlkrQ_!4gB^4G9wSoMRTr)^%_Zt5vU6K${f5<~1nL$1!KrK#!Z z6M}EfS$pK~6$VQ&`5&(9@>cCOkIwbN`gN$tk2e0u;0-E=kTEy zf`p>@2z@)VCEU^Et}m7Bj6t7XfX4h>7jH5n>s|8OGE?8PMN$XQo_619*_D?Ge?oQ= zL;Ir%>G#ft=s!I~C=U_P>UO>%Y!A6OSb{Y8*3cf*)s@xS3}=eetbtOjZqD})5W>F5 z@BZ|DVE5dKla0hfD;$*|nS!8W+}q3FDUe!s^ch!!l&t8^$P7OHkJPH89~;M}3fV9_ z#j+(lhel}6^OADKR_EXay>(U~of2hSUnh}d2FY+y3x`xrO*(Q-IeJu1#_(~LpjmRU zAkkn#zv%~>zX84)-omXAUVw426xdx=dePQMeb9~wk*1=cur|F1y~PZ2f0bWMKp_+! z5xy@~W1*cEcH#D*@WNWHuvZQn3u8X`VZwr&?aw^SezTB4MIOI`P@c%c5LZag#yj4z zxGi!T5g!7kfOXcOGw)R`Dj;PI7L^tD;)97*x;m~jSXfC(w{^P|{mDtF_)VS8ETpfF zPcHh~5lQIe&tHBu9-I%hIjWN(r=Z-@Rp2HYceFg)d37!sL62R-(i=qLBq%HK-c%bw zc77id#}(#L@q)=p;#qsszLN~k&|yG;!+5Up-;CEnBJe!5=v@!xm3)~th}taJLK2^w zWidcq+iM4cn<8_|eY`OoQ)$>>XwWmdFTMPgt3a{!ctf{X5%as##AM?2%|R^mRu@7H{PN}2xl*y>9nt>!u_1>dEd_qUgE3m*eyoVKct zHgjE<4~zS^7QZG$l`nc;bZ)vUtn~4D{G9}npBp9o&qC!xKkyooCDDoY#c%LETr3V~ ze48yF+O@x<)9_SN^eqIZ8+nc|ceQS{=LS+#3@4@7B7jbP9eqOljMCB+v%_V7{;@%) z?PEGo`*$x744#;?WIi+uzrx?N+HY&5k~*y8i@ErnS|IJ49HnghY)i67C|+CshT;Mm zTI0{WK9WP;=s5d~!Y1f$grG{K-dIcR`2oB%QTuW@*|5`GIDzn(cD#BE-OLR(+ zsrtbeaCWk5*WHkZu^atlmMKFtbu@uW&ZLcy=Zwa1T$*Sa7bX|y5X0FJ_o7dryv*e| zx|pfW@P|RZezV8KRDZWGTdVPh$J(D{cy};-_MuU}uuy7Z`P?6aY<^`5D`lhI^Yt?K z!ypWGdG_=0UCChloA;oj!cbd(4uLY3-s`&;_LzlAV4|k{Giz>)PA;}jZ2WMQ;_#?F zq~}TesRM-4d3&o;{sBK0?K7>i@3W4wWepuGRr=TjJJLN#Ty-Sm0>x{$N zovrBy6$V9C&ehKe5S8T%w_6T+Xk;HU;!*R2Q%B3CcprQOcV9BdYavN7kEwgEy>~}z zH>ycAsIE2_Tsh^|J{NQAwcrpjNW24ZoS|vtg}fiEAh6Zka&+?Z^MwO)jd?XeoUY3P zu(hJpll$wgp)+jAt<$zIiC&x45r)1}BK$3sv z&lBLB7QP<|H$ZNPo&Wx#8FReB*V1K@!py1Jmsw?0>wF`&Yxe$SwuPmxi{s8d`Ai%e zK~1qffj55q)4ym++2aTo9~=Z7RS3QlSi&y`kbpsVi0l`w+#N{|o1!Ca;^^#j;R5!b zW%d)6eyF7j{uTD-j?yqK00ixiQ9cHz$bUhG6$^Hm8<~^E)3BTR6AmU&gI4-ugngmL z_|7<9PXmAIeIDtLFmgDSQ>;n$g)&(&VG_t2c39WZUVei5EkzbM&DFO`1(~Du06?cU z804d@3R><@k$YDof|S4U>WFH2`StajGU+5L8JVf$wZ2%_!uhyn#HtZj9Wd5t$*q@;o~$PS^NEJ@da7#wFqaL|HfAd zE96Ak7Z9q%QwS5|bH1}&xAOus?>$o_)wi(VU`Osc%pH||--%NHl!cHL+8QzD5 zfIlADCZVE=XAZrd!1^BCe*U_!Zf)od5>KzrQiU9gJNltf*GRIzMT^Qb?rH8E|&=rY5}stt7n`8e%0#n z7c$@@y3KZTw&$5QSJZ357ORmW#ey0v^KIUarw2=*KFyZ>?Y=1ded9@EGLE`n+}E3} z{~ORy^duwK;9Y8vcH5(8o~P&)?x-SaTbTk z(%vOsXi}T^u3<>rk8(9~_m&KX|BI-%YhxR!o?L6iGfQT z7N(#^&9nkFk%zKmDSmtT)k!s}!~bNS>QiK&lC?F5&mJV1l23nmAZ`nV55E-b!6@wQ zWuodm( zYX1cX?h)k7)OPrp)ZrMoQBMB?1|BUBOx*qYzZ*|6mO?fB?^EKi>M`Jw{moaAYVI8W z+zWU$+h7{Y|JOx%d|mr?1szv94X+t7Ffam=Z>1Mur=Vx#Zl_zi3%u)!6aRm9QV2XAspP@-G7qNghwdVT3+dO93SIzY8n5(_$JU}RyFJ40(2!ahOh?- z)T_C!a-gq}7eLK6k|5X}z0~e&F`k|RWS~h;(JQsozYES<&Ge@#wkN`6zF9O_R!Ol`;(uHhPTK1Kxu0@;I$L;TO^x8~?d=L86q%Kh z^61|=YN;PTpW3aNonDSV0H`rXAA(>93^C{Vcm5}~><3d)j(2{%h=Kqgb9DtG1(8PLePf8k{l+GpLmXXWY zxi|r1PX(w6a?)f5S8Wc4j!K30zjcjs=o&3gxaly`)A9$HhmQt z&J-9*V~>%wdk-4KAq#~UyfxgQAdC0u z?IQr+7e1YNXjo^5U|I|Yn-qvp59ynQBr!F6iIZ*?nujFCQ}P)~33n|v zDv?QqPX1Qtr{^^6MYWTpjNG#O>GmuLD-qmD({*Eu*5)s|pq3aE%mLupeaT!k zH#ebzkYk720c(6VBrGFWYwa~ybPzq#L@7>w8c3fCMwM6bn&ZAtMlYtlL z?w5yA{x$z(mVxPathj&DDnR-ON;0f;6FFavPZa~F{1$EdiQ=&dP39D=K_80HBcFDGfQ1DYGPA z&LbPeukSw}E~68(s%KXE?T$95N&7Ob|0qCr?LcC6U0oXm-A#+z>vR{lVos#5j*3I; z1@EoEYq~Dyp6DH(LJkD+nt z!$v3m*6EItjxN6apaUNkbjIIBgL?2QtHykmJm5`-_<`(h#Y#0JDJiK>$FydZ&}A#D z&s}Mp{4(<903_=Ri*W6}8vFHCuQgsX=v@Cdr{>X5)xx$l*z!sYldvHD-;HE|c8gBJ zOf4wPMq8U=Z)IBP*&F3Hh!MAo%lW7{=AIJm)^@GOH1A1mk^AUnPFRY3m}~bP)*Ye^ zeWBU4pRpMh=Dg5s1%M?;r=2*k>s*|c+A!OF5Pc^~g{mlNL{9~~sX}+i9SIYY3$1ZP zMED8D?Mq-MW zPb9Xa=lNdi0!>X0xpPr+Q^PrEN69Vv>xXI-3ip4gPE9p`L4|0$(UN(*^^73Ir#23k zf{2?X=yD=vM!zyN<)9hjZd*HAC@9Yqvd*dx?X-thO0F)-?4LPJbCb23;gxZ0?1rZoTE;pldpwLm147?UO-@}Y4H30PJH(jkIJ&Fci`)&J%M zB^xoSvw{;1@AmRrl$2;Kw)v{cBK|qrL-gDlYkY21*8s~ZIO*S+8cw~s63 z{vO*%X|+@d?6%Js7+3}dXz7Yrn3(JbFBy^3mK~EzS~Kb+P!oAAQ8}YnMWSrF30_DU=Sp|hT1#Cqyj@2T%Ev8&I^ZI?^KU| zmNBzyAIJbx=7TouS-b_nwRCN{A@&s6L?EA7^t=P@Gj!5rKRGdO=Hv{QWb&6eLTBDJ zr6AV>biKW=1`M(vA^H`stLi;?W^~qzFIV^zypt2-H4^zf1Ow9Cn$tubet*oukrg6h zl8Th{w6(g0ukADag&)+(QK%c?NYuOsWg5$hIX9;M`R~$aj#rG?%oc?rlUX$e$^5zE zxeA@$3$HZ_kzB}ZDM}H$Q5o`?<)|UAFf`2CfHU59L0;ZpwDWQB)zvkP+8=oj68!l; zJk*_3bcQ$J(eqvNEFA!3{x6Q zEtCsx^EVQ8S%f5`$O(8XEQgDh&4+L50!H7eoA<^25^J`H_EkR{c0`l4j4d;m!WC+!}=3->DIg#yQ%_%2+pay>Rj^LMX@V81?!Ie*I#wmWbF8~)w&9R_5e+FHjk zgKp#T>XEE&mVmP^LO`x){W8BzO+hC0;{?fU!PdWv8Lc)hQHGPskPRz}6Pa%{u|0b` zJ<;Ld87Z+AbXn0~=L(Zs@Q*4`H}E-cb5Iq^S$=k$f>sn9vN==Ga!DgnPJbf~Csm=NYNe z+Ds4Md_PPaeKwI?ev%yq{!Y`UXo_dDrkYRp`Xa>QI``aB$jBsrVWlcY#( z?oMx*yK}hvJBUYf#X%|ZclJ;U3V}C`Z=IdX_pjqEu?C}Vuka1tk452AL#VqPet)^# zZ-zOQB?IJwVJz0L`K5Y+jQ<3H^zmse@*zG=Dt{9O);!DiU9F*lP7${K7R9K9<;;RR zA;--^+d#h+$$4i$G2PrAy>XC7u7&SD##u;Rn9~owUfOmV?BIS=iIFL27H@ACh$Qcb zX`+{%O+N==rY9QJK$;5m6Y!VL)5+tXu|$MNR5nPqmau&qIXv%+%Mvh=YR{Y%s<|)A zvL)P8ni50IT(7X^e_*h(3-wH(&@x5%6j5;X^-Z?yx;-V4@H;HJ8_gE}lLI(1gjAFu$%(Kwtj))*#LKgF>aFaGOIq;4KcB zW^#jnk`QJ7gbQ0o+cZ?~VaGrIi+F^yWmlb;Ht%cT$u1>iamTsH23Q@6Xmbd-fbua~lf%}%bnm3}73n9?Me@mEH zlo(1t?So>CoUi#!6aSBFVUa%wJVlNsVMx=c#PB3nL&u;YAS!>@tSHswV(?PCa%9A? zp~Ptpuj%p{+Wx7|d)W3*rIFuuDug(a1qzEjX;(A zUaFe#=dh&JT|`m1mq176+vLA>hx>z{sjTc>g@=SslY(dgtxdIh(O=kZ#%mAS_?wYF z87y~2g6Ni}`y;(p)DR5LH&&!diBi0(m>CFMd%D(7JXEZYkWLlRcM)}%M;p$3*q8gs zbkG+T;{R$8rw=nQo)jAUu7$(&Qa?9=jDwrSPX!|$D=1X%h{Z9pl3TAd7XI5z$FJO| z>;M%pdnw*#AZ;@wAt7V0NE`v4g)q!kETt^%Or2 zoFq$vU{z46Dm$ z!lUAi?2C#w@BoFOV_A;>jySw_tOpeh=SXR;h7*wbFw2zE+0w>GB=D!wOZSKey zeLk4-LvNW^ogjihjLV8}rK78e;aQVz|KOVBIea2s=QDALk7jkk&?RVF>0;%`^WHKB?X7#i?xf#X~w!hH) zCVObl@VBtXz_1T&WczpZHJj7|4I^Vx6h1t^uC;~|5W>vSsrcW-0z8!E!uh_0`znUs zXRQt7Q$PvY$rS7hTxxaxyb{cNK=CV+$I<0s9I1HO#1~DY4}AJ#)p7BO-@D5!wJo3> zty|L#)b+Nbws8Xf!OF@QAvbs8>%TJiR6-#qhD!mb9#7SLz-jm~-iW{j6BPB^_pDZ& zGN(Vtsn^GTe+gCv!bEm1+AZ$=XLnCfH4mE_pu^**5cuImn9(E$GW$&}e$=BoSO56k zAzfDW_6{Aul4WTVNh%T1n&aQRZ$#FVCsxjp7bQ=670)Gku8t|6?23smc;ZnDytN^4 z-w#>QSDudG5Na=QtEm0>K$Fk&_!Pbx|AZNC!*5>opaBqwIz{H1 zquzG7pfb>D@n-R0Q@KPx`#%fVB35+RqvBgrJcj9ABSFf{Z1=RoH!Z*YRh|s36&w1# z8D~#oa&n!cpZ4nG4z42_m>GVpDfHSz{Sq@OlIXYtr5Y53LV?6oFr)QG%!I(_TuV*P zx&BJy5i1w+A)soL_FAAy0F?a&tgxHT14tL6w9j4-U}_-Uz)C3~&J}a5w=!yWY0T(H zaA^*NMPS!1EP;2MoMD^4AXY}-{15;tVApEH{j>Wa4e35_-AFDBl2r3@C4PoIrS^Os z`T1EwQBRS`4ke+XyqWxkcs`>Yy}eGQVFMi~nW+v}-98qe?%rfS+Zh+SyuNDtNf%R8 z^u);7rqf|u_&nC;u+d|~V^$lqX9}g>Iao7mL%WmQ_vL6gAI}ZjxN5(4pFEip-D;_) zXCQMj!-*%Qh-f(@u*!iD!nOS9ri`yNH|DeX^qI)zG6f8`cD7@L^yPN5>@}{kNq^nU z6*Az@zt)8j@&q!OLuaZwZmEoGEr$6fhOA%pjO?|6$Z#HJ{FW6&G;GEE#p6N` zV@;OA4kEs%k8~a{=lO!V!Ul*RORFa;qO7dYombroR!;LR_x~WUfGCU@h$j)#V#;WB z(&=R@B24vYjhI|GaMQ~ZHs9`>l0=Ds%MIj_SGw*?oh@Bh!#-7dIRH0~rGyeZr6v;SS==U(8suhhleauBa!y(mDI?T#VbHr%vI z-932*GBPhaElnU3|} z%vQwSeR^%V2)6a@-wfSP0uhX!=gjhfsH*=O!KjJ%*^pSWw1JbJ>yvZ4dsBqFv#qdq zv(Z~_t+lY(gJy2$a{NY2FdJ?2^?&VVqV+Y@_5(U`@sdBC4aSHGSF}t5vV~K{oB`?a zV+?Jm)*!hyVN;cv1dj23;pNgSmAcogEb;PgjU1@0v0^`jh>EbCQ`7YkyU>fx5gIYC z+Plt^>Ko?;8afftSb(lcqkS!U2$*?DfXYE>Ay@ii-PrCn=J6DH3JaTzES#ccU)=RIMupOsiFdpehmu(6k>n*i{|_F@E>siI_%j&`HtI{ zD5#=8+)&d{g8j<2m__9ZmPJBU5?pE7CK1QN^^9 zL1zt!Rx-X-`|B#>m_lsYz0V_sw<%6%@&x>tH`DEX>7cdOY|OfVQ4t|&Ba