diff --git a/lib/app.dart b/lib/app.dart index e6999c01..3f6b2a53 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,7 +1,7 @@ -import 'package:apidash/widgets/window_caption.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:window_manager/window_manager.dart' hide WindowCaption; +import 'widgets/widgets.dart' show WindowCaption; import 'providers/providers.dart'; import 'screens/screens.dart'; import 'consts.dart'; @@ -85,18 +85,19 @@ class _DashAppState extends ConsumerState { scaffoldBody: CollectionPane(), ) : Stack( - children: [ - kIsLinux ? const Dashboard() : const App(), - if (kIsWindows) - SizedBox( - height: 29, - child: WindowCaption( - backgroundColor: Colors.transparent, - brightness: isDarkMode ? Brightness.dark : Brightness.light, - ), + children: [ + kIsLinux ? const Dashboard() : const App(), + if (kIsWindows) + SizedBox( + height: 29, + child: WindowCaption( + backgroundColor: Colors.transparent, + brightness: + isDarkMode ? Brightness.dark : Brightness.light, + ), + ), + ], ), - ], - ), ); } } diff --git a/lib/codegen/codegen.dart b/lib/codegen/codegen.dart index 356854f6..45b72014 100644 --- a/lib/codegen/codegen.dart +++ b/lib/codegen/codegen.dart @@ -1,6 +1,7 @@ import 'package:apidash/models/models.dart' show RequestModel; import 'package:apidash/consts.dart'; import 'dart/http.dart'; +import 'dart/dio.dart'; import 'kotlin/okhttp.dart'; import 'python/http_client.dart'; import 'python/requests.dart'; @@ -22,6 +23,8 @@ class Codegen { return HARCodeGen().getCode(requestModel, defaultUriScheme); case CodegenLanguage.dartHttp: return DartHttpCodeGen().getCode(requestModel, defaultUriScheme); + case CodegenLanguage.dartDio: + return DartDioCodeGen().getCode(requestModel, defaultUriScheme); case CodegenLanguage.jsAxios: return AxiosCodeGen().getCode(requestModel, defaultUriScheme); case CodegenLanguage.jsFetch: diff --git a/lib/codegen/dart/dio.dart b/lib/codegen/dart/dio.dart new file mode 100644 index 00000000..fcaa8314 --- /dev/null +++ b/lib/codegen/dart/dio.dart @@ -0,0 +1,127 @@ +import 'package:apidash/consts.dart'; +import 'package:apidash/models/request_model.dart' show RequestModel; +import 'package:code_builder/code_builder.dart'; +import 'package:dart_style/dart_style.dart'; + +import 'shared.dart'; + +class DartDioCodeGen { + String? getCode( + RequestModel requestModel, + String defaultUriScheme, + ) { + try { + String url = requestModel.url; + if (!url.contains("://") && url.isNotEmpty) { + url = "$defaultUriScheme://$url"; + } + final next = generatedDartCode( + url: url, + method: requestModel.method, + queryParams: requestModel.paramsMap, + headers: requestModel.headersMap, + body: requestModel.requestBody, + contentType: requestModel.requestBodyContentType, + ); + return next; + } catch (e) { + return null; + } + } + + String generatedDartCode({ + required String url, + required HTTPVerb method, + required Map queryParams, + required Map headers, + required String? body, + required ContentType contentType, + }) { + final sbf = StringBuffer(); + final emitter = DartEmitter(); + final dioImport = Directive.import('package:dio/dio.dart', as: 'dio'); + sbf.writeln(dioImport.accept(emitter)); + + Expression? queryParamExp; + if (queryParams.isNotEmpty) { + queryParamExp = declareFinal('queryParams').assign( + literalMap(queryParams.map((key, value) => MapEntry(key, value))), + ); + } + Expression? headerExp; + if (headers.isNotEmpty) { + headerExp = declareFinal('headers').assign( + literalMap(headers.map((key, value) => MapEntry(key, value))), + ); + } + + Expression? dataExp; + if (kMethodsWithBody.contains(method) && (body?.isNotEmpty ?? false)) { + final strContent = CodeExpression(Code('r\'\'\'$body\'\'\'')); + switch (contentType) { + // dio dosen't need pass `content-type` header when body is json or plain text + case ContentType.json: + final convertImport = Directive.import('dart:convert', as: 'convert'); + sbf.writeln(convertImport.accept(emitter)); + dataExp = declareFinal('data') + .assign(refer('convert.json.decode').call([strContent])); + case ContentType.text: + dataExp = declareFinal('data').assign(strContent); + // when add new type of [ContentType], need update [dataExp]. + } + } + final responseExp = declareFinal('response').assign(InvokeExpression.newOf( + refer('dio.Dio'), + [literalString(url)], + { + if (queryParamExp != null) 'queryParameters': refer('queryParams'), + if (headerExp != null) + 'options': refer('Options').newInstance( + [], + {'headers': refer('headers')}, + ), + if (dataExp != null) 'data': refer('data'), + }, + [], + method.name, + ).awaited); + + final mainFunction = Method((m) { + final content = declareTryCatch( + showStackStrace: true, + body: [ + if (queryParamExp != null) queryParamExp, + if (headerExp != null) headerExp, + if (dataExp != null) dataExp, + responseExp, + refer('print').call([refer('response').property('statusCode')]), + refer('print').call([refer('response').property('data')]), + ], + onError: { + 'DioException': [ + refer('print').call([ + refer('e').property('response').nullSafeProperty('statusCode'), + ]), + refer('print').call([ + refer('e').property('response').nullSafeProperty('data'), + ]), + refer('print').call([refer('s')]), + ], + null: [ + refer('print').call([refer('e')]), + refer('print').call([refer('s')]), + ], + }, + ); + m + ..name = 'main' + ..returns = refer('void') + ..modifier = MethodModifier.async + ..body = content; + }); + + sbf.writeln(mainFunction.accept(emitter)); + + return DartFormatter(pageWidth: 160).format(sbf.toString()); + } +} diff --git a/lib/codegen/dart/http.dart b/lib/codegen/dart/http.dart index 2c0e016c..67184c25 100644 --- a/lib/codegen/dart/http.dart +++ b/lib/codegen/dart/http.dart @@ -1,147 +1,163 @@ import 'dart:io'; -import 'dart:convert'; -import 'package:jinja/jinja.dart' as jj; + import 'package:apidash/consts.dart'; -import 'package:apidash/utils/utils.dart' show padMultilineString; import 'package:apidash/models/models.dart' show RequestModel; +import 'package:code_builder/code_builder.dart'; +import 'package:dart_style/dart_style.dart'; + +import 'shared.dart'; class DartHttpCodeGen { - String kTemplateStart = """import 'package:http/http.dart' as http; - -void main() async { - var uri = Uri.parse('{{url}}'); - -"""; - - String kTemplateParams = """ - - var queryParams = {{params}}; -"""; - int kParamsPadding = 20; - - String kStringUrlParams = """ - - var urlQueryParams = Map.from(uri.queryParameters); - urlQueryParams.addAll(queryParams); - uri = uri.replace(queryParameters: urlQueryParams); -"""; - - String kStringNoUrlParams = """ - - uri = uri.replace(queryParameters: queryParams); -"""; - - String kTemplateBody = """ - - String body = r'''{{body}}'''; - -"""; - - String kTemplateHeaders = """ - - var headers = {{headers}}; - -"""; - int kHeadersPadding = 16; - - String kTemplateRequest = """ - - final response = await http.{{method}}(uri"""; - - String kStringRequestHeaders = """, - headers: headers"""; - - String kStringRequestBody = """, - body: body"""; - String kStringRequestEnd = r"""); - - int statusCode = response.statusCode; - if (statusCode >= 200 && statusCode < 300) { - print('Status Code: $statusCode'); - print('Response Body: ${response.body}'); - } - else{ - print('Error Status Code: $statusCode'); - print('Error Response Body: ${response.body}'); - } -} -"""; - String? getCode( RequestModel requestModel, String defaultUriScheme, ) { try { - String result = ""; - bool hasHeaders = false; - bool hasBody = false; - String url = requestModel.url; if (!url.contains("://") && url.isNotEmpty) { url = "$defaultUriScheme://$url"; } - var templateStartUrl = jj.Template(kTemplateStart); - result += templateStartUrl.render({"url": url}); - - var paramsList = requestModel.requestParams; - if (paramsList != null) { - var params = requestModel.paramsMap; - if (params.isNotEmpty) { - var templateParams = jj.Template(kTemplateParams); - var paramsString = kEncoder.convert(params); - paramsString = padMultilineString(paramsString, kParamsPadding); - result += templateParams.render({"params": paramsString}); - Uri uri = Uri.parse(url); - if (uri.hasQuery) { - result += kStringUrlParams; - } else { - result += kStringNoUrlParams; - } - } - } - - var method = requestModel.method; - var requestBody = requestModel.requestBody; - if (kMethodsWithBody.contains(method) && requestBody != null) { - var contentLength = utf8.encode(requestBody).length; - if (contentLength > 0) { - hasBody = true; - var templateBody = jj.Template(kTemplateBody); - result += templateBody.render({"body": requestBody}); - } - } - - var headersList = requestModel.requestHeaders; - if (headersList != null || hasBody) { - var headers = requestModel.headersMap; - if (headers.isNotEmpty || hasBody) { - hasHeaders = true; - if (hasBody) { - headers[HttpHeaders.contentTypeHeader] = - kContentTypeMap[requestModel.requestBodyContentType] ?? ""; - } - var headersString = kEncoder.convert(headers); - headersString = padMultilineString(headersString, kHeadersPadding); - var templateHeaders = jj.Template(kTemplateHeaders); - result += templateHeaders.render({"headers": headersString}); - } - } - - var templateRequest = jj.Template(kTemplateRequest); - result += templateRequest.render({"method": method.name}); - - if (hasHeaders) { - result += kStringRequestHeaders; - } - - if (hasBody) { - result += kStringRequestBody; - } - - result += kStringRequestEnd; - return result; + final next = generatedDartCode( + url: url, + method: requestModel.method, + queryParams: requestModel.paramsMap, + headers: requestModel.headersMap, + body: requestModel.requestBody, + contentType: requestModel.requestBodyContentType, + ); + return next; } catch (e) { return null; } } + + String generatedDartCode({ + required String url, + required HTTPVerb method, + required Map queryParams, + required Map headers, + required String? body, + required ContentType contentType, + }) { + final uri = Uri.parse(url); + + final sbf = StringBuffer(); + final emitter = DartEmitter(); + final dioImport = Directive.import('package:http/http.dart', as: 'http'); + sbf.writeln(dioImport.accept(emitter)); + + final uriExp = + declareVar('uri').assign(refer('Uri.parse').call([literalString(url)])); + + final composeHeaders = headers; + Expression? dataExp; + if (kMethodsWithBody.contains(method) && (body?.isNotEmpty ?? false)) { + final strContent = CodeExpression(Code('r\'\'\'$body\'\'\'')); + dataExp = declareVar('body', type: refer('String')).assign(strContent); + + composeHeaders.putIfAbsent(HttpHeaders.contentTypeHeader, + () => kContentTypeMap[contentType] ?? ''); + } + + Expression? queryParamExp; + List? uriReassignExps; + // var urlQueryParams = Map.from(uri.queryParameters); + // urlQueryParams.addAll(queryParams); + // uri = uri.replace(queryParameters: urlQueryParams); + + if (queryParams.isNotEmpty) { + queryParamExp = declareVar('queryParams').assign( + literalMap(queryParams.map((key, value) => MapEntry(key, value))), + ); + + uriReassignExps = [ + if (uri.hasQuery) + declareVar('urlQueryParams').assign( + InvokeExpression.newOf( + refer('Map'), + [refer('uri.queryParameters')], + {}, + [], + 'from', + ), + ), + if (uri.hasQuery) + refer('urlQueryParams') + .property('addAll') + .call([refer('queryParams')], {}), + refer('uri').assign(refer('uri').property('replace').call([], { + 'queryParameters': + uri.hasQuery ? refer('urlQueryParams') : refer('queryParams'), + })) + ]; + } + + Expression? headerExp; + if (headers.isNotEmpty) { + headerExp = declareVar('headers').assign( + literalMap(headers.map((key, value) => MapEntry(key, value))), + ); + } + final responseExp = declareFinal('response').assign(InvokeExpression.newOf( + refer('http.${method.name}'), + [refer('uri')], + { + if (headerExp != null) 'headers': refer('headers'), + if (dataExp != null) 'body': refer('body'), + }, + [], + ).awaited); + + final mainFunction = Method((m) { + final statusRef = refer('statusCode'); + m + ..name = 'main' + ..returns = refer('void') + ..modifier = MethodModifier.async + ..body = Block((b) { + b.statements.add(uriExp.statement); + if (queryParamExp != null) { + b.statements.add(const Code('\n')); + b.statements.add(queryParamExp.statement); + } + if (uriReassignExps != null) { + b.statements.addAll(uriReassignExps.map((e) => e.statement)); + } + if (dataExp != null) { + b.statements.add(const Code('\n')); + b.statements.add(dataExp.statement); + } + if (headerExp != null) { + b.statements.add(const Code('\n')); + b.statements.add(headerExp.statement); + } + b.statements.add(const Code('\n')); + b.statements.add(responseExp.statement); + b.statements.add(const Code('\n')); + b.statements.add(declareVar('statusCode', type: refer('int')) + .assign(refer('response').property('statusCode')) + .statement); + b.statements.add(declareIfElse( + condition: statusRef + .greaterOrEqualTo(literalNum(200)) + .and(statusRef.lessThan(literalNum(300))), + body: [ + refer('print').call([literalString(r'Status Code: $statusCode')]), + refer('print') + .call([literalString(r'Response Body: ${response.body}')]), + ], + elseBody: [ + refer('print') + .call([literalString(r'Error Status Code: $statusCode')]), + refer('print').call( + [literalString(r'Error Response Body: ${response.body}')]), + ], + )); + }); + }); + + sbf.writeln(mainFunction.accept(emitter)); + + return DartFormatter(pageWidth: 160).format(sbf.toString()); + } } diff --git a/lib/codegen/dart/shared.dart b/lib/codegen/dart/shared.dart new file mode 100644 index 00000000..073112dd --- /dev/null +++ b/lib/codegen/dart/shared.dart @@ -0,0 +1,52 @@ +import 'package:code_builder/code_builder.dart'; + +Code _toStatement(Spec spec) { + if (spec is Expression) { + return spec.statement; + } else if (spec is Code) { + return spec; + } else { + throw UnimplementedError(); + } +} + +Block declareTryCatch({ + required List body, + required Map> onError, + bool showStackStrace = false, +}) { + return Block((b) { + b.statements.add(const Code('try')); + b.statements.add(const Code('{')); + b.statements.addAll(body.map(_toStatement).toList()); + final entries = onError.entries; + + for (var error in entries) { + b.statements.add(const Code('}')); + if (error.key != null) { + b.statements.add(Code('on ${error.key}')); + } + b.statements.add(Code(showStackStrace ? 'catch(e,s)' : 'catch(e)')); + + b.statements.add(const Code('{')); + b.statements.addAll(error.value.map(_toStatement).toList()); + if (entries.last.key == error.key) b.statements.add(const Code('}')); + } + }); +} + +Block declareIfElse({ + required Expression condition, + required List body, + required List elseBody, +}) { + return Block.of([ + const Code('if('), + condition.code, + const Code('){'), + ...body.map(_toStatement), + const Code('} else {'), + ...elseBody.map(_toStatement), + const Code('}'), + ]); +} diff --git a/lib/common/utils.dart b/lib/common/utils.dart new file mode 100644 index 00000000..8eaf2b2c --- /dev/null +++ b/lib/common/utils.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:apidash/utils/utils.dart'; +import 'package:apidash/widgets/widgets.dart'; + +Future saveCollection( + Map data, ScaffoldMessengerState sm) async { + var message = ""; + try { + var pth = await getFileDownloadpath(null, "har"); + if (pth != null) { + await saveFile(pth, jsonMapToBytes(data)); + var sp = getShortPath(pth); + message = 'Saved to $sp'; + } + } catch (e) { + message = "An error occurred while exporting."; + } + sm.hideCurrentSnackBar(); + sm.showSnackBar(getSnackBar(message, small: false)); +} diff --git a/lib/consts.dart b/lib/consts.dart index 895c0460..d881fb86 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -63,14 +63,27 @@ const kP10 = EdgeInsets.all(10); const kPt24o8 = EdgeInsets.only(top: 24, left: 8.0, right: 8.0, bottom: 8.0); const kPt5o10 = EdgeInsets.only(left: 10.0, right: 10.0, top: 5.0, bottom: 10.0); +const kPh20 = EdgeInsets.symmetric( + horizontal: 20, +); const kPh20t40 = EdgeInsets.only( left: 20, right: 20, top: 40, ); const kPh60 = EdgeInsets.symmetric(horizontal: 60); -const kP24CollectionPane = EdgeInsets.only(top: 24, left: 8.0, bottom: 8.0); -const kP8CollectionPane = EdgeInsets.only(top: 8.0, left: 8.0, bottom: 8.0); +const kP24CollectionPane = EdgeInsets.only( + top: 24, + left: 4.0, + //right: 4.0, + // bottom: 8.0, +); +const kP8CollectionPane = EdgeInsets.only( + top: 8.0, + left: 4.0, + //right: 4.0, + // bottom: 8.0, +); const kPr8CollectionPane = EdgeInsets.only(right: 8.0); const kHSpacer4 = SizedBox(width: 4); @@ -242,6 +255,7 @@ enum CodegenLanguage { curl("cURL", "bash", "curl"), har("HAR", "json", "har"), dartHttp("Dart (http)", "dart", "dart"), + dartDio("Dart (dio)", "dart", "dart"), jsAxios("JavaScript (axios)", "javascript", "js"), jsFetch("JavaScript (fetch)", "javascript", "js"), nodejsAxios("node.js (axios)", "javascript", "js"), diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index 19d2ca28..35189fd0 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -6,16 +6,11 @@ import 'home_page/home_page.dart'; import 'intro_page.dart'; import 'settings_page.dart'; -class Dashboard extends ConsumerStatefulWidget { +class Dashboard extends ConsumerWidget { const Dashboard({super.key}); @override - ConsumerState createState() => _DashboardState(); -} - -class _DashboardState extends ConsumerState { - @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final railIdx = ref.watch(navRailIndexStateProvider); return Scaffold( body: SafeArea( @@ -50,13 +45,13 @@ class _DashboardState extends ConsumerState { children: [ Padding( padding: const EdgeInsets.only(bottom: 16.0), - child: bottomButton(context, railIdx, 1, Icons.help, - Icons.help_outline), + child: bottomButton(context, ref, railIdx, 1, + Icons.help, Icons.help_outline), ), Padding( padding: const EdgeInsets.only(bottom: 16.0), - child: bottomButton(context, railIdx, 2, Icons.settings, - Icons.settings_outlined), + child: bottomButton(context, ref, railIdx, 2, + Icons.settings, Icons.settings_outlined), ), ], ), @@ -99,6 +94,7 @@ class _DashboardState extends ConsumerState { TextButton bottomButton( BuildContext context, + WidgetRef ref, int railIdx, int buttonIdx, IconData selectedIcon, diff --git a/lib/screens/home_page/editor_pane/url_card.dart b/lib/screens/home_page/editor_pane/url_card.dart index 753fb34a..b7df1d66 100644 --- a/lib/screens/home_page/editor_pane/url_card.dart +++ b/lib/screens/home_page/editor_pane/url_card.dart @@ -4,20 +4,9 @@ import 'package:apidash/providers/providers.dart'; import 'package:apidash/widgets/widgets.dart'; import 'package:apidash/consts.dart'; -class EditorPaneRequestURLCard extends StatefulWidget { +class EditorPaneRequestURLCard extends StatelessWidget { const EditorPaneRequestURLCard({super.key}); - @override - State createState() => - _EditorPaneRequestURLCardState(); -} - -class _EditorPaneRequestURLCardState extends State { - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { return Card( @@ -52,25 +41,13 @@ class _EditorPaneRequestURLCardState extends State { } } -class DropdownButtonHTTPMethod extends ConsumerStatefulWidget { +class DropdownButtonHTTPMethod extends ConsumerWidget { const DropdownButtonHTTPMethod({ super.key, }); @override - ConsumerState createState() => - _DropdownButtonHTTPMethodState(); -} - -class _DropdownButtonHTTPMethodState - extends ConsumerState { - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final method = ref.watch(activeRequestModelProvider.select((value) => value?.method)); return DropdownButtonHttpMethod( @@ -85,23 +62,13 @@ class _DropdownButtonHTTPMethodState } } -class URLTextField extends ConsumerStatefulWidget { +class URLTextField extends ConsumerWidget { const URLTextField({ super.key, }); @override - ConsumerState createState() => _URLTextFieldState(); -} - -class _URLTextFieldState extends ConsumerState { - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final activeId = ref.watch(activeIdStateProvider); return URLField( activeId: activeId!, @@ -118,23 +85,13 @@ class _URLTextFieldState extends ConsumerState { } } -class SendButton extends ConsumerStatefulWidget { +class SendButton extends ConsumerWidget { const SendButton({ super.key, }); @override - ConsumerState createState() => _SendButtonState(); -} - -class _SendButtonState extends ConsumerState { - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final activeId = ref.watch(activeIdStateProvider); final sentRequestId = ref.watch(sentRequestIdStateProvider); return SendRequestButton( diff --git a/lib/screens/home_page/home_page.dart b/lib/screens/home_page/home_page.dart index d8843f0d..a7338a08 100644 --- a/lib/screens/home_page/home_page.dart +++ b/lib/screens/home_page/home_page.dart @@ -3,14 +3,9 @@ import 'package:apidash/widgets/widgets.dart'; import 'editor_pane/editor_pane.dart'; import 'collection_pane.dart'; -class HomePage extends StatefulWidget { +class HomePage extends StatelessWidget { const HomePage({super.key}); - @override - HomePageState createState() => HomePageState(); -} - -class HomePageState extends State { @override Widget build(BuildContext context) { return const Column( diff --git a/lib/screens/intro_page.dart b/lib/screens/intro_page.dart index f7ca937b..83d32253 100644 --- a/lib/screens/intro_page.dart +++ b/lib/screens/intro_page.dart @@ -1,14 +1,9 @@ import 'package:flutter/material.dart'; import 'package:apidash/widgets/widgets.dart'; -class IntroPage extends StatefulWidget { +class IntroPage extends StatelessWidget { const IntroPage({super.key}); - @override - State createState() => _IntroPageState(); -} - -class _IntroPageState extends State { @override Widget build(BuildContext context) { return const IntroMessage(); diff --git a/lib/screens/settings_page.dart b/lib/screens/settings_page.dart index e1249812..c40c8dd2 100644 --- a/lib/screens/settings_page.dart +++ b/lib/screens/settings_page.dart @@ -2,36 +2,40 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../providers/providers.dart'; import '../widgets/widgets.dart'; -import '../utils/utils.dart'; -import 'package:apidash/consts.dart'; +import '../common/utils.dart'; +import '../consts.dart'; -class SettingsPage extends ConsumerStatefulWidget { +class SettingsPage extends ConsumerWidget { const SettingsPage({super.key}); @override - ConsumerState createState() => _SettingsPageState(); -} - -class _SettingsPageState extends ConsumerState { - @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final settings = ref.watch(settingsProvider); final clearingData = ref.watch(clearDataStateProvider); var sm = ScaffoldMessenger.of(context); return Column( - crossAxisAlignment: CrossAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - constraints: const BoxConstraints(maxWidth: 800), + Padding( + padding: kPh20t40, + child: kIsDesktop + ? Text("Settings", + style: Theme.of(context).textTheme.headlineLarge) + : const SizedBox.shrink(), + ), + kIsDesktop + ? const Padding( + padding: kPh20, + child: Divider( + height: 1, + ), + ) + : const SizedBox.shrink(), + Expanded( child: ListView( - padding: kPh20t40, shrinkWrap: true, + padding: kPh20, children: [ - kIsDesktop - ? Text("Settings", - style: Theme.of(context).textTheme.headlineLarge) - : const SizedBox.shrink(), - kIsDesktop ? const Divider() : const SizedBox.shrink(), SwitchListTile( contentPadding: EdgeInsets.zero, hoverColor: kColorTransparent, @@ -114,26 +118,18 @@ class _SettingsPageState extends ConsumerState { title: const Text('Export Data'), subtitle: const Text( 'Export your collection to HAR (HTTP Archive format).\nVersion control this file or import in other API clients.'), - trailing: FilledButton( + trailing: FilledButton.icon( onPressed: () async { - var message = ""; - try { - var data = await ref - .read(collectionStateNotifierProvider.notifier) - .exportDataToHAR(); - var pth = await getFileDownloadpath(null, "har"); - if (pth != null) { - await saveFile(pth, jsonMapToBytes(data)); - var sp = getShortPath(pth); - message = 'Saved to $sp'; - } - } catch (e) { - message = "An error occurred while exporting."; - } - sm.hideCurrentSnackBar(); - sm.showSnackBar(getSnackBar(message, small: false)); + var data = await ref + .read(collectionStateNotifierProvider.notifier) + .exportDataToHAR(); + await saveCollection(data, sm); }, - child: const Text("Export Data"), + label: const Text("Export"), + icon: const Icon( + Icons.arrow_outward_rounded, + size: 20, + ), ), ), ListTile( @@ -141,7 +137,7 @@ class _SettingsPageState extends ConsumerState { hoverColor: kColorTransparent, title: const Text('Clear Data'), subtitle: const Text('Delete all requests data from the disk'), - trailing: FilledButton.tonal( + trailing: FilledButton.tonalIcon( style: FilledButton.styleFrom( backgroundColor: settings.isDark ? kColorDarkDanger @@ -185,7 +181,11 @@ class _SettingsPageState extends ConsumerState { ], ), ), - child: const Text("Clear Data"), + label: const Text("Clear"), + icon: const Icon( + Icons.delete_forever_rounded, + size: 20, + ), ), ), ], diff --git a/lib/services/hive_services.dart b/lib/services/hive_services.dart index 3052024e..43ed769c 100644 --- a/lib/services/hive_services.dart +++ b/lib/services/hive_services.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:hive_flutter/hive_flutter.dart'; -const String kDataBox = "data"; +const String kDataBox = "apidash-data"; const String kKeyDataBoxIds = "ids"; -const String kSettingsBox = "settings"; +const String kSettingsBox = "apidash-settings"; Future openBoxes() async { await Hive.initFlutter(); diff --git a/lib/utils/file_utils.dart b/lib/utils/file_utils.dart index be18a209..e9594864 100644 --- a/lib/utils/file_utils.dart +++ b/lib/utils/file_utils.dart @@ -7,6 +7,10 @@ import 'package:path_provider/path_provider.dart'; const uuid = Uuid(); +String getNewUuid() { + return uuid.v1(); +} + String? getFileExtension(String? mimeType) { if (mimeType == null) { return null; diff --git a/lib/widgets/buttons.dart b/lib/widgets/buttons.dart index 10d0c59c..348b81ca 100644 --- a/lib/widgets/buttons.dart +++ b/lib/widgets/buttons.dart @@ -5,7 +5,7 @@ import 'package:apidash/utils/utils.dart'; import 'package:apidash/consts.dart'; import "snackbars.dart"; -class CopyButton extends StatefulWidget { +class CopyButton extends StatelessWidget { const CopyButton({ super.key, required this.toCopy, @@ -15,21 +15,16 @@ class CopyButton extends StatefulWidget { final String toCopy; final bool showLabel; - @override - State createState() => _CopyButtonState(); -} - -class _CopyButtonState extends State { @override Widget build(BuildContext context) { var sm = ScaffoldMessenger.of(context); return Tooltip( - message: widget.showLabel ? '' : kLabelCopy, + message: showLabel ? '' : kLabelCopy, child: SizedBox( - width: widget.showLabel ? null : kTextButtonMinWidth, + width: showLabel ? null : kTextButtonMinWidth, child: TextButton( onPressed: () async { - await Clipboard.setData(ClipboardData(text: widget.toCopy)); + await Clipboard.setData(ClipboardData(text: toCopy)); sm.hideCurrentSnackBar(); sm.showSnackBar(getSnackBar("Copied")); }, @@ -40,7 +35,7 @@ class _CopyButtonState extends State { Icons.content_copy, size: 20, ), - if (widget.showLabel) const Text(kLabelCopy) + if (showLabel) const Text(kLabelCopy) ], ), ), @@ -49,7 +44,7 @@ class _CopyButtonState extends State { } } -class SendRequestButton extends StatefulWidget { +class SendRequestButton extends StatelessWidget { const SendRequestButton({ super.key, required this.activeId, @@ -61,29 +56,17 @@ class SendRequestButton extends StatefulWidget { final String? sentRequestId; final void Function() onTap; - @override - State createState() => _SendRequestButtonState(); -} - -class _SendRequestButtonState extends State { - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { - bool disable = widget.sentRequestId != null; + bool disable = sentRequestId != null; return FilledButton( - onPressed: disable ? null : widget.onTap, + onPressed: disable ? null : onTap, child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( disable - ? (widget.activeId == widget.sentRequestId - ? kLabelSending - : kLabelBusy) + ? (activeId == sentRequestId ? kLabelSending : kLabelBusy) : kLabelSend, style: kTextStyleButton, ), @@ -99,7 +82,7 @@ class _SendRequestButtonState extends State { } } -class SaveInDownloadsButton extends StatefulWidget { +class SaveInDownloadsButton extends StatelessWidget { const SaveInDownloadsButton({ super.key, this.content, @@ -115,29 +98,24 @@ class SaveInDownloadsButton extends StatefulWidget { final String? name; final bool showLabel; - @override - State createState() => _SaveInDownloadsButtonState(); -} - -class _SaveInDownloadsButtonState extends State { @override Widget build(BuildContext context) { var sm = ScaffoldMessenger.of(context); return Tooltip( - message: widget.showLabel ? '' : kLabelDownload, + message: showLabel ? '' : kLabelDownload, child: SizedBox( - width: widget.showLabel ? null : kTextButtonMinWidth, + width: showLabel ? null : kTextButtonMinWidth, child: TextButton( - onPressed: (widget.content != null) + onPressed: (content != null) ? () async { var message = ""; var path = await getFileDownloadpath( - widget.name, - widget.ext ?? getFileExtension(widget.mimeType), + name, + ext ?? getFileExtension(mimeType), ); if (path != null) { try { - await saveFile(path, widget.content!); + await saveFile(path, content!); var sp = getShortPath(path); message = 'Saved to $sp'; } catch (e) { @@ -157,7 +135,7 @@ class _SaveInDownloadsButtonState extends State { Icons.download, size: 20, ), - if (widget.showLabel) const Text(kLabelDownload) + if (showLabel) const Text(kLabelDownload) ], ), ), @@ -166,7 +144,7 @@ class _SaveInDownloadsButtonState extends State { } } -class RepoButton extends StatefulWidget { +class RepoButton extends StatelessWidget { const RepoButton({ super.key, this.text, @@ -176,15 +154,10 @@ class RepoButton extends StatefulWidget { final String? text; final IconData? icon; - @override - State createState() => _RepoButtonState(); -} - -class _RepoButtonState extends State { @override Widget build(BuildContext context) { - var label = widget.text ?? "GitHub"; - if (widget.icon == null) { + var label = text ?? "GitHub"; + if (icon == null) { return FilledButton( onPressed: () { launchUrl(Uri.parse(kGitUrl)); @@ -200,7 +173,7 @@ class _RepoButtonState extends State { launchUrl(Uri.parse(kGitUrl)); }, icon: Icon( - widget.icon, + icon, size: 20.0, ), label: Text( @@ -211,7 +184,7 @@ class _RepoButtonState extends State { } } -class DiscordButton extends StatefulWidget { +class DiscordButton extends StatelessWidget { const DiscordButton({ super.key, this.text, @@ -219,14 +192,9 @@ class DiscordButton extends StatefulWidget { final String? text; - @override - State createState() => _DiscordButtonState(); -} - -class _DiscordButtonState extends State { @override Widget build(BuildContext context) { - var label = widget.text ?? 'Discord Server'; + var label = text ?? 'Discord Server'; return FilledButton.icon( onPressed: () { launchUrl(Uri.parse(kDiscordUrl)); @@ -242,3 +210,27 @@ class _DiscordButtonState extends State { ); } } + +class SaveButton extends StatelessWidget { + const SaveButton({ + super.key, + this.onPressed, + }); + + final VoidCallback? onPressed; + + @override + Widget build(BuildContext context) { + return TextButton.icon( + onPressed: onPressed, + icon: const Icon( + Icons.save, + size: 20, + ), + label: const Text( + kLabelSave, + style: kTextStyleButton, + ), + ); + } +} diff --git a/lib/widgets/cards.dart b/lib/widgets/cards.dart index 43b3cb0c..e99a71f8 100644 --- a/lib/widgets/cards.dart +++ b/lib/widgets/cards.dart @@ -129,15 +129,11 @@ class SidebarRequestCard extends StatelessWidget { } } -class RequestDetailsCard extends StatefulWidget { +class RequestDetailsCard extends StatelessWidget { const RequestDetailsCard({super.key, this.child}); final Widget? child; @override - State createState() => _RequestDetailsCardState(); -} - -class _RequestDetailsCardState extends State { @override Widget build(BuildContext context) { return Card( @@ -148,7 +144,7 @@ class _RequestDetailsCardState extends State { borderRadius: kBorderRadius12, ), elevation: 0, - child: widget.child, + child: child, ); } } diff --git a/lib/widgets/codegen_previewer.dart b/lib/widgets/codegen_previewer.dart index 537458b7..298610c7 100644 --- a/lib/widgets/codegen_previewer.dart +++ b/lib/widgets/codegen_previewer.dart @@ -97,7 +97,7 @@ List generateSpans( return spans; } -class ViewCodePane extends StatefulWidget { +class ViewCodePane extends StatelessWidget { const ViewCodePane({ super.key, required this.code, @@ -109,11 +109,6 @@ class ViewCodePane extends StatefulWidget { final CodegenLanguage codegenLanguage; final Function(CodegenLanguage?) onChangedCodegenLanguage; - @override - State createState() => _ViewCodePaneState(); -} - -class _ViewCodePaneState extends State { @override Widget build(BuildContext context) { var codeTheme = Theme.of(context).brightness == Brightness.light @@ -145,17 +140,17 @@ class _ViewCodePaneState extends State { children: [ Expanded( child: DropdownButtonCodegenLanguage( - codegenLanguage: widget.codegenLanguage, - onChanged: widget.onChangedCodegenLanguage, + codegenLanguage: codegenLanguage, + onChanged: onChangedCodegenLanguage, ), ), CopyButton( - toCopy: widget.code, + toCopy: code, showLabel: showLabel, ), SaveInDownloadsButton( - content: stringToBytes(widget.code), - ext: widget.codegenLanguage.ext, + content: stringToBytes(code), + ext: codegenLanguage.ext, showLabel: showLabel, ) ], @@ -168,9 +163,9 @@ class _ViewCodePaneState extends State { padding: kP8, decoration: textContainerdecoration, child: CodeGenPreviewer( - code: widget.code, + code: code, theme: codeTheme, - language: widget.codegenLanguage.codeHighlightLang, + language: codegenLanguage.codeHighlightLang, textStyle: kCodeStyle, ), ), diff --git a/lib/widgets/dropdowns.dart b/lib/widgets/dropdowns.dart index e8290714..940ac34c 100644 --- a/lib/widgets/dropdowns.dart +++ b/lib/widgets/dropdowns.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:apidash/utils/utils.dart'; import 'package:apidash/consts.dart'; -class DropdownButtonHttpMethod extends StatefulWidget { +class DropdownButtonHttpMethod extends StatelessWidget { const DropdownButtonHttpMethod({ super.key, this.method, @@ -12,30 +12,19 @@ class DropdownButtonHttpMethod extends StatefulWidget { final HTTPVerb? method; final void Function(HTTPVerb? value)? onChanged; - @override - State createState() => - _DropdownButtonHttpMethodState(); -} - -class _DropdownButtonHttpMethodState extends State { - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { final surfaceColor = Theme.of(context).colorScheme.surface; return DropdownButton( focusColor: surfaceColor, - value: widget.method, + value: method, icon: const Icon(Icons.unfold_more_rounded), elevation: 4, underline: Container( height: 0, ), borderRadius: kBorderRadius12, - onChanged: widget.onChanged, + onChanged: onChanged, items: HTTPVerb.values.map>((HTTPVerb value) { return DropdownMenuItem( value: value, @@ -58,7 +47,7 @@ class _DropdownButtonHttpMethodState extends State { } } -class DropdownButtonContentType extends StatefulWidget { +class DropdownButtonContentType extends StatelessWidget { const DropdownButtonContentType({ super.key, this.contentType, @@ -68,18 +57,12 @@ class DropdownButtonContentType extends StatefulWidget { final ContentType? contentType; final void Function(ContentType?)? onChanged; - @override - State createState() => - _DropdownButtonContentTypeState(); -} - -class _DropdownButtonContentTypeState extends State { @override Widget build(BuildContext context) { final surfaceColor = Theme.of(context).colorScheme.surface; return DropdownButton( focusColor: surfaceColor, - value: widget.contentType, + value: contentType, icon: const Icon( Icons.unfold_more_rounded, size: 16, @@ -91,7 +74,7 @@ class _DropdownButtonContentTypeState extends State { underline: Container( height: 0, ), - onChanged: widget.onChanged, + onChanged: onChanged, borderRadius: kBorderRadius12, items: ContentType.values .map>((ContentType value) { @@ -110,28 +93,22 @@ class _DropdownButtonContentTypeState extends State { } } -class DropdownButtonCodegenLanguage extends StatefulWidget { +class DropdownButtonCodegenLanguage extends StatelessWidget { const DropdownButtonCodegenLanguage({ super.key, this.codegenLanguage, this.onChanged, }); - @override - State createState() => - _DropdownButtonCodegenLanguageState(); final CodegenLanguage? codegenLanguage; final void Function(CodegenLanguage?)? onChanged; -} -class _DropdownButtonCodegenLanguageState - extends State { @override Widget build(BuildContext context) { final surfaceColor = Theme.of(context).colorScheme.surface; return DropdownButton( focusColor: surfaceColor, - value: widget.codegenLanguage, + value: codegenLanguage, icon: const Icon( Icons.unfold_more_rounded, size: 16, @@ -143,7 +120,7 @@ class _DropdownButtonCodegenLanguageState underline: Container( height: 0, ), - onChanged: widget.onChanged, + onChanged: onChanged, borderRadius: kBorderRadius12, items: CodegenLanguage.values .map>((CodegenLanguage value) { diff --git a/lib/widgets/intro_message.dart b/lib/widgets/intro_message.dart index 3feaf712..970596d3 100644 --- a/lib/widgets/intro_message.dart +++ b/lib/widgets/intro_message.dart @@ -5,16 +5,11 @@ import '../consts.dart'; import 'markdown.dart'; import 'error_message.dart'; -class IntroMessage extends StatefulWidget { +class IntroMessage extends StatelessWidget { const IntroMessage({ super.key, }); - @override - State createState() => _IntroMessageState(); -} - -class _IntroMessageState extends State { @override Widget build(BuildContext context) { late String text; diff --git a/lib/widgets/markdown.dart b/lib/widgets/markdown.dart index a9dde1dc..c1467fba 100644 --- a/lib/widgets/markdown.dart +++ b/lib/widgets/markdown.dart @@ -4,7 +4,7 @@ import 'package:markdown/markdown.dart' as md; import 'package:url_launcher/url_launcher.dart'; import 'buttons.dart'; -class CustomMarkdown extends StatefulWidget { +class CustomMarkdown extends StatelessWidget { const CustomMarkdown({ super.key, required this.data, @@ -13,11 +13,6 @@ class CustomMarkdown extends StatefulWidget { final String data; final EdgeInsets padding; - @override - State createState() => _CustomMarkdownState(); -} - -class _CustomMarkdownState extends State { @override Widget build(BuildContext context) { final mdStyleSheet = MarkdownStyleSheet( @@ -25,9 +20,9 @@ class _CustomMarkdownState extends State { p: Theme.of(context).textTheme.titleMedium, ); return Markdown( - padding: widget.padding, + padding: padding, styleSheet: mdStyleSheet, - data: widget.data, + data: data, selectable: true, extensionSet: md.ExtensionSet.gitHubFlavored, onTapLink: (text, href, title) { diff --git a/lib/widgets/menus.dart b/lib/widgets/menus.dart index 02a5a15c..81958a02 100644 --- a/lib/widgets/menus.dart +++ b/lib/widgets/menus.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:apidash/consts.dart'; -class RequestCardMenu extends StatefulWidget { +class RequestCardMenu extends StatelessWidget { const RequestCardMenu({ super.key, this.onSelected, @@ -9,18 +9,13 @@ class RequestCardMenu extends StatefulWidget { final Function(RequestItemMenuOption)? onSelected; - @override - State createState() => _RequestCardMenuState(); -} - -class _RequestCardMenuState extends State { @override Widget build(BuildContext context) { return PopupMenuButton( padding: EdgeInsets.zero, splashRadius: 14, iconSize: 14, - onSelected: widget.onSelected, + onSelected: onSelected, itemBuilder: (BuildContext context) => >[ const PopupMenuItem( diff --git a/lib/widgets/response_widgets.dart b/lib/widgets/response_widgets.dart index da905749..960fc9dd 100644 --- a/lib/widgets/response_widgets.dart +++ b/lib/widgets/response_widgets.dart @@ -49,7 +49,7 @@ class SendingWidget extends StatelessWidget { } } -class ResponsePaneHeader extends StatefulWidget { +class ResponsePaneHeader extends StatelessWidget { const ResponsePaneHeader({ super.key, this.responseStatus, @@ -60,11 +60,7 @@ class ResponsePaneHeader extends StatefulWidget { final int? responseStatus; final String? message; final Duration? time; - @override - State createState() => _ResponsePaneHeaderState(); -} -class _ResponsePaneHeaderState extends State { @override Widget build(BuildContext context) { return Padding( @@ -81,10 +77,10 @@ class _ResponsePaneHeaderState extends State { text: "Response (", ), TextSpan( - text: "${widget.responseStatus}", + text: "$responseStatus", style: TextStyle( color: getResponseStatusCodeColor( - widget.responseStatus, + responseStatus, brightness: Theme.of(context).brightness, ), fontFamily: kCodeStyle.fontFamily, @@ -100,13 +96,13 @@ class _ResponsePaneHeaderState extends State { kHSpacer20, Expanded( child: Text( - widget.message ?? "", + message ?? "", softWrap: false, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.titleMedium!.copyWith( fontFamily: kCodeStyle.fontFamily, color: getResponseStatusCodeColor( - widget.responseStatus, + responseStatus, brightness: Theme.of(context).brightness, ), ), @@ -114,7 +110,7 @@ class _ResponsePaneHeaderState extends State { ), kHSpacer20, Text( - humanizeDuration(widget.time), + humanizeDuration(time), style: Theme.of(context).textTheme.titleMedium!.copyWith( fontFamily: kCodeStyle.fontFamily, color: Theme.of(context).colorScheme.secondary, @@ -207,7 +203,7 @@ class _ResponseTabViewState extends State } } -class ResponseHeadersHeader extends StatefulWidget { +class ResponseHeadersHeader extends StatelessWidget { const ResponseHeadersHeader({ super.key, required this.map, @@ -216,11 +212,7 @@ class ResponseHeadersHeader extends StatefulWidget { final Map map; final String name; - @override - State createState() => _ResponseHeadersHeaderState(); -} -class _ResponseHeadersHeaderState extends State { @override Widget build(BuildContext context) { return SizedBox( @@ -229,15 +221,15 @@ class _ResponseHeadersHeaderState extends State { children: [ Expanded( child: Text( - "${widget.name} (${widget.map.length} items)", + "$name (${map.length} items)", style: Theme.of(context).textTheme.labelLarge!.copyWith( fontWeight: FontWeight.bold, ), ), ), - if (widget.map.isNotEmpty) + if (map.isNotEmpty) CopyButton( - toCopy: kEncoder.convert(widget.map), + toCopy: kEncoder.convert(map), ), ], ), @@ -247,7 +239,7 @@ class _ResponseHeadersHeaderState extends State { const kHeaderRow = ["Header Name", "Header Value"]; -class ResponseHeaders extends StatefulWidget { +class ResponseHeaders extends StatelessWidget { const ResponseHeaders({ super.key, required this.responseHeaders, @@ -256,11 +248,7 @@ class ResponseHeaders extends StatefulWidget { final Map responseHeaders; final Map requestHeaders; - @override - State createState() => _ResponseHeadersState(); -} -class _ResponseHeadersState extends State { @override Widget build(BuildContext context) { return Padding( @@ -268,25 +256,25 @@ class _ResponseHeadersState extends State { child: ListView( children: [ ResponseHeadersHeader( - map: widget.responseHeaders, + map: responseHeaders, name: "Response Headers", ), - if (widget.responseHeaders.isNotEmpty) kVSpacer5, - if (widget.responseHeaders.isNotEmpty) + if (responseHeaders.isNotEmpty) kVSpacer5, + if (responseHeaders.isNotEmpty) MapTable( - map: widget.responseHeaders, + map: responseHeaders, colNames: kHeaderRow, firstColumnHeaderCase: true, ), kVSpacer10, ResponseHeadersHeader( - map: widget.requestHeaders, + map: requestHeaders, name: "Request Headers", ), - if (widget.requestHeaders.isNotEmpty) kVSpacer5, - if (widget.requestHeaders.isNotEmpty) + if (requestHeaders.isNotEmpty) kVSpacer5, + if (requestHeaders.isNotEmpty) MapTable( - map: widget.requestHeaders, + map: requestHeaders, colNames: kHeaderRow, firstColumnHeaderCase: true, ), @@ -296,21 +284,17 @@ class _ResponseHeadersState extends State { } } -class ResponseBody extends StatefulWidget { +class ResponseBody extends StatelessWidget { const ResponseBody({ super.key, this.activeRequestModel, }); final RequestModel? activeRequestModel; - @override - State createState() => _ResponseBodyState(); -} -class _ResponseBodyState extends State { @override Widget build(BuildContext context) { - final responseModel = widget.activeRequestModel?.responseModel; + final responseModel = activeRequestModel?.responseModel; if (responseModel == null) { return const ErrorMessage( message: @@ -348,7 +332,7 @@ class _ResponseBodyState extends State { } return BodySuccess( - key: Key("${widget.activeRequestModel!.id}-response"), + key: Key("${activeRequestModel!.id}-response"), mediaType: mediaType, options: options, bytes: responseModel.bodyBytes!, diff --git a/lib/widgets/tables.dart b/lib/widgets/tables.dart index 49d0021b..c238dae0 100644 --- a/lib/widgets/tables.dart +++ b/lib/widgets/tables.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:apidash/utils/utils.dart'; import 'package:apidash/consts.dart'; -class MapTable extends StatefulWidget { +class MapTable extends StatelessWidget { const MapTable( {super.key, required this.map, @@ -13,11 +13,6 @@ class MapTable extends StatefulWidget { final List colNames; final bool firstColumnHeaderCase; - @override - State createState() => _MapTableState(); -} - -class _MapTableState extends State { @override Widget build(BuildContext context) { return Table( @@ -33,7 +28,7 @@ class _MapTableState extends State { defaultVerticalAlignment: TableCellVerticalAlignment.middle, children: [ TableRow( - children: widget.colNames + children: colNames .map( (e) => TableCell( verticalAlignment: TableCellVerticalAlignment.top, @@ -51,7 +46,7 @@ class _MapTableState extends State { ) .toList(), ), - ...widget.map.entries.map( + ...map.entries.map( (entry) => TableRow( children: [ TableCell( @@ -59,7 +54,7 @@ class _MapTableState extends State { child: Padding( padding: kP1, child: SelectableText( - widget.firstColumnHeaderCase + firstColumnHeaderCase ? formatHeaderCase(entry.key) : entry.key, style: kCodeStyle.copyWith( diff --git a/lib/widgets/textfields.dart b/lib/widgets/textfields.dart index ff7b7313..76d1a9dc 100644 --- a/lib/widgets/textfields.dart +++ b/lib/widgets/textfields.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:apidash/consts.dart'; -class URLField extends StatefulWidget { +class URLField extends StatelessWidget { const URLField({ super.key, required this.activeId, @@ -13,21 +13,11 @@ class URLField extends StatefulWidget { final String? initialValue; final void Function(String)? onChanged; - @override - State createState() => _URLFieldState(); -} - -class _URLFieldState extends State { - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { return TextFormField( - key: Key("url-${widget.activeId}"), - initialValue: widget.initialValue, + key: Key("url-$activeId"), + initialValue: initialValue, style: kCodeStyle, decoration: InputDecoration( hintText: kHintTextUrlCard, @@ -38,12 +28,12 @@ class _URLFieldState extends State { ), border: InputBorder.none, ), - onChanged: widget.onChanged, + onChanged: onChanged, ); } } -class CellField extends StatefulWidget { +class CellField extends StatelessWidget { const CellField({ super.key, required this.keyId, @@ -59,41 +49,36 @@ class CellField extends StatefulWidget { final void Function(String)? onChanged; final ColorScheme? colorScheme; - @override - State createState() => _CellFieldState(); -} - -class _CellFieldState extends State { @override Widget build(BuildContext context) { - var colorScheme = widget.colorScheme ?? Theme.of(context).colorScheme; + var clrScheme = colorScheme ?? Theme.of(context).colorScheme; return TextFormField( - key: Key(widget.keyId), - initialValue: widget.initialValue, + key: Key(keyId), + initialValue: initialValue, style: kCodeStyle.copyWith( - color: colorScheme.onSurface, + color: clrScheme.onSurface, ), decoration: InputDecoration( hintStyle: kCodeStyle.copyWith( - color: colorScheme.outline.withOpacity( + color: clrScheme.outline.withOpacity( kHintOpacity, ), ), - hintText: widget.hintText, + hintText: hintText, focusedBorder: UnderlineInputBorder( borderSide: BorderSide( - color: colorScheme.primary.withOpacity( + color: clrScheme.primary.withOpacity( kHintOpacity, ), ), ), enabledBorder: UnderlineInputBorder( borderSide: BorderSide( - color: colorScheme.surfaceVariant, + color: clrScheme.surfaceVariant, ), ), ), - onChanged: widget.onChanged, + onChanged: onChanged, ); } } diff --git a/lib/widgets/texts.dart b/lib/widgets/texts.dart index 861574d7..f2cf5c92 100644 --- a/lib/widgets/texts.dart +++ b/lib/widgets/texts.dart @@ -12,10 +12,14 @@ class MethodBox extends StatelessWidget { if (method == HTTPVerb.delete) { text = "DEL"; } + if (method == HTTPVerb.patch) { + text = "PAT"; + } return SizedBox( - width: 28, + width: 24, child: Text( text, + textAlign: TextAlign.center, style: TextStyle( fontSize: 8, fontWeight: FontWeight.bold, diff --git a/lib/widgets/widgets.dart b/lib/widgets/widgets.dart index 23929f62..a4921bdc 100644 --- a/lib/widgets/widgets.dart +++ b/lib/widgets/widgets.dart @@ -1,22 +1,23 @@ -export 'editor.dart'; export 'buttons.dart'; -export 'tables.dart'; -export 'previewer.dart'; +export 'cards.dart'; export 'code_previewer.dart'; export 'codegen_previewer.dart'; -export 'error_message.dart'; export 'dropdowns.dart'; -export 'splitviews.dart'; -export 'texts.dart'; -export 'textfields.dart'; +export 'editor.dart'; +export 'error_message.dart'; export 'headerfield.dart'; -export 'menus.dart'; -export 'cards.dart'; export 'intro_message.dart'; +export 'json_previewer.dart'; +export 'markdown.dart'; +export 'menus.dart'; +export 'previewer.dart'; export 'request_widgets.dart'; export 'response_widgets.dart'; export 'snackbars.dart'; -export 'markdown.dart'; -export 'uint8_audio_player.dart'; +export 'splitviews.dart'; +export 'tables.dart'; export 'tabs.dart'; -export 'json_previewer.dart'; +export 'textfields.dart'; +export 'texts.dart'; +export 'uint8_audio_player.dart'; +export 'window_caption.dart'; diff --git a/pubspec.lock b/pubspec.lock index 246dc90d..55fc4e74 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,26 +5,26 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "61.0.0" + version: "64.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "6.2.0" archive: dependency: transitive description: name: archive - sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03" + sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" url: "https://pub.dev" source: hosted - version: "3.4.6" + version: "3.4.9" args: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: audio_session - sha256: "8a2bc5e30520e18f3fb0e366793d78057fb64cd5287862c76af0c8771f2a52ad" + sha256: "6fdf255ed3af86535c96452c33ecff1245990bb25a605bfb1958661ccc3d467f" url: "https://pub.dev" source: hosted - version: "0.1.16" + version: "0.1.18" axis_layout: dependency: transitive description: @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: barcode - sha256: "789f898eef0bd88312470bdb2cc996f895ad7dd5f89e9adde84b204546a90b45" + sha256: "2a8b2ee065f419c2aeda141436cc556d91ae772d220fd80679f4d431d6c2ab43" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.2.5" bidi: dependency: transitive description: @@ -101,26 +101,26 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.7" build_runner_core: dependency: transitive description: @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: built_value - sha256: a8de5955205b4d1dbbbc267daddf2178bd737e4bab8987c04a500478c9651e74 + sha256: c9aabae0718ec394e5bc3c7272e6bb0dc0b32201a08fe185ec1d8401d3e39309 url: "https://pub.dev" source: hosted - version: "8.6.3" + version: "8.8.1" characters: dependency: transitive description: @@ -165,10 +165,10 @@ packages: dependency: transitive description: name: cli_util - sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.4.1" clock: dependency: transitive description: @@ -178,13 +178,13 @@ packages: source: hosted version: "1.1.1" code_builder: - dependency: transitive + dependency: "direct main" description: name: code_builder - sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" + sha256: feee43a5c05e7b3199bb375a86430b8ada1b04104f2923d0e03cc01ca87b6d84 url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.9.0" collection: dependency: "direct main" description: @@ -205,10 +205,10 @@ packages: dependency: transitive description: name: coverage - sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" + sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" url: "https://pub.dev" source: hosted - version: "1.6.4" + version: "1.7.2" crypto: dependency: transitive description: @@ -218,13 +218,13 @@ packages: source: hosted version: "3.0.3" dart_style: - dependency: transitive + dependency: "direct main" description: name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.4" davi: dependency: "direct main" description: @@ -354,26 +354,26 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: ad76540d21c066228ee3f9d1dad64a9f7e46530e8bb7c85011a88bc1fd874bc5 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.0.1" flutter_markdown: dependency: "direct main" description: name: flutter_markdown - sha256: "8afc9a6aa6d8e8063523192ba837149dbf3d377a37c0b0fc579149a1fbd4a619" + sha256: "35108526a233cc0755664d445f8a6b4b61e6f8fe993b3658b80b4a26827fc196" url: "https://pub.dev" source: hosted - version: "0.6.18" + version: "0.6.18+2" flutter_riverpod: dependency: "direct main" description: name: flutter_riverpod - sha256: bdba94be666ecb1beeb0f5a748d96cdd6a37215f27e6b48c7673b95cecb800c8 + sha256: da9591d1f8d5881628ccd5c25c40e74fc3eef50ba45e40c3905a06e1712412d5 url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "2.4.9" flutter_svg: dependency: "direct main" description: @@ -404,10 +404,10 @@ packages: dependency: "direct dev" description: name: freezed - sha256: "21bf2825311de65501d22e563e3d7605dff57fb5e6da982db785ae5372ff018a" + sha256: "6c5031daae12c7072b3a87eff98983076434b4889ef2a44384d0cae3f82372ba" url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "2.4.6" freezed_annotation: dependency: "direct main" description: @@ -484,10 +484,10 @@ packages: dependency: "direct main" description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" http_multi_server: dependency: transitive description: @@ -573,10 +573,10 @@ packages: dependency: "direct main" description: name: just_audio - sha256: "5ed0cd723e17dfd8cd4b0253726221e67f6546841ea4553635cf895061fc335b" + sha256: b607cd1a43bac03d85c3aaee00448ff4a589ef2a77104e3d409889ff079bf823 url: "https://pub.dev" source: hosted - version: "0.9.35" + version: "0.9.36" just_audio_mpv: dependency: "direct main" description: @@ -589,18 +589,18 @@ packages: dependency: transitive description: name: just_audio_platform_interface - sha256: d8409da198bbc59426cd45d4c92fca522a2ec269b576ce29459d6d6fcaeb44df + sha256: c3dee0014248c97c91fe6299edb73dc4d6c6930a2f4f713579cd692d9e47f4a1 url: "https://pub.dev" source: hosted - version: "4.2.1" + version: "4.2.2" just_audio_web: dependency: transitive description: name: just_audio_web - sha256: ff62f733f437b25a0ff590f0e295fa5441dcb465f1edbdb33b3dea264705bc13 + sha256: "134356b0fe3d898293102b33b5fd618831ffdc72bb7a1b726140abdf22772b70" url: "https://pub.dev" source: hosted - version: "0.4.8" + version: "0.4.9" just_audio_windows: dependency: "direct main" description: @@ -765,10 +765,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" path_provider_foundation: dependency: transitive description: @@ -805,18 +805,18 @@ packages: dependency: transitive description: name: pdf - sha256: "9f75fc7f5580ea5e635b5724de58fb27f684c9ad03ed46fdc1aac768e4557315" + sha256: "93cbb2c06de9bab91844550f19896b2373e7a5ce25173995e7e5ec5e1741429d" url: "https://pub.dev" source: hosted - version: "3.10.4" + version: "3.10.7" petitparser: dependency: transitive description: name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "6.0.2" platform: dependency: transitive description: @@ -829,18 +829,18 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" pointer_interceptor: dependency: transitive description: name: pointer_interceptor - sha256: "7626e034489820fd599380d2bb4d3f4a0a5e3529370b62bfce53ab736b91adb2" + sha256: adf7a637f97c077041d36801b43be08559fd4322d2127b3f20bb7be1b9eebc22 url: "https://pub.dev" source: hosted - version: "0.9.3+6" + version: "0.9.3+7" pointycastle: dependency: transitive description: @@ -869,10 +869,10 @@ packages: dependency: "direct main" description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" pub_semver: dependency: transitive description: @@ -901,10 +901,10 @@ packages: dependency: transitive description: name: riverpod - sha256: "2af3d127a6e4e34b89b8f1f018086f5ded04b8e538174f0510bba3e4c0d878b1" + sha256: "942999ee48b899f8a46a860f1e13cee36f2f77609eb54c5b7a669bb20d550b11" url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "2.4.9" rxdart: dependency: transitive description: @@ -970,10 +970,10 @@ packages: dependency: transitive description: name: source_gen - sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.5.0" source_helper: dependency: transitive description: @@ -1114,74 +1114,74 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" + sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 url: "https://pub.dev" source: hosted - version: "6.1.14" + version: "6.2.2" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 + sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.2.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f" + sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.2.1" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.1.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88 + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.1.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618" + sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.2.0" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5" + sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9" url: "https://pub.dev" source: hosted - version: "2.0.20" + version: "2.2.2" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069" + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.1.1" uuid: dependency: "direct main" description: name: uuid - sha256: b715b8d3858b6fa9f68f87d20d98830283628014750c2b09b6f516c1da4af2a7 + sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.2.2" vector_graphics: dependency: transitive description: @@ -1218,10 +1218,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "11.10.0" + version: "13.0.0" watcher: dependency: transitive description: @@ -1258,10 +1258,10 @@ packages: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.1.1" window_manager: dependency: "direct main" description: @@ -1291,10 +1291,10 @@ packages: dependency: "direct main" description: name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.5.0" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 419b6397..cb791e3c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,8 @@ dependencies: scrollable_positioned_list: ^0.2.3 flutter_svg: ^2.0.9 vector_graphics_compiler: ^1.1.9+1 + code_builder: ^4.9.0 + dart_style: ^2.3.4 json_text_field: ^1.0.0 dev_dependencies: diff --git a/test/codegen/dart_dio_codegen_test.dart b/test/codegen/dart_dio_codegen_test.dart new file mode 100644 index 00000000..6f1e3149 --- /dev/null +++ b/test/codegen/dart_dio_codegen_test.dart @@ -0,0 +1,458 @@ +import 'package:apidash/codegen/dart/dio.dart'; +import 'package:test/test.dart'; + +import '../request_models.dart'; + +void main() { + final dartDioCodeGen = DartDioCodeGen(); + + group('GET Request', () { + test('GET 1', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; + +void main() async { + try { + final response = await dio.Dio.get('https://api.foss42.com'); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelGet1, "https"), expectedCode); + }); + + test('GET 2', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; + +void main() async { + try { + final queryParams = {'code': 'US'}; + final response = await dio.Dio.get( + 'https://api.foss42.com/country/data', + queryParameters: queryParams, + ); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelGet2, "https"), expectedCode); + }); + + test('GET 3', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; + +void main() async { + try { + final queryParams = {'code': 'IND'}; + final response = await dio.Dio.get( + 'https://api.foss42.com/country/data?code=US', + queryParameters: queryParams, + ); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelGet3, "https"), expectedCode); + }); + + test('GET 4', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; + +void main() async { + try { + final queryParams = { + 'num': '8700000', + 'digits': '3', + 'system': 'SS', + 'add_space': 'true', + 'trailing_zeros': 'true', + }; + final response = await dio.Dio.get( + 'https://api.foss42.com/humanize/social', + queryParameters: queryParams, + ); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelGet4, "https"), expectedCode); + }); + + test('GET 5', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; + +void main() async { + try { + final headers = {'User-Agent': 'Test Agent'}; + final response = await dio.Dio.get( + 'https://api.github.com/repos/foss42/apidash', + options: Options(headers: headers), + ); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelGet5, "https"), expectedCode); + }); + + test('GET 6', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; + +void main() async { + try { + final queryParams = {'raw': 'true'}; + final headers = {'User-Agent': 'Test Agent'}; + final response = await dio.Dio.get( + 'https://api.github.com/repos/foss42/apidash', + queryParameters: queryParams, + options: Options(headers: headers), + ); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelGet6, "https"), expectedCode); + }); + + test('GET 7', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; + +void main() async { + try { + final response = await dio.Dio.get('https://api.foss42.com'); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelGet7, "https"), expectedCode); + }); + + test('GET 8', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; + +void main() async { + try { + final queryParams = {'raw': 'true'}; + final headers = {'User-Agent': 'Test Agent'}; + final response = await dio.Dio.get( + 'https://api.github.com/repos/foss42/apidash', + queryParameters: queryParams, + options: Options(headers: headers), + ); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelGet8, "https"), expectedCode); + }); + }); + + group('HEAD Request', () { + test('HEAD 1', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; + +void main() async { + try { + final response = await dio.Dio.head('https://api.foss42.com'); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelHead1, "https"), expectedCode); + }); + + test('HEAD 2', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; + +void main() async { + try { + final response = await dio.Dio.head('http://api.foss42.com'); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelHead2, "http"), expectedCode); + }); + }); + + group('POST Request', () { + test('POST 1', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; + +void main() async { + try { + final data = r'''{ +"text": "I LOVE Flutter" +}'''; + final response = await dio.Dio.post( + 'https://api.foss42.com/case/lower', + data: data, + ); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelPost1, "https"), expectedCode); + }); + + test('POST 2', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; +import 'dart:convert' as convert; + +void main() async { + try { + final data = convert.json.decode(r'''{ +"text": "I LOVE Flutter" +}'''); + final response = await dio.Dio.post( + 'https://api.foss42.com/case/lower', + data: data, + ); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelPost2, "https"), expectedCode); + }); + + test('POST 3', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; +import 'dart:convert' as convert; + +void main() async { + try { + final headers = {'User-Agent': 'Test Agent'}; + final data = convert.json.decode(r'''{ +"text": "I LOVE Flutter" +}'''); + final response = await dio.Dio.post( + 'https://api.foss42.com/case/lower', + options: Options(headers: headers), + data: data, + ); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelPost3, "https"), expectedCode); + }); + }); + group('PUT Request', () { + test('PUT 1', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; +import 'dart:convert' as convert; + +void main() async { + try { + final data = convert.json.decode(r'''{ +"name": "morpheus", +"job": "zion resident" +}'''); + final response = await dio.Dio.put( + 'https://reqres.in/api/users/2', + data: data, + ); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelPut1, "https"), expectedCode); + }); + }); + + group('PATCH Request', () { + test('PATCH 1', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; +import 'dart:convert' as convert; + +void main() async { + try { + final data = convert.json.decode(r'''{ +"name": "marfeus", +"job": "accountant" +}'''); + final response = await dio.Dio.patch( + 'https://reqres.in/api/users/2', + data: data, + ); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect(dartDioCodeGen.getCode(requestModelPatch1, "https"), expectedCode); + }); + }); + + group('DELETE Request', () { + test('DELETE 1', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; + +void main() async { + try { + final response = await dio.Dio.delete('https://reqres.in/api/users/2'); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect( + dartDioCodeGen.getCode(requestModelDelete1, "https"), expectedCode); + }); + + test('DELETE 2', () { + const expectedCode = r"""import 'package:dio/dio.dart' as dio; +import 'dart:convert' as convert; + +void main() async { + try { + final data = convert.json.decode(r'''{ +"name": "marfeus", +"job": "accountant" +}'''); + final response = await dio.Dio.delete( + 'https://reqres.in/api/users/2', + data: data, + ); + print(response.statusCode); + print(response.data); + } on DioException catch (e, s) { + print(e.response?.statusCode); + print(e.response?.data); + print(s); + } catch (e, s) { + print(e); + print(s); + } +} +"""; + expect( + dartDioCodeGen.getCode(requestModelDelete2, "https"), expectedCode); + }); + }); +} diff --git a/test/codegen/dart_http_codegen_test.dart b/test/codegen/dart_http_codegen_test.dart index e384b628..b28018c5 100644 --- a/test/codegen/dart_http_codegen_test.dart +++ b/test/codegen/dart_http_codegen_test.dart @@ -1,5 +1,6 @@ import 'package:apidash/codegen/dart/http.dart'; import 'package:test/test.dart'; + import '../request_models.dart'; void main() { @@ -18,8 +19,7 @@ void main() async { if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -34,9 +34,7 @@ void main() async { void main() async { var uri = Uri.parse('https://api.foss42.com/country/data'); - var queryParams = { - "code": "US" - }; + var queryParams = {'code': 'US'}; uri = uri.replace(queryParameters: queryParams); final response = await http.get(uri); @@ -45,13 +43,13 @@ void main() async { if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } } """; + expect(dartHttpCodeGen.getCode(requestModelGet2, "https"), expectedCode); }); @@ -61,10 +59,8 @@ void main() async { void main() async { var uri = Uri.parse('https://api.foss42.com/country/data?code=US'); - var queryParams = { - "code": "IND" - }; - var urlQueryParams = Map.from(uri.queryParameters); + var queryParams = {'code': 'IND'}; + var urlQueryParams = Map.from(uri.queryParameters); urlQueryParams.addAll(queryParams); uri = uri.replace(queryParameters: urlQueryParams); @@ -74,8 +70,7 @@ void main() async { if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -91,12 +86,12 @@ void main() async { var uri = Uri.parse('https://api.foss42.com/humanize/social'); var queryParams = { - "num": "8700000", - "digits": "3", - "system": "SS", - "add_space": "true", - "trailing_zeros": "true" - }; + 'num': '8700000', + 'digits': '3', + 'system': 'SS', + 'add_space': 'true', + 'trailing_zeros': 'true', + }; uri = uri.replace(queryParameters: queryParams); final response = await http.get(uri); @@ -105,8 +100,7 @@ void main() async { if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -121,19 +115,18 @@ void main() async { void main() async { var uri = Uri.parse('https://api.github.com/repos/foss42/apidash'); - var headers = { - "User-Agent": "Test Agent" - }; + var headers = {'User-Agent': 'Test Agent'}; - final response = await http.get(uri, - headers: headers); + final response = await http.get( + uri, + headers: headers, + ); int statusCode = response.statusCode; if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -148,24 +141,21 @@ void main() async { void main() async { var uri = Uri.parse('https://api.github.com/repos/foss42/apidash'); - var queryParams = { - "raw": "true" - }; + var queryParams = {'raw': 'true'}; uri = uri.replace(queryParameters: queryParams); - var headers = { - "User-Agent": "Test Agent" - }; + var headers = {'User-Agent': 'Test Agent'}; - final response = await http.get(uri, - headers: headers); + final response = await http.get( + uri, + headers: headers, + ); int statusCode = response.statusCode; if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -186,8 +176,7 @@ void main() async { if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -202,24 +191,21 @@ void main() async { void main() async { var uri = Uri.parse('https://api.github.com/repos/foss42/apidash'); - var queryParams = { - "raw": "true" - }; + var queryParams = {'raw': 'true'}; uri = uri.replace(queryParameters: queryParams); - var headers = { - "User-Agent": "Test Agent" - }; + var headers = {'User-Agent': 'Test Agent'}; - final response = await http.get(uri, - headers: headers); + final response = await http.get( + uri, + headers: headers, + ); int statusCode = response.statusCode; if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -242,8 +228,7 @@ void main() async { if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -264,8 +249,7 @@ void main() async { if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -286,20 +270,19 @@ void main() async { "text": "I LOVE Flutter" }'''; - var headers = { - "content-type": "text/plain" - }; + var headers = {'content-type': 'text/plain'}; - final response = await http.post(uri, - headers: headers, - body: body); + final response = await http.post( + uri, + headers: headers, + body: body, + ); int statusCode = response.statusCode; if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -318,20 +301,19 @@ void main() async { "text": "I LOVE Flutter" }'''; - var headers = { - "content-type": "application/json" - }; + var headers = {'content-type': 'application/json'}; - final response = await http.post(uri, - headers: headers, - body: body); + final response = await http.post( + uri, + headers: headers, + body: body, + ); int statusCode = response.statusCode; if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -351,20 +333,21 @@ void main() async { }'''; var headers = { - "User-Agent": "Test Agent", - "content-type": "application/json" - }; + 'User-Agent': 'Test Agent', + 'content-type': 'application/json', + }; - final response = await http.post(uri, - headers: headers, - body: body); + final response = await http.post( + uri, + headers: headers, + body: body, + ); int statusCode = response.statusCode; if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -385,20 +368,19 @@ void main() async { "job": "zion resident" }'''; - var headers = { - "content-type": "application/json" - }; + var headers = {'content-type': 'application/json'}; - final response = await http.put(uri, - headers: headers, - body: body); + final response = await http.put( + uri, + headers: headers, + body: body, + ); int statusCode = response.statusCode; if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -420,20 +402,19 @@ void main() async { "job": "accountant" }'''; - var headers = { - "content-type": "application/json" - }; + var headers = {'content-type': 'application/json'}; - final response = await http.patch(uri, - headers: headers, - body: body); + final response = await http.patch( + uri, + headers: headers, + body: body, + ); int statusCode = response.statusCode; if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -457,8 +438,7 @@ void main() async { if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); } @@ -479,20 +459,19 @@ void main() async { "job": "accountant" }'''; - var headers = { - "content-type": "application/json" - }; + var headers = {'content-type': 'application/json'}; - final response = await http.delete(uri, - headers: headers, - body: body); + final response = await http.delete( + uri, + headers: headers, + body: body, + ); int statusCode = response.statusCode; if (statusCode >= 200 && statusCode < 300) { print('Status Code: $statusCode'); print('Response Body: ${response.body}'); - } - else{ + } else { print('Error Status Code: $statusCode'); print('Error Response Body: ${response.body}'); }